diff --git a/.appveyor.yml b/.appveyor.yml index 708961fd924c..567da9cd5d5c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,7 +4,7 @@ environment: HOME: "%HOMEDRIVE%%HOMEPATH%" PYTHON: C:\Python27 SCONS_CACHE_ROOT: "%HOME%\\scons_cache" - SCONS_CACHE_LIMIT: 512 + SCONS_CACHE_LIMIT: 1024 matrix: - VS: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat GD_PLATFORM: windows @@ -17,7 +17,8 @@ cache: install: - SET "PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - - pip install --egg scons # it will fail on AppVeyor without --egg flag + - pip install -U wheel # needed for pip install scons to work, otherwise a flag is missing + - pip install scons - if defined VS call "%VS%" %ARCH% # if defined - so we can also use mingw before_build: @@ -28,4 +29,4 @@ before_build: - SET "SCONS_CACHE=%SCONS_CACHE_ROOT%\master" build_script: -- scons platform=%GD_PLATFORM% target=%TARGET% tools=%TOOLS% verbose=yes progress=no gdnative_wrapper=yes +- scons platform=%GD_PLATFORM% target=%TARGET% tools=%TOOLS% debug_symbols=no verbose=yes progress=no gdnative_wrapper=yes diff --git a/.clang-format b/.clang-format index 212bc25109da..3a2c39a174be 100644 --- a/.clang-format +++ b/.clang-format @@ -1,6 +1,6 @@ # Commented out parameters are those with the same value as base LLVM style # We can uncomment them if we want to change their value, or enforce the -# chosen value in case the base style changes (last sync: Clang 5.0.0). +# chosen value in case the base style changes (last sync: Clang 6.0.1). --- ### General config, applies to all languages ### BasedOnStyle: LLVM @@ -32,6 +32,7 @@ AllowShortIfStatementsOnASingleLine: true # AfterObjCDeclaration: false # AfterStruct: false # AfterUnion: false +# AfterExternBlock: false # BeforeCatch: false # BeforeElse: false # IndentBraces: false @@ -60,6 +61,7 @@ Cpp11BracedListStyle: false # - foreach # - Q_FOREACH # - BOOST_FOREACH +# IncludeBlocks: Preserve IncludeCategories: - Regex: '".*"' Priority: 1 @@ -69,6 +71,7 @@ IncludeCategories: Priority: 3 # IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: true +# IndentPPDirectives: None IndentWidth: 4 # IndentWrappedFunctionNames: false # JavaScriptQuotes: Leave @@ -86,6 +89,10 @@ IndentWidth: 4 # PenaltyExcessCharacter: 1000000 # PenaltyReturnTypeOnItsOwnLine: 60 # PointerAlignment: Right +# RawStringFormats: +# - Delimiter: pb +# Language: TextProto +# BasedOnStyle: google # ReflowComments: true # SortIncludes: true # SortUsingDeclarations: true diff --git a/.editorconfig b/.editorconfig index e51d0d4c60d5..d4b7fbb4719a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,7 +9,7 @@ insert_final_newline = true [*.{cpp,hpp,c,h,mm}] trim_trailing_whitespace = true -[*.py] +[*.{py,cs}] indent_style = space indent_size = 4 diff --git a/.gitattributes b/.gitattributes index 03c6f96f80eb..47ba2b45bb09 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 f8296ef51ebf..fe504482b3f8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ platform/android/java/build.gradle platform/android/java/AndroidManifest.xml platform/android/java/libs/* platform/android/java/assets +platform/android/java/.idea/* +platform/android/java/*.iml # General c++ generated files *.lib @@ -44,6 +46,7 @@ gmon.out # QT project files *.config *.creator +*.creator.* *.files *.includes @@ -88,6 +91,9 @@ bld/ *.debug *.dSYM +# Visual Studio cache/options directory +.vs/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -95,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 @@ -235,6 +238,7 @@ ClientBin/ *.pfx *.publishsettings node_modules/ +__pycache__/ # KDE .directory @@ -288,6 +292,7 @@ logo.h TAGS !TAGS/ tags +*.tags !tags/ gtags.files GTAGS @@ -306,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 57580c1c41ea..a8bc8289e4ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,15 @@ language: cpp +# OS config, depends on actual 'os' in build matrix dist: trusty - sudo: false env: global: - SCONS_CACHE=$HOME/.scons_cache - SCONS_CACHE_LIMIT=1024 - - OPTIONS="verbose=yes progress=no gdnative_wrapper=yes" - - secure: AnjB84ZZYDxDQdWwsT9PJrD5tEnwcauzvVeSG+us2GxgfyJ2HFArkZ/IjlvbsfYIiiHghJ2Ssy9yPtUT921BtNNHoWuN/8YQziGrlwiSfLGmS/n8GFH22OYwzDSa7UC7ODts5La2I4+JzrdtF933TwE+4QzH4E3GyaKbznh402E= + - OPTIONS="debug_symbols=no verbose=yes progress=no gdnative_wrapper=yes" + - secure: "uch9QszCgsl1qVbuzY41P7S2hWL2IiNFV4SbAYRCdi0oJ9MIu+pVyrQdpf3+jG4rH6j4Rffl+sN17Zz4dIDDioFL1JwqyCqyCyswR8uACC0Rr8gr4Mi3+HIRbv+2s2P4cIQq41JM8FJe84k9jLEMGCGh69w+ibCWoWs74CokYVA=" cache: directories: @@ -20,90 +20,81 @@ matrix: - env: STATIC_CHECKS=yes os: linux compiler: gcc - - env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools" + addons: + apt: + sources: + - llvm-toolchain-trusty-6.0 + - ubuntu-toolchain-r-test + packages: + - clang-format-6.0 + - libstdc++6 # >= 4.9 needed for clang-format-6.0 + + - 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 - - env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang" + addons: + apt: + sources: + - 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, 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: PLATFORM=x11 TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="werror=yes" os: linux compiler: clang - #- env: GODOT_TARGET=windows TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools - # os: linux - # compiler: gcc - - env: GODOT_TARGET=android TOOLS=no CACHE_NAME=${GODOT_TARGET}-gcc + addons: + apt: + packages: + - *linux_deps + + - env: PLATFORM=android TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="werror=yes" os: linux - compiler: gcc - - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-clang-tools - os: osx - osx_image: xcode9.3 compiler: clang - - env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang + + - env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang os: osx - osx_image: xcode9.3 compiler: clang -addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-5.0 - packages: - - build-essential - - scons - - pkg-config - - libx11-dev - - libxcursor-dev - - libxi-dev - - libxinerama-dev - - libxrandr-dev - - libgl1-mesa-dev - - libglu1-mesa-dev - - libasound2-dev - - libfreetype6-dev - - # For cross-compiling to Windows. - #- binutils-mingw-w64-i686 - #- binutils-mingw-w64-x86-64 - #- gcc-mingw-w64-i686 - #- gcc-mingw-w64-x86-64 - #- g++-mingw-w64-i686 - #- g++-mingw-w64-x86-64 - #- mingw-w64 - - # For style checks. - - clang-format-5.0 + - env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang + os: osx + compiler: clang - 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: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc + os: linux + compiler: gcc + addons: + apt: + packages: + - *linux_deps before_install: - if [ "$STATIC_CHECKS" = "yes" ]; then unset SCONS_CACHE; - else - if [ "$TRAVIS_BRANCH" = "coverity_scan" ]; then - echo "This job runs in the Coverity Scan branch and is not the STATIC_CHECKS job meant for it, so aborting with exit code 0."; - travis_terminate 0; - fi; 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 @@ -112,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 $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 a5ef24e82546..2f109aff6eea 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) @@ -38,15 +39,20 @@ name is available. Ariel Manzur (punto-) Bastiaan Olij (BastiaanOlij) Ben Brookshire (sheepandshepherd) + Benjamin (Nallebeorn) Bernard Liebl (poke1024) - Bojidar Marinov (bojidar-bg) Błażej Szczygieł (zaps166) + Bojidar Marinov (bojidar-bg) + bruvzg Carl Olsson (not-surt) + Chris Bradfield (cbscribe) Dana Olson (adolson) Daniel J. Ramirez (djrm) Dmitry Koteroff (Krakean) - Дмитрий Сальников (DmitriySalnikov) + DualMatrix Emmanuel Leblond (touilleMan) + Eric Lasota (elasota) + est31 Fabio Alessandrelli (Faless) Ferenc Arn (tagcup) Franklin Sobrinho (TheHX) @@ -61,30 +67,40 @@ name is available. Hubert Jarosz (Marqin) Hugo Locurcio (Calinou) Ian Bishop (ianb96) + Ibrahn Sahir (ibrahn) Ignacio Etcheverry (neikeq) Indah Sylvia (ISylvox) J08nY Jakub Grzesik (kubecz3k) Jérôme GULLY (Nutriz) + Joan Fons Sanchez (JFonS) Johan Manuel (29jm) Joshua Grams (JoshuaGrams) Juan Linietsky (reduz) Julian Murgia (StraToN) Justo Delgado (mrcdk) + Kelly Thomas (KellyThomas) Kostadin Damyanov (Max-Might) + K. S. Ernest (iFire) Lee (fire) Leon Krause (eska014) - Marc Gilleron (Zylann) + m4nu3lf Marcelo Fernandez (marcelofg55) + Marc Gilleron (Zylann) + Marcin Zawiejski (dragmz) Mariano Javier Suligoy (MarianoGnu) Mario Schlack (hurikhan) + Martin Capitanio (capnm) Martin Sjursen (binbitten) + marynate Masoud BH (masoudbh3) Matthias Hölzl (hoelzl) Max Hilbrunner (mhilbrunner) Michael Alexsander Silva Dias (YeldhamDev) + mrezai Nathan Warden (NathanWarden) Nuno Donato (nunodonato) Ovnuniarchos + Pascal Richter (ShyRed) Patrick (firefly2442) Paul Batty (Paulb23) Paul Joannon (paulloz) @@ -97,28 +113,25 @@ name is available. Ramesh Ravone (RameshRavone) Ray Koopa (RayKoopa) Rémi Verschelde (akien-mga) + Rhody Lugo (rraallvv) + Roberto F. Arroyo (robfram) + romulox-x Ruslan Mustakov (endragor) Saniko (sanikoyes) SaracenOne + sersoong Theo Hallenius (TheoXD) Thomas Herzog (karroffel) Timo (toger5) - V. Vamsi Krishna (vkbsb) Vinzenz Feenstra (vinzenz) + 박한얼 (volzhs) + V. Vamsi Krishna (vkbsb) Wilhem Barbier (nounoursheureux) Will Nations (willnationsdev) Wilson E. Alvarez (Rubonnek) + Xavier Cho (mysticfall) + yg2f (SuperUserNameMan) Yuri Roubinski (Chaosus) Zher Huei Lee (leezh) ZuBsPaCe - 박한얼 (volzhs) - bruvzg - est31 - m4nu3lf - marynate - mrezai - robfram - romulox-x - rraallvv - sersoong - yg2f (SuperUserNameMan) + Дмитрий Сальников (DmitriySalnikov) diff --git a/CHANGELOG.md b/CHANGELOG.md index 533968886ef5..6a0310f904b9 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/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000000..27315f2c9d20 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,37 @@ +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners. +# Owners can be @users, @org/teams or emails + +core/* @reduz + +doc/* @godotengine/documentation + +drivers/gles2/* @karroffel +drivers/gles3/* @reduz + +editor/icons/* @djrm + +main/* @reduz + +misc/* @akien-mga + +modules/bullet/* @AndreaCatania +modules/enet/* @godotengine/network +modules/gdnative/* @karroffel +modules/gdscript/* @reduz @vnen @bojidar-bg +modules/mbedtls/* @godotengine/network +modules/mobile_vr/* @BastiaanOlij +modules/mono/* @neikeq +modules/regex/* @LeeZH +modules/upnp/* @godotengine/network +modules/websocket/* @godotengine/network + +platform/javascript/* @eska014 +platform/uwp/* @vnen + +scene/main/* @reduz + +server/physics* @reduz @AndreaCatania +server/visual* @reduz @karroffel + +thirdparty/* @akien-mga diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 68ec20a52577..a8e4650710d0 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 6b6ee4c50960..894cd1a53a99 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 @@ -135,6 +141,12 @@ Comment: CA certificates Copyright: Mozilla Contributors License: MPL-2.0 +Files: ./thirdparty/cvtt/ +Comment: Convection Texture Tools Stand-Alone Kernels +Copyright: 2018, Eric Lasota + 2018, Microsoft Corp. +License: Expat + Files: ./thirdparty/enet/ Comment: ENet Copyright: 2002-2016, Lee Salzman @@ -185,7 +197,7 @@ Files: ./thirdparty/libpng/ Comment: libpng Copyright: 1995-1996, Guy Eric Schalnat, Group 42, Inc. 1996-1997, Andreas Dilger - 1998-2016, Glenn Randers-Pehrson + 1998-2018, Glenn Randers-Pehrson License: Zlib Files: ./thirdparty/libsimplewebm/ @@ -218,11 +230,21 @@ Comment: WebP codec Copyright: 2010, Google Inc. License: BSD-3-clause +Files: ./thirdparty/libwebsockets/ +Comment: libwebsockets +Copyright: 2010-2017, Andy Green +License: LGPL-2.1+SLE (libwebsockets) + Files: ./thirdparty/mbedtls/ Comment: Mbed TLS Copyright: 2006-2015, ARM Limited License: Apache-2.0 +Files: ./thirdparty/miniupnpc/ +Comment: MiniUPnPc +Copyright: 2005-2016, Thomas Bernard +License: BSD-3-clause + Files: ./thirdparty/minizip/ Comment: MiniZip Copyright: 1998-2010, Gilles Vollant @@ -244,12 +266,23 @@ Comment: BASE64 conversion methods Copyright: Ari Edelkind License: public-domain +Files: ./thirdparty/misc/clipper.cpp + ./thirdparty/misc/clipper.hpp +Comment: Clipper +Copyright: 2010-2017, Angus Johnson +License: BSL-1.0 + Files: ./thirdparty/misc/curl_hostcheck.c ./thirdparty/misc/curl_hostcheck.h 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 @@ -351,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 @@ -361,7 +394,7 @@ License: Zlib Files: ./thirdparty/zstd/ Comment: Zstandard Copyright: 2016-2018, Facebook, Inc. -License: BSD-3-Clause +License: BSD-3-clause @@ -378,30 +411,6 @@ License: Apache-2.0 See the License for the specific language governing permissions and limitations under the License. -License: BSD-2-clause - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - . - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - . - 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. Neither the name of the author - 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. - License: Bitstream Vera Fonts Copyright Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. @@ -444,6 +453,28 @@ License: Bitstream Vera Fonts Copyright authorization from the GNOME Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. +License: BSD-2-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + . + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + . + * 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. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -472,6 +503,31 @@ License: BSD-3-clause OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +License: BSL-1.0 + Boost Software License - Version 1.0 - August 17th, 2003 + . + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, + must be included in all copies of the Software, in whole or in part, and + all derivative works of the Software, unless such copies or derivative + works are solely in the form of machine-executable object code generated by + a source language processor. + . + 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + License: CC-BY-3.0 Creative Commons Attribution 3.0 Unported . @@ -1022,6 +1078,564 @@ License: ISC ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +License: LGPL-2.1+SLE (libwebsockets) + Libwebsockets and included programs are provided under the terms of the GNU + Library General Public License (LGPL) 2.1, with the following exceptions: + . + 1) Any reference, whether in these modifications or in the GNU + Library General Public License 2.1, to this License, these terms, the + GNU Lesser Public License, GNU Library General Public License, LGPL, or + any similar reference shall refer to the GNU Library General Public + License 2.1 as modified by these paragraphs 1) through 4). + . + 2) Static linking of programs with the libwebsockets library does not + constitute a derivative work and does not require the author to provide + source code for the program, use the shared libwebsockets libraries, or + link their program against a user-supplied version of libwebsockets. + . + If you link the program to a modified version of libwebsockets, then the + changes to libwebsockets must be provided under the terms of the LGPL in + sections 1, 2, and 4. + . + 3) You do not have to provide a copy of the libwebsockets license with + programs that are linked to the libwebsockets library, nor do you have to + identify the libwebsockets license in your program or documentation as + required by section 6 of the LGPL. + . + However, programs must still identify their use of libwebsockets. The + following example statement can be included in user documentation to + satisfy this requirement: + . + "[program] is based in part on the work of the libwebsockets project + (https://libwebsockets.org)" + . + 4) Some sources included have their own, more liberal licenses, or options + to get original sources with the liberal terms. + . + Original liberal license retained + . + - lib/misc/sha-1.c - 3-clause BSD license retained, link to original + - win32port/zlib - ZLIB license (see zlib.h) + - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls) + . + Relicensed to libwebsocket license + . + - lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original + - lib/misc/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE, + link to original Public Domain version + . + Public Domain (CC-zero) to simplify reuse + . + - test-apps/*.c + - test-apps/*.h + - minimal-examples/* + - lwsws/* + . + ------ end of exceptions + . + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + . + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + . + [This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + . + Preamble + . + The licenses for most software are designed to take away your + freedom to share and change it. By contrast, the GNU General Public + Licenses are intended to guarantee your freedom to share and change + free software--to make sure the software is free for all its users. + . + This license, the Lesser General Public License, applies to some + specially designated software packages--typically libraries--of the + Free Software Foundation and other authors who decide to use it. You + can use it too, but we suggest you first think carefully about whether + this license or the ordinary General Public License is the better + strategy to use in any particular case, based on the explanations below. + . + When we speak of free software, we are referring to freedom of use, + not price. Our General Public Licenses are designed to make sure that + you have the freedom to distribute copies of free software (and charge + for this service if you wish); that you receive source code or can get + it if you want it; that you can change the software and use pieces of + it in new free programs; and that you are informed that you can do + these things. + . + To protect your rights, we need to make restrictions that forbid + distributors to deny you these rights or to ask you to surrender these + rights. These restrictions translate to certain responsibilities for + you if you distribute copies of the library or if you modify it. + . + For example, if you distribute copies of the library, whether gratis + or for a fee, you must give the recipients all the rights that we gave + you. You must make sure that they, too, receive or can get the source + code. If you link other code with the library, you must provide + complete object files to the recipients, so that they can relink them + with the library after making changes to the library and recompiling + it. And you must show them these terms so they know their rights. + . + We protect your rights with a two-step method: (1) we copyright the + library, and (2) we offer you this license, which gives you legal + permission to copy, distribute and/or modify the library. + . + To protect each distributor, we want to make it very clear that + there is no warranty for the free library. Also, if the library is + modified by someone else and passed on, the recipients should know + that what they have is not the original version, so that the original + author's reputation will not be affected by problems that might be + introduced by others. + . + Finally, software patents pose a constant threat to the existence of + any free program. We wish to make sure that a company cannot + effectively restrict the users of a free program by obtaining a + restrictive license from a patent holder. Therefore, we insist that + any patent license obtained for a version of the library must be + consistent with the full freedom of use specified in this license. + . + Most GNU software, including some libraries, is covered by the + ordinary GNU General Public License. This license, the GNU Lesser + General Public License, applies to certain designated libraries, and + is quite different from the ordinary General Public License. We use + this license for certain libraries in order to permit linking those + libraries into non-free programs. + . + When a program is linked with a library, whether statically or using + a shared library, the combination of the two is legally speaking a + combined work, a derivative of the original library. The ordinary + General Public License therefore permits such linking only if the + entire combination fits its criteria of freedom. The Lesser General + Public License permits more lax criteria for linking other code with + the library. + . + We call this license the "Lesser" General Public License because it + does Less to protect the user's freedom than the ordinary General + Public License. It also provides other free software developers Less + of an advantage over competing non-free programs. These disadvantages + are the reason we use the ordinary General Public License for many + libraries. However, the Lesser license provides advantages in certain + special circumstances. + . + For example, on rare occasions, there may be a special need to + encourage the widest possible use of a certain library, so that it becomes + a de-facto standard. To achieve this, non-free programs must be + allowed to use the library. A more frequent case is that a free + library does the same job as widely used non-free libraries. In this + case, there is little to gain by limiting the free library to free + software only, so we use the Lesser General Public License. + . + In other cases, permission to use a particular library in non-free + programs enables a greater number of people to use a large body of + free software. For example, permission to use the GNU C Library in + non-free programs enables many more people to use the whole GNU + operating system, as well as its variant, the GNU/Linux operating + system. + . + Although the Lesser General Public License is Less protective of the + users' freedom, it does ensure that the user of a program that is + linked with the Library has the freedom and the wherewithal to run + that program using a modified version of the Library. + . + The precise terms and conditions for copying, distribution and + modification follow. Pay close attention to the difference between a + "work based on the library" and a "work that uses the library". The + former contains code derived from the library, whereas the latter must + be combined with the library in order to run. + . + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + . + 0. This License Agreement applies to any software library or other + program which contains a notice placed by the copyright holder or + other authorized party saying it may be distributed under the terms of + this Lesser General Public License (also called "this License"). + Each licensee is addressed as "you". + . + A "library" means a collection of software functions and/or data + prepared so as to be conveniently linked with application programs + (which use some of those functions and data) to form executables. + . + The "Library", below, refers to any such software library or work + which has been distributed under these terms. A "work based on the + Library" means either the Library or any derivative work under + copyright law: that is to say, a work containing the Library or a + portion of it, either verbatim or with modifications and/or translated + straightforwardly into another language. (Hereinafter, translation is + included without limitation in the term "modification".) + . + "Source code" for a work means the preferred form of the work for + making modifications to it. For a library, complete source code means + all the source code for all modules it contains, plus any associated + interface definition files, plus the scripts used to control compilation + and installation of the library. + . + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of + running a program using the Library is not restricted, and output from + such a program is covered only if its contents constitute a work based + on the Library (independent of the use of the Library in a tool for + writing it). Whether that is true depends on what the Library does + and what the program that uses the Library does. + . + 1. You may copy and distribute verbatim copies of the Library's + complete source code as you receive it, in any medium, provided that + you conspicuously and appropriately publish on each copy an + appropriate copyright notice and disclaimer of warranty; keep intact + all the notices that refer to this License and to the absence of any + warranty; and distribute a copy of this License along with the + Library. + . + You may charge a fee for the physical act of transferring a copy, + and you may at your option offer warranty protection in exchange for a + fee. + . + 2. You may modify your copy or copies of the Library or any portion + of it, thus forming a work based on the Library, and copy and + distribute such modifications or work under the terms of Section 1 + above, provided that you also meet all of these conditions: + . + a) The modified work must itself be a software library. + . + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + . + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + . + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + . + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + . + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Library, + and can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based + on the Library, the distribution of the whole must be on the terms of + this License, whose permissions for other licensees extend to the + entire whole, and thus to each and every part regardless of who wrote + it. + . + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Library. + . + In addition, mere aggregation of another work not based on the Library + with the Library (or with a work based on the Library) on a volume of + a storage or distribution medium does not bring the other work under + the scope of this License. + . + 3. You may opt to apply the terms of the ordinary GNU General Public + License instead of this License to a given copy of the Library. To do + this, you must alter all the notices that refer to this License, so + that they refer to the ordinary GNU General Public License, version 2, + instead of to this License. (If a newer version than version 2 of the + ordinary GNU General Public License has appeared, then you can specify + that version instead if you wish.) Do not make any other change in + these notices. + . + Once this change is made in a given copy, it is irreversible for + that copy, so the ordinary GNU General Public License applies to all + subsequent copies and derivative works made from that copy. + . + This option is useful when you wish to copy part of the code of + the Library into a program that is not a library. + . + 4. You may copy and distribute the Library (or a portion or + derivative of it, under Section 2) in object code or executable form + under the terms of Sections 1 and 2 above provided that you accompany + it with the complete corresponding machine-readable source code, which + must be distributed under the terms of Sections 1 and 2 above on a + medium customarily used for software interchange. + . + If distribution of object code is made by offering access to copy + from a designated place, then offering equivalent access to copy the + source code from the same place satisfies the requirement to + distribute the source code, even though third parties are not + compelled to copy the source along with the object code. + . + 5. A program that contains no derivative of any portion of the + Library, but is designed to work with the Library by being compiled or + linked with it, is called a "work that uses the Library". Such a + work, in isolation, is not a derivative work of the Library, and + therefore falls outside the scope of this License. + . + However, linking a "work that uses the Library" with the Library + creates an executable that is a derivative of the Library (because it + contains portions of the Library), rather than a "work that uses the + library". The executable is therefore covered by this License. + Section 6 states terms for distribution of such executables. + . + When a "work that uses the Library" uses material from a header file + that is part of the Library, the object code for the work may be a + derivative work of the Library even though the source code is not. + Whether this is true is especially significant if the work can be + linked without the Library, or if the work is itself a library. The + threshold for this to be true is not precisely defined by law. + . + If such an object file uses only numerical parameters, data + structure layouts and accessors, and small macros and small inline + functions (ten lines or less in length), then the use of the object + file is unrestricted, regardless of whether it is legally a derivative + work. (Executables containing this object code plus portions of the + Library will still fall under Section 6.) + . + Otherwise, if the work is a derivative of the Library, you may + distribute the object code for the work under the terms of Section 6. + Any executables containing that work also fall under Section 6, + whether or not they are linked directly with the Library itself. + . + 6. As an exception to the Sections above, you may also combine or + link a "work that uses the Library" with the Library to produce a + work containing portions of the Library, and distribute that work + under terms of your choice, provided that the terms permit + modification of the work for the customer's own use and reverse + engineering for debugging such modifications. + . + You must give prominent notice with each copy of the work that the + Library is used in it and that the Library and its use are covered by + this License. You must supply a copy of this License. If the work + during execution displays copyright notices, you must include the + copyright notice for the Library among them, as well as a reference + directing the user to the copy of this License. Also, you must do one + of these things: + . + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + . + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + . + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + . + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + . + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + . + For an executable, the required form of the "work that uses the + Library" must include any data and utility programs needed for + reproducing the executable from it. However, as a special exception, + the materials to be distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on + which the executable runs, unless that component itself accompanies + the executable. + . + It may happen that this requirement contradicts the license + restrictions of other proprietary libraries that do not normally + accompany the operating system. Such a contradiction means you cannot + use both them and the Library together in an executable that you + distribute. + . + 7. You may place library facilities that are a work based on the + Library side-by-side in a single library together with other library + facilities not covered by this License, and distribute such a combined + library, provided that the separate distribution of the work based on + the Library and of the other library facilities is otherwise + permitted, and provided that you do these two things: + . + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + . + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + . + 8. You may not copy, modify, sublicense, link with, or distribute + the Library except as expressly provided under this License. Any + attempt otherwise to copy, modify, sublicense, link with, or + distribute the Library is void, and will automatically terminate your + rights under this License. However, parties who have received copies, + or rights, from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + . + 9. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Library or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Library (or any work based on the + Library), you indicate your acceptance of this License to do so, and + all its terms and conditions for copying, distributing or modifying + the Library or works based on it. + . + 10. Each time you redistribute the Library (or any work based on the + Library), the recipient automatically receives a license from the + original licensor to copy, distribute, link with or modify the Library + subject to these terms and conditions. You may not impose any further + restrictions on the recipients' exercise of the rights granted herein. + You are not responsible for enforcing compliance by third parties with + this License. + . + 11. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot + distribute so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you + may not distribute the Library at all. For example, if a patent + license would not permit royalty-free redistribution of the Library by + all those who receive copies directly or indirectly through you, then + the only way you could satisfy both it and this License would be to + refrain entirely from distribution of the Library. + . + If any portion of this section is held invalid or unenforceable under any + particular circumstance, the balance of the section is intended to apply, + and the section as a whole is intended to apply in other circumstances. + . + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system which is + implemented by public license practices. Many people have made + generous contributions to the wide range of software distributed + through that system in reliance on consistent application of that + system; it is up to the author/donor to decide if he or she is willing + to distribute software through any other system and a licensee cannot + impose that choice. + . + This section is intended to make thoroughly clear what is believed to + be a consequence of the rest of this License. + . + 12. If the distribution and/or use of the Library is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Library under this License may add + an explicit geographical distribution limitation excluding those countries, + so that distribution is permitted only in or among countries not thus + excluded. In such case, this License incorporates the limitation as if + written in the body of this License. + . + 13. The Free Software Foundation may publish revised and/or new + versions of the Lesser General Public License from time to time. + Such new versions will be similar in spirit to the present version, + but may differ in detail to address new problems or concerns. + . + Each version is given a distinguishing version number. If the Library + specifies a version number of this License which applies to it and + "any later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Library does not specify a + license version number, you may choose any version ever published by + the Free Software Foundation. + . + 14. If you wish to incorporate parts of the Library into other free + programs whose distribution conditions are incompatible with these, + write to the author to ask for permission. For software which is + copyrighted by the Free Software Foundation, write to the Free + Software Foundation; we sometimes make exceptions for this. Our + decision will be guided by the two goals of preserving the free status + of all derivatives of our free software and of promoting the sharing + and reuse of software generally. + . + NO WARRANTY + . + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE + LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME + THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + . + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY + AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU + FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE + LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING + RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A + FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF + SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. + . + END OF TERMS AND CONDITIONS + . + How to Apply These Terms to Your New Libraries + . + If you develop a new library, and you want it to be of the greatest + possible use to the public, we recommend making it free software that + everyone can redistribute and change. You can do so by permitting + redistribution under these terms (or, alternatively, under the terms of the + ordinary General Public License). + . + To apply these terms, attach the following notices to the library. It is + safest to attach them to the start of each source file to most effectively + convey the exclusion of warranty; and each file should have at least the + "copyright" line and a pointer to where the full notice is found. + . + + Copyright (C) + . + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + . + Also add information on how to contact you by electronic and paper mail. + . + You should also get your employer (if you work as a programmer) or your + school, if any, to sign a "copyright disclaimer" for the library, if + necessary. Here is a sample; alter the names: + . + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + . + , 1 April 1990 + Ty Coon, President of Vice + . + That's all there is to it! + License: MPL-2.0 Mozilla Public License Version 2.0 ================================== diff --git a/DONORS.md b/DONORS.md index 1a129e36d72a..2c9bb1eb73f6 100644 --- a/DONORS.md +++ b/DONORS.md @@ -17,258 +17,278 @@ generous deed immortalized in the next stable release of Godot Engine. ## Gold sponsors Gamblify - GameDev.TV - Skirmish + GameDev.TV + Image Campus ## Mini sponsors - Andreas + Andrew Dunai Brandon Lamb Christian Uldall Pedersen - Christopher Igoe Christoph Woinke - codetrotter - E Hewert + David Graham + GameDev.net Hein-Pieter van Braam - Igors Vaitkus + iDev.Network Studios Jamal Alyafei - Jay Sistar + Leona Eden Matthieu Huvé Mike King Nathan Warden Neal Gompa (Conan Kudo) - Pascal Julien + Patrick Aarstad Ruslan Mustakov - Sébastien Manin Slobodan Milnovic Stephan Lanfermann Stoney Meyerhoeffer + thechris Thomas Mathews + VilliHaukka ## Gold donors - 3Dexplorer - Alexander Otto Asdf cheese65536 - Jake Bo - Kris Michael + K9Kraken Manuele Finocchiaro + Nathanael Beisiegel Officine Pixel S.n.c. - Rémi Verschelde + Retro Village Zaven Muradyan + 13MHz Allen Schade Andreas Schüle Austen McRae - Benjamin Botwin - Bernhard Liebl - Catalin Moldovan - DeepSquid + Daniel Lynn + David Gehrig Florian Breisch Gary Oberbrunner + Jay Horton Johannes Wuensch - Josep G. Camarasa Joshua Lesperance - Libre-Dépanne - Matthew Bennett + Krzysztof Dluzniewski + Kyle Szklenski + Mohammad Taleb + Moonwards + Paul LaMotte Ranoller - Rob Messick + Sergey Svenne Krap - Timothy Hagberg + Xananax BanjoNode2D - Brandon + Beliar Chris Serino + Christian Padilla Conrad Curry Craig Smith + Daniel Egger David Churchill Dean Harmon - Dexter Miguel - Garrett Dockins - Guilherme Felipe de C. G. da Silva - Harman Bains John Justo Delgado Baudí - Karsten Bock + KTL Laurence Bannister + Markus Wiesner + paul gruenbacher Rami Robert Willes Robin Arys - Rufus Sasparilla + Ronnie Ashlock ScottMakesGames - Testus Maximus Thomas Bjarnelöf Wojciech Chojnacki - Xavier Tan + Xavier PATRICELLI Zaq Poi Alessandra Pereyra Alexey Dyadchenko Amanda Haldy + Chau Siu Hung Chris Brown - Chris Wilson + Chris Petrich Cody Parker D - Daniel Eliasinski + Deadly Lampshade + E.G. + Eric Eric Monson + Ethan Bennis + Fidget Sinner flesk - François Cantin G Barnes + Gero GGGames.org + Giovanni Solimeno + Guilherme Felipe de C. G. da Silva + Hasen Judy Heath Hayes Jeppe Zapp - Jeremi Biernacki joe513 - John O'Mahoney - Jordan M Lucas Juraj Móza Justin Arnold Leandro Voltolino - Lisandro Lorea - Marco Andrew Cafolla - Markus Wiesner + Marius Kamm Marvin + Nahuel Sacchetti Nick Nikitin Pablo Cholaky - Patrick Schnorbus Pete Goodwin - Phyronnaz - Simon De Greve + ray-tracer + Ruben Soares Luis Sofox - Ted - Travis Womack + Stoned Xander Trent McPheron - Vladimir + WytRabbit + Zachariah Gibbons ## Silver donors 1D_Inc Adam Carr Adam Smeltzer + Adisibio Alder Stefano Alessandro Senese - Álvaro Domínguez López - Andrea Badii - Andrew Thomas + Alexander Koppe + Anders Jensen-Urstad Anthony Bongiovanni Arda Erol Arthur S. Muszynski + Artur Barichello Aubrey Falconer Avencherus + Bailey Bastian Böhm Benedikt - Benjamin Beshara Ben Vercammen Blair Allen - Bryanna M + Brandon Bryan Stevenson + Carl Winder Carwyn Edwards Casey Foote Chris Chapin Christian Baune Christian Winter Collin Shooltz - Daniel Egger + Daniel Johnson Daniel Kaplan - Daniel Langegger - Daniel Mircea + DanielMaximiano David David Cravens David May + Disktra Dominik Wetzel + Duy Kevin Nguyen + Edward Herbert + Elias Nykrem Eric Martini + Eric Williams + Eugenio Hugo Salgüero Jáñez Fabian Becker fengjiongmax - Francesco Lisi - Frédéric Alix + Foomf G3Dev sàrl - Geequlim + George Lesica Gerrit Großkopf - Gerrit Procee Gilberto K. Otubo + Greg Olson Guldoman Heribert Hirth - hubert jenkins Hunter Jones ialex32x - Ivan Vodopiviz + Jahn Johansen Jaime Ruiz-Borau Vizárraga - Jed + Jako Danar Jeff Hungerford Joel Fivat Johan Lindberg + Jonas Rudlang Jonas Yamazaki Jonathan Martin Jonathan Nieto - Jonathon + Jon Bonazza + Jon Sully + Jose Malheiro Josh 'Cheeseness' Bush + Juanfran Juan Negrier Judd - JuDelCo Julian Murgia Justin Luk KC Chan Kevin Boyer Kevin Kamper Meejach Petersen Klavdij Voncina - Lars pfeffer + Krzysztof Jankowski + Krzysztof Sierszecki + KsyTek Games Linus Lind Lundgren + Loreshaper Games + Luis Moraes Macil magodev + Malcolm Martin Eigel - Matthew Fitzpatrick - Matthias Hölzl + Matt Welke Max R.R. Collada - memoryruins + Maxwell + Mertcan Mermerkaya mhilbrunner Michael Dürwald Michael Gringauz Michael Labbe Mikael Olsson MoM - monokrome Moritz Laass + Moritz Weissenberger + Natrim nee Neil Blakey-Milner + Nick Pavlica Niclas Eriksen - Nik Lee - Niko Leopold - nivardus + Nicolás Montaña + Nicolas SAN AGUSTIN + Nik Noi Sek - Oleg Tyshchenko - Pablo Seibelt Pan Ip - Pat LaBine + Pascal Grüter Patrick Nafarrete - Patric Vormstein + 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 Roger Smith Roman Tinkov - Ryan Whited Sasori Olkof - Scott D. Yelich Sootstone Theo Cranmore Thibault Barbaroux Thomas Bell - Thomas Herzog & Xananax + Thomas Holmes Thomas Kurz + tiansheng li + Tim Tom Larrow + Trym Nilsen + Tyggve Sollid Tyler Stafos UltyX - Victor Gonzalez Fernandez - Victor Holt + Vaiktorg + Victor Viktor Ferenczi - werner mendizabal + waka nya Wout Standaert - Yu He ## Bronze donors diff --git a/README.md b/README.md index 8ad738c23d90..06380b5e0ab9 100644 --- a/README.md +++ b/README.md @@ -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 63105bfa8443..628aac42afb3 100644 --- a/SConstruct +++ b/SConstruct @@ -2,16 +2,14 @@ EnsureSConsVersion(0, 98, 1) - import string import os import os.path import glob import sys import methods - -# moved below to compensate with module version string -# methods.update_version() +import gles_builders +from platform_methods import run_in_subprocess # scan possible build platforms @@ -25,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 @@ -53,9 +51,6 @@ for x in glob.glob("platform/*"): module_list = methods.detect_modules() - -# print "Detected Platforms: "+str(platform_list) - methods.save_active_platforms(active_platforms, active_platform_ids) custom_tools = ['default'] @@ -81,6 +76,7 @@ env_base.android_gradle_plugins = [] env_base.android_gradle_classpath = [] env_base.android_java_dirs = [] env_base.android_res_dirs = [] +env_base.android_asset_dirs = [] env_base.android_aidl_dirs = [] env_base.android_jni_dirs = [] env_base.android_default_config = [] @@ -101,12 +97,12 @@ env_base.Decider('MD5-timestamp') # http://scons.org/doc/production/HTML/scons-user/ch06s04.html env_base.SetOption('implicit_cache', 1) - env_base.__class__.android_add_maven_repository = methods.android_add_maven_repository env_base.__class__.android_add_flat_dir = methods.android_add_flat_dir env_base.__class__.android_add_dependency = methods.android_add_dependency env_base.__class__.android_add_java_dir = methods.android_add_java_dir env_base.__class__.android_add_res_dir = methods.android_add_res_dir +env_base.__class__.android_add_asset_dir = methods.android_add_asset_dir env_base.__class__.android_add_aidl_dir = methods.android_add_aidl_dir env_base.__class__.android_add_jni_dir = methods.android_add_jni_dir env_base.__class__.android_add_default_config = methods.android_add_default_config @@ -126,6 +122,8 @@ env_base.__class__.split_lib = methods.split_lib 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 @@ -145,52 +143,57 @@ if profile: opts = Variables(customs, ARGUMENTS) # Target build options -opts.Add('arch', "Platform-dependent architecture (arm/arm64/x86/x64/mips/etc)", '') +opts.Add('arch', "Platform-dependent architecture (arm/arm64/x86/x64/mips/...)", '') opts.Add(EnumVariable('bits', "Target platform bits", 'default', ('default', '32', '64'))) opts.Add('p', "Platform (alias for 'platform')", '') opts.Add('platform', "Target platform (%s)" % ('|'.join(platform_list), ), '') opts.Add(EnumVariable('target', "Compilation target", 'debug', ('debug', 'release_debug', 'release'))) -opts.Add(BoolVariable('tools', "Build the tools a.k.a. the Godot editor", True)) -opts.Add(BoolVariable('use_lto', 'Use linking time optimization', False)) +opts.Add(EnumVariable('optimize', "Optimization type", 'speed', ('speed', 'size'))) +opts.Add(BoolVariable('tools', "Build the tools (a.k.a. the Godot editor)", True)) +opts.Add(BoolVariable('use_lto', 'Use link-time optimization', False)) # Components opts.Add(BoolVariable('deprecated', "Enable deprecated features", True)) -opts.Add(BoolVariable('gdscript', "Build GDSCript support", True)) -opts.Add(BoolVariable('minizip', "Build minizip archive support", True)) -opts.Add(BoolVariable('xaudio2', "XAudio2 audio driver", False)) -opts.Add(BoolVariable('xml', "XML format support for resources", 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)) # Advanced options -opts.Add(BoolVariable('disable_3d', "Disable 3D nodes for 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('unix_global_settings_path', "UNIX-specific path to system-wide settings. Currently only used for templates", '') opts.Add(BoolVariable('verbose', "Enable verbose output for the compilation", False)) -opts.Add(BoolVariable('vsproj', "Generate Visual Studio Project", 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 build", True)) +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(EnumVariable('macports_clang', "Build using clang from MacPorts", 'no', ('no', '5.0', 'devel'))) +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 builtin bullet library", True)) -opts.Add(BoolVariable('builtin_enet', "Use the builtin enet library", True)) -opts.Add(BoolVariable('builtin_freetype', "Use the builtin freetype library", True)) -opts.Add(BoolVariable('builtin_libogg', "Use the builtin libogg library", True)) -opts.Add(BoolVariable('builtin_libpng', "Use the builtin libpng library", True)) -opts.Add(BoolVariable('builtin_libtheora', "Use the builtin libtheora library", True)) -opts.Add(BoolVariable('builtin_libvorbis', "Use the builtin libvorbis library", True)) -opts.Add(BoolVariable('builtin_libvpx', "Use the builtin libvpx library", True)) -opts.Add(BoolVariable('builtin_libwebp', "Use the builtin libwebp library", True)) -opts.Add(BoolVariable('builtin_mbedtls', "Use the builtin mbedTLS library", True)) -opts.Add(BoolVariable('builtin_opus', "Use the builtin opus library", True)) -opts.Add(BoolVariable('builtin_pcre2', "Use the builtin pcre2 library)", True)) -opts.Add(BoolVariable('builtin_recast', "Use the builtin recast library", True)) -opts.Add(BoolVariable('builtin_squish', "Use the builtin squish library", True)) -opts.Add(BoolVariable('builtin_thekla_atlas', "Use the builtin thekla_altas library", True)) -opts.Add(BoolVariable('builtin_zlib', "Use the builtin zlib library", True)) -opts.Add(BoolVariable('builtin_zstd', "Use the builtin zstd library", True)) -opts.Add(BoolVariable('no_editor_splash', "Don't use the custom splash screen for the editor", False)) +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)) +opts.Add(BoolVariable('builtin_libpng', "Use the built-in libpng library", True)) +opts.Add(BoolVariable('builtin_libtheora', "Use the built-in libtheora library", True)) +opts.Add(BoolVariable('builtin_libvorbis', "Use the built-in libvorbis library", True)) +opts.Add(BoolVariable('builtin_libvpx', "Use the built-in libvpx library", True)) +opts.Add(BoolVariable('builtin_libwebp', "Use the built-in libwebp library", True)) +opts.Add(BoolVariable('builtin_libwebsockets', "Use the built-in libwebsockets library", True)) +opts.Add(BoolVariable('builtin_mbedtls', "Use the built-in mbedTLS library", True)) +opts.Add(BoolVariable('builtin_miniupnpc', "Use the built-in miniupnpc library", True)) +opts.Add(BoolVariable('builtin_opus', "Use the built-in Opus library", True)) +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)) # Compilation environment setup opts.Add("CXX", "C++ compiler") @@ -201,7 +204,6 @@ opts.Add("CXXFLAGS", "Custom flags for the C++ compiler") opts.Add("CFLAGS", "Custom flags for the C compiler") opts.Add("LINKFLAGS", "Custom flags for the linker") - # add platform specific options for k in platform_opts.keys(): @@ -226,22 +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 -""" -sys.path.append("./platform/"+env_base["platform"]) -import detect -detect.configure(env_base) -sys.path.remove("./platform/"+env_base["platform"]) -sys.modules.pop('detect') -""" - 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']) @@ -251,7 +245,6 @@ if not env_base['deprecated']: env_base.platforms = {} - selected_platform = "" if env_base['platform'] != "": @@ -260,7 +253,6 @@ elif env_base['p'] != "": selected_platform = env_base['p'] env_base["platform"] = selected_platform - if selected_platform in platform_list: sys.path.append("./platform/" + selected_platform) @@ -326,35 +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']) - #env['platform_libsuffix'] = env['LIBSUFFIX'] + 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"]: @@ -396,7 +394,17 @@ if selected_platform in platform_list: sys.path.append(tmppath) env.current_module = x import config - if (config.can_build(selected_platform)): + # can_build changed number of arguments between 3.0 (1) and 3.1 (2), + # so try both to preserve compatibility for 3.0 modules + can_build = False + try: + can_build = config.can_build(env, selected_platform) + except TypeError: + print("Warning: module '%s' uses a deprecated `can_build` " + "signature in its config.py file, it should be " + "`can_build(env, platform)`." % x) + can_build = config.can_build(selected_platform) + if (can_build): config.configure(env) env.module_list.append(x) try: @@ -414,36 +422,41 @@ if selected_platform in platform_list: env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"] env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"] + # (SH)LIBSUFFIX will be used for our own built libraries + # LIBSUFFIXES contains LIBSUFFIX and SHLIBSUFFIX by default, + # so we need to append the default suffixes to keep the ability + # to link against thirdparty libraries (.a, .so, .dll, etc.). + env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]] env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"] env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"] if (env.use_ptrcall): env.Append(CPPDEFINES=['PTRCALL_ENABLED']) - - # to test 64 bits compiltion - # env.Append(CPPFLAGS=['-m64']) - if env['tools']: env.Append(CPPDEFINES=['TOOLS_ENABLED']) if env['disable_3d']: - env.Append(CPPDEFINES=['_3D_DISABLED']) + if env['tools']: + print("Build option 'disable_3d=yes' cannot be used with 'tools=yes' (editor), only with 'tools=no' (export template).") + sys.exit(255) + else: + env.Append(CPPDEFINES=['_3D_DISABLED']) if env['gdscript']: env.Append(CPPDEFINES=['GDSCRIPT_ENABLED']) if env['disable_advanced_gui']: - env.Append(CPPDEFINES=['ADVANCED_GUI_DISABLED']) - + if env['tools']: + print("Build option 'disable_advanced_gui=yes' cannot be used with 'tools=yes' (editor), only with 'tools=no' (export template).") + sys.exit(255) + else: + 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) if (not env["platform"] == "server"): # FIXME: detect GLES3 - env.Append( BUILDERS = { 'GLES3_GLSL' : env.Builder(action = methods.build_gles3_headers, suffix = 'glsl.gen.h', src_suffix = '.glsl') } ) - env.Append( BUILDERS = { 'GLES2_GLSL' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.gen.h', src_suffix = '.glsl') } ) + env.Append(BUILDERS = { 'GLES3_GLSL' : env.Builder(action=run_in_subprocess(gles_builders.build_gles3_headers), suffix='glsl.gen.h', src_suffix='.glsl')}) + env.Append(BUILDERS = { 'GLES2_GLSL' : env.Builder(action=run_in_subprocess(gles_builders.build_gles2_headers), suffix='glsl.gen.h', src_suffix='.glsl')}) scons_cache_path = os.environ.get("SCONS_CACHE") if scons_cache_path != None: @@ -482,11 +495,10 @@ if selected_platform in platform_list: else: print("No valid target platform selected.") - print("The following were detected:") + print("The following platforms were detected:") for x in platform_list: print("\t" + x) - print("\nPlease run scons again with argument: platform=") - + print("\nPlease run SCons again with the argument: platform=") # The following only makes sense when the env is defined, and assumes it is if 'env' in locals(): @@ -535,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 @@ -560,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/compat.py b/compat.py index 32c685f5b813..de99eef9c285 100644 --- a/compat.py +++ b/compat.py @@ -16,6 +16,8 @@ def decode_utf8(x): return x def iteritems(d): return d.iteritems() + def itervalues(d): + return d.itervalues() def escape_string(s): if isinstance(s, unicode): s = s.encode('ascii') @@ -44,6 +46,8 @@ def decode_utf8(x): return codecs.utf_8_decode(x)[0] def iteritems(d): return iter(d.items()) + def itervalues(d): + return iter(d.values()) def charcode_to_c_escapes(c): rev_result = [] while c >= 256: diff --git a/core/SCsub b/core/SCsub index 383aaf0e127e..c6d0b7e5b170 100644 --- a/core/SCsub +++ b/core/SCsub @@ -2,8 +2,11 @@ Import('env') -env.core_sources = [] +import core_builders +import make_binders +from platform_methods import run_in_subprocess +env.core_sources = [] # Generate global defaults gd_call = "" @@ -14,12 +17,12 @@ 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" with open("global_defaults.gen.cpp", "w") as f: - f.write(gd_cpp) + f.write(gd_cpp) # Generate AES256 script encryption key @@ -46,63 +49,135 @@ if ("SCRIPT_AES256_ENCRYPTION_KEY" in os.environ): txt = "0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0" print("Invalid AES256 encryption key, not 64 bits hex: " + e) +# NOTE: It is safe to generate this file here, since this is still executed serially with open("script_encryption_key.gen.cpp", "w") as f: - f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") + f.write("#include \"core/project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") -# Add required thirdparty code. Header paths are hardcoded, we don't need to append +# Add required thirdparty code. +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() + +# 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 = [ - # C sources - "base64.c", - "fastlz.c", - "sha256.c", - "smaz.c", - - # C++ sources - "aes256.cpp", - "hq2x.cpp", - "md5.cpp", - "pcg.cpp", - "triangulator.cpp", +thirdparty_misc_dir = "#thirdparty/misc/" +thirdparty_misc_sources = [ + # C sources + "base64.c", + "fastlz.c", + "sha256.c", + "smaz.c", + + # C++ sources + "aes256.cpp", + "hq2x.cpp", + "md5.cpp", + "pcg.cpp", + "triangulator.cpp", ] -thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.core_sources, thirdparty_sources) - -# 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 = [ - "ioapi.c", - "unzip.c", - "zip.c", + "ioapi.c", + "unzip.c", + "zip.c", ] thirdparty_minizip_sources = [thirdparty_minizip_dir + file for file in thirdparty_minizip_sources] -env.add_source_files(env.core_sources, thirdparty_minizip_sources) - -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 -import make_binders -env.Command(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', make_binders.run) +env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) + +# Authors +env.Depends('#core/authors.gen.h', "../AUTHORS.md") +env.CommandNoCache('#core/authors.gen.h', "../AUTHORS.md", run_in_subprocess(core_builders.make_authors_header)) + +# Donors +env.Depends('#core/donors.gen.h', "../DONORS.md") +env.CommandNoCache('#core/donors.gen.h', "../DONORS.md", run_in_subprocess(core_builders.make_donors_header)) +# License +env.Depends('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"]) +env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], run_in_subprocess(core_builders.make_license_header)) # Chain load SCsubs 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 e17ab298d638..840b117958b4 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 class BalloonAllocator { diff --git a/core/array.cpp b/core/array.cpp index 9e3250fd4730..970845285068 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: @@ -72,7 +72,7 @@ void Array::_unref() const { Variant &Array::operator[](int p_idx) { - return _p->array[p_idx]; + return _p->array.write[p_idx]; } const Variant &Array::operator[](int p_idx) const { @@ -259,7 +259,7 @@ Array &Array::sort_custom(Object *p_obj, const StringName &p_function) { ERR_FAIL_NULL_V(p_obj, *this); - SortArray avs; + SortArray avs; avs.compare.obj = p_obj; avs.compare.func = p_function; avs.sort(_p->array.ptrw(), _p->array.size()); @@ -355,11 +355,58 @@ Variant Array::pop_front() { return Variant(); } +Variant Array::min() const { + + Variant minval; + for (int i = 0; i < size(); i++) { + if (i == 0) { + minval = get(i); + } else { + bool valid; + Variant ret; + Variant test = get(i); + Variant::evaluate(Variant::OP_LESS, test, minval, ret, valid); + if (!valid) { + return Variant(); //not a valid comparison + } + if (bool(ret)) { + //is less + minval = test; + } + } + } + return minval; +} + +Variant Array::max() const { + + Variant maxval; + for (int i = 0; i < size(); i++) { + if (i == 0) { + maxval = get(i); + } else { + bool valid; + Variant ret; + Variant test = get(i); + Variant::evaluate(Variant::OP_GREATER, test, maxval, ret, valid); + if (!valid) { + return Variant(); //not a valid comparison + } + if (bool(ret)) { + //is less + maxval = test; + } + } + } + return maxval; +} + Array::Array(const Array &p_from) { _p = NULL; _ref(p_from); } + Array::Array() { _p = memnew(ArrayPrivate); diff --git a/core/array.h b/core/array.h index e549a886e662..fa5113a37639 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; @@ -90,6 +91,9 @@ class Array { Array duplicate(bool p_deep = false) const; + Variant min() const; + Variant max() const; + Array(const Array &p_from); Array(); ~Array(); diff --git a/core/bind/SCsub b/core/bind/SCsub index 4efc90271768..1c5f954470dc 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 a51f2397c6cf..0032c4317985 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" @@ -112,11 +112,22 @@ PoolStringArray _ResourceLoader::get_dependencies(const String &p_path) { return ret; }; +#ifndef DISABLE_DEPRECATED bool _ResourceLoader::has(const String &p_path) { + WARN_PRINTS("ResourceLoader.has() is deprecated, please replace it with the equivalent has_cached() or the new exists()."); + return has_cached(p_path); +} +#endif // DISABLE_DEPRECATED + +bool _ResourceLoader::has_cached(const String &p_path) { String local_path = ProjectSettings::get_singleton()->localize_path(p_path); return ResourceCache::has(local_path); -}; +} + +bool _ResourceLoader::exists(const String &p_path, const String &p_type_hint) { + return ResourceLoader::exists(p_path, p_type_hint); +} void _ResourceLoader::_bind_methods() { @@ -125,7 +136,11 @@ void _ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &_ResourceLoader::get_recognized_extensions_for_type); ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &_ResourceLoader::set_abort_on_missing_resources); ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies); + ClassDB::bind_method(D_METHOD("has_cached", "path"), &_ResourceLoader::has_cached); + ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &_ResourceLoader::exists, DEFVAL("")); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("has", "path"), &_ResourceLoader::has); +#endif // DISABLE_DEPRECATED } _ResourceLoader::_ResourceLoader() { @@ -165,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() { @@ -221,6 +237,18 @@ String _OS::get_audio_driver_name(int p_driver) const { return OS::get_singleton()->get_audio_driver_name(p_driver); } +PoolStringArray _OS::get_connected_midi_inputs() { + return OS::get_singleton()->get_connected_midi_inputs(); +} + +void _OS::open_midi_inputs() { + return OS::get_singleton()->open_midi_inputs(); +} + +void _OS::close_midi_inputs() { + return OS::get_singleton()->close_midi_inputs(); +} + void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) { OS::VideoMode vm; @@ -336,10 +364,23 @@ void _OS::set_borderless_window(bool p_borderless) { OS::get_singleton()->set_borderless_window(p_borderless); } +bool _OS::get_window_per_pixel_transparency_enabled() const { + return OS::get_singleton()->get_window_per_pixel_transparency_enabled(); +} + +void _OS::set_window_per_pixel_transparency_enabled(bool p_enabled) { + OS::get_singleton()->set_window_per_pixel_transparency_enabled(p_enabled); +} + bool _OS::get_borderless_window() const { return OS::get_singleton()->get_borderless_window(); } +void _OS::set_ime_active(const bool p_active) { + + return OS::get_singleton()->set_ime_active(p_active); +} + void _OS::set_ime_position(const Point2 &p_pos) { return OS::get_singleton()->set_ime_position(p_pos); @@ -391,7 +432,7 @@ Error _OS::shell_open(String p_uri) { int _OS::execute(const String &p_path, const Vector &p_arguments, bool p_blocking, Array p_output) { - OS::ProcessID pid; + OS::ProcessID pid = -2; List args; for (int i = 0; i < p_arguments.size(); i++) args.push_back(p_arguments[i]); @@ -404,6 +445,7 @@ int _OS::execute(const String &p_path, const Vector &p_arguments, bool p else return pid; } + Error _OS::kill(int p_pid) { return OS::get_singleton()->kill(p_pid); @@ -625,7 +667,7 @@ Dictionary _OS::get_time(bool utc) const { * * @return epoch calculated */ -uint64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { +int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { // Bunch of conversion constants static const unsigned int SECONDS_PER_MINUTE = 60; @@ -670,13 +712,18 @@ uint64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { // Calculate all the seconds from months past in this year uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY; - uint64_t SECONDS_FROM_YEARS_PAST = 0; - for (unsigned int iyear = EPOCH_YR; iyear < year; iyear++) { - - SECONDS_FROM_YEARS_PAST += YEARSIZE(iyear) * SECONDS_PER_DAY; + int64_t SECONDS_FROM_YEARS_PAST = 0; + if (year >= EPOCH_YR) { + for (unsigned int iyear = EPOCH_YR; iyear < year; iyear++) { + SECONDS_FROM_YEARS_PAST += YEARSIZE(iyear) * SECONDS_PER_DAY; + } + } else { + for (unsigned int iyear = EPOCH_YR - 1; iyear >= year; iyear--) { + SECONDS_FROM_YEARS_PAST -= YEARSIZE(iyear) * SECONDS_PER_DAY; + } } - uint64_t epoch = + int64_t epoch = second + minute * SECONDS_PER_MINUTE + hour * SECONDS_PER_HOUR + @@ -699,34 +746,36 @@ uint64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { * * @return dictionary of date and time values */ -Dictionary _OS::get_datetime_from_unix_time(uint64_t unix_time_val) const { - - // Just fail if unix time is negative (when interpreted as an int). - // This means the user passed in a negative value by accident - ERR_EXPLAIN("unix_time_val was really huge!" + itos(unix_time_val) + " You probably passed in a negative value!"); - ERR_FAIL_COND_V((int64_t)unix_time_val < 0, Dictionary()); +Dictionary _OS::get_datetime_from_unix_time(int64_t unix_time_val) const { OS::Date date; OS::Time time; - unsigned long dayclock, dayno; + long dayclock, dayno; int year = EPOCH_YR; - dayclock = (unsigned long)unix_time_val % SECS_DAY; - dayno = (unsigned long)unix_time_val / SECS_DAY; + if (unix_time_val >= 0) { + dayno = unix_time_val / SECS_DAY; + dayclock = unix_time_val % SECS_DAY; + /* day 0 was a thursday */ + date.weekday = static_cast((dayno + 4) % 7); + while (dayno >= YEARSIZE(year)) { + dayno -= YEARSIZE(year); + year++; + } + } else { + dayno = (unix_time_val - SECS_DAY + 1) / SECS_DAY; + dayclock = unix_time_val - dayno * SECS_DAY; + date.weekday = static_cast((dayno - 3) % 7 + 7); + do { + year--; + dayno += YEARSIZE(year); + } while (dayno < 0); + } time.sec = dayclock % 60; time.min = (dayclock % 3600) / 60; time.hour = dayclock / 3600; - - /* day 0 was a thursday */ - date.weekday = static_cast((dayno + 4) % 7); - - while (dayno >= YEARSIZE(year)) { - dayno -= YEARSIZE(year); - year++; - } - date.year = year; size_t imonth = 0; @@ -785,6 +834,11 @@ uint32_t _OS::get_ticks_msec() const { return OS::get_singleton()->get_ticks_msec(); } +uint64_t _OS::get_ticks_usec() const { + + return OS::get_singleton()->get_ticks_usec(); +} + uint32_t _OS::get_splash_tick_msec() const { return OS::get_singleton()->get_splash_tick_msec(); @@ -966,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 @@ -1039,6 +1098,9 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name); ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count); ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name); + ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs); + ClassDB::bind_method(D_METHOD("open_midi_inputs"), &_OS::open_midi_inputs); + ClassDB::bind_method(D_METHOD("close_midi_inputs"), &_OS::close_midi_inputs); ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count); ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen); @@ -1064,10 +1126,14 @@ 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); + ClassDB::bind_method(D_METHOD("get_window_per_pixel_transparency_enabled"), &_OS::get_window_per_pixel_transparency_enabled); + ClassDB::bind_method(D_METHOD("set_window_per_pixel_transparency_enabled", "enabled"), &_OS::set_window_per_pixel_transparency_enabled); + ClassDB::bind_method(D_METHOD("set_ime_position", "position"), &_OS::set_ime_position); ClassDB::bind_method(D_METHOD("set_screen_orientation", "orientation"), &_OS::set_screen_orientation); @@ -1114,6 +1180,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &_OS::delay_usec); ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &_OS::delay_msec); ClassDB::bind_method(D_METHOD("get_ticks_msec"), &_OS::get_ticks_msec); + ClassDB::bind_method(D_METHOD("get_ticks_usec"), &_OS::get_ticks_usec); ClassDB::bind_method(D_METHOD("get_splash_tick_msec"), &_OS::get_splash_tick_msec); ClassDB::bind_method(D_METHOD("get_locale"), &_OS::get_locale); ClassDB::bind_method(D_METHOD("get_latin_keyboard_variant"), &_OS::get_latin_keyboard_variant); @@ -1185,6 +1252,7 @@ void _OS::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"), "set_screen_orientation", "get_screen_orientation"); ADD_GROUP("Window", "window_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_borderless"), "set_borderless_window", "get_borderless_window"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_per_pixel_transparency_enabled"), "set_window_per_pixel_transparency_enabled", "get_window_per_pixel_transparency_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_fullscreen"), "set_window_fullscreen", "is_window_fullscreen"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_maximized"), "set_window_maximized", "is_window_maximized"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_minimized"), "set_window_minimized", "is_window_minimized"); @@ -1281,6 +1349,16 @@ Variant _Geometry::segment_intersects_segment_2d(const Vector2 &p_from_a, const }; }; +Variant _Geometry::line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b) { + + Vector2 result; + if (Geometry::line_intersects_line_2d(p_from_a, p_dir_a, p_from_b, p_dir_b, result)) { + return result; + } else { + return Variant(); + } +} + PoolVector _Geometry::get_closest_points_between_segments_2d(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2) { Vector2 r1, r2; @@ -1436,6 +1514,7 @@ void _Geometry::_bind_methods() { ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &_Geometry::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &_Geometry::segment_intersects_circle); ClassDB::bind_method(D_METHOD("segment_intersects_segment_2d", "from_a", "to_a", "from_b", "to_b"), &_Geometry::segment_intersects_segment_2d); + ClassDB::bind_method(D_METHOD("line_intersects_line_2d", "from_a", "dir_a", "from_b", "dir_b"), &_Geometry::line_intersects_line_2d); ClassDB::bind_method(D_METHOD("get_closest_points_between_segments_2d", "p1", "q1", "p2", "q2"), &_Geometry::get_closest_points_between_segments_2d); ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "p2", "q1", "q2"), &_Geometry::get_closest_points_between_segments); @@ -1675,9 +1754,9 @@ String _File::get_line() const { return f->get_line(); } -Vector _File::get_csv_line(String delim) const { +Vector _File::get_csv_line(const String &p_delim) const { ERR_FAIL_COND_V(!f, Vector()); - return f->get_csv_line(delim); + return f->get_csv_line(p_delim); } /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) @@ -1774,6 +1853,11 @@ void _File::store_line(const String &p_string) { f->store_line(p_string); } +void _File::store_csv_line(const Vector &p_values, const String &p_delim) { + ERR_FAIL_COND(!f); + f->store_csv_line(p_values, p_delim); +} + void _File::store_buffer(const PoolVector &p_buffer) { ERR_FAIL_COND(!f); @@ -1857,6 +1941,7 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("get_real"), &_File::get_real); ClassDB::bind_method(D_METHOD("get_buffer", "len"), &_File::get_buffer); ClassDB::bind_method(D_METHOD("get_line"), &_File::get_line); + ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &_File::get_csv_line, DEFVAL(",")); ClassDB::bind_method(D_METHOD("get_as_text"), &_File::get_as_text); ClassDB::bind_method(D_METHOD("get_md5", "path"), &_File::get_md5); ClassDB::bind_method(D_METHOD("get_sha256", "path"), &_File::get_sha256); @@ -1864,7 +1949,6 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("set_endian_swap", "enable"), &_File::set_endian_swap); ClassDB::bind_method(D_METHOD("get_error"), &_File::get_error); ClassDB::bind_method(D_METHOD("get_var"), &_File::get_var); - ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &_File::get_csv_line, DEFVAL(",")); ClassDB::bind_method(D_METHOD("store_8", "value"), &_File::store_8); ClassDB::bind_method(D_METHOD("store_16", "value"), &_File::store_16); @@ -1875,6 +1959,7 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("store_real", "value"), &_File::store_real); ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &_File::store_buffer); ClassDB::bind_method(D_METHOD("store_line", "line"), &_File::store_line); + ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &_File::store_csv_line, DEFVAL(",")); ClassDB::bind_method(D_METHOD("store_string", "string"), &_File::store_string); ClassDB::bind_method(D_METHOD("store_var", "value"), &_File::store_var); @@ -2415,7 +2500,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); } ///////////////////////////////////// @@ -2641,6 +2726,14 @@ int _Engine::get_iterations_per_second() const { return Engine::get_singleton()->get_iterations_per_second(); } +void _Engine::set_physics_jitter_fix(float p_threshold) { + Engine::get_singleton()->set_physics_jitter_fix(p_threshold); +} + +float _Engine::get_physics_jitter_fix() const { + return Engine::get_singleton()->get_physics_jitter_fix(); +} + void _Engine::set_target_fps(int p_fps) { Engine::get_singleton()->set_target_fps(p_fps); } @@ -2679,6 +2772,26 @@ Dictionary _Engine::get_version_info() const { return Engine::get_singleton()->get_version_info(); } +Dictionary _Engine::get_author_info() const { + return Engine::get_singleton()->get_author_info(); +} + +Array _Engine::get_copyright_info() const { + return Engine::get_singleton()->get_copyright_info(); +} + +Dictionary _Engine::get_donor_info() const { + return Engine::get_singleton()->get_donor_info(); +} + +Dictionary _Engine::get_license_info() const { + return Engine::get_singleton()->get_license_info(); +} + +String _Engine::get_license_text() const { + return Engine::get_singleton()->get_license_text(); +} + bool _Engine::is_in_physics_frame() const { return Engine::get_singleton()->is_in_physics_frame(); } @@ -2707,6 +2820,8 @@ void _Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_iterations_per_second", "iterations_per_second"), &_Engine::set_iterations_per_second); ClassDB::bind_method(D_METHOD("get_iterations_per_second"), &_Engine::get_iterations_per_second); + ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &_Engine::set_physics_jitter_fix); + ClassDB::bind_method(D_METHOD("get_physics_jitter_fix"), &_Engine::get_physics_jitter_fix); ClassDB::bind_method(D_METHOD("set_target_fps", "target_fps"), &_Engine::set_target_fps); ClassDB::bind_method(D_METHOD("get_target_fps"), &_Engine::get_target_fps); @@ -2719,6 +2834,11 @@ void _Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_main_loop"), &_Engine::get_main_loop); ClassDB::bind_method(D_METHOD("get_version_info"), &_Engine::get_version_info); + ClassDB::bind_method(D_METHOD("get_author_info"), &_Engine::get_author_info); + ClassDB::bind_method(D_METHOD("get_copyright_info"), &_Engine::get_copyright_info); + ClassDB::bind_method(D_METHOD("get_donor_info"), &_Engine::get_donor_info); + ClassDB::bind_method(D_METHOD("get_license_info"), &_Engine::get_license_info); + ClassDB::bind_method(D_METHOD("get_license_text"), &_Engine::get_license_text); ClassDB::bind_method(D_METHOD("is_in_physics_frame"), &_Engine::is_in_physics_frame); @@ -2732,6 +2852,7 @@ void _Engine::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "iterations_per_second"), "set_iterations_per_second", "get_iterations_per_second"); ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_scale"), "set_time_scale", "get_time_scale"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "physics_jitter_fix"), "set_physics_jitter_fix", "get_physics_jitter_fix"); } _Engine *_Engine::singleton = NULL; @@ -2751,10 +2872,10 @@ void JSONParseResult::_bind_methods() { ClassDB::bind_method(D_METHOD("set_error_line", "error_line"), &JSONParseResult::set_error_line); ClassDB::bind_method(D_METHOD("set_result", "result"), &JSONParseResult::set_result); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "error", PROPERTY_HINT_NONE, "Error", PROPERTY_USAGE_CLASS_IS_ENUM), "set_error", "get_error"); - ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "error_string"), "set_error_string", "get_error_string"); - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "error_line"), "set_error_line", "get_error_line"); - ADD_PROPERTYNZ(PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_result", "get_result"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "error", PROPERTY_HINT_NONE, "Error", PROPERTY_USAGE_CLASS_IS_ENUM), "set_error", "get_error"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "error_string"), "set_error_string", "get_error_string"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "error_line"), "set_error_line", "get_error_line"); + ADD_PROPERTY(PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_result", "get_result"); } void JSONParseResult::set_error(Error p_error) { diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 1790c687570e..720b14bf564c 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); @@ -55,7 +55,11 @@ class _ResourceLoader : public Object { PoolVector get_recognized_extensions_for_type(const String &p_type); void set_abort_on_missing_resources(bool p_abort); PoolStringArray get_dependencies(const String &p_path); +#ifndef DISABLE_DEPRECATED bool has(const String &p_path); +#endif // DISABLE_DEPRECATED + bool has_cached(const String &p_path); + bool exists(const String &p_path, const String &p_type_hint = ""); _ResourceLoader(); }; @@ -76,6 +80,7 @@ class _ResourceSaver : public Object { FLAG_OMIT_EDITOR_PROPERTIES = 8, FLAG_SAVE_BIG_ENDIAN = 16, FLAG_COMPRESS = 32, + FLAG_REPLACE_SUBRESOURCE_PATHS = 64, }; static _ResourceSaver *get_singleton() { return singleton; } @@ -152,6 +157,10 @@ class _OS : public Object { virtual int get_audio_driver_count() const; virtual String get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual void open_midi_inputs(); + virtual void close_midi_inputs(); + virtual int get_screen_count() const; virtual int get_current_screen() const; virtual void set_current_screen(int p_screen); @@ -176,10 +185,15 @@ class _OS : public Object { 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; + virtual bool get_window_per_pixel_transparency_enabled() const; + virtual void set_window_per_pixel_transparency_enabled(bool p_enabled); + + virtual void set_ime_active(const bool p_active); virtual void set_ime_position(const Point2 &p_pos); Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track); @@ -260,8 +274,8 @@ class _OS : public Object { Dictionary get_date(bool utc) const; Dictionary get_time(bool utc) const; Dictionary get_datetime(bool utc) const; - Dictionary get_datetime_from_unix_time(uint64_t unix_time_val) const; - uint64_t get_unix_time_from_datetime(Dictionary datetime) const; + Dictionary get_datetime_from_unix_time(int64_t unix_time_val) const; + int64_t get_unix_time_from_datetime(Dictionary datetime) const; Dictionary get_time_zone_info() const; uint64_t get_unix_time() const; uint64_t get_system_time_secs() const; @@ -273,6 +287,7 @@ class _OS : public Object { void delay_usec(uint32_t p_usec) const; void delay_msec(uint32_t p_msec) const; uint32_t get_ticks_msec() const; + uint64_t get_ticks_usec() const; uint32_t get_splash_tick_msec() const; bool can_use_threads() const; @@ -358,6 +373,7 @@ class _Geometry : public Object { PoolVector build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); PoolVector build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); Variant segment_intersects_segment_2d(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b); + Variant line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b); PoolVector get_closest_points_between_segments_2d(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2); PoolVector get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2); Vector2 get_closest_point_to_segment_2d(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b); @@ -439,6 +455,7 @@ class _File : public Reference { PoolVector get_buffer(int p_length) const; ///< get an array of bytes String get_line() const; + Vector get_csv_line(const String &p_delim = ",") const; String get_as_text() const; String get_md5(const String &p_path) const; String get_sha256(const String &p_path) const; @@ -464,12 +481,11 @@ class _File : public Reference { void store_string(const String &p_string); void store_line(const String &p_string); + void store_csv_line(const Vector &p_values, const String &p_delim = ","); virtual void store_pascal_string(const String &p_string); virtual String get_pascal_string(); - Vector get_csv_line(String delim = ",") const; - void store_buffer(const PoolVector &p_buffer); ///< store an array of bytes void store_var(const Variant &p_var); @@ -669,6 +685,9 @@ class _Engine : public Object { void set_iterations_per_second(int p_ips); int get_iterations_per_second() const; + void set_physics_jitter_fix(float p_threshold); + float get_physics_jitter_fix() const; + void set_target_fps(int p_fps); int get_target_fps() const; @@ -682,6 +701,11 @@ class _Engine : public Object { MainLoop *get_main_loop() const; Dictionary get_version_info() const; + Dictionary get_author_info() const; + Array get_copyright_info() const; + Dictionary get_donor_info() const; + Dictionary get_license_info() const; + String get_license_text() const; bool is_in_physics_frame() const; diff --git a/core/class_db.cpp b/core/class_db.cpp index 92aa131e2d63..6565d242a2a3 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); @@ -58,8 +59,8 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(2); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); return md; } @@ -68,9 +69,9 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(3); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); return md; } @@ -79,10 +80,10 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(4); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); return md; } @@ -91,11 +92,11 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(5); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); return md; } @@ -104,12 +105,12 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(6); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); return md; } @@ -118,13 +119,13 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(7); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); return md; } @@ -133,14 +134,14 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(8); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); return md; } @@ -149,15 +150,15 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(9); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); return md; } @@ -166,16 +167,16 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(10); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); return md; } @@ -184,17 +185,17 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(11); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); return md; } @@ -203,18 +204,18 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(12); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); - md.args[11] = StaticCString::create(p_arg12); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); + md.args.write[11] = StaticCString::create(p_arg12); return md; } @@ -223,19 +224,19 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(13); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); - md.args[11] = StaticCString::create(p_arg12); - md.args[12] = StaticCString::create(p_arg13); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); + md.args.write[11] = StaticCString::create(p_arg12); + md.args.write[12] = StaticCString::create(p_arg13); return md; } @@ -248,17 +249,19 @@ void ClassDB::set_current_api(APIType p_api) { current_api = p_api; } -HashMap ClassDB::classes; -HashMap ClassDB::resource_base_extensions; -HashMap ClassDB::compat_classes; +HashMap ClassDB::classes; +HashMap ClassDB::resource_base_extensions; +HashMap ClassDB::compat_classes; ClassDB::ClassInfo::ClassInfo() { + api = API_NONE; creation_func = NULL; inherits_ptr = NULL; disabled = false; exposed = false; } + ClassDB::ClassInfo::~ClassInfo() { } @@ -355,7 +358,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { ClassInfo *t = classes.getptr(E->get()); ERR_FAIL_COND_V(!t, 0); - if (t->api != p_api) + if (t->api != p_api || !t->exposed) continue; hash = hash_djb2_one_64(t->name.hash(), hash); hash = hash_djb2_one_64(t->inherits.hash(), hash); @@ -510,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) { @@ -801,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)); @@ -1244,7 +1252,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c defvals.resize(p_defcount); for (int i = 0; i < p_defcount; i++) { - defvals[i] = *p_defs[p_defcount - i - 1]; + defvals.write[i] = *p_defs[p_defcount - i - 1]; } p_bind->set_default_arguments(defvals); @@ -1359,6 +1367,41 @@ void ClassDB::get_extensions_for_type(const StringName &p_class, List *p } } +HashMap > ClassDB::default_values; + +Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property) { + + if (!default_values.has(p_class)) { + + default_values[p_class] = HashMap(); + + if (ClassDB::can_instance(p_class)) { + + Object *c = ClassDB::instance(p_class); + List plist; + c->get_property_list(&plist); + for (List::Element *E = plist.front(); E; E = E->next()) { + if (E->get().usage & (PROPERTY_USAGE_STORAGE | 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]; +} + RWLock *ClassDB::lock = NULL; void ClassDB::init() { @@ -1385,6 +1428,7 @@ void ClassDB::cleanup() { classes.clear(); resource_base_extensions.clear(); compat_classes.clear(); + default_values.clear(); memdelete(lock); } diff --git a/core/class_db.h b/core/class_db.h index 2c77ffe65f1c..75f9e8d6a71c 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -31,14 +31,18 @@ #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 */ +/** To bind more then 6 parameters include this: + * #include "core/method_bind_ext.gen.inc" + */ + #define DEFVAL(m_defval) (m_defval) //#define SIMPLE_METHODDEF @@ -114,10 +118,10 @@ class ClassDB { APIType api; ClassInfo *inherits_ptr; - HashMap method_map; - HashMap constant_map; + HashMap method_map; + HashMap constant_map; HashMap > enum_map; - HashMap signal_map; + HashMap signal_map; List property_list; #ifdef DEBUG_METHODS_ENABLED List constant_order; @@ -126,7 +130,7 @@ class ClassDB { List virtual_methods; StringName category; #endif - HashMap property_setget; + HashMap property_setget; StringName inherits; StringName name; @@ -143,9 +147,9 @@ class ClassDB { } static RWLock *lock; - static HashMap classes; - static HashMap resource_base_extensions; - static HashMap compat_classes; + static HashMap classes; + static HashMap resource_base_extensions; + static HashMap compat_classes; #ifdef DEBUG_METHODS_ENABLED static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount); @@ -157,6 +161,8 @@ class ClassDB { static void _add_class2(const StringName &p_class, const StringName &p_inherits); + static HashMap > default_values; + public: // DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!! template @@ -348,6 +354,8 @@ class ClassDB { static void get_enum_list(const StringName &p_class, List *p_enums, bool p_no_inheritance = false); static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List *p_constants, bool p_no_inheritance = false); + static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property); + static StringName get_category(const StringName &p_node); static void set_class_enabled(StringName p_class, bool p_enable); diff --git a/core/color.cpp b/core/color.cpp index b2f5889166e3..ac314417ecab 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -30,45 +30,85 @@ #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 { - uint32_t c = (uint8_t)(a * 255); + uint32_t c = (uint8_t)Math::round(a * 255); c <<= 8; - c |= (uint8_t)(r * 255); + c |= (uint8_t)Math::round(r * 255); c <<= 8; - c |= (uint8_t)(g * 255); + c |= (uint8_t)Math::round(g * 255); c <<= 8; - c |= (uint8_t)(b * 255); + c |= (uint8_t)Math::round(b * 255); return c; } uint32_t Color::to_abgr32() const { - uint32_t c = (uint8_t)(a * 255); + + uint32_t c = (uint8_t)Math::round(a * 255); c <<= 8; - c |= (uint8_t)(b * 255); + c |= (uint8_t)Math::round(b * 255); c <<= 8; - c |= (uint8_t)(g * 255); + c |= (uint8_t)Math::round(g * 255); c <<= 8; - c |= (uint8_t)(r * 255); + c |= (uint8_t)Math::round(r * 255); return c; } uint32_t Color::to_rgba32() const { - uint32_t c = (uint8_t)(r * 255); + uint32_t c = (uint8_t)Math::round(r * 255); c <<= 8; - c |= (uint8_t)(g * 255); + c |= (uint8_t)Math::round(g * 255); c <<= 8; - c |= (uint8_t)(b * 255); + c |= (uint8_t)Math::round(b * 255); c <<= 8; - c |= (uint8_t)(a * 255); + c |= (uint8_t)Math::round(a * 255); + + return c; +} + +uint64_t Color::to_abgr64() const { + + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + + return c; +} + +uint64_t Color::to_argb64() const { + + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + + return c; +} + +uint64_t Color::to_rgba64() const { + + uint64_t c = (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(a * 65535); return c; } @@ -200,6 +240,34 @@ Color Color::hex(uint32_t p_hex) { return Color(r, g, b, a); } +Color Color::hex64(uint64_t p_hex) { + + float a = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float b = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float g = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float r = (p_hex & 0xFFFF) / 65535.0; + + return Color(r, g, b, a); +} + +Color Color::from_rgbe9995(uint32_t p_rgbe) { + + float r = p_rgbe & 0x1ff; + float g = (p_rgbe >> 9) & 0x1ff; + float b = (p_rgbe >> 18) & 0x1ff; + float e = (p_rgbe >> 27); + float m = Math::pow(2, e - 15.0 - 9.0); + + float rd = r * m; + float gd = g * m; + float bd = b * m; + + return Color(rd, gd, bd, 1.0f); +} + static float _parse_col(const String &p_str, int p_ofs) { int ig = 0; @@ -368,7 +436,7 @@ Color Color::named(const String &p_name) { String _to_hex(float p_val) { - int v = p_val * 255; + int v = Math::round(p_val * 255); v = CLAMP(v, 0, 255); String ret; @@ -400,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) @@ -453,8 +521,11 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) { return Color(m + r, m + g, m + b, p_a); } +// FIXME: Remove once Godot 3.1 has been released float Color::gray() const { + ERR_EXPLAIN("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation."); + WARN_DEPRECATED return (r + g + b) / 3.0; } diff --git a/core/color.h b/core/color.h index a2015a34d68d..d6ad5f91c560 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 */ @@ -55,6 +55,9 @@ struct Color { uint32_t to_rgba32() const; uint32_t to_argb32() const; uint32_t to_abgr32() const; + uint64_t to_rgba64() const; + uint64_t to_argb64() const; + uint64_t to_abgr64() const; float gray() const; float get_h() const; float get_s() const; @@ -186,11 +189,13 @@ struct Color { } static Color hex(uint32_t p_hex); + static Color hex64(uint64_t p_hex); static Color html(const String &p_color); static bool html_is_valid(const String &p_color); static Color named(const String &p_name); 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 operator String() const; diff --git a/core/color_names.inc b/core/color_names.inc index b05684acc6e2..e126bfe0f8b2 100644 --- a/core/color_names.inc +++ b/core/color_names.inc @@ -1,9 +1,9 @@ // 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 _named_colors; static void _populate_named_colors() { - if(!_named_colors.empty()) return; + if (!_named_colors.empty()) return; _named_colors.insert("aliceblue", Color(0.94, 0.97, 1.00)); _named_colors.insert("antiquewhite", Color(0.98, 0.92, 0.84)); _named_colors.insert("aqua", Color(0.00, 1.00, 1.00)); diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp index a39c920dfaa9..1b3a74bd0d50 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 3942b961d3a7..5911cf5006bf 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 */ @@ -54,9 +54,13 @@ #define _COMMA_10 , #define _COMMA_11 , #define _COMMA_12 , +#define _COMMA_13 , // 1-based comma separated list of ITEMs #define COMMA_SEP_LIST(ITEM, LENGTH) _COMMA_SEP_LIST_##LENGTH(ITEM) +#define _COMMA_SEP_LIST_13(ITEM) \ + _COMMA_SEP_LIST_12(ITEM) \ + , ITEM(13) #define _COMMA_SEP_LIST_12(ITEM) \ _COMMA_SEP_LIST_11(ITEM) \ , ITEM(12) @@ -97,6 +101,9 @@ // 1-based semicolon separated list of ITEMs #define SEMIC_SEP_LIST(ITEM, LENGTH) _SEMIC_SEP_LIST_##LENGTH(ITEM) +#define _SEMIC_SEP_LIST_13(ITEM) \ + _SEMIC_SEP_LIST_12(ITEM); \ + ITEM(13) #define _SEMIC_SEP_LIST_12(ITEM) \ _SEMIC_SEP_LIST_11(ITEM); \ ITEM(12) @@ -137,6 +144,9 @@ // 1-based space separated list of ITEMs #define SPACE_SEP_LIST(ITEM, LENGTH) _SPACE_SEP_LIST_##LENGTH(ITEM) +#define _SPACE_SEP_LIST_13(ITEM) \ + _SPACE_SEP_LIST_12(ITEM) \ + ITEM(13) #define _SPACE_SEP_LIST_12(ITEM) \ _SPACE_SEP_LIST_11(ITEM) \ ITEM(12) @@ -262,7 +272,7 @@ ss->sem->wait(); \ } -#define MAX_CMD_PARAMS 12 +#define MAX_CMD_PARAMS 13 class CommandQueueMT { @@ -290,15 +300,15 @@ class CommandQueueMT { }; DECL_CMD(0) - SPACE_SEP_LIST(DECL_CMD, 12) + SPACE_SEP_LIST(DECL_CMD, 13) /* comands that return */ DECL_CMD_RET(0) - SPACE_SEP_LIST(DECL_CMD_RET, 12) + SPACE_SEP_LIST(DECL_CMD_RET, 13) /* commands that don't return but sync */ DECL_CMD_SYNC(0) - SPACE_SEP_LIST(DECL_CMD_SYNC, 12) + SPACE_SEP_LIST(DECL_CMD_SYNC, 13) /***** BASE *******/ @@ -432,15 +442,15 @@ class CommandQueueMT { public: /* NORMAL PUSH COMMANDS */ DECL_PUSH(0) - SPACE_SEP_LIST(DECL_PUSH, 12) + SPACE_SEP_LIST(DECL_PUSH, 13) /* PUSH AND RET COMMANDS */ DECL_PUSH_AND_RET(0) - SPACE_SEP_LIST(DECL_PUSH_AND_RET, 12) + SPACE_SEP_LIST(DECL_PUSH_AND_RET, 13) /* PUSH AND RET SYNC COMMANDS*/ DECL_PUSH_AND_SYNC(0) - SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 12) + SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 13) void wait_and_flush_one() { ERR_FAIL_COND(!sync); diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp index 266d793af7b3..7dd5308fab16 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" @@ -50,7 +50,6 @@ void PHashTranslation::generate(const Ref &p_from) { int size = Math::larger_prime(keys.size()); - print_line("compressing keys: " + itos(keys.size())); Vector > > buckets; Vector > table; Vector hfunc_table; @@ -73,7 +72,7 @@ void PHashTranslation::generate(const Ref &p_from) { Pair p; p.first = idx; p.second = cs; - buckets[h % size].push_back(p); + buckets.write[h % size].push_back(p); //compress string CharString src_s = p_from->get_message(E->get()).operator String().utf8(); @@ -100,25 +99,22 @@ void PHashTranslation::generate(const Ref &p_from) { ps.compressed[0] = 0; } - compressed[idx] = ps; + compressed.write[idx] = ps; total_compression_size += ps.compressed.size(); total_string_size += src_s.size(); idx++; } int bucket_table_size = 0; - print_line("total compressed string size: " + itos(total_compression_size) + " (" + itos(total_string_size) + " uncompressed)."); for (int i = 0; i < size; i++) { - Vector > &b = buckets[i]; - Map &t = table[i]; + const Vector > &b = buckets[i]; + Map &t = table.write[i]; if (b.size() == 0) continue; - //print_line("bucket: "+itos(i)+" - elements: "+itos(b.size())); - int d = 1; int item = 0; @@ -136,13 +132,10 @@ void PHashTranslation::generate(const Ref &p_from) { } } - hfunc_table[i] = d; + hfunc_table.write[i] = d; bucket_table_size += 2 + b.size() * 4; } - print_line("bucket table size: " + itos(bucket_table_size * 4)); - print_line("hash table size: " + itos(size * 4)); - hash_table.resize(size); bucket_table.resize(bucket_table_size); @@ -157,7 +150,7 @@ void PHashTranslation::generate(const Ref &p_from) { for (int i = 0; i < size; i++) { - Map &t = table[i]; + const Map &t = table[i]; if (t.size() == 0) { htw[i] = 0xFFFFFFFF; //nothing continue; @@ -178,8 +171,6 @@ void PHashTranslation::generate(const Ref &p_from) { } } - print_line("total collisions: " + itos(collisions)); - strings.resize(total_compression_size); PoolVector::Write cw = strings.write(); @@ -198,15 +189,11 @@ bool PHashTranslation::_set(const StringName &p_name, const Variant &p_value) { String name = p_name.operator String(); if (name == "hash_table") { hash_table = p_value; - //print_line("translation: loaded hash table of size: "+itos(hash_table.size())); } else if (name == "bucket_table") { bucket_table = p_value; - //print_line("translation: loaded bucket table of size: "+itos(bucket_table.size())); } else if (name == "strings") { strings = p_value; - //print_line("translation: loaded string table of size: "+itos(strings.size())); } else if (name == "load_from") { - //print_line("generating"); generate(p_value); } else return false; @@ -248,11 +235,7 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { uint32_t p = htptr[h % htsize]; - //print_line("String: "+p_src_text.operator String()); - //print_line("Hash: "+itos(p)); - if (p == 0xFFFFFFFF) { - //print_line("GETMSG: Nothing!"); return StringName(); //nothing } @@ -271,9 +254,7 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { } } - //print_line("bucket pos: "+itos(idx)); if (idx == -1) { - //print_line("GETMSG: Not in Bucket!"); return StringName(); } @@ -281,8 +262,6 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { String rstr; rstr.parse_utf8(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].uncomp_size); - //print_line("Uncompressed, size: "+itos(bucket.elem[idx].comp_size)); - //print_line("Return: "+rstr); return rstr; } else { @@ -292,8 +271,6 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { smaz_decompress(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].comp_size, uncomp.ptrw(), bucket.elem[idx].uncomp_size); String rstr; rstr.parse_utf8(uncomp.get_data()); - //print_line("Compressed, size: "+itos(bucket.elem[idx].comp_size)); - //print_line("Return: "+rstr); return rstr; } } diff --git a/core/compressed_translation.h b/core/compressed_translation.h index ccc47d0bf6d6..b3d193b47873 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 new file mode 100644 index 000000000000..f3a9e3b221cd --- /dev/null +++ b/core/core_builders.py @@ -0,0 +1,269 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +from platform_methods import subprocess_main +from compat import iteritems, itervalues, open_utf8, escape_string, 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): + sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"] + sections_id = ["AUTHORS_FOUNDERS", "AUTHORS_LEAD_DEVELOPERS", "AUTHORS_PROJECT_MANAGERS", "AUTHORS_DEVELOPERS"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_AUTHORS_H\n") + g.write("#define _EDITOR_AUTHORS_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_donors_header(target, source, env): + sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors", + "Gold donors", "Silver donors", "Bronze donors"] + sections_id = ["DONORS_SPONSOR_PLAT", "DONORS_SPONSOR_GOLD", "DONORS_SPONSOR_MINI", + "DONORS_GOLD", "DONORS_SILVER", "DONORS_BRONZE"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_DONORS_H\n") + g.write("#define _EDITOR_DONORS_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading >= 0: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_license_header(target, source, env): + src_copyright = source[0] + src_license = source[1] + dst = target[0] + + class LicenseReader: + def __init__(self, license_file): + self._license_file = license_file + self.line_num = 0 + self.current = self.next_line() + + def next_line(self): + line = self._license_file.readline() + self.line_num += 1 + while line.startswith("#"): + line = self._license_file.readline() + self.line_num += 1 + self.current = line + return line + + def next_tag(self): + if not ':' in self.current: + return ('', []) + tag, line = self.current.split(":", 1) + lines = [line.strip()] + while self.next_line() and self.current.startswith(" "): + lines.append(self.current.strip()) + return (tag, lines) + + from collections import OrderedDict + projects = OrderedDict() + license_list = [] + + with open_utf8(src_copyright, "r") as copyright_file: + reader = LicenseReader(copyright_file) + part = {} + while reader.current: + tag, content = reader.next_tag() + if tag in ("Files", "Copyright", "License"): + part[tag] = content[:] + elif tag == "Comment": + # attach part to named project + projects[content[0]] = projects.get(content[0], []) + [part] + + if not tag or not reader.current: + # end of a paragraph start a new part + if "License" in part and not "Files" in part: + # no Files tag in this one, so assume standalone license + license_list.append(part["License"]) + part = {} + reader.next_line() + + data_list = [] + for project in itervalues(projects): + for part in project: + part["file_index"] = len(data_list) + data_list += part["Files"] + part["copyright_index"] = len(data_list) + data_list += part["Copyright"] + + with open_utf8(dst, "w") as f: + + f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + f.write("#ifndef _EDITOR_LICENSE_H\n") + f.write("#define _EDITOR_LICENSE_H\n") + f.write("const char *const GODOT_LICENSE_TEXT =") + + with open_utf8(src_license, "r") as license_file: + for line in license_file: + escaped_string = escape_string(line.strip()) + f.write("\n\t\t\"" + escaped_string + "\\n\"") + f.write(";\n\n") + + f.write("struct ComponentCopyrightPart {\n" + "\tconst char *license;\n" + "\tconst char *const *files;\n" + "\tconst char *const *copyright_statements;\n" + "\tint file_count;\n" + "\tint copyright_count;\n" + "};\n\n") + + f.write("struct ComponentCopyright {\n" + "\tconst char *name;\n" + "\tconst ComponentCopyrightPart *parts;\n" + "\tint part_count;\n" + "};\n\n") + + f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n") + for line in data_list: + f.write("\t\"" + escape_string(line) + "\",\n") + f.write("};\n\n") + + f.write("const ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n") + part_index = 0 + part_indexes = {} + for project_name, project in iteritems(projects): + part_indexes[project_name] = part_index + for part in project: + f.write("\t{ \"" + escape_string(part["License"][0]) + "\", " + + "©RIGHT_INFO_DATA[" + str(part["file_index"]) + "], " + + "©RIGHT_INFO_DATA[" + str(part["copyright_index"]) + "], " + + str(len(part["Files"])) + ", " + + str(len(part["Copyright"])) + " },\n") + part_index += 1 + f.write("};\n\n") + + f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n") + + f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n") + for project_name, project in iteritems(projects): + f.write("\t{ \"" + escape_string(project_name) + "\", " + + "©RIGHT_PROJECT_PARTS[" + str(part_indexes[project_name]) + "], " + + str(len(project)) + " },\n") + f.write("};\n\n") + + f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") + + f.write("const char *const LICENSE_NAMES[] = {\n") + for l in license_list: + f.write("\t\"" + escape_string(l[0]) + "\",\n") + f.write("};\n\n") + + f.write("const char *const LICENSE_BODIES[] = {\n\n") + for l in license_list: + for line in l[1:]: + if line == ".": + f.write("\t\"\\n\"\n") + else: + f.write("\t\"" + escape_string(line) + "\\n\"\n") + f.write("\t\"\",\n\n") + f.write("};\n\n") + + f.write("#endif\n") + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/core/core_string_names.h b/core/core_string_names.h index dcbce14aac7c..3fdb2404089d 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 new file mode 100644 index 000000000000..54ece4c85656 --- /dev/null +++ b/core/cowdata.h @@ -0,0 +1,341 @@ +/*************************************************************************/ +/* cowdata.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef COWDATA_H_ +#define COWDATA_H_ + +#include "core/os/memory.h" +#include "core/safe_refcount.h" + +template +class Vector; +class String; +class CharString; +template +class VMap; + +template +class CowData { + template + friend class Vector; + friend class String; + friend class CharString; + template + friend class VMap; + +private: + mutable T *_ptr; + + // internal helpers + + _FORCE_INLINE_ uint32_t *_get_refcount() const { + + if (!_ptr) + return NULL; + + return reinterpret_cast(_ptr) - 2; + } + + _FORCE_INLINE_ uint32_t *_get_size() const { + + if (!_ptr) + return NULL; + + return reinterpret_cast(_ptr) - 1; + } + + _FORCE_INLINE_ T *_get_data() const { + + if (!_ptr) + return NULL; + return reinterpret_cast(_ptr); + } + + _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { + //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); + return next_power_of_2(p_elements * sizeof(T)); + } + + _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { +#if defined(_add_overflow) && defined(_mul_overflow) + size_t o; + size_t p; + if (_mul_overflow(p_elements, sizeof(T), &o)) { + *out = 0; + return false; + } + *out = next_power_of_2(o); + if (_add_overflow(o, static_cast(32), &p)) return false; //no longer allocated here + return true; +#else + // Speed is more important than correctness here, do the operations unchecked + // and hope the best + *out = _get_alloc_size(p_elements); + return true; +#endif + } + + void _unref(void *p_data); + void _ref(const CowData *p_from); + void _ref(const CowData &p_from); + void _copy_on_write(); + +public: + void operator=(const CowData &p_from) { _ref(p_from); } + + _FORCE_INLINE_ T *ptrw() { + _copy_on_write(); + return (T *)_get_data(); + } + + _FORCE_INLINE_ const T *ptr() const { + return _get_data(); + } + + _FORCE_INLINE_ int size() const { + uint32_t *size = (uint32_t *)_get_size(); + if (size) + return *size; + else + return 0; + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _ptr == 0; } + + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { + + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + _get_data()[p_index] = p_elem; + } + + _FORCE_INLINE_ T &get_m(int p_index) { + + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + return _get_data()[p_index]; + } + + _FORCE_INLINE_ const T &get(int p_index) const { + + CRASH_BAD_INDEX(p_index, size()); + + return _get_data()[p_index]; + } + + Error resize(int p_size); + + _FORCE_INLINE_ void remove(int p_index) { + + ERR_FAIL_INDEX(p_index, size()); + T *p = ptrw(); + int len = size(); + for (int i = p_index; i < len - 1; i++) { + + p[i] = p[i + 1]; + }; + + resize(len - 1); + }; + + Error insert(int p_pos, const T &p_val) { + + ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); + resize(size() + 1); + for (int i = (size() - 1); i > p_pos; i--) + set(i, get(i - 1)); + set(p_pos, p_val); + + return OK; + }; + + _FORCE_INLINE_ CowData(); + _FORCE_INLINE_ ~CowData(); + _FORCE_INLINE_ CowData(CowData &p_from) { _ref(p_from); }; +}; + +template +void CowData::_unref(void *p_data) { + + if (!p_data) + return; + + uint32_t *refc = _get_refcount(); + + if (atomic_decrement(refc) > 0) + return; // still in use + // clean up + + uint32_t *count = _get_size(); + T *data = (T *)(count + 1); + + for (uint32_t i = 0; i < *count; ++i) { + // call destructors + data[i].~T(); + } + + // free mem + Memory::free_static((uint8_t *)p_data, true); +} + +template +void CowData::_copy_on_write() { + + if (!_ptr) + return; + + uint32_t *refc = _get_refcount(); + + if (unlikely(*refc > 1)) { + /* in use by more than me */ + uint32_t current_size = *_get_size(); + + uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); + + *(mem_new - 2) = 1; //refcount + *(mem_new - 1) = current_size; //size + + T *_data = (T *)(mem_new); + + // initialize new elements + for (uint32_t i = 0; i < current_size; i++) { + + memnew_placement(&_data[i], T(_get_data()[i])); + } + + _unref(_ptr); + _ptr = _data; + } +} + +template +Error CowData::resize(int p_size) { + + ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); + + if (p_size == size()) + return OK; + + if (p_size == 0) { + // wants to clean up + _unref(_ptr); + _ptr = NULL; + return OK; + } + + // possibly changing size, copy on write + _copy_on_write(); + + size_t alloc_size; + ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); + + if (p_size > size()) { + + if (size() == 0) { + // alloc from scratch + uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); + ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); + *(ptr - 1) = 0; //size, currently none + *(ptr - 2) = 1; //refcount + + _ptr = (T *)ptr; + + } else { + void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + _ptr = (T *)(_ptrnew); + } + + // construct the newly created elements + T *elems = _get_data(); + + for (int i = *_get_size(); i < p_size; i++) { + + memnew_placement(&elems[i], T); + } + + *_get_size() = p_size; + + } else if (p_size < size()) { + + // deinitialize no longer needed elements + for (uint32_t i = p_size; i < *_get_size(); i++) { + + T *t = &_get_data()[i]; + t->~T(); + } + + void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + + _ptr = (T *)(_ptrnew); + + *_get_size() = p_size; + } + + return OK; +} + +template +void CowData::_ref(const CowData *p_from) { + _ref(*p_from); +} + +template +void CowData::_ref(const CowData &p_from) { + + if (_ptr == p_from._ptr) + return; // self assign, do nothing. + + _unref(_ptr); + _ptr = NULL; + + if (!p_from._ptr) + return; //nothing to do + + if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference + _ptr = p_from._ptr; + } +} + +template +CowData::CowData() { + + _ptr = NULL; +} + +template +CowData::~CowData() { + + _unref(_ptr); +} + +#endif /* COW_H_ */ diff --git a/core/dictionary.cpp b/core/dictionary.cpp index ba0de95861dd..ccbdff38165a 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 { @@ -50,6 +50,32 @@ void Dictionary::get_key_list(List *p_keys) const { } } +Variant Dictionary::get_key_at_index(int p_index) const { + + int index = 0; + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + if (index == p_index) { + return E.key(); + } + index++; + } + + return Variant(); +} + +Variant Dictionary::get_value_at_index(int p_index) const { + + int index = 0; + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + if (index == p_index) { + return E.value(); + } + index++; + } + + return Variant(); +} + Variant &Dictionary::operator[](const Variant &p_key) { return _p->variant_map[p_key]; @@ -109,9 +135,9 @@ bool Dictionary::has_all(const Array &p_keys) const { return true; } -void Dictionary::erase(const Variant &p_key) { +bool Dictionary::erase(const Variant &p_key) { - _p->variant_map.erase(p_key); + return _p->variant_map.erase(p_key); } bool Dictionary::operator==(const Dictionary &p_dictionary) const { @@ -119,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 9eef265d5bf3..d3b98c2f6339 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; @@ -47,6 +48,8 @@ class Dictionary { public: void get_key_list(List *p_keys) const; + Variant get_key_at_index(int p_index) const; + Variant get_value_at_index(int p_index) const; Variant &operator[](const Variant &p_key); const Variant &operator[](const Variant &p_key) const; @@ -63,9 +66,10 @@ class Dictionary { bool has(const Variant &p_key) const; bool has_all(const Array &p_keys) const; - void erase(const Variant &p_key); + 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 c0190fb9e3f9..2830c57ec06e 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,8 +149,8 @@ class PoolVector { } } - if (old_alloc->refcount.unref() == true) { - //this should never happen but.. + if (old_alloc->refcount.unref()) { + //this should never happen but.. #ifdef DEBUG_ENABLED MemoryPool::alloc_mutex->lock(); @@ -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 af9052110f07..fceb602dcf0f 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -30,8 +30,11 @@ #include "engine.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) { @@ -42,6 +45,16 @@ int Engine::get_iterations_per_second() const { return ips; } +void Engine::set_physics_jitter_fix(float p_threshold) { + if (p_threshold < 0) + p_threshold = 0; + physics_jitter_fix = p_threshold; +} + +float Engine::get_physics_jitter_fix() const { + return physics_jitter_fix; +} + void Engine::set_target_fps(int p_fps) { _target_fps = p_fps > 0 ? p_fps : 0; } @@ -101,6 +114,78 @@ Dictionary Engine::get_version_info() const { return dict; } +static Array array_from_info(const char *const *info_list) { + Array arr; + for (int i = 0; info_list[i] != NULL; i++) { + arr.push_back(info_list[i]); + } + return arr; +} + +static Array array_from_info_count(const char *const *info_list, int info_count) { + Array arr; + for (int i = 0; i < info_count; i++) { + arr.push_back(info_list[i]); + } + return arr; +} + +Dictionary Engine::get_author_info() const { + Dictionary dict; + + dict["lead_developers"] = array_from_info(AUTHORS_LEAD_DEVELOPERS); + dict["project_managers"] = array_from_info(AUTHORS_PROJECT_MANAGERS); + dict["founders"] = array_from_info(AUTHORS_FOUNDERS); + dict["developers"] = array_from_info(AUTHORS_DEVELOPERS); + + return dict; +} + +Array Engine::get_copyright_info() const { + Array components; + for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) { + const ComponentCopyright &cp_info = COPYRIGHT_INFO[component_index]; + Dictionary component_dict; + component_dict["name"] = cp_info.name; + Array parts; + for (int i = 0; i < cp_info.part_count; i++) { + const ComponentCopyrightPart &cp_part = cp_info.parts[i]; + Dictionary part_dict; + part_dict["files"] = array_from_info_count(cp_part.files, cp_part.file_count); + part_dict["copyright"] = array_from_info_count(cp_part.copyright_statements, cp_part.copyright_count); + part_dict["license"] = cp_part.license; + parts.push_back(part_dict); + } + component_dict["parts"] = parts; + + components.push_back(component_dict); + } + return components; +} + +Dictionary Engine::get_donor_info() const { + Dictionary donors; + donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLAT); + donors["gold_sponsors"] = array_from_info(DONORS_SPONSOR_GOLD); + donors["mini_sponsors"] = array_from_info(DONORS_SPONSOR_MINI); + donors["gold_donors"] = array_from_info(DONORS_GOLD); + donors["silver_donors"] = array_from_info(DONORS_SILVER); + donors["bronze_donors"] = array_from_info(DONORS_BRONZE); + return donors; +} + +Dictionary Engine::get_license_info() const { + Dictionary licenses; + for (int i = 0; i < LICENSE_COUNT; i++) { + licenses[LICENSE_NAMES[i]] = LICENSE_BODIES[i]; + } + return licenses; +} + +String Engine::get_license_text() const { + return String(GODOT_LICENSE_TEXT); +} + void Engine::add_singleton(const Singleton &p_singleton) { singletons.push_back(p_singleton); @@ -137,6 +222,7 @@ Engine::Engine() { singleton = this; frames_drawn = 0; ips = 60; + physics_jitter_fix = 0.5; _frame_delay = 0; _fps = 1; _target_fps = 0; diff --git a/core/engine.h b/core/engine.h index 54b30ab81f76..82b1e5d6814f 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 { @@ -57,6 +57,7 @@ class Engine { float _frame_step; int ips; + float physics_jitter_fix; float _fps; int _target_fps; float _time_scale; @@ -79,6 +80,9 @@ class Engine { virtual void set_iterations_per_second(int p_ips); virtual int get_iterations_per_second() const; + void set_physics_jitter_fix(float p_threshold); + float get_physics_jitter_fix() const; + virtual void set_target_fps(int p_fps); virtual float get_target_fps() const; @@ -114,8 +118,14 @@ class Engine { #endif Dictionary get_version_info() const; + Dictionary get_author_info() const; + Array get_copyright_info() const; + Dictionary get_donor_info() const; + Dictionary get_license_info() const; + String get_license_text() const; Engine(); + virtual ~Engine() {} }; #endif // ENGINE_H diff --git a/core/error_macros.cpp b/core/error_macros.cpp index 578680293030..c1a0dd0dfcea 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 168b2e06fe2e..8cec18d24f1b 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 @@ -311,14 +311,14 @@ extern bool _err_error_exists; _err_error_exists = false; \ } -#define WARN_DEPRECATED \ - { \ - static bool warning_shown=false;\ - if (!warning_shown) {\ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__,"This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ - _err_error_exists = false; \ - warning_shown=true;\ - }\ +#define WARN_DEPRECATED \ + { \ + static volatile bool warning_shown = false; \ + if (!warning_shown) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ + _err_error_exists = false; \ + warning_shown = true; \ + } \ } #endif diff --git a/core/func_ref.h b/core/func_ref.h index 681fe747d691..930c376abf0a 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 04810afe73c7..7e9b8b393c12 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 { @@ -89,6 +89,7 @@ VARIANT_ENUM_CAST(KeyList); VARIANT_ENUM_CAST(KeyModifierMask); VARIANT_ENUM_CAST(ButtonList); VARIANT_ENUM_CAST(JoystickList); +VARIANT_ENUM_CAST(MidiMessageList); void register_global_constants() { @@ -378,6 +379,8 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(BUTTON_LEFT); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_RIGHT); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MIDDLE); + BIND_GLOBAL_ENUM_CONSTANT(BUTTON_XBUTTON1); + BIND_GLOBAL_ENUM_CONSTANT(BUTTON_XBUTTON2); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_UP); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_DOWN); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_LEFT); @@ -385,6 +388,8 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_LEFT); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_RIGHT); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_MIDDLE); + BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON1); + BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON2); //joypads BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_0); @@ -454,6 +459,15 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2); BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2); + // midi + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND); + // error list BIND_GLOBAL_ENUM_CONSTANT(OK); @@ -518,6 +532,7 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT); + BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS); @@ -532,8 +547,9 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_INTERNATIONALIZED); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_GROUP); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_CATEGORY); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONZERO); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONONE); + //deprecated, replaced by ClassDB function to check default value + //BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONZERO); + //BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONONE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_NO_INSTANCE_STATE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE); diff --git a/core/global_constants.h b/core/global_constants.h index 76f618989c50..48f16934a375 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 2df743ba7d05..3869cd3c360b 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 @@ class HashMap { 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 @@ class HashMap { 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 ae99fa39c87d..457cfadc9ca3 100644 --- a/core/hashfuncs.h +++ b/core/hashfuncs.h @@ -31,10 +31,12 @@ #ifndef HASHFUNCS_H #define HASHFUNCS_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/node_path.h" +#include "core/string_db.h" +#include "core/typedefs.h" +#include "core/ustring.h" /** * Hashing functions @@ -131,6 +133,7 @@ static inline uint64_t make_uint64_t(T p_in) { } struct HashMapHasherDefault { + static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); } static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); } @@ -145,6 +148,10 @@ struct HashMapHasherDefault { static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; } static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; } static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; } + + static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); } + static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); } + //static _FORCE_INLINE_ uint32_t hash(const void* p_ptr) { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); } }; diff --git a/core/helper/SCsub b/core/helper/SCsub deleted file mode 100644 index 4efc90271768..000000000000 --- 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 2ac8ffea56e8..698a0b0b987c 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -30,10 +30,12 @@ #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 "print_string.h" +#include "core/print_string.h" #include "thirdparty/misc/hq2x.h" @@ -305,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); @@ -366,6 +369,8 @@ int Image::get_mipmap_count() const { template static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p_dst) { + uint32_t max_bytes = MAX(read_bytes, write_bytes); + for (int y = 0; y < p_height; y++) { for (int x = 0; x < p_width; x++) { @@ -379,7 +384,8 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p rgba[1] = rofs[0]; rgba[2] = rofs[0]; } else { - for (uint32_t i = 0; i < MAX(read_bytes, write_bytes); i++) { + + for (uint32_t i = 0; i < max_bytes; i++) { rgba[i] = (i < read_bytes) ? rofs[i] : 0; } @@ -494,8 +500,6 @@ void Image::convert(Format p_new_format) { bool gen_mipmaps = mipmaps; - //mipmaps=false; - _copy_internals_from(new_img); if (gen_mipmaps) @@ -521,8 +525,8 @@ static double _bicubic_interp_kernel(double x) { return bc; } -template -static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +template +static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { // get source image size int width = p_src_width; @@ -552,7 +556,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi // initial pixel value - uint8_t *dst = p_dst + (y * p_dst_width + x) * CC; + T *__restrict dst = ((T *)p_dst) + (y * p_dst_width + x) * CC; double color[CC]; for (int i = 0; i < CC; i++) { @@ -580,24 +584,33 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi ox2 = xmax; // get pixel of original image - const uint8_t *p = p_src + (oy2 * p_src_width + ox2) * CC; + const T *__restrict p = ((T *)p_src) + (oy2 * p_src_width + ox2) * CC; for (int i = 0; i < CC; i++) { - - color[i] += p[i] * k2; + if (sizeof(T) == 2) { //half float + color[i] = Math::half_to_float(p[i]); + } else { + color[i] += p[i] * k2; + } } } } for (int i = 0; i < CC; i++) { - dst[i] = CLAMP(Math::fast_ftoi(color[i]), 0, 255); + if (sizeof(T) == 1) { //byte + dst[i] = CLAMP(Math::fast_ftoi(color[i]), 0, 255); + } else if (sizeof(T) == 2) { //half float + dst[i] = Math::make_half_float(color[i]); + } else { + dst[i] = color[i]; + } } } } } -template -static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +template +static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { enum { FRAC_BITS = 8, @@ -636,23 +649,59 @@ static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src for (uint32_t l = 0; l < CC; l++) { - uint32_t p00 = p_src[y_ofs_up + src_xofs_left + l] << FRAC_BITS; - uint32_t p10 = p_src[y_ofs_up + src_xofs_right + l] << FRAC_BITS; - uint32_t p01 = p_src[y_ofs_down + src_xofs_left + l] << FRAC_BITS; - uint32_t p11 = p_src[y_ofs_down + src_xofs_right + l] << FRAC_BITS; - - uint32_t interp_up = p00 + (((p10 - p00) * src_xofs_frac) >> FRAC_BITS); - uint32_t interp_down = p01 + (((p11 - p01) * src_xofs_frac) >> FRAC_BITS); - uint32_t interp = interp_up + (((interp_down - interp_up) * src_yofs_frac) >> FRAC_BITS); - interp >>= FRAC_BITS; - p_dst[i * p_dst_width * CC + j * CC + l] = interp; + if (sizeof(T) == 1) { //uint8 + uint32_t p00 = p_src[y_ofs_up + src_xofs_left + l] << FRAC_BITS; + uint32_t p10 = p_src[y_ofs_up + src_xofs_right + l] << FRAC_BITS; + uint32_t p01 = p_src[y_ofs_down + src_xofs_left + l] << FRAC_BITS; + uint32_t p11 = p_src[y_ofs_down + src_xofs_right + l] << FRAC_BITS; + + uint32_t interp_up = p00 + (((p10 - p00) * src_xofs_frac) >> FRAC_BITS); + uint32_t interp_down = p01 + (((p11 - p01) * src_xofs_frac) >> FRAC_BITS); + uint32_t interp = interp_up + (((interp_down - interp_up) * src_yofs_frac) >> FRAC_BITS); + interp >>= FRAC_BITS; + p_dst[i * p_dst_width * CC + j * CC + l] = interp; + } else if (sizeof(T) == 2) { //half float + + float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS); + float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS); + const T *src = ((const T *)p_src); + T *dst = ((T *)p_dst); + + float p00 = Math::half_to_float(src[y_ofs_up + src_xofs_left + l]); + float p10 = Math::half_to_float(src[y_ofs_up + src_xofs_right + l]); + float p01 = Math::half_to_float(src[y_ofs_down + src_xofs_left + l]); + float p11 = Math::half_to_float(src[y_ofs_down + src_xofs_right + l]); + + float interp_up = p00 + (p10 - p00) * xofs_frac; + float interp_down = p01 + (p11 - p01) * xofs_frac; + float interp = interp_up + ((interp_down - interp_up) * yofs_frac); + + dst[i * p_dst_width * CC + j * CC + l] = Math::make_half_float(interp); + } else if (sizeof(T) == 4) { //float + + float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS); + float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS); + const T *src = ((const T *)p_src); + T *dst = ((T *)p_dst); + + float p00 = src[y_ofs_up + src_xofs_left + l]; + float p10 = src[y_ofs_up + src_xofs_right + l]; + float p01 = src[y_ofs_down + src_xofs_left + l]; + float p11 = src[y_ofs_down + src_xofs_right + l]; + + float interp_up = p00 + (p10 - p00) * xofs_frac; + float interp_down = p01 + (p11 - p01) * xofs_frac; + float interp = interp_up + ((interp_down - interp_up) * yofs_frac); + + dst[i * p_dst_width * CC + j * CC + l] = interp; + } } } } } -template -static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +template +static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { for (uint32_t i = 0; i < p_dst_height; i++) { @@ -666,13 +715,26 @@ static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_ for (uint32_t l = 0; l < CC; l++) { - uint32_t p = p_src[y_ofs + src_xofs + l]; - p_dst[i * p_dst_width * CC + j * CC + l] = p; + const T *src = ((const T *)p_src); + T *dst = ((T *)p_dst); + + T p = src[y_ofs + src_xofs + l]; + dst[i * p_dst_width * CC + j * CC + l] = p; } } } } +static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) { + + uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256); + + for (uint32_t i = 0; i < p_width * p_height * p_pixel_size; i++) { + + p_dst[i] = (p_dst[i] * (256 - alpha) + p_src[i] * alpha) >> 8; + } +} + void Image::resize_to_po2(bool p_square) { if (!_can_modify(format)) { @@ -704,6 +766,8 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { ERR_FAIL(); } + bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; + ERR_FAIL_COND(p_width <= 0); ERR_FAIL_COND(p_height <= 0); ERR_FAIL_COND(p_width > MAX_WIDTH); @@ -714,6 +778,33 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { Image dst(p_width, p_height, 0, format); + // Setup mipmap-aware scaling + Image dst2; + int mip1 = 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) { + mipmap_aware = false; + } else { + float level = Math::log(1.0f / avg_scale) / Math::log(2.0f); + mip1 = CLAMP((int)Math::floor(level), 0, get_mipmap_count()); + mip2 = CLAMP((int)Math::ceil(level), 0, get_mipmap_count()); + mip1_weight = 1.0f - (level - mip1); + } + } + bool interpolate_mipmaps = mipmap_aware && mip1 != mip2; + if (interpolate_mipmaps) { + dst2.create(p_width, p_height, 0, format); + } + + bool had_mipmaps = mipmaps; + if (interpolate_mipmaps && !had_mipmaps) { + generate_mipmaps(); + } + // -- + PoolVector::Read r = data.read(); const unsigned char *r_ptr = r.ptr(); @@ -724,45 +815,143 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { case INTERPOLATE_NEAREST: { - switch (get_format_pixel_size(format)) { - case 1: _scale_nearest<1>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_nearest<2>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_nearest<3>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_nearest<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; + if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { + switch (get_format_pixel_size(format)) { + case 1: _scale_nearest<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: _scale_nearest<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 3: _scale_nearest<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_nearest<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { + switch (get_format_pixel_size(format)) { + case 4: _scale_nearest<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_nearest<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 12: _scale_nearest<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 16: _scale_nearest<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + + } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { + switch (get_format_pixel_size(format)) { + case 2: _scale_nearest<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_nearest<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 6: _scale_nearest<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_nearest<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } } + } break; - case INTERPOLATE_BILINEAR: { + case INTERPOLATE_BILINEAR: + case INTERPOLATE_TRILINEAR: { + + for (int i = 0; i < 2; ++i) { + int src_width; + int src_height; + const unsigned char *src_ptr; + + if (!mipmap_aware) { + if (i == 0) { + // Standard behavior + src_width = width; + src_height = height; + src_ptr = r_ptr; + } else { + // No need for a second iteration + break; + } + } else { + if (i == 0) { + // Read from the first mipmap that will be interpolated + // (if both levels are the same, we will not interpolate, but at least we'll sample from the right level) + int offs; + _get_mipmap_offset_and_size(mip1, offs, src_width, src_height); + src_ptr = r_ptr + offs; + } else if (!interpolate_mipmaps) { + // No need generate a second image + break; + } else { + // Switch to read from the second mipmap that will be interpolated + int offs; + _get_mipmap_offset_and_size(mip2, offs, src_width, src_height); + src_ptr = r_ptr + offs; + // Switch to write to the second destination image + w = dst2.data.write(); + w_ptr = w.ptr(); + } + } - switch (get_format_pixel_size(format)) { - case 1: _scale_bilinear<1>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_bilinear<2>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_bilinear<3>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_bilinear<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; + if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { + switch (get_format_pixel_size(format)) { + case 1: _scale_bilinear<1, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 2: _scale_bilinear<2, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 3: _scale_bilinear<3, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 4: _scale_bilinear<4, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } + } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { + switch (get_format_pixel_size(format)) { + case 4: _scale_bilinear<1, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 8: _scale_bilinear<2, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 12: _scale_bilinear<3, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 16: _scale_bilinear<4, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } + } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { + switch (get_format_pixel_size(format)) { + case 2: _scale_bilinear<1, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 4: _scale_bilinear<2, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 6: _scale_bilinear<3, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 8: _scale_bilinear<4, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } + } + } + + if (interpolate_mipmaps) { + // Switch to read again from the first scaled mipmap to overlay it over the second + r = dst.data.read(); + _overlay(r.ptr(), w.ptr(), mip1_weight, p_width, p_height, get_format_pixel_size(format)); } } break; case INTERPOLATE_CUBIC: { - switch (get_format_pixel_size(format)) { - case 1: _scale_cubic<1>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_cubic<2>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_cubic<3>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_cubic<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; + if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { + switch (get_format_pixel_size(format)) { + case 1: _scale_cubic<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: _scale_cubic<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 3: _scale_cubic<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_cubic<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { + switch (get_format_pixel_size(format)) { + case 4: _scale_cubic<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_cubic<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 12: _scale_cubic<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 16: _scale_cubic<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { + switch (get_format_pixel_size(format)) { + case 2: _scale_cubic<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_cubic<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 6: _scale_cubic<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_cubic<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } } - } break; } r = PoolVector::Read(); w = PoolVector::Write(); - if (mipmaps > 0) + if (interpolate_mipmaps) { + dst._copy_internals_from(dst2); + } + + if (had_mipmaps) dst.generate_mipmaps(); _copy_internals_from(dst); } 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(); @@ -808,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); } @@ -825,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::Write w = data.write(); @@ -849,8 +1038,9 @@ void Image::flip_y() { } } - if (gm) + if (used_mipmaps) { generate_mipmaps(); + } } void Image::flip_x() { @@ -860,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::Write w = data.write(); @@ -883,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) { @@ -897,8 +1089,10 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & int pixsize = get_format_pixel_size(p_format); int pixshift = get_format_pixel_rshift(p_format); int block = get_format_block_size(p_format); - int minw, minh; - get_format_min_pixel_size(p_format, minw, minh); + //technically, you can still compress up to 1 px no matter the format, so commenting this + //int minw, minh; + //get_format_min_pixel_size(p_format, minw, minh); + int minw = 1, minh = 1; while (true) { @@ -937,35 +1131,38 @@ bool Image::_can_modify(Format p_format) const { return p_format <= FORMAT_RGBE9995; } -template -static void _generate_po2_mipmap(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height) { +template +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 uint8_t *rup_ptr = &p_src[i * 2 * p_width * CC]; - const uint8_t *rdown_ptr = rup_ptr + p_width * CC; - uint8_t *dst_ptr = &p_dst[i * dst_w * 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 + right_step], rdown_ptr[j], rdown_ptr[j + right_step]); + } - uint16_t val = 0; - val += rup_ptr[j]; - val += rup_ptr[j + CC]; - val += rdown_ptr[j]; - val += rdown_ptr[j + CC]; - dst_ptr[j] = val >> 2; + if (renormalize) { + renormalize_func(dst_ptr); } dst_ptr += CC; - rup_ptr += CC * 2; - rdown_ptr += CC * 2; + rup_ptr += right_step * 2; + rdown_ptr += right_step * 2; } } } @@ -974,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); @@ -1000,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(); } @@ -1045,11 +1245,23 @@ void Image::shrink_x2() { switch (format) { case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<1>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_LA8: _generate_po2_mipmap<2>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RG8: _generate_po2_mipmap<2>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RGB8: _generate_po2_mipmap<3>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RGBA8: _generate_po2_mipmap<4>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_R8: _generate_po2_mipmap(r.ptr(), w.ptr(), width, height); break; + case FORMAT_LA8: _generate_po2_mipmap(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RG8: _generate_po2_mipmap(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RGB8: _generate_po2_mipmap(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RGBA8: _generate_po2_mipmap(r.ptr(), w.ptr(), width, height); break; + + case FORMAT_RF: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; + case FORMAT_RGF: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; + case FORMAT_RGBF: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; + case FORMAT_RGBAF: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; + + case FORMAT_RH: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; + case FORMAT_RGH: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; + case FORMAT_RGBH: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; + case FORMAT_RGBAH: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; + + case FORMAT_RGBE9995: _generate_po2_mipmap(reinterpret_cast(r.ptr()), reinterpret_cast(w.ptr()), width, height); break; default: {} } } @@ -1060,7 +1272,37 @@ void Image::shrink_x2() { } } -Error Image::generate_mipmaps() { +void Image::normalize() { + + bool used_mipmaps = has_mipmaps(); + if (used_mipmaps) { + clear_mipmaps(); + } + + lock(); + + for (int y = 0; y < height; y++) { + + for (int x = 0; x < width; x++) { + + Color c = get_pixel(x, y); + Vector3 v(c.r * 2.0 - 1.0, c.g * 2.0 - 1.0, c.b * 2.0 - 1.0); + v.normalize(); + c.r = v.x * 0.5 + 0.5; + c.g = v.y * 0.5 + 0.5; + c.b = v.z * 0.5 + 0.5; + set_pixel(x, y, c); + } + } + + unlock(); + + if (used_mipmaps) { + generate_mipmaps(true); + } +} + +Error Image::generate_mipmaps(bool p_renormalize) { if (!_can_modify(format)) { ERR_EXPLAIN("Cannot generate mipmaps in indexed, compressed or custom image formats."); @@ -1081,7 +1323,7 @@ Error Image::generate_mipmaps() { 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); @@ -1089,11 +1331,69 @@ Error Image::generate_mipmaps() { switch (format) { case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_R8: _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; case FORMAT_LA8: - case FORMAT_RG8: _generate_po2_mipmap<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; - case FORMAT_RGB8: _generate_po2_mipmap<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; - case FORMAT_RGBA8: _generate_po2_mipmap<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_RG8: _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_RGB8: + if (p_renormalize) + _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + else + _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + + break; + case FORMAT_RGBA8: + if (p_renormalize) + _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + else + _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + break; + case FORMAT_RF: + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + break; + case FORMAT_RGF: + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + break; + case FORMAT_RGBF: + if (p_renormalize) + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + + break; + case FORMAT_RGBAF: + if (p_renormalize) + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + + break; + case FORMAT_RH: + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + break; + case FORMAT_RGH: + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + break; + case FORMAT_RGBH: + if (p_renormalize) + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + + break; + case FORMAT_RGBAH: + if (p_renormalize) + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + + break; + case FORMAT_RGBE9995: + if (p_renormalize) + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); + + break; default: {} } @@ -1134,6 +1434,9 @@ PoolVector Image::get_data() const { void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { + ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH); + ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT); + int mm = 0; int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); data.resize(size); @@ -1157,7 +1460,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); if (size != p_data.size()) { - ERR_EXPLAIN("Expected data size of " + itos(size) + " in Image::create()"); + ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); ERR_FAIL_COND(p_data.size() != size); } @@ -1170,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; @@ -1186,8 +1490,8 @@ void Image::create(const char **p_xpm) { int line = 0; HashMap colormap; - int colormap_size; - uint32_t pixel_size; + int colormap_size = 0; + uint32_t pixel_size = 0; PoolVector::Write w; while (status != DONE) { @@ -1433,7 +1737,11 @@ Image::AlphaMode Image::detect_alpha() const { } Error Image::load(const String &p_path) { - +#ifdef DEBUG_ENABLED + if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) { + WARN_PRINTS("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource."); + } +#endif return ImageLoader::load_image(p_path, this); } @@ -1445,10 +1753,10 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref((Image *)this)); } -int Image::get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps) { +int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; - return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps); + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0); } int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format) { @@ -1458,14 +1766,25 @@ int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format return mm; } +int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) { + + if (p_mipmap <= 0) { + return 0; + } + int mm; + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1); +} + bool Image::is_compressed() const { return format > FORMAT_RGBE9995; } Error Image::decompress() { - if (format >= FORMAT_DXT1 && format <= FORMAT_BPTC_RGBFU && _image_decompress_bc) + if (format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG && _image_decompress_bc) _image_decompress_bc(this); + else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc) + _image_decompress_bptc(this); else if (format >= FORMAT_PVRTC2 && format <= FORMAT_PVRTC4A && _image_decompress_pvrtc) _image_decompress_pvrtc(this); else if (format == FORMAT_ETC && _image_decompress_etc1) @@ -1484,7 +1803,7 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss case COMPRESS_S3TC: { ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); - _image_compress_bc_func(this, p_source); + _image_compress_bc_func(this, p_lossy_quality, p_source); } break; case COMPRESS_PVRTC2: { @@ -1506,6 +1825,11 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE); _image_compress_etc2_func(this, p_lossy_quality, p_source); } break; + case COMPRESS_BPTC: { + + ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); + _image_compress_bptc_func(this, p_lossy_quality, p_source); + } break; } return OK; @@ -1598,6 +1922,12 @@ void Image::blit_rect(const Ref &p_src, const Rect2 &p_src_rect, const Po ERR_FAIL_COND(format != p_src->format); Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + + if (p_dest.x < 0) + clipped_src_rect.position.x = ABS(p_dest.x); + if (p_dest.y < 0) + clipped_src_rect.position.y = ABS(p_dest.y); + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; @@ -1646,6 +1976,12 @@ void Image::blit_rect_mask(const Ref &p_src, const Ref &p_mask, co ERR_FAIL_COND(format != p_src->format); Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + + if (p_dest.x < 0) + clipped_src_rect.position.x = ABS(p_dest.x); + if (p_dest.y < 0) + clipped_src_rect.position.y = ABS(p_dest.y); + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; @@ -1697,6 +2033,12 @@ void Image::blend_rect(const Ref &p_src, const Rect2 &p_src_rect, const P ERR_FAIL_COND(format != p_src->format); Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + + if (p_dest.x < 0) + clipped_src_rect.position.x = ABS(p_dest.x); + if (p_dest.y < 0) + clipped_src_rect.position.y = ABS(p_dest.y); + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; @@ -1745,6 +2087,12 @@ void Image::blend_rect_mask(const Ref &p_src, const Ref &p_mask, c ERR_FAIL_COND(format != p_src->format); Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + + if (p_dest.x < 0) + clipped_src_rect.position.x = ABS(p_dest.x); + if (p_dest.y < 0) + clipped_src_rect.position.y = ABS(p_dest.y); + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; @@ -1814,16 +2162,19 @@ void Image::fill(const Color &c) { unlock(); } -Ref (*Image::_png_mem_loader_func)(const uint8_t *, int) = NULL; -Ref (*Image::_jpg_mem_loader_func)(const uint8_t *, int) = NULL; +ImageMemLoadFunc Image::_png_mem_loader_func = NULL; +ImageMemLoadFunc Image::_jpg_mem_loader_func = NULL; +ImageMemLoadFunc Image::_webp_mem_loader_func = NULL; -void (*Image::_image_compress_bc_func)(Image *, Image::CompressSource) = NULL; +void (*Image::_image_compress_bc_func)(Image *, float, Image::CompressSource) = NULL; +void (*Image::_image_compress_bptc_func)(Image *, float, Image::CompressSource) = NULL; void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL; void (*Image::_image_compress_pvrtc4_func)(Image *) = NULL; void (*Image::_image_compress_etc1_func)(Image *, float) = NULL; void (*Image::_image_compress_etc2_func)(Image *, float, Image::CompressSource) = NULL; void (*Image::_image_decompress_pvrtc)(Image *) = NULL; void (*Image::_image_decompress_bc)(Image *) = NULL; +void (*Image::_image_decompress_bptc)(Image *) = NULL; void (*Image::_image_decompress_etc1)(Image *) = NULL; void (*Image::_image_decompress_etc2)(Image *) = NULL; @@ -1880,6 +2231,10 @@ void Image::unlock() { write_lock = PoolVector::Write(); } +Color Image::get_pixelv(const Point2 &p_src) const { + return get_pixel(p_src.x, p_src.y); +} + Color Image::get_pixel(int p_x, int p_y) const { uint8_t *ptr = write_lock.ptr(); @@ -2003,18 +2358,7 @@ Color Image::get_pixel(int p_x, int p_y) const { return Color(Math::half_to_float(r), Math::half_to_float(g), Math::half_to_float(b), Math::half_to_float(a)); } break; case FORMAT_RGBE9995: { - uint32_t rgbe = ((uint32_t *)ptr)[ofs]; - float r = rgbe & 0x1ff; - float g = (rgbe >> 9) & 0x1ff; - float b = (rgbe >> 18) & 0x1ff; - float e = (rgbe >> 27); - float m = Math::pow(2, e - 15.0 - 9.0); - ; - float rd = r * m; - float gd = g * m; - float bd = b * m; - - return Color(rd, gd, bd, 1.0); + return Color::from_rgbe9995(((uint32_t *)ptr)[ofs]); } break; default: { @@ -2026,6 +2370,10 @@ Color Image::get_pixel(int p_x, int p_y) const { return Color(); } +void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) { + return set_pixel(p_dst.x, p_dst.y, p_color); +} + void Image::set_pixel(int p_x, int p_y, const Color &p_color) { uint8_t *ptr = write_lock.ptr(); @@ -2044,10 +2392,10 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { switch (format) { case FORMAT_L8: { - ptr[ofs] = uint8_t(CLAMP(p_color.gray() * 255.0, 0, 255)); + ptr[ofs] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255)); } break; case FORMAT_LA8: { - ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.gray() * 255.0, 0, 255)); + ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255)); ptr[ofs * 2 + 1] = uint8_t(CLAMP(p_color.a * 255.0, 0, 255)); } break; case FORMAT_R8: { @@ -2196,6 +2544,17 @@ Image::DetectChannels Image::get_detected_channels() { return DETECTED_RGBA; } +void Image::optimize_channels() { + switch (get_detected_channels()) { + case DETECTED_L: convert(FORMAT_L8); break; + case DETECTED_LA: convert(FORMAT_LA8); break; + case DETECTED_R: convert(FORMAT_R8); break; + case DETECTED_RG: convert(FORMAT_RG8); break; + case DETECTED_RGB: convert(FORMAT_RGB8); break; + case DETECTED_RGBA: convert(FORMAT_RGBA8); break; + } +} + void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_width"), &Image::get_width); @@ -2217,7 +2576,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("crop", "width", "height"), &Image::crop); ClassDB::bind_method(D_METHOD("flip_x"), &Image::flip_x); ClassDB::bind_method(D_METHOD("flip_y"), &Image::flip_y); - ClassDB::bind_method(D_METHOD("generate_mipmaps"), &Image::generate_mipmaps); + ClassDB::bind_method(D_METHOD("generate_mipmaps", "renormalize"), &Image::generate_mipmaps, DEFVAL(false)); ClassDB::bind_method(D_METHOD("clear_mipmaps"), &Image::clear_mipmaps); ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::_create_empty); @@ -2239,6 +2598,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha); ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear); ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy); + ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb); ClassDB::bind_method(D_METHOD("bumpmap_to_normalmap", "bump_scale"), &Image::bumpmap_to_normalmap, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect); @@ -2257,11 +2617,14 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("lock"), &Image::lock); ClassDB::bind_method(D_METHOD("unlock"), &Image::unlock); - ClassDB::bind_method(D_METHOD("set_pixel", "x", "y", "color"), &Image::set_pixel); + ClassDB::bind_method(D_METHOD("get_pixelv", "src"), &Image::get_pixelv); ClassDB::bind_method(D_METHOD("get_pixel", "x", "y"), &Image::get_pixel); + ClassDB::bind_method(D_METHOD("set_pixelv", "dst", "color"), &Image::set_pixelv); + ClassDB::bind_method(D_METHOD("set_pixel", "x", "y", "color"), &Image::set_pixel); ClassDB::bind_method(D_METHOD("load_png_from_buffer", "buffer"), &Image::load_png_from_buffer); ClassDB::bind_method(D_METHOD("load_jpg_from_buffer", "buffer"), &Image::load_jpg_from_buffer); + ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data"); @@ -2307,6 +2670,7 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST); BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR); BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC); + BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR); BIND_ENUM_CONSTANT(ALPHA_NONE); BIND_ENUM_CONSTANT(ALPHA_BIT); @@ -2323,11 +2687,16 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESS_SOURCE_NORMAL); } -void Image::set_compress_bc_func(void (*p_compress_func)(Image *, CompressSource)) { +void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)) { _image_compress_bc_func = p_compress_func; } +void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource)) { + + _image_compress_bptc_func = p_compress_func; +} + void Image::normalmap_to_xy() { convert(Image::FORMAT_RGBA8); @@ -2348,6 +2717,37 @@ void Image::normalmap_to_xy() { convert(Image::FORMAT_LA8); } +Ref Image::rgbe_to_srgb() { + + if (data.size() == 0) + return Ref(); + + ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref()); + + Ref new_image; + new_image.instance(); + new_image->create(width, height, 0, Image::FORMAT_RGB8); + + lock(); + + new_image->lock(); + + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + new_image->set_pixel(col, row, get_pixel(col, row).to_srgb()); + } + } + + unlock(); + new_image->unlock(); + + if (has_mipmaps()) { + new_image->generate_mipmaps(); + } + + return new_image; +} + void Image::bumpmap_to_normalmap(float bump_scale) { ERR_FAIL_COND(!_can_modify(format)); convert(Image::FORMAT_RF); @@ -2523,15 +2923,26 @@ String Image::get_format_name(Format p_format) { } Error Image::load_png_from_buffer(const PoolVector &p_array) { + return _load_from_buffer(p_array, _png_mem_loader_func); +} + +Error Image::load_jpg_from_buffer(const PoolVector &p_array) { + return _load_from_buffer(p_array, _jpg_mem_loader_func); +} + +Error Image::load_webp_from_buffer(const PoolVector &p_array) { + return _load_from_buffer(p_array, _webp_mem_loader_func); +} +Error Image::_load_from_buffer(const PoolVector &p_array, ImageMemLoadFunc p_loader) { int buffer_size = p_array.size(); ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!_png_mem_loader_func, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!p_loader, ERR_INVALID_PARAMETER); PoolVector::Read r = p_array.read(); - Ref image = _png_mem_loader_func(r.ptr(), buffer_size); + Ref image = p_loader(r.ptr(), buffer_size); ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR); copy_internals_from(image); @@ -2539,21 +2950,53 @@ Error Image::load_png_from_buffer(const PoolVector &p_array) { return OK; } -Error Image::load_jpg_from_buffer(const PoolVector &p_array) { +void Image::average_4_uint8(uint8_t &p_out, const uint8_t &p_a, const uint8_t &p_b, const uint8_t &p_c, const uint8_t &p_d) { + p_out = static_cast((p_a + p_b + p_c + p_d + 2) >> 2); +} - int buffer_size = p_array.size(); +void Image::average_4_float(float &p_out, const float &p_a, const float &p_b, const float &p_c, const float &p_d) { + p_out = (p_a + p_b + p_c + p_d) * 0.25f; +} - ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!_jpg_mem_loader_func, ERR_INVALID_PARAMETER); +void Image::average_4_half(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d) { + p_out = Math::make_half_float((Math::half_to_float(p_a) + Math::half_to_float(p_b) + Math::half_to_float(p_c) + Math::half_to_float(p_d)) * 0.25f); +} - PoolVector::Read r = p_array.read(); +void Image::average_4_rgbe9995(uint32_t &p_out, const uint32_t &p_a, const uint32_t &p_b, const uint32_t &p_c, const uint32_t &p_d) { + p_out = ((Color::from_rgbe9995(p_a) + Color::from_rgbe9995(p_b) + Color::from_rgbe9995(p_c) + Color::from_rgbe9995(p_d)) * 0.25f).to_rgbe9995(); +} - Ref image = _jpg_mem_loader_func(r.ptr(), buffer_size); - ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR); +void Image::renormalize_uint8(uint8_t *p_rgb) { + Vector3 n(p_rgb[0] / 255.0, p_rgb[1] / 255.0, p_rgb[2] / 255.0); + n *= 2.0; + n -= Vector3(1, 1, 1); + n.normalize(); + n += Vector3(1, 1, 1); + n *= 0.5; + n *= 255; + p_rgb[0] = CLAMP(int(n.x), 0, 255); + p_rgb[1] = CLAMP(int(n.y), 0, 255); + p_rgb[2] = CLAMP(int(n.z), 0, 255); +} - copy_internals_from(image); +void Image::renormalize_float(float *p_rgb) { + Vector3 n(p_rgb[0], p_rgb[1], p_rgb[2]); + n.normalize(); + p_rgb[0] = n.x; + p_rgb[1] = n.y; + p_rgb[2] = n.z; +} - return OK; +void Image::renormalize_half(uint16_t *p_rgb) { + Vector3 n(Math::half_to_float(p_rgb[0]), Math::half_to_float(p_rgb[1]), Math::half_to_float(p_rgb[2])); + n.normalize(); + p_rgb[0] = Math::make_half_float(n.x); + p_rgb[1] = Math::make_half_float(n.y); + p_rgb[2] = Math::make_half_float(n.z); +} + +void Image::renormalize_rgbe9995(uint32_t *p_rgb) { + // Never used } Image::Image(const uint8_t *p_mem_png_jpg, int p_len) { diff --git a/core/image.h b/core/image.h index 17477d88ea40..0770eb953ea4 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 "math_2d.h" -#include "resource.h" +#include "core/color.h" +#include "core/dvector.h" +#include "core/math/rect2.h" +#include "core/resource.h" /** * @author Juan Linietsky @@ -47,6 +47,7 @@ class Image; typedef Error (*SavePNGFunc)(const String &p_path, const Ref &p_img); +typedef Ref (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size); class Image : public Resource { GDCLASS(Image, Resource); @@ -107,21 +108,26 @@ class Image : public Resource { INTERPOLATE_NEAREST, INTERPOLATE_BILINEAR, INTERPOLATE_CUBIC, + INTERPOLATE_TRILINEAR, + /* INTERPOLATE_TRICUBIC, */ /* INTERPOLATE GAUSS */ }; enum CompressSource { COMPRESS_SOURCE_GENERIC, COMPRESS_SOURCE_SRGB, - COMPRESS_SOURCE_NORMAL + COMPRESS_SOURCE_NORMAL, + COMPRESS_SOURCE_LAYERED, }; //some functions provided by something else - static Ref (*_png_mem_loader_func)(const uint8_t *p_png, int p_size); - static Ref (*_jpg_mem_loader_func)(const uint8_t *p_png, int p_size); + static ImageMemLoadFunc _png_mem_loader_func; + static ImageMemLoadFunc _jpg_mem_loader_func; + static ImageMemLoadFunc _webp_mem_loader_func; - static void (*_image_compress_bc_func)(Image *, CompressSource p_source); + static void (*_image_compress_bc_func)(Image *, float, CompressSource p_source); + static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, CompressSource p_source); static void (*_image_compress_pvrtc2_func)(Image *); static void (*_image_compress_pvrtc4_func)(Image *); static void (*_image_compress_etc1_func)(Image *, float); @@ -129,6 +135,7 @@ class Image : public Resource { static void (*_image_decompress_pvrtc)(Image *); static void (*_image_decompress_bc)(Image *); + static void (*_image_decompress_bptc)(Image *); static void (*_image_decompress_etc1)(Image *); static void (*_image_decompress_etc2)(Image *); @@ -175,6 +182,17 @@ class Image : public Resource { void _set_data(const Dictionary &p_data); Dictionary _get_data() const; + Error _load_from_buffer(const PoolVector &p_array, ImageMemLoadFunc p_loader); + + static void average_4_uint8(uint8_t &p_out, const uint8_t &p_a, const uint8_t &p_b, const uint8_t &p_c, const uint8_t &p_d); + static void average_4_float(float &p_out, const float &p_a, const float &p_b, const float &p_c, const float &p_d); + static void average_4_half(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d); + static void average_4_rgbe9995(uint32_t &p_out, const uint32_t &p_a, const uint32_t &p_b, const uint32_t &p_c, const uint32_t &p_d); + static void renormalize_uint8(uint8_t *p_rgb); + static void renormalize_float(float *p_rgb); + static void renormalize_half(uint16_t *p_rgb); + static void renormalize_rgbe9995(uint32_t *p_rgb); + public: int get_width() const; ///< Get image width int get_height() const; ///< Get image height @@ -217,9 +235,10 @@ class Image : public Resource { /** * Generate a mipmap to an image (creates an image 1/4 the size, with averaging of 4->1) */ - Error generate_mipmaps(); + Error generate_mipmaps(bool p_renormalize = false); void clear_mipmaps(); + void normalize(); //for normal maps /** * Create a new image of a given size and format. Current image will be lost @@ -265,8 +284,9 @@ class Image : public Resource { static int get_format_block_size(Format p_format); static void get_format_min_pixel_size(Format p_format, int &r_w, int &r_h); - static int get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps = 0); + static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false); static int get_image_required_mipmaps(int p_width, int p_height, Format p_format); + static int get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap); enum CompressMode { COMPRESS_S3TC, @@ -274,6 +294,7 @@ class Image : public Resource { COMPRESS_PVRTC4, COMPRESS_ETC, COMPRESS_ETC2, + COMPRESS_BPTC }; Error compress(CompressMode p_mode = COMPRESS_S3TC, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7); @@ -284,6 +305,7 @@ class Image : public Resource { void premultiply_alpha(); void srgb_to_linear(); void normalmap_to_xy(); + Ref rgbe_to_srgb(); void bumpmap_to_normalmap(float bump_scale = 1.0); void blit_rect(const Ref &p_src, const Rect2 &p_src_rect, const Point2 &p_dest); @@ -295,11 +317,13 @@ class Image : public Resource { Rect2 get_used_rect() const; Ref get_rect(const Rect2 &p_area) const; - static void set_compress_bc_func(void (*p_compress_func)(Image *, CompressSource)); + static void set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)); + static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource)); static String get_format_name(Format p_format); Error load_png_from_buffer(const PoolVector &p_array); Error load_jpg_from_buffer(const PoolVector &p_array); + Error load_webp_from_buffer(const PoolVector &p_array); Image(const uint8_t *p_mem_png_jpg, int p_len = -1); Image(const char **p_xpm); @@ -320,8 +344,11 @@ class Image : public Resource { }; DetectChannels get_detected_channels(); + void optimize_channels(); + Color get_pixelv(const Point2 &p_src) const; Color get_pixel(int p_x, int p_y) const; + void set_pixelv(const Point2 &p_dest, const Color &p_color); void set_pixel(int p_x, int p_y, const Color &p_color); void copy_internals_from(const Ref &p_image) { diff --git a/core/input_map.cpp b/core/input_map.cpp index 973edcb5b5be..b88d99470acf 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; @@ -44,10 +44,11 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f)); ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action); - ClassDB::bind_method(D_METHOD("action_set_deadzone", "deadzone"), &InputMap::action_set_deadzone); + ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone); ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event); ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event); ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event); + ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events); ClassDB::bind_method(D_METHOD("get_action_list", "action"), &InputMap::_get_action_list); ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action); ClassDB::bind_method(D_METHOD("load_from_globals"), &InputMap::load_from_globals); @@ -98,7 +99,7 @@ List InputMap::get_actions() const { return actions; } -List >::Element *InputMap::_find_event(Action p_action, const Ref &p_event, bool *p_pressed, float *p_strength) const { +List >::Element *InputMap::_find_event(Action &p_action, const Ref &p_event, bool *p_pressed, float *p_strength) const { for (List >::Element *E = p_action.inputs.front(); E; E = E->next()) { @@ -155,6 +156,13 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref &p_event, const Str Ref 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 a00be8c859dd..8f286277d169 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 { @@ -55,7 +55,7 @@ class InputMap : public Object { mutable Map input_map; - List >::Element *_find_event(Action p_action, const Ref &p_event, bool *p_pressed = NULL, float *p_strength = NULL) const; + List >::Element *_find_event(Action &p_action, const Ref &p_event, bool *p_pressed = NULL, float *p_strength = NULL) const; Array _get_action_list(const StringName &p_action); Array _get_actions(); @@ -75,6 +75,7 @@ class InputMap : public Object { void action_add_event(const StringName &p_action, const Ref &p_event); bool action_has_event(const StringName &p_action, const Ref &p_event); void action_erase_event(const StringName &p_action, const Ref &p_event); + void action_erase_events(const StringName &p_action); const List > *get_action_list(const StringName &p_action); bool event_is_action(const Ref &p_event, const StringName &p_action) const; diff --git a/core/io/SCsub b/core/io/SCsub index 79b56cb71669..1c5f954470dc 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 bc3bfcf356d0..3c0b6541bd61 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 a0ccd539cb0e..2f770e6aeebb 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 aa06ae5cc00d..e2bc0f5d8ff8 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 ac749bed76f3..2bc439d4137c 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 dcaf99e24acb..d44c8a9585a2 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 - -#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 f4ed47d6bcc4..a36a9a460364 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 d6547ba19f96..645d97ae7eea 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 587f58a7c6f7..5be42a6d32e2 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 221f457b7831..28bf55b96209 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" @@ -43,7 +43,6 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector &p_key, Mode p_mode) { - //print_line("open and parse!"); ERR_FAIL_COND_V(file != NULL, ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); @@ -89,7 +88,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector > *files = NULL; @@ -92,7 +92,7 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { Map >::Element *E = files->find(name); ERR_FAIL_COND_V(!E, ERR_FILE_NOT_FOUND); - data = &(E->get()[0]); + data = E->get().ptrw(); length = E->get().size(); pos = 0; diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 2136f4cc0cd9..e474fb0d0515 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 21e3a4172b75..b9544ac16644 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()); @@ -93,8 +94,6 @@ void FileAccessNetworkClient::_thread_func() { DEBUG_TIME("sem_unlock"); //DEBUG_PRINT("semwait returned "+itos(werr)); DEBUG_PRINT("MUTEX LOCK " + itos(lockcount)); - DEBUG_PRINT("POPO"); - DEBUG_PRINT("PEPE"); lock_mutex(); DEBUG_PRINT("MUTEX PASS"); @@ -228,7 +227,7 @@ FileAccessNetworkClient::FileAccessNetworkClient() { quit = false; singleton = this; last_id = 0; - client = Ref(StreamPeerTCP::create_ref()); + client.instance(); sem = Semaphore::create(); lockcount = 0; } @@ -258,8 +257,8 @@ void FileAccessNetwork::_set_block(int p_offset, const Vector &p_block) } buffer_mutex->lock(); - pages[page].buffer = p_block; - pages[page].queued = false; + pages.write[page].buffer = p_block; + pages.write[page].queued = false; buffer_mutex->unlock(); if (waiting_on_page == page) { @@ -389,7 +388,7 @@ void FileAccessNetwork::_queue_page(int p_page) const { br.offset = size_t(p_page) * page_size; br.size = page_size; nc->block_requests.push_back(br); - pages[p_page].queued = true; + pages.write[p_page].queued = true; nc->blockrequest_mutex->unlock(); DEBUG_PRINT("QUEUE PAGE POST"); nc->sem->post(); @@ -433,12 +432,12 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { _queue_page(page + j); } - buff = pages[page].buffer.ptrw(); + buff = pages.write[page].buffer.ptrw(); //queue pages buffer_mutex->unlock(); } - buff = pages[page].buffer.ptrw(); + buff = pages.write[page].buffer.ptrw(); last_page_buff = buff; last_page = page; } @@ -501,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() { @@ -520,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 be9bdb1af6ae..c929e8446dfc 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 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 efb4c7a07343..382328579264 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 @@ -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++) { @@ -454,7 +455,7 @@ String DirAccessPack::get_current_dir() { while (pd->parent) { pd = pd->parent; - p = pd->name + "/" + p; + p = pd->name.plus_file(p); } return "res://" + p; diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 8a40e6d78c0f..9e31bcf88a40 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; @@ -175,7 +175,6 @@ class FileAccessPack : public FileAccess { FileAccess *PackedData::try_open_path(const String &p_path) { - //print_line("try open path " + p_path); PathMD5 pmd5(p_path.md5_buffer()); Map::Element *E = files.find(pmd5); if (!E) diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index df83575f6ac5..9bb1ad221ae7 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 9e301ccac59c..36dd688e770b 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "http_client.h" -#include "io/stream_peer_ssl.h" + +#include "core/io/stream_peer_ssl.h" +#include "core/version.h" const char *HTTPClient::_methods[METHOD_MAX] = { "GET", @@ -121,16 +123,30 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; } bool add_clen = p_body.size() > 0; + bool add_uagent = true; + bool add_accept = true; for (int i = 0; i < p_headers.size(); i++) { request += p_headers[i] + "\r\n"; - if (add_clen && p_headers[i].find("Content-Length:") == 0) { + if (add_clen && p_headers[i].findn("Content-Length:") == 0) { add_clen = false; } + if (add_uagent && p_headers[i].findn("User-Agent:") == 0) { + add_uagent = false; + } + if (add_accept && p_headers[i].findn("Accept:") == 0) { + add_accept = false; + } } if (add_clen) { request += "Content-Length: " + itos(p_body.size()) + "\r\n"; // Should it add utf8 encoding? } + if (add_uagent) { + request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n"; + } + if (add_accept) { + request += "Accept: */*\r\n"; + } request += "\r\n"; CharString cs = request.utf8(); @@ -173,17 +189,31 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector 0; for (int i = 0; i < p_headers.size(); i++) { request += p_headers[i] + "\r\n"; - if (add_clen && p_headers[i].find("Content-Length:") == 0) { + if (add_clen && p_headers[i].findn("Content-Length:") == 0) { add_clen = false; } + if (add_uagent && p_headers[i].findn("User-Agent:") == 0) { + add_uagent = false; + } + if (add_accept && p_headers[i].findn("Accept:") == 0) { + add_accept = false; + } } if (add_clen) { request += "Content-Length: " + itos(p_body.utf8().length()) + "\r\n"; // Should it add utf8 encoding? } + if (add_uagent) { + request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n"; + } + if (add_accept) { + request += "Accept: */*\r\n"; + } request += "\r\n"; request += p_body; @@ -245,10 +275,12 @@ 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; response_num = 0; + handshaking = false; } Error HTTPClient::poll() { @@ -297,16 +329,40 @@ Error HTTPClient::poll() { } break; case StreamPeerTCP::STATUS_CONNECTED: { if (ssl) { - Ref ssl = StreamPeerSSL::create(); - Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); - if (err != OK) { + Ref ssl; + if (!handshaking) { + // Connect the StreamPeerSSL and start handshaking + ssl = Ref(StreamPeerSSL::create()); + ssl->set_blocking_handshake_enabled(false); + Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); + if (err != OK) { + close(); + status = STATUS_SSL_HANDSHAKE_ERROR; + return ERR_CANT_CONNECT; + } + connection = ssl; + handshaking = true; + } else { + // We are already handshaking, which means we can use your already active SSL connection + ssl = static_cast >(connection); + ssl->poll(); // Try to finish the handshake + } + + if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { + // Handshake has been successful + handshaking = false; + status = STATUS_CONNECTED; + return OK; + } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { + // Handshake has failed close(); status = STATUS_SSL_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - connection = ssl; + // ... we will need to poll more for handshake to finish + } else { + status = STATUS_CONNECTED; } - status = STATUS_CONNECTED; return OK; } break; case StreamPeerTCP::STATUS_ERROR: @@ -318,7 +374,20 @@ Error HTTPClient::poll() { } break; } } break; + case STATUS_BODY: case STATUS_CONNECTED: { + // Check if we are still connected + if (ssl) { + Ref 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; @@ -348,14 +417,21 @@ Error HTTPClient::poll() { String response; response.parse_utf8((const char *)response_str.ptr()); Vector responses = response.split("\n"); - body_size = 0; + body_size = -1; chunked = false; body_left = 0; chunk_left = 0; + read_until_eof = false; response_str.clear(); response_headers.clear(); response_num = RESPONSE_OK; + // Per the HTTP 1.1 spec, keep-alive is the default, but in practice + // it's safe to assume it only if the explicit header is found, allowing + // to handle body-up-to-EOF responses on naive servers; that's what Curl + // and browsers do + bool keep_alive = false; + for (int i = 0; i < responses.size(); i++) { String header = responses[i].strip_edges(); @@ -365,13 +441,14 @@ Error HTTPClient::poll() { if (s.begins_with("content-length:")) { body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int(); body_left = body_size; - } - if (s.begins_with("transfer-encoding:")) { + } else if (s.begins_with("transfer-encoding:")) { String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges(); if (encoding == "chunked") { chunked = true; } + } else if (s.begins_with("connection: keep-alive")) { + keep_alive = true; } if (i == 0 && responses[i].begins_with("HTTP")) { @@ -384,11 +461,16 @@ Error HTTPClient::poll() { } } - if (body_size == 0 && !chunked) { + if (body_size != -1 || chunked) { - status = STATUS_CONNECTED; // Ready for new requests - } else { status = STATUS_BODY; + } else if (!keep_alive) { + + read_until_eof = true; + status = STATUS_BODY; + } else { + + status = STATUS_CONNECTED; } return OK; } @@ -399,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: { @@ -484,7 +567,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } else { int rec = 0; - err = _get_http_data(&chunk[chunk.size() - chunk_left], chunk_left, rec); + err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec); if (rec == 0) { break; } @@ -515,34 +598,53 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } else { - int to_read = MIN(body_left, read_chunk_size); + int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size; PoolByteArray ret; ret.resize(to_read); int _offset = 0; - while (to_read > 0) { + while (read_until_eof || to_read > 0) { int rec = 0; { PoolByteArray::Write w = ret.write(); err = _get_http_data(w.ptr() + _offset, to_read, rec); } - if (rec > 0) { - body_left -= rec; - to_read -= rec; - _offset += rec; - } else { + if (rec < 0) { if (to_read > 0) // Ended up reading less ret.resize(_offset); break; + } else { + _offset += rec; + if (!read_until_eof) { + body_left -= rec; + to_read -= rec; + } else { + if (rec < to_read) { + ret.resize(_offset); + err = ERR_FILE_EOF; + break; + } + ret.resize(_offset + to_read); + } } } - if (body_left == 0) { - status = STATUS_CONNECTED; + if (!read_until_eof) { + if (body_left == 0) { + status = STATUS_CONNECTED; + } + return ret; + } else { + if (err == ERR_FILE_EOF) { + err = OK; // EOF is expected here + close(); + return ret; + } } - return ret; } if (err != OK) { + close(); + if (err == ERR_FILE_EOF) { status = STATUS_DISCONNECTED; // Server disconnected @@ -577,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); @@ -595,17 +710,19 @@ 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; chunk_left = 0; response_num = 0; ssl = false; blocking = false; + handshaking = false; read_chunk_size = 4096; } diff --git a/core/io/http_client.h b/core/io/http_client.h index 839012e70102..9e674e4c1da0 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 { @@ -165,6 +165,7 @@ class HTTPClient : public Reference { bool ssl; bool ssl_verify_host; bool blocking; + bool handshaking; Vector response_str; @@ -173,6 +174,7 @@ class HTTPClient : public Reference { int chunk_left; int body_size; int body_left; + bool read_until_eof; Ref tcp_connection; Ref connection; diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 999c9a8ca275..3ae9ff676ccd 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -30,14 +30,15 @@ #include "image_loader.h" -#include "print_string.h" +#include "core/print_string.h" + bool ImageFormatLoader::recognize(const String &p_extension) const { List extensions; get_recognized_extensions(&extensions); for (List::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(p_extension.get_extension()) == 0) + if (E->get().nocasecmp_to(p_extension) == 0) return true; } @@ -107,3 +108,82 @@ void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { ERR_FAIL_COND(loader_count >= MAX_LOADERS); loader[loader_count++] = p_loader; } + +///////////////// + +RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + return RES(); + } + + uint8_t header[4] = { 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + bool unrecognized = header[0] != 'G' || header[1] != 'D' || header[2] != 'I' || header[3] != 'M'; + if (unrecognized) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + String extension = f->get_pascal_string(); + + int idx = -1; + + for (int i = 0; i < ImageLoader::loader_count; i++) { + if (ImageLoader::loader[i]->recognize(extension)) { + idx = i; + break; + } + } + + if (idx == -1) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + Ref image; + image.instance(); + + Error err = ImageLoader::loader[idx]->load_image(image, f, false, 1.0); + + memdelete(f); + + if (err != OK) { + if (r_error) { + *r_error = err; + } + return RES(); + } + + if (r_error) { + *r_error = OK; + } + + return image; +} + +void ResourceFormatLoaderImage::get_recognized_extensions(List *p_extensions) const { + + p_extensions->push_back("image"); +} + +bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { + + return p_type == "Image"; +} + +String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { + + return p_path.get_extension().to_lower() == "image" ? "Image" : String(); +} diff --git a/core/io/image_loader.h b/core/io/image_loader.h index 052a8b8a4032..561f275e0c5a 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -31,10 +31,12 @@ #ifndef IMAGE_LOADER_H #define IMAGE_LOADER_H -#include "image.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 */ @@ -55,6 +57,7 @@ class ImageLoader; class ImageFormatLoader { friend class ImageLoader; + friend class ResourceFormatLoaderImage; protected: virtual Error load_image(Ref p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) = 0; @@ -70,7 +73,7 @@ class ImageLoader { enum { MAX_LOADERS = 8 }; - + friend class ResourceFormatLoaderImage; static ImageFormatLoader *loader[MAX_LOADERS]; static int loader_count; @@ -83,4 +86,12 @@ class ImageLoader { static void add_image_format_loader(ImageFormatLoader *p_loader); }; +class ResourceFormatLoaderImage : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + #endif diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 66bd96df4f34..f56e850796bb 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 d55b05b6feef..967f04a4bd07 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 d7b031b960d1..607ce81ef444 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 7b2c5a62dfa5..26e18828e538 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 9c1242379880..af6c463331a5 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 983b829d8d68..01755c8ee938 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++ 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); } @@ -112,7 +116,7 @@ void RotatedFileLogger::clear_old_backups() { int max_backups = max_files - 1; // -1 for the current file String basename = base_path.get_file().get_basename(); - String extension = "." + base_path.get_extension(); + String extension = base_path.get_extension(); DirAccess *da = DirAccess::open(base_path.get_base_dir()); if (!da) { @@ -123,7 +127,7 @@ void RotatedFileLogger::clear_old_backups() { String f = da->get_next(); Set backups; while (f != String()) { - if (!da->current_is_dir() && f.begins_with(basename) && f.ends_with(extension) && f != base_path.get_file()) { + if (!da->current_is_dir() && f.begins_with(basename) && f.get_extension() == extension && f != base_path.get_file()) { backups.insert(f); } f = da->get_next(); @@ -152,7 +156,10 @@ void RotatedFileLogger::rotate_file() { OS::Time time = OS::get_singleton()->get_time(); sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); - String backup_name = base_path.get_basename() + timestamp + "." + base_path.get_extension(); + String backup_name = base_path.get_basename() + timestamp; + if (base_path.get_extension() != String()) { + backup_name += "." + base_path.get_extension(); + } DirAccess *da = DirAccess::open(base_path.get_base_dir()); if (da) { diff --git a/core/io/logger.h b/core/io/logger.h index 631e7a058945..d32b43b0304e 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 class Logger { @@ -106,4 +107,4 @@ class CompositeLogger : public Logger { virtual ~CompositeLogger(); }; -#endif \ No newline at end of file +#endif diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 2ebe8d6df7e1..6338cee39d07 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -29,11 +29,18 @@ /*************************************************************************/ #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 #include +#define _S(a) ((int32_t)a) +#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) +#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) + void EncodedObjectAsID::_bind_methods() { ClassDB::bind_method(D_METHOD("set_object_id", "id"), &EncodedObjectAsID::set_object_id); ClassDB::bind_method(D_METHOD("get_object_id"), &EncodedObjectAsID::get_object_id); @@ -60,23 +67,31 @@ EncodedObjectAsID::EncodedObjectAsID() { static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); + int32_t strlen = decode_uint32(buf); + int32_t pad = 0; + + // Handle padding + if (strlen % 4) { + pad = 4 - strlen % 4; + } + buf += 4; len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_FILE_EOF); + + // Ensure buffer is big enough + ERR_FAIL_ADD_OF(strlen, pad, ERR_FILE_EOF); + ERR_FAIL_COND_V(strlen < 0 || strlen + pad > len, ERR_FILE_EOF); String str; - str.parse_utf8((const char *)buf, strlen); + ERR_FAIL_COND_V(str.parse_utf8((const char *)buf, strlen), ERR_INVALID_DATA); r_string = str; - //handle padding - if (strlen % 4) { - strlen += 4 - strlen % 4; - } + // Add padding + strlen += pad; + // Update buffer pos, left data count, and return size buf += strlen; len -= strlen; - if (r_len) { (*r_len) += 4 + strlen; } @@ -119,14 +134,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::INT: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); if (type & ENCODE_FLAG_64) { + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); int64_t val = decode_uint64(buf); r_variant = val; if (r_len) (*r_len) += 8; } else { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t val = decode_uint32(buf); r_variant = val; if (r_len) @@ -136,14 +152,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::REAL: { - ERR_FAIL_COND_V(len < (int)4, ERR_INVALID_DATA); - if (type & ENCODE_FLAG_64) { + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); double val = decode_double(buf); r_variant = val; if (r_len) (*r_len) += 8; } else { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); float val = decode_float(buf); r_variant = val; if (r_len) @@ -164,7 +180,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // math types case Variant::VECTOR2: { - ERR_FAIL_COND_V(len < (int)4 * 2, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA); Vector2 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -176,7 +192,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; // 5 case Variant::RECT2: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Rect2 val; val.position.x = decode_float(&buf[0]); val.position.y = decode_float(&buf[4]); @@ -190,7 +206,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::VECTOR3: { - ERR_FAIL_COND_V(len < (int)4 * 3, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA); Vector3 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -203,7 +219,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM2D: { - ERR_FAIL_COND_V(len < (int)4 * 6, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); Transform2D val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { @@ -220,7 +236,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PLANE: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Plane val; val.normal.x = decode_float(&buf[0]); val.normal.y = decode_float(&buf[4]); @@ -234,7 +250,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::QUAT: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Quat val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -248,7 +264,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::AABB: { - ERR_FAIL_COND_V(len < (int)4 * 6, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); AABB val; val.position.x = decode_float(&buf[0]); val.position.y = decode_float(&buf[4]); @@ -264,7 +280,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::BASIS: { - ERR_FAIL_COND_V(len < (int)4 * 9, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 9, ERR_INVALID_DATA); Basis val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -281,7 +297,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM: { - ERR_FAIL_COND_V(len < (int)4 * 12, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 12, ERR_INVALID_DATA); Transform val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -303,7 +319,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // misc types case Variant::COLOR: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Color val; val.r = decode_float(&buf[0]); val.g = decode_float(&buf[4]); @@ -318,7 +334,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::NODE_PATH: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); + int32_t strlen = decode_uint32(buf); if (strlen & 0x80000000) { //new format @@ -343,31 +359,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int for (uint32_t i = 0; i < total; i++) { - ERR_FAIL_COND_V((int)len < 4, ERR_INVALID_DATA); - strlen = decode_uint32(buf); - - int pad = 0; - - if (strlen % 4) - pad += 4 - strlen % 4; - - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen + pad > len, ERR_INVALID_DATA); - String str; - str.parse_utf8((const char *)buf, strlen); + Error err = _decode_string(buf, len, r_len, str); + if (err) + return err; if (i < namecount) names.push_back(str); else subnames.push_back(str); - - buf += strlen + pad; - len -= strlen + pad; - - if (r_len) - (*r_len) += 4 + strlen + pad; } r_variant = NodePath(names, subnames, flags & 1); @@ -375,17 +375,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } else { //old format, just a string - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_INVALID_DATA); - - String str; - str.parse_utf8((const char *)buf, strlen); - - r_variant = NodePath(str); - - if (r_len) - (*r_len) += 4 + strlen; + ERR_FAIL_V(ERR_INVALID_DATA); } } break; @@ -402,6 +392,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (type & ENCODE_FLAG_OBJECT_AS_ID) { //this _is_ allowed + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); ObjectID val = decode_uint64(buf); if (r_len) (*r_len) += 8; @@ -475,7 +466,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::DICTIONARY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -488,7 +479,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Dictionary d; - for (uint32_t i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { Variant key, value; @@ -520,7 +511,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -533,7 +524,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Array varr; - for (uint32_t i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { int used = 0; Variant v; @@ -555,17 +546,17 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_BYTE_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count > len, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count > len, ERR_INVALID_DATA); PoolVector data; if (count) { data.resize(count); PoolVector::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = buf[i]; } @@ -585,10 +576,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_INT_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); PoolVector data; @@ -596,7 +588,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int //const int*rbuf=(const int*)buf; data.resize(count); PoolVector::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = decode_uint32(&buf[i * 4]); } @@ -612,10 +604,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_REAL_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); PoolVector data; @@ -623,7 +616,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int //const float*rbuf=(const float*)buf; data.resize(count); PoolVector::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = decode_float(&buf[i * 4]); } @@ -640,7 +633,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_STRING_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); PoolVector strings; buf += 4; @@ -650,35 +643,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += 4; //printf("string count: %i\n",count); - for (int i = 0; i < (int)count; i++) { - - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); - - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_INVALID_DATA); + for (int32_t i = 0; i < count; i++) { - //printf("loaded string: %s\n",(const char*)buf); String str; - str.parse_utf8((const char *)buf, strlen); + Error err = _decode_string(buf, len, r_len, str); + if (err) + return err; strings.push_back(str); - - buf += strlen; - len -= strlen; - - if (r_len) - (*r_len) += 4 + strlen; - - if (strlen % 4) { - int pad = 4 - (strlen % 4); - buf += pad; - len -= pad; - if (r_len) { - (*r_len) += pad; - } - } } r_variant = strings; @@ -687,11 +659,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_VECTOR2_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 2 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 2, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 2 > len, ERR_INVALID_DATA); PoolVector varray; if (r_len) { @@ -702,7 +675,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int varray.resize(count); PoolVector::Write w = varray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].x = decode_float(buf + i * 4 * 2 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 2 + 4 * 1); @@ -722,11 +695,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_VECTOR3_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 3 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 3, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 3 > len, ERR_INVALID_DATA); + PoolVector varray; if (r_len) { @@ -737,7 +712,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int varray.resize(count); PoolVector::Write w = varray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].x = decode_float(buf + i * 4 * 3 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 3 + 4 * 1); @@ -758,11 +733,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_COLOR_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 4 > len, ERR_INVALID_DATA); + PoolVector carray; if (r_len) { @@ -773,7 +750,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int carray.resize(count); PoolVector::Write w = carray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].r = decode_float(buf + i * 4 * 4 + 4 * 0); w[i].g = decode_float(buf + i * 4 * 4 + 4 * 1); @@ -830,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; @@ -847,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) { @@ -872,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; @@ -890,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); } @@ -1211,7 +1186,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += len; if (buf) buf += len; - encode_variant(d[E->get()], buf, len, p_object_as_id); + Variant *v = d.getptr(E->get()); + ERR_FAIL_COND_V(!v, ERR_BUG); + encode_variant(*v, buf, len, p_object_as_id); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; if (buf) @@ -1321,7 +1298,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo while (r_len % 4) { r_len++; //pad if (buf) - buf++; + *(buf++) = 0; } } diff --git a/core/io/marshalls.h b/core/io/marshalls.h index 381e4e3d2068..1284520945aa 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 cbe7f87d9283..b3f0a76a80b2 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -1,7 +1,92 @@ -#include "core/io/multiplayer_api.h" +/*************************************************************************/ +/* multiplayer_api.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 "multiplayer_api.h" + #include "core/io/marshalls.h" #include "scene/main/node.h" +_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { + + switch (mode) { + + case MultiplayerAPI::RPC_MODE_DISABLED: { + // Do nothing. + } break; + case MultiplayerAPI::RPC_MODE_REMOTE: { + // 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_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; // I am the master, so skip remote call. + return is_master; + } break; + case MultiplayerAPI::RPC_MODE_PUPPET: { + return !is_master; + } break; + } + return false; +} + +_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { + switch (mode) { + + case MultiplayerAPI::RPC_MODE_DISABLED: { + return false; + } break; + 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_PUPPETSYNC: + case MultiplayerAPI::RPC_MODE_PUPPET: { + return !p_node->is_network_master() && p_remote_id == p_node->get_network_master(); + } break; + } + + return false; +} + void MultiplayerAPI::poll() { if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) @@ -9,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()) { @@ -28,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. } } } @@ -47,11 +132,11 @@ void MultiplayerAPI::set_root_node(Node *p_node) { void MultiplayerAPI::set_network_peer(const Ref &p_peer) { if (network_peer.is_valid()) { - network_peer->disconnect("peer_connected", this, "add_peer"); - network_peer->disconnect("peer_disconnected", this, "del_peer"); - network_peer->disconnect("connection_succeeded", this, "connected_to_server"); - network_peer->disconnect("connection_failed", this, "connection_failed"); - network_peer->disconnect("server_disconnected", this, "server_disconnected"); + network_peer->disconnect("peer_connected", this, "_add_peer"); + network_peer->disconnect("peer_disconnected", this, "_del_peer"); + network_peer->disconnect("connection_succeeded", this, "_connected_to_server"); + network_peer->disconnect("connection_failed", this, "_connection_failed"); + network_peer->disconnect("server_disconnected", this, "_server_disconnected"); clear(); } @@ -61,11 +146,11 @@ void MultiplayerAPI::set_network_peer(const Ref &p_pee ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED); if (network_peer.is_valid()) { - network_peer->connect("peer_connected", this, "add_peer"); - network_peer->connect("peer_disconnected", this, "del_peer"); - network_peer->connect("connection_succeeded", this, "connected_to_server"); - network_peer->connect("connection_failed", this, "connection_failed"); - network_peer->connect("server_disconnected", this, "server_disconnected"); + network_peer->connect("peer_connected", this, "_add_peer"); + network_peer->connect("peer_disconnected", this, "_del_peer"); + network_peer->connect("connection_succeeded", this, "_connected_to_server"); + network_peer->connect("connection_failed", this, "_connection_failed"); + network_peer->connect("server_disconnected", this, "_server_disconnected"); } } @@ -75,8 +160,10 @@ Ref 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_FAIL_COND(p_packet_len < 5); + ERR_EXPLAIN("Invalid packet received. Size too small."); + ERR_FAIL_COND(p_packet_len < 1); uint8_t packet_type = p_packet[0]; @@ -95,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) { @@ -109,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]); @@ -123,6 +213,11 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } } break; + + case NETWORK_COMMAND_RAW: { + + _process_raw(p_from, p_packet, p_packet_len); + } break; } } @@ -132,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; @@ -147,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::Element *E = path_get_cache.find(p_from); + ERR_EXPLAIN("Invalid packet received. Requests invalid peer cache."); ERR_FAIL_COND_V(!E, NULL); Map::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) @@ -167,11 +266,22 @@ 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) { - if (!p_node->can_call_rpc(p_name, p_from)) - return; + 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. + RPCMode rpc_mode = RPC_MODE_DISABLED; + const Map::Element *E = p_node->get_node_rpc_mode(p_name); + if (E) { + rpc_mode = E->get(); + } 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]; Vector args; Vector argp; @@ -182,12 +292,15 @@ 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[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + ERR_EXPLAIN("Invalid packet received. Unable to decode RPC argument."); ERR_FAIL_COND(err != OK); - //args[i]=p_packet[3+i]; - argp[i] = &args[i]; + + argp.write[i] = &args[i]; p_offset += vlen; } @@ -203,13 +316,26 @@ 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) { - if (!p_node->can_call_rset(p_name, p_from)) - return; - + 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. + RPCMode rset_mode = RPC_MODE_DISABLED; + const Map::Element *E = p_node->get_node_rset_mode(p_name); + if (E) { + rset_mode = E->get(); + } 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; @@ -222,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]); @@ -240,17 +367,15 @@ 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); Vector packet; packet.resize(1 + len); - packet[0] = NETWORK_COMMAND_CONFIRM_PATH; - encode_cstring(pname.get_data(), &packet[1]); + packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; + encode_cstring(pname.get_data(), &packet.write[1]); network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_target_peer(p_from); @@ -259,37 +384,42 @@ 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; paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1); 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::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 peers_to_add; //if one is missing, take note to add it + List peers_to_add; // If one is missing, take note to add it. for (Set::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::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()); } @@ -297,26 +427,26 @@ 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::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); Vector packet; packet.resize(1 + 4 + len); - packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH; - encode_uint32(psc->id, &packet[1]); - encode_cstring(pname.get_data(), &packet[5]); + packet.write[0] = NETWORK_COMMAND_SIMPLIFY_PATH; + encode_uint32(psc->id, &packet.write[1]); + encode_cstring(pname.get_data(), &packet.write[5]); - network_peer->set_target_peer(E->get()); //to all of you + network_peer->set_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; @@ -355,191 +485,147 @@ 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[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; ofs += 1; - //encode ID + // Encode ID. MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &(packet_cache[ofs])); + encode_uint32(psc->id, &(packet_cache.write[ofs])); ofs += 4; - //encode function name + // Encode function name. CharString name = String(p_name).utf8(); int len = encode_cstring(name.get_data(), NULL); MAKE_ROOM(ofs + len); - encode_cstring(name.get_data(), &(packet_cache[ofs])); + encode_cstring(name.get_data(), &(packet_cache.write[ofs])); ofs += len; if (p_set) { - //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[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[ofs] = p_argcount; + packet_cache.write[ofs] = p_argcount; ofs += 1; for (int i = 0; i < p_argcount; i++) { Error err = encode_variant(*p_arg[i], NULL, len); + ERR_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[ofs]), len); + encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len); ofs += len; } } - //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); - encode_cstring(pname.get_data(), &(packet_cache[ofs])); + encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); for (Set::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::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 - encode_uint32(psc->id, &(packet_cache[1])); + 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[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); } } } } -void MultiplayerAPI::add_peer(int p_id) { +void MultiplayerAPI::_add_peer(int p_id) { connected_peers.insert(p_id); path_get_cache.insert(p_id, PathGetCache()); emit_signal("network_peer_connected", p_id); } -void MultiplayerAPI::del_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); } -void MultiplayerAPI::connected_to_server() { +void MultiplayerAPI::_connected_to_server() { emit_signal("connected_to_server"); } -void MultiplayerAPI::connection_failed() { +void MultiplayerAPI::_connection_failed() { emit_signal("connection_failed"); } -void MultiplayerAPI::server_disconnected() { +void MultiplayerAPI::_server_disconnected() { emit_signal("server_disconnected"); } -bool _should_call_native(Node::RPCMode mode, bool is_master, bool &r_skip_rpc) { - - switch (mode) { - - case Node::RPC_MODE_DISABLED: { - //do nothing - } break; - case Node::RPC_MODE_REMOTE: { - //do nothing also, no need to call local - } break; - case Node::RPC_MODE_SYNC: { - //call it, sync always results in call - return true; - } break; - case Node::RPC_MODE_MASTER: { - if (is_master) - r_skip_rpc = true; //no other master so.. - return is_master; - } break; - case Node::RPC_MODE_SLAVE: { - return !is_master; - } break; - } - return false; -} - -bool _should_call_script(ScriptInstance::RPCMode mode, bool is_master, bool &r_skip_rpc) { - switch (mode) { - - case ScriptInstance::RPC_MODE_DISABLED: { - //do nothing - } break; - case ScriptInstance::RPC_MODE_REMOTE: { - //do nothing also, no need to call local - } break; - case ScriptInstance::RPC_MODE_SYNC: { - //call it, sync always results in call - return true; - } break; - case ScriptInstance::RPC_MODE_MASTER: { - if (is_master) - r_skip_rpc = true; //no other master so.. - return is_master; - } break; - case ScriptInstance::RPC_MODE_SLAVE: { - return !is_master; - } break; - } - return false; -} - 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; @@ -548,19 +634,19 @@ 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::Element *E = p_node->get_node_rpc_mode(p_method); + const Map::Element *E = p_node->get_node_rpc_mode(p_method); if (E) { - call_local_native = _should_call_native(E->get(), is_master, skip_rpc); + call_local_native = _should_call_local(E->get(), is_master, skip_rpc); } if (call_local_native) { - // done below + // Done below. } else if (p_node->get_script_instance()) { - //attempt with script - ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); - call_local_script = _should_call_script(rpc_mode, is_master, skip_rpc); + // 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); } } @@ -594,22 +680,26 @@ 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; - const Map::Element *E = p_node->get_node_rset_mode(p_property); + const Map::Element *E = p_node->get_node_rset_mode(p_property); if (E) { - set_local = _should_call_native(E->get(), is_master, skip_rset); + set_local = _should_call_local(E->get(), is_master, skip_rset); } if (set_local) { @@ -622,10 +712,10 @@ 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 - ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); + // Attempt with script. + RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); - set_local = _should_call_script(rpc_mode, is_master, skip_rset); + set_local = _should_call_local(rpc_mode, is_master, skip_rset); if (set_local) { @@ -648,32 +738,73 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1); } +Error MultiplayerAPI::send_bytes(PoolVector 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); + PoolVector::Read r = p_data.read(); + packet_cache.write[0] = NETWORK_COMMAND_RAW; + memcpy(&packet_cache.write[1], &r[0], p_data.size()); + + network_peer->set_target_peer(p_to); + network_peer->set_transfer_mode(p_mode); + + return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); +} + +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 out; + int len = p_packet_len - 1; + out.resize(len); + { + PoolVector::Write w = out.write(); + memcpy(&w[0], &p_packet[1], len); + } + emit_signal("network_peer_packet", p_from, out); +} + 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 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()); Vector ret; @@ -686,20 +817,21 @@ Vector MultiplayerAPI::get_network_connected_peers() const { void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); + ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server); ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id); - ClassDB::bind_method(D_METHOD("add_peer", "id"), &MultiplayerAPI::add_peer); - ClassDB::bind_method(D_METHOD("del_peer", "id"), &MultiplayerAPI::del_peer); + ClassDB::bind_method(D_METHOD("_add_peer", "id"), &MultiplayerAPI::_add_peer); + ClassDB::bind_method(D_METHOD("_del_peer", "id"), &MultiplayerAPI::_del_peer); ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer); ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear); - ClassDB::bind_method(D_METHOD("connected_to_server"), &MultiplayerAPI::connected_to_server); - ClassDB::bind_method(D_METHOD("connection_failed"), &MultiplayerAPI::connection_failed); - ClassDB::bind_method(D_METHOD("server_disconnected"), &MultiplayerAPI::server_disconnected); + ClassDB::bind_method(D_METHOD("_connected_to_server"), &MultiplayerAPI::_connected_to_server); + ClassDB::bind_method(D_METHOD("_connection_failed"), &MultiplayerAPI::_connection_failed); + ClassDB::bind_method(D_METHOD("_server_disconnected"), &MultiplayerAPI::_server_disconnected); ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers); ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections); ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); @@ -708,9 +840,20 @@ void MultiplayerAPI::_bind_methods() { ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::POOL_BYTE_ARRAY, "packet"))); ADD_SIGNAL(MethodInfo("connected_to_server")); ADD_SIGNAL(MethodInfo("connection_failed")); ADD_SIGNAL(MethodInfo("server_disconnected")); + + BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); + BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); + BIND_ENUM_CONSTANT(RPC_MODE_MASTER); + 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_PUPPETSYNC); } MultiplayerAPI::MultiplayerAPI() { diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index e7c6ffbea63e..c86e76e91ad4 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* multiplayer_api.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 MULTIPLAYER_PROTOCOL_H #define MULTIPLAYER_PROTOCOL_H @@ -43,6 +73,7 @@ class MultiplayerAPI : public Reference { Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount); bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_from); @@ -53,6 +84,20 @@ class MultiplayerAPI : public Reference { NETWORK_COMMAND_REMOTE_SET, NETWORK_COMMAND_SIMPLIFY_PATH, NETWORK_COMMAND_CONFIRM_PATH, + NETWORK_COMMAND_RAW, + }; + + enum RPCMode { + + 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_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote + 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_PUPPETSYNC, // Using rpc() on it will call method / set property in all puppets peers and locally }; void poll(); @@ -60,17 +105,18 @@ class MultiplayerAPI : public Reference { void set_root_node(Node *p_node); void set_network_peer(const Ref &p_peer); Ref get_network_peer() const; + Error send_bytes(PoolVector p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer::TransferMode p_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); // Called by Node.rpc void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); // Called by Node.rset void rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value); - void add_peer(int p_id); - void del_peer(int p_id); - void connected_to_server(); - void connection_failed(); - void server_disconnected(); + void _add_peer(int p_id); + void _del_peer(int p_id); + void _connected_to_server(); + void _connection_failed(); + void _server_disconnected(); bool has_network_peer() const { return network_peer.is_valid(); } Vector get_network_connected_peers() const; @@ -84,4 +130,6 @@ class MultiplayerAPI : public Reference { ~MultiplayerAPI(); }; +VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode); + #endif // MULTIPLAYER_PROTOCOL_H diff --git a/core/io/net_socket.cpp b/core/io/net_socket.cpp new file mode 100644 index 000000000000..10bcf62eda38 --- /dev/null +++ b/core/io/net_socket.cpp @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* net_socket.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.h" + +NetSocket *(*NetSocket::_create)() = NULL; + +NetSocket *NetSocket::create() { + + if (_create) + return _create(); + + ERR_PRINT("Unable to create network socket, platform not supported"); + return NULL; +} diff --git a/core/io/net_socket.h b/core/io/net_socket.h new file mode 100644 index 000000000000..0665bec9fd11 --- /dev/null +++ b/core/io/net_socket.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* net_socket.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 NET_SOCKET_H +#define NET_SOCKET_H + +#include "core/io/ip.h" +#include "core/reference.h" + +class NetSocket : public Reference { + +protected: + static NetSocket *(*_create)(); + +public: + 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 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 // NET_SOCKET_H diff --git a/core/io/networked_multiplayer_peer.h b/core/io/networked_multiplayer_peer.h index 66089c27b9fa..8e83c528a064 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 bd851ebb6d79..b6dd4eaf6fdf 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() { @@ -172,7 +173,8 @@ Error PacketPeerStream::_poll_buffer() const { ERR_FAIL_COND_V(peer.is_null(), ERR_UNCONFIGURED); int read = 0; - Error err = peer->get_partial_data(&input_buffer[0], ring_buffer.space_left(), read); + ERR_FAIL_COND_V(input_buffer.size() < ring_buffer.space_left(), ERR_UNAVAILABLE); + Error err = peer->get_partial_data(input_buffer.ptrw(), ring_buffer.space_left(), read); if (err) return err; if (read == 0) @@ -223,8 +225,9 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) uint32_t len = decode_uint32(lbuf); ERR_FAIL_COND_V(remaining < (int)len, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(input_buffer.size() < len, ERR_UNAVAILABLE); ring_buffer.read(lbuf, 4); //get rid of first 4 bytes - ring_buffer.read(&input_buffer[0], len); // read packet + ring_buffer.read(input_buffer.ptrw(), len); // read packet *r_buffer = &input_buffer[0]; r_buffer_size = len; @@ -245,8 +248,8 @@ Error PacketPeerStream::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(p_buffer_size < 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_buffer_size + 4 > output_buffer.size(), ERR_INVALID_PARAMETER); - encode_uint32(p_buffer_size, &output_buffer[0]); - uint8_t *dst = &output_buffer[4]; + encode_uint32(p_buffer_size, output_buffer.ptrw()); + uint8_t *dst = &output_buffer.write[4]; for (int i = 0; i < p_buffer_size; i++) dst[i] = p_buffer[i]; diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index b10152e96be3..a6559df46044 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 bfbea15582a7..d33ba6f85528 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(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::create_ref() { - - if (!_create) - return Ref(); - return Ref(_create()); -} - -PacketPeerUDP *PacketPeerUDP::create() { +PacketPeerUDP::PacketPeerUDP() { - if (!_create) - return NULL; - return _create(); + _sock = Ref(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 035f4ad1c975..4366b0eb8292 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 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 _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 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 596060221ecc..3df8c01760b0 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -31,6 +31,7 @@ #include "pck_packer.h" #include "core/os/file_access.h" +#include "core/version.h" static uint64_t _align(uint64_t p_n, int p_alignment) { @@ -70,9 +71,9 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment) { alignment = p_alignment; file->store_32(0x43504447); // MAGIC - file->store_32(0); // # version - file->store_32(0); // # major - file->store_32(0); // # minor + file->store_32(1); // # version + file->store_32(VERSION_MAJOR); // # major + file->store_32(VERSION_MINOR); // # minor file->store_32(0); // # revision for (int i = 0; i < 16; i++) { @@ -120,7 +121,7 @@ Error PCKPacker::flush(bool p_verbose) { for (int i = 0; i < files.size(); i++) { file->store_pascal_string(files[i].path); - files[i].offset_offset = file->get_position(); + files.write[i].offset_offset = file->get_position(); file->store_64(0); // offset file->store_64(files[i].size); // size diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 0c626c197b42..6f3a8c3d2e97 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -894,7 +894,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { for (uint32_t i = 0; i < string_table_size; i++) { StringName s = get_unicode_string(); - string_map[i] = s; + string_map.write[i] = s; } print_bl("strings: " + itos(string_table_size)); @@ -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); } @@ -1813,8 +1813,13 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p Property p; p.name_idx = get_string_index(F->get().name); p.value = E->get()->get(F->get().name); - if (((F->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && p.value.is_zero()) || ((F->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && p.value.is_one())) + + Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name); + + if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, p.value, default_value))) { continue; + } + p.pi = F->get(); rd.properties.push_back(p); @@ -1834,7 +1839,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p save_order.resize(external_resources.size()); for (Map::Element *E = external_resources.front(); E; E = E->next()) { - save_order[E->get()] = E->key(); + save_order.write[E->get()] = E->key(); } for (int i = 0; i < save_order.size(); i++) { diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 021f7f6a2f54..513252055f79 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 resource_set; diff --git a/core/io/resource_import.cpp b/core/io/resource_import.cpp index cfe6655504e2..ffea43b6bffe 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 { diff --git a/core/io/resource_import.h b/core/io/resource_import.h index 80e0743edaee..53718bd789f9 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 1351030d1ec7..71b01aa94a3e 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; @@ -123,6 +125,10 @@ Ref ResourceFormatLoader::load_interactive(const Stri return ril; } +bool ResourceFormatLoader::exists(const String &p_path) const { + return FileAccess::exists(p_path); //by default just check file +} + RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error) { String path = p_path; @@ -198,13 +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(); + } - if (OS::get_singleton()->is_stdout_verbose()) - print_line("load 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; @@ -212,9 +237,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p ERR_FAIL_COND_V(path == "", RES()); - if (OS::get_singleton()->is_stdout_verbose()) - print_line("load resource: " + path); - + print_verbose("Loading resource: " + path); RES res = _load(path, local_path, p_type_hint, p_no_cache, r_error); if (res.is_null()) { @@ -236,9 +259,43 @@ 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; } +bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { + + String local_path; + if (p_path.is_rel_path()) + local_path = "res://" + p_path; + else + local_path = ProjectSettings::get_singleton()->localize_path(p_path); + + if (ResourceCache::has(local_path)) { + + return true; // If cached, it probably exists + } + + bool xl_remapped = false; + String path = _path_remap(local_path, &xl_remapped); + + // Try all loaders and pick the first match for the type hint + for (int i = 0; i < loader_count; i++) { + + if (!loader[i]->recognize_path(path, p_type_hint)) { + continue; + } + + if (loader[i]->exists(path)) + return true; + } + + return false; +} + Ref ResourceLoader::load_interactive(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { if (r_error) @@ -252,9 +309,7 @@ Ref ResourceLoader::load_interactive(const String &p_ if (!p_no_cache && ResourceCache::has(local_path)) { - if (OS::get_singleton()->is_stdout_verbose()) - print_line("load resource: " + local_path + " (cached)"); - + print_verbose("Loading resource: " + local_path + " (cached)"); Ref res_cached = ResourceCache::get(local_path); Ref ril = Ref(memnew(ResourceInteractiveLoaderDefault)); @@ -264,14 +319,10 @@ Ref ResourceLoader::load_interactive(const String &p_ bool xl_remapped = false; String path = _path_remap(local_path, &xl_remapped); - ERR_FAIL_COND_V(path == "", Ref()); - - if (OS::get_singleton()->is_stdout_verbose()) - print_line("load resource: "); + print_verbose("Loading resource: " + path); bool found = false; - for (int i = 0; i < loader_count; i++) { if (!loader[i]->recognize_path(path, p_type_hint)) @@ -556,7 +607,7 @@ void ResourceLoader::load_translation_remaps() { Vector lang_remaps; lang_remaps.resize(langs.size()); for (int i = 0; i < langs.size(); i++) { - lang_remaps[i] = langs[i]; + lang_remaps.write[i] = langs[i]; } translation_remaps[String(E->get())] = lang_remaps; @@ -588,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; @@ -600,3 +657,5 @@ bool ResourceLoader::timestamp_on_load = false; SelfList::List ResourceLoader::remapped_list; HashMap > ResourceLoader::translation_remaps; HashMap ResourceLoader::path_remaps; + +ResourceLoaderImport ResourceLoader::import = NULL; diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 9be82abb4200..7ade4a2dfcc0 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 @@ -60,6 +60,7 @@ class ResourceFormatLoader { public: virtual Ref load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual bool exists(const String &p_path) const; virtual void get_recognized_extensions(List *p_extensions) const = 0; virtual void get_recognized_extensions_for_type(const String &p_type, List *p_extensions) const; virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const; @@ -76,6 +77,9 @@ class ResourceFormatLoader { 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 { @@ -103,9 +107,12 @@ 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 load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); static RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); + static bool exists(const String &p_path, const String &p_type_hint = ""); static void get_recognized_extensions_for_type(const String &p_type, List *p_extensions); static void add_resource_format_loader(ResourceFormatLoader *p_format_loader, bool p_at_front = false); @@ -145,6 +152,9 @@ class ResourceLoader { 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 609dd7e93caf..097e81e30899 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]; @@ -56,7 +57,7 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t for (List::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(extension.get_extension()) == 0) + if (E->get().nocasecmp_to(extension) == 0) recognized = true; } @@ -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 396f37d414cf..6134d9db5760 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 @@ -46,7 +46,7 @@ class ResourceFormatSaver { virtual ~ResourceFormatSaver() {} }; -typedef void (*ResourceSavedCallback)(const String &p_path); +typedef void (*ResourceSavedCallback)(Ref p_resource, const String &p_path); class ResourceSaver { diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 927b9f6366ec..3f608b720cdd 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 &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 buf; @@ -331,11 +340,13 @@ String StreamPeer::get_string(int p_bytes) { ERR_FAIL_COND_V(err != OK, String()); err = get_data((uint8_t *)&buf[0], p_bytes); ERR_FAIL_COND_V(err != OK, String()); - buf[p_bytes] = 0; + buf.write[p_bytes] = 0; return buf.ptr(); } String StreamPeer::get_utf8_string(int p_bytes) { + if (p_bytes < 0) + p_bytes = get_u32(); ERR_FAIL_COND_V(p_bytes < 0, String()); Vector 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 605b0a7980f3..f189960cbddc 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 @@ class StreamPeer : public Reference { 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 @@ class StreamPeer : public Reference { 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 012ba78c6d32..138f91301eef 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,17 +44,51 @@ 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; } +void StreamPeerSSL::set_blocking_handshake_enabled(bool p_enabled) { + blocking_handshake = p_enabled; +} + +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; @@ -59,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_line("Loaded certs from '" + certs_path); + print_verbose("Loaded builtin certs"); #endif - } } +#endif return out; } @@ -84,16 +118,22 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); - ClassDB::bind_method(D_METHOD("accept_stream", "stream"), &StreamPeerSSL::accept_stream); + ClassDB::bind_method(D_METHOD("accept_stream", "base"), &StreamPeerSSL::accept_stream); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); + ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled); + ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerSSL::is_blocking_handshake_enabled); + + 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_NO_CERTIFICATE); + BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); } StreamPeerSSL::StreamPeerSSL() { + blocking_handshake = true; } diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 77301a7c8745..8ce36d7e7df4 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,17 +46,20 @@ class StreamPeerSSL : public StreamPeer { static LoadCertsFromMemory load_certs_func; static bool available; - friend class Main; - static bool initialize_certs; + bool blocking_handshake; public: enum Status { STATUS_DISCONNECTED, + STATUS_HANDSHAKING, STATUS_CONNECTED, - STATUS_ERROR_NO_CERTIFICATE, + STATUS_ERROR, STATUS_ERROR_HOSTNAME_MISMATCH }; + void set_blocking_handshake_enabled(bool p_enabled); + bool is_blocking_handshake_enabled() const; + virtual void poll() = 0; virtual Error accept_stream(Ref p_base) = 0; virtual Error connect_to_stream(Ref p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0; @@ -66,7 +69,9 @@ class StreamPeerSSL : public StreamPeer { 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 5d008904ffe1..28561e8cbc00 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 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::create_ref() { +StreamPeerTCP::StreamPeerTCP() { - if (!_create) - return Ref(); - return Ref(_create()); + _sock = Ref(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 8a16d820f287..de364915cd51 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 @@ class StreamPeerTCP : public StreamPeer { }; protected: - virtual Error _connect(const String &p_address, int p_port); - static StreamPeerTCP *(*_create)(); + Ref _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 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 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 5916d58390b6..b8194cb17f2a 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::create_ref() { + err = _sock->listen(MAX_PENDING_CONNECTIONS); - if (!_create) - return NULL; - return Ref(_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 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 conn; + if (!is_connection_available()) { + return conn; + } + + Ref ns; + IP_Address ip; + uint16_t port = 0; + ns = _sock->accept(ip, port); + if (!ns.is_valid()) + return conn; + + conn = Ref(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::create()); +} + +TCP_Server::~TCP_Server() { + + stop(); } diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index a250e8b24937..4c89197d9aa4 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 _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 take_connection() = 0; + Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + bool is_connection_available() const; + Ref take_connection(); - virtual void stop() = 0; //stop listening - - static Ref 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 e01e2a84c543..830c2b6694ce 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) { @@ -54,32 +55,25 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S int line = 1; bool skip_this = false; bool skip_next = false; + bool is_eof = false; - while (true) { - - String l = f->get_line(); + while (!is_eof) { - if (f->eof_reached()) { + String l = f->get_line().strip_edges(); + is_eof = f->eof_reached(); - if (status == STATUS_READING_STRING) { - - if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") - config = msg_str; - break; + // If we reached last line and it's not a content line, break, otherwise let processing that last loop + if (is_eof && l.empty()) { - } else if (status == STATUS_NONE) + if (status == STATUS_READING_ID) { + memdelete(f); + ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V(RES()); + } else { break; - - memdelete(f); - ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); - ERR_FAIL_V(RES()); + } } - l = l.strip_edges(); - if (l.begins_with("msgid")) { if (status == STATUS_READING_ID) { @@ -160,6 +154,15 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S f->close(); memdelete(f); + if (status == STATUS_READING_STRING) { + + if (msg_id != "") { + if (!skip_this) + translation->add_message(msg_id, msg_str); + } else if (config == "") + config = msg_str; + } + if (config == "") { ERR_EXPLAIN("No config found in file: " + p_path); ERR_FAIL_V(RES()); @@ -175,7 +178,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S String prop = c.substr(0, p).strip_edges(); String value = c.substr(p + 1, c.length()).strip_edges(); - if (prop == "X-Language") { + if (prop == "X-Language" || prop == "Language") { translation->set_locale(value); } } diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 33cf9bd8b4da..670a9fdd7ee7 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 33c9b56d5afe..8c0cbd6e55c1 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 297b57ffdc19..bbc7671a0e93 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 000000000000..b7f841b66f8d --- /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 3a7fdb030208..bba7d673320c 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 f977df46346f..281f253a5917 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/make_binders.py b/core/make_binders.py index 6a7602cd5d8f..4c61b90d99e6 100644 --- a/core/make_binders.py +++ b/core/make_binders.py @@ -1,6 +1,5 @@ # -*- coding: ibm850 -*- - template_typed = """ #ifdef TYPED_METHOD_BIND template @@ -265,8 +264,13 @@ def run(target, source, env): else: text += t - with open(target[0].path, "w") as f: + with open(target[0], "w") as f: f.write(text) - with open(target[1].path, "w") as f: + with open(target[1], "w") as f: f.write(text_ext) + + +if __name__ == '__main__': + from platform_methods import subprocess_main + subprocess_main(globals()) diff --git a/core/map.h b/core/map.h index 700d4b8693da..cab8965bb38b 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 diff --git a/core/math/SCsub b/core/math/SCsub index 4efc90271768..1c5f954470dc 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 6908d7831daf..451c45cadec4 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 { @@ -96,11 +97,11 @@ void AStar::remove_point(int p_id) { Point *p = points[p_id]; - for (int i = 0; i < p->neighbours.size(); i++) { + for (Set::Element *E = p->neighbours.front(); E; E = E->next()) { - Segment s(p_id, p->neighbours[i]->id); + Segment s(p_id, E->get()->id); segments.erase(s); - p->neighbours[i]->neighbours.erase(p); + E->get()->neighbours.erase(p); } memdelete(p); @@ -115,10 +116,10 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { Point *a = points[p_id]; Point *b = points[p_with_id]; - a->neighbours.push_back(b); + a->neighbours.insert(b); if (bidirectional) - b->neighbours.push_back(a); + b->neighbours.insert(a); Segment s(p_id, p_with_id); if (s.from == p_id) { @@ -168,8 +169,8 @@ PoolVector AStar::get_point_connections(int p_id) { Point *p = points[p_id]; - for (int i = 0; i < p->neighbours.size(); i++) { - point_list.push_back(p->neighbours[i]->id); + for (Set::Element *E = p->neighbours.front(); E; E = E->next()) { + point_list.push_back(E->get()->id); } return point_list; @@ -242,21 +243,16 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { bool found_route = false; - for (int i = 0; i < begin_point->neighbours.size(); i++) { + for (Set::Element *E = begin_point->neighbours.front(); E; E = E->next()) { - Point *n = begin_point->neighbours[i]; + Point *n = E->get(); n->prev_point = begin_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,19 +272,20 @@ 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(); - // Open the neighbours for search - int es = p->neighbours.size(); + if (p == end_point) { + found_route = true; + break; + } - for (int i = 0; i < es; i++) { + for (Set::Element *E = p->neighbours.front(); E; E = E->next()) { - Point *e = p->neighbours[i]; + Point *e = E->get(); real_t distance = _compute_cost(p->id, e->id) * e->weight_scale + p->distance; @@ -296,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; } @@ -307,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 f89e17c7bbe4..d2ef76500683 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 @@ -54,7 +55,7 @@ class AStar : public Reference { real_t weight_scale; uint64_t last_pass; - Vector neighbours; + Set neighbours; // Used for pathfinding Point *prev_point; diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index cff19f990cd1..d0cb2b519517 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 { @@ -245,7 +245,6 @@ Vector3 AABB::get_longest_axis() const { if (size.z > max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -262,7 +261,6 @@ int AABB::get_longest_axis_index() const { if (size.z > max_size) { axis = 2; - max_size = size.z; } return axis; @@ -280,7 +278,6 @@ Vector3 AABB::get_shortest_axis() const { if (size.z < max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -297,7 +294,6 @@ int AABB::get_shortest_axis_index() const { if (size.z < max_size) { axis = 2; - max_size = size.z; } return axis; diff --git a/core/math/aabb.h b/core/math/aabb.h index 39b8f403e71d..0b03b7d3146b 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) @@ -76,6 +76,7 @@ class AABB { _FORCE_INLINE_ bool smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const; _FORCE_INLINE_ bool intersects_convex_shape(const Plane *p_planes, int p_plane_count) const; + _FORCE_INLINE_ bool inside_convex_shape(const Plane *p_planes, int p_plane_count) const; bool intersects_plane(const Plane &p_plane) const; _FORCE_INLINE_ bool has_point(const Vector3 &p_point) const; @@ -207,6 +208,25 @@ bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count) con return true; } +bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const { + + Vector3 half_extents = size * 0.5; + Vector3 ofs = position + half_extents; + + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + Vector3 point( + (p.normal.x < 0) ? -half_extents.x : half_extents.x, + (p.normal.y < 0) ? -half_extents.y : half_extents.y, + (p.normal.z < 0) ? -half_extents.z : half_extents.z); + point += ofs; + if (p.is_point_over(point)) + return false; + } + + return true; +} + bool AABB::has_point(const Vector3 &p_point) const { if (p_point.x < position.x) diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index 67ba025e1ce2..fde26e8056bf 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 b1424e1d786b..6ffc9637835c 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 @@ -244,10 +244,8 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { const Node *nodesptr = &nodes[0]; const Plane *planesptr = &planes[0]; - int plane_count = planes.size(); int idx = node_count - 1; - int steps = 0; while (true) { @@ -259,21 +257,19 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { return true; } - uint16_t plane = nodesptr[idx].plane; #ifdef DEBUG_ENABLED - + int plane_count = planes.size(); + uint16_t plane = nodesptr[idx].plane; ERR_FAIL_INDEX_V(plane, plane_count, false); #endif + bool over = planesptr[nodesptr[idx].plane].is_point_over(p_point); idx = over ? nodes[idx].over : nodes[idx].under; #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, false); #endif - - steps++; } return false; @@ -453,10 +449,10 @@ BSP_Tree::operator Variant() const { for (int i = 0; i < planes.size(); i++) { - plane_values[i * 4 + 0] = planes[i].normal.x; - plane_values[i * 4 + 1] = planes[i].normal.y; - plane_values[i * 4 + 2] = planes[i].normal.z; - plane_values[i * 4 + 3] = planes[i].d; + plane_values.write[i * 4 + 0] = planes[i].normal.x; + plane_values.write[i * 4 + 1] = planes[i].normal.y; + plane_values.write[i * 4 + 2] = planes[i].normal.z; + plane_values.write[i * 4 + 3] = planes[i].d; } d["planes"] = plane_values; @@ -502,10 +498,10 @@ BSP_Tree::BSP_Tree(const Variant &p_variant) { PoolVector::Read r = src_planes.read(); for (int i = 0; i < plane_count / 4; i++) { - planes[i].normal.x = r[i * 4 + 0]; - planes[i].normal.y = r[i * 4 + 1]; - planes[i].normal.z = r[i * 4 + 2]; - planes[i].d = r[i * 4 + 3]; + planes.write[i].normal.x = r[i * 4 + 0]; + planes.write[i].normal.y = r[i * 4 + 1]; + planes.write[i].normal.z = r[i * 4 + 2]; + planes.write[i].d = r[i * 4 + 3]; } } @@ -524,9 +520,9 @@ BSP_Tree::BSP_Tree(const Variant &p_variant) { for (int i = 0; i < nodes.size(); i++) { - nodes[i].over = r[i * 3 + 0]; - nodes[i].under = r[i * 3 + 1]; - nodes[i].plane = r[i * 3 + 2]; + nodes.write[i].over = r[i * 3 + 0]; + nodes.write[i].under = r[i * 3 + 1]; + nodes.write[i].plane = r[i * 3 + 2]; } } diff --git a/core/math/bsp_tree.h b/core/math/bsp_tree.h index fb16818ae7a6..b06e6b853955 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 */ diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 1ab9b3532ebd..3a082d572076 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 226b4d572bd0..bd20908ad909 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 "math_2d.h" -#include "transform.h" +#include "core/math/rect2.h" +#include "core/math/transform.h" + /** @author Juan Linietsky */ diff --git a/core/math/delaunay.h b/core/math/delaunay.h new file mode 100644 index 000000000000..9c5eef9069a1 --- /dev/null +++ b/core/math/delaunay.h @@ -0,0 +1,175 @@ +/*************************************************************************/ +/* 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 "core/math/rect2.h" + +class Delaunay2D { +public: + struct Triangle { + + int points[3]; + bool bad; + Triangle() { bad = false; } + Triangle(int p_a, int p_b, int p_c) { + points[0] = p_a; + points[1] = p_b; + points[2] = p_c; + bad = false; + } + }; + + struct Edge { + int edge[2]; + bool bad; + Edge() { bad = false; } + Edge(int p_a, int p_b) { + bad = false; + edge[0] = p_a; + edge[1] = p_b; + } + }; + + static bool circum_circle_contains(const Vector &p_vertices, const Triangle &p_triangle, int p_vertex) { + + Vector2 p1 = p_vertices[p_triangle.points[0]]; + Vector2 p2 = p_vertices[p_triangle.points[1]]; + Vector2 p3 = p_vertices[p_triangle.points[2]]; + + real_t ab = p1.x * p1.x + p1.y * p1.y; + real_t cd = p2.x * p2.x + p2.y * p2.y; + real_t ef = p3.x * p3.x + p3.y * p3.y; + + Vector2 circum( + (ab * (p3.y - p2.y) + cd * (p1.y - p3.y) + ef * (p2.y - p1.y)) / (p1.x * (p3.y - p2.y) + p2.x * (p1.y - p3.y) + p3.x * (p2.y - p1.y)), + (ab * (p3.x - p2.x) + cd * (p1.x - p3.x) + ef * (p2.x - p1.x)) / (p1.y * (p3.x - p2.x) + p2.y * (p1.x - p3.x) + p3.y * (p2.x - p1.x))); + + circum *= 0.5; + float r = p1.distance_squared_to(circum); + float d = p_vertices[p_vertex].distance_squared_to(circum); + return d <= r; + } + + static bool edge_compare(const Vector &p_vertices, const Edge &p_a, const Edge &p_b) { + if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON) { + return true; + } + + if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON) { + return true; + } + + return false; + } + + static Vector triangulate(const Vector &p_points) { + + Vector points = p_points; + Vector triangles; + + Rect2 rect; + for (int i = 0; i < p_points.size(); i++) { + if (i == 0) { + rect.position = p_points[i]; + } else { + rect.expand_to(p_points[i]); + } + } + + float delta_max = MAX(rect.size.width, rect.size.height); + Vector2 center = rect.position + rect.size * 0.5; + + points.push_back(Vector2(center.x - 20 * delta_max, center.y - delta_max)); + points.push_back(Vector2(center.x, center.y + 20 * delta_max)); + points.push_back(Vector2(center.x + 20 * delta_max, center.y - delta_max)); + + triangles.push_back(Triangle(p_points.size() + 0, p_points.size() + 1, p_points.size() + 2)); + + for (int i = 0; i < p_points.size(); i++) { + //std::cout << "Traitement du point " << *p << std::endl; + //std::cout << "_triangles contains " << _triangles.size() << " elements" << std::endl; + + Vector polygon; + + for (int j = 0; j < triangles.size(); j++) { + if (circum_circle_contains(points, triangles[j], i)) { + triangles.write[j].bad = true; + polygon.push_back(Edge(triangles[j].points[0], triangles[j].points[1])); + polygon.push_back(Edge(triangles[j].points[1], triangles[j].points[2])); + polygon.push_back(Edge(triangles[j].points[2], triangles[j].points[0])); + } + } + + for (int j = 0; j < triangles.size(); j++) { + if (triangles[j].bad) { + triangles.remove(j); + j--; + } + } + + for (int j = 0; j < polygon.size(); j++) { + for (int k = j + 1; k < polygon.size(); k++) { + if (edge_compare(points, polygon[j], polygon[k])) { + polygon.write[j].bad = true; + polygon.write[k].bad = true; + } + } + } + + for (int j = 0; j < polygon.size(); j++) { + + if (polygon[j].bad) { + continue; + } + triangles.push_back(Triangle(polygon[j].edge[0], polygon[j].edge[1], i)); + } + } + + for (int i = 0; i < triangles.size(); i++) { + bool invalid = false; + for (int j = 0; j < 3; j++) { + if (triangles[i].points[j] >= p_points.size()) { + invalid = true; + break; + } + } + if (invalid) { + triangles.remove(i); + i--; + } + } + + return triangles; + } +}; + +#endif // DELAUNAY_H diff --git a/core/math/expression.cpp b/core/math/expression.cpp new file mode 100644 index 000000000000..0cfb54234c02 --- /dev/null +++ b/core/math/expression.cpp @@ -0,0 +1,2174 @@ +/*************************************************************************/ +/* 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 "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", + "cos", + "tan", + "sinh", + "cosh", + "tanh", + "asin", + "acos", + "atan", + "atan2", + "sqrt", + "fmod", + "fposmod", + "floor", + "ceil", + "round", + "abs", + "sign", + "pow", + "log", + "exp", + "is_nan", + "is_inf", + "ease", + "decimals", + "stepify", + "lerp", + "inverse_lerp", + "range_lerp", + "dectime", + "randomize", + "randi", + "randf", + "rand_range", + "seed", + "rand_seed", + "deg2rad", + "rad2deg", + "linear2db", + "db2linear", + "polar2cartesian", + "cartesian2polar", + "wrapi", + "wrapf", + "max", + "min", + "clamp", + "nearest_po2", + "weakref", + "funcref", + "convert", + "typeof", + "type_exists", + "char", + "str", + "print", + "printerr", + "printraw", + "var2str", + "str2var", + "var2bytes", + "bytes2var", + "color_named", +}; + +Expression::BuiltinFunc Expression::find_function(const String &p_string) { + + for (int i = 0; i < FUNC_MAX; i++) { + if (p_string == func_name[i]) + return BuiltinFunc(i); + } + + return FUNC_MAX; +} + +String Expression::get_func_name(BuiltinFunc p_func) { + + ERR_FAIL_INDEX_V(p_func, FUNC_MAX, String()); + return func_name[p_func]; +} + +int Expression::get_func_argument_count(BuiltinFunc p_func) { + + switch (p_func) { + + case MATH_RANDOMIZE: + case MATH_RAND: + case MATH_RANDF: + return 0; + case MATH_SIN: + case MATH_COS: + case MATH_TAN: + case MATH_SINH: + case MATH_COSH: + case MATH_TANH: + case MATH_ASIN: + case MATH_ACOS: + case MATH_ATAN: + case MATH_SQRT: + case MATH_FLOOR: + case MATH_CEIL: + case MATH_ROUND: + case MATH_ABS: + case MATH_SIGN: + case MATH_LOG: + case MATH_EXP: + case MATH_ISNAN: + case MATH_ISINF: + case MATH_DECIMALS: + case MATH_SEED: + case MATH_RANDSEED: + case MATH_DEG2RAD: + case MATH_RAD2DEG: + case MATH_LINEAR2DB: + case MATH_DB2LINEAR: + case LOGIC_NEAREST_PO2: + case OBJ_WEAKREF: + case TYPE_OF: + case TEXT_CHAR: + case TEXT_STR: + case TEXT_PRINT: + case TEXT_PRINTERR: + case TEXT_PRINTRAW: + case VAR_TO_STR: + case STR_TO_VAR: + case VAR_TO_BYTES: + case BYTES_TO_VAR: + case TYPE_EXISTS: + return 1; + case MATH_ATAN2: + case MATH_FMOD: + case MATH_FPOSMOD: + case MATH_POW: + case MATH_EASE: + case MATH_STEPIFY: + case MATH_RANDOM: + case MATH_POLAR2CARTESIAN: + case MATH_CARTESIAN2POLAR: + case LOGIC_MAX: + case LOGIC_MIN: + case FUNC_FUNCREF: + case TYPE_CONVERT: + case COLORN: + return 2; + case MATH_LERP: + case MATH_INVERSE_LERP: + case MATH_DECTIME: + case MATH_WRAP: + case MATH_WRAPF: + case LOGIC_CLAMP: + return 3; + case MATH_RANGE_LERP: + return 5; + case FUNC_MAX: { + } + } + return 0; +} + +#define VALIDATE_ARG_NUM(m_arg) \ + if (!p_inputs[m_arg]->is_num()) { \ + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = Variant::REAL; \ + return; \ + } + +void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str) { + r_error.error = Variant::CallError::CALL_OK; + switch (p_func) { + case MATH_SIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sin((double)*p_inputs[0]); + } break; + case MATH_COS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cos((double)*p_inputs[0]); + } break; + case MATH_TAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tan((double)*p_inputs[0]); + } break; + case MATH_SINH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sinh((double)*p_inputs[0]); + } break; + case MATH_COSH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cosh((double)*p_inputs[0]); + } break; + case MATH_TANH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tanh((double)*p_inputs[0]); + } break; + case MATH_ASIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::asin((double)*p_inputs[0]); + } break; + case MATH_ACOS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::acos((double)*p_inputs[0]); + } break; + case MATH_ATAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::atan((double)*p_inputs[0]); + } break; + case MATH_ATAN2: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::atan2((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SQRT: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sqrt((double)*p_inputs[0]); + } break; + case MATH_FMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FPOSMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fposmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FLOOR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::floor((double)*p_inputs[0]); + } break; + case MATH_CEIL: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::ceil((double)*p_inputs[0]); + } break; + case MATH_ROUND: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::round((double)*p_inputs[0]); + } break; + case MATH_ABS: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = ABS(i); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = Math::abs(r); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_SIGN: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = i < 0 ? -1 : (i > 0 ? +1 : 0); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_POW: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::pow((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LOG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::log((double)*p_inputs[0]); + } break; + case MATH_EXP: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::exp((double)*p_inputs[0]); + } break; + case MATH_ISNAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_nan((double)*p_inputs[0]); + } break; + case MATH_ISINF: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_inf((double)*p_inputs[0]); + } break; + case MATH_EASE: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::ease((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_DECIMALS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::step_decimals((double)*p_inputs[0]); + } break; + case MATH_STEPIFY: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::stepify((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_INVERSE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANGE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + VALIDATE_ARG_NUM(3); + VALIDATE_ARG_NUM(4); + *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); + } break; + case MATH_DECTIME: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::dectime((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANDOMIZE: { + Math::randomize(); + + } break; + case MATH_RAND: { + *r_return = Math::rand(); + } break; + case MATH_RANDF: { + *r_return = Math::randf(); + } break; + case MATH_RANDOM: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + Math::seed(seed); + + } break; + case MATH_RANDSEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + int ret = Math::rand_from_seed(&seed); + Array reta; + reta.push_back(ret); + reta.push_back(seed); + *r_return = reta; + + } break; + case MATH_DEG2RAD: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::deg2rad((double)*p_inputs[0]); + } break; + case MATH_RAD2DEG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::rad2deg((double)*p_inputs[0]); + } break; + case MATH_LINEAR2DB: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::linear2db((double)*p_inputs[0]); + } break; + case MATH_DB2LINEAR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::db2linear((double)*p_inputs[0]); + } break; + case MATH_POLAR2CARTESIAN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double r = *p_inputs[0]; + double th = *p_inputs[1]; + *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); + } break; + case MATH_CARTESIAN2POLAR: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double x = *p_inputs[0]; + double y = *p_inputs[1]; + *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); + } break; + case MATH_WRAP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapi((int64_t)*p_inputs[0], (int64_t)*p_inputs[1], (int64_t)*p_inputs[2]); + } break; + case MATH_WRAPF: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapf((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case LOGIC_MAX: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MAX(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MAX(a, b); + } + + } break; + case LOGIC_MIN: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MIN(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MIN(a, b); + } + } break; + case LOGIC_CLAMP: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT && p_inputs[2]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + int64_t c = *p_inputs[2]; + *r_return = CLAMP(a, b, c); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + real_t c = *p_inputs[2]; + + *r_return = CLAMP(a, b, c); + } + } break; + case LOGIC_NEAREST_PO2: { + + VALIDATE_ARG_NUM(0); + int64_t num = *p_inputs[0]; + *r_return = next_power_of_2(num); + } break; + case OBJ_WEAKREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + + if (p_inputs[0]->is_ref()) { + + REF r = *p_inputs[0]; + if (!r.is_valid()) { + + return; + } + + Ref wref = memnew(WeakRef); + wref->set_ref(r); + *r_return = wref; + } else { + Object *obj = *p_inputs[0]; + if (!obj) { + + return; + } + Ref wref = memnew(WeakRef); + wref->set_obj(obj); + *r_return = wref; + } + + } break; + case FUNC_FUNCREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + if (p_inputs[1]->get_type() != Variant::STRING && p_inputs[1]->get_type() != Variant::NODE_PATH) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING; + + return; + } + + Ref fr = memnew(FuncRef); + + fr->set_instance(*p_inputs[0]); + fr->set_function(*p_inputs[1]); + + *r_return = fr; + + } break; + case TYPE_CONVERT: { + + VALIDATE_ARG_NUM(1); + int type = *p_inputs[1]; + if (type < 0 || type >= Variant::VARIANT_MAX) { + + r_error_str = RTR("Invalid type argument to convert(), use TYPE_* constants."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::INT; + return; + + } else { + + *r_return = Variant::construct(Variant::Type(type), p_inputs, 1, r_error); + } + } break; + case TYPE_OF: { + + *r_return = p_inputs[0]->get_type(); + + } break; + case TYPE_EXISTS: { + + *r_return = ClassDB::class_exists(*p_inputs[0]); + + } break; + case TEXT_CHAR: { + + CharType result[2] = { *p_inputs[0], 0 }; + + *r_return = String(result); + + } break; + case TEXT_STR: { + + String str = *p_inputs[0]; + + *r_return = str; + + } break; + case TEXT_PRINT: { + + String str = *p_inputs[0]; + print_line(str); + + } break; + + case TEXT_PRINTERR: { + + String str = *p_inputs[0]; + print_error(str); + + } break; + case TEXT_PRINTRAW: { + + String str = *p_inputs[0]; + OS::get_singleton()->print("%s", str.utf8().get_data()); + + } break; + case VAR_TO_STR: { + + String vars; + VariantWriter::write_to_string(*p_inputs[0], vars); + *r_return = vars; + } break; + case STR_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::STRING) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + + return; + } + + VariantParser::StreamString ss; + ss.s = *p_inputs[0]; + + String errs; + int line; + Error err = VariantParser::parse(&ss, *r_return, errs, line); + + if (err != OK) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + *r_return = "Parse error at line " + itos(line) + ": " + errs; + return; + } + + } break; + case VAR_TO_BYTES: { + + PoolByteArray barr; + int len; + Error err = encode_variant(*p_inputs[0], NULL, len); + if (err) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + r_error_str = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; + return; + } + + barr.resize(len); + { + PoolByteArray::Write w = barr.write(); + encode_variant(*p_inputs[0], w.ptr(), len); + } + *r_return = barr; + } break; + case BYTES_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::POOL_BYTE_ARRAY) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + + return; + } + + PoolByteArray varr = *p_inputs[0]; + Variant ret; + { + PoolByteArray::Read r = varr.read(); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + if (err != OK) { + r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + return; + } + } + + *r_return = ret; + + } break; + case COLORN: { + + VALIDATE_ARG_NUM(1); + + Color color = Color::named(*p_inputs[0]); + color.a = *p_inputs[1]; + + *r_return = String(color); + + } break; + default: {} + } +} + +//////// + +static bool _is_number(CharType c) { + return (c >= '0' && c <= '9'); +} + +Error Expression::_get_token(Token &r_token) { + + while (true) { +#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) + + CharType cchar = GET_CHAR(); + if (cchar == 0) { + r_token.type = TK_EOF; + return OK; + } + + switch (cchar) { + + case 0: { + r_token.type = TK_EOF; + return OK; + } break; + case '{': { + + r_token.type = TK_CURLY_BRACKET_OPEN; + return OK; + }; + case '}': { + + r_token.type = TK_CURLY_BRACKET_CLOSE; + return OK; + }; + case '[': { + + r_token.type = TK_BRACKET_OPEN; + return OK; + }; + case ']': { + + r_token.type = TK_BRACKET_CLOSE; + return OK; + }; + case '(': { + + r_token.type = TK_PARENTHESIS_OPEN; + return OK; + }; + case ')': { + + r_token.type = TK_PARENTHESIS_CLOSE; + return OK; + }; + case ',': { + + r_token.type = TK_COMMA; + return OK; + }; + case ':': { + + r_token.type = TK_COLON; + return OK; + }; + case '$': { + + r_token.type = TK_INPUT; + int index = 0; + do { + if (!_is_number(expression[str_ofs])) { + _set_error("Expected number after '$'"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + index *= 10; + index += expression[str_ofs] - '0'; + str_ofs++; + + } while (_is_number(expression[str_ofs])); + + r_token.value = index; + return OK; + }; + case '=': { + + cchar = GET_CHAR(); + if (cchar == '=') { + r_token.type = TK_OP_EQUAL; + } else { + _set_error("Expected '='"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + return OK; + }; + case '!': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_NOT_EQUAL; + str_ofs++; + } else { + r_token.type = TK_OP_NOT; + } + return OK; + }; + case '>': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_GREATER_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '>') { + r_token.type = TK_OP_SHIFT_RIGHT; + str_ofs++; + } else { + r_token.type = TK_OP_GREATER; + } + return OK; + }; + case '<': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_LESS_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '<') { + r_token.type = TK_OP_SHIFT_LEFT; + str_ofs++; + } else { + r_token.type = TK_OP_LESS; + } + return OK; + }; + case '+': { + r_token.type = TK_OP_ADD; + return OK; + }; + case '-': { + r_token.type = TK_OP_SUB; + return OK; + }; + case '/': { + r_token.type = TK_OP_DIV; + return OK; + }; + case '*': { + r_token.type = TK_OP_MUL; + return OK; + }; + case '%': { + r_token.type = TK_OP_MOD; + return OK; + }; + case '&': { + + if (expression[str_ofs] == '&') { + r_token.type = TK_OP_AND; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_AND; + } + return OK; + }; + case '|': { + + if (expression[str_ofs] == '|') { + r_token.type = TK_OP_OR; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_OR; + } + return OK; + }; + case '^': { + + r_token.type = TK_OP_BIT_XOR; + + return OK; + }; + case '~': { + + r_token.type = TK_OP_BIT_INVERT; + + return OK; + }; + case '"': { + + String str; + while (true) { + + CharType ch = GET_CHAR(); + + if (ch == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else if (ch == '"') { + break; + } else if (ch == '\\') { + //escaped characters... + + CharType next = GET_CHAR(); + if (next == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType res = 0; + + switch (next) { + + case 'b': res = 8; break; + case 't': res = 9; break; + case 'n': res = 10; break; + case 'f': res = 12; break; + case 'r': res = 13; break; + case 'u': { + //hexnumbarh - oct is deprecated + + for (int j = 0; j < 4; j++) { + CharType c = GET_CHAR(); + + if (c == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + if (!(_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 (_is_number(c)) { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = c - 'a'; + v += 10; + } else if (c >= 'A' && c <= 'F') { + v = c - 'A'; + v += 10; + } else { + ERR_PRINT("BUG"); + v = 0; + } + + res <<= 4; + res |= v; + } + + } break; + //case '\"': res='\"'; break; + //case '\\': res='\\'; break; + //case '/': res='/'; break; + default: { + res = next; + //r_err_str="Invalid escape sequence"; + //return ERR_PARSE_ERROR; + } break; + } + + str += res; + + } else { + str += ch; + } + } + + r_token.type = TK_CONSTANT; + r_token.value = str; + return OK; + + } break; + default: { + + if (cchar <= 32) { + break; + } + + CharType next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; + if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) { + //a number + + String num; +#define READING_SIGN 0 +#define READING_INT 1 +#define READING_DEC 2 +#define READING_EXP 3 +#define READING_DONE 4 + int reading = READING_INT; + + CharType c = cchar; + bool exp_sign = false; + bool exp_beg = false; + bool is_float = false; + + while (true) { + + switch (reading) { + case READING_INT: { + + if (_is_number(c)) { + //pass + } else if (c == '.') { + reading = READING_DEC; + is_float = true; + } else if (c == 'e') { + reading = READING_EXP; + } else { + reading = READING_DONE; + } + + } break; + case READING_DEC: { + + if (_is_number(c)) { + + } else if (c == 'e') { + reading = READING_EXP; + + } else { + reading = READING_DONE; + } + + } break; + case READING_EXP: { + + if (_is_number(c)) { + exp_beg = true; + + } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { + if (c == '-') + is_float = true; + exp_sign = true; + + } else { + reading = READING_DONE; + } + } break; + } + + if (reading == READING_DONE) + break; + num += String::chr(c); + c = GET_CHAR(); + } + + str_ofs--; + + r_token.type = TK_CONSTANT; + + if (is_float) + r_token.value = num.to_double(); + else + r_token.value = num.to_int(); + return OK; + + } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + + String id; + bool first = true; + + while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) { + + id += String::chr(cchar); + cchar = GET_CHAR(); + first = false; + } + + str_ofs--; //go back one + + if (id == "in") { + r_token.type = TK_OP_IN; + } else if (id == "null") { + r_token.type = TK_CONSTANT; + r_token.value = Variant(); + } else if (id == "true") { + r_token.type = TK_CONSTANT; + r_token.value = true; + } else if (id == "false") { + r_token.type = TK_CONSTANT; + r_token.value = false; + } else if (id == "PI") { + r_token.type = TK_CONSTANT; + r_token.value = Math_PI; + } else if (id == "TAU") { + r_token.type = TK_CONSTANT; + r_token.value = Math_TAU; + } else if (id == "INF") { + r_token.type = TK_CONSTANT; + r_token.value = Math_INF; + } else if (id == "NAN") { + r_token.type = TK_CONSTANT; + r_token.value = Math_NAN; + } else if (id == "not") { + r_token.type = TK_OP_NOT; + } else if (id == "or") { + r_token.type = TK_OP_OR; + } else if (id == "and") { + r_token.type = TK_OP_AND; + } else if (id == "self") { + r_token.type = TK_SELF; + } else { + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (id == Variant::get_type_name(Variant::Type(i))) { + r_token.type = TK_BASIC_TYPE; + r_token.value = i; + return OK; + } + } + + BuiltinFunc bifunc = find_function(id); + if (bifunc != FUNC_MAX) { + r_token.type = TK_BUILTIN_FUNC; + r_token.value = bifunc; + return OK; + } + + r_token.type = TK_IDENTIFIER; + r_token.value = id; + } + + return OK; + + } else 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; + return ERR_PARSE_ERROR; + } + } + } +#undef GET_CHAR + } + + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; +} + +const char *Expression::token_name[TK_MAX] = { + "CURLY BRACKET OPEN", + "CURLY BRACKET CLOSE", + "BRACKET OPEN", + "BRACKET CLOSE", + "PARENTHESIS OPEN", + "PARENTHESIS CLOSE", + "IDENTIFIER", + "BUILTIN FUNC", + "SELF", + "CONSTANT", + "BASIC TYPE", + "COLON", + "COMMA", + "PERIOD", + "OP IN", + "OP EQUAL", + "OP NOT EQUAL", + "OP LESS", + "OP LESS EQUAL", + "OP GREATER", + "OP GREATER EQUAL", + "OP AND", + "OP OR", + "OP NOT", + "OP ADD", + "OP SUB", + "OP MUL", + "OP DIV", + "OP MOD", + "OP SHIFT LEFT", + "OP SHIFT RIGHT", + "OP BIT AND", + "OP BIT OR", + "OP BIT XOR", + "OP BIT INVERT", + "OP INPUT", + "EOF", + "ERROR" +}; + +Expression::ENode *Expression::_parse_expression() { + + Vector expression; + + while (true) { + //keep appending stuff to expression + ENode *expr = NULL; + + Token tk; + _get_token(tk); + if (error_set) + return NULL; + + switch (tk.type) { + case TK_CURLY_BRACKET_OPEN: { + //a dictionary + DictionaryNode *dn = alloc_node(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_CURLY_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + dn->dict.push_back(expr); + + _get_token(tk); + if (tk.type != TK_COLON) { + _set_error("Expected ':'"); + return NULL; + } + + expr = _parse_expression(); + if (!expr) + return NULL; + + dn->dict.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_CURLY_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or '}'"); + } + } + + expr = dn; + } break; + case TK_BRACKET_OPEN: { + //an array + + ArrayNode *an = alloc_node(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + an->array.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ']'"); + } + } + + expr = an; + } break; + case TK_PARENTHESIS_OPEN: { + //a suexpression + ENode *e = _parse_expression(); + if (error_set) + return NULL; + _get_token(tk); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); + return NULL; + } + + expr = e; + + } break; + case TK_IDENTIFIER: { + + String identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node(); + func_call->method = identifier; + SelfNode *self_node = alloc_node(); + func_call->base = self_node; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + int input_index = -1; + for (int i = 0; i < input_names.size(); i++) { + if (input_names[i] == identifier) { + input_index = i; + break; + } + } + + if (input_index != -1) { + InputNode *input = alloc_node(); + input->index = input_index; + expr = input; + } else { + + NamedIndexNode *index = alloc_node(); + SelfNode *self_node = alloc_node(); + index->base = self_node; + index->name = identifier; + expr = index; + } + } + } break; + case TK_INPUT: { + + InputNode *input = alloc_node(); + input->index = tk.value; + expr = input; + } break; + case TK_SELF: { + + SelfNode *self = alloc_node(); + expr = self; + } break; + case TK_CONSTANT: { + ConstantNode *constant = alloc_node(); + constant->value = tk.value; + expr = constant; + } break; + case TK_BASIC_TYPE: { + //constructor.. + + Variant::Type bt = Variant::Type(int(tk.value)); + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + ConstructorNode *constructor = alloc_node(); + constructor->data_type = bt; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + constructor->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = constructor; + + } break; + case TK_BUILTIN_FUNC: { + //builtin function + + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + BuiltinFuncNode *bifunc = alloc_node(); + bifunc->func = BuiltinFunc(int(tk.value)); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + bifunc->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + int expected_args = get_func_argument_count(bifunc->func); + if (bifunc->arguments.size() != expected_args) { + _set_error("Builtin func '" + get_func_name(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); + } + + expr = bifunc; + + } break; + case TK_OP_SUB: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NEGATE; + expression.push_back(e); + continue; + } break; + case TK_OP_NOT: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NOT; + expression.push_back(e); + continue; + } break; + + default: { + _set_error("Expected expression."); + return NULL; + } break; + } + + //before going to operators, must check indexing! + + while (true) { + int cofs2 = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + bool done = false; + + switch (tk.type) { + case TK_BRACKET_OPEN: { + //value indexing + + IndexNode *index = alloc_node(); + index->base = expr; + + ENode *what = _parse_expression(); + if (!what) + return NULL; + + index->index = what; + + _get_token(tk); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']' at end of index."); + return NULL; + } + expr = index; + + } break; + case TK_PERIOD: { + //named indexing or function call + _get_token(tk); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier after '.'"); + return NULL; + } + + StringName identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node(); + func_call->method = identifier; + func_call->base = expr; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + NamedIndexNode *index = alloc_node(); + index->base = expr; + index->name = identifier; + expr = index; + } + + } break; + default: { + str_ofs = cofs2; + done = true; + } break; + } + + if (done) + break; + } + + //push expression + { + ExpressionNode e; + e.is_op = false; + e.node = expr; + expression.push_back(e); + } + + //ok finally look for an operator + + int cofs = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + Variant::Operator op = Variant::OP_MAX; + + switch (tk.type) { + case TK_OP_IN: op = Variant::OP_IN; break; + case TK_OP_EQUAL: op = Variant::OP_EQUAL; break; + case TK_OP_NOT_EQUAL: op = Variant::OP_NOT_EQUAL; break; + case TK_OP_LESS: op = Variant::OP_LESS; break; + case TK_OP_LESS_EQUAL: op = Variant::OP_LESS_EQUAL; break; + case TK_OP_GREATER: op = Variant::OP_GREATER; break; + case TK_OP_GREATER_EQUAL: op = Variant::OP_GREATER_EQUAL; break; + case TK_OP_AND: op = Variant::OP_AND; break; + case TK_OP_OR: op = Variant::OP_OR; break; + case TK_OP_NOT: op = Variant::OP_NOT; break; + case TK_OP_ADD: op = Variant::OP_ADD; break; + case TK_OP_SUB: op = Variant::OP_SUBTRACT; break; + case TK_OP_MUL: op = Variant::OP_MULTIPLY; break; + case TK_OP_DIV: op = Variant::OP_DIVIDE; break; + case TK_OP_MOD: op = Variant::OP_MODULE; break; + case TK_OP_SHIFT_LEFT: op = Variant::OP_SHIFT_LEFT; break; + case TK_OP_SHIFT_RIGHT: op = Variant::OP_SHIFT_RIGHT; break; + case TK_OP_BIT_AND: op = Variant::OP_BIT_AND; break; + case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; + case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; + case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; + default: {}; + } + + if (op == Variant::OP_MAX) { //stop appending stuff + str_ofs = cofs; + break; + } + + //push operator and go on + { + ExpressionNode e; + e.is_op = true; + e.op = op; + expression.push_back(e); + } + } + + /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ + + while (expression.size() > 1) { + + int next_op = -1; + int min_priority = 0xFFFFF; + bool is_unary = false; + + for (int i = 0; i < expression.size(); i++) { + + if (!expression[i].is_op) { + + continue; + } + + int priority; + + bool unary = false; + + switch (expression[i].op) { + + case Variant::OP_BIT_NEGATE: + priority = 0; + unary = true; + break; + case Variant::OP_NEGATE: + priority = 1; + unary = true; + break; + + case Variant::OP_MULTIPLY: priority = 2; break; + case Variant::OP_DIVIDE: priority = 2; break; + case Variant::OP_MODULE: priority = 2; break; + + case Variant::OP_ADD: priority = 3; break; + case Variant::OP_SUBTRACT: priority = 3; break; + + case Variant::OP_SHIFT_LEFT: priority = 4; break; + case Variant::OP_SHIFT_RIGHT: priority = 4; break; + + case Variant::OP_BIT_AND: priority = 5; break; + case Variant::OP_BIT_XOR: priority = 6; break; + case Variant::OP_BIT_OR: priority = 7; break; + + case Variant::OP_LESS: priority = 8; break; + case Variant::OP_LESS_EQUAL: priority = 8; break; + case Variant::OP_GREATER: priority = 8; break; + case Variant::OP_GREATER_EQUAL: priority = 8; break; + + case Variant::OP_EQUAL: priority = 8; break; + case Variant::OP_NOT_EQUAL: priority = 8; break; + + case Variant::OP_IN: priority = 10; break; + + case Variant::OP_NOT: + priority = 11; + unary = true; + break; + case Variant::OP_AND: priority = 12; break; + case Variant::OP_OR: priority = 13; break; + + default: { + _set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op)); + return NULL; + } + } + + if (priority < min_priority) { + // < is used for left to right (default) + // <= is used for right to left + + next_op = i; + min_priority = priority; + is_unary = unary; + } + } + + if (next_op == -1) { + + _set_error("Yet another parser bug...."); + ERR_FAIL_COND_V(next_op == -1, NULL); + } + + // OK! create operator.. + if (is_unary) { + + int expr_pos = next_op; + while (expression[expr_pos].is_op) { + + expr_pos++; + if (expr_pos == expression.size()) { + //can happen.. + _set_error("Unexpected end of expression..."); + return NULL; + } + } + + //consecutively do unary opeators + for (int i = expr_pos - 1; i >= next_op; i--) { + + OperatorNode *op = alloc_node(); + op->op = expression[i].op; + op->nodes[0] = expression[i + 1].node; + op->nodes[1] = NULL; + expression.write[i].is_op = false; + expression.write[i].node = op; + expression.remove(i + 1); + } + + } else { + + if (next_op < 1 || next_op >= (expression.size() - 1)) { + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + OperatorNode *op = alloc_node(); + op->op = expression[next_op].op; + + if (expression[next_op - 1].is_op) { + + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + if (expression[next_op + 1].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by a unary op in a valid combination, + // due to how precedence works, unaries will always disappear first + + _set_error("Unexpected two consecutive operators."); + return NULL; + } + + op->nodes[0] = expression[next_op - 1].node; //expression goes as left + op->nodes[1] = expression[next_op + 1].node; //next expression goes as right + + //replace all 3 nodes by this operator and make it an expression + expression.write[next_op - 1].node = op; + expression.remove(next_op); + expression.remove(next_op); + } + } + + return expression[0].node; +} + +bool Expression::_compile_expression() { + + if (!expression_dirty) + return error_set; + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return true; + } + + expression_dirty = false; + return false; +} + +bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) { + + switch (p_node->type) { + case Expression::ENode::TYPE_INPUT: { + + const Expression::InputNode *in = static_cast(p_node); + if (in->index < 0 || in->index >= p_inputs.size()) { + r_error_str = vformat(RTR("Invalid input %i (not passed) in expression"), in->index); + return true; + } + r_ret = p_inputs[in->index]; + } break; + case Expression::ENode::TYPE_CONSTANT: { + + const Expression::ConstantNode *c = static_cast(p_node); + r_ret = c->value; + + } break; + case Expression::ENode::TYPE_SELF: { + + if (!p_instance) { + r_error_str = RTR("self can't be used because instance is null (not passed)"); + return true; + } + r_ret = p_instance; + } break; + case Expression::ENode::TYPE_OPERATOR: { + + const Expression::OperatorNode *op = static_cast(p_node); + + Variant a; + bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str); + if (ret) + return true; + + Variant b; + + if (op->nodes[1]) { + bool ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str); + if (ret) + return true; + } + + bool valid = true; + Variant::evaluate(op->op, a, b, r_ret, valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid operands to operator %s, %s and %s."), Variant::get_operator_name(op->op), Variant::get_type_name(a.get_type()), Variant::get_type_name(b.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_INDEX: { + + const Expression::IndexNode *index = static_cast(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + Variant idx; + + ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get(idx, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid index of type %s for base type %s"), Variant::get_type_name(idx.get_type()), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_NAMED_INDEX: { + + const Expression::NamedIndexNode *index = static_cast(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get_named(index->name, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid named index '%s' for base type %s"), String(index->name), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_ARRAY: { + const Expression::ArrayNode *array = static_cast(p_node); + + Array arr; + arr.resize(array->array.size()); + for (int i = 0; i < array->array.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str); + + if (ret) + return true; + arr[i] = value; + } + + r_ret = arr; + + } break; + case Expression::ENode::TYPE_DICTIONARY: { + const Expression::DictionaryNode *dictionary = static_cast(p_node); + + Dictionary d; + for (int i = 0; i < dictionary->dict.size(); i += 2) { + + Variant key; + bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str); + + if (ret) + return true; + + Variant value; + ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str); + if (ret) + return true; + + d[key] = value; + } + + r_ret = d; + } break; + case Expression::ENode::TYPE_CONSTRUCTOR: { + + const Expression::ConstructorNode *constructor = static_cast(p_node); + + Vector arr; + Vector argp; + arr.resize(constructor->arguments.size()); + argp.resize(constructor->arguments.size()); + + for (int i = 0; i < constructor->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("Invalid arguments to construct '%s'"), Variant::get_type_name(constructor->data_type)); + return true; + } + + } break; + case Expression::ENode::TYPE_BUILTIN_FUNC: { + + const Expression::BuiltinFuncNode *bifunc = static_cast(p_node); + + Vector arr; + Vector argp; + arr.resize(bifunc->arguments.size()); + argp.resize(bifunc->arguments.size()); + + for (int i = 0; i < bifunc->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str); + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = "Builtin Call Failed. " + r_error_str; + return true; + } + + } break; + case Expression::ENode::TYPE_CALL: { + + const Expression::CallNode *call = static_cast(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str); + + if (ret) + return true; + + Vector arr; + Vector argp; + arr.resize(call->arguments.size()); + argp.resize(call->arguments.size()); + + for (int i = 0; i < call->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("On call to '%s':"), String(call->method)); + return true; + } + + } break; + } + return false; +} + +Error Expression::parse(const String &p_expression, const Vector &p_input_names) { + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + input_names = p_input_names; + + expression = p_expression; + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return ERR_INVALID_PARAMETER; + } + + return OK; +} + +Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { + if (error_set) { + ERR_EXPLAIN("There was previously a parse error: " + error_str); + ERR_FAIL_V(Variant()); + } + + execution_error = false; + Variant output; + String error_txt; + bool err = _execute(p_inputs, p_base, root, output, error_txt); + if (err) { + execution_error = true; + error_str = error_txt; + if (p_show_error) { + ERR_EXPLAIN(error_str); + ERR_FAIL_V(Variant()); + } + } + + return output; +} + +bool Expression::has_execute_failed() const { + return execution_error; +} + +String Expression::get_error_text() const { + return error_str; +} + +void Expression::_bind_methods() { + + ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector())); + ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed); + ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text); +} + +Expression::Expression() { + output_type = Variant::NIL; + error_set = true; + root = NULL; + nodes = NULL; + sequenced = false; + execution_error = false; +} + +Expression::~Expression() { + + if (nodes) { + memdelete(nodes); + } +} diff --git a/core/math/expression.h b/core/math/expression.h new file mode 100644 index 000000000000..ac2416d0dddd --- /dev/null +++ b/core/math/expression.h @@ -0,0 +1,355 @@ +/*************************************************************************/ +/* 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 + +#include "core/reference.h" + +class Expression : public Reference { + GDCLASS(Expression, Reference) +public: + enum BuiltinFunc { + MATH_SIN, + MATH_COS, + MATH_TAN, + MATH_SINH, + MATH_COSH, + MATH_TANH, + MATH_ASIN, + MATH_ACOS, + MATH_ATAN, + MATH_ATAN2, + MATH_SQRT, + MATH_FMOD, + MATH_FPOSMOD, + MATH_FLOOR, + MATH_CEIL, + MATH_ROUND, + MATH_ABS, + MATH_SIGN, + MATH_POW, + MATH_LOG, + MATH_EXP, + MATH_ISNAN, + MATH_ISINF, + MATH_EASE, + MATH_DECIMALS, + MATH_STEPIFY, + MATH_LERP, + MATH_INVERSE_LERP, + MATH_RANGE_LERP, + MATH_DECTIME, + MATH_RANDOMIZE, + MATH_RAND, + MATH_RANDF, + MATH_RANDOM, + MATH_SEED, + MATH_RANDSEED, + MATH_DEG2RAD, + MATH_RAD2DEG, + MATH_LINEAR2DB, + MATH_DB2LINEAR, + MATH_POLAR2CARTESIAN, + MATH_CARTESIAN2POLAR, + MATH_WRAP, + MATH_WRAPF, + LOGIC_MAX, + LOGIC_MIN, + LOGIC_CLAMP, + LOGIC_NEAREST_PO2, + OBJ_WEAKREF, + FUNC_FUNCREF, + TYPE_CONVERT, + TYPE_OF, + TYPE_EXISTS, + TEXT_CHAR, + TEXT_STR, + TEXT_PRINT, + TEXT_PRINTERR, + TEXT_PRINTRAW, + VAR_TO_STR, + STR_TO_VAR, + VAR_TO_BYTES, + BYTES_TO_VAR, + COLORN, + FUNC_MAX + }; + + static int get_func_argument_count(BuiltinFunc p_func); + static String get_func_name(BuiltinFunc p_func); + static void exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str); + static BuiltinFunc find_function(const String &p_string); + +private: + static const char *func_name[FUNC_MAX]; + + struct Input { + + Variant::Type type; + String name; + + Input() { type = Variant::NIL; } + }; + + Vector inputs; + Variant::Type output_type; + + String expression; + + bool sequenced; + int str_ofs; + bool expression_dirty; + + bool _compile_expression(); + + enum TokenType { + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_PARENTHESIS_OPEN, + TK_PARENTHESIS_CLOSE, + TK_IDENTIFIER, + TK_BUILTIN_FUNC, + TK_SELF, + TK_CONSTANT, + TK_BASIC_TYPE, + TK_COLON, + TK_COMMA, + TK_PERIOD, + TK_OP_IN, + TK_OP_EQUAL, + TK_OP_NOT_EQUAL, + TK_OP_LESS, + TK_OP_LESS_EQUAL, + TK_OP_GREATER, + TK_OP_GREATER_EQUAL, + TK_OP_AND, + TK_OP_OR, + TK_OP_NOT, + TK_OP_ADD, + TK_OP_SUB, + TK_OP_MUL, + TK_OP_DIV, + TK_OP_MOD, + TK_OP_SHIFT_LEFT, + TK_OP_SHIFT_RIGHT, + TK_OP_BIT_AND, + TK_OP_BIT_OR, + TK_OP_BIT_XOR, + TK_OP_BIT_INVERT, + TK_INPUT, + TK_EOF, + TK_ERROR, + TK_MAX + }; + + static const char *token_name[TK_MAX]; + struct Token { + + TokenType type; + Variant value; + }; + + void _set_error(const String &p_err) { + if (error_set) + return; + error_str = p_err; + error_set = true; + } + + Error _get_token(Token &r_token); + + String error_str; + bool error_set; + + struct ENode { + + enum Type { + TYPE_INPUT, + TYPE_CONSTANT, + TYPE_SELF, + TYPE_OPERATOR, + TYPE_INDEX, + TYPE_NAMED_INDEX, + TYPE_ARRAY, + TYPE_DICTIONARY, + TYPE_CONSTRUCTOR, + TYPE_BUILTIN_FUNC, + TYPE_CALL + }; + + ENode *next; + + Type type; + + ENode() { next = NULL; } + virtual ~ENode() { + if (next) { + memdelete(next); + } + } + }; + + struct ExpressionNode { + + bool is_op; + union { + Variant::Operator op; + ENode *node; + }; + }; + + ENode *_parse_expression(); + + struct InputNode : public ENode { + + int index; + InputNode() { + type = TYPE_INPUT; + } + }; + + struct ConstantNode : public ENode { + + Variant value; + ConstantNode() { + type = TYPE_CONSTANT; + } + }; + + struct OperatorNode : public ENode { + + Variant::Operator op; + + ENode *nodes[2]; + + OperatorNode() { + type = TYPE_OPERATOR; + } + }; + + struct SelfNode : public ENode { + + SelfNode() { + type = TYPE_SELF; + } + }; + + struct IndexNode : public ENode { + ENode *base; + ENode *index; + + IndexNode() { + type = TYPE_INDEX; + } + }; + + struct NamedIndexNode : public ENode { + ENode *base; + StringName name; + + NamedIndexNode() { + type = TYPE_NAMED_INDEX; + } + }; + + struct ConstructorNode : public ENode { + Variant::Type data_type; + Vector arguments; + + ConstructorNode() { + type = TYPE_CONSTRUCTOR; + } + }; + + struct CallNode : public ENode { + ENode *base; + StringName method; + Vector arguments; + + CallNode() { + type = TYPE_CALL; + } + }; + + struct ArrayNode : public ENode { + Vector array; + ArrayNode() { + type = TYPE_ARRAY; + } + }; + + struct DictionaryNode : public ENode { + Vector dict; + DictionaryNode() { + type = TYPE_DICTIONARY; + } + }; + + struct BuiltinFuncNode : public ENode { + BuiltinFunc func; + Vector arguments; + BuiltinFuncNode() { + type = TYPE_BUILTIN_FUNC; + } + }; + + template + T *alloc_node() { + T *node = memnew(T); + node->next = nodes; + nodes = node; + return node; + } + + ENode *root; + ENode *nodes; + + Vector input_names; + + bool execution_error; + bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str); + +protected: + static void _bind_methods(); + +public: + Error parse(const String &p_expression, const Vector &p_input_names = Vector()); + Variant execute(Array p_inputs, Object *p_base = NULL, bool p_show_error = true); + bool has_execute_failed() const; + String get_error_text() const; + + Expression(); + ~Expression(); +}; + +#endif // EXPRESSION_H diff --git a/core/math/face3.cpp b/core/math/face3.cpp index 801f2a3b4dcb..836613713155 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 faed0fa8d4f1..b41daf04d477 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 24f077a4ca61..be5e40e4e63c 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 &p_polygon) { @@ -56,7 +57,7 @@ void Geometry::MeshData::optimize_vertices() { vtx_remap[idx] = ni; } - faces[i].indices[j] = vtx_remap[idx]; + faces.write[i].indices.write[j] = vtx_remap[idx]; } } @@ -74,8 +75,8 @@ void Geometry::MeshData::optimize_vertices() { vtx_remap[b] = ni; } - edges[i].a = vtx_remap[a]; - edges[i].b = vtx_remap[b]; + edges.write[i].a = vtx_remap[a]; + edges.write[i].b = vtx_remap[b]; } Vector new_vertices; @@ -84,7 +85,7 @@ void Geometry::MeshData::optimize_vertices() { for (int i = 0; i < vertices.size(); i++) { if (vtx_remap.has(i)) - new_vertices[vtx_remap[i]] = vertices[i]; + new_vertices.write[vtx_remap[i]] = vertices[i]; } vertices = new_vertices; } @@ -626,7 +627,6 @@ PoolVector Geometry::wrap_geometry(PoolVector p_array, real_t *p_e voxelsize.z /= div_z; // create and initialize cells to zero - //print_line("Wrapper: Initializing Cells"); uint8_t ***cell_status = memnew_arr(uint8_t **, div_x); for (int i = 0; i < div_x; i++) { @@ -645,7 +645,6 @@ PoolVector Geometry::wrap_geometry(PoolVector p_array, real_t *p_e } // plot faces into cells - //print_line("Wrapper (1/6): Plotting Faces"); for (int i = 0; i < face_count; i++) { @@ -659,8 +658,6 @@ PoolVector Geometry::wrap_geometry(PoolVector p_array, real_t *p_e // determine which cells connect to the outside by traversing the outside and recursively flood-fill marking - //print_line("Wrapper (2/6): Flood Filling"); - for (int i = 0; i < div_x; i++) { for (int j = 0; j < div_y; j++) { @@ -690,8 +687,6 @@ PoolVector Geometry::wrap_geometry(PoolVector p_array, real_t *p_e // build faces for the inside-outside cell divisors - //print_line("Wrapper (3/6): Building Faces"); - PoolVector wrapped_faces; for (int i = 0; i < div_x; i++) { @@ -705,8 +700,6 @@ PoolVector Geometry::wrap_geometry(PoolVector p_array, real_t *p_e } } - //print_line("Wrapper (4/6): Transforming Back Vertices"); - // transform face vertices to global coords int wrapped_faces_count = wrapped_faces.size(); @@ -724,7 +717,6 @@ PoolVector Geometry::wrap_geometry(PoolVector p_array, real_t *p_e } // clean up grid - //print_line("Wrapper (5/6): Grid Cleanup"); for (int i = 0; i < div_x; i++) { @@ -740,7 +732,6 @@ PoolVector Geometry::wrap_geometry(PoolVector p_array, real_t *p_e if (p_error) *p_error = voxelsize.length(); - //print_line("Wrapper (6/6): Finished."); return wrapped_faces; } @@ -1014,8 +1005,8 @@ void Geometry::make_atlas(const Vector &p_rects, Vector &r_resu Vector<_AtlasWorkRect> wrects; wrects.resize(p_rects.size()); for (int i = 0; i < p_rects.size(); i++) { - wrects[i].s = p_rects[i]; - wrects[i].idx = i; + wrects.write[i].s = p_rects[i]; + wrects.write[i].idx = i; } wrects.sort(); int widest = wrects[0].s.width; @@ -1033,7 +1024,7 @@ void Geometry::make_atlas(const Vector &p_rects, Vector &r_resu Vector hmax; hmax.resize(w); for (int j = 0; j < w; j++) - hmax[j] = 0; + hmax.write[j] = 0; //place them int ofs = 0; @@ -1052,8 +1043,8 @@ void Geometry::make_atlas(const Vector &p_rects, Vector &r_resu from_y = hmax[ofs + k]; } - wrects[j].p.x = ofs; - wrects[j].p.y = from_y; + wrects.write[j].p.x = ofs; + wrects.write[j].p.y = from_y; int end_h = from_y + wrects[j].s.height; int end_w = ofs + wrects[j].s.width; if (ofs == 0) @@ -1061,7 +1052,7 @@ void Geometry::make_atlas(const Vector &p_rects, Vector &r_resu for (int k = 0; k < wrects[j].s.width; k++) { - hmax[ofs + k] = end_h; + hmax.write[ofs + k] = end_h; } if (end_h > max_h) @@ -1101,7 +1092,7 @@ void Geometry::make_atlas(const Vector &p_rects, Vector &r_resu for (int i = 0; i < p_rects.size(); i++) { - r_result[results[best].result[i].idx] = results[best].result[i].p; + r_result.write[results[best].result[i].idx] = results[best].result[i].p; } r_size = Size2(results[best].max_w, results[best].max_h); diff --git a/core/math/geometry.h b/core/math/geometry.h index 73a53c53b6de..df63f0dabedf 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 "math_2d.h" -#include "object.h" -#include "print_string.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 */ @@ -529,6 +530,21 @@ class Geometry { return p_segment[0] + n * d; // inside } + static bool line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b, Vector2 &r_result) { + + // see http://paulbourke.net/geometry/pointlineplane/ + + const real_t denom = p_dir_b.y * p_dir_a.x - p_dir_b.x * p_dir_a.y; + if (Math::abs(denom) < CMP_EPSILON) { // parallel? + return false; + } + + const Vector2 v = p_from_a - p_from_b; + const real_t t = (p_dir_b.x * v.y - p_dir_b.y * v.x) / denom; + r_result = p_from_a + t * p_dir_a; + return true; + } + static bool segment_intersects_segment_2d(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b, Vector2 *r_result) { Vector2 B = p_to_a - p_from_a; @@ -784,6 +800,21 @@ class Geometry { return Vector >(); } + static bool is_polygon_clockwise(const Vector &p_polygon) { + int c = p_polygon.size(); + if (c < 3) + return false; + const Vector2 *p = p_polygon.ptr(); + real_t sum = 0; + for (int i = 0; i < c; i++) { + const Vector2 &v1 = p[i]; + const Vector2 &v2 = p[(i + 1) % c]; + sum += (v2.x - v1.x) * (v2.y + v1.y); + } + + return sum > 0.0f; + } + static PoolVector > separate_objects(PoolVector p_array); static PoolVector wrap_geometry(PoolVector p_array, real_t *p_error = NULL); ///< create a "wrap" that encloses the given geometry @@ -875,14 +906,14 @@ class Geometry { for (int i = 0; i < n; ++i) { while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; - H[k++] = P[i]; + H.write[k++] = P[i]; } // Build upper hull for (int i = n - 2, t = k + 1; i >= 0; i--) { while (k >= t && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; - H[k++] = P[i]; + H.write[k++] = P[i]; } H.resize(k); diff --git a/core/math/math_2d.h b/core/math/math_2d.h deleted file mode 100644 index e7188da85b9b..000000000000 --- a/core/math/math_2d.h +++ /dev/null @@ -1,991 +0,0 @@ -/*************************************************************************/ -/* math_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 MATH_2D_H -#define MATH_2D_H - -#include "math_funcs.h" -#include "ustring.h" -/** - @author Juan Linietsky -*/ -enum Margin { - - MARGIN_LEFT, - MARGIN_TOP, - MARGIN_RIGHT, - MARGIN_BOTTOM -}; - -enum Corner { - - CORNER_TOP_LEFT, - CORNER_TOP_RIGHT, - CORNER_BOTTOM_RIGHT, - CORNER_BOTTOM_LEFT -}; - -enum Orientation { - - HORIZONTAL, - VERTICAL -}; - -enum HAlign { - - HALIGN_LEFT, - HALIGN_CENTER, - HALIGN_RIGHT -}; - -enum VAlign { - - VALIGN_TOP, - VALIGN_CENTER, - VALIGN_BOTTOM -}; - -struct Vector2 { - - union { - real_t x; - real_t width; - }; - union { - real_t y; - real_t height; - }; - - _FORCE_INLINE_ real_t &operator[](int p_idx) { - return p_idx ? y : x; - } - _FORCE_INLINE_ const real_t &operator[](int p_idx) const { - return p_idx ? y : x; - } - - void normalize(); - Vector2 normalized() const; - bool is_normalized() const; - - real_t length() const; - real_t length_squared() const; - - real_t distance_to(const Vector2 &p_vector2) const; - real_t distance_squared_to(const Vector2 &p_vector2) const; - real_t angle_to(const Vector2 &p_vector2) const; - real_t angle_to_point(const Vector2 &p_vector2) const; - - real_t dot(const Vector2 &p_other) const; - real_t cross(const Vector2 &p_other) const; - Vector2 project(const Vector2 &p_vec) const; - - Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; - - Vector2 clamped(real_t p_len) const; - - _FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t); - _FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const; - Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const; - - Vector2 slide(const Vector2 &p_normal) const; - Vector2 bounce(const Vector2 &p_normal) const; - Vector2 reflect(const Vector2 &p_normal) const; - - Vector2 operator+(const Vector2 &p_v) const; - void operator+=(const Vector2 &p_v); - Vector2 operator-(const Vector2 &p_v) const; - void operator-=(const Vector2 &p_v); - Vector2 operator*(const Vector2 &p_v1) const; - - Vector2 operator*(const real_t &rvalue) const; - void operator*=(const real_t &rvalue); - void operator*=(const Vector2 &rvalue) { *this = *this * rvalue; } - - Vector2 operator/(const Vector2 &p_v1) const; - - Vector2 operator/(const real_t &rvalue) const; - - void operator/=(const real_t &rvalue); - - Vector2 operator-() const; - - bool operator==(const Vector2 &p_vec2) const; - bool operator!=(const Vector2 &p_vec2) const; - - bool operator<(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } - bool operator<=(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y <= p_vec2.y) : (x <= p_vec2.x); } - - real_t angle() const; - - void set_rotation(real_t p_radians) { - - x = Math::cos(p_radians); - y = Math::sin(p_radians); - } - - _FORCE_INLINE_ Vector2 abs() const { - - return Vector2(Math::abs(x), Math::abs(y)); - } - - Vector2 rotated(real_t p_by) const; - Vector2 tangent() const { - - return Vector2(y, -x); - } - - Vector2 floor() const; - Vector2 snapped(const Vector2 &p_by) const; - real_t aspect() const { return width / height; } - - operator String() const { return String::num(x) + ", " + String::num(y); } - - _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) { - x = p_x; - y = p_y; - } - _FORCE_INLINE_ Vector2() { - x = 0; - y = 0; - } -}; - -_FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { - - return p_vec - *this * (dot(p_vec) - p_d); -} - -_FORCE_INLINE_ Vector2 operator*(real_t p_scalar, const Vector2 &p_vec) { - - return p_vec * p_scalar; -} - -_FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const { - - return Vector2(x + p_v.x, y + p_v.y); -} -_FORCE_INLINE_ void Vector2::operator+=(const Vector2 &p_v) { - - x += p_v.x; - y += p_v.y; -} -_FORCE_INLINE_ Vector2 Vector2::operator-(const Vector2 &p_v) const { - - return Vector2(x - p_v.x, y - p_v.y); -} -_FORCE_INLINE_ void Vector2::operator-=(const Vector2 &p_v) { - - x -= p_v.x; - y -= p_v.y; -} - -_FORCE_INLINE_ Vector2 Vector2::operator*(const Vector2 &p_v1) const { - - return Vector2(x * p_v1.x, y * p_v1.y); -}; - -_FORCE_INLINE_ Vector2 Vector2::operator*(const real_t &rvalue) const { - - return Vector2(x * rvalue, y * rvalue); -}; -_FORCE_INLINE_ void Vector2::operator*=(const real_t &rvalue) { - - x *= rvalue; - y *= rvalue; -}; - -_FORCE_INLINE_ Vector2 Vector2::operator/(const Vector2 &p_v1) const { - - return Vector2(x / p_v1.x, y / p_v1.y); -}; - -_FORCE_INLINE_ Vector2 Vector2::operator/(const real_t &rvalue) const { - - return Vector2(x / rvalue, y / rvalue); -}; - -_FORCE_INLINE_ void Vector2::operator/=(const real_t &rvalue) { - - x /= rvalue; - y /= rvalue; -}; - -_FORCE_INLINE_ Vector2 Vector2::operator-() const { - - return Vector2(-x, -y); -} - -_FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const { - - return x == p_vec2.x && y == p_vec2.y; -} -_FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { - - return x != p_vec2.x || y != p_vec2.y; -} - -Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const { - - Vector2 res = *this; - - res.x += (p_t * (p_b.x - x)); - res.y += (p_t * (p_b.y - y)); - - return res; -} - -Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) { - - Vector2 res = p_a; - - res.x += (p_t * (p_b.x - p_a.x)); - res.y += (p_t * (p_b.y - p_a.y)); - - return res; -} - -typedef Vector2 Size2; -typedef Vector2 Point2; - -struct Transform2D; - -struct Rect2 { - - Point2 position; - Size2 size; - - const Vector2 &get_position() const { return position; } - void set_position(const Vector2 &p_pos) { position = p_pos; } - const Vector2 &get_size() const { return size; } - void set_size(const Vector2 &p_size) { size = p_size; } - - real_t get_area() const { return size.width * size.height; } - - inline bool intersects(const Rect2 &p_rect) const { - if (position.x >= (p_rect.position.x + p_rect.size.width)) - return false; - if ((position.x + size.width) <= p_rect.position.x) - return false; - if (position.y >= (p_rect.position.y + p_rect.size.height)) - return false; - if ((position.y + size.height) <= p_rect.position.y) - return false; - - return true; - } - - inline real_t distance_to(const Vector2 &p_point) const { - - real_t dist = 0.0; - bool inside = true; - - if (p_point.x < position.x) { - real_t d = position.x - p_point.x; - dist = inside ? d : MIN(dist, d); - inside = false; - } - if (p_point.y < position.y) { - real_t d = position.y - p_point.y; - dist = inside ? d : MIN(dist, d); - inside = false; - } - if (p_point.x >= (position.x + size.x)) { - real_t d = p_point.x - (position.x + size.x); - dist = inside ? d : MIN(dist, d); - inside = false; - } - if (p_point.y >= (position.y + size.y)) { - real_t d = p_point.y - (position.y + size.y); - dist = inside ? d : MIN(dist, d); - inside = false; - } - - if (inside) - return 0; - else - return dist; - } - - _FORCE_INLINE_ bool intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const; - - bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = NULL, Point2 *r_normal = NULL) const; - - inline bool encloses(const Rect2 &p_rect) const { - - return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && - ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && - ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); - } - - inline bool has_no_area() const { - - return (size.x <= 0 || size.y <= 0); - } - inline Rect2 clip(const Rect2 &p_rect) const { /// return a clipped rect - - Rect2 new_rect = p_rect; - - if (!intersects(new_rect)) - return Rect2(); - - new_rect.position.x = MAX(p_rect.position.x, position.x); - new_rect.position.y = MAX(p_rect.position.y, position.y); - - Point2 p_rect_end = p_rect.position + p_rect.size; - Point2 end = position + size; - - new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; - new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; - - return new_rect; - } - - inline Rect2 merge(const Rect2 &p_rect) const { ///< return a merged rect - - Rect2 new_rect; - - new_rect.position.x = MIN(p_rect.position.x, position.x); - new_rect.position.y = MIN(p_rect.position.y, position.y); - - new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); - new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); - - new_rect.size = new_rect.size - new_rect.position; //make relative again - - return new_rect; - }; - inline bool has_point(const Point2 &p_point) const { - if (p_point.x < position.x) - return false; - if (p_point.y < position.y) - return false; - - if (p_point.x >= (position.x + size.x)) - return false; - if (p_point.y >= (position.y + size.y)) - return false; - - return true; - } - - inline bool no_area() const { return (size.width <= 0 || size.height <= 0); } - - bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } - bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } - - inline Rect2 grow(real_t p_by) const { - - Rect2 g = *this; - g.position.x -= p_by; - g.position.y -= p_by; - g.size.width += p_by * 2; - g.size.height += p_by * 2; - return g; - } - - inline Rect2 grow_margin(Margin p_margin, real_t p_amount) const { - Rect2 g = *this; - g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, - (MARGIN_TOP == p_margin) ? p_amount : 0, - (MARGIN_RIGHT == p_margin) ? p_amount : 0, - (MARGIN_BOTTOM == p_margin) ? p_amount : 0); - return g; - } - - inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { - - Rect2 g = *this; - g.position.x -= p_left; - g.position.y -= p_top; - g.size.width += p_left + p_right; - g.size.height += p_top + p_bottom; - - return g; - } - - inline Rect2 expand(const Vector2 &p_vector) const { - - Rect2 r = *this; - r.expand_to(p_vector); - return r; - } - - inline void expand_to(const Vector2 &p_vector) { //in place function for speed - - Vector2 begin = position; - Vector2 end = position + size; - - if (p_vector.x < begin.x) - begin.x = p_vector.x; - if (p_vector.y < begin.y) - begin.y = p_vector.y; - - if (p_vector.x > end.x) - end.x = p_vector.x; - if (p_vector.y > end.y) - end.y = p_vector.y; - - position = begin; - size = end - begin; - } - - inline Rect2 abs() const { - - return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); - } - - operator String() const { return String(position) + ", " + String(size); } - - Rect2() {} - Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) : - position(Point2(p_x, p_y)), - size(Size2(p_width, p_height)) { - } - Rect2(const Point2 &p_pos, const Size2 &p_size) : - position(p_pos), - size(p_size) { - } -}; - -/* INTEGER STUFF */ - -struct Point2i { - - union { - int x; - int width; - }; - union { - int y; - int height; - }; - - _FORCE_INLINE_ int &operator[](int p_idx) { - return p_idx ? y : x; - } - _FORCE_INLINE_ const int &operator[](int p_idx) const { - return p_idx ? y : x; - } - - Point2i operator+(const Point2i &p_v) const; - void operator+=(const Point2i &p_v); - Point2i operator-(const Point2i &p_v) const; - void operator-=(const Point2i &p_v); - Point2i operator*(const Point2i &p_v1) const; - - Point2i operator*(const int &rvalue) const; - void operator*=(const int &rvalue); - - Point2i operator/(const Point2i &p_v1) const; - - Point2i operator/(const int &rvalue) const; - - void operator/=(const int &rvalue); - - Point2i operator-() const; - bool operator<(const Point2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } - bool operator>(const Point2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } - - bool operator==(const Point2i &p_vec2) const; - bool operator!=(const Point2i &p_vec2) const; - - real_t get_aspect() const { return width / (real_t)height; } - - operator String() const { return String::num(x) + ", " + String::num(y); } - - operator Vector2() const { return Vector2(x, y); } - inline Point2i(const Vector2 &p_vec2) { - x = (int)p_vec2.x; - y = (int)p_vec2.y; - } - inline Point2i(int p_x, int p_y) { - x = p_x; - y = p_y; - } - inline Point2i() { - x = 0; - y = 0; - } -}; - -typedef Point2i Size2i; - -struct Rect2i { - - Point2i position; - Size2i size; - - const Point2i &get_position() const { return position; } - void set_position(const Point2i &p_pos) { position = p_pos; } - const Point2i &get_size() const { return size; } - void set_size(const Point2i &p_size) { size = p_size; } - - int get_area() const { return size.width * size.height; } - - inline bool intersects(const Rect2i &p_rect) const { - if (position.x > (p_rect.position.x + p_rect.size.width)) - return false; - if ((position.x + size.width) < p_rect.position.x) - return false; - if (position.y > (p_rect.position.y + p_rect.size.height)) - return false; - if ((position.y + size.height) < p_rect.position.y) - return false; - - return true; - } - - inline bool encloses(const Rect2i &p_rect) const { - - return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && - ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && - ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); - } - - inline bool has_no_area() const { - - return (size.x <= 0 || size.y <= 0); - } - inline Rect2i clip(const Rect2i &p_rect) const { /// return a clipped rect - - Rect2i new_rect = p_rect; - - if (!intersects(new_rect)) - return Rect2i(); - - new_rect.position.x = MAX(p_rect.position.x, position.x); - new_rect.position.y = MAX(p_rect.position.y, position.y); - - Point2 p_rect_end = p_rect.position + p_rect.size; - Point2 end = position + size; - - new_rect.size.x = (int)(MIN(p_rect_end.x, end.x) - new_rect.position.x); - new_rect.size.y = (int)(MIN(p_rect_end.y, end.y) - new_rect.position.y); - - return new_rect; - } - - inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect - - Rect2i new_rect; - - new_rect.position.x = MIN(p_rect.position.x, position.x); - new_rect.position.y = MIN(p_rect.position.y, position.y); - - new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); - new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); - - new_rect.size = new_rect.size - new_rect.position; //make relative again - - return new_rect; - }; - bool has_point(const Point2 &p_point) const { - if (p_point.x < position.x) - return false; - if (p_point.y < position.y) - return false; - - if (p_point.x >= (position.x + size.x)) - return false; - if (p_point.y >= (position.y + size.y)) - return false; - - return true; - } - - bool no_area() { return (size.width <= 0 || size.height <= 0); } - - bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } - bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } - - Rect2i grow(int p_by) const { - - Rect2i g = *this; - g.position.x -= p_by; - g.position.y -= p_by; - g.size.width += p_by * 2; - g.size.height += p_by * 2; - return g; - } - - inline void expand_to(const Point2i &p_vector) { - - Point2i begin = position; - Point2i end = position + size; - - if (p_vector.x < begin.x) - begin.x = p_vector.x; - if (p_vector.y < begin.y) - begin.y = p_vector.y; - - if (p_vector.x > end.x) - end.x = p_vector.x; - if (p_vector.y > end.y) - end.y = p_vector.y; - - position = begin; - size = end - begin; - } - - operator String() const { return String(position) + ", " + String(size); } - - operator Rect2() const { return Rect2(position, size); } - Rect2i(const Rect2 &p_r2) : - position(p_r2.position), - size(p_r2.size) { - } - Rect2i() {} - Rect2i(int p_x, int p_y, int p_width, int p_height) : - position(Point2(p_x, p_y)), - size(Size2(p_width, p_height)) { - } - Rect2i(const Point2 &p_pos, const Size2 &p_size) : - position(p_pos), - size(p_size) { - } -}; - -struct Transform2D { - // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": - // M = (elements[0][0] elements[1][0]) - // (elements[0][1] elements[1][1]) - // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as elements[i]. - // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to elements[1][0] here. - // This requires additional care when working with explicit indices. - // See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading. - - // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down, - // and angle is measure from +X to +Y in a clockwise-fashion. - - Vector2 elements[3]; - - _FORCE_INLINE_ real_t tdotx(const Vector2 &v) const { return elements[0][0] * v.x + elements[1][0] * v.y; } - _FORCE_INLINE_ real_t tdoty(const Vector2 &v) const { return elements[0][1] * v.x + elements[1][1] * v.y; } - - const Vector2 &operator[](int p_idx) const { return elements[p_idx]; } - Vector2 &operator[](int p_idx) { return elements[p_idx]; } - - _FORCE_INLINE_ Vector2 get_axis(int p_axis) const { - ERR_FAIL_INDEX_V(p_axis, 3, Vector2()); - return elements[p_axis]; - } - _FORCE_INLINE_ void set_axis(int p_axis, const Vector2 &p_vec) { - ERR_FAIL_INDEX(p_axis, 3); - elements[p_axis] = p_vec; - } - - void invert(); - Transform2D inverse() const; - - void affine_invert(); - Transform2D affine_inverse() const; - - void set_rotation(real_t p_rot); - real_t get_rotation() const; - _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); - void rotate(real_t p_phi); - - void scale(const Size2 &p_scale); - void scale_basis(const Size2 &p_scale); - void translate(real_t p_tx, real_t p_ty); - void translate(const Vector2 &p_translation); - - real_t basis_determinant() const; - - Size2 get_scale() const; - - _FORCE_INLINE_ const Vector2 &get_origin() const { return elements[2]; } - _FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { elements[2] = p_origin; } - - Transform2D scaled(const Size2 &p_scale) const; - Transform2D basis_scaled(const Size2 &p_scale) const; - Transform2D translated(const Vector2 &p_offset) const; - Transform2D rotated(real_t p_phi) const; - - Transform2D untranslated() const; - - void orthonormalize(); - Transform2D orthonormalized() const; - - bool operator==(const Transform2D &p_transform) const; - bool operator!=(const Transform2D &p_transform) const; - - void operator*=(const Transform2D &p_transform); - Transform2D operator*(const Transform2D &p_transform) const; - - Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; - - _FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const; - _FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const; - _FORCE_INLINE_ Vector2 xform(const Vector2 &p_vec) const; - _FORCE_INLINE_ Vector2 xform_inv(const Vector2 &p_vec) const; - _FORCE_INLINE_ Rect2 xform(const Rect2 &p_rect) const; - _FORCE_INLINE_ Rect2 xform_inv(const Rect2 &p_rect) const; - - operator String() const; - - Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { - - elements[0][0] = xx; - elements[0][1] = xy; - elements[1][0] = yx; - elements[1][1] = yy; - elements[2][0] = ox; - elements[2][1] = oy; - } - - Transform2D(real_t p_rot, const Vector2 &p_pos); - Transform2D() { - elements[0][0] = 1.0; - elements[1][1] = 1.0; - } -}; - -bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { - - //SAT intersection between local and transformed rect2 - - Vector2 xf_points[4] = { - p_xform.xform(p_rect.position), - p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)), - p_xform.xform(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), - p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), - }; - - real_t low_limit; - - //base rect2 first (faster) - - if (xf_points[0].y > position.y) - goto next1; - if (xf_points[1].y > position.y) - goto next1; - if (xf_points[2].y > position.y) - goto next1; - if (xf_points[3].y > position.y) - goto next1; - - return false; - -next1: - - low_limit = position.y + size.y; - - if (xf_points[0].y < low_limit) - goto next2; - if (xf_points[1].y < low_limit) - goto next2; - if (xf_points[2].y < low_limit) - goto next2; - if (xf_points[3].y < low_limit) - goto next2; - - return false; - -next2: - - if (xf_points[0].x > position.x) - goto next3; - if (xf_points[1].x > position.x) - goto next3; - if (xf_points[2].x > position.x) - goto next3; - if (xf_points[3].x > position.x) - goto next3; - - return false; - -next3: - - low_limit = position.x + size.x; - - if (xf_points[0].x < low_limit) - goto next4; - if (xf_points[1].x < low_limit) - goto next4; - if (xf_points[2].x < low_limit) - goto next4; - if (xf_points[3].x < low_limit) - goto next4; - - return false; - -next4: - - Vector2 xf_points2[4] = { - position, - Vector2(position.x + size.x, position.y), - Vector2(position.x, position.y + size.y), - Vector2(position.x + size.x, position.y + size.y), - }; - - real_t maxa = p_xform.elements[0].dot(xf_points2[0]); - real_t mina = maxa; - - real_t dp = p_xform.elements[0].dot(xf_points2[1]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - dp = p_xform.elements[0].dot(xf_points2[2]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - dp = p_xform.elements[0].dot(xf_points2[3]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - real_t maxb = p_xform.elements[0].dot(xf_points[0]); - real_t minb = maxb; - - dp = p_xform.elements[0].dot(xf_points[1]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - dp = p_xform.elements[0].dot(xf_points[2]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - dp = p_xform.elements[0].dot(xf_points[3]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - if (mina > maxb) - return false; - if (minb > maxa) - return false; - - maxa = p_xform.elements[1].dot(xf_points2[0]); - mina = maxa; - - dp = p_xform.elements[1].dot(xf_points2[1]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - dp = p_xform.elements[1].dot(xf_points2[2]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - dp = p_xform.elements[1].dot(xf_points2[3]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - maxb = p_xform.elements[1].dot(xf_points[0]); - minb = maxb; - - dp = p_xform.elements[1].dot(xf_points[1]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - dp = p_xform.elements[1].dot(xf_points[2]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - dp = p_xform.elements[1].dot(xf_points[3]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - if (mina > maxb) - return false; - if (minb > maxa) - return false; - - return true; -} - -Vector2 Transform2D::basis_xform(const Vector2 &p_vec) const { - - return Vector2( - tdotx(p_vec), - tdoty(p_vec)); -} - -Vector2 Transform2D::basis_xform_inv(const Vector2 &p_vec) const { - - return Vector2( - elements[0].dot(p_vec), - elements[1].dot(p_vec)); -} - -Vector2 Transform2D::xform(const Vector2 &p_vec) const { - - return Vector2( - tdotx(p_vec), - tdoty(p_vec)) + - elements[2]; -} -Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { - - Vector2 v = p_vec - elements[2]; - - return Vector2( - elements[0].dot(v), - elements[1].dot(v)); -} -Rect2 Transform2D::xform(const Rect2 &p_rect) const { - - Vector2 x = elements[0] * p_rect.size.x; - Vector2 y = elements[1] * p_rect.size.y; - Vector2 pos = xform(p_rect.position); - - Rect2 new_rect; - new_rect.position = pos; - new_rect.expand_to(pos + x); - new_rect.expand_to(pos + y); - new_rect.expand_to(pos + x + y); - return new_rect; -} - -void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { - - elements[0][0] = Math::cos(p_rot) * p_scale.x; - elements[1][1] = Math::cos(p_rot) * p_scale.y; - elements[1][0] = -Math::sin(p_rot) * p_scale.y; - elements[0][1] = Math::sin(p_rot) * p_scale.x; -} - -Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { - - Vector2 ends[4] = { - xform_inv(p_rect.position), - xform_inv(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), - xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), - xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)) - }; - - Rect2 new_rect; - new_rect.position = ends[0]; - new_rect.expand_to(ends[1]); - new_rect.expand_to(ends[2]); - new_rect.expand_to(ends[3]); - - return new_rect; -} - -#endif diff --git a/core/math/math_defs.h b/core/math/math_defs.h index d3484d8d02a3..a5feee6eb581 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -36,30 +36,71 @@ #define CMP_NORMALIZE_TOLERANCE 0.000001 #define CMP_POINT_IN_PLANE_EPSILON 0.00001 +#define Math_SQRT12 0.7071067811865475244008443621048490 +#define Math_SQRT2 1.4142135623730950488016887242 +#define Math_LN2 0.6931471805599453094172321215 +#define Math_TAU 6.2831853071795864769252867666 +#define Math_PI 3.1415926535897932384626433833 +#define Math_E 2.7182818284590452353602874714 +#define Math_INF INFINITY +#define Math_NAN NAN + #ifdef DEBUG_ENABLED #define MATH_CHECKS #endif #define USEC_TO_SEC(m_usec) ((m_usec) / 1000000.0) -/** - * "Real" is a type that will be translated to either floats or fixed depending - * on the compilation setting - */ enum ClockDirection { - CLOCKWISE, COUNTERCLOCKWISE }; -#ifdef REAL_T_IS_DOUBLE +enum Orientation { -typedef double real_t; + HORIZONTAL, + VERTICAL +}; -#else +enum HAlign { -typedef float real_t; + HALIGN_LEFT, + HALIGN_CENTER, + HALIGN_RIGHT +}; + +enum VAlign { + + VALIGN_TOP, + VALIGN_CENTER, + VALIGN_BOTTOM +}; +enum Margin { + + MARGIN_LEFT, + MARGIN_TOP, + MARGIN_RIGHT, + MARGIN_BOTTOM +}; + +enum Corner { + + CORNER_TOP_LEFT, + CORNER_TOP_RIGHT, + CORNER_BOTTOM_RIGHT, + CORNER_BOTTOM_LEFT +}; + +/** + * The "Real" type is an abstract type used for real numbers, such as 1.5, + * in contrast to integer numbers. Precision can be controlled with the + * presence or absence of the REAL_T_IS_DOUBLE define. + */ +#ifdef REAL_T_IS_DOUBLE +typedef double real_t; +#else +typedef float real_t; #endif #endif // MATH_DEFS_H diff --git a/core/helper/math_fieldwise.cpp b/core/math/math_fieldwise.cpp similarity index 99% rename from core/helper/math_fieldwise.cpp rename to core/math/math_fieldwise.cpp index ff3f8b352038..20b2341ab0ce 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 similarity index 100% rename from core/helper/math_fieldwise.h rename to core/math/math_fieldwise.h diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index 5c8512d8bda9..06355d15ed97 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -30,34 +30,31 @@ #include "math_funcs.h" -#include "core/os/os.h" - -pcg32_random_t Math::default_pcg = { 12047754176567800795ULL, PCG_DEFAULT_INC_64 }; +RandomPCG Math::default_rand(RandomPCG::DEFAULT_SEED, RandomPCG::DEFAULT_INC); #define PHI 0x9e3779b9 -// TODO: we should eventually expose pcg.inc too uint32_t Math::rand_from_seed(uint64_t *seed) { - pcg32_random_t pcg = { *seed, PCG_DEFAULT_INC_64 }; - uint32_t r = pcg32_random_r(&pcg); - *seed = pcg.state; + RandomPCG rng = RandomPCG(*seed, RandomPCG::DEFAULT_INC); + uint32_t r = rng.rand(); + *seed = rng.get_seed(); return r; } void Math::seed(uint64_t x) { - default_pcg.state = x; + default_rand.seed(x); } void Math::randomize() { - seed(OS::get_singleton()->get_ticks_usec() * default_pcg.state + PCG_DEFAULT_INC_64); + default_rand.randomize(); } uint32_t Math::rand() { - return pcg32_random_r(&default_pcg); + return default_rand.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 +64,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) { @@ -167,13 +166,9 @@ uint32_t Math::larger_prime(uint32_t p_val) { } double Math::random(double from, double to) { - unsigned int r = Math::rand(); - double ret = (double)r / (double)RANDOM_MAX; - return (ret) * (to - from) + from; + return default_rand.random(from, to); } float Math::random(float from, float to) { - unsigned int r = Math::rand(); - float ret = (float)r / (float)RANDOM_MAX; - return (ret) * (to - from) + from; + return default_rand.random(from, to); } diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 20001bb9a6f2..f9d89d5d5ac9 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -31,29 +31,23 @@ #ifndef MATH_FUNCS_H #define MATH_FUNCS_H -#include "math_defs.h" -#include "typedefs.h" +#include "core/math/math_defs.h" +#include "core/math/random_pcg.h" +#include "core/typedefs.h" #include "thirdparty/misc/pcg.h" #include #include -#define Math_PI 3.14159265358979323846 -#define Math_TAU 6.28318530717958647692 -#define Math_SQRT12 0.7071067811865475244008443621048490 -#define Math_LN2 0.693147180559945309417 -#define Math_INF INFINITY -#define Math_NAN NAN - class Math { - static pcg32_random_t default_pcg; + static RandomPCG default_rand; 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); } @@ -182,8 +176,22 @@ class Math { static _ALWAYS_INLINE_ float abs(float g) { return absf(g); } static _ALWAYS_INLINE_ int abs(int g) { return g > 0 ? g : -g; } - static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) { return (p_x >= 0) ? Math::fmod(p_x, p_y) : p_y - Math::fmod(-p_x, p_y); } - static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) { return (p_x >= 0) ? Math::fmod(p_x, p_y) : p_y - Math::fmod(-p_x, p_y); } + static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) { + double value = Math::fmod(p_x, p_y); + if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) { + value += p_y; + } + value += 0.0; + return value; + } + static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) { + float value = Math::fmod(p_x, p_y); + if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) { + value += p_y; + } + value += 0.0; + return value; + } static _ALWAYS_INLINE_ double deg2rad(double p_y) { return p_y * Math_PI / 180.0; } static _ALWAYS_INLINE_ float deg2rad(float p_y) { return p_y * Math_PI / 180.0; } @@ -298,16 +306,6 @@ class Math { 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 b0b05d1ec87f..925a7b3f1e77 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]) @@ -242,18 +244,11 @@ void Basis::scale_local(const Vector3 &p_scale) { Basis Basis::scaled_local(const Vector3 &p_scale) const { Basis b; - b.set_scale(p_scale); + b.set_diagonal(p_scale); return (*this) * b; } -void Basis::set_scale(const Vector3 &p_scale) { - - set_axis(0, get_axis(0).normalized() * p_scale.x); - set_axis(1, get_axis(1).normalized() * p_scale.y); - set_axis(2, get_axis(2).normalized() * p_scale.z); -} - Vector3 Basis::get_scale_abs() const { return Vector3( @@ -304,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(); } @@ -356,8 +351,7 @@ void Basis::rotate(const Quat &p_quat) { *this = rotated(p_quat); } -// TODO: rename this to get_rotation_euler -Vector3 Basis::get_rotation() const { +Vector3 Basis::get_rotation_euler() const { // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). // See the comment in get_scale() for further information. @@ -371,6 +365,20 @@ Vector3 Basis::get_rotation() const { return m.get_euler(); } +Quat Basis::get_rotation_quat() const { + // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, + // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). + // See the comment in get_scale() for further information. + Basis m = orthonormalized(); + real_t det = m.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. + m.scale(Vector3(-1, -1, -1)); + } + + return m.get_quat(); +} + void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const { // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). @@ -422,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) { @@ -489,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]; @@ -548,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; } } @@ -591,10 +599,9 @@ Basis::operator String() const { } Quat Basis::get_quat() const { - //commenting this check because precision issues cause it to fail when it shouldn't - //#ifdef MATH_CHECKS - //ERR_FAIL_COND_V(is_rotation() == false, Quat()); - //#endif +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!is_rotation(), Quat()); +#endif real_t trace = elements[0][0] + elements[1][1] + elements[2][2]; real_t temp[4]; @@ -690,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 @@ -778,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); @@ -826,3 +833,16 @@ void Basis::set_diagonal(const Vector3 p_diag) { elements[2][1] = 0; elements[2][2] = p_diag.z; } + +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(), Basis()); + ERR_FAIL_COND_V(!target.is_rotation(), Basis()); +#endif + + Quat from(*this); + Quat to(target); + + return Basis(from.slerp(to, t)); +} diff --git a/core/math/matrix3.h b/core/math/matrix3.h index fd383fc673c4..35bf75bbe4a6 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 */ + class Basis { public: Vector3 elements[3]; @@ -84,9 +86,11 @@ class Basis { void rotate(const Quat &p_quat); Basis rotated(const Quat &p_quat) const; - Vector3 get_rotation() const; + Vector3 get_rotation_euler() const; void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const; void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const; + Quat get_rotation_quat() const; + Vector3 get_rotation() const { return get_rotation_euler(); }; Vector3 rotref_posscale_decomposition(Basis &rotref) const; @@ -110,7 +114,6 @@ class Basis { void scale_local(const Vector3 &p_scale); Basis scaled_local(const Vector3 &p_scale) const; - void set_scale(const Vector3 &p_scale); Vector3 get_scale() const; Vector3 get_scale_abs() const; Vector3 get_scale_local() const; @@ -155,6 +158,8 @@ class Basis { bool is_diagonal() const; bool is_rotation() const; + Basis slerp(const Basis &target, const real_t &t) const; + operator String() const; /* create / set */ @@ -228,10 +233,13 @@ class Basis { operator Quat() const { return get_quat(); } Basis(const Quat &p_quat) { set_quat(p_quat); }; + Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } + Basis(const Vector3 &p_euler) { set_euler(p_euler); } + Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); } + Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); } Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); } - Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { elements[0] = row0; diff --git a/core/math/octree.h b/core/math/octree.h index 4e3d6257f085..cd89743a5a16 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 @@ -478,7 +478,7 @@ void Octree::_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 78bb1771a4b9..3c597d57f8f6 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 e567422dd0fb..4eedebb79e8b 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 4f61401ac72a..791e84f0891e 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, @@ -98,6 +99,9 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) { // and similar for other axes. // 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(), Vector3(0, 0, 0)); +#endif Basis m(*this); return m.get_euler_yxz(); } @@ -131,15 +135,21 @@ 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(), 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(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); +#endif Quat to1; real_t omega, cosom, sinom, scale0, scale1; @@ -183,7 +193,10 @@ 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(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); +#endif const Quat &from = *this; real_t dot = from.dot(q); @@ -202,7 +215,10 @@ 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(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); +#endif //the only way to do slerp :| real_t t2 = (1.0 - t) * t * 2; Quat sp = this->slerp(q, t); @@ -215,7 +231,10 @@ Quat::operator String() const { return String::num(x) + ", " + String::num(y) + ", " + String::num(z) + ", " + String::num(w); } -Quat::Quat(const Vector3 &axis, const real_t &angle) { +void Quat::set_axis_angle(const Vector3 &axis, const real_t &angle) { +#ifdef MATH_CHECKS + ERR_FAIL_COND(!axis.is_normalized()); +#endif real_t d = axis.length(); if (d == 0) set(0, 0, 0, 0); diff --git a/core/math/quat.h b/core/math/quat.h index ebc924504ba6..c4f9b3a732da 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 */ + class Quat { public: real_t x, y, z, w; @@ -64,11 +66,13 @@ class Quat { Quat slerpni(const Quat &q, const real_t &t) const; Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const; + void set_axis_angle(const Vector3 &axis, const real_t &angle); _FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { r_angle = 2 * Math::acos(w); - r_axis.x = x / Math::sqrt(1 - w * w); - r_axis.y = y / Math::sqrt(1 - w * w); - r_axis.z = z / Math::sqrt(1 - w * w); + real_t r = ((real_t)1) / Math::sqrt(1 - w * w); + r_axis.x = x * r; + r_axis.y = y * r; + r_axis.z = z * r; } void operator*=(const Quat &q); @@ -82,10 +86,12 @@ class Quat { } _FORCE_INLINE_ Vector3 xform(const Vector3 &v) const { - - Quat q = *this * v; - q *= this->inverse(); - return Vector3(q.x, q.y, q.z); +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!is_normalized(), v); +#endif + Vector3 u(x, y, z); + Vector3 uv = u.cross(v); + return v + ((uv * w) + u.cross(uv)) * ((real_t)2); } _FORCE_INLINE_ void operator+=(const Quat &q); @@ -115,7 +121,15 @@ class Quat { z = p_z; w = p_w; } - Quat(const Vector3 &axis, const real_t &angle); + Quat(const Vector3 &axis, const real_t &angle) { set_axis_angle(axis, angle); } + + Quat(const Vector3 &euler) { set_euler(euler); } + Quat(const Quat &q) { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + } Quat(const Vector3 &v0, const Vector3 &v1) // shortest arc { diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 102e454e021a..23823b339a51 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; @@ -61,10 +62,9 @@ Error QuickHull::build(const Vector &p_points, Geometry::MeshData &r_me Vector3 sp = p_points[i].snapped(Vector3(0.0001, 0.0001, 0.0001)); if (valid_cache.has(sp)) { - valid_points[i] = false; - //print_line("INVALIDATED: "+itos(i)); + valid_points.write[i] = false; } else { - valid_points[i] = true; + valid_points.write[i] = true; valid_cache.insert(sp); } } @@ -74,7 +74,7 @@ Error QuickHull::build(const Vector &p_points, Geometry::MeshData &r_me int longest_axis = aabb.get_longest_axis_index(); //first two vertices are the most distant - int simplex[4]; + int simplex[4] = { 0 }; { real_t max = 0, min = 0; @@ -397,7 +397,6 @@ Error QuickHull::build(const Vector &p_points, Geometry::MeshData &r_me Map::Element *F = ret_edges.find(e); ERR_CONTINUE(!F); - List::Element *O = F->get().left == E ? F->get().right : F->get().left; ERR_CONTINUE(O == E); ERR_CONTINUE(O == NULL); @@ -426,7 +425,6 @@ Error QuickHull::build(const Vector &p_points, Geometry::MeshData &r_me Edge e2(idx, idxn); Map::Element *F2 = ret_edges.find(e2); - ERR_CONTINUE(!F2); //change faceconnect, point to this face instead if (F2->get().left == O) @@ -439,6 +437,15 @@ Error QuickHull::build(const Vector &p_points, Geometry::MeshData &r_me } } + // remove all edge connections to this face + for (Map::Element *E = ret_edges.front(); E; E = E->next()) { + if (E->get().left == O) + E->get().left = NULL; + + if (E->get().right == O) + E->get().right = NULL; + } + ret_edges.erase(F); //remove the edge ret_faces.erase(O); //remove the face } @@ -448,11 +455,10 @@ Error QuickHull::build(const Vector &p_points, Geometry::MeshData &r_me //fill mesh r_mesh.faces.clear(); r_mesh.faces.resize(ret_faces.size()); - //print_line("FACECOUNT: "+itos(r_mesh.faces.size())); int idx = 0; for (List::Element *E = ret_faces.front(); E; E = E->next()) { - r_mesh.faces[idx++] = E->get(); + r_mesh.faces.write[idx++] = E->get(); } r_mesh.edges.resize(ret_edges.size()); idx = 0; @@ -461,17 +467,10 @@ Error QuickHull::build(const Vector &p_points, Geometry::MeshData &r_me Geometry::MeshData::Edge e; e.a = E->key().vertices[0]; e.b = E->key().vertices[1]; - r_mesh.edges[idx++] = e; + r_mesh.edges.write[idx++] = e; } r_mesh.vertices = p_points; - //r_mesh.optimize_vertices(); - /* - print_line("FACES: "+itos(r_mesh.faces.size())); - print_line("EDGES: "+itos(r_mesh.edges.size())); - print_line("VERTICES: "+itos(r_mesh.vertices.size())); -*/ - return OK; } diff --git a/core/math/quick_hull.h b/core/math/quick_hull.h index eef4a9adff70..0ac275832323 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/random_number_generator.cpp b/core/math/random_number_generator.cpp new file mode 100644 index 000000000000..e4ec0dac9979 --- /dev/null +++ b/core/math/random_number_generator.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* random_number_generator.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 "random_number_generator.h" + +RandomNumberGenerator::RandomNumberGenerator() : + randbase() {} + +void RandomNumberGenerator::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_seed", "seed"), &RandomNumberGenerator::set_seed); + ClassDB::bind_method(D_METHOD("get_seed"), &RandomNumberGenerator::get_seed); + ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); + + ClassDB::bind_method(D_METHOD("randi"), &RandomNumberGenerator::randi); + ClassDB::bind_method(D_METHOD("randf"), &RandomNumberGenerator::randf); + ClassDB::bind_method(D_METHOD("rand_range", "from", "to"), &RandomNumberGenerator::rand_range); + ClassDB::bind_method(D_METHOD("randomize"), &RandomNumberGenerator::randomize); +} diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h new file mode 100644 index 000000000000..557863fdbdf1 --- /dev/null +++ b/core/math/random_number_generator.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* random_number_generator.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 RANDOM_NUMBER_GENERATOR_H +#define RANDOM_NUMBER_GENERATOR_H + +#include "core/math/random_pcg.h" +#include "core/reference.h" + +class RandomNumberGenerator : public Reference { + GDCLASS(RandomNumberGenerator, Reference); + + RandomPCG randbase; + +protected: + static void _bind_methods(); + +public: + _FORCE_INLINE_ void set_seed(uint64_t seed) { randbase.seed(seed); } + + _FORCE_INLINE_ uint64_t get_seed() { return randbase.get_seed(); } + + _FORCE_INLINE_ void randomize() { return randbase.randomize(); } + + _FORCE_INLINE_ uint32_t randi() { return randbase.rand(); } + + _FORCE_INLINE_ real_t randf() { return randbase.randf(); } + + _FORCE_INLINE_ real_t rand_range(real_t from, real_t to) { return randbase.random(from, to); } + + RandomNumberGenerator(); +}; + +#endif // RANDOM_NUMBER_GENERATOR_H diff --git a/drivers/unix/tcp_server_posix.h b/core/math/random_pcg.cpp similarity index 79% rename from drivers/unix/tcp_server_posix.h rename to core/math/random_pcg.cpp index c749314fb3ba..16899f79da0d 100644 --- a/drivers/unix/tcp_server_posix.h +++ b/core/math/random_pcg.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* tcp_server_posix.h */ +/* random_pcg.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,31 +28,28 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TCP_SERVER_POSIX_H -#define TCP_SERVER_POSIX_H +#include "random_pcg.h" -#ifdef UNIX_ENABLED -#include "core/io/tcp_server.h" +#include "core/os/os.h" -class TCPServerPosix : public TCP_Server { +RandomPCG::RandomPCG(uint64_t seed, uint64_t inc) : + pcg() { + pcg.state = seed; + pcg.inc = inc; +} - int listen_sockfd; - IP::Type sock_type; +void RandomPCG::randomize() { + seed(OS::get_singleton()->get_ticks_usec() * pcg.state + PCG_DEFAULT_INC_64); +} - static TCP_Server *_create(); +double RandomPCG::random(double from, double to) { + unsigned int r = rand(); + double ret = (double)r / (double)RANDOM_MAX; + return (ret) * (to - from) + from; +} -public: - virtual Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); - virtual bool is_connection_available() const; - virtual Ref take_connection(); - - virtual void stop(); - - static void make_default(); - - TCPServerPosix(); - ~TCPServerPosix(); -}; - -#endif // TCP_SERVER_POSIX_H -#endif +float RandomPCG::random(float from, float to) { + unsigned int r = rand(); + float ret = (float)r / (float)RANDOM_MAX; + return (ret) * (to - from) + from; +} diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h new file mode 100644 index 000000000000..4a43c36ede5a --- /dev/null +++ b/core/math/random_pcg.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* random_pcg.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 RANDOM_PCG_H +#define RANDOM_PCG_H + +#include "core/math/math_defs.h" + +#include "thirdparty/misc/pcg.h" + +class RandomPCG { + pcg32_random_t pcg; + +public: + static const uint64_t DEFAULT_SEED = 12047754176567800795ULL; + static const uint64_t DEFAULT_INC = PCG_DEFAULT_INC_64; + static const uint64_t RANDOM_MAX = 4294967295; + + RandomPCG(uint64_t seed = DEFAULT_SEED, uint64_t inc = PCG_DEFAULT_INC_64); + + _FORCE_INLINE_ void seed(uint64_t seed) { pcg.state = seed; } + _FORCE_INLINE_ uint64_t get_seed() { return pcg.state; } + + void randomize(); + _FORCE_INLINE_ uint32_t rand() { return pcg32_random_r(&pcg); } + _FORCE_INLINE_ double randf() { return (double)rand() / (double)RANDOM_MAX; } + _FORCE_INLINE_ float randd() { return (float)rand() / (float)RANDOM_MAX; } + + double random(double from, double to); + float random(float from, float to); + real_t random(int from, int to) { return (real_t)random((real_t)from, (real_t)to); } +}; + +#endif // RANDOM_PCG_H diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp new file mode 100644 index 000000000000..24c1c8c9847f --- /dev/null +++ b/core/math/rect2.cpp @@ -0,0 +1,240 @@ +/*************************************************************************/ +/* rect2.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 "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 { + + real_t min = 0, max = 1; + int axis = 0; + real_t sign = 0; + + for (int i = 0; i < 2; i++) { + real_t seg_from = p_from[i]; + real_t seg_to = p_to[i]; + real_t box_begin = position[i]; + real_t box_end = box_begin + size[i]; + real_t cmin, cmax; + real_t csign; + + if (seg_from < seg_to) { + + if (seg_from > box_end || seg_to < box_begin) + return false; + real_t length = seg_to - seg_from; + cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; + cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; + csign = -1.0; + + } else { + + if (seg_to > box_end || seg_from < box_begin) + return false; + real_t length = seg_to - seg_from; + cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; + cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; + csign = 1.0; + } + + if (cmin > min) { + min = cmin; + axis = i; + sign = csign; + } + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + Vector2 rel = p_to - p_from; + + if (r_normal) { + Vector2 normal; + normal[axis] = sign; + *r_normal = normal; + } + + if (r_pos) + *r_pos = p_from + rel * min; + + return true; +} + +bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { + + //SAT intersection between local and transformed rect2 + + Vector2 xf_points[4] = { + p_xform.xform(p_rect.position), + p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)), + p_xform.xform(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), + p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), + }; + + real_t low_limit; + + //base rect2 first (faster) + + if (xf_points[0].y > position.y) + goto next1; + if (xf_points[1].y > position.y) + goto next1; + if (xf_points[2].y > position.y) + goto next1; + if (xf_points[3].y > position.y) + goto next1; + + return false; + +next1: + + low_limit = position.y + size.y; + + if (xf_points[0].y < low_limit) + goto next2; + if (xf_points[1].y < low_limit) + goto next2; + if (xf_points[2].y < low_limit) + goto next2; + if (xf_points[3].y < low_limit) + goto next2; + + return false; + +next2: + + if (xf_points[0].x > position.x) + goto next3; + if (xf_points[1].x > position.x) + goto next3; + if (xf_points[2].x > position.x) + goto next3; + if (xf_points[3].x > position.x) + goto next3; + + return false; + +next3: + + low_limit = position.x + size.x; + + if (xf_points[0].x < low_limit) + goto next4; + if (xf_points[1].x < low_limit) + goto next4; + if (xf_points[2].x < low_limit) + goto next4; + if (xf_points[3].x < low_limit) + goto next4; + + return false; + +next4: + + Vector2 xf_points2[4] = { + position, + Vector2(position.x + size.x, position.y), + Vector2(position.x, position.y + size.y), + Vector2(position.x + size.x, position.y + size.y), + }; + + real_t maxa = p_xform.elements[0].dot(xf_points2[0]); + real_t mina = maxa; + + real_t dp = p_xform.elements[0].dot(xf_points2[1]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[0].dot(xf_points2[2]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[0].dot(xf_points2[3]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + real_t maxb = p_xform.elements[0].dot(xf_points[0]); + real_t minb = maxb; + + dp = p_xform.elements[0].dot(xf_points[1]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[0].dot(xf_points[2]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[0].dot(xf_points[3]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + if (mina > maxb) + return false; + if (minb > maxa) + return false; + + maxa = p_xform.elements[1].dot(xf_points2[0]); + mina = maxa; + + dp = p_xform.elements[1].dot(xf_points2[1]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[1].dot(xf_points2[2]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[1].dot(xf_points2[3]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + maxb = p_xform.elements[1].dot(xf_points[0]); + minb = maxb; + + dp = p_xform.elements[1].dot(xf_points[1]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[1].dot(xf_points[2]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[1].dot(xf_points[3]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + if (mina > maxb) + return false; + if (minb > maxa) + return false; + + return true; +} diff --git a/core/math/rect2.h b/core/math/rect2.h new file mode 100644 index 000000000000..96c0e177d3fe --- /dev/null +++ b/core/math/rect2.h @@ -0,0 +1,371 @@ +/*************************************************************************/ +/* rect2.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 RECT2_H +#define RECT2_H + +#include "core/math/vector2.h" // also includes math_funcs and ustring + +struct Transform2D; + +struct Rect2 { + + Point2 position; + Size2 size; + + const Vector2 &get_position() const { return position; } + void set_position(const Vector2 &p_pos) { position = p_pos; } + const Vector2 &get_size() const { return size; } + void set_size(const Vector2 &p_size) { size = p_size; } + + real_t get_area() const { return size.width * size.height; } + + inline bool intersects(const Rect2 &p_rect) const { + if (position.x >= (p_rect.position.x + p_rect.size.width)) + return false; + if ((position.x + size.width) <= p_rect.position.x) + return false; + if (position.y >= (p_rect.position.y + p_rect.size.height)) + return false; + if ((position.y + size.height) <= p_rect.position.y) + return false; + + return true; + } + + inline real_t distance_to(const Vector2 &p_point) const { + + real_t dist = 0.0; + bool inside = true; + + if (p_point.x < position.x) { + real_t d = position.x - p_point.x; + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.y < position.y) { + real_t d = position.y - p_point.y; + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.x >= (position.x + size.x)) { + real_t d = p_point.x - (position.x + size.x); + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.y >= (position.y + size.y)) { + real_t d = p_point.y - (position.y + size.y); + dist = inside ? d : MIN(dist, d); + inside = false; + } + + if (inside) + return 0; + else + return dist; + } + + bool intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const; + + bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = NULL, Point2 *r_normal = NULL) const; + + inline bool encloses(const Rect2 &p_rect) const { + + return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && + ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + } + + inline bool has_no_area() const { + + return (size.x <= 0 || size.y <= 0); + } + inline Rect2 clip(const Rect2 &p_rect) const { /// return a clipped rect + + Rect2 new_rect = p_rect; + + if (!intersects(new_rect)) + return Rect2(); + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2 p_rect_end = p_rect.position + p_rect.size; + Point2 end = position + size; + + new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; + new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + + return new_rect; + } + + inline Rect2 merge(const Rect2 &p_rect) const { ///< return a merged rect + + Rect2 new_rect; + + new_rect.position.x = MIN(p_rect.position.x, position.x); + new_rect.position.y = MIN(p_rect.position.y, position.y); + + new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); + new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + + new_rect.size = new_rect.size - new_rect.position; //make relative again + + return new_rect; + }; + inline bool has_point(const Point2 &p_point) const { + if (p_point.x < position.x) + return false; + if (p_point.y < position.y) + return false; + + if (p_point.x >= (position.x + size.x)) + return false; + if (p_point.y >= (position.y + size.y)) + return false; + + return true; + } + + inline bool no_area() const { return (size.width <= 0 || size.height <= 0); } + + bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } + bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } + + inline Rect2 grow(real_t p_by) const { + + Rect2 g = *this; + g.position.x -= p_by; + g.position.y -= p_by; + g.size.width += p_by * 2; + g.size.height += p_by * 2; + return g; + } + + inline Rect2 grow_margin(Margin p_margin, real_t p_amount) const { + Rect2 g = *this; + g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, + (MARGIN_TOP == p_margin) ? p_amount : 0, + (MARGIN_RIGHT == p_margin) ? p_amount : 0, + (MARGIN_BOTTOM == p_margin) ? p_amount : 0); + return g; + } + + inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { + + Rect2 g = *this; + g.position.x -= p_left; + g.position.y -= p_top; + g.size.width += p_left + p_right; + g.size.height += p_top + p_bottom; + + return g; + } + + inline Rect2 expand(const Vector2 &p_vector) const { + + Rect2 r = *this; + r.expand_to(p_vector); + return r; + } + + inline void expand_to(const Vector2 &p_vector) { //in place function for speed + + Vector2 begin = position; + Vector2 end = position + size; + + if (p_vector.x < begin.x) + begin.x = p_vector.x; + if (p_vector.y < begin.y) + begin.y = p_vector.y; + + if (p_vector.x > end.x) + end.x = p_vector.x; + if (p_vector.y > end.y) + end.y = p_vector.y; + + position = begin; + size = end - begin; + } + + inline Rect2 abs() const { + + return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); + } + + operator String() const { return String(position) + ", " + String(size); } + + Rect2() {} + Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) : + position(Point2(p_x, p_y)), + size(Size2(p_width, p_height)) { + } + Rect2(const Point2 &p_pos, const Size2 &p_size) : + position(p_pos), + size(p_size) { + } +}; + +struct Rect2i { + + Point2i position; + Size2i size; + + const Point2i &get_position() const { return position; } + void set_position(const Point2i &p_position) { position = p_position; } + const Size2i &get_size() const { return size; } + void set_size(const Size2i &p_size) { size = p_size; } + + int get_area() const { return size.width * size.height; } + + inline bool intersects(const Rect2i &p_rect) const { + if (position.x > (p_rect.position.x + p_rect.size.width)) + return false; + if ((position.x + size.width) < p_rect.position.x) + return false; + if (position.y > (p_rect.position.y + p_rect.size.height)) + return false; + if ((position.y + size.height) < p_rect.position.y) + return false; + + return true; + } + + inline bool encloses(const Rect2i &p_rect) const { + + return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && + ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + } + + inline bool has_no_area() const { + + return (size.x <= 0 || size.y <= 0); + } + inline Rect2i clip(const Rect2i &p_rect) const { /// return a clipped rect + + Rect2i new_rect = p_rect; + + if (!intersects(new_rect)) + return Rect2i(); + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2 p_rect_end = p_rect.position + p_rect.size; + Point2 end = position + size; + + new_rect.size.x = (int)(MIN(p_rect_end.x, end.x) - new_rect.position.x); + new_rect.size.y = (int)(MIN(p_rect_end.y, end.y) - new_rect.position.y); + + return new_rect; + } + + inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect + + Rect2i new_rect; + + new_rect.position.x = MIN(p_rect.position.x, position.x); + new_rect.position.y = MIN(p_rect.position.y, position.y); + + new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); + new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + + new_rect.size = new_rect.size - new_rect.position; //make relative again + + return new_rect; + }; + bool has_point(const Point2 &p_point) const { + if (p_point.x < position.x) + return false; + if (p_point.y < position.y) + return false; + + if (p_point.x >= (position.x + size.x)) + return false; + if (p_point.y >= (position.y + size.y)) + return false; + + return true; + } + + bool no_area() { return (size.width <= 0 || size.height <= 0); } + + bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } + bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } + + Rect2i grow(int p_by) const { + + Rect2i g = *this; + g.position.x -= p_by; + g.position.y -= p_by; + g.size.width += p_by * 2; + g.size.height += p_by * 2; + return g; + } + + inline void expand_to(const Point2i &p_vector) { + + Point2i begin = position; + Point2i end = position + size; + + if (p_vector.x < begin.x) + begin.x = p_vector.x; + if (p_vector.y < begin.y) + begin.y = p_vector.y; + + if (p_vector.x > end.x) + end.x = p_vector.x; + if (p_vector.y > end.y) + end.y = p_vector.y; + + position = begin; + size = end - begin; + } + + operator String() const { return String(position) + ", " + String(size); } + + operator Rect2() const { return Rect2(position, size); } + Rect2i(const Rect2 &p_r2) : + position(p_r2.position), + size(p_r2.size) { + } + Rect2i() {} + Rect2i(int p_x, int p_y, int p_width, int p_height) : + position(Point2(p_x, p_y)), + size(Size2(p_width, p_height)) { + } + Rect2i(const Point2 &p_pos, const Size2 &p_size) : + position(p_pos), + size(p_size) { + } +}; + +#endif // RECT2_H diff --git a/core/math/transform.cpp b/core/math/transform.cpp index 7cd186ca607c..75257a6e6063 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() { @@ -120,19 +121,18 @@ Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) /* not sure if very "efficient" but good enough? */ Vector3 src_scale = basis.get_scale(); - Quat src_rot = basis.orthonormalized(); + Quat src_rot = basis.get_rotation_quat(); Vector3 src_loc = origin; Vector3 dst_scale = p_transform.basis.get_scale(); - Quat dst_rot = p_transform.basis; + Quat dst_rot = p_transform.basis.get_rotation_quat(); Vector3 dst_loc = p_transform.origin; - Transform dst; //this could be made faster by using a single function in Basis.. - dst.basis = src_rot.slerp(dst_rot, p_c).normalized(); - dst.basis.set_scale(src_scale.linear_interpolate(dst_scale, p_c)); - dst.origin = src_loc.linear_interpolate(dst_loc, p_c); + Transform interp; + interp.basis.set_quat_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.linear_interpolate(dst_scale, p_c)); + interp.origin = src_loc.linear_interpolate(dst_loc, p_c); - return dst; + return interp; } void Transform::scale(const Vector3 &p_scale) { diff --git a/core/math/transform.h b/core/math/transform.h index c06eaec604dd..97c8bf9ab075 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 */ + class Transform { public: Basis basis; diff --git a/core/math/math_2d.cpp b/core/math/transform_2d.cpp similarity index 57% rename from core/math/math_2d.cpp rename to core/math/transform_2d.cpp index 3767d298a187..4bb763c87971 100644 --- a/core/math/math_2d.cpp +++ b/core/math/transform_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* math_2d.cpp */ +/* transform_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,277 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "math_2d.h" - -real_t Vector2::angle() const { - - return Math::atan2(y, x); -} - -real_t Vector2::length() const { - - return Math::sqrt(x * x + y * y); -} - -real_t Vector2::length_squared() const { - - return x * x + y * y; -} - -void Vector2::normalize() { - - real_t l = x * x + y * y; - if (l != 0) { - - l = Math::sqrt(l); - x /= l; - y /= l; - } -} - -Vector2 Vector2::normalized() const { - - Vector2 v = *this; - v.normalize(); - return v; -} - -bool Vector2::is_normalized() const { - // use length_squared() instead of length() to avoid sqrt(), makes it more stringent. - return Math::is_equal_approx(length_squared(), 1.0); -} - -real_t Vector2::distance_to(const Vector2 &p_vector2) const { - - return Math::sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y)); -} - -real_t Vector2::distance_squared_to(const Vector2 &p_vector2) const { - - return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y); -} - -real_t Vector2::angle_to(const Vector2 &p_vector2) const { - - return Math::atan2(cross(p_vector2), dot(p_vector2)); -} - -real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { - - return Math::atan2(y - p_vector2.y, x - p_vector2.x); -} - -real_t Vector2::dot(const Vector2 &p_other) const { - - return x * p_other.x + y * p_other.y; -} - -real_t Vector2::cross(const Vector2 &p_other) const { - - return x * p_other.y - y * p_other.x; -} - -Vector2 Vector2::floor() const { - - return Vector2(Math::floor(x), Math::floor(y)); -} - -Vector2 Vector2::rotated(real_t p_by) const { - - Vector2 v; - v.set_rotation(angle() + p_by); - v *= length(); - return v; -} - -Vector2 Vector2::project(const Vector2 &p_vec) const { - - Vector2 v1 = p_vec; - Vector2 v2 = *this; - return v2 * (v1.dot(v2) / v2.dot(v2)); -} - -Vector2 Vector2::snapped(const Vector2 &p_by) const { - - return Vector2( - Math::stepify(x, p_by.x), - Math::stepify(y, p_by.y)); -} - -Vector2 Vector2::clamped(real_t p_len) const { - - real_t l = length(); - Vector2 v = *this; - if (l > 0 && p_len < l) { - - v /= l; - v *= p_len; - } - - return v; -} - -Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const { - - Vector2 p0 = p_pre_a; - Vector2 p1 = *this; - Vector2 p2 = p_b; - Vector2 p3 = p_post_b; - - real_t t = p_t; - real_t t2 = t * t; - real_t t3 = t2 * t; - - Vector2 out; - out = 0.5 * ((p1 * 2.0) + - (-p0 + p2) * t + - (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 + - (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); - return out; -} - -// 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()); -#endif - return *this - p_normal * this->dot(p_normal); -} - -Vector2 Vector2::bounce(const Vector2 &p_normal) const { - return -reflect(p_normal); -} - -Vector2 Vector2::reflect(const Vector2 &p_normal) const { -#ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector2()); -#endif - return 2.0 * p_normal * this->dot(p_normal) - *this; -} - -bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { - - real_t min = 0, max = 1; - int axis = 0; - real_t sign = 0; - - for (int i = 0; i < 2; i++) { - real_t seg_from = p_from[i]; - real_t seg_to = p_to[i]; - real_t box_begin = position[i]; - real_t box_end = box_begin + size[i]; - real_t cmin, cmax; - real_t csign; - - if (seg_from < seg_to) { - - if (seg_from > box_end || seg_to < box_begin) - return false; - real_t length = seg_to - seg_from; - cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; - cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; - csign = -1.0; - - } else { - - if (seg_to > box_end || seg_from < box_begin) - return false; - real_t length = seg_to - seg_from; - cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; - cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; - csign = 1.0; - } - - if (cmin > min) { - min = cmin; - axis = i; - sign = csign; - } - if (cmax < max) - max = cmax; - if (max < min) - return false; - } - - Vector2 rel = p_to - p_from; - - if (r_normal) { - Vector2 normal; - normal[axis] = sign; - *r_normal = normal; - } - - if (r_pos) - *r_pos = p_from + rel * min; - - return true; -} - -/* Point2i */ - -Point2i Point2i::operator+(const Point2i &p_v) const { - - return Point2i(x + p_v.x, y + p_v.y); -} -void Point2i::operator+=(const Point2i &p_v) { - - x += p_v.x; - y += p_v.y; -} -Point2i Point2i::operator-(const Point2i &p_v) const { - - return Point2i(x - p_v.x, y - p_v.y); -} -void Point2i::operator-=(const Point2i &p_v) { - - x -= p_v.x; - y -= p_v.y; -} - -Point2i Point2i::operator*(const Point2i &p_v1) const { - - return Point2i(x * p_v1.x, y * p_v1.y); -}; - -Point2i Point2i::operator*(const int &rvalue) const { - - return Point2i(x * rvalue, y * rvalue); -}; -void Point2i::operator*=(const int &rvalue) { - - x *= rvalue; - y *= rvalue; -}; - -Point2i Point2i::operator/(const Point2i &p_v1) const { - - return Point2i(x / p_v1.x, y / p_v1.y); -}; - -Point2i Point2i::operator/(const int &rvalue) const { - - return Point2i(x / rvalue, y / rvalue); -}; - -void Point2i::operator/=(const int &rvalue) { - - x /= rvalue; - y /= rvalue; -}; - -Point2i Point2i::operator-() const { - - return Point2i(-x, -y); -} - -bool Point2i::operator==(const Point2i &p_vec2) const { - - return x == p_vec2.x && y == p_vec2.y; -} -bool Point2i::operator!=(const Point2i &p_vec2) const { - - return x != p_vec2.x || y != p_vec2.y; -} +#include "transform_2d.h" void Transform2D::invert() { // FIXME: this function assumes the basis is a rotation matrix, with no scaling. diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h new file mode 100644 index 000000000000..c8fc3c39e369 --- /dev/null +++ b/core/math/transform_2d.h @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* transform_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 TRANSFORM_2D_H +#define TRANSFORM_2D_H + +#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": + // M = (elements[0][0] elements[1][0]) + // (elements[0][1] elements[1][1]) + // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as elements[i]. + // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to elements[1][0] here. + // This requires additional care when working with explicit indices. + // See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading. + + // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down, + // and angle is measure from +X to +Y in a clockwise-fashion. + + Vector2 elements[3]; + + _FORCE_INLINE_ real_t tdotx(const Vector2 &v) const { return elements[0][0] * v.x + elements[1][0] * v.y; } + _FORCE_INLINE_ real_t tdoty(const Vector2 &v) const { return elements[0][1] * v.x + elements[1][1] * v.y; } + + const Vector2 &operator[](int p_idx) const { return elements[p_idx]; } + Vector2 &operator[](int p_idx) { return elements[p_idx]; } + + _FORCE_INLINE_ Vector2 get_axis(int p_axis) const { + ERR_FAIL_INDEX_V(p_axis, 3, Vector2()); + return elements[p_axis]; + } + _FORCE_INLINE_ void set_axis(int p_axis, const Vector2 &p_vec) { + ERR_FAIL_INDEX(p_axis, 3); + elements[p_axis] = p_vec; + } + + void invert(); + Transform2D inverse() const; + + void affine_invert(); + Transform2D affine_inverse() const; + + void set_rotation(real_t p_rot); + real_t get_rotation() const; + _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); + void rotate(real_t p_phi); + + void scale(const Size2 &p_scale); + void scale_basis(const Size2 &p_scale); + void translate(real_t p_tx, real_t p_ty); + void translate(const Vector2 &p_translation); + + real_t basis_determinant() const; + + Size2 get_scale() const; + + _FORCE_INLINE_ const Vector2 &get_origin() const { return elements[2]; } + _FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { elements[2] = p_origin; } + + Transform2D scaled(const Size2 &p_scale) const; + Transform2D basis_scaled(const Size2 &p_scale) const; + Transform2D translated(const Vector2 &p_offset) const; + Transform2D rotated(real_t p_phi) const; + + Transform2D untranslated() const; + + void orthonormalize(); + Transform2D orthonormalized() const; + + bool operator==(const Transform2D &p_transform) const; + bool operator!=(const Transform2D &p_transform) const; + + void operator*=(const Transform2D &p_transform); + Transform2D operator*(const Transform2D &p_transform) const; + + Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; + + _FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 xform(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 xform_inv(const Vector2 &p_vec) const; + _FORCE_INLINE_ Rect2 xform(const Rect2 &p_rect) const; + _FORCE_INLINE_ Rect2 xform_inv(const Rect2 &p_rect) const; + + operator String() const; + + Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { + + elements[0][0] = xx; + elements[0][1] = xy; + elements[1][0] = yx; + elements[1][1] = yy; + elements[2][0] = ox; + elements[2][1] = oy; + } + + Transform2D(real_t p_rot, const Vector2 &p_pos); + Transform2D() { + elements[0][0] = 1.0; + elements[1][1] = 1.0; + } +}; + +Vector2 Transform2D::basis_xform(const Vector2 &p_vec) const { + + return Vector2( + tdotx(p_vec), + tdoty(p_vec)); +} + +Vector2 Transform2D::basis_xform_inv(const Vector2 &p_vec) const { + + return Vector2( + elements[0].dot(p_vec), + elements[1].dot(p_vec)); +} + +Vector2 Transform2D::xform(const Vector2 &p_vec) const { + + return Vector2( + tdotx(p_vec), + tdoty(p_vec)) + + elements[2]; +} +Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { + + Vector2 v = p_vec - elements[2]; + + return Vector2( + elements[0].dot(v), + elements[1].dot(v)); +} +Rect2 Transform2D::xform(const Rect2 &p_rect) const { + + Vector2 x = elements[0] * p_rect.size.x; + Vector2 y = elements[1] * p_rect.size.y; + Vector2 pos = xform(p_rect.position); + + Rect2 new_rect; + new_rect.position = pos; + new_rect.expand_to(pos + x); + new_rect.expand_to(pos + y); + new_rect.expand_to(pos + x + y); + return new_rect; +} + +void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { + + elements[0][0] = Math::cos(p_rot) * p_scale.x; + elements[1][1] = Math::cos(p_rot) * p_scale.y; + elements[1][0] = -Math::sin(p_rot) * p_scale.y; + elements[0][1] = Math::sin(p_rot) * p_scale.x; +} + +Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { + + Vector2 ends[4] = { + xform_inv(p_rect.position), + xform_inv(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), + xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), + xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)) + }; + + Rect2 new_rect; + new_rect.position = ends[0]; + new_rect.expand_to(ends[1]); + new_rect.expand_to(ends[2]); + new_rect.expand_to(ends[3]); + + return new_rect; +} + +#endif // TRANSFORM_2D_H diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index edd4ad344115..6b8dc5eeb380 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) { @@ -88,6 +89,26 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in return index; } +void TriangleMesh::get_indices(PoolVector *r_triangles_indices) const { + + if (!valid) + return; + + const int triangles_num = triangles.size(); + + // Parse vertices indices + PoolVector::Read triangles_read = triangles.read(); + + r_triangles_indices->resize(triangles_num * 3); + PoolVector::Write r_indices_write = r_triangles_indices->write(); + + for (int i = 0; i < triangles_num; ++i) { + r_indices_write[3 * i + 0] = triangles_read[i].indices[0]; + r_indices_write[3 * i + 1] = triangles_read[i].indices[1]; + r_indices_write[3 * i + 2] = triangles_read[i].indices[2]; + } +} + void TriangleMesh::create(const PoolVector &p_faces) { valid = false; @@ -490,6 +511,222 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V return inters; } +bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_count) const { + uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); + + //p_fully_inside = true; + + enum { + TEST_AABB_BIT = 0, + VISIT_LEFT_BIT = 1, + VISIT_RIGHT_BIT = 2, + VISIT_DONE_BIT = 3, + VISITED_BIT_SHIFT = 29, + NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1, + VISITED_BIT_MASK = ~NODE_IDX_MASK, + + }; + + int level = 0; + + PoolVector::Read trianglesr = triangles.read(); + PoolVector::Read verticesr = vertices.read(); + PoolVector::Read bvhr = bvh.read(); + + const Triangle *triangleptr = trianglesr.ptr(); + const Vector3 *vertexptr = verticesr.ptr(); + int pos = bvh.size() - 1; + const BVH *bvhptr = bvhr.ptr(); + + stack[0] = pos; + while (true) { + + uint32_t node = stack[level] & NODE_IDX_MASK; + const BVH &b = bvhptr[node]; + bool done = false; + + switch (stack[level] >> VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + bool valid = b.aabb.intersects_convex_shape(p_planes, p_plane_count); + if (!valid) { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + if (b.face_index >= 0) { + + const Triangle &s = triangleptr[b.face_index]; + + for (int j = 0; j < 3; ++j) { + const Vector3 &point = vertexptr[s.indices[j]]; + const Vector3 &next_point = vertexptr[s.indices[(j + 1) % 3]]; + Vector3 res; + bool over = true; + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + + if (p.intersects_segment(point, next_point, &res)) { + bool inisde = true; + for (int k = 0; k < p_plane_count; k++) { + if (k == i) continue; + const Plane &pp = p_planes[k]; + if (pp.is_point_over(res)) { + inisde = false; + break; + } + } + if (inisde) return true; + } + + if (p.is_point_over(point)) { + over = false; + break; + } + } + if (over) return true; + } + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; + } + } + continue; + } + case VISIT_LEFT_BIT: { + + stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.left | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_RIGHT_BIT: { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.right | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_DONE_BIT: { + + if (level == 0) { + done = true; + break; + } else + level--; + continue; + } + } + + if (done) + break; + } + + return false; +} + +bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count, Vector3 p_scale) const { + uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); + + enum { + TEST_AABB_BIT = 0, + VISIT_LEFT_BIT = 1, + VISIT_RIGHT_BIT = 2, + VISIT_DONE_BIT = 3, + VISITED_BIT_SHIFT = 29, + NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1, + VISITED_BIT_MASK = ~NODE_IDX_MASK, + + }; + + int level = 0; + + PoolVector::Read trianglesr = triangles.read(); + PoolVector::Read verticesr = vertices.read(); + PoolVector::Read bvhr = bvh.read(); + + Transform scale(Basis().scaled(p_scale)); + + const Triangle *triangleptr = trianglesr.ptr(); + const Vector3 *vertexptr = verticesr.ptr(); + int pos = bvh.size() - 1; + const BVH *bvhptr = bvhr.ptr(); + + stack[0] = pos; + while (true) { + + uint32_t node = stack[level] & NODE_IDX_MASK; + const BVH &b = bvhptr[node]; + bool done = false; + + switch (stack[level] >> VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + bool intersects = scale.xform(b.aabb).intersects_convex_shape(p_planes, p_plane_count); + if (!intersects) return false; + + bool inside = scale.xform(b.aabb).inside_convex_shape(p_planes, p_plane_count); + if (inside) { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + if (b.face_index >= 0) { + const Triangle &s = triangleptr[b.face_index]; + for (int j = 0; j < 3; ++j) { + Vector3 point = scale.xform(vertexptr[s.indices[j]]); + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + if (p.is_point_over(point)) return false; + } + } + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; + } + } + continue; + } + case VISIT_LEFT_BIT: { + + stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.left | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_RIGHT_BIT: { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.right | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_DONE_BIT: { + + if (level == 0) { + done = true; + break; + } else + level--; + continue; + } + } + + if (done) + break; + } + + return true; +} + bool TriangleMesh::is_valid() const { return valid; diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index 9f145f2afbe6..e5f181fba762 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); @@ -89,9 +90,15 @@ class TriangleMesh : public Reference { bool is_valid() const; bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const; bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const; + bool intersect_convex_shape(const Plane *p_planes, int p_plane_count) const; + bool inside_convex_shape(const Plane *p_planes, int p_plane_count, Vector3 p_scale = Vector3(1, 1, 1)) const; Vector3 get_area_normal(const AABB &p_aabb) const; PoolVector get_faces() const; + PoolVector get_triangles() const { return triangles; } + PoolVector get_vertices() const { return vertices; } + void get_indices(PoolVector *p_triangles_indices) const; + void create(const PoolVector &p_faces); TriangleMesh(); }; diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index 563ba7268fb3..69ffc95946f4 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -128,10 +128,10 @@ bool Triangulate::triangulate(const Vector &contour, Vector &resul if (0.0 < get_area(contour)) for (int v = 0; v < n; v++) - V[v] = v; + V.write[v] = v; else for (int v = 0; v < n; v++) - V[v] = (n - 1) - v; + V.write[v] = (n - 1) - v; bool relaxed = false; @@ -182,11 +182,11 @@ bool Triangulate::triangulate(const Vector &contour, Vector &resul /* remove v from remaining polygon */ for (s = v, t = v + 1; t < nv; s++, t++) - V[s] = V[t]; + V.write[s] = V[t]; nv--; - /* resest error detection counter */ + /* reset error detection counter */ count = 2 * nv; } } diff --git a/core/math/triangulate.h b/core/math/triangulate.h index b1a583d0c566..2b0557ee55e6 100644 --- a/core/math/triangulate.h +++ b/core/math/triangulate.h @@ -31,7 +31,7 @@ #ifndef TRIANGULATE_H #define TRIANGULATE_H -#include "math_2d.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 new file mode 100644 index 000000000000..7c6f056f0935 --- /dev/null +++ b/core/math/vector2.cpp @@ -0,0 +1,250 @@ +/*************************************************************************/ +/* vector2.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 "vector2.h" + +real_t Vector2::angle() const { + + return Math::atan2(y, x); +} + +real_t Vector2::length() const { + + return Math::sqrt(x * x + y * y); +} + +real_t Vector2::length_squared() const { + + return x * x + y * y; +} + +void Vector2::normalize() { + + real_t l = x * x + y * y; + if (l != 0) { + + l = Math::sqrt(l); + x /= l; + y /= l; + } +} + +Vector2 Vector2::normalized() const { + + Vector2 v = *this; + v.normalize(); + return v; +} + +bool Vector2::is_normalized() const { + // use length_squared() instead of length() to avoid sqrt(), makes it more stringent. + return Math::is_equal_approx(length_squared(), 1.0); +} + +real_t Vector2::distance_to(const Vector2 &p_vector2) const { + + return Math::sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y)); +} + +real_t Vector2::distance_squared_to(const Vector2 &p_vector2) const { + + return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y); +} + +real_t Vector2::angle_to(const Vector2 &p_vector2) const { + + return Math::atan2(cross(p_vector2), dot(p_vector2)); +} + +real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { + + return Math::atan2(y - p_vector2.y, x - p_vector2.x); +} + +real_t Vector2::dot(const Vector2 &p_other) const { + + return x * p_other.x + y * p_other.y; +} + +real_t Vector2::cross(const Vector2 &p_other) const { + + return x * p_other.y - y * p_other.x; +} + +Vector2 Vector2::floor() const { + + return Vector2(Math::floor(x), Math::floor(y)); +} + +Vector2 Vector2::ceil() const { + + return Vector2(Math::ceil(x), Math::ceil(y)); +} + +Vector2 Vector2::round() const { + + return Vector2(Math::round(x), Math::round(y)); +} + +Vector2 Vector2::rotated(real_t p_by) const { + + Vector2 v; + v.set_rotation(angle() + p_by); + v *= length(); + return v; +} + +Vector2 Vector2::project(const Vector2 &p_b) const { + return p_b * (dot(p_b) / p_b.length_squared()); +} + +Vector2 Vector2::snapped(const Vector2 &p_by) const { + + return Vector2( + Math::stepify(x, p_by.x), + Math::stepify(y, p_by.y)); +} + +Vector2 Vector2::clamped(real_t p_len) const { + + real_t l = length(); + Vector2 v = *this; + if (l > 0 && p_len < l) { + + v /= l; + v *= p_len; + } + + return v; +} + +Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const { + + Vector2 p0 = p_pre_a; + Vector2 p1 = *this; + Vector2 p2 = p_b; + Vector2 p3 = p_post_b; + + real_t t = p_t; + real_t t2 = t * t; + real_t t3 = t2 * t; + + Vector2 out; + out = 0.5 * ((p1 * 2.0) + + (-p0 + p2) * t + + (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 + + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); + return out; +} + +// 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(), Vector2()); +#endif + return *this - p_normal * this->dot(p_normal); +} + +Vector2 Vector2::bounce(const Vector2 &p_normal) const { + return -reflect(p_normal); +} + +Vector2 Vector2::reflect(const Vector2 &p_normal) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector2()); +#endif + return 2.0 * p_normal * this->dot(p_normal) - *this; +} + +/* Vector2i */ + +Vector2i Vector2i::operator+(const Vector2i &p_v) const { + + return Vector2i(x + p_v.x, y + p_v.y); +} +void Vector2i::operator+=(const Vector2i &p_v) { + + x += p_v.x; + y += p_v.y; +} +Vector2i Vector2i::operator-(const Vector2i &p_v) const { + + return Vector2i(x - p_v.x, y - p_v.y); +} +void Vector2i::operator-=(const Vector2i &p_v) { + + x -= p_v.x; + y -= p_v.y; +} + +Vector2i Vector2i::operator*(const Vector2i &p_v1) const { + + return Vector2i(x * p_v1.x, y * p_v1.y); +}; + +Vector2i Vector2i::operator*(const int &rvalue) const { + + return Vector2i(x * rvalue, y * rvalue); +}; +void Vector2i::operator*=(const int &rvalue) { + + x *= rvalue; + y *= rvalue; +}; + +Vector2i Vector2i::operator/(const Vector2i &p_v1) const { + + return Vector2i(x / p_v1.x, y / p_v1.y); +}; + +Vector2i Vector2i::operator/(const int &rvalue) const { + + return Vector2i(x / rvalue, y / rvalue); +}; + +void Vector2i::operator/=(const int &rvalue) { + + x /= rvalue; + y /= rvalue; +}; + +Vector2i Vector2i::operator-() const { + + return Vector2i(-x, -y); +} + +bool Vector2i::operator==(const Vector2i &p_vec2) const { + + return x == p_vec2.x && y == p_vec2.y; +} +bool Vector2i::operator!=(const Vector2i &p_vec2) const { + + return x != p_vec2.x || y != p_vec2.y; +} diff --git a/core/math/vector2.h b/core/math/vector2.h new file mode 100644 index 000000000000..e5e555597d8e --- /dev/null +++ b/core/math/vector2.h @@ -0,0 +1,316 @@ +/*************************************************************************/ +/* vector2.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 VECTOR2_H +#define VECTOR2_H + +#include "core/math/math_funcs.h" +#include "core/ustring.h" + +struct Vector2i; + +struct Vector2 { + + union { + real_t x; + real_t width; + }; + union { + real_t y; + real_t height; + }; + + _FORCE_INLINE_ real_t &operator[](int p_idx) { + return p_idx ? y : x; + } + _FORCE_INLINE_ const real_t &operator[](int p_idx) const { + return p_idx ? y : x; + } + + void normalize(); + Vector2 normalized() const; + bool is_normalized() const; + + real_t length() const; + real_t length_squared() const; + + real_t distance_to(const Vector2 &p_vector2) const; + real_t distance_squared_to(const Vector2 &p_vector2) const; + real_t angle_to(const Vector2 &p_vector2) const; + real_t angle_to_point(const Vector2 &p_vector2) const; + + real_t dot(const Vector2 &p_other) const; + real_t cross(const Vector2 &p_other) const; + Vector2 project(const Vector2 &p_b) const; + + Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; + + Vector2 clamped(real_t p_len) const; + + _FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t); + _FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const; + _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const; + Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const; + + Vector2 slide(const Vector2 &p_normal) const; + Vector2 bounce(const Vector2 &p_normal) const; + Vector2 reflect(const Vector2 &p_normal) const; + + Vector2 operator+(const Vector2 &p_v) const; + void operator+=(const Vector2 &p_v); + Vector2 operator-(const Vector2 &p_v) const; + void operator-=(const Vector2 &p_v); + Vector2 operator*(const Vector2 &p_v1) const; + + Vector2 operator*(const real_t &rvalue) const; + void operator*=(const real_t &rvalue); + void operator*=(const Vector2 &rvalue) { *this = *this * rvalue; } + + Vector2 operator/(const Vector2 &p_v1) const; + + Vector2 operator/(const real_t &rvalue) const; + + void operator/=(const real_t &rvalue); + + Vector2 operator-() const; + + bool operator==(const Vector2 &p_vec2) const; + bool operator!=(const Vector2 &p_vec2) const; + + bool operator<(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator<=(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y <= p_vec2.y) : (x <= p_vec2.x); } + + real_t angle() const; + + void set_rotation(real_t p_radians) { + + x = Math::cos(p_radians); + y = Math::sin(p_radians); + } + + _FORCE_INLINE_ Vector2 abs() const { + + return Vector2(Math::abs(x), Math::abs(y)); + } + + Vector2 rotated(real_t p_by) const; + Vector2 tangent() const { + + return Vector2(y, -x); + } + + Vector2 floor() const; + Vector2 ceil() const; + Vector2 round() const; + Vector2 snapped(const Vector2 &p_by) const; + real_t aspect() const { return width / height; } + + operator String() const { return String::num(x) + ", " + String::num(y); } + + _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) { + x = p_x; + y = p_y; + } + _FORCE_INLINE_ Vector2() { + x = 0; + y = 0; + } +}; + +_FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { + + return p_vec - *this * (dot(p_vec) - p_d); +} + +_FORCE_INLINE_ Vector2 operator*(real_t p_scalar, const Vector2 &p_vec) { + + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const { + + return Vector2(x + p_v.x, y + p_v.y); +} +_FORCE_INLINE_ void Vector2::operator+=(const Vector2 &p_v) { + + x += p_v.x; + y += p_v.y; +} +_FORCE_INLINE_ Vector2 Vector2::operator-(const Vector2 &p_v) const { + + return Vector2(x - p_v.x, y - p_v.y); +} +_FORCE_INLINE_ void Vector2::operator-=(const Vector2 &p_v) { + + x -= p_v.x; + y -= p_v.y; +} + +_FORCE_INLINE_ Vector2 Vector2::operator*(const Vector2 &p_v1) const { + + return Vector2(x * p_v1.x, y * p_v1.y); +}; + +_FORCE_INLINE_ Vector2 Vector2::operator*(const real_t &rvalue) const { + + return Vector2(x * rvalue, y * rvalue); +}; +_FORCE_INLINE_ void Vector2::operator*=(const real_t &rvalue) { + + x *= rvalue; + y *= rvalue; +}; + +_FORCE_INLINE_ Vector2 Vector2::operator/(const Vector2 &p_v1) const { + + return Vector2(x / p_v1.x, y / p_v1.y); +}; + +_FORCE_INLINE_ Vector2 Vector2::operator/(const real_t &rvalue) const { + + return Vector2(x / rvalue, y / rvalue); +}; + +_FORCE_INLINE_ void Vector2::operator/=(const real_t &rvalue) { + + x /= rvalue; + y /= rvalue; +}; + +_FORCE_INLINE_ Vector2 Vector2::operator-() const { + + return Vector2(-x, -y); +} + +_FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const { + + return x == p_vec2.x && y == p_vec2.y; +} +_FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { + + return x != p_vec2.x || y != p_vec2.y; +} + +Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const { + + Vector2 res = *this; + + res.x += (p_t * (p_b.x - x)); + res.y += (p_t * (p_b.y - y)); + + return res; +} + +Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!is_normalized(), Vector2()); +#endif + real_t theta = angle_to(p_b); + return rotated(theta * p_t); +} + +Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) { + + Vector2 res = p_a; + + res.x += (p_t * (p_b.x - p_a.x)); + res.y += (p_t * (p_b.y - p_a.y)); + + return res; +} + +typedef Vector2 Size2; +typedef Vector2 Point2; + +/* INTEGER STUFF */ + +struct Vector2i { + + union { + int x; + int width; + }; + union { + int y; + int height; + }; + + _FORCE_INLINE_ int &operator[](int p_idx) { + return p_idx ? y : x; + } + _FORCE_INLINE_ const int &operator[](int p_idx) const { + return p_idx ? y : x; + } + + Vector2i operator+(const Vector2i &p_v) const; + void operator+=(const Vector2i &p_v); + Vector2i operator-(const Vector2i &p_v) const; + void operator-=(const Vector2i &p_v); + Vector2i operator*(const Vector2i &p_v1) const; + + Vector2i operator*(const int &rvalue) const; + void operator*=(const int &rvalue); + + Vector2i operator/(const Vector2i &p_v1) const; + + Vector2i operator/(const int &rvalue) const; + + void operator/=(const int &rvalue); + + Vector2i operator-() const; + bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } + + bool operator==(const Vector2i &p_vec2) const; + bool operator!=(const Vector2i &p_vec2) const; + + real_t get_aspect() const { return width / (real_t)height; } + + operator String() const { return String::num(x) + ", " + String::num(y); } + + operator Vector2() const { return Vector2(x, y); } + inline Vector2i(const Vector2 &p_vec2) { + x = (int)p_vec2.x; + y = (int)p_vec2.y; + } + inline Vector2i(int p_x, int p_y) { + x = p_x; + y = p_y; + } + inline Vector2i() { + x = 0; + y = 0; + } +}; + +typedef Vector2i Size2i; +typedef Vector2i Point2i; + +#endif // VECTOR2_H diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 78d52d5cd183..5dbb01493d1f 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 10ec4f5641b5..16feba6a0ce1 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; @@ -91,6 +91,7 @@ struct Vector3 { /* Static Methods between 2 vector3s */ _FORCE_INLINE_ Vector3 linear_interpolate(const Vector3 &p_b, real_t p_t) const; + _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_b, real_t p_t) const; Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const; Vector3 cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const; @@ -103,10 +104,13 @@ struct Vector3 { _FORCE_INLINE_ Vector3 floor() const; _FORCE_INLINE_ Vector3 sign() const; _FORCE_INLINE_ Vector3 ceil() const; + _FORCE_INLINE_ Vector3 round() const; _FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const; _FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const; + _FORCE_INLINE_ Vector3 project(const Vector3 &p_b) const; + _FORCE_INLINE_ real_t angle_to(const Vector3 &p_b) const; _FORCE_INLINE_ Vector3 slide(const Vector3 &p_normal) const; @@ -146,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 { @@ -204,6 +203,11 @@ Vector3 Vector3::ceil() const { return Vector3(Math::ceil(x), Math::ceil(y), Math::ceil(z)); } +Vector3 Vector3::round() const { + + return Vector3(Math::round(x), Math::round(y), Math::round(z)); +} + Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const { return Vector3( @@ -212,6 +216,15 @@ Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const { z + (p_t * (p_b.z - z))); } +Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!is_normalized(), Vector3()); +#endif + + real_t theta = angle_to(p_b); + return rotated(cross(p_b), theta * p_t); +} + real_t Vector3::distance_to(const Vector3 &p_b) const { return (p_b - *this).length(); @@ -222,6 +235,10 @@ real_t Vector3::distance_squared_to(const Vector3 &p_b) const { return (p_b - *this).length_squared(); } +Vector3 Vector3::project(const Vector3 &p_b) const { + return p_b * (dot(p_b) / p_b.length_squared()); +} + real_t Vector3::angle_to(const Vector3 &p_b) const { return Math::atan2(cross(p_b).length(), dot(p_b)); @@ -413,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); } @@ -424,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 25ee6eafaecb..abfc73407a62 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; @@ -50,9 +50,9 @@ Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const V String type; if (ObjectDB::get_instance(p_id)) type = ObjectDB::get_instance(p_id)->get_class(); - print_line("failed method: " + type + ":" + p_method + " target ID: " + itos(p_id)); + print_line("Failed method: " + type + ":" + p_method + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings"); + ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); ERR_FAIL_V(ERR_OUT_OF_MEMORY); } @@ -101,9 +101,9 @@ Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Vari String type; if (ObjectDB::get_instance(p_id)) type = ObjectDB::get_instance(p_id)->get_class(); - print_line("failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id)); + print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings"); + ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); ERR_FAIL_V(ERR_OUT_OF_MEMORY); } @@ -134,9 +134,9 @@ Error MessageQueue::push_notification(ObjectID p_id, int p_notification) { String type; if (ObjectDB::get_instance(p_id)) type = ObjectDB::get_instance(p_id)->get_class(); - print_line("failed notification: " + itos(p_notification) + " target ID: " + itos(p_id)); + print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings"); + ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); ERR_FAIL_V(ERR_OUT_OF_MEMORY); } @@ -210,8 +210,7 @@ void MessageQueue::statistics() { } //object was deleted - //WARN_PRINT("Object was deleted while awaiting a callback") - //should it print a warning? + print_line("Object was deleted while awaiting a callback"); } else { null_count++; @@ -226,17 +225,14 @@ void MessageQueue::statistics() { print_line("NULL count: " + itos(null_count)); for (Map::Element *E = set_count.front(); E; E = E->next()) { - print_line("SET " + E->key() + ": " + itos(E->get())); } for (Map::Element *E = call_count.front(); E; E = E->next()) { - print_line("CALL " + E->key() + ": " + itos(E->get())); } for (Map::Element *E = notify_count.front(); E; E = E->next()) { - print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get())); } } @@ -268,7 +264,6 @@ void MessageQueue::flush() { if (buffer_end > buffer_max_used) { buffer_max_used = buffer_end; - //statistics(); } uint32_t read_pos = 0; @@ -342,7 +337,8 @@ MessageQueue::MessageQueue() { buffer_end = 0; buffer_max_used = 0; - buffer_size = GLOBAL_DEF("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); + 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 be5ffe4fae7d..2083bb063941 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 52ee9e08489b..fa5b88b5ac7f 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 e02d64c935fe..7788de919ce5 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 /** @@ -45,7 +46,7 @@ #define DEBUG_METHODS_ENABLED #endif -#include "type_info.h" +#include "core/type_info.h" enum MethodFlags { @@ -128,10 +129,36 @@ struct VariantCaster { // Object enum casts must go here VARIANT_ENUM_CAST(Object::ConnectFlags); +template +struct VariantObjectClassChecker { + static _FORCE_INLINE_ bool check(const Variant &p_variant) { + return true; + } +}; + +template <> +struct VariantObjectClassChecker { + static _FORCE_INLINE_ bool check(const Variant &p_variant) { + Object *obj = p_variant; + Node *node = p_variant; + return node || !obj; + } +}; + +template <> +struct VariantObjectClassChecker { + static _FORCE_INLINE_ bool check(const Variant &p_variant) { + Object *obj = p_variant; + Control *control = p_variant; + return control || !obj; + } +}; + #define CHECK_ARG(m_arg) \ if ((m_arg - 1) < p_arg_count) { \ Variant::Type argtype = get_argument_type(m_arg - 1); \ - if (!Variant::can_convert_strict(p_args[m_arg - 1]->get_type(), argtype)) { \ + if (!Variant::can_convert_strict(p_args[m_arg - 1]->get_type(), argtype) || \ + !VariantObjectClassChecker::check(*p_args[m_arg - 1])) { \ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; \ r_error.argument = m_arg - 1; \ r_error.expected = argtype; \ @@ -328,7 +355,7 @@ class MethodBindVarArg : public MethodBind { for (int i = 0; i < p_info.arguments.size(); i++) { at[i + 1] = p_info.arguments[i].type; - names[i] = p_info.arguments[i].name; + names.write[i] = p_info.arguments[i].name; } set_argument_names(names); diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h index 2007c3def5dd..b706e65d5343 100644 --- a/core/method_ptrcall.h +++ b/core/method_ptrcall.h @@ -31,9 +31,9 @@ #ifndef METHOD_PTRCALL_H #define METHOD_PTRCALL_H -#include "math_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 @@ -180,7 +180,7 @@ struct PtrToArg { { \ PoolVector::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -207,13 +207,57 @@ struct PtrToArg { { \ PoolVector::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ } \ } +#define MAKE_VECARG_ALT(m_type, m_type_alt) \ + template <> \ + struct PtrToArg > { \ + _FORCE_INLINE_ static Vector convert(const void *p_ptr) { \ + const PoolVector *dvs = reinterpret_cast *>(p_ptr); \ + Vector ret; \ + int len = dvs->size(); \ + ret.resize(len); \ + { \ + PoolVector::Read r = dvs->read(); \ + for (int i = 0; i < len; i++) { \ + ret.write[i] = r[i]; \ + } \ + } \ + return ret; \ + } \ + _FORCE_INLINE_ static void encode(Vector p_vec, void *p_ptr) { \ + PoolVector *dv = reinterpret_cast *>(p_ptr); \ + int len = p_vec.size(); \ + dv->resize(len); \ + { \ + PoolVector::Write w = dv->write(); \ + for (int i = 0; i < len; i++) { \ + w[i] = p_vec[i]; \ + } \ + } \ + } \ + }; \ + template <> \ + struct PtrToArg &> { \ + _FORCE_INLINE_ static Vector convert(const void *p_ptr) { \ + const PoolVector *dvs = reinterpret_cast *>(p_ptr); \ + Vector ret; \ + int len = dvs->size(); \ + ret.resize(len); \ + { \ + PoolVector::Read r = dvs->read(); \ + for (int i = 0; i < len; i++) { \ + ret.write[i] = r[i]; \ + } \ + } \ + return ret; \ + } \ + } MAKE_VECARG(String); MAKE_VECARG(uint8_t); MAKE_VECARG(int); @@ -221,6 +265,7 @@ MAKE_VECARG(float); MAKE_VECARG(Vector2); MAKE_VECARG(Vector3); MAKE_VECARG(Color); +MAKE_VECARG_ALT(String, StringName); //for stuff that gets converted to Array vectors #define MAKE_VECARR(m_type) \ @@ -232,7 +277,7 @@ MAKE_VECARG(Color); int len = arr->size(); \ ret.resize(len); \ for (int i = 0; i < len; i++) { \ - ret[i] = (*arr)[i]; \ + ret.write[i] = (*arr)[i]; \ } \ return ret; \ } \ @@ -253,7 +298,7 @@ MAKE_VECARG(Color); int len = arr->size(); \ ret.resize(len); \ for (int i = 0; i < len; i++) { \ - ret[i] = (*arr)[i]; \ + ret.write[i] = (*arr)[i]; \ } \ return ret; \ } \ diff --git a/core/node_path.cpp b/core/node_path.cpp index cd7ad7753413..91e2aa5f4e99 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -30,12 +30,9 @@ #include "node_path.h" -#include "print_string.h" +#include "core/print_string.h" -uint32_t NodePath::hash() const { - - if (!data) - return 0; +void NodePath::_update_hash_cache() const { uint32_t h = data->absolute ? 1 : 0; int pc = data->path.size(); @@ -49,13 +46,15 @@ uint32_t NodePath::hash() const { h = h ^ ssn[i].hash(); } - return h; + data->hash_cache_valid = true; + data->hash_cache = h; } void NodePath::prepend_period() { if (data->path.size() && data->path[0].operator String() != ".") { data->path.insert(0, "."); + data->hash_cache_valid = false; } } @@ -114,21 +113,33 @@ bool NodePath::operator==(const NodePath &p_path) const { if (data->absolute != p_path.data->absolute) return false; - if (data->path.size() != p_path.data->path.size()) + int path_size = data->path.size(); + + if (path_size != p_path.data->path.size()) { return false; + } + + int subpath_size = data->subpath.size(); - if (data->subpath.size() != p_path.data->subpath.size()) + if (subpath_size != p_path.data->subpath.size()) { return false; + } - for (int i = 0; i < data->path.size(); i++) { + const StringName *l_path_ptr = data->path.ptr(); + const StringName *r_path_ptr = p_path.data->path.ptr(); + + for (int i = 0; i < path_size; i++) { - if (data->path[i] != p_path.data->path[i]) + if (l_path_ptr[i] != r_path_ptr[i]) return false; } - for (int i = 0; i < data->subpath.size(); i++) { + const StringName *l_subpath_ptr = data->subpath.ptr(); + const StringName *r_subpath_ptr = p_path.data->subpath.ptr(); + + for (int i = 0; i < subpath_size; i++) { - if (data->subpath[i] != p_path.data->subpath[i]) + if (l_subpath_ptr[i] != r_subpath_ptr[i]) return false; } @@ -264,8 +275,9 @@ NodePath NodePath::get_as_property_path() const { Vector new_path = data->subpath; String initial_subname = data->path[0]; - for (size_t i = 1; i < data->path.size(); i++) { - initial_subname += i == 0 ? data->path[i].operator String() : "/" + data->path[i]; + + for (int i = 1; i < data->path.size(); i++) { + initial_subname += "/" + data->path[i]; } new_path.insert(0, initial_subname); @@ -285,6 +297,7 @@ NodePath::NodePath(const Vector &p_path, bool p_absolute) { data->absolute = p_absolute; data->path = p_path; data->has_slashes = true; + data->hash_cache_valid = false; } NodePath::NodePath(const Vector &p_path, const Vector &p_subpath, bool p_absolute) { @@ -300,6 +313,7 @@ NodePath::NodePath(const Vector &p_path, const Vector &p data->path = p_path; data->subpath = p_subpath; data->has_slashes = true; + data->hash_cache_valid = false; } void NodePath::simplify() { @@ -323,6 +337,7 @@ void NodePath::simplify() { } } } + data->hash_cache_valid = false; } NodePath NodePath::simplified() const { @@ -395,6 +410,7 @@ NodePath::NodePath(const String &p_path) { data->absolute = absolute ? true : false; data->has_slashes = has_slashes; data->subpath = subpath; + data->hash_cache_valid = false; if (slices == 0) return; @@ -411,7 +427,7 @@ NodePath::NodePath(const String &p_path) { String name = path.substr(from, i - from); ERR_FAIL_INDEX(slice, data->path.size()); - data->path[slice++] = name; + data->path.write[slice++] = name; } from = i + 1; last_is_slash = true; diff --git a/core/node_path.h b/core/node_path.h index 288f39721f8b..4a72d4a4d555 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 */ @@ -47,11 +48,15 @@ class NodePath { StringName concatenated_subpath; bool absolute; bool has_slashes; + mutable bool hash_cache_valid; + mutable uint32_t hash_cache; }; - Data *data; + mutable Data *data; void unref(); + void _update_hash_cache() const; + public: _FORCE_INLINE_ StringName get_sname() const { @@ -78,7 +83,14 @@ class NodePath { NodePath get_parent() const; - uint32_t hash() const; + _FORCE_INLINE_ uint32_t hash() const { + if (!data) + return 0; + if (!data->hash_cache_valid) { + _update_hash_cache(); + } + return data->hash_cache; + } operator String() const; bool is_empty() const; diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index 280aea6a14c6..984044251922 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -31,181 +31,186 @@ #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" - -// uncomment this to disable initial local storage. -#define OA_HASH_MAP_INITIAL_LOCAL_STORAGE +#include "core/hashfuncs.h" +#include "core/math/math_funcs.h" +#include "core/os/copymem.h" +#include "core/os/memory.h" /** - * This class implements a hash map datastructure that uses open addressing with - * local probing. - * - * It can give huge performance improvements over a chained HashMap because of - * the increased data locality. - * - * Because of that locality property it's important to not use "large" value - * types as the "TData" type. If TData values are too big it can cause more - * cache misses then chaining. If larger values are needed then storing those - * in a separate array and using pointers or indices to reference them is the - * better solution. - * - * This hash map also implements real-time incremental rehashing. + * A HashMap implementation that uses open addressing with robinhood hashing. + * Robinhood hashing swaps out entries that have a smaller probing distance + * than the to-be-inserted entry, that evens out the average probing distance + * and enables faster lookups. * + * The entries are stored inplace, so huge keys or values might fill cache lines + * a lot faster. */ -template > class OAHashMap { private: -#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE - TData local_data[INITIAL_NUM_ELEMENTS]; - TKey local_keys[INITIAL_NUM_ELEMENTS]; - uint32_t local_hashes[INITIAL_NUM_ELEMENTS]; - uint8_t local_flags[INITIAL_NUM_ELEMENTS / 4 + (INITIAL_NUM_ELEMENTS % 4 != 0 ? 1 : 0)]; -#endif + TValue *values; + TKey *keys; + uint32_t *hashes; - struct { - TData *data; - TKey *keys; - uint32_t *hashes; + uint32_t capacity; - // This is actually an array of bits, 4 bit pairs per octet. - // | ba ba ba ba | ba ba ba ba | .... - // - // if a is set it means that there is an element present. - // if b is set it means that an element was deleted. This is needed for - // the local probing to work without relocating any succeeding and - // colliding entries. - uint8_t *flags; + uint32_t num_elements; - uint32_t capacity; - } table, old_table; + static const uint32_t EMPTY_HASH = 0; + static const uint32_t DELETED_HASH_BIT = 1 << 31; - bool is_rehashing; - uint32_t rehash_position; - uint32_t rehash_amount; + _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) { + uint32_t hash = Hasher::hash(p_key); - uint32_t elements; + if (hash == EMPTY_HASH) { + hash = EMPTY_HASH + 1; + } else if (hash & DELETED_HASH_BIT) { + hash &= ~DELETED_HASH_BIT; + } - /* Methods */ + return hash; + } - // returns true if the value already existed, false if it's a new entry - bool _raw_set_with_hash(uint32_t p_hash, const TKey &p_key, const TData &p_data) { - for (int i = 0; i < table.capacity; i++) { + _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) { + p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not - int pos = (p_hash + i) % table.capacity; + uint32_t original_pos = p_hash % capacity; - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; + return p_pos - original_pos; + } - bool is_filled_flag = table.flags[flags_pos] & (1 << (2 * flags_pos_offset)); - bool is_deleted_flag = table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1)); + _FORCE_INLINE_ void _construct(uint32_t p_pos, uint32_t p_hash, const TKey &p_key, const TValue &p_value) { + memnew_placement(&keys[p_pos], TKey(p_key)); + memnew_placement(&values[p_pos], TValue(p_value)); + hashes[p_pos] = p_hash; - if (is_filled_flag) { - if (table.hashes[pos] == p_hash && Comparator::compare(table.keys[pos], p_key)) { - table.data[pos] = p_data; - return true; - } - continue; + num_elements++; + } + + bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) { + uint32_t hash = _hash(p_key); + uint32_t pos = hash % capacity; + uint32_t distance = 0; + + while (42) { + if (hashes[pos] == EMPTY_HASH) { + return false; } - table.keys[pos] = p_key; - table.data[pos] = p_data; - table.hashes[pos] = p_hash; + if (distance > _get_probe_length(pos, hashes[pos])) { + return false; + } - table.flags[flags_pos] |= (1 << (2 * flags_pos_offset)); - table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset + 1)); + if (hashes[pos] == hash && Comparator::compare(keys[pos], p_key)) { + r_pos = pos; + return true; + } - return false; + pos = (pos + 1) % capacity; + distance++; } - return false; } -public: - _FORCE_INLINE_ uint32_t get_capacity() const { return table.capacity; } - _FORCE_INLINE_ uint32_t get_num_elements() const { return elements; } + void _insert_with_hash(uint32_t p_hash, const TKey &p_key, const TValue &p_value) { - void set(const TKey &p_key, const TData &p_data) { + uint32_t hash = p_hash; + uint32_t distance = 0; + uint32_t pos = hash % capacity; - uint32_t hash = Hasher::hash(p_key); + TKey key = p_key; + TValue value = p_value; - // We don't progress the rehashing if the table just got resized - // to keep the cost of this function low. - if (is_rehashing) { + while (42) { + if (hashes[pos] == EMPTY_HASH) { + _construct(pos, hash, key, value); - // rehash progress + return; + } - for (int i = 0; i <= rehash_amount && rehash_position < old_table.capacity; rehash_position++) { + // not an empty slot, let's check the probing length of the existing one + uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos]); + if (existing_probe_len < distance) { - int flags_pos = rehash_position / 4; - int flags_pos_offset = rehash_position % 4; + if (hashes[pos] & DELETED_HASH_BIT) { + // we found a place where we can fit in! + _construct(pos, hash, key, value); - bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; + return; + } - if (is_filled_flag) { - _raw_set_with_hash(old_table.hashes[rehash_position], old_table.keys[rehash_position], old_table.data[rehash_position]); + SWAP(hash, hashes[pos]); + SWAP(key, keys[pos]); + SWAP(value, values[pos]); + distance = existing_probe_len; + } - old_table.keys[rehash_position].~TKey(); - old_table.data[rehash_position].~TData(); + pos = (pos + 1) % capacity; + distance++; + } + } + void _resize_and_rehash() { - memnew_placement(&old_table.keys[rehash_position], TKey); - memnew_placement(&old_table.data[rehash_position], TData); + TKey *old_keys = keys; + TValue *old_values = values; + uint32_t *old_hashes = hashes; - old_table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset)); - old_table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1)); - } - } + uint32_t old_capacity = capacity; - if (rehash_position >= old_table.capacity) { + capacity = old_capacity * 2; + num_elements = 0; - // wohooo, we can get rid of the old table. - is_rehashing = false; + keys = memnew_arr(TKey, capacity); + values = memnew_arr(TValue, capacity); + hashes = memnew_arr(uint32_t, capacity); -#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE - if (old_table.data == local_data) { - // Everything is local, so no cleanup :P - } else -#endif - { - memdelete_arr(old_table.data); - memdelete_arr(old_table.keys); - memdelete_arr(old_table.hashes); - memdelete_arr(old_table.flags); - } - } + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = 0; } - // Table is almost full, resize and start rehashing process. - if (elements >= table.capacity * 0.7) { + for (uint32_t i = 0; i < old_capacity; i++) { + if (old_hashes[i] == EMPTY_HASH) { + continue; + } + if (old_hashes[i] & DELETED_HASH_BIT) { + continue; + } - old_table.capacity = table.capacity; - old_table.data = table.data; - old_table.flags = table.flags; - old_table.hashes = table.hashes; - old_table.keys = table.keys; + _insert_with_hash(old_hashes[i], old_keys[i], old_values[i]); + } - table.capacity = old_table.capacity * 2; + memdelete_arr(old_keys); + memdelete_arr(old_values); + memdelete_arr(old_hashes); + } - table.data = memnew_arr(TData, table.capacity); - table.flags = memnew_arr(uint8_t, table.capacity / 4 + (table.capacity % 4 != 0 ? 1 : 0)); - table.hashes = memnew_arr(uint32_t, table.capacity); - table.keys = memnew_arr(TKey, table.capacity); +public: + _FORCE_INLINE_ uint32_t get_capacity() const { return capacity; } + _FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; } - zeromem(table.flags, table.capacity / 4 + (table.capacity % 4 != 0 ? 1 : 0)); + void insert(const TKey &p_key, const TValue &p_value) { - is_rehashing = true; - rehash_position = 0; - rehash_amount = (elements * 2) / (table.capacity * 0.7 - old_table.capacity); + if ((float)num_elements / (float)capacity > 0.9) { + _resize_and_rehash(); } - if (!_raw_set_with_hash(hash, p_key, p_data)) - elements++; + uint32_t hash = _hash(p_key); + + _insert_with_hash(hash, p_key, p_value); + } + + void set(const TKey &p_key, const TValue &p_data) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + values[pos].~TValue(); + memnew_placement(&values[pos], TValue(p_data)); + } else { + insert(p_key, p_data); + } } /** @@ -214,380 +219,108 @@ class OAHashMap { * if r_data is not NULL then the value will be written to the object * it points to. */ - bool lookup(const TKey &p_key, TData *r_data) { - - uint32_t hash = Hasher::hash(p_key); - - bool check_old_table = is_rehashing; - bool check_new_table = true; - - // search for the key and return the value associated with it - // - // if we're rehashing we need to check both the old and the - // current table. If we find a value in the old table we still - // need to continue searching in the new table as it might have - // been added after - - TData *value = NULL; - - for (int i = 0; i < table.capacity; i++) { - - if (!check_new_table && !check_old_table) { - - break; - } - - // if we're rehashing check the old table - if (check_old_table && i < old_table.capacity) { - - int pos = (hash + i) % old_table.capacity; - - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; - - bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; - - if (is_filled_flag) { - // found our entry? - if (old_table.hashes[pos] == hash && Comparator::compare(old_table.keys[pos], p_key)) { - value = &old_table.data[pos]; - check_old_table = false; - } - } else if (!is_deleted_flag) { - - // we hit an empty field here, we don't - // need to further check this old table - // because we know it's not in here. + bool lookup(const TKey &p_key, TValue &r_data) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); - check_old_table = false; - } - } - - if (check_new_table) { - - int pos = (hash + i) % table.capacity; - - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; - - bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - bool is_deleted_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; - - if (is_filled_flag) { - // found our entry? - if (table.hashes[pos] == hash && Comparator::compare(table.keys[pos], p_key)) { - if (r_data != NULL) - *r_data = table.data[pos]; - return true; - } - continue; - } else if (is_deleted_flag) { - continue; - } else if (value != NULL) { - - // We found a value in the old table - if (r_data != NULL) - *r_data = *value; - return true; - } else { - check_new_table = false; - } - } - } - - if (value != NULL) { - if (r_data != NULL) - *r_data = *value; + if (exists) { + r_data.~TValue(); + memnew_placement(&r_data, TValue(values[pos])); return true; } + return false; } _FORCE_INLINE_ bool has(const TKey &p_key) { - return lookup(p_key, NULL); + uint32_t _pos = 0; + return _lookup_pos(p_key, _pos); } void remove(const TKey &p_key) { - uint32_t hash = Hasher::hash(p_key); - - bool check_old_table = is_rehashing; - bool check_new_table = true; - - for (int i = 0; i < table.capacity; i++) { - - if (!check_new_table && !check_old_table) { - return; - } - - // if we're rehashing check the old table - if (check_old_table && i < old_table.capacity) { - - int pos = (hash + i) % old_table.capacity; - - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; - - bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; - - if (is_filled_flag) { - // found our entry? - if (old_table.hashes[pos] == hash && Comparator::compare(old_table.keys[pos], p_key)) { - old_table.keys[pos].~TKey(); - old_table.data[pos].~TData(); + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); - memnew_placement(&old_table.keys[pos], TKey); - memnew_placement(&old_table.data[pos], TData); - - old_table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset)); - old_table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1)); - - elements--; - return; - } - } else if (!is_deleted_flag) { - - // we hit an empty field here, we don't - // need to further check this old table - // because we know it's not in here. - - check_old_table = false; - } - } - - if (check_new_table) { - - int pos = (hash + i) % table.capacity; - - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; - - bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - bool is_deleted_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; - - if (is_filled_flag) { - // found our entry? - if (table.hashes[pos] == hash && Comparator::compare(table.keys[pos], p_key)) { - table.keys[pos].~TKey(); - table.data[pos].~TData(); - - memnew_placement(&table.keys[pos], TKey); - memnew_placement(&table.data[pos], TData); - - table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset)); - table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1)); - - // don't return here, this value might still be in the old table - // if it was already relocated. - - elements--; - return; - } - continue; - } else if (is_deleted_flag) { - continue; - } else { - check_new_table = false; - } - } + if (!exists) { + return; } + + hashes[pos] |= DELETED_HASH_BIT; + values[pos].~TValue(); + keys[pos].~TKey(); + num_elements--; } struct Iterator { bool valid; - uint32_t hash; - const TKey *key; - const TData *data; + const TValue *value; private: + uint32_t pos; friend class OAHashMap; - bool was_from_old_table; }; Iterator iter() const { Iterator it; - it.valid = false; - it.was_from_old_table = false; - - bool check_old_table = is_rehashing; - - for (int i = 0; i < table.capacity; i++) { - - // if we're rehashing check the old table first - if (check_old_table && i < old_table.capacity) { - - int pos = i; - - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; - - bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - - if (is_filled_flag) { - it.valid = true; - it.hash = old_table.hashes[pos]; - it.data = &old_table.data[pos]; - it.key = &old_table.keys[pos]; - - it.was_from_old_table = true; - - return it; - } - } - - { - - int pos = i; - - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; - - bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - - if (is_filled_flag) { - it.valid = true; - it.hash = table.hashes[pos]; - it.data = &table.data[pos]; - it.key = &table.keys[pos]; - - return it; - } - } - } + it.valid = true; + it.pos = 0; - return it; + return next_iter(it); } Iterator next_iter(const Iterator &p_iter) const { + if (!p_iter.valid) { return p_iter; } Iterator it; - it.valid = false; - it.was_from_old_table = false; - - bool check_old_table = is_rehashing; - - // we use this to skip the first check or not - bool was_from_old_table = p_iter.was_from_old_table; - - int prev_index = (p_iter.data - (p_iter.was_from_old_table ? old_table.data : table.data)); - - if (!was_from_old_table) { - prev_index++; - } + it.pos = p_iter.pos; + it.key = NULL; + it.value = NULL; - for (int i = prev_index; i < table.capacity; i++) { + for (uint32_t i = it.pos; i < capacity; i++) { + it.pos = i + 1; - // if we're rehashing check the old table first - if (check_old_table && i < old_table.capacity && !was_from_old_table) { - - int pos = i; - - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; - - bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - - if (is_filled_flag) { - it.valid = true; - it.hash = old_table.hashes[pos]; - it.data = &old_table.data[pos]; - it.key = &old_table.keys[pos]; - - it.was_from_old_table = true; - - return it; - } + if (hashes[i] == EMPTY_HASH) { + continue; } - - was_from_old_table = false; - - { - int pos = i; - - int flags_pos = pos / 4; - int flags_pos_offset = pos % 4; - - bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; - - if (is_filled_flag) { - it.valid = true; - it.hash = table.hashes[pos]; - it.data = &table.data[pos]; - it.key = &table.keys[pos]; - - return it; - } + if (hashes[i] & DELETED_HASH_BIT) { + continue; } + + it.valid = true; + it.key = &keys[i]; + it.value = &values[i]; + return it; } return it; } - OAHashMap(uint32_t p_initial_capacity = INITIAL_NUM_ELEMENTS) { + OAHashMap(uint32_t p_initial_capacity = 64) { -#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE + capacity = p_initial_capacity; + num_elements = 0; - if (p_initial_capacity <= INITIAL_NUM_ELEMENTS) { - table.data = local_data; - table.keys = local_keys; - table.hashes = local_hashes; - table.flags = local_flags; + keys = memnew_arr(TKey, p_initial_capacity); + values = memnew_arr(TValue, p_initial_capacity); + hashes = memnew_arr(uint32_t, p_initial_capacity); - zeromem(table.flags, INITIAL_NUM_ELEMENTS / 4 + (INITIAL_NUM_ELEMENTS % 4 != 0 ? 1 : 0)); - - table.capacity = INITIAL_NUM_ELEMENTS; - elements = 0; - } else -#endif - { - table.data = memnew_arr(TData, p_initial_capacity); - table.keys = memnew_arr(TKey, p_initial_capacity); - table.hashes = memnew_arr(uint32_t, p_initial_capacity); - table.flags = memnew_arr(uint8_t, p_initial_capacity / 4 + (p_initial_capacity % 4 != 0 ? 1 : 0)); - - zeromem(table.flags, p_initial_capacity / 4 + (p_initial_capacity % 4 != 0 ? 1 : 0)); - - table.capacity = p_initial_capacity; - elements = 0; + for (uint32_t i = 0; i < p_initial_capacity; i++) { + hashes[i] = 0; } - - is_rehashing = false; - rehash_position = 0; } ~OAHashMap() { -#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE - if (table.capacity <= INITIAL_NUM_ELEMENTS) { - return; // Everything is local, so no cleanup :P - } -#endif - if (is_rehashing) { - -#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE - if (old_table.data == local_data) { - // Everything is local, so no cleanup :P - } else -#endif - { - memdelete_arr(old_table.data); - memdelete_arr(old_table.keys); - memdelete_arr(old_table.hashes); - memdelete_arr(old_table.flags); - } - } - memdelete_arr(table.data); - memdelete_arr(table.keys); - memdelete_arr(table.hashes); - memdelete_arr(table.flags); + memdelete_arr(keys); + memdelete_arr(values); + memdelete_arr(hashes); } }; diff --git a/core/object.cpp b/core/object.cpp index aaa37e6cf2f4..1eff56f02696 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,26 +440,41 @@ 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]); - } + } + + //something inside the object... :| + bool success = _setv(p_name, p_value); + if (success) { if (r_valid) *r_valid = true; return; -#endif - } else { - //something inside the object... :| - bool success = _setv(p_name, p_value); - if (success) { + } + + { + bool valid; + setvar(p_name, p_value, &valid); + if (valid) { if (r_valid) *r_valid = true; return; } - setvar(p_name, p_value, r_valid); } + +#ifdef TOOLS_ENABLED + if (script_instance) { + bool valid; + script_instance->property_set_fallback(p_name, p_value, &valid); + if (valid) { + if (r_valid) + *r_valid = true; + return; + } + } +#endif + + if (r_valid) + *r_valid = false; + return; } Variant Object::get(const StringName &p_name, bool *r_valid) const { @@ -495,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::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); @@ -513,8 +519,33 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { *r_valid = true; return ret; } + //if nothing else, use getvar - return getvar(p_name, r_valid); + { + bool valid; + ret = getvar(p_name, &valid); + if (valid) { + if (r_valid) + *r_valid = true; + return ret; + } + } + +#ifdef TOOLS_ENABLED + if (script_instance) { + bool valid; + ret = script_instance->property_get_fallback(p_name, &valid); + if (valid) { + if (r_valid) + *r_valid = true; + return ret; + } + } +#endif + + if (r_valid) + *r_valid = false; + return Variant(); } } @@ -601,15 +632,15 @@ void Object::get_property_list(List *p_list, bool p_reversed) cons _get_property_listv(p_list, p_reversed); - if (!is_class("Script")) // can still be set, but this is for userfriendlyness - p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NONZERO)); + if (!is_class("Script")) { // can still be set, but this is for userfriendlyness #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)); - } + p_list->push_back(PropertyInfo(Variant::NIL, "Script", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); #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)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT)); + } + if (!metadata.empty()) { + p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + } if (script_instance && !p_reversed) { p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); script_instance->get_property_list(p_list); @@ -814,8 +845,8 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) { argptrs.resize(p_args.size()); for (int i = 0; i < p_args.size(); i++) { - args[i] = p_args[i]; - argptrs[i] = &args[i]; + args.write[i] = p_args[i]; + argptrs.write[i] = &args[i]; } Variant::CallError ce; @@ -975,9 +1006,14 @@ void Object::set_script(const RefPtr &p_script) { script = p_script; Ref + + + diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png index 6b9b10daaead..1299ceaee5d9 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png index 50497496b7c1..604a7ba701b9 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png index 1c489de69f52..bffb8c9fdebb 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png index d82dfce9363b..47826cd683b3 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png index 5120595df852..0f44a704b5c6 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png index cf6cf1347ad4..07ab777bc2ed 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png index 4eb167ae18ae..774b9c5bbf1e 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png index a9f951deac75..fff36679c720 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png index 06d16412e2e2..0804519faafe 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png index d70cab17bef3..833142222ce1 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png differ diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png index f9349710742c..4c934c4a53b5 100644 Binary files a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ diff --git a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist index 70932c194375..6907ae4a9db8 100644 --- a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist +++ b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist @@ -41,11 +41,15 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown UISupportedInterfaceOrientations~ipad UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown $additional_plist_content diff --git a/misc/dist/linux/godot.6 b/misc/dist/linux/godot.6 index 26ebe01af734..50cb420e4e80 100644 --- a/misc/dist/linux/godot.6 +++ b/misc/dist/linux/godot.6 @@ -1,4 +1,4 @@ -.TH GODOT "6" "February 2018" "godot 3.1" "Games" +.TH GODOT "6" "Februaryot 2018" "godot 3.1" "Games" .SH NAME godot \- multi\-platform 2D and 3D game engine with a feature\-rich editor .SH SYNOPSIS @@ -127,11 +127,14 @@ Print the frames per second to the stdout. \fB\-s\fR, \fB\-\-script\fR