diff options
252 files changed, 5682 insertions, 5006 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 91528465c0..7e8c5fd740 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,13 +6,24 @@ labels: '' assignees: '' --- +<!-- Please search existing issues for potential duplicates before filing yours: +https://github.com/godotengine/godot/issues?q=is%3Aissue +--> **Godot version:** +<!-- Specify commit hash if using non-official build. --> + **OS/device including version:** +<!-- Specify GPU model, drivers, and the backend (GLES2, GLES3, Vulkan) if graphics-related. --> + **Issue description:** +<!-- What happened, and what was expected. --> + **Steps to reproduce:** + **Minimal reproduction project:** +<!-- A small Godot project which reproduces the issue. Drag and drop a zip archive to upload it. --> diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..f787bec00e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: false + +contact_links: + - name: Godot proposals + url: https://github.com/godotengine/godot-proposals + about: Please submit feature proposals on the Godot proposals repository, not here. + + - name: Godot documentation repository + url: https://github.com/godotengine/godot-docs + about: Please report issues with documentation on the Godot documentation repository, not here. + + - name: Godot community channels + url: https://godotengine.org/community + about: Please ask for technical support on one of the other community channels, not here. diff --git a/.github/ISSUE_TEMPLATE/feature---enhancement-request.md b/.github/ISSUE_TEMPLATE/feature---enhancement-request.md deleted file mode 100644 index 496e0197ca..0000000000 --- a/.github/ISSUE_TEMPLATE/feature---enhancement-request.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Feature / Enhancement Request -about: Adding new features or improving existing ones. -title: 'IMPORTANT: This repository no longer accepts feature / enhancement requests.' -labels: '' -assignees: '' - ---- - -**IMPORTANT, PLEASE READ** - -Feature / Enhancement requests are no longer accepted in the main Godot repository. -Please open an item by filling the relevant fields in the *Proposals* repository: - -https://github.com/godotengine/godot-proposals/issues/new/choose - -Do not submit to this repository or your issue will be closed. - -**IMPORTANT, PLEASE READ** diff --git a/.travis.yml b/.travis.yml index 8878935e52..58c3733d22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,7 +69,7 @@ matrix: stage: build env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang EXTRA_ARGS="warnings=extra werror=yes" os: osx - osx_image: xcode11.3 + osx_image: xcode11.5 compiler: clang addons: homebrew: @@ -83,7 +83,7 @@ matrix: # stage: build # env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang # os: osx -# osx_image: xcode11.3 +# osx_image: xcode11.5 # compiler: clang # addons: # homebrew: @@ -128,7 +128,7 @@ install: - if [ "$PLATFORM" = "javascript" ]; then git clone --depth 1 "https://github.com/emscripten-core/emsdk.git"; ./emsdk/emsdk install latest; - ./emsdk/emsdk activate latest; + ./emsdk/emsdk activate --no-embedded latest; fi - if [ "$STATIC_CHECKS" = "yes" ]; then unset SCONS_CACHE; diff --git a/AUTHORS.md b/AUTHORS.md index 0f002cfaed..e8bb91d4bb 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -95,6 +95,7 @@ name is available. Indah Sylvia (ISylvox) J08nY Jakub Grzesik (kubecz3k) + James Buck (jbuck3) Jérôme Gully (Nutriz) Joan Fons Sanchez (JFonS) Johan Manuel (29jm) diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 2d32bf1fd9..fc3079c361 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -299,7 +299,7 @@ License: Zlib Files: ./thirdparty/oidn/ Comment: Intel Open Image Denoise -Copyright: 2009-2020, Intel Corporation +Copyright: 2009-2019, Intel Corporation License: Apache-2.0 Files: ./thirdparty/opus/ @@ -24,11 +24,12 @@ generous deed immortalized in the next stable release of Godot Engine. AD Ford Alan Beauchamp - Anand Mallik + albinaask Andrew Dunai Brandon Lamb Christian Baune Christopher Montesano + Darius Pranskus Darkhan Baimyrza Darrin Massena Dov Zimring @@ -41,15 +42,18 @@ generous deed immortalized in the next stable release of Godot Engine. Jasper Brooks Javary Co. Jeffery Chiu + John Benard (Linuxydable) Justin Arnold Justo Delgado Baudí Kyle Szklenski + Marcel Kräml Matthieu Huvé Maxim Karsten Mike King Nathan Warden Neal Gompa (Conan Kudo) Péter Magyar + Ronnie Cheng Slobodan Milnovic Stephan Lanfermann Steve @@ -58,12 +62,14 @@ generous deed immortalized in the next stable release of Godot Engine. ## Gold donors + Bjarke David Gehrig David Graham David Snopek Ed Morley Florian Rämisch Jakub Grzesik + HardRound Manuele Finocchiaro Officine Pixel S.n.c. Ronan Zeegers @@ -92,10 +98,10 @@ generous deed immortalized in the next stable release of Godot Engine. Maciej Pendolski Matthew Hillier Mohamed Ikbel Boulabiar - Mored4u Rene Retro Village Rob Messick + Roland Fredenhagen Ryan Badour Sandro Jenny Scott Wadden @@ -108,16 +114,19 @@ generous deed immortalized in the next stable release of Godot Engine. Alex Khayrullin alice gambrell + Barugon Chris Goddard Chris Serino Christian Padilla Conrad Curry Craig Smith Darrian Little + Hoai Nam Tran Horváth Péter - Ivan Trombley + Jamal Aboudrar Joan Fons Joshua Flores + Leo Fidel R Liban Petr Malac Rami Rob @@ -149,20 +158,20 @@ generous deed immortalized in the next stable release of Godot Engine. Christoph Schröder Codee Leaf Cody Parker - Coldragon Craig Ostrin + curtis Kramer D Easypete + Edgar Sun Eric Monson Eugenio Hugo Salgüero Jáñez - Fain flesk Gary Hulst gavlig GGGames.org Guilherme Felipe de C. G. da Silva - Halom Vered Heath Hayes + Hu Hund Isaac Clausman Jared White Jeff Nyte @@ -171,18 +180,21 @@ generous deed immortalized in the next stable release of Godot Engine. Jose Malheiro Joshua Lesperance Juan Velandia + Julian Todd Juraj Móza Kelteseth kickmaniac kinfox Lain Ballard Marcelo Dornbusch Lopes + Marcelo Henrique Gonçalves Markus Fehr Markus Wiesner Martin Eigel Matt Eunson m kaersten MuffinManKen + Nick Abousselam Oliver Dick Oscar Campos Patrick Ting @@ -197,14 +209,16 @@ generous deed immortalized in the next stable release of Godot Engine. Samuel Judd Scott Pilet Sean Morgan + Sean Robertson + Sébastien Serban Serafimescu - Sindre Sømme SleepCircle spilldata Stoned Xander TheLevelOfDetail . Thomas Kurz Tobias Bocanegra + Trent Fehl Urho William Foster Zhou Tuizhi @@ -214,6 +228,7 @@ generous deed immortalized in the next stable release of Godot Engine. ## Silver donors 1D_Inc + Aaron Winter Abraham Haskins Acheron Adam @@ -229,7 +244,6 @@ generous deed immortalized in the next stable release of Godot Engine. Agustinus Arya Aidan O'Flannagain Aki Mimoto - Alan Mervitz Alan Stice Albin Jonasson Svärdsby Alder Stefano @@ -237,14 +251,17 @@ generous deed immortalized in the next stable release of Godot Engine. Alessandro Senese Alexander Erlemann alex clavelle + Alfred Reinold Baudisch Allan Davis Allen Schade - Andreas Evers Andreas Krampitz + Andres Hernandez André Simões + andrew james morris + Andrew Mansuetti Andrew Thomas + Ano Nim Anthony Avina - Anthony Staunton AP Condomines Arda Erol Armin Preiml @@ -252,10 +269,11 @@ generous deed immortalized in the next stable release of Godot Engine. Arthur S. Muszynski Asger Ashley Claymore + Ashton Scott Snapp Aubrey Falconer - Avencherus B A Balázs Batári + Bartosz Bielecki Benedikt Ben Vercammen Bernd Jänichen @@ -265,10 +283,8 @@ generous deed immortalized in the next stable release of Godot Engine. Bobby CC Wong Bram brian - bugcaptor Burney Waring Cameron Meyer - Carlo Sitaro Carl van der Geest Carwyn Edwards Cas Brugman @@ -289,7 +305,7 @@ generous deed immortalized in the next stable release of Godot Engine. Daniel Tebbutt David May David Woodard - DiCola Jamn + Dimitri Stanojevic Dominic Cooney Dominik Wetzel Donn Eddy @@ -301,24 +317,25 @@ generous deed immortalized in the next stable release of Godot Engine. Eduardo Teixeira Edward Herbert Edward Swartz + Eelco F Hillenius Egon Elbre Elgenzay Elias Nykrem - Elmeri '- Duy Kevin Nguyen Ephemeral Eric Ellingson - Eric Rogers Eric Williams Erkki Seppälä Evan Rose + Fain + Faisal Alkubaisi Fancy Ants Studios Fekinox Felix Bohmann Felix Kollmann Flaredown Forty Doubleu - fox FuDiggity + Frank Gadzhi Kharkharov gamedev by Celio Gary Thomas @@ -328,16 +345,17 @@ generous deed immortalized in the next stable release of Godot Engine. Greyson Richey Grid Guillaume Audirac + Guillaume Pham Ngoc Guldoman Hal A Heribert Hirth Hunter Jones Hylpher - Ichiro Dohi Iiari iKlem IndustrialRobot Ivan Nikolaev + Jackson Harmer Jacob Jaiden Gerig Jaime Ruiz-Borau Vizárraga @@ -346,9 +364,11 @@ generous deed immortalized in the next stable release of Godot Engine. Janders Jannik Gröger JARKKO PARVIAINEN + Jean-Baptiste LEPESME Jeff Hungerford Jennifer Graves Jesse Dubay + Joe Alden Joel Fivat Joel Höglund Joel Setterberg @@ -363,14 +383,18 @@ generous deed immortalized in the next stable release of Godot Engine. Jonathan G Jon Bonazza Jon Sully + Jordy Goodridge + Jorge Antunes Jorge Caballero Jose Aleman Jose C. Rubio Joseph Catrambone Josh Mitchell + Joshua Southerland Juanfran Judd Julian Murgia + June Little JungleRobba Justin Hamilton Justin Spedding @@ -382,7 +406,6 @@ generous deed immortalized in the next stable release of Godot Engine. Kent Jofur Kevin McPhillips Kiri Jolly - Kiyohiro Kawamura (kyorohiro) Kjetil Haugland Klagsam KsyTek Games @@ -390,26 +413,24 @@ generous deed immortalized in the next stable release of Godot Engine. kycho Kyle Appelgate Laurent Tréguier + Leonard Meagher Leonardo Dimano Levi Lindsey Lin Chear Linus Lind Lundgren Lionel Gaillard - Lukáš Rendvanský Luigi Renna LunaticInAHat Lurkars Major Haul Malcolm - Malik Ahmed - Malik Nejer + Marco Lardelli Markus Michael Egger Martin Holas Martin Liška Marvin - Mathieu Rimelen + Mathieu Matt Edwards - Matthew Little Mauro Pellegrini Max Fiedler Maxime Blade @@ -426,7 +447,7 @@ generous deed immortalized in the next stable release of Godot Engine. Mikayla Mike Birkhead Mike Cunningham - Mitchell J. Wagner + Molinghu MoM Mored4u Nathan Fish @@ -435,8 +456,10 @@ generous deed immortalized in the next stable release of Godot Engine. Neil Blakey-Milner Neil Wang Nerdforge + Nerdyninja Nicholas Nicholas Girga + Nick Allen Nick Macholl Niclas Eriksen Nicolás Montaña @@ -453,11 +476,12 @@ generous deed immortalized in the next stable release of Godot Engine. Paweł Kowal Pedro Assuncao Penguin + Peter Philip Cohoe Point08 + pwab Rad Cat Rafa Laguna - rainerLinux Remi Rampin Rémi Verschelde Ricardo Alcantara @@ -470,6 +494,7 @@ generous deed immortalized in the next stable release of Godot Engine. Roman Tinkov Ronald Ho Hip (CrimsonZA) Ronan + Ronny Mühle Ryan Groom Ryan Hentz Sam Edson @@ -483,6 +508,7 @@ generous deed immortalized in the next stable release of Godot Engine. Shane Shane Sicienski Shane Spoor + Shiomi '- Duy Kevin Nguyen Siim Raidma Simon Jonas Larsen Simon Wenner @@ -490,17 +516,21 @@ generous deed immortalized in the next stable release of Godot Engine. SK smbe19 smo1704 + soft circles + Squirrel Stefano Caronia Steve Cloete Svenne Krap Taylor Fahlman Terry tezuvholovdr + TheVoiceInMyHead thomas Thomas Bechtold Thomas Detoy Thomas Kelly Tim Drumheller + Tim Erskine Timothy B. MacDonald Title Plinsut Tobbun @@ -516,6 +546,7 @@ generous deed immortalized in the next stable release of Godot Engine. Tyler Compton Tyler Stafos UltyX + Valentí Gàmez Vaughan Ling Victor Vigilant Watch @@ -525,10 +556,12 @@ generous deed immortalized in the next stable release of Godot Engine. werner mendizabal Wiley Thompson Will - William Hogben Wyatt Goodin Yegor + Yuri LaPointe Yuri Sizov + Zgegnesh Hemomancer + 郝晨煜 ## Bronze donors diff --git a/SConstruct b/SConstruct index f74940b059..b424363dc8 100644 --- a/SConstruct +++ b/SConstruct @@ -8,6 +8,7 @@ import glob import os import pickle import sys +from collections import OrderedDict # Local import methods @@ -50,8 +51,6 @@ for x in sorted(glob.glob("platform/*")): sys.path.remove(tmppath) sys.modules.pop("detect") -module_list = methods.detect_modules() - methods.save_active_platforms(active_platforms, active_platform_ids) custom_tools = ["default"] @@ -87,6 +86,7 @@ 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.__class__.module_check_dependencies = methods.module_check_dependencies env_base["x86_libtheora_opt_gcc"] = False env_base["x86_libtheora_opt_vc"] = False @@ -123,6 +123,7 @@ opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise e opts.Add(BoolVariable("deprecated", "Enable deprecated features", True)) opts.Add(BoolVariable("minizip", "Enable ZIP archive support using minizip", True)) opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False)) +opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "") # Advanced options opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False)) @@ -181,21 +182,49 @@ for k in platform_opts.keys(): for o in opt_list: opts.Add(o) -for x in module_list: - module_enabled = True - tmppath = "./modules/" + x - sys.path.insert(0, tmppath) +# Update the environment now as the "custom_modules" option may be +# defined in a file rather than specified via the command line. +opts.Update(env_base) + +# Detect modules. +modules_detected = OrderedDict() +module_search_paths = ["modules"] # Built-in path. + +if env_base["custom_modules"]: + paths = env_base["custom_modules"].split(",") + for p in paths: + try: + module_search_paths.append(methods.convert_custom_modules_path(p)) + except ValueError as e: + print(e) + Exit(255) + +for path in module_search_paths: + # Note: custom modules can override built-in ones. + modules_detected.update(methods.detect_modules(path)) + include_path = os.path.dirname(path) + if include_path: + env_base.Prepend(CPPPATH=[include_path]) + +# Add module options. +for name, path in modules_detected.items(): + enabled = True + sys.path.insert(0, path) import config - enabled_attr = getattr(config, "is_enabled", None) - if callable(enabled_attr) and not config.is_enabled(): - module_enabled = False - sys.path.remove(tmppath) + try: + enabled = config.is_enabled() + except AttributeError: + pass + sys.path.remove(path) sys.modules.pop("config") - opts.Add(BoolVariable("module_" + x + "_enabled", "Enable module '%s'" % (x,), module_enabled)) + opts.Add(BoolVariable("module_" + name + "_enabled", "Enable module '%s'" % (name,), enabled)) -opts.Update(env_base) # update environment -Help(opts.GenerateHelpText(env_base)) # generate help +methods.write_modules(modules_detected) + +# Update the environment again after all the module options are added. +opts.Update(env_base) +Help(opts.GenerateHelpText(env_base)) # add default include paths @@ -370,7 +399,7 @@ if selected_platform in platform_list: 'newer GCC version, or Clang 6 or later by passing "use_llvm=yes" ' "to the SCons command line." ) - sys.exit(255) + Exit(255) elif cc_version_major < 7: print( "Detected GCC version older than 7, which does not fully support " @@ -378,7 +407,7 @@ if selected_platform in platform_list: 'version, or Clang 6 or later by passing "use_llvm=yes" to the ' "SCons command line." ) - sys.exit(255) + Exit(255) elif methods.using_clang(env): # Apple LLVM versions differ from upstream LLVM version \o/, compare # in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions @@ -389,19 +418,19 @@ if selected_platform in platform_list: "Detected Clang version older than 6, which does not fully support " "C++17. Supported versions are Clang 6 and later." ) - sys.exit(255) + Exit(255) elif not vanilla and cc_version_major < 10: print( "Detected Apple Clang version older than 10, which does not fully " "support C++17. Supported versions are Apple Clang 10 and later." ) - sys.exit(255) + Exit(255) elif cc_version_major < 6: print( "Detected Clang version older than 6, which does not fully support " "C++17. Supported versions are Clang 6 and later." ) - sys.exit(255) + Exit(255) # Configure compiler warnings if env.msvc: @@ -474,7 +503,7 @@ if selected_platform in platform_list: if env["target"] == "release": if env["tools"]: print("Tools can only be built with targets 'debug' and 'release_debug'.") - sys.exit(255) + Exit(255) suffix += ".opt" env.Append(CPPDEFINES=["NDEBUG"]) @@ -501,41 +530,41 @@ if selected_platform in platform_list: sys.path.remove(tmppath) sys.modules.pop("detect") - env.module_list = [] + modules_enabled = OrderedDict() env.module_icons_paths = [] env.doc_class_path = {} - for x in sorted(module_list): - if not env["module_" + x + "_enabled"]: + for name, path in modules_detected.items(): + if not env["module_" + name + "_enabled"]: continue - tmppath = "./modules/" + x - sys.path.insert(0, tmppath) - env.current_module = x + sys.path.insert(0, path) + env.current_module = name import config if config.can_build(env, selected_platform): config.configure(env) - env.module_list.append(x) - # Get doc classes paths (if present) try: doc_classes = config.get_doc_classes() doc_path = config.get_doc_path() for c in doc_classes: - env.doc_class_path[c] = "modules/" + x + "/" + doc_path + env.doc_class_path[c] = path + "/" + doc_path except: pass # Get icon paths (if present) try: icons_path = config.get_icons_path() - env.module_icons_paths.append("modules/" + x + "/" + icons_path) + env.module_icons_paths.append(path + "/" + icons_path) except: # Default path for module icons - env.module_icons_paths.append("modules/" + x + "/" + "icons") + env.module_icons_paths.append(path + "/" + "icons") + modules_enabled[name] = path - sys.path.remove(tmppath) + sys.path.remove(path) sys.modules.pop("config") + env.module_list = modules_enabled + methods.update_version(env.module_version_string) env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"] @@ -563,7 +592,7 @@ if selected_platform in platform_list: "Build option 'disable_3d=yes' cannot be used with 'tools=yes' (editor), " "only with 'tools=no' (export template)." ) - sys.exit(255) + Exit(255) else: env.Append(CPPDEFINES=["_3D_DISABLED"]) if env["disable_advanced_gui"]: @@ -572,21 +601,19 @@ if selected_platform in platform_list: "Build option 'disable_advanced_gui=yes' cannot be used with 'tools=yes' (editor), " "only with 'tools=no' (export template)." ) - sys.exit(255) + Exit(255) else: env.Append(CPPDEFINES=["ADVANCED_GUI_DISABLED"]) if env["minizip"]: env.Append(CPPDEFINES=["MINIZIP_ENABLED"]) editor_module_list = ["regex"] - for x in editor_module_list: - if not env["module_" + x + "_enabled"]: - if env["tools"]: - print( - "Build option 'module_" + x + "_enabled=no' cannot be used with 'tools=yes' (editor), " - "only with 'tools=no' (export template)." - ) - sys.exit(255) + if env["tools"] and not env.module_check_dependencies("tools", editor_module_list): + print( + "Build option 'module_" + x + "_enabled=no' cannot be used with 'tools=yes' (editor), " + "only with 'tools=no' (export template)." + ) + Exit(255) if not env["verbose"]: methods.no_verbose(sys, env) @@ -662,126 +689,10 @@ elif selected_platform != "": if selected_platform == "list": # Exit early to suppress the rest of the built-in SCons messages - sys.exit(0) + Exit() else: - sys.exit(255) + Exit(255) # The following only makes sense when the env is defined, and assumes it is if "env" in locals(): - screen = sys.stdout - # Progress reporting is not available in non-TTY environments since it - # messes with the output (for example, when writing to a file) - show_progress = env["progress"] and sys.stdout.isatty() - node_count = 0 - node_count_max = 0 - node_count_interval = 1 - node_count_fname = str(env.Dir("#")) + "/.scons_node_count" - - import time, math - - class cache_progress: - # The default is 1 GB cache and 12 hours half life - def __init__(self, path=None, limit=1073741824, half_life=43200): - self.path = path - self.limit = limit - self.exponent_scale = math.log(2) / half_life - if env["verbose"] and path != None: - screen.write( - "Current cache limit is {} (used: {})\n".format( - self.convert_size(limit), self.convert_size(self.get_size(path)) - ) - ) - self.delete(self.file_list()) - - def __call__(self, node, *args, **kw): - global node_count, node_count_max, node_count_interval, node_count_fname, show_progress - if show_progress: - # Print the progress percentage - node_count += node_count_interval - if node_count_max > 0 and node_count <= node_count_max: - screen.write("\r[%3d%%] " % (node_count * 100 / node_count_max)) - screen.flush() - elif node_count_max > 0 and node_count > node_count_max: - screen.write("\r[100%] ") - screen.flush() - else: - screen.write("\r[Initial build] ") - screen.flush() - - def delete(self, files): - if len(files) == 0: - return - if env["verbose"]: - # Utter something - screen.write("\rPurging %d %s from cache...\n" % (len(files), len(files) > 1 and "files" or "file")) - [os.remove(f) for f in files] - - def file_list(self): - if self.path is None: - # Nothing to do - return [] - # Gather a list of (filename, (size, atime)) within the - # cache directory - file_stat = [(x, os.stat(x)[6:8]) for x in glob.glob(os.path.join(self.path, "*", "*"))] - if file_stat == []: - # Nothing to do - return [] - # Weight the cache files by size (assumed to be roughly - # proportional to the recompilation time) times an exponential - # decay since the ctime, and return a list with the entries - # (filename, size, weight). - current_time = time.time() - file_stat = [(x[0], x[1][0], (current_time - x[1][1])) for x in file_stat] - # Sort by the most recently accessed files (most sensible to keep) first - file_stat.sort(key=lambda x: x[2]) - # Search for the first entry where the storage limit is - # reached - sum, mark = 0, None - for i, x in enumerate(file_stat): - sum += x[1] - if sum > self.limit: - mark = i - break - if mark is None: - return [] - else: - return [x[0] for x in file_stat[mark:]] - - def convert_size(self, size_bytes): - if size_bytes == 0: - return "0 bytes" - size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") - i = int(math.floor(math.log(size_bytes, 1024))) - p = math.pow(1024, i) - s = round(size_bytes / p, 2) - return "%s %s" % (int(s) if i == 0 else s, size_name[i]) - - def get_size(self, start_path="."): - total_size = 0 - for dirpath, dirnames, filenames in os.walk(start_path): - for f in filenames: - fp = os.path.join(dirpath, f) - total_size += os.path.getsize(fp) - return total_size - - def progress_finish(target, source, env): - global node_count, progressor - with open(node_count_fname, "w") as f: - f.write("%d\n" % node_count) - progressor.delete(progressor.file_list()) - - try: - with open(node_count_fname) as f: - node_count_max = int(f.readline()) - except: - pass - - cache_directory = os.environ.get("SCONS_CACHE") - # Simple cache pruning, attached to SCons' progress callback. Trim the - # cache directory to a size not larger than cache_limit. - cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024 - progressor = cache_progress(cache_directory, cache_limit) - Progress(progressor, interval=node_count_interval) - - progress_finish_command = Command("progress_finish", [], progress_finish) - AlwaysBuild(progress_finish_command) + methods.show_progress(env) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 10f44d357b..e81351a3a6 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -35,7 +35,8 @@ #include "core/io/file_access_encrypted.h" #include "core/io/json.h" #include "core/io/marshalls.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" +#include "core/math/geometry_3d.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/project_settings.h" @@ -828,55 +829,43 @@ void _OS::_bind_methods() { BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES); } -////// _Geometry ////// +////// _Geometry2D ////// -_Geometry *_Geometry::singleton = nullptr; +_Geometry2D *_Geometry2D::singleton = nullptr; -_Geometry *_Geometry::get_singleton() { +_Geometry2D *_Geometry2D::get_singleton() { return singleton; } -Vector<Plane> _Geometry::build_box_planes(const Vector3 &p_extents) { - return Geometry::build_box_planes(p_extents); +bool _Geometry2D::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { + return Geometry2D::is_point_in_circle(p_point, p_circle_pos, p_circle_radius); } -Vector<Plane> _Geometry::build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis) { - return Geometry::build_cylinder_planes(p_radius, p_height, p_sides, p_axis); +real_t _Geometry2D::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { + return Geometry2D::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius); } -Vector<Plane> _Geometry::build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { - return Geometry::build_capsule_planes(p_radius, p_height, p_sides, p_lats, p_axis); -} - -bool _Geometry::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { - return Geometry::is_point_in_circle(p_point, p_circle_pos, p_circle_radius); -} - -real_t _Geometry::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { - return Geometry::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius); -} - -Variant _Geometry::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 _Geometry2D::segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b) { Vector2 result; - if (Geometry::segment_intersects_segment_2d(p_from_a, p_to_a, p_from_b, p_to_b, &result)) { + if (Geometry2D::segment_intersects_segment(p_from_a, p_to_a, p_from_b, p_to_b, &result)) { return result; } else { return Variant(); } } -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) { +Variant _Geometry2D::line_intersects_line(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)) { + if (Geometry2D::line_intersects_line(p_from_a, p_dir_a, p_from_b, p_dir_b, result)) { return result; } else { return Variant(); } } -Vector<Vector2> _Geometry::get_closest_points_between_segments_2d(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2) { +Vector<Vector2> _Geometry2D::get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2) { Vector2 r1, r2; - Geometry::get_closest_points_between_segments(p1, q1, p2, q2, r1, r2); + Geometry2D::get_closest_points_between_segments(p1, q1, p2, q2, r1, r2); Vector<Vector2> r; r.resize(2); r.set(0, r1); @@ -884,123 +873,42 @@ Vector<Vector2> _Geometry::get_closest_points_between_segments_2d(const Vector2 return r; } -Vector<Vector3> _Geometry::get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2) { - Vector3 r1, r2; - Geometry::get_closest_points_between_segments(p1, p2, q1, q2, r1, r2); - Vector<Vector3> r; - r.resize(2); - r.set(0, r1); - r.set(1, r2); - return r; -} - -Vector2 _Geometry::get_closest_point_to_segment_2d(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) { +Vector2 _Geometry2D::get_closest_point_to_segment(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) { Vector2 s[2] = { p_a, p_b }; - return Geometry::get_closest_point_to_segment_2d(p_point, s); -} - -Vector3 _Geometry::get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) { - Vector3 s[2] = { p_a, p_b }; - return Geometry::get_closest_point_to_segment(p_point, s); + return Geometry2D::get_closest_point_to_segment(p_point, s); } -Vector2 _Geometry::get_closest_point_to_segment_uncapped_2d(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) { +Vector2 _Geometry2D::get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) { Vector2 s[2] = { p_a, p_b }; - return Geometry::get_closest_point_to_segment_uncapped_2d(p_point, s); -} - -Vector3 _Geometry::get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) { - Vector3 s[2] = { p_a, p_b }; - return Geometry::get_closest_point_to_segment_uncapped(p_point, s); -} - -Variant _Geometry::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { - Vector3 res; - if (Geometry::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) { - return res; - } else { - return Variant(); - } -} - -Variant _Geometry::segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { - Vector3 res; - if (Geometry::segment_intersects_triangle(p_from, p_to, p_v0, p_v1, p_v2, &res)) { - return res; - } else { - return Variant(); - } -} - -bool _Geometry::point_is_inside_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) const { - return Geometry::is_point_in_triangle(s, a, b, c); -} - -Vector<Vector3> _Geometry::segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius) { - Vector<Vector3> r; - Vector3 res, norm; - if (!Geometry::segment_intersects_sphere(p_from, p_to, p_sphere_pos, p_sphere_radius, &res, &norm)) { - return r; - } - - r.resize(2); - r.set(0, res); - r.set(1, norm); - return r; + return Geometry2D::get_closest_point_to_segment_uncapped(p_point, s); } -Vector<Vector3> _Geometry::segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius) { - Vector<Vector3> r; - Vector3 res, norm; - if (!Geometry::segment_intersects_cylinder(p_from, p_to, p_height, p_radius, &res, &norm)) { - return r; - } - - r.resize(2); - r.set(0, res); - r.set(1, norm); - return r; +bool _Geometry2D::point_is_inside_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) const { + return Geometry2D::is_point_in_triangle(s, a, b, c); } -Vector<Vector3> _Geometry::segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes) { - Vector<Vector3> r; - Vector3 res, norm; - if (!Geometry::segment_intersects_convex(p_from, p_to, p_planes.ptr(), p_planes.size(), &res, &norm)) { - return r; - } - - r.resize(2); - r.set(0, res); - r.set(1, norm); - return r; -} - -bool _Geometry::is_polygon_clockwise(const Vector<Vector2> &p_polygon) { - return Geometry::is_polygon_clockwise(p_polygon); -} - -bool _Geometry::is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon) { - return Geometry::is_point_in_polygon(p_point, p_polygon); +bool _Geometry2D::is_polygon_clockwise(const Vector<Vector2> &p_polygon) { + return Geometry2D::is_polygon_clockwise(p_polygon); } -Vector<int> _Geometry::triangulate_polygon(const Vector<Vector2> &p_polygon) { - return Geometry::triangulate_polygon(p_polygon); +bool _Geometry2D::is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon) { + return Geometry2D::is_point_in_polygon(p_point, p_polygon); } -Vector<int> _Geometry::triangulate_delaunay_2d(const Vector<Vector2> &p_points) { - return Geometry::triangulate_delaunay_2d(p_points); +Vector<int> _Geometry2D::triangulate_polygon(const Vector<Vector2> &p_polygon) { + return Geometry2D::triangulate_polygon(p_polygon); } -Vector<Point2> _Geometry::convex_hull_2d(const Vector<Point2> &p_points) { - return Geometry::convex_hull_2d(p_points); +Vector<int> _Geometry2D::triangulate_delaunay(const Vector<Vector2> &p_points) { + return Geometry2D::triangulate_delaunay(p_points); } -Vector<Vector3> _Geometry::clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane) { - return Geometry::clip_polygon(p_points, p_plane); +Vector<Point2> _Geometry2D::convex_hull(const Vector<Point2> &p_points) { + return Geometry2D::convex_hull(p_points); } -Array _Geometry::merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { - Vector<Vector<Point2>> polys = Geometry::merge_polygons_2d(p_polygon_a, p_polygon_b); +Array _Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + Vector<Vector<Point2>> polys = Geometry2D::merge_polygons(p_polygon_a, p_polygon_b); Array ret; @@ -1010,8 +918,8 @@ Array _Geometry::merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vec return ret; } -Array _Geometry::clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { - Vector<Vector<Point2>> polys = Geometry::clip_polygons_2d(p_polygon_a, p_polygon_b); +Array _Geometry2D::clip_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + Vector<Vector<Point2>> polys = Geometry2D::clip_polygons(p_polygon_a, p_polygon_b); Array ret; @@ -1021,8 +929,8 @@ Array _Geometry::clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vect return ret; } -Array _Geometry::intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { - Vector<Vector<Point2>> polys = Geometry::intersect_polygons_2d(p_polygon_a, p_polygon_b); +Array _Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + Vector<Vector<Point2>> polys = Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b); Array ret; @@ -1032,8 +940,8 @@ Array _Geometry::intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const return ret; } -Array _Geometry::exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { - Vector<Vector<Point2>> polys = Geometry::exclude_polygons_2d(p_polygon_a, p_polygon_b); +Array _Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + Vector<Vector<Point2>> polys = Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b); Array ret; @@ -1043,8 +951,8 @@ Array _Geometry::exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const V return ret; } -Array _Geometry::clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { - Vector<Vector<Point2>> polys = Geometry::clip_polyline_with_polygon_2d(p_polyline, p_polygon); +Array _Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + Vector<Vector<Point2>> polys = Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon); Array ret; @@ -1054,8 +962,8 @@ Array _Geometry::clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline return ret; } -Array _Geometry::intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { - Vector<Vector<Point2>> polys = Geometry::intersect_polyline_with_polygon_2d(p_polyline, p_polygon); +Array _Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + Vector<Vector<Point2>> polys = Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon); Array ret; @@ -1065,8 +973,8 @@ Array _Geometry::intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_pol return ret; } -Array _Geometry::offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { - Vector<Vector<Point2>> polys = Geometry::offset_polygon_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type)); +Array _Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { + Vector<Vector<Point2>> polys = Geometry2D::offset_polygon(p_polygon, p_delta, Geometry2D::PolyJoinType(p_join_type)); Array ret; @@ -1076,8 +984,8 @@ Array _Geometry::offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_de return ret; } -Array _Geometry::offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { - Vector<Vector<Point2>> polys = Geometry::offset_polyline_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type), Geometry::PolyEndType(p_end_type)); +Array _Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + Vector<Vector<Point2>> polys = Geometry2D::offset_polyline(p_polygon, p_delta, Geometry2D::PolyJoinType(p_join_type), Geometry2D::PolyEndType(p_end_type)); Array ret; @@ -1087,7 +995,7 @@ Array _Geometry::offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_d return ret; } -Dictionary _Geometry::make_atlas(const Vector<Size2> &p_rects) { +Dictionary _Geometry2D::make_atlas(const Vector<Size2> &p_rects) { Dictionary ret; Vector<Size2i> rects; @@ -1098,7 +1006,7 @@ Dictionary _Geometry::make_atlas(const Vector<Size2> &p_rects) { Vector<Point2i> result; Size2i size; - Geometry::make_atlas(rects, result, size); + Geometry2D::make_atlas(rects, result, size); Size2 r_size = size; Vector<Point2> r_result; @@ -1112,56 +1020,37 @@ Dictionary _Geometry::make_atlas(const Vector<Size2> &p_rects) { return ret; } -int _Geometry::get_uv84_normal_bit(const Vector3 &p_vector) { - return Geometry::get_uv84_normal_bit(p_vector); -} - -void _Geometry::_bind_methods() { - ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &_Geometry::build_box_planes); - ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &_Geometry::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); - 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("is_point_in_circle", "point", "circle_position", "circle_radius"), &_Geometry::is_point_in_circle); - 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); +void _Geometry2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &_Geometry2D::is_point_in_circle); + ClassDB::bind_method(D_METHOD("segment_intersects_segment", "from_a", "to_a", "from_b", "to_b"), &_Geometry2D::segment_intersects_segment); + ClassDB::bind_method(D_METHOD("line_intersects_line", "from_a", "dir_a", "from_b", "dir_b"), &_Geometry2D::line_intersects_line); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_2d", "point", "s1", "s2"), &_Geometry::get_closest_point_to_segment_2d); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &_Geometry::get_closest_point_to_segment); + ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "q1", "p2", "q2"), &_Geometry2D::get_closest_points_between_segments); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped_2d", "point", "s1", "s2"), &_Geometry::get_closest_point_to_segment_uncapped_2d); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &_Geometry::get_closest_point_to_segment_uncapped); + ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &_Geometry2D::get_closest_point_to_segment); - ClassDB::bind_method(D_METHOD("get_uv84_normal_bit", "normal"), &_Geometry::get_uv84_normal_bit); + ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &_Geometry2D::get_closest_point_to_segment_uncapped); - ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &_Geometry::ray_intersects_triangle); - ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &_Geometry::segment_intersects_triangle); - ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &_Geometry::segment_intersects_sphere); - ClassDB::bind_method(D_METHOD("segment_intersects_cylinder", "from", "to", "height", "radius"), &_Geometry::segment_intersects_cylinder); - ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &_Geometry::segment_intersects_convex); - ClassDB::bind_method(D_METHOD("point_is_inside_triangle", "point", "a", "b", "c"), &_Geometry::point_is_inside_triangle); + ClassDB::bind_method(D_METHOD("point_is_inside_triangle", "point", "a", "b", "c"), &_Geometry2D::point_is_inside_triangle); - ClassDB::bind_method(D_METHOD("is_polygon_clockwise", "polygon"), &_Geometry::is_polygon_clockwise); - ClassDB::bind_method(D_METHOD("is_point_in_polygon", "point", "polygon"), &_Geometry::is_point_in_polygon); - ClassDB::bind_method(D_METHOD("triangulate_polygon", "polygon"), &_Geometry::triangulate_polygon); - ClassDB::bind_method(D_METHOD("triangulate_delaunay_2d", "points"), &_Geometry::triangulate_delaunay_2d); - ClassDB::bind_method(D_METHOD("convex_hull_2d", "points"), &_Geometry::convex_hull_2d); - ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &_Geometry::clip_polygon); + ClassDB::bind_method(D_METHOD("is_polygon_clockwise", "polygon"), &_Geometry2D::is_polygon_clockwise); + ClassDB::bind_method(D_METHOD("is_point_in_polygon", "point", "polygon"), &_Geometry2D::is_point_in_polygon); + ClassDB::bind_method(D_METHOD("triangulate_polygon", "polygon"), &_Geometry2D::triangulate_polygon); + ClassDB::bind_method(D_METHOD("triangulate_delaunay", "points"), &_Geometry2D::triangulate_delaunay); + ClassDB::bind_method(D_METHOD("convex_hull", "points"), &_Geometry2D::convex_hull); - ClassDB::bind_method(D_METHOD("merge_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::merge_polygons_2d); - ClassDB::bind_method(D_METHOD("clip_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::clip_polygons_2d); - ClassDB::bind_method(D_METHOD("intersect_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::intersect_polygons_2d); - ClassDB::bind_method(D_METHOD("exclude_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::exclude_polygons_2d); + ClassDB::bind_method(D_METHOD("merge_polygons", "polygon_a", "polygon_b"), &_Geometry2D::merge_polygons); + ClassDB::bind_method(D_METHOD("clip_polygons", "polygon_a", "polygon_b"), &_Geometry2D::clip_polygons); + ClassDB::bind_method(D_METHOD("intersect_polygons", "polygon_a", "polygon_b"), &_Geometry2D::intersect_polygons); + ClassDB::bind_method(D_METHOD("exclude_polygons", "polygon_a", "polygon_b"), &_Geometry2D::exclude_polygons); - ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon_2d", "polyline", "polygon"), &_Geometry::clip_polyline_with_polygon_2d); - ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon_2d", "polyline", "polygon"), &_Geometry::intersect_polyline_with_polygon_2d); + ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon", "polyline", "polygon"), &_Geometry2D::clip_polyline_with_polygon); + ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon", "polyline", "polygon"), &_Geometry2D::intersect_polyline_with_polygon); - ClassDB::bind_method(D_METHOD("offset_polygon_2d", "polygon", "delta", "join_type"), &_Geometry::offset_polygon_2d, DEFVAL(JOIN_SQUARE)); - ClassDB::bind_method(D_METHOD("offset_polyline_2d", "polyline", "delta", "join_type", "end_type"), &_Geometry::offset_polyline_2d, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE)); + ClassDB::bind_method(D_METHOD("offset_polygon", "polygon", "delta", "join_type"), &_Geometry2D::offset_polygon, DEFVAL(JOIN_SQUARE)); + ClassDB::bind_method(D_METHOD("offset_polyline", "polyline", "delta", "join_type", "end_type"), &_Geometry2D::offset_polyline, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE)); - ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry::make_atlas); + ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry2D::make_atlas); BIND_ENUM_CONSTANT(OPERATION_UNION); BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE); @@ -1179,6 +1068,133 @@ void _Geometry::_bind_methods() { BIND_ENUM_CONSTANT(END_ROUND); } +////// _Geometry3D ////// + +_Geometry3D *_Geometry3D::singleton = nullptr; + +_Geometry3D *_Geometry3D::get_singleton() { + return singleton; +} + +Vector<Plane> _Geometry3D::build_box_planes(const Vector3 &p_extents) { + return Geometry3D::build_box_planes(p_extents); +} + +Vector<Plane> _Geometry3D::build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis) { + return Geometry3D::build_cylinder_planes(p_radius, p_height, p_sides, p_axis); +} + +Vector<Plane> _Geometry3D::build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { + return Geometry3D::build_capsule_planes(p_radius, p_height, p_sides, p_lats, p_axis); +} + +Vector<Vector3> _Geometry3D::get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2) { + Vector3 r1, r2; + Geometry3D::get_closest_points_between_segments(p1, p2, q1, q2, r1, r2); + Vector<Vector3> r; + r.resize(2); + r.set(0, r1); + r.set(1, r2); + return r; +} + +Vector3 _Geometry3D::get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) { + Vector3 s[2] = { p_a, p_b }; + return Geometry3D::get_closest_point_to_segment(p_point, s); +} + +Vector3 _Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) { + Vector3 s[2] = { p_a, p_b }; + return Geometry3D::get_closest_point_to_segment_uncapped(p_point, s); +} + +Variant _Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { + Vector3 res; + if (Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) { + return res; + } else { + return Variant(); + } +} + +Variant _Geometry3D::segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { + Vector3 res; + if (Geometry3D::segment_intersects_triangle(p_from, p_to, p_v0, p_v1, p_v2, &res)) { + return res; + } else { + return Variant(); + } +} + +Vector<Vector3> _Geometry3D::segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius) { + Vector<Vector3> r; + Vector3 res, norm; + if (!Geometry3D::segment_intersects_sphere(p_from, p_to, p_sphere_pos, p_sphere_radius, &res, &norm)) { + return r; + } + + r.resize(2); + r.set(0, res); + r.set(1, norm); + return r; +} + +Vector<Vector3> _Geometry3D::segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius) { + Vector<Vector3> r; + Vector3 res, norm; + if (!Geometry3D::segment_intersects_cylinder(p_from, p_to, p_height, p_radius, &res, &norm)) { + return r; + } + + r.resize(2); + r.set(0, res); + r.set(1, norm); + return r; +} + +Vector<Vector3> _Geometry3D::segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes) { + Vector<Vector3> r; + Vector3 res, norm; + if (!Geometry3D::segment_intersects_convex(p_from, p_to, p_planes.ptr(), p_planes.size(), &res, &norm)) { + return r; + } + + r.resize(2); + r.set(0, res); + r.set(1, norm); + return r; +} + +Vector<Vector3> _Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane) { + return Geometry3D::clip_polygon(p_points, p_plane); +} + +int _Geometry3D::get_uv84_normal_bit(const Vector3 &p_vector) { + return Geometry3D::get_uv84_normal_bit(p_vector); +} + +void _Geometry3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &_Geometry3D::build_box_planes); + ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &_Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); + ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &_Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); + + ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "p2", "q1", "q2"), &_Geometry3D::get_closest_points_between_segments); + + ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &_Geometry3D::get_closest_point_to_segment); + + ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &_Geometry3D::get_closest_point_to_segment_uncapped); + + ClassDB::bind_method(D_METHOD("get_uv84_normal_bit", "normal"), &_Geometry3D::get_uv84_normal_bit); + + ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &_Geometry3D::ray_intersects_triangle); + ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &_Geometry3D::segment_intersects_triangle); + ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &_Geometry3D::segment_intersects_sphere); + ClassDB::bind_method(D_METHOD("segment_intersects_cylinder", "from", "to", "height", "radius"), &_Geometry3D::segment_intersects_cylinder); + ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &_Geometry3D::segment_intersects_convex); + + ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &_Geometry3D::clip_polygon); +} + ////// _File ////// Error _File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index e5bd70262d..26d0f7b8af 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -258,44 +258,31 @@ VARIANT_ENUM_CAST(_OS::Weekday); VARIANT_ENUM_CAST(_OS::Month); VARIANT_ENUM_CAST(_OS::SystemDir); -class _Geometry : public Object { - GDCLASS(_Geometry, Object); +class _Geometry2D : public Object { + GDCLASS(_Geometry2D, Object); - static _Geometry *singleton; + static _Geometry2D *singleton; protected: static void _bind_methods(); public: - static _Geometry *get_singleton(); - Vector<Plane> build_box_planes(const Vector3 &p_extents); - Vector<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); - Vector<Plane> 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); - Vector<Vector2> get_closest_points_between_segments_2d(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2); - Vector<Vector3> 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); - Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); - Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b); - Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); - Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); - Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); + static _Geometry2D *get_singleton(); + Variant segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b); + Variant line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b); + Vector<Vector2> get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2); + Vector2 get_closest_point_to_segment(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b); + Vector2 get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b); bool point_is_inside_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) const; - Vector<Vector3> segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius); - Vector<Vector3> segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius); - Vector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes); bool is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius); real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius); - int get_uv84_normal_bit(const Vector3 &p_vector); bool is_polygon_clockwise(const Vector<Vector2> &p_polygon); bool is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon); Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon); - Vector<int> triangulate_delaunay_2d(const Vector<Vector2> &p_points); - Vector<Point2> convex_hull_2d(const Vector<Point2> &p_points); - Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane); + Vector<int> triangulate_delaunay(const Vector<Vector2> &p_points); + Vector<Point2> convex_hull(const Vector<Point2> &p_points); enum PolyBooleanOperation { OPERATION_UNION, @@ -304,14 +291,14 @@ public: OPERATION_XOR }; // 2D polygon boolean operations. - Array merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Union (add). - Array clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Difference (subtract). - Array intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Common area (multiply). - Array exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // All but common area (xor). + Array merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Union (add). + Array clip_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Difference (subtract). + Array intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Common area (multiply). + Array exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // All but common area (xor). // 2D polyline vs polygon operations. - Array clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Cut. - Array intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Chop. + Array clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Cut. + Array intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Chop. // 2D offset polygons/polylines. enum PolyJoinType { @@ -326,17 +313,46 @@ public: END_SQUARE, END_ROUND }; - Array offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE); - Array offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE); + Array offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE); + Array offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE); Dictionary make_atlas(const Vector<Size2> &p_rects); - _Geometry() { singleton = this; } + _Geometry2D() { singleton = this; } }; -VARIANT_ENUM_CAST(_Geometry::PolyBooleanOperation); -VARIANT_ENUM_CAST(_Geometry::PolyJoinType); -VARIANT_ENUM_CAST(_Geometry::PolyEndType); +VARIANT_ENUM_CAST(_Geometry2D::PolyBooleanOperation); +VARIANT_ENUM_CAST(_Geometry2D::PolyJoinType); +VARIANT_ENUM_CAST(_Geometry2D::PolyEndType); + +class _Geometry3D : public Object { + GDCLASS(_Geometry3D, Object); + + static _Geometry3D *singleton; + +protected: + static void _bind_methods(); + +public: + static _Geometry3D *get_singleton(); + Vector<Plane> build_box_planes(const Vector3 &p_extents); + Vector<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); + Vector<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); + Vector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2); + Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); + Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); + Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); + Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); + + Vector<Vector3> segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius); + Vector<Vector3> segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius); + Vector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes); + int get_uv84_normal_bit(const Vector3 &p_vector); + + Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane); + + _Geometry3D() { singleton = this; } +}; class _File : public Reference { GDCLASS(_File, Reference); diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp index eb942c60c9..a9a7cabee9 100644 --- a/core/crypto/crypto.cpp +++ b/core/crypto/crypto.cpp @@ -70,7 +70,7 @@ Crypto *Crypto::create() { if (_create) { return _create(); } - return memnew(Crypto); + ERR_FAIL_V_MSG(nullptr, "Crypto is not available when the mbedtls module is disabled."); } void Crypto::load_default_certificates(String p_path) { @@ -85,18 +85,6 @@ void Crypto::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_self_signed_certificate", "key", "issuer_name", "not_before", "not_after"), &Crypto::generate_self_signed_certificate, DEFVAL("CN=myserver,O=myorganisation,C=IT"), DEFVAL("20140101000000"), DEFVAL("20340101000000")); } -PackedByteArray Crypto::generate_random_bytes(int p_bytes) { - ERR_FAIL_V_MSG(PackedByteArray(), "generate_random_bytes is not available when mbedtls module is disabled."); -} - -Ref<CryptoKey> Crypto::generate_rsa(int p_bytes) { - ERR_FAIL_V_MSG(nullptr, "generate_rsa is not available when mbedtls module is disabled."); -} - -Ref<X509Certificate> Crypto::generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) { - ERR_FAIL_V_MSG(nullptr, "generate_self_signed_certificate is not available when mbedtls module is disabled."); -} - /// Resource loader/saver RES ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h index cf21648a4a..6cc5f46164 100644 --- a/core/crypto/crypto.h +++ b/core/crypto/crypto.h @@ -75,9 +75,9 @@ public: static Crypto *create(); static void load_default_certificates(String p_path); - virtual PackedByteArray generate_random_bytes(int p_bytes); - virtual Ref<CryptoKey> generate_rsa(int p_bytes); - virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after); + virtual PackedByteArray generate_random_bytes(int p_bytes) = 0; + virtual Ref<CryptoKey> generate_rsa(int p_bytes) = 0; + virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) = 0; Crypto() {} }; diff --git a/core/hash_map.h b/core/hash_map.h index 78592f8d82..843430d082 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -524,28 +524,14 @@ public: copy_from(p_table); } - void get_key_value_ptr_array(const Pair **p_pairs) const { + void get_key_list(List<TKey> *r_keys) const { if (unlikely(!hash_table)) { return; } for (int i = 0; i < (1 << hash_table_power); i++) { Element *e = hash_table[i]; while (e) { - *p_pairs = &e->pair; - p_pairs++; - e = e->next; - } - } - } - - void get_key_list(List<TKey> *p_keys) const { - if (unlikely(!hash_table)) { - return; - } - for (int i = 0; i < (1 << hash_table_power); i++) { - Element *e = hash_table[i]; - while (e) { - p_keys->push_back(e->pair.key); + r_keys->push_back(e->pair.key); e = e->next; } } diff --git a/core/image.cpp b/core/image.cpp index 4ab71128cd..0f15574053 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -2539,12 +2539,11 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P int dst_y = dest_rect.position.y + i; Color sc = img->get_pixel(src_x, src_y); - Color dc = get_pixel(dst_x, dst_y); - dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r); - dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g); - dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b); - dc.a = (double)(sc.a + dc.a * (1.0 - sc.a)); - set_pixel(dst_x, dst_y, dc); + if (sc.a != 0) { + Color dc = get_pixel(dst_x, dst_y); + dc = dc.blend(sc); + set_pixel(dst_x, dst_y, dc); + } } } } @@ -2594,12 +2593,11 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c int dst_y = dest_rect.position.y + i; Color sc = img->get_pixel(src_x, src_y); - Color dc = get_pixel(dst_x, dst_y); - dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r); - dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g); - dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b); - dc.a = (double)(sc.a + dc.a * (1.0 - sc.a)); - set_pixel(dst_x, dst_y, dc); + if (sc.a != 0) { + Color dc = get_pixel(dst_x, dst_y); + dc = dc.blend(sc); + set_pixel(dst_x, dst_y, dc); + } } } } diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index 90d309c1c8..7b5abdd61b 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -4,37 +4,38 @@ # Windows 03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, 03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, -03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00015900000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00065280000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00006228000000000000,8BitDo SN30 GP,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000031000000000000,8BitDo Wireless Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, @@ -73,6 +74,7 @@ 03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows, 03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000007d0400000840000000000000,Destroyer Tiltpad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows, 03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, 030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -88,7 +90,6 @@ 030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, 030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, -03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, 03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, @@ -98,6 +99,7 @@ 03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006f0e00000102000000007801,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000009b2800003200000000000000,GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, @@ -146,6 +148,8 @@ 030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows, +03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, 03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -302,6 +306,7 @@ 03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00000302000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:+a3,righty:+a4,start:b4,x:b2,y:b3,platform:Windows, 030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, @@ -310,7 +315,7 @@ 03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +030000009b2800006000000000000000,GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, # Mac OS X 030000008f0e00000300000009010000,2In1 USB Joystick,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, @@ -330,7 +335,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, @@ -351,6 +355,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, 030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000242f00002d00000007010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -406,6 +411,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X, 030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, 03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, @@ -455,12 +461,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, -05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 030000005e0400008e02000020010000,8BitDo Wireless Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000031000011010000,8BitDo Wireless Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -499,6 +506,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, 03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000632500002605000010010000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, 030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f0000c100000011010000,HORI CO. LTD. HORIPAD S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -529,6 +537,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, +03000000242f00002d00000011010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, 030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -581,6 +591,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux, +060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500000920000001800000,Nintendo Switch Pro Controller (joycond),a:b0,b:b1,x:b3,y:b2,back:b9,guide:b11,start:b10,leftstick:b12,rightstick:b13,leftshoulder:b5,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b8,platform:Linux, +030000007e0500000920000011810000,Nintendo Switch Pro Controller Wired (joycond),a:b0,b:b1,x:b3,y:b2,back:b9,guide:b11,start:b10,leftstick:b12,rightstick:b13,leftshoulder:b5,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b8,platform:Linux, 030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux, 050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, @@ -593,9 +606,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, 05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +03000000830500005020000010010000,Padix Co. Ltd. Rockfire PSX/USB Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux, 03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -629,6 +644,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, +030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, 030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -650,9 +666,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000a30600001005000000010000,Saitek Saitek P150,platform:Linux,a:b0,b:b1,y:b4,x:b3,leftshoulder:b7,rightshoulder:b2,dpup:-a1,dpleft:-a0,dpdown:+a1,dpright:+a0,lefttrigger:b6,righttrigger:b5, 03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, 03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux, -03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, 03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux, 03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, @@ -667,6 +684,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, @@ -679,16 +697,21 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,x:b1,y:b3,back:b7,start:b8,leftshoulder:b6,rightshoulder:b9,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b5,platform:Linux, 03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, +03000000457500002211000010010000,SZMY-POWER CO. LTD. GAMEPAD,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000008f0e00000d31000010010000,SZMY-POWER CO. LTD. GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,x:b3,y:b5,back:b11,start:b1,leftstick:b10,rightstick:b0,leftshoulder:b6,rightshoulder:b8,leftx:a0,lefty:a1,lefttrigger:b7,righttrigger:b9,platform:Linux, 030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux, 030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000007d0000000010000,Thrustmaster T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, @@ -721,8 +744,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux, 03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux, +030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, # Android 05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, @@ -748,6 +772,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android, +7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Android, # iOS 05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp index 0278027c50..e43b1f5385 100644 --- a/core/io/dtls_server.cpp +++ b/core/io/dtls_server.cpp @@ -37,7 +37,10 @@ DTLSServer *(*DTLSServer::_create)() = nullptr; bool DTLSServer::available = false; DTLSServer *DTLSServer::create() { - return _create(); + if (_create) { + return _create(); + } + return nullptr; } bool DTLSServer::is_available() { diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp index 67579c339a..632f86a9f6 100644 --- a/core/io/packet_peer_dtls.cpp +++ b/core/io/packet_peer_dtls.cpp @@ -36,7 +36,10 @@ PacketPeerDTLS *(*PacketPeerDTLS::_create)() = nullptr; bool PacketPeerDTLS::available = false; PacketPeerDTLS *PacketPeerDTLS::create() { - return _create(); + if (_create) { + return _create(); + } + return nullptr; } bool PacketPeerDTLS::is_available() { diff --git a/core/local_vector.h b/core/local_vector.h index b09a28b25a..7f96b25f8b 100644 --- a/core/local_vector.h +++ b/core/local_vector.h @@ -75,7 +75,7 @@ public: } void erase(const T &p_val) { - U idx = find(p_val); + int64_t idx = find(p_val); if (idx >= 0) { remove(idx); } diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 45c4a207c3..580a7cf7bb 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -30,7 +30,7 @@ #include "a_star.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #include "core/script_language.h" #include "scene/scene_string_names.h" @@ -309,7 +309,7 @@ Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const { to_point->pos, }; - Vector3 p = Geometry::get_closest_point_to_segment(p_point, segment); + Vector3 p = Geometry3D::get_closest_point_to_segment(p_point, segment); real_t d = p_point.distance_squared_to(p); if (!found || d < closest_dist) { closest_point = p; diff --git a/core/math/face3.cpp b/core/math/face3.cpp index 6d76e116be..db2bfaa58b 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -30,7 +30,7 @@ #include "face3.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_over[3]) const { ERR_FAIL_COND_V(is_degenerate(), 0); @@ -108,11 +108,11 @@ int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_ } bool Face3::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection) const { - return Geometry::ray_intersects_triangle(p_from, p_dir, vertex[0], vertex[1], vertex[2], p_intersection); + return Geometry3D::ray_intersects_triangle(p_from, p_dir, vertex[0], vertex[1], vertex[2], p_intersection); } bool Face3::intersects_segment(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection) const { - return Geometry::segment_intersects_triangle(p_from, p_dir, vertex[0], vertex[1], vertex[2], p_intersection); + return Geometry3D::segment_intersects_triangle(p_from, p_dir, vertex[0], vertex[1], vertex[2], p_intersection); } bool Face3::is_degenerate() const { diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp new file mode 100644 index 0000000000..7d8fde8bcc --- /dev/null +++ b/core/math/geometry_2d.cpp @@ -0,0 +1,384 @@ +/*************************************************************************/ +/* geometry.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 "geometry_2d.h" + +#include "thirdparty/misc/clipper.hpp" +#include "thirdparty/misc/triangulator.h" +#define STB_RECT_PACK_IMPLEMENTATION +#include "thirdparty/misc/stb_rect_pack.h" + +#define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON. + +Vector<Vector<Vector2>> Geometry2D::decompose_polygon_in_convex(Vector<Point2> polygon) { + Vector<Vector<Vector2>> decomp; + List<TriangulatorPoly> in_poly, out_poly; + + TriangulatorPoly inp; + inp.Init(polygon.size()); + for (int i = 0; i < polygon.size(); i++) { + inp.GetPoint(i) = polygon[i]; + } + inp.SetOrientation(TRIANGULATOR_CCW); + in_poly.push_back(inp); + TriangulatorPartition tpart; + if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { // Failed. + ERR_PRINT("Convex decomposing failed!"); + return decomp; + } + + decomp.resize(out_poly.size()); + int idx = 0; + for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) { + TriangulatorPoly &tp = I->get(); + + decomp.write[idx].resize(tp.GetNumPoints()); + + for (int64_t i = 0; i < tp.GetNumPoints(); i++) { + decomp.write[idx].write[i] = tp.GetPoint(i); + } + + idx++; + } + + return decomp; +} + +struct _AtlasWorkRect { + Size2i s; + Point2i p; + int idx; + _FORCE_INLINE_ bool operator<(const _AtlasWorkRect &p_r) const { return s.width > p_r.s.width; }; +}; + +struct _AtlasWorkRectResult { + Vector<_AtlasWorkRect> result; + int max_w; + int max_h; +}; + +void Geometry2D::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size) { + // Super simple, almost brute force scanline stacking fitter. + // It's pretty basic for now, but it tries to make sure that the aspect ratio of the + // resulting atlas is somehow square. This is necessary because video cards have limits. + // On texture size (usually 2048 or 4096), so the more square a texture, the more chances. + // It will work in every hardware. + // For example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a + // 256x8192 atlas (won't work anywhere). + + ERR_FAIL_COND(p_rects.size() == 0); + + Vector<_AtlasWorkRect> wrects; + wrects.resize(p_rects.size()); + for (int i = 0; i < p_rects.size(); i++) { + wrects.write[i].s = p_rects[i]; + wrects.write[i].idx = i; + } + wrects.sort(); + int widest = wrects[0].s.width; + + Vector<_AtlasWorkRectResult> results; + + for (int i = 0; i <= 12; i++) { + int w = 1 << i; + int max_h = 0; + int max_w = 0; + if (w < widest) { + continue; + } + + Vector<int> hmax; + hmax.resize(w); + for (int j = 0; j < w; j++) { + hmax.write[j] = 0; + } + + // Place them. + int ofs = 0; + int limit_h = 0; + for (int j = 0; j < wrects.size(); j++) { + if (ofs + wrects[j].s.width > w) { + ofs = 0; + } + + int from_y = 0; + for (int k = 0; k < wrects[j].s.width; k++) { + if (hmax[ofs + k] > from_y) { + from_y = hmax[ofs + k]; + } + } + + 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) { + limit_h = end_h; + } + + for (int k = 0; k < wrects[j].s.width; k++) { + hmax.write[ofs + k] = end_h; + } + + if (end_h > max_h) { + max_h = end_h; + } + + if (end_w > max_w) { + max_w = end_w; + } + + if (ofs == 0 || end_h > limit_h) { // While h limit not reached, keep stacking. + ofs += wrects[j].s.width; + } + } + + _AtlasWorkRectResult result; + result.result = wrects; + result.max_h = max_h; + result.max_w = max_w; + results.push_back(result); + } + + // Find the result with the best aspect ratio. + + int best = -1; + real_t best_aspect = 1e20; + + for (int i = 0; i < results.size(); i++) { + real_t h = next_power_of_2(results[i].max_h); + real_t w = next_power_of_2(results[i].max_w); + real_t aspect = h > w ? h / w : w / h; + if (aspect < best_aspect) { + best = i; + best_aspect = aspect; + } + } + + r_result.resize(p_rects.size()); + + for (int i = 0; i < p_rects.size(); i++) { + r_result.write[results[best].result[i].idx] = results[best].result[i].p; + } + + r_size = Size2(results[best].max_w, results[best].max_h); +} + +Vector<Vector<Point2>> Geometry2D::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open) { + using namespace ClipperLib; + + ClipType op = ctUnion; + + switch (p_op) { + case OPERATION_UNION: + op = ctUnion; + break; + case OPERATION_DIFFERENCE: + op = ctDifference; + break; + case OPERATION_INTERSECTION: + op = ctIntersection; + break; + case OPERATION_XOR: + op = ctXor; + break; + } + Path path_a, path_b; + + // Need to scale points (Clipper's requirement for robust computation). + for (int i = 0; i != p_polypath_a.size(); ++i) { + path_a << IntPoint(p_polypath_a[i].x * SCALE_FACTOR, p_polypath_a[i].y * SCALE_FACTOR); + } + for (int i = 0; i != p_polypath_b.size(); ++i) { + path_b << IntPoint(p_polypath_b[i].x * SCALE_FACTOR, p_polypath_b[i].y * SCALE_FACTOR); + } + Clipper clp; + clp.AddPath(path_a, ptSubject, !is_a_open); // Forward compatible with Clipper 10.0.0. + clp.AddPath(path_b, ptClip, true); // Polylines cannot be set as clip. + + Paths paths; + + if (is_a_open) { + PolyTree tree; // Needed to populate polylines. + clp.Execute(op, tree); + OpenPathsFromPolyTree(tree, paths); + } else { + clp.Execute(op, paths); // Works on closed polygons only. + } + // Have to scale points down now. + Vector<Vector<Point2>> polypaths; + + for (Paths::size_type i = 0; i < paths.size(); ++i) { + Vector<Vector2> polypath; + + const Path &scaled_path = paths[i]; + + for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { + polypath.push_back(Point2( + static_cast<real_t>(scaled_path[j].X) / SCALE_FACTOR, + static_cast<real_t>(scaled_path[j].Y) / SCALE_FACTOR)); + } + polypaths.push_back(polypath); + } + return polypaths; +} + +Vector<Vector<Point2>> Geometry2D::_polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + using namespace ClipperLib; + + JoinType jt = jtSquare; + + switch (p_join_type) { + case JOIN_SQUARE: + jt = jtSquare; + break; + case JOIN_ROUND: + jt = jtRound; + break; + case JOIN_MITER: + jt = jtMiter; + break; + } + + EndType et = etClosedPolygon; + + switch (p_end_type) { + case END_POLYGON: + et = etClosedPolygon; + break; + case END_JOINED: + et = etClosedLine; + break; + case END_BUTT: + et = etOpenButt; + break; + case END_SQUARE: + et = etOpenSquare; + break; + case END_ROUND: + et = etOpenRound; + break; + } + ClipperOffset co(2.0, 0.25 * SCALE_FACTOR); // Defaults from ClipperOffset. + Path path; + + // Need to scale points (Clipper's requirement for robust computation). + for (int i = 0; i != p_polypath.size(); ++i) { + path << IntPoint(p_polypath[i].x * SCALE_FACTOR, p_polypath[i].y * SCALE_FACTOR); + } + co.AddPath(path, jt, et); + + Paths paths; + co.Execute(paths, p_delta * SCALE_FACTOR); // Inflate/deflate. + + // Have to scale points down now. + Vector<Vector<Point2>> polypaths; + + for (Paths::size_type i = 0; i < paths.size(); ++i) { + Vector<Vector2> polypath; + + const Path &scaled_path = paths[i]; + + for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { + polypath.push_back(Point2( + static_cast<real_t>(scaled_path[j].X) / SCALE_FACTOR, + static_cast<real_t>(scaled_path[j].Y) / SCALE_FACTOR)); + } + polypaths.push_back(polypath); + } + return polypaths; +} + +Vector<Point2i> Geometry2D::pack_rects(const Vector<Size2i> &p_sizes, const Size2i &p_atlas_size) { + Vector<stbrp_node> nodes; + nodes.resize(p_atlas_size.width); + + stbrp_context context; + stbrp_init_target(&context, p_atlas_size.width, p_atlas_size.height, nodes.ptrw(), p_atlas_size.width); + + Vector<stbrp_rect> rects; + rects.resize(p_sizes.size()); + + for (int i = 0; i < p_sizes.size(); i++) { + rects.write[i].id = 0; + rects.write[i].w = p_sizes[i].width; + rects.write[i].h = p_sizes[i].height; + rects.write[i].x = 0; + rects.write[i].y = 0; + rects.write[i].was_packed = 0; + } + + int res = stbrp_pack_rects(&context, rects.ptrw(), rects.size()); + if (res == 0) { //pack failed + return Vector<Point2i>(); + } + + Vector<Point2i> ret; + ret.resize(p_sizes.size()); + + for (int i = 0; i < p_sizes.size(); i++) { + Point2i r(rects[i].x, rects[i].y); + ret.write[i] = r; + } + + return ret; +} + +Vector<Vector3i> Geometry2D::partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size) { + Vector<stbrp_node> nodes; + nodes.resize(p_atlas_size.width); + zeromem(nodes.ptrw(), sizeof(stbrp_node) * nodes.size()); + + stbrp_context context; + stbrp_init_target(&context, p_atlas_size.width, p_atlas_size.height, nodes.ptrw(), p_atlas_size.width); + + Vector<stbrp_rect> rects; + rects.resize(p_sizes.size()); + + for (int i = 0; i < p_sizes.size(); i++) { + rects.write[i].id = i; + rects.write[i].w = p_sizes[i].width; + rects.write[i].h = p_sizes[i].height; + rects.write[i].x = 0; + rects.write[i].y = 0; + rects.write[i].was_packed = 0; + } + + stbrp_pack_rects(&context, rects.ptrw(), rects.size()); + + Vector<Vector3i> ret; + ret.resize(p_sizes.size()); + + for (int i = 0; i < p_sizes.size(); i++) { + ret.write[rects[i].id] = Vector3i(rects[i].x, rects[i].y, rects[i].was_packed != 0 ? 1 : 0); + } + + return ret; +} diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h new file mode 100644 index 0000000000..cfd7abfacb --- /dev/null +++ b/core/math/geometry_2d.h @@ -0,0 +1,398 @@ +/*************************************************************************/ +/* geometry_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 GEOMETRY_2D_H +#define GEOMETRY_2D_H + +#include "core/math/delaunay_2d.h" +#include "core/math/rect2.h" +#include "core/math/triangulate.h" +#include "core/object.h" +#include "core/vector.h" + +class Geometry2D { + Geometry2D(); + +public: + static real_t get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2, Vector2 &c1, Vector2 &c2) { + Vector2 d1 = q1 - p1; // Direction vector of segment S1. + Vector2 d2 = q2 - p2; // Direction vector of segment S2. + Vector2 r = p1 - p2; + real_t a = d1.dot(d1); // Squared length of segment S1, always nonnegative. + real_t e = d2.dot(d2); // Squared length of segment S2, always nonnegative. + real_t f = d2.dot(r); + real_t s, t; + // Check if either or both segments degenerate into points. + if (a <= CMP_EPSILON && e <= CMP_EPSILON) { + // Both segments degenerate into points. + c1 = p1; + c2 = p2; + return Math::sqrt((c1 - c2).dot(c1 - c2)); + } + if (a <= CMP_EPSILON) { + // First segment degenerates into a point. + s = 0.0; + t = f / e; // s = 0 => t = (b*s + f) / e = f / e + t = CLAMP(t, 0.0, 1.0); + } else { + real_t c = d1.dot(r); + if (e <= CMP_EPSILON) { + // Second segment degenerates into a point. + t = 0.0; + s = CLAMP(-c / a, 0.0, 1.0); // t = 0 => s = (b*t - c) / a = -c / a + } else { + // The general nondegenerate case starts here. + real_t b = d1.dot(d2); + real_t denom = a * e - b * b; // Always nonnegative. + // If segments not parallel, compute closest point on L1 to L2 and + // clamp to segment S1. Else pick arbitrary s (here 0). + if (denom != 0.0) { + s = CLAMP((b * f - c * e) / denom, 0.0, 1.0); + } else { + s = 0.0; + } + // Compute point on L2 closest to S1(s) using + // t = Dot((P1 + D1*s) - P2,D2) / Dot(D2,D2) = (b*s + f) / e + t = (b * s + f) / e; + + //If t in [0,1] done. Else clamp t, recompute s for the new value + // of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a + // and clamp s to [0, 1]. + if (t < 0.0) { + t = 0.0; + s = CLAMP(-c / a, 0.0, 1.0); + } else if (t > 1.0) { + t = 1.0; + s = CLAMP((b - c) / a, 0.0, 1.0); + } + } + } + c1 = p1 + d1 * s; + c2 = p2 + d2 * t; + return Math::sqrt((c1 - c2).dot(c1 - c2)); + } + + static Vector2 get_closest_point_to_segment(const Vector2 &p_point, const Vector2 *p_segment) { + Vector2 p = p_point - p_segment[0]; + Vector2 n = p_segment[1] - p_segment[0]; + real_t l2 = n.length_squared(); + if (l2 < 1e-20) { + return p_segment[0]; // Both points are the same, just give any. + } + + real_t d = n.dot(p) / l2; + + if (d <= 0.0) { + return p_segment[0]; // Before first point. + } else if (d >= 1.0) { + return p_segment[1]; // After first point. + } else { + return p_segment[0] + n * d; // Inside. + } + } + + static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) { + Vector2 an = a - s; + Vector2 bn = b - s; + Vector2 cn = c - s; + + bool orientation = an.cross(bn) > 0; + + if ((bn.cross(cn) > 0) != orientation) { + return false; + } + + return (cn.cross(an) > 0) == orientation; + } + + static Vector2 get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 *p_segment) { + Vector2 p = p_point - p_segment[0]; + Vector2 n = p_segment[1] - p_segment[0]; + real_t l2 = n.length_squared(); + if (l2 < 1e-20) { + return p_segment[0]; // Both points are the same, just give any. + } + + real_t d = n.dot(p) / l2; + + return p_segment[0] + n * d; // Inside. + } + + static bool line_intersects_line(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::is_zero_approx(denom)) { // 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(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; + Vector2 C = p_from_b - p_from_a; + Vector2 D = p_to_b - p_from_a; + + real_t ABlen = B.dot(B); + if (ABlen <= 0) { + return false; + } + Vector2 Bn = B / ABlen; + C = Vector2(C.x * Bn.x + C.y * Bn.y, C.y * Bn.x - C.x * Bn.y); + D = Vector2(D.x * Bn.x + D.y * Bn.y, D.y * Bn.x - D.x * Bn.y); + + if ((C.y < 0 && D.y < 0) || (C.y >= 0 && D.y >= 0)) { + return false; + } + + real_t ABpos = D.x + (C.x - D.x) * D.y / (D.y - C.y); + + // Fail if segment C-D crosses line A-B outside of segment A-B. + if (ABpos < 0 || ABpos > 1.0) { + return false; + } + + // (4) Apply the discovered position to line A-B in the original coordinate system. + if (r_result) { + *r_result = p_from_a + B * ABpos; + } + + return true; + } + + static inline bool is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { + return p_point.distance_squared_to(p_circle_pos) <= p_circle_radius * p_circle_radius; + } + + static real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { + Vector2 line_vec = p_to - p_from; + Vector2 vec_to_line = p_from - p_circle_pos; + + // Create a quadratic formula of the form ax^2 + bx + c = 0 + real_t a, b, c; + + a = line_vec.dot(line_vec); + b = 2 * vec_to_line.dot(line_vec); + c = vec_to_line.dot(vec_to_line) - p_circle_radius * p_circle_radius; + + // Solve for t. + real_t sqrtterm = b * b - 4 * a * c; + + // If the term we intend to square root is less than 0 then the answer won't be real, + // so it definitely won't be t in the range 0 to 1. + if (sqrtterm < 0) { + return -1; + } + + // If we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) + // then the following can be skipped and we can just return the equivalent of res1. + sqrtterm = Math::sqrt(sqrtterm); + real_t res1 = (-b - sqrtterm) / (2 * a); + real_t res2 = (-b + sqrtterm) / (2 * a); + + if (res1 >= 0 && res1 <= 1) { + return res1; + } + if (res2 >= 0 && res2 <= 1) { + return res2; + } + return -1; + } + + enum PolyBooleanOperation { + OPERATION_UNION, + OPERATION_DIFFERENCE, + OPERATION_INTERSECTION, + OPERATION_XOR + }; + enum PolyJoinType { + JOIN_SQUARE, + JOIN_ROUND, + JOIN_MITER + }; + enum PolyEndType { + END_POLYGON, + END_JOINED, + END_BUTT, + END_SQUARE, + END_ROUND + }; + + static Vector<Vector<Point2>> merge_polygons(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { + return _polypaths_do_operation(OPERATION_UNION, p_polygon_a, p_polygon_b); + } + + static Vector<Vector<Point2>> clip_polygons(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { + return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polygon_a, p_polygon_b); + } + + static Vector<Vector<Point2>> intersect_polygons(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { + return _polypaths_do_operation(OPERATION_INTERSECTION, p_polygon_a, p_polygon_b); + } + + static Vector<Vector<Point2>> exclude_polygons(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { + return _polypaths_do_operation(OPERATION_XOR, p_polygon_a, p_polygon_b); + } + + static Vector<Vector<Point2>> clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polyline, p_polygon, true); + } + + static Vector<Vector<Point2>> intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + return _polypaths_do_operation(OPERATION_INTERSECTION, p_polyline, p_polygon, true); + } + + static Vector<Vector<Point2>> offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { + return _polypath_offset(p_polygon, p_delta, p_join_type, END_POLYGON); + } + + static Vector<Vector<Point2>> offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + ERR_FAIL_COND_V_MSG(p_end_type == END_POLYGON, Vector<Vector<Point2>>(), "Attempt to offset a polyline like a polygon (use offset_polygon instead)."); + + return _polypath_offset(p_polygon, p_delta, p_join_type, p_end_type); + } + + static Vector<int> triangulate_delaunay(const Vector<Vector2> &p_points) { + Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points); + Vector<int> triangles; + + for (int i = 0; i < tr.size(); i++) { + triangles.push_back(tr[i].points[0]); + triangles.push_back(tr[i].points[1]); + triangles.push_back(tr[i].points[2]); + } + return triangles; + } + + static Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon) { + Vector<int> triangles; + if (!Triangulate::triangulate(p_polygon, triangles)) { + return Vector<int>(); //fail + } + return triangles; + } + + static bool is_polygon_clockwise(const Vector<Vector2> &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; + } + + // Alternate implementation that should be faster. + static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) { + int c = p_polygon.size(); + if (c < 3) { + return false; + } + const Vector2 *p = p_polygon.ptr(); + Vector2 further_away(-1e20, -1e20); + Vector2 further_away_opposite(1e20, 1e20); + + for (int i = 0; i < c; i++) { + further_away.x = MAX(p[i].x, further_away.x); + further_away.y = MAX(p[i].y, further_away.y); + further_away_opposite.x = MIN(p[i].x, further_away_opposite.x); + further_away_opposite.y = MIN(p[i].y, further_away_opposite.y); + } + + // Make point outside that won't intersect with points in segment from p_point. + further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312); + + int intersections = 0; + for (int i = 0; i < c; i++) { + const Vector2 &v1 = p[i]; + const Vector2 &v2 = p[(i + 1) % c]; + if (segment_intersects_segment(v1, v2, p_point, further_away, nullptr)) { + intersections++; + } + } + + return (intersections & 1); + } + + static real_t vec2_cross(const Point2 &O, const Point2 &A, const Point2 &B) { + return (real_t)(A.x - O.x) * (B.y - O.y) - (real_t)(A.y - O.y) * (B.x - O.x); + } + + // Returns a list of points on the convex hull in counter-clockwise order. + // Note: the last point in the returned list is the same as the first one. + static Vector<Point2> convex_hull(Vector<Point2> P) { + int n = P.size(), k = 0; + Vector<Point2> H; + H.resize(2 * n); + + // Sort points lexicographically. + P.sort(); + + // Build lower hull. + for (int i = 0; i < n; ++i) { + while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) { + k--; + } + 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.write[k++] = P[i]; + } + + H.resize(k); + return H; + } + static Vector<Vector<Vector2>> decompose_polygon_in_convex(Vector<Point2> polygon); + + static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size); + static Vector<Point2i> pack_rects(const Vector<Size2i> &p_sizes, const Size2i &p_atlas_size); + static Vector<Vector3i> partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size); + +private: + static Vector<Vector<Point2>> _polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open = false); + static Vector<Vector<Point2>> _polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type); +}; + +#endif // GEOMETRY_2D_H diff --git a/core/math/geometry.cpp b/core/math/geometry_3d.cpp index a4e9169a6f..3b30f4b1fe 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry_3d.cpp @@ -28,32 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "geometry.h" +#include "geometry_3d.h" #include "core/print_string.h" #include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/triangulator.h" -#define STB_RECT_PACK_IMPLEMENTATION -#include "thirdparty/misc/stb_rect_pack.h" - -#define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON. - -// This implementation is very inefficient, commenting unless bugs happen. See the other one. -/* -bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) { - Vector<int> indices = Geometry::triangulate_polygon(p_polygon); - for (int j = 0; j + 3 <= indices.size(); j += 3) { - int i1 = indices[j], i2 = indices[j + 1], i3 = indices[j + 2]; - if (Geometry::is_point_in_triangle(p_point, p_polygon[i1], p_polygon[i2], p_polygon[i3])) { - return true; - } - } - return false; -} -*/ -void Geometry::MeshData::optimize_vertices() { +void Geometry3D::MeshData::optimize_vertices() { Map<int, int> vtx_remap; for (int i = 0; i < faces.size(); i++) { @@ -200,7 +182,7 @@ static bool _group_face(_FaceClassify *p_faces, int len, int p_index, int p_grou return true; } -Vector<Vector<Face3>> Geometry::separate_objects(Vector<Face3> p_array) { +Vector<Vector<Face3>> Geometry3D::separate_objects(Vector<Face3> p_array) { Vector<Vector<Face3>> objects; int len = p_array.size(); @@ -510,7 +492,7 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i } } -Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { +Vector<Face3> Geometry3D::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { #define _MIN_SIZE 1.0 #define _MAX_LENGTH 20 @@ -646,41 +628,7 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { return wrapped_faces; } -Vector<Vector<Vector2>> Geometry::decompose_polygon_in_convex(Vector<Point2> polygon) { - Vector<Vector<Vector2>> decomp; - List<TriangulatorPoly> in_poly, out_poly; - - TriangulatorPoly inp; - inp.Init(polygon.size()); - for (int i = 0; i < polygon.size(); i++) { - inp.GetPoint(i) = polygon[i]; - } - inp.SetOrientation(TRIANGULATOR_CCW); - in_poly.push_back(inp); - TriangulatorPartition tpart; - if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { // Failed. - ERR_PRINT("Convex decomposing failed!"); - return decomp; - } - - decomp.resize(out_poly.size()); - int idx = 0; - for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) { - TriangulatorPoly &tp = I->get(); - - decomp.write[idx].resize(tp.GetNumPoints()); - - for (int64_t i = 0; i < tp.GetNumPoints(); i++) { - decomp.write[idx].write[i] = tp.GetPoint(i); - } - - idx++; - } - - return decomp; -} - -Geometry::MeshData Geometry::build_convex_mesh(const Vector<Plane> &p_planes) { +Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes) { MeshData mesh; #define SUBPLANE_SIZE 1024.0 @@ -815,7 +763,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const Vector<Plane> &p_planes) { return mesh; } -Vector<Plane> Geometry::build_box_planes(const Vector3 &p_extents) { +Vector<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) { Vector<Plane> planes; planes.push_back(Plane(Vector3(1, 0, 0), p_extents.x)); @@ -828,7 +776,7 @@ Vector<Plane> Geometry::build_box_planes(const Vector3 &p_extents) { return planes; } -Vector<Plane> Geometry::build_cylinder_planes(real_t p_radius, real_t p_height, int p_sides, Vector3::Axis p_axis) { +Vector<Plane> Geometry3D::build_cylinder_planes(real_t p_radius, real_t p_height, int p_sides, Vector3::Axis p_axis) { Vector<Plane> planes; for (int i = 0; i < p_sides; i++) { @@ -848,7 +796,7 @@ Vector<Plane> Geometry::build_cylinder_planes(real_t p_radius, real_t p_height, return planes; } -Vector<Plane> Geometry::build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis) { +Vector<Plane> Geometry3D::build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis) { Vector<Plane> planes; Vector3 axis; @@ -878,7 +826,7 @@ Vector<Plane> Geometry::build_sphere_planes(real_t p_radius, int p_lats, int p_l return planes; } -Vector<Plane> Geometry::build_capsule_planes(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { +Vector<Plane> Geometry3D::build_capsule_planes(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { Vector<Plane> planes; Vector3 axis; @@ -907,252 +855,7 @@ Vector<Plane> Geometry::build_capsule_planes(real_t p_radius, real_t p_height, i return planes; } -struct _AtlasWorkRect { - Size2i s; - Point2i p; - int idx; - _FORCE_INLINE_ bool operator<(const _AtlasWorkRect &p_r) const { return s.width > p_r.s.width; }; -}; - -struct _AtlasWorkRectResult { - Vector<_AtlasWorkRect> result; - int max_w; - int max_h; -}; - -void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size) { - // Super simple, almost brute force scanline stacking fitter. - // It's pretty basic for now, but it tries to make sure that the aspect ratio of the - // resulting atlas is somehow square. This is necessary because video cards have limits. - // On texture size (usually 2048 or 4096), so the more square a texture, the more chances. - // It will work in every hardware. - // For example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a - // 256x8192 atlas (won't work anywhere). - - ERR_FAIL_COND(p_rects.size() == 0); - - Vector<_AtlasWorkRect> wrects; - wrects.resize(p_rects.size()); - for (int i = 0; i < p_rects.size(); i++) { - wrects.write[i].s = p_rects[i]; - wrects.write[i].idx = i; - } - wrects.sort(); - int widest = wrects[0].s.width; - - Vector<_AtlasWorkRectResult> results; - - for (int i = 0; i <= 12; i++) { - int w = 1 << i; - int max_h = 0; - int max_w = 0; - if (w < widest) { - continue; - } - - Vector<int> hmax; - hmax.resize(w); - for (int j = 0; j < w; j++) { - hmax.write[j] = 0; - } - - // Place them. - int ofs = 0; - int limit_h = 0; - for (int j = 0; j < wrects.size(); j++) { - if (ofs + wrects[j].s.width > w) { - ofs = 0; - } - - int from_y = 0; - for (int k = 0; k < wrects[j].s.width; k++) { - if (hmax[ofs + k] > from_y) { - from_y = hmax[ofs + k]; - } - } - - 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) { - limit_h = end_h; - } - - for (int k = 0; k < wrects[j].s.width; k++) { - hmax.write[ofs + k] = end_h; - } - - if (end_h > max_h) { - max_h = end_h; - } - - if (end_w > max_w) { - max_w = end_w; - } - - if (ofs == 0 || end_h > limit_h) { // While h limit not reached, keep stacking. - ofs += wrects[j].s.width; - } - } - - _AtlasWorkRectResult result; - result.result = wrects; - result.max_h = max_h; - result.max_w = max_w; - results.push_back(result); - } - - // Find the result with the best aspect ratio. - - int best = -1; - real_t best_aspect = 1e20; - - for (int i = 0; i < results.size(); i++) { - real_t h = next_power_of_2(results[i].max_h); - real_t w = next_power_of_2(results[i].max_w); - real_t aspect = h > w ? h / w : w / h; - if (aspect < best_aspect) { - best = i; - best_aspect = aspect; - } - } - - r_result.resize(p_rects.size()); - - for (int i = 0; i < p_rects.size(); i++) { - r_result.write[results[best].result[i].idx] = results[best].result[i].p; - } - - r_size = Size2(results[best].max_w, results[best].max_h); -} - -Vector<Vector<Point2>> Geometry::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open) { - using namespace ClipperLib; - - ClipType op = ctUnion; - - switch (p_op) { - case OPERATION_UNION: - op = ctUnion; - break; - case OPERATION_DIFFERENCE: - op = ctDifference; - break; - case OPERATION_INTERSECTION: - op = ctIntersection; - break; - case OPERATION_XOR: - op = ctXor; - break; - } - Path path_a, path_b; - - // Need to scale points (Clipper's requirement for robust computation). - for (int i = 0; i != p_polypath_a.size(); ++i) { - path_a << IntPoint(p_polypath_a[i].x * SCALE_FACTOR, p_polypath_a[i].y * SCALE_FACTOR); - } - for (int i = 0; i != p_polypath_b.size(); ++i) { - path_b << IntPoint(p_polypath_b[i].x * SCALE_FACTOR, p_polypath_b[i].y * SCALE_FACTOR); - } - Clipper clp; - clp.AddPath(path_a, ptSubject, !is_a_open); // Forward compatible with Clipper 10.0.0. - clp.AddPath(path_b, ptClip, true); // Polylines cannot be set as clip. - - Paths paths; - - if (is_a_open) { - PolyTree tree; // Needed to populate polylines. - clp.Execute(op, tree); - OpenPathsFromPolyTree(tree, paths); - } else { - clp.Execute(op, paths); // Works on closed polygons only. - } - // Have to scale points down now. - Vector<Vector<Point2>> polypaths; - - for (Paths::size_type i = 0; i < paths.size(); ++i) { - Vector<Vector2> polypath; - - const Path &scaled_path = paths[i]; - - for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { - polypath.push_back(Point2( - static_cast<real_t>(scaled_path[j].X) / SCALE_FACTOR, - static_cast<real_t>(scaled_path[j].Y) / SCALE_FACTOR)); - } - polypaths.push_back(polypath); - } - return polypaths; -} - -Vector<Vector<Point2>> Geometry::_polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { - using namespace ClipperLib; - - JoinType jt = jtSquare; - - switch (p_join_type) { - case JOIN_SQUARE: - jt = jtSquare; - break; - case JOIN_ROUND: - jt = jtRound; - break; - case JOIN_MITER: - jt = jtMiter; - break; - } - - EndType et = etClosedPolygon; - - switch (p_end_type) { - case END_POLYGON: - et = etClosedPolygon; - break; - case END_JOINED: - et = etClosedLine; - break; - case END_BUTT: - et = etOpenButt; - break; - case END_SQUARE: - et = etOpenSquare; - break; - case END_ROUND: - et = etOpenRound; - break; - } - ClipperOffset co(2.0, 0.25 * SCALE_FACTOR); // Defaults from ClipperOffset. - Path path; - - // Need to scale points (Clipper's requirement for robust computation). - for (int i = 0; i != p_polypath.size(); ++i) { - path << IntPoint(p_polypath[i].x * SCALE_FACTOR, p_polypath[i].y * SCALE_FACTOR); - } - co.AddPath(path, jt, et); - - Paths paths; - co.Execute(paths, p_delta * SCALE_FACTOR); // Inflate/deflate. - - // Have to scale points down now. - Vector<Vector<Point2>> polypaths; - - for (Paths::size_type i = 0; i < paths.size(); ++i) { - Vector<Vector2> polypath; - - const Path &scaled_path = paths[i]; - - for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { - polypath.push_back(Point2( - static_cast<real_t>(scaled_path[j].X) / SCALE_FACTOR, - static_cast<real_t>(scaled_path[j].Y) / SCALE_FACTOR)); - } - polypaths.push_back(polypath); - } - return polypaths; -} - -Vector<Vector3> Geometry::compute_convex_mesh_points(const Plane *p_planes, int p_plane_count) { +Vector<Vector3> Geometry3D::compute_convex_mesh_points(const Plane *p_planes, int p_plane_count) { Vector<Vector3> points; // Iterate through every unique combination of any three planes. @@ -1188,73 +891,6 @@ Vector<Vector3> Geometry::compute_convex_mesh_points(const Plane *p_planes, int return points; } -Vector<Point2i> Geometry::pack_rects(const Vector<Size2i> &p_sizes, const Size2i &p_atlas_size) { - Vector<stbrp_node> nodes; - nodes.resize(p_atlas_size.width); - - stbrp_context context; - stbrp_init_target(&context, p_atlas_size.width, p_atlas_size.height, nodes.ptrw(), p_atlas_size.width); - - Vector<stbrp_rect> rects; - rects.resize(p_sizes.size()); - - for (int i = 0; i < p_sizes.size(); i++) { - rects.write[i].id = 0; - rects.write[i].w = p_sizes[i].width; - rects.write[i].h = p_sizes[i].height; - rects.write[i].x = 0; - rects.write[i].y = 0; - rects.write[i].was_packed = 0; - } - - int res = stbrp_pack_rects(&context, rects.ptrw(), rects.size()); - if (res == 0) { //pack failed - return Vector<Point2i>(); - } - - Vector<Point2i> ret; - ret.resize(p_sizes.size()); - - for (int i = 0; i < p_sizes.size(); i++) { - Point2i r(rects[i].x, rects[i].y); - ret.write[i] = r; - } - - return ret; -} - -Vector<Vector3i> Geometry::partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size) { - Vector<stbrp_node> nodes; - nodes.resize(p_atlas_size.width); - zeromem(nodes.ptrw(), sizeof(stbrp_node) * nodes.size()); - - stbrp_context context; - stbrp_init_target(&context, p_atlas_size.width, p_atlas_size.height, nodes.ptrw(), p_atlas_size.width); - - Vector<stbrp_rect> rects; - rects.resize(p_sizes.size()); - - for (int i = 0; i < p_sizes.size(); i++) { - rects.write[i].id = i; - rects.write[i].w = p_sizes[i].width; - rects.write[i].h = p_sizes[i].height; - rects.write[i].x = 0; - rects.write[i].y = 0; - rects.write[i].was_packed = 0; - } - - stbrp_pack_rects(&context, rects.ptrw(), rects.size()); - - Vector<Vector3i> ret; - ret.resize(p_sizes.size()); - - for (int i = 0; i < p_sizes.size(); i++) { - ret.write[rects[i].id] = Vector3i(rects[i].x, rects[i].y, rects[i].was_packed != 0 ? 1 : 0); - } - - return ret; -} - #define square(m_s) ((m_s) * (m_s)) #define INF 1e20 @@ -1296,7 +932,7 @@ static void edt(float *f, int stride, int n) { #undef square -Vector<uint32_t> Geometry::generate_edf(const Vector<bool> &p_voxels, const Vector3i &p_size, bool p_negative) { +Vector<uint32_t> Geometry3D::generate_edf(const Vector<bool> &p_voxels, const Vector3i &p_size, bool p_negative) { uint32_t float_count = p_size.x * p_size.y * p_size.z; ERR_FAIL_COND_V((uint32_t)p_voxels.size() != float_count, Vector<uint32_t>()); @@ -1360,7 +996,7 @@ Vector<uint32_t> Geometry::generate_edf(const Vector<bool> &p_voxels, const Vect return ret; } -Vector<int8_t> Geometry::generate_sdf8(const Vector<uint32_t> &p_positive, const Vector<uint32_t> &p_negative) { +Vector<int8_t> Geometry3D::generate_sdf8(const Vector<uint32_t> &p_positive, const Vector<uint32_t> &p_negative) { ERR_FAIL_COND_V(p_positive.size() != p_negative.size(), Vector<int8_t>()); Vector<int8_t> sdf8; int s = p_positive.size(); diff --git a/core/math/geometry.h b/core/math/geometry_3d.h index a61bf20c4c..64cd34892e 100644 --- a/core/math/geometry.h +++ b/core/math/geometry_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* geometry.h */ +/* geometry_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,80 +28,17 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GEOMETRY_H -#define GEOMETRY_H +#ifndef GEOMETRY_3D_H +#define GEOMETRY_3D_H -#include "core/math/delaunay_2d.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" -class Geometry { - Geometry(); +class Geometry3D { + Geometry3D(); public: - static real_t get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2, Vector2 &c1, Vector2 &c2) { - Vector2 d1 = q1 - p1; // Direction vector of segment S1. - Vector2 d2 = q2 - p2; // Direction vector of segment S2. - Vector2 r = p1 - p2; - real_t a = d1.dot(d1); // Squared length of segment S1, always nonnegative. - real_t e = d2.dot(d2); // Squared length of segment S2, always nonnegative. - real_t f = d2.dot(r); - real_t s, t; - // Check if either or both segments degenerate into points. - if (a <= CMP_EPSILON && e <= CMP_EPSILON) { - // Both segments degenerate into points. - c1 = p1; - c2 = p2; - return Math::sqrt((c1 - c2).dot(c1 - c2)); - } - if (a <= CMP_EPSILON) { - // First segment degenerates into a point. - s = 0.0; - t = f / e; // s = 0 => t = (b*s + f) / e = f / e - t = CLAMP(t, 0.0, 1.0); - } else { - real_t c = d1.dot(r); - if (e <= CMP_EPSILON) { - // Second segment degenerates into a point. - t = 0.0; - s = CLAMP(-c / a, 0.0, 1.0); // t = 0 => s = (b*t - c) / a = -c / a - } else { - // The general nondegenerate case starts here. - real_t b = d1.dot(d2); - real_t denom = a * e - b * b; // Always nonnegative. - // If segments not parallel, compute closest point on L1 to L2 and - // clamp to segment S1. Else pick arbitrary s (here 0). - if (denom != 0.0) { - s = CLAMP((b * f - c * e) / denom, 0.0, 1.0); - } else { - s = 0.0; - } - // Compute point on L2 closest to S1(s) using - // t = Dot((P1 + D1*s) - P2,D2) / Dot(D2,D2) = (b*s + f) / e - t = (b * s + f) / e; - - //If t in [0,1] done. Else clamp t, recompute s for the new value - // of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a - // and clamp s to [0, 1]. - if (t < 0.0) { - t = 0.0; - s = CLAMP(-c / a, 0.0, 1.0); - } else if (t > 1.0) { - t = 1.0; - s = CLAMP((b - c) / a, 0.0, 1.0); - } - } - } - c1 = p1 + d1 * s; - c2 = p2 + d2 * t; - return Math::sqrt((c1 - c2).dot(c1 - c2)); - } - static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) { // Do the function 'd' as defined by pb. I think is is dot product of some sort. #define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z)) @@ -501,98 +438,6 @@ public: return p_segment[0] + n * d; // Inside. } - static Vector2 get_closest_point_to_segment_2d(const Vector2 &p_point, const Vector2 *p_segment) { - Vector2 p = p_point - p_segment[0]; - Vector2 n = p_segment[1] - p_segment[0]; - real_t l2 = n.length_squared(); - if (l2 < 1e-20) { - return p_segment[0]; // Both points are the same, just give any. - } - - real_t d = n.dot(p) / l2; - - if (d <= 0.0) { - return p_segment[0]; // Before first point. - } else if (d >= 1.0) { - return p_segment[1]; // After first point. - } else { - return p_segment[0] + n * d; // Inside. - } - } - - static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) { - Vector2 an = a - s; - Vector2 bn = b - s; - Vector2 cn = c - s; - - bool orientation = an.cross(bn) > 0; - - if ((bn.cross(cn) > 0) != orientation) { - return false; - } - - return (cn.cross(an) > 0) == orientation; - } - - static Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2 &p_point, const Vector2 *p_segment) { - Vector2 p = p_point - p_segment[0]; - Vector2 n = p_segment[1] - p_segment[0]; - real_t l2 = n.length_squared(); - if (l2 < 1e-20) { - return p_segment[0]; // Both points are the same, just give any. - } - - real_t d = n.dot(p) / l2; - - 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::is_zero_approx(denom)) { // 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; - Vector2 C = p_from_b - p_from_a; - Vector2 D = p_to_b - p_from_a; - - real_t ABlen = B.dot(B); - if (ABlen <= 0) { - return false; - } - Vector2 Bn = B / ABlen; - C = Vector2(C.x * Bn.x + C.y * Bn.y, C.y * Bn.x - C.x * Bn.y); - D = Vector2(D.x * Bn.x + D.y * Bn.y, D.y * Bn.x - D.x * Bn.y); - - if ((C.y < 0 && D.y < 0) || (C.y >= 0 && D.y >= 0)) { - return false; - } - - real_t ABpos = D.x + (C.x - D.x) * D.y / (D.y - C.y); - - // Fail if segment C-D crosses line A-B outside of segment A-B. - if (ABpos < 0 || ABpos > 1.0) { - return false; - } - - // (4) Apply the discovered position to line A-B in the original coordinate system. - if (r_result) { - *r_result = p_from_a + B * ABpos; - } - - return true; - } - static inline bool point_in_projected_triangle(const Vector3 &p_point, const Vector3 &p_v1, const Vector3 &p_v2, const Vector3 &p_v3) { Vector3 face_n = (p_v1 - p_v3).cross(p_v1 - p_v2); @@ -629,7 +474,7 @@ public: /** 2nd) TEST INSIDE TRIANGLE **/ - if (Geometry::point_in_projected_triangle(contact, p_triangle[0], p_triangle[1], p_triangle[2])) { + if (Geometry3D::point_in_projected_triangle(contact, p_triangle[0], p_triangle[1], p_triangle[2])) { r_triangle_contact = contact; r_sphere_contact = p_sphere_pos - p_normal * p_sphere_radius; //printf("solved inside triangle\n"); @@ -695,45 +540,6 @@ public: return false; } - static inline bool is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { - return p_point.distance_squared_to(p_circle_pos) <= p_circle_radius * p_circle_radius; - } - - static real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { - Vector2 line_vec = p_to - p_from; - Vector2 vec_to_line = p_from - p_circle_pos; - - // Create a quadratic formula of the form ax^2 + bx + c = 0 - real_t a, b, c; - - a = line_vec.dot(line_vec); - b = 2 * vec_to_line.dot(line_vec); - c = vec_to_line.dot(vec_to_line) - p_circle_radius * p_circle_radius; - - // Solve for t. - real_t sqrtterm = b * b - 4 * a * c; - - // If the term we intend to square root is less than 0 then the answer won't be real, - // so it definitely won't be t in the range 0 to 1. - if (sqrtterm < 0) { - return -1; - } - - // If we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) - // then the following can be skipped and we can just return the equivalent of res1. - sqrtterm = Math::sqrt(sqrtterm); - real_t res1 = (-b - sqrtterm) / (2 * a); - real_t res2 = (-b + sqrtterm) / (2 * a); - - if (res1 >= 0 && res1 <= 1) { - return res1; - } - if (res2 >= 0 && res2 <= 1) { - return res2; - } - return -1; - } - static inline Vector<Vector3> clip_polygon(const Vector<Vector3> &polygon, const Plane &p_plane) { enum LocationCache { LOC_INSIDE = 1, @@ -806,127 +612,6 @@ public: return clipped; } - enum PolyBooleanOperation { - OPERATION_UNION, - OPERATION_DIFFERENCE, - OPERATION_INTERSECTION, - OPERATION_XOR - }; - enum PolyJoinType { - JOIN_SQUARE, - JOIN_ROUND, - JOIN_MITER - }; - enum PolyEndType { - END_POLYGON, - END_JOINED, - END_BUTT, - END_SQUARE, - END_ROUND - }; - - static Vector<Vector<Point2>> merge_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { - return _polypaths_do_operation(OPERATION_UNION, p_polygon_a, p_polygon_b); - } - - static Vector<Vector<Point2>> clip_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { - return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polygon_a, p_polygon_b); - } - - static Vector<Vector<Point2>> intersect_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { - return _polypaths_do_operation(OPERATION_INTERSECTION, p_polygon_a, p_polygon_b); - } - - static Vector<Vector<Point2>> exclude_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { - return _polypaths_do_operation(OPERATION_XOR, p_polygon_a, p_polygon_b); - } - - static Vector<Vector<Point2>> clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { - return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polyline, p_polygon, true); - } - - static Vector<Vector<Point2>> intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { - return _polypaths_do_operation(OPERATION_INTERSECTION, p_polyline, p_polygon, true); - } - - static Vector<Vector<Point2>> offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { - return _polypath_offset(p_polygon, p_delta, p_join_type, END_POLYGON); - } - - static Vector<Vector<Point2>> offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { - ERR_FAIL_COND_V_MSG(p_end_type == END_POLYGON, Vector<Vector<Point2>>(), "Attempt to offset a polyline like a polygon (use offset_polygon_2d instead)."); - - return _polypath_offset(p_polygon, p_delta, p_join_type, p_end_type); - } - - static Vector<int> triangulate_delaunay_2d(const Vector<Vector2> &p_points) { - Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points); - Vector<int> triangles; - - for (int i = 0; i < tr.size(); i++) { - triangles.push_back(tr[i].points[0]); - triangles.push_back(tr[i].points[1]); - triangles.push_back(tr[i].points[2]); - } - return triangles; - } - - static Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon) { - Vector<int> triangles; - if (!Triangulate::triangulate(p_polygon, triangles)) { - return Vector<int>(); //fail - } - return triangles; - } - - static bool is_polygon_clockwise(const Vector<Vector2> &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; - } - - // Alternate implementation that should be faster. - static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) { - int c = p_polygon.size(); - if (c < 3) { - return false; - } - const Vector2 *p = p_polygon.ptr(); - Vector2 further_away(-1e20, -1e20); - Vector2 further_away_opposite(1e20, 1e20); - - for (int i = 0; i < c; i++) { - further_away.x = MAX(p[i].x, further_away.x); - further_away.y = MAX(p[i].y, further_away.y); - further_away_opposite.x = MIN(p[i].x, further_away_opposite.x); - further_away_opposite.y = MIN(p[i].y, further_away_opposite.y); - } - - // Make point outside that won't intersect with points in segment from p_point. - further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312); - - int intersections = 0; - for (int i = 0; i < c; i++) { - const Vector2 &v1 = p[i]; - const Vector2 &v2 = p[(i + 1) % c]; - if (segment_intersects_segment_2d(v1, v2, p_point, further_away, nullptr)) { - intersections++; - } - } - - return (intersections & 1); - } - static Vector<Vector<Face3>> separate_objects(Vector<Face3> p_array); // Create a "wrap" that encloses the given geometry. @@ -999,50 +684,12 @@ public: return ret; } } - - static real_t vec2_cross(const Point2 &O, const Point2 &A, const Point2 &B) { - return (real_t)(A.x - O.x) * (B.y - O.y) - (real_t)(A.y - O.y) * (B.x - O.x); - } - - // Returns a list of points on the convex hull in counter-clockwise order. - // Note: the last point in the returned list is the same as the first one. - static Vector<Point2> convex_hull_2d(Vector<Point2> P) { - int n = P.size(), k = 0; - Vector<Point2> H; - H.resize(2 * n); - - // Sort points lexicographically. - P.sort(); - - // Build lower hull. - for (int i = 0; i < n; ++i) { - while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) { - k--; - } - 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.write[k++] = P[i]; - } - - H.resize(k); - return H; - } - static Vector<Vector<Vector2>> decompose_polygon_in_convex(Vector<Point2> polygon); - static MeshData build_convex_mesh(const Vector<Plane> &p_planes); static Vector<Plane> build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis = Vector3::AXIS_Z); static Vector<Plane> build_box_planes(const Vector3 &p_extents); static Vector<Plane> build_cylinder_planes(real_t p_radius, real_t p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); static Vector<Plane> build_capsule_planes(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); - static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size); - static Vector<Vector3> compute_convex_mesh_points(const Plane *p_planes, int p_plane_count); #define FINDMINMAX(x0, x1, x2, min, max) \ @@ -1255,9 +902,6 @@ public: return planeBoxOverlap(normal, d, boxhalfsize); /* if true, box and triangle overlaps */ } - static Vector<Point2i> pack_rects(const Vector<Size2i> &p_sizes, const Size2i &p_atlas_size); - static Vector<Vector3i> partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size); - static Vector<uint32_t> generate_edf(const Vector<bool> &p_voxels, const Vector3i &p_size, bool p_negative); static Vector<int8_t> generate_sdf8(const Vector<uint32_t> &p_positive, const Vector<uint32_t> &p_negative); @@ -1301,10 +945,6 @@ public: return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6); #undef STP } - -private: - static Vector<Vector<Point2>> _polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open = false); - static Vector<Vector<Point2>> _polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type); }; -#endif // GEOMETRY_H +#endif // GEOMETRY_3D_H diff --git a/core/math/octree.h b/core/math/octree.h index c05fc4e9ed..5d9688d442 100644 --- a/core/math/octree.h +++ b/core/math/octree.h @@ -34,7 +34,7 @@ #include "core/list.h" #include "core/map.h" #include "core/math/aabb.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #include "core/math/vector3.h" #include "core/print_string.h" #include "core/variant.h" @@ -1201,7 +1201,7 @@ int Octree<T, use_pairs, AL>::cull_convex(const Vector<Plane> &p_convex, T **p_r return 0; } - Vector<Vector3> convex_points = Geometry::compute_convex_mesh_points(&p_convex[0], p_convex.size()); + Vector<Vector3> convex_points = Geometry3D::compute_convex_mesh_points(&p_convex[0], p_convex.size()); if (convex_points.size() == 0) { return 0; } diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index fe16904448..8ba1ba9286 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -34,7 +34,7 @@ uint32_t QuickHull::debug_stop_after = 0xFFFFFFFF; -Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_mesh) { +Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_mesh) { /* CREATE AABB VOLUME */ AABB aabb; @@ -334,17 +334,17 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me //make a map of edges again Map<Edge, RetFaceConnect> ret_edges; - List<Geometry::MeshData::Face> ret_faces; + List<Geometry3D::MeshData::Face> ret_faces; for (List<Face>::Element *E = faces.front(); E; E = E->next()) { - Geometry::MeshData::Face f; + Geometry3D::MeshData::Face f; f.plane = E->get().plane; for (int i = 0; i < 3; i++) { f.indices.push_back(E->get().vertices[i]); } - List<Geometry::MeshData::Face>::Element *F = ret_faces.push_back(f); + List<Geometry3D::MeshData::Face>::Element *F = ret_faces.push_back(f); for (int i = 0; i < 3; i++) { uint32_t a = E->get().vertices[i]; @@ -366,8 +366,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me //fill faces - for (List<Geometry::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { - Geometry::MeshData::Face &f = E->get(); + for (List<Geometry3D::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { + Geometry3D::MeshData::Face &f = E->get(); for (int i = 0; i < f.indices.size(); i++) { int a = E->get().indices[i]; @@ -377,7 +377,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Map<Edge, RetFaceConnect>::Element *F = ret_edges.find(e); ERR_CONTINUE(!F); - List<Geometry::MeshData::Face>::Element *O = F->get().left == E ? F->get().right : F->get().left; + List<Geometry3D::MeshData::Face>::Element *O = F->get().left == E ? F->get().right : F->get().left; ERR_CONTINUE(O == E); ERR_CONTINUE(O == nullptr); @@ -439,13 +439,13 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me r_mesh.faces.resize(ret_faces.size()); int idx = 0; - for (List<Geometry::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { + for (List<Geometry3D::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { r_mesh.faces.write[idx++] = E->get(); } r_mesh.edges.resize(ret_edges.size()); idx = 0; for (Map<Edge, RetFaceConnect>::Element *E = ret_edges.front(); E; E = E->next()) { - Geometry::MeshData::Edge e; + Geometry3D::MeshData::Edge e; e.a = E->key().vertices[0]; e.b = E->key().vertices[1]; r_mesh.edges.write[idx++] = e; diff --git a/core/math/quick_hull.h b/core/math/quick_hull.h index 29f709febe..cac8e58d23 100644 --- a/core/math/quick_hull.h +++ b/core/math/quick_hull.h @@ -33,7 +33,7 @@ #include "core/list.h" #include "core/math/aabb.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #include "core/set.h" class QuickHull { @@ -74,13 +74,13 @@ private: FaceConnect() {} }; struct RetFaceConnect { - List<Geometry::MeshData::Face>::Element *left, *right = nullptr; + List<Geometry3D::MeshData::Face>::Element *left, *right = nullptr; RetFaceConnect() {} }; public: static uint32_t debug_stop_after; - static Error build(const Vector<Vector3> &p_points, Geometry::MeshData &r_mesh); + static Error build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_mesh); }; #endif // QUICK_HULL_H diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index c595e445d5..775e17fdb5 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -48,17 +48,19 @@ * * Only used keys and values are constructed. For free positions there's space * in the arrays for each, but that memory is kept uninitialized. + * + * The assignment operator copy the pairs from one map to the other. */ template <class TKey, class TValue, class Hasher = HashMapHasherDefault, class Comparator = HashMapComparatorDefault<TKey>> class OAHashMap { private: - TValue *values; - TKey *keys; - uint32_t *hashes; + TValue *values = nullptr; + TKey *keys = nullptr; + uint32_t *hashes = nullptr; - uint32_t capacity; + uint32_t capacity = 0; uint32_t num_elements = 0; @@ -142,7 +144,9 @@ private: void _resize_and_rehash(uint32_t p_new_capacity) { uint32_t old_capacity = capacity; - capacity = p_new_capacity; + + // Capacity can't be 0. + capacity = MAX(1, p_new_capacity); TKey *old_keys = keys; TValue *old_values = values; @@ -157,6 +161,11 @@ private: hashes[i] = 0; } + if (old_capacity == 0) { + // Nothing to do. + return; + } + for (uint32_t i = 0; i < old_capacity; i++) { if (old_hashes[i] == EMPTY_HASH) { continue; @@ -341,17 +350,32 @@ public: return it; } - OAHashMap(const OAHashMap &) = delete; // Delete the copy constructor so we don't get unexpected copies and dangling pointers. - OAHashMap &operator=(const OAHashMap &) = delete; // Same for assignment operator. + OAHashMap(const OAHashMap &p_other) { + (*this) = p_other; + } + + OAHashMap &operator=(const OAHashMap &p_other) { + if (capacity != 0) { + clear(); + } + + _resize_and_rehash(p_other.capacity); + + for (Iterator it = p_other.iter(); it.valid; it = p_other.next_iter(it)) { + set(*it.key, *it.value); + } + return *this; + } OAHashMap(uint32_t p_initial_capacity = 64) { - capacity = p_initial_capacity; + // Capacity can't be 0. + capacity = MAX(1, p_initial_capacity); keys = static_cast<TKey *>(Memory::alloc_static(sizeof(TKey) * capacity)); values = static_cast<TValue *>(Memory::alloc_static(sizeof(TValue) * capacity)); hashes = static_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity)); - for (uint32_t i = 0; i < p_initial_capacity; i++) { + for (uint32_t i = 0; i < capacity; i++) { hashes[i] = EMPTY_HASH; } } diff --git a/core/os/os.cpp b/core/os/os.cpp index c29930e485..56755bcf51 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -457,18 +457,22 @@ PackedStringArray OS::get_connected_midi_inputs() { } PackedStringArray list; - return list; + ERR_FAIL_V_MSG(list, vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name())); } void OS::open_midi_inputs() { if (MIDIDriver::get_singleton()) { MIDIDriver::get_singleton()->open(); + } else { + ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name())); } } void OS::close_midi_inputs() { if (MIDIDriver::get_singleton()) { MIDIDriver::get_singleton()->close(); + } else { + ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name())); } } diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 83d94ad607..5247f6da40 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -295,10 +295,16 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) { * using the following merit order: * - If using NetworkClient, try to lookup project file or fail. * - If --main-pack was passed by the user (`p_main_pack`), load it or fail. - * - Search for .pck file matching binary name. There are two possibilities: - * o exec_path.get_basename() + '.pck' (e.g. 'win_game.exe' -> 'win_game.pck') - * o exec_path + '.pck' (e.g. 'linux_game' -> 'linux_game.pck') - * For each tentative, if the file exists, load it or fail. + * - Search for project PCKs automatically. For each step we try loading a potential + * PCK, and if it doesn't work, we proceed to the next step. If any step succeeds, + * we try loading the project settings, and abort if it fails. Steps: + * o Bundled PCK in the executable. + * o [macOS only] PCK with same basename as the binary in the .app resource dir. + * o PCK with same basename as the binary in the binary's directory. We handle both + * changing the extension to '.pck' (e.g. 'win_game.exe' -> 'win_game.pck') and + * appending '.pck' to the binary name (e.g. 'linux_game' -> 'linux_game.pck'). + * o PCK with the same basename as the binary in the current working directory. + * Same as above for the two possible PCK file names. * - On relevant platforms (Android/iOS), lookup project file in OS resource path. * If found, load it or fail. * - Lookup project file in passed `p_path` (--path passed by the user), i.e. we @@ -339,65 +345,68 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b String exec_path = OS::get_singleton()->get_executable_path(); if (exec_path != "") { - // Attempt with exec_name.pck - // (This is the usual case when distributing a Godot game.) + // We do several tests sequentially until one succeeds to find a PCK, + // and if so we attempt loading it at the end. - // Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle) - // or the exec path's basename + '.pck' (Windows). - // We need to test both possibilities as extensions for Linux binaries are optional - // (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck'). + // Attempt with PCK bundled into executable. + bool found = _load_resource_pack(exec_path); + // Attempt with exec_name.pck. + // (This is the usual case when distributing a Godot game.) String exec_dir = exec_path.get_base_dir(); String exec_filename = exec_path.get_file(); String exec_basename = exec_filename.get_basename(); - // Attempt with PCK bundled into executable - bool found = _load_resource_pack(exec_path); + // Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle) + // or the exec path's basename + '.pck' (Windows). + // We need to test both possibilities as extensions for Linux binaries are optional + // (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck'). #ifdef OSX_ENABLED if (!found) { - // Attempt to load PCK from macOS .app bundle resources + // Attempt to load PCK from macOS .app bundle resources. found = _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().plus_file(exec_basename + ".pck")); } #endif if (!found) { - // Try to load data pack at the location of the executable - // As mentioned above, we have two potential names to attempt + // Try to load data pack at the location of the executable. + // As mentioned above, we have two potential names to attempt. found = _load_resource_pack(exec_dir.plus_file(exec_basename + ".pck")) || _load_resource_pack(exec_dir.plus_file(exec_filename + ".pck")); + } - if (!found) { - // If we couldn't find them next to the executable, we attempt - // the current working directory. Same story, two tests. - found = _load_resource_pack(exec_basename + ".pck") || _load_resource_pack(exec_filename + ".pck"); - } + if (!found) { + // If we couldn't find them next to the executable, we attempt + // the current working directory. Same story, two tests. + found = _load_resource_pack(exec_basename + ".pck") || _load_resource_pack(exec_filename + ".pck"); } - // If we opened our package, try and load our project + // If we opened our package, try and load our project. if (found) { Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { - // Load override from location of executable - // Optional, we don't mind if it fails + // Load override from location of the executable. + // Optional, we don't mind if it fails. _load_settings_text(exec_path.get_base_dir().plus_file("override.cfg")); } return err; } } - // Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this) + // Try to use the filesystem for files, according to OS. + // (Only Android -when reading from pck- and iOS use this.) if (OS::get_singleton()->get_resource_dir() != "") { // OS will call ProjectSettings->get_resource_path which will be empty if not overridden! // If the OS would rather use a specific location, then it will not be empty. resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/"); if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') { - resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end + resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end. } Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { - // Optional, we don't mind if it fails + // Optional, we don't mind if it fails. _load_settings_text("res://override.cfg"); } return err; @@ -418,7 +427,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b while (true) { err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary")); if (err == OK) { - // Optional, we don't mind if it fails + // Optional, we don't mind if it fails. _load_settings_text(current_dir.plus_file("override.cfg")); candidate = current_dir; found = true; @@ -438,7 +447,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b } resource_path = candidate; - resource_path = resource_path.replace("\\", "/"); // windows path to unix path just in case + resource_path = resource_path.replace("\\", "/"); // Windows path to Unix path just in case. memdelete(d); if (!found) { @@ -446,7 +455,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b } if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') { - resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end + resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end. } return OK; diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 3870141ecf..c0f9eea76d 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -60,7 +60,8 @@ #include "core/io/xml_parser.h" #include "core/math/a_star.h" #include "core/math/expression.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" +#include "core/math/geometry_3d.h" #include "core/math/random_number_generator.h" #include "core/math/triangle_mesh.h" #include "core/os/main_loop.h" @@ -87,7 +88,8 @@ static _JSON *_json = nullptr; static IP *ip = nullptr; -static _Geometry *_geometry = nullptr; +static _Geometry2D *_geometry_2d = nullptr; +static _Geometry3D *_geometry_3d = nullptr; extern Mutex _global_mutex; @@ -213,7 +215,8 @@ void register_core_types() { ip = IP::create(); - _geometry = memnew(_Geometry); + _geometry_2d = memnew(_Geometry2D); + _geometry_3d = memnew(_Geometry3D); _resource_loader = memnew(_ResourceLoader); _resource_saver = memnew(_ResourceSaver); @@ -238,7 +241,8 @@ void register_core_settings() { void register_core_singletons() { ClassDB::register_class<ProjectSettings>(); ClassDB::register_virtual_class<IP>(); - ClassDB::register_class<_Geometry>(); + ClassDB::register_class<_Geometry2D>(); + ClassDB::register_class<_Geometry3D>(); ClassDB::register_class<_ResourceLoader>(); ClassDB::register_class<_ResourceSaver>(); ClassDB::register_class<_OS>(); @@ -253,7 +257,8 @@ void register_core_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry", _Geometry::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry2D", _Geometry2D::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry3D", _Geometry3D::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceLoader", _ResourceLoader::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceSaver", _ResourceSaver::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("OS", _OS::get_singleton())); @@ -275,7 +280,8 @@ void unregister_core_types() { memdelete(_marshalls); memdelete(_json); - memdelete(_geometry); + memdelete(_geometry_2d); + memdelete(_geometry_3d); ResourceLoader::remove_resource_format_loader(resource_format_image); resource_format_image.unref(); diff --git a/core/ustring.cpp b/core/ustring.cpp index 7dbaed9fbe..cfb547742a 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -4338,13 +4338,14 @@ String TTR(const String &p_text) { } String DTR(const String &p_text) { + // Comes straight from the XML, so remove indentation and any trailing whitespace. + const String text = p_text.dedent().strip_edges(); + if (TranslationServer::get_singleton()) { - // Comes straight from the XML, so remove indentation and any trailing whitespace. - const String text = p_text.dedent().strip_edges(); return TranslationServer::get_singleton()->doc_translate(text); } - return p_text; + return text; } #endif diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 404468a7b4..f1b2a1547d 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -1296,10 +1296,16 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i // math types case VECTOR2: return Vector2(); + case VECTOR2I: + return Vector2i(); case RECT2: return Rect2(); + case RECT2I: + return Rect2i(); case VECTOR3: return Vector3(); + case VECTOR3I: + return Vector3i(); case TRANSFORM2D: return Transform2D(); case PLANE: diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index bdcad03353..74f4f32c0e 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -479,12 +479,6 @@ Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct, } Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser) { - /* { - Error err = get_token(p_stream,token,line,r_err_str); - if (err) - return err; - }*/ - if (token.type == TK_CURLY_BRACKET_OPEN) { Dictionary d; Error err = _parse_dictionary(d, p_stream, line, r_err_str, p_res_parser); @@ -501,7 +495,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = a; return OK; - } else if (token.type == TK_IDENTIFIER) { String id = token.value; if (id == "true") { @@ -523,10 +516,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 2) { r_err_str = "Expected 2 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Vector2(args[0], args[1]); - return OK; } else if (id == "Vector2i") { Vector<int32_t> args; Error err = _parse_construct<int32_t>(p_stream, args, line, r_err_str); @@ -536,10 +529,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 2) { r_err_str = "Expected 2 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Vector2i(args[0], args[1]); - return OK; } else if (id == "Rect2") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -549,10 +542,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 4) { r_err_str = "Expected 4 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Rect2(args[0], args[1], args[2], args[3]); - return OK; } else if (id == "Rect2i") { Vector<int32_t> args; Error err = _parse_construct<int32_t>(p_stream, args, line, r_err_str); @@ -562,10 +555,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 4) { r_err_str = "Expected 4 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Rect2i(args[0], args[1], args[2], args[3]); - return OK; } else if (id == "Vector3") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -575,10 +568,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 3) { r_err_str = "Expected 3 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Vector3(args[0], args[1], args[2]); - return OK; } else if (id == "Vector3i") { Vector<int32_t> args; Error err = _parse_construct<int32_t>(p_stream, args, line, r_err_str); @@ -588,12 +581,11 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 3) { r_err_str = "Expected 3 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Vector3i(args[0], args[1], args[2]); - return OK; } else if (id == "Transform2D" || id == "Matrix32") { //compatibility - Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); if (err) { @@ -602,13 +594,14 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 6) { r_err_str = "Expected 6 arguments for constructor"; + return ERR_PARSE_ERROR; } + Transform2D m; m[0] = Vector2(args[0], args[1]); m[1] = Vector2(args[2], args[3]); m[2] = Vector2(args[4], args[5]); value = m; - return OK; } else if (id == "Plane") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -618,10 +611,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 4) { r_err_str = "Expected 4 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Plane(args[0], args[1], args[2], args[3]); - return OK; } else if (id == "Quat") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -631,11 +624,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 4) { r_err_str = "Expected 4 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Quat(args[0], args[1], args[2], args[3]); - return OK; - } else if (id == "AABB" || id == "Rect3") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -645,13 +637,11 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 6) { r_err_str = "Expected 6 arguments for constructor"; + return ERR_PARSE_ERROR; } value = AABB(Vector3(args[0], args[1], args[2]), Vector3(args[3], args[4], args[5])); - return OK; - } else if (id == "Basis" || id == "Matrix3") { //compatibility - Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); if (err) { @@ -660,10 +650,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 9) { r_err_str = "Expected 9 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Basis(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); - return OK; } else if (id == "Transform") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -673,11 +663,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 12) { r_err_str = "Expected 12 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Transform(Basis(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]), Vector3(args[9], args[10], args[11])); - return OK; - } else if (id == "Color") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -687,11 +676,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, if (args.size() != 4) { r_err_str = "Expected 4 arguments for constructor"; + return ERR_PARSE_ERROR; } value = Color(args[0], args[1], args[2], args[3]); - return OK; - } else if (id == "NodePath") { get_token(p_stream, token, line, r_err_str); if (token.type != TK_PARENTHESIS_OPEN) { @@ -712,7 +700,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, r_err_str = "Expected ')'"; return ERR_PARSE_ERROR; } - } else if (id == "RID") { get_token(p_stream, token, line, r_err_str); if (token.type != TK_PARENTHESIS_OPEN) { @@ -733,8 +720,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, r_err_str = "Expected ')'"; return ERR_PARSE_ERROR; } - - return OK; } else if (id == "Object") { get_token(p_stream, token, line, r_err_str); if (token.type != TK_PARENTHESIS_OPEN) { @@ -834,7 +819,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, at_key = true; } } - } else if (id == "Resource" || id == "SubResource" || id == "ExtResource") { get_token(p_stream, token, line, r_err_str); if (token.type != TK_PARENTHESIS_OPEN) { @@ -850,8 +834,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = res; - - return OK; } else if (p_res_parser && id == "ExtResource" && p_res_parser->ext_func) { RES res; Error err = p_res_parser->ext_func(p_res_parser->userdata, p_stream, res, line, r_err_str); @@ -860,8 +842,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = res; - - return OK; } else if (p_res_parser && id == "SubResource" && p_res_parser->sub_func) { RES res; Error err = p_res_parser->sub_func(p_res_parser->userdata, p_stream, res, line, r_err_str); @@ -870,8 +850,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = res; - - return OK; } else { get_token(p_stream, token, line, r_err_str); if (token.type == TK_STRING) { @@ -889,14 +867,11 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = res; - return OK; - } else { r_err_str = "Expected string as argument for Resource()."; return ERR_PARSE_ERROR; } } - } else if (id == "PackedByteArray" || id == "PoolByteArray" || id == "ByteArray") { Vector<uint8_t> args; Error err = _parse_construct<uint8_t>(p_stream, args, line, r_err_str); @@ -915,9 +890,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; - } else if (id == "PackedInt32Array" || id == "PackedIntArray" || id == "PoolIntArray" || id == "IntArray") { Vector<int32_t> args; Error err = _parse_construct<int32_t>(p_stream, args, line, r_err_str); @@ -936,9 +908,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; - } else if (id == "PackedInt64Array") { Vector<int64_t> args; Error err = _parse_construct<int64_t>(p_stream, args, line, r_err_str); @@ -957,9 +926,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; - } else if (id == "PackedFloat32Array" || id == "PackedRealArray" || id == "PoolRealArray" || id == "FloatArray") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -978,8 +944,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; } else if (id == "PackedFloat64Array") { Vector<double> args; Error err = _parse_construct<double>(p_stream, args, line, r_err_str); @@ -998,8 +962,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; } else if (id == "PackedStringArray" || id == "PoolStringArray" || id == "StringArray") { get_token(p_stream, token, line, r_err_str); if (token.type != TK_PARENTHESIS_OPEN) { @@ -1046,9 +1008,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; - } else if (id == "PackedVector2Array" || id == "PoolVector2Array" || id == "Vector2Array") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -1067,9 +1026,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; - } else if (id == "PackedVector3Array" || id == "PoolVector3Array" || id == "Vector3Array") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -1088,9 +1044,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; - } else if (id == "PackedColorArray" || id == "PoolColorArray" || id == "ColorArray") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); @@ -1109,15 +1062,13 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } value = arr; - - return OK; } else { r_err_str = "Unexpected identifier: '" + id + "'."; return ERR_PARSE_ERROR; } + // All above branches end up here unless they had an early return. return OK; - } else if (token.type == TK_NUMBER) { value = token.value; return OK; diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 03eb733e54..41811a48b1 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -27,8 +27,11 @@ <member name="Engine" type="Engine" setter="" getter=""> The [Engine] singleton. </member> - <member name="Geometry" type="Geometry" setter="" getter=""> - The [Geometry] singleton. + <member name="Geometry2D" type="Geometry2D" setter="" getter=""> + The [Geometry2D] singleton. + </member> + <member name="Geometry3D" type="Geometry3D" setter="" getter=""> + The [Geometry3D] singleton. </member> <member name="IP" type="IP" setter="" getter=""> The [IP] singleton. diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 9642dd1c70..dd04f4ce3f 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -4,6 +4,7 @@ A node to be used for advanced animation transitions in an [AnimationPlayer]. </brief_description> <description> + Note: When linked with an [AnimationPlayer], several properties and methods of the corresponding [AnimationPlayer] will not function as expected. Playback and transitions should be handled using only the [AnimationTree] and its constituent [AnimationNode](s). The [AnimationPlayer] node should be used solely for adding, deleting, and editing animations. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/animation/animation_tree.html</link> @@ -23,6 +24,7 @@ <return type="Transform"> </return> <description> + Retrieve the motion of the [member root_motion_track] as a [Transform] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_TRANSFORM], returns an identity transformation. </description> </method> <method name="rename_parameter"> @@ -47,6 +49,8 @@ The process mode of this [AnimationTree]. See [enum AnimationProcessMode] for available modes. </member> <member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath("")"> + The path to the Animation track used for root motion. Paths must be valid scene-tree paths to a node, and must be specified starting from the parent node of the node that will reproduce the animation. To specify a track that controls properties or bones, append its name after the path, separated by [code]":"[/code]. For example, [code]"character/skeleton:ankle"[/code] or [code]"character/mesh:transform/local"[/code]. + If the track has type [constant Animation.TYPE_TRANSFORM], the transformation will be cancelled visually, and the animation will appear to stay in place. </member> <member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root"> The root animation node of this [AnimationTree]. See [AnimationNode]. diff --git a/doc/classes/BitmapFont.xml b/doc/classes/BitmapFont.xml index 421b405808..87cffdaca0 100644 --- a/doc/classes/BitmapFont.xml +++ b/doc/classes/BitmapFont.xml @@ -65,17 +65,6 @@ Creates a BitmapFont from the [code]*.fnt[/code] file at [code]path[/code]. </description> </method> - <method name="get_char_size" qualifiers="const"> - <return type="Vector2"> - </return> - <argument index="0" name="char" type="int"> - </argument> - <argument index="1" name="next" type="int" default="0"> - </argument> - <description> - Returns the size of a character, optionally taking kerning into account if the next character is provided. - </description> - </method> <method name="get_kerning_pair" qualifiers="const"> <return type="int"> </return> diff --git a/doc/classes/ConvexPolygonShape2D.xml b/doc/classes/ConvexPolygonShape2D.xml index cba446fff8..42951e2158 100644 --- a/doc/classes/ConvexPolygonShape2D.xml +++ b/doc/classes/ConvexPolygonShape2D.xml @@ -16,7 +16,7 @@ <argument index="0" name="point_cloud" type="PackedVector2Array"> </argument> <description> - Based on the set of points provided, this creates and assigns the [member points] property using the convex hull algorithm. Removing all unneeded points. See [method Geometry.convex_hull_2d] for details. + Based on the set of points provided, this creates and assigns the [member points] property using the convex hull algorithm. Removing all unneeded points. See [method Geometry2D.convex_hull] for details. </description> </method> </methods> diff --git a/doc/classes/DirectionalLight3D.xml b/doc/classes/DirectionalLight3D.xml index 6c88dcf42e..f66be6ddde 100644 --- a/doc/classes/DirectionalLight3D.xml +++ b/doc/classes/DirectionalLight3D.xml @@ -42,19 +42,19 @@ </members> <constants> <constant name="SHADOW_ORTHOGONAL" value="0" enum="ShadowMode"> - Renders the entire scene's shadow map from an orthogonal point of view. May result in blockier shadows on close objects. + Renders the entire scene's shadow map from an orthogonal point of view. This is the fastest directional shadow mode. May result in blurrier shadows on close objects. </constant> <constant name="SHADOW_PARALLEL_2_SPLITS" value="1" enum="ShadowMode"> - Splits the view frustum in 2 areas, each with its own shadow map. + Splits the view frustum in 2 areas, each with its own shadow map. This shadow mode is a compromise between [constant SHADOW_ORTHOGONAL] and [constant SHADOW_PARALLEL_4_SPLITS] in terms of performance. </constant> <constant name="SHADOW_PARALLEL_4_SPLITS" value="2" enum="ShadowMode"> - Splits the view frustum in 4 areas, each with its own shadow map. + Splits the view frustum in 4 areas, each with its own shadow map. This is the slowest directional shadow mode. </constant> <constant name="SHADOW_DEPTH_RANGE_STABLE" value="0" enum="ShadowDepthRange"> Keeps the shadow stable when the camera moves, at the cost of lower effective shadow resolution. </constant> <constant name="SHADOW_DEPTH_RANGE_OPTIMIZED" value="1" enum="ShadowDepthRange"> - Tries to achieve maximum shadow resolution. May result in saw effect on shadow edges. + Tries to achieve maximum shadow resolution. May result in saw effect on shadow edges. This mode typically works best in games where the camera will often move at high speeds, such as most racing games. </constant> </constants> </class> diff --git a/doc/classes/DynamicFontData.xml b/doc/classes/DynamicFontData.xml index 2b4ec17bf1..483da96f3f 100644 --- a/doc/classes/DynamicFontData.xml +++ b/doc/classes/DynamicFontData.xml @@ -12,7 +12,7 @@ </methods> <members> <member name="antialiased" type="bool" setter="set_antialiased" getter="is_antialiased" default="true"> - If [code]true[/code], the font is rendered with anti-aliasing. + If [code]true[/code], the font is rendered with anti-aliasing. This property applies both to the main font and its outline (if it has one). </member> <member name="font_path" type="String" setter="set_font_path" getter="get_font_path" default=""""> The path to the vector font file. diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 499c3b8271..dbe725f08b 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -182,14 +182,6 @@ Selects the file, with the path provided by [code]file[/code], in the FileSystem dock. </description> </method> - <method name="set_distraction_free_mode"> - <return type="void"> - </return> - <argument index="0" name="enter" type="bool"> - </argument> - <description> - </description> - </method> <method name="set_main_screen_editor"> <return type="void"> </return> @@ -210,6 +202,10 @@ </description> </method> </methods> + <members> + <member name="distraction_free_mode" type="bool" setter="set_distraction_free_mode" getter="is_distraction_free_mode_enabled"> + </member> + </members> <constants> </constants> </class> diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index 882f819e37..30b8c1fe76 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -54,6 +54,17 @@ Returns the font ascent (number of pixels above the baseline). </description> </method> + <method name="get_char_size" qualifiers="const"> + <return type="Vector2"> + </return> + <argument index="0" name="char" type="int"> + </argument> + <argument index="1" name="next" type="int" default="0"> + </argument> + <description> + Returns the size of a character, optionally taking kerning into account if the next character is provided. + </description> + </method> <method name="get_descent" qualifiers="const"> <return type="float"> </return> diff --git a/doc/classes/Geometry.xml b/doc/classes/Geometry2D.xml index 4d6f7b60a3..ffd4bd108d 100644 --- a/doc/classes/Geometry.xml +++ b/doc/classes/Geometry2D.xml @@ -1,67 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Geometry" inherits="Object" version="4.0"> +<class name="Geometry2D" inherits="Object" version="4.0"> <brief_description> - Helper node to calculate generic geometry operations. + Helper node to calculate generic geometry operations in 2D space. </brief_description> <description> - Geometry provides users with a set of helper functions to create geometric shapes, compute intersections between shapes, and process various other geometric operations. + Geometry2D provides users with a set of helper functions to create geometric shapes, compute intersections between shapes, and process various other geometric operations. </description> <tutorials> </tutorials> <methods> - <method name="build_box_planes"> - <return type="Array"> - </return> - <argument index="0" name="extents" type="Vector3"> - </argument> - <description> - Returns an array with 6 [Plane]s that describe the sides of a box centered at the origin. The box size is defined by [code]extents[/code], which represents one (positive) corner of the box (i.e. half its actual size). - </description> - </method> - <method name="build_capsule_planes"> - <return type="Array"> - </return> - <argument index="0" name="radius" type="float"> - </argument> - <argument index="1" name="height" type="float"> - </argument> - <argument index="2" name="sides" type="int"> - </argument> - <argument index="3" name="lats" type="int"> - </argument> - <argument index="4" name="axis" type="int" enum="Vector3.Axis" default="2"> - </argument> - <description> - Returns an array of [Plane]s closely bounding a faceted capsule centered at the origin with radius [code]radius[/code] and height [code]height[/code]. The parameter [code]sides[/code] defines how many planes will be generated for the side part of the capsule, whereas [code]lats[/code] gives the number of latitudinal steps at the bottom and top of the capsule. The parameter [code]axis[/code] describes the axis along which the capsule is oriented (0 for X, 1 for Y, 2 for Z). - </description> - </method> - <method name="build_cylinder_planes"> - <return type="Array"> - </return> - <argument index="0" name="radius" type="float"> - </argument> - <argument index="1" name="height" type="float"> - </argument> - <argument index="2" name="sides" type="int"> - </argument> - <argument index="3" name="axis" type="int" enum="Vector3.Axis" default="2"> - </argument> - <description> - Returns an array of [Plane]s closely bounding a faceted cylinder centered at the origin with radius [code]radius[/code] and height [code]height[/code]. The parameter [code]sides[/code] defines how many planes will be generated for the round part of the cylinder. The parameter [code]axis[/code] describes the axis along which the cylinder is oriented (0 for X, 1 for Y, 2 for Z). - </description> - </method> - <method name="clip_polygon"> - <return type="PackedVector3Array"> - </return> - <argument index="0" name="points" type="PackedVector3Array"> - </argument> - <argument index="1" name="plane" type="Plane"> - </argument> - <description> - Clips the polygon defined by the points in [code]points[/code] against the [code]plane[/code] and returns the points of the clipped polygon. - </description> - </method> - <method name="clip_polygons_2d"> + <method name="clip_polygons"> <return type="Array"> </return> <argument index="0" name="polygon_a" type="PackedVector2Array"> @@ -73,7 +21,7 @@ If [code]polygon_b[/code] is enclosed by [code]polygon_a[/code], returns an outer polygon (boundary) and inner polygon (hole) which could be distinguished by calling [method is_polygon_clockwise]. </description> </method> - <method name="clip_polyline_with_polygon_2d"> + <method name="clip_polyline_with_polygon"> <return type="Array"> </return> <argument index="0" name="polyline" type="PackedVector2Array"> @@ -84,7 +32,7 @@ Clips [code]polyline[/code] against [code]polygon[/code] and returns an array of clipped polylines. This performs [constant OPERATION_DIFFERENCE] between the polyline and the polygon. This operation can be thought of as cutting a line with a closed shape. </description> </method> - <method name="convex_hull_2d"> + <method name="convex_hull"> <return type="PackedVector2Array"> </return> <argument index="0" name="points" type="PackedVector2Array"> @@ -93,7 +41,7 @@ Given an array of [Vector2]s, returns the convex hull as a list of points in counterclockwise order. The last point is the same as the first one. </description> </method> - <method name="exclude_polygons_2d"> + <method name="exclude_polygons"> <return type="Array"> </return> <argument index="0" name="polygon_a" type="PackedVector2Array"> @@ -101,24 +49,11 @@ <argument index="1" name="polygon_b" type="PackedVector2Array"> </argument> <description> - Mutually excludes common area defined by intersection of [code]polygon_a[/code] and [code]polygon_b[/code] (see [method intersect_polygons_2d]) and returns an array of excluded polygons. This performs [constant OPERATION_XOR] between polygons. In other words, returns all but common area between polygons. + Mutually excludes common area defined by intersection of [code]polygon_a[/code] and [code]polygon_b[/code] (see [method intersect_polygons]) and returns an array of excluded polygons. This performs [constant OPERATION_XOR] between polygons. In other words, returns all but common area between polygons. The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. </description> </method> <method name="get_closest_point_to_segment"> - <return type="Vector3"> - </return> - <argument index="0" name="point" type="Vector3"> - </argument> - <argument index="1" name="s1" type="Vector3"> - </argument> - <argument index="2" name="s2" type="Vector3"> - </argument> - <description> - Returns the 3D point on the 3D segment ([code]s1[/code], [code]s2[/code]) that is closest to [code]point[/code]. The returned point will always be inside the specified segment. - </description> - </method> - <method name="get_closest_point_to_segment_2d"> <return type="Vector2"> </return> <argument index="0" name="point" type="Vector2"> @@ -132,19 +67,6 @@ </description> </method> <method name="get_closest_point_to_segment_uncapped"> - <return type="Vector3"> - </return> - <argument index="0" name="point" type="Vector3"> - </argument> - <argument index="1" name="s1" type="Vector3"> - </argument> - <argument index="2" name="s2" type="Vector3"> - </argument> - <description> - Returns the 3D point on the 3D line defined by ([code]s1[/code], [code]s2[/code]) that is closest to [code]point[/code]. The returned point can be inside the segment ([code]s1[/code], [code]s2[/code]) or outside of it, i.e. somewhere on the line extending from the segment. - </description> - </method> - <method name="get_closest_point_to_segment_uncapped_2d"> <return type="Vector2"> </return> <argument index="0" name="point" type="Vector2"> @@ -158,21 +80,6 @@ </description> </method> <method name="get_closest_points_between_segments"> - <return type="PackedVector3Array"> - </return> - <argument index="0" name="p1" type="Vector3"> - </argument> - <argument index="1" name="p2" type="Vector3"> - </argument> - <argument index="2" name="q1" type="Vector3"> - </argument> - <argument index="3" name="q2" type="Vector3"> - </argument> - <description> - Given the two 3D segments ([code]p1[/code], [code]p2[/code]) and ([code]q1[/code], [code]q2[/code]), finds those two points on the two segments that are closest to each other. Returns a [PackedVector3Array] that contains this point on ([code]p1[/code], [code]p2[/code]) as well the accompanying point on ([code]q1[/code], [code]q2[/code]). - </description> - </method> - <method name="get_closest_points_between_segments_2d"> <return type="PackedVector2Array"> </return> <argument index="0" name="p1" type="Vector2"> @@ -187,16 +94,7 @@ Given the two 2D segments ([code]p1[/code], [code]p2[/code]) and ([code]q1[/code], [code]q2[/code]), finds those two points on the two segments that are closest to each other. Returns a [PackedVector2Array] that contains this point on ([code]p1[/code], [code]p2[/code]) as well the accompanying point on ([code]q1[/code], [code]q2[/code]). </description> </method> - <method name="get_uv84_normal_bit"> - <return type="int"> - </return> - <argument index="0" name="normal" type="Vector3"> - </argument> - <description> - Used internally by the engine. - </description> - </method> - <method name="intersect_polygons_2d"> + <method name="intersect_polygons"> <return type="Array"> </return> <argument index="0" name="polygon_a" type="PackedVector2Array"> @@ -208,7 +106,7 @@ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. </description> </method> - <method name="intersect_polyline_with_polygon_2d"> + <method name="intersect_polyline_with_polygon"> <return type="Array"> </return> <argument index="0" name="polyline" type="PackedVector2Array"> @@ -252,7 +150,7 @@ Returns [code]true[/code] if [code]polygon[/code]'s vertices are ordered in clockwise order, otherwise returns [code]false[/code]. </description> </method> - <method name="line_intersects_line_2d"> + <method name="line_intersects_line"> <return type="Variant"> </return> <argument index="0" name="from_a" type="Vector2"> @@ -277,7 +175,7 @@ Given an array of [Vector2]s representing tiles, builds an atlas. The returned dictionary has two keys: [code]points[/code] is a vector of [Vector2] that specifies the positions of each tile, [code]size[/code] contains the overall size of the whole atlas as [Vector2]. </description> </method> - <method name="merge_polygons_2d"> + <method name="merge_polygons"> <return type="Array"> </return> <argument index="0" name="polygon_a" type="PackedVector2Array"> @@ -289,14 +187,14 @@ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. </description> </method> - <method name="offset_polygon_2d"> + <method name="offset_polygon"> <return type="Array"> </return> <argument index="0" name="polygon" type="PackedVector2Array"> </argument> <argument index="1" name="delta" type="float"> </argument> - <argument index="2" name="join_type" type="int" enum="Geometry.PolyJoinType" default="0"> + <argument index="2" name="join_type" type="int" enum="Geometry2D.PolyJoinType" default="0"> </argument> <description> Inflates or deflates [code]polygon[/code] by [code]delta[/code] units (pixels). If [code]delta[/code] is positive, makes the polygon grow outward. If [code]delta[/code] is negative, shrinks the polygon inward. Returns an array of polygons because inflating/deflating may result in multiple discrete polygons. Returns an empty array if [code]delta[/code] is negative and the absolute value of it approximately exceeds the minimum bounding rectangle dimensions of the polygon. @@ -304,16 +202,16 @@ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. </description> </method> - <method name="offset_polyline_2d"> + <method name="offset_polyline"> <return type="Array"> </return> <argument index="0" name="polyline" type="PackedVector2Array"> </argument> <argument index="1" name="delta" type="float"> </argument> - <argument index="2" name="join_type" type="int" enum="Geometry.PolyJoinType" default="0"> + <argument index="2" name="join_type" type="int" enum="Geometry2D.PolyJoinType" default="0"> </argument> - <argument index="3" name="end_type" type="int" enum="Geometry.PolyEndType" default="3"> + <argument index="3" name="end_type" type="int" enum="Geometry2D.PolyEndType" default="3"> </argument> <description> Inflates or deflates [code]polyline[/code] by [code]delta[/code] units (pixels), producing polygons. If [code]delta[/code] is positive, makes the polyline grow outward. Returns an array of polygons because inflating/deflating may result in multiple discrete polygons. If [code]delta[/code] is negative, returns an empty array. @@ -337,67 +235,7 @@ Returns if [code]point[/code] is inside the triangle specified by [code]a[/code], [code]b[/code] and [code]c[/code]. </description> </method> - <method name="ray_intersects_triangle"> - <return type="Variant"> - </return> - <argument index="0" name="from" type="Vector3"> - </argument> - <argument index="1" name="dir" type="Vector3"> - </argument> - <argument index="2" name="a" type="Vector3"> - </argument> - <argument index="3" name="b" type="Vector3"> - </argument> - <argument index="4" name="c" type="Vector3"> - </argument> - <description> - Tests if the 3D ray starting at [code]from[/code] with the direction of [code]dir[/code] intersects the triangle specified by [code]a[/code], [code]b[/code] and [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, an empty [Variant] is returned. - </description> - </method> - <method name="segment_intersects_circle"> - <return type="float"> - </return> - <argument index="0" name="segment_from" type="Vector2"> - </argument> - <argument index="1" name="segment_to" type="Vector2"> - </argument> - <argument index="2" name="circle_position" type="Vector2"> - </argument> - <argument index="3" name="circle_radius" type="float"> - </argument> - <description> - Given the 2D segment ([code]segment_from[/code], [code]segment_to[/code]), returns the position on the segment (as a number between 0 and 1) at which the segment hits the circle that is located at position [code]circle_position[/code] and has radius [code]circle_radius[/code]. If the segment does not intersect the circle, -1 is returned (this is also the case if the line extending the segment would intersect the circle, but the segment does not). - </description> - </method> - <method name="segment_intersects_convex"> - <return type="PackedVector3Array"> - </return> - <argument index="0" name="from" type="Vector3"> - </argument> - <argument index="1" name="to" type="Vector3"> - </argument> - <argument index="2" name="planes" type="Array"> - </argument> - <description> - Given a convex hull defined though the [Plane]s in the array [code]planes[/code], tests if the segment ([code]from[/code], [code]to[/code]) intersects with that hull. If an intersection is found, returns a [PackedVector3Array] containing the point the intersection and the hull's normal. If no intersecion is found, an the returned array is empty. - </description> - </method> - <method name="segment_intersects_cylinder"> - <return type="PackedVector3Array"> - </return> - <argument index="0" name="from" type="Vector3"> - </argument> - <argument index="1" name="to" type="Vector3"> - </argument> - <argument index="2" name="height" type="float"> - </argument> - <argument index="3" name="radius" type="float"> - </argument> - <description> - Checks if the segment ([code]from[/code], [code]to[/code]) intersects the cylinder with height [code]height[/code] that is centered at the origin and has radius [code]radius[/code]. If no, returns an empty [PackedVector3Array]. If an intersection takes place, the returned array contains the point of intersection and the cylinder's normal at the point of intersection. - </description> - </method> - <method name="segment_intersects_segment_2d"> + <method name="segment_intersects_segment"> <return type="Variant"> </return> <argument index="0" name="from_a" type="Vector2"> @@ -412,39 +250,7 @@ Checks if the two segments ([code]from_a[/code], [code]to_a[/code]) and ([code]from_b[/code], [code]to_b[/code]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns an empty [Variant]. </description> </method> - <method name="segment_intersects_sphere"> - <return type="PackedVector3Array"> - </return> - <argument index="0" name="from" type="Vector3"> - </argument> - <argument index="1" name="to" type="Vector3"> - </argument> - <argument index="2" name="sphere_position" type="Vector3"> - </argument> - <argument index="3" name="sphere_radius" type="float"> - </argument> - <description> - Checks if the segment ([code]from[/code], [code]to[/code]) intersects the sphere that is located at [code]sphere_position[/code] and has radius [code]sphere_radius[/code]. If no, returns an empty [PackedVector3Array]. If yes, returns a [PackedVector3Array] containing the point of intersection and the sphere's normal at the point of intersection. - </description> - </method> - <method name="segment_intersects_triangle"> - <return type="Variant"> - </return> - <argument index="0" name="from" type="Vector3"> - </argument> - <argument index="1" name="to" type="Vector3"> - </argument> - <argument index="2" name="a" type="Vector3"> - </argument> - <argument index="3" name="b" type="Vector3"> - </argument> - <argument index="4" name="c" type="Vector3"> - </argument> - <description> - Tests if the segment ([code]from[/code], [code]to[/code]) intersects the triangle [code]a[/code], [code]b[/code], [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, an empty [Variant] is returned. - </description> - </method> - <method name="triangulate_delaunay_2d"> + <method name="triangulate_delaunay"> <return type="PackedInt32Array"> </return> <argument index="0" name="points" type="PackedVector2Array"> diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml new file mode 100644 index 0000000000..ec685a5ce7 --- /dev/null +++ b/doc/classes/Geometry3D.xml @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="Geometry3D" inherits="Object" version="4.0"> + <brief_description> + Helper node to calculate generic geometry operations in 3D space. + </brief_description> + <description> + Geometry3D provides users with a set of helper functions to create geometric shapes, compute intersections between shapes, and process various other geometric operations. + </description> + <tutorials> + </tutorials> + <methods> + <method name="build_box_planes"> + <return type="Array"> + </return> + <argument index="0" name="extents" type="Vector3"> + </argument> + <description> + Returns an array with 6 [Plane]s that describe the sides of a box centered at the origin. The box size is defined by [code]extents[/code], which represents one (positive) corner of the box (i.e. half its actual size). + </description> + </method> + <method name="build_capsule_planes"> + <return type="Array"> + </return> + <argument index="0" name="radius" type="float"> + </argument> + <argument index="1" name="height" type="float"> + </argument> + <argument index="2" name="sides" type="int"> + </argument> + <argument index="3" name="lats" type="int"> + </argument> + <argument index="4" name="axis" type="int" enum="Vector3.Axis" default="2"> + </argument> + <description> + Returns an array of [Plane]s closely bounding a faceted capsule centered at the origin with radius [code]radius[/code] and height [code]height[/code]. The parameter [code]sides[/code] defines how many planes will be generated for the side part of the capsule, whereas [code]lats[/code] gives the number of latitudinal steps at the bottom and top of the capsule. The parameter [code]axis[/code] describes the axis along which the capsule is oriented (0 for X, 1 for Y, 2 for Z). + </description> + </method> + <method name="build_cylinder_planes"> + <return type="Array"> + </return> + <argument index="0" name="radius" type="float"> + </argument> + <argument index="1" name="height" type="float"> + </argument> + <argument index="2" name="sides" type="int"> + </argument> + <argument index="3" name="axis" type="int" enum="Vector3.Axis" default="2"> + </argument> + <description> + Returns an array of [Plane]s closely bounding a faceted cylinder centered at the origin with radius [code]radius[/code] and height [code]height[/code]. The parameter [code]sides[/code] defines how many planes will be generated for the round part of the cylinder. The parameter [code]axis[/code] describes the axis along which the cylinder is oriented (0 for X, 1 for Y, 2 for Z). + </description> + </method> + <method name="clip_polygon"> + <return type="PackedVector3Array"> + </return> + <argument index="0" name="points" type="PackedVector3Array"> + </argument> + <argument index="1" name="plane" type="Plane"> + </argument> + <description> + Clips the polygon defined by the points in [code]points[/code] against the [code]plane[/code] and returns the points of the clipped polygon. + </description> + </method> + <method name="get_closest_point_to_segment"> + <return type="Vector3"> + </return> + <argument index="0" name="point" type="Vector3"> + </argument> + <argument index="1" name="s1" type="Vector3"> + </argument> + <argument index="2" name="s2" type="Vector3"> + </argument> + <description> + Returns the 3D point on the 3D segment ([code]s1[/code], [code]s2[/code]) that is closest to [code]point[/code]. The returned point will always be inside the specified segment. + </description> + </method> + <method name="get_closest_point_to_segment_uncapped"> + <return type="Vector3"> + </return> + <argument index="0" name="point" type="Vector3"> + </argument> + <argument index="1" name="s1" type="Vector3"> + </argument> + <argument index="2" name="s2" type="Vector3"> + </argument> + <description> + Returns the 3D point on the 3D line defined by ([code]s1[/code], [code]s2[/code]) that is closest to [code]point[/code]. The returned point can be inside the segment ([code]s1[/code], [code]s2[/code]) or outside of it, i.e. somewhere on the line extending from the segment. + </description> + </method> + <method name="get_closest_points_between_segments"> + <return type="PackedVector3Array"> + </return> + <argument index="0" name="p1" type="Vector3"> + </argument> + <argument index="1" name="p2" type="Vector3"> + </argument> + <argument index="2" name="q1" type="Vector3"> + </argument> + <argument index="3" name="q2" type="Vector3"> + </argument> + <description> + Given the two 3D segments ([code]p1[/code], [code]p2[/code]) and ([code]q1[/code], [code]q2[/code]), finds those two points on the two segments that are closest to each other. Returns a [PackedVector3Array] that contains this point on ([code]p1[/code], [code]p2[/code]) as well the accompanying point on ([code]q1[/code], [code]q2[/code]). + </description> + </method> + <method name="get_uv84_normal_bit"> + <return type="int"> + </return> + <argument index="0" name="normal" type="Vector3"> + </argument> + <description> + Used internally by the engine. + </description> + </method> + <method name="ray_intersects_triangle"> + <return type="Variant"> + </return> + <argument index="0" name="from" type="Vector3"> + </argument> + <argument index="1" name="dir" type="Vector3"> + </argument> + <argument index="2" name="a" type="Vector3"> + </argument> + <argument index="3" name="b" type="Vector3"> + </argument> + <argument index="4" name="c" type="Vector3"> + </argument> + <description> + Tests if the 3D ray starting at [code]from[/code] with the direction of [code]dir[/code] intersects the triangle specified by [code]a[/code], [code]b[/code] and [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, an empty [Variant] is returned. + </description> + </method> + <method name="segment_intersects_convex"> + <return type="PackedVector3Array"> + </return> + <argument index="0" name="from" type="Vector3"> + </argument> + <argument index="1" name="to" type="Vector3"> + </argument> + <argument index="2" name="planes" type="Array"> + </argument> + <description> + Given a convex hull defined though the [Plane]s in the array [code]planes[/code], tests if the segment ([code]from[/code], [code]to[/code]) intersects with that hull. If an intersection is found, returns a [PackedVector3Array] containing the point the intersection and the hull's normal. If no intersecion is found, an the returned array is empty. + </description> + </method> + <method name="segment_intersects_cylinder"> + <return type="PackedVector3Array"> + </return> + <argument index="0" name="from" type="Vector3"> + </argument> + <argument index="1" name="to" type="Vector3"> + </argument> + <argument index="2" name="height" type="float"> + </argument> + <argument index="3" name="radius" type="float"> + </argument> + <description> + Checks if the segment ([code]from[/code], [code]to[/code]) intersects the cylinder with height [code]height[/code] that is centered at the origin and has radius [code]radius[/code]. If no, returns an empty [PackedVector3Array]. If an intersection takes place, the returned array contains the point of intersection and the cylinder's normal at the point of intersection. + </description> + </method> + <method name="segment_intersects_sphere"> + <return type="PackedVector3Array"> + </return> + <argument index="0" name="from" type="Vector3"> + </argument> + <argument index="1" name="to" type="Vector3"> + </argument> + <argument index="2" name="sphere_position" type="Vector3"> + </argument> + <argument index="3" name="sphere_radius" type="float"> + </argument> + <description> + Checks if the segment ([code]from[/code], [code]to[/code]) intersects the sphere that is located at [code]sphere_position[/code] and has radius [code]sphere_radius[/code]. If no, returns an empty [PackedVector3Array]. If yes, returns a [PackedVector3Array] containing the point of intersection and the sphere's normal at the point of intersection. + </description> + </method> + <method name="segment_intersects_triangle"> + <return type="Variant"> + </return> + <argument index="0" name="from" type="Vector3"> + </argument> + <argument index="1" name="to" type="Vector3"> + </argument> + <argument index="2" name="a" type="Vector3"> + </argument> + <argument index="3" name="b" type="Vector3"> + </argument> + <argument index="4" name="c" type="Vector3"> + </argument> + <description> + Tests if the segment ([code]from[/code], [code]to[/code]) intersects the triangle [code]a[/code], [code]b[/code], [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, an empty [Variant] is returned. + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 55d2275194..aa8c8d2443 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -436,6 +436,12 @@ Saves the image as a PNG file to [code]path[/code]. </description> </method> + <method name="save_png_to_buffer" qualifiers="const"> + <return type="PackedByteArray"> + </return> + <description> + </description> + </method> <method name="set_pixel"> <return type="void"> </return> diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index 9da739e57a..d7821b1045 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -864,28 +864,28 @@ Creates a damped spring joint between two bodies. If not specified, the second body is assumed to be the joint itself. </description> </method> - <method name="damped_string_joint_get_param" qualifiers="const"> + <method name="damped_spring_joint_get_param" qualifiers="const"> <return type="float"> </return> <argument index="0" name="joint" type="RID"> </argument> - <argument index="1" name="param" type="int" enum="PhysicsServer2D.DampedStringParam"> + <argument index="1" name="param" type="int" enum="PhysicsServer2D.DampedSpringParam"> </argument> <description> - Returns the value of a damped spring joint parameter. + Returns the value of a damped spring joint parameter. See [enum DampedSpringParam] for a list of available parameters. </description> </method> - <method name="damped_string_joint_set_param"> + <method name="damped_spring_joint_set_param"> <return type="void"> </return> <argument index="0" name="joint" type="RID"> </argument> - <argument index="1" name="param" type="int" enum="PhysicsServer2D.DampedStringParam"> + <argument index="1" name="param" type="int" enum="PhysicsServer2D.DampedSpringParam"> </argument> <argument index="2" name="value" type="float"> </argument> <description> - Sets a damped spring joint parameter. See [enum DampedStringParam] for a list of available parameters. + Sets a damped spring joint parameter. See [enum DampedSpringParam] for a list of available parameters. </description> </method> <method name="free_rid"> @@ -1247,13 +1247,13 @@ </constant> <constant name="JOINT_PARAM_MAX_FORCE" value="2" enum="JointParam"> </constant> - <constant name="DAMPED_STRING_REST_LENGTH" value="0" enum="DampedStringParam"> + <constant name="DAMPED_SPRING_REST_LENGTH" value="0" enum="DampedSpringParam"> Sets the resting length of the spring joint. The joint will always try to go to back this length when pulled apart. </constant> - <constant name="DAMPED_STRING_STIFFNESS" value="1" enum="DampedStringParam"> + <constant name="DAMPED_SPRING_STIFFNESS" value="1" enum="DampedSpringParam"> Sets the stiffness of the spring joint. The joint applies a force equal to the stiffness times the distance from its resting length. </constant> - <constant name="DAMPED_STRING_DAMPING" value="2" enum="DampedStringParam"> + <constant name="DAMPED_SPRING_DAMPING" value="2" enum="DampedSpringParam"> Sets the damping ratio of the spring joint. A value of 0 indicates an undamped spring, while 1 causes the system to reach equilibrium as fast as possible (critical damping). </constant> <constant name="CCD_MODE_DISABLED" value="0" enum="CCDMode"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index daf8cb1d2f..92e5b4a84f 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -137,7 +137,7 @@ <argument index="0" name="file" type="String"> </argument> <description> - Saves the configuration to a custom file. + Saves the configuration to a custom file. The file extension must be [code].godot[/code] (to save in text-based [ConfigFile] format) or [code].binary[/code] (to save in binary format). </description> </method> <method name="set_initial_value"> diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml index 2e8be384c1..f3a7ba0476 100644 --- a/doc/classes/Rect2i.xml +++ b/doc/classes/Rect2i.xml @@ -46,7 +46,129 @@ Constructs a new [Rect2i] from [Rect2]. The floating point coordinates will be truncated. </description> </method> + <method name="abs"> + <return type="Rect2i"> + </return> + <description> + Returns a [Rect2i] with equivalent position and area, modified so that the top-left corner is the origin and [code]width[/code] and [code]height[/code] are positive. + </description> + </method> + <method name="clip"> + <return type="Rect2i"> + </return> + <argument index="0" name="b" type="Rect2i"> + </argument> + <description> + Returns the intersection of this [Rect2i] and b. + </description> + </method> + <method name="encloses"> + <return type="bool"> + </return> + <argument index="0" name="b" type="Rect2i"> + </argument> + <description> + Returns [code]true[/code] if this [Rect2i] completely encloses another one. + </description> + </method> + <method name="expand"> + <return type="Rect2i"> + </return> + <argument index="0" name="to" type="Vector2i"> + </argument> + <description> + Returns this [Rect2i] expanded to include a given point. + </description> + </method> + <method name="get_area"> + <return type="int"> + </return> + <description> + Returns the area of the [Rect2i]. + </description> + </method> + <method name="grow"> + <return type="Rect2i"> + </return> + <argument index="0" name="by" type="int"> + </argument> + <description> + Returns a copy of the [Rect2i] grown a given amount of units towards all the sides. + </description> + </method> + <method name="grow_individual"> + <return type="Rect2i"> + </return> + <argument index="0" name="left" type="int"> + </argument> + <argument index="1" name="top" type="int"> + </argument> + <argument index="2" name="right" type="int"> + </argument> + <argument index="3" name=" bottom" type="int"> + </argument> + <description> + Returns a copy of the [Rect2i] grown a given amount of units towards each direction individually. + </description> + </method> + <method name="grow_margin"> + <return type="Rect2i"> + </return> + <argument index="0" name="margin" type="int"> + </argument> + <argument index="1" name="by" type="int"> + </argument> + <description> + Returns a copy of the [Rect2i] grown a given amount of units towards the [enum Margin] direction. + </description> + </method> + <method name="has_no_area"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if the [Rect2i] is flat or empty. + </description> + </method> + <method name="has_point"> + <return type="bool"> + </return> + <argument index="0" name="point" type="Vector2i"> + </argument> + <description> + Returns [code]true[/code] if the [Rect2i] contains a point. + </description> + </method> + <method name="intersects"> + <return type="bool"> + </return> + <argument index="0" name="b" type="Rect2i"> + </argument> + <description> + Returns [code]true[/code] if the [Rect2i] overlaps with [code]b[/code] (i.e. they have at least one point in common). + If [code]include_borders[/code] is [code]true[/code], they will also be considered overlapping if their borders touch, even without intersection. + </description> + </method> + <method name="merge"> + <return type="Rect2i"> + </return> + <argument index="0" name="b" type="Rect2i"> + </argument> + <description> + Returns a larger [Rect2i] that contains this [Rect2i] and [code]b[/code]. + </description> + </method> </methods> + <members> + <member name="end" type="Vector2i" setter="" getter="" default="Vector2i( 0, 0 )"> + Ending corner. + </member> + <member name="position" type="Vector2i" setter="" getter="" default="Vector2i( 0, 0 )"> + Position (starting corner). + </member> + <member name="size" type="Vector2i" setter="" getter="" default="Vector2i( 0, 0 )"> + Size from position to end. + </member> + </members> <constants> </constants> </class> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index efc0c9d600..db036d7d88 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -20,8 +20,10 @@ </argument> <argument index="2" name="height" type="int" default="0"> </argument> + <argument index="3" name="color" type="Color" default="Color( 1, 1, 1, 1 )"> + </argument> <description> - Adds an image's opening and closing tags to the tag stack, optionally providing a [code]width[/code] and [code]height[/code] to resize the image. + Adds an image's opening and closing tags to the tag stack, optionally providing a [code]width[/code] and [code]height[/code] to resize the image and a [code]color[/code] to tint the image. If [code]width[/code] or [code]height[/code] is set to 0, the image size will be adjusted in order to keep the original aspect ratio. </description> </method> diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index 4460b519fc..640cbe84f5 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -106,6 +106,12 @@ Returns the pose transform of the specified bone. Pose is applied on top of the custom pose, which is applied on top the rest pose. </description> </method> + <method name="get_bone_process_orders"> + <return type="PackedInt32Array"> + </return> + <description> + </description> + </method> <method name="get_bone_rest" qualifiers="const"> <return type="Transform"> </return> @@ -268,6 +274,12 @@ <member name="animate_physical_bones" type="bool" setter="set_animate_physical_bones" getter="get_animate_physical_bones" default="true"> </member> </members> + <signals> + <signal name="pose_updated"> + <description> + </description> + </signal> + </signals> <constants> <constant name="NOTIFICATION_UPDATE_SKELETON" value="50"> </constant> diff --git a/doc/classes/Vector2i.xml b/doc/classes/Vector2i.xml index a516eb01dd..71c7aaa4e5 100644 --- a/doc/classes/Vector2i.xml +++ b/doc/classes/Vector2i.xml @@ -31,7 +31,36 @@ Constructs a new [Vector2i] from [Vector2]. The floating point coordinates will be truncated. </description> </method> + <method name="abs"> + <return type="Vector2i"> + </return> + <description> + Returns a new vector with all components in absolute values (i.e. positive). + </description> + </method> + <method name="aspect"> + <return type="float"> + </return> + <description> + Returns the ratio of [member x] to [member y]. + </description> + </method> + <method name="sign"> + <return type="Vector2i"> + </return> + <description> + Returns the vector with each component set to one or negative one, depending on the signs of the components. + </description> + </method> </methods> + <members> + <member name="x" type="int" setter="" getter="" default="0"> + The vector's X component. Also accessible by using the index position [code][0][/code]. + </member> + <member name="y" type="int" setter="" getter="" default="0"> + The vector's Y component. Also accessible by using the index position [code][1][/code]. + </member> + </members> <constants> <constant name="AXIS_X" value="0"> Enumerated value for the X axis. diff --git a/doc/classes/Vector3i.xml b/doc/classes/Vector3i.xml index 4f5a658b89..c5aa3d0347 100644 --- a/doc/classes/Vector3i.xml +++ b/doc/classes/Vector3i.xml @@ -33,7 +33,39 @@ Constructs a new [Vector3i] from [Vector3]. The floating point coordinates will be truncated. </description> </method> + <method name="max_axis"> + <return type="int"> + </return> + <description> + Returns the axis of the vector's largest value. See [code]AXIS_*[/code] constants. + </description> + </method> + <method name="min_axis"> + <return type="int"> + </return> + <description> + Returns the axis of the vector's smallest value. See [code]AXIS_*[/code] constants. + </description> + </method> + <method name="sign"> + <return type="Vector3i"> + </return> + <description> + Returns the vector with each component set to one or negative one, depending on the signs of the components. + </description> + </method> </methods> + <members> + <member name="x" type="int" setter="" getter="" default="0"> + The vector's X component. Also accessible by using the index position [code][0][/code]. + </member> + <member name="y" type="int" setter="" getter="" default="0"> + The vector's Y component. Also accessible by using the index position [code][1][/code]. + </member> + <member name="z" type="int" setter="" getter="" default="0"> + The vector's Z component. Also accessible by using the index position [code][2][/code]. + </member> + </members> <constants> <constant name="AXIS_X" value="0"> Enumerated value for the X axis. diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index a14ef7c665..c0bba38799 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -287,6 +287,8 @@ def main(): # type: () -> None ) args = parser.parse_args() + print("Checking for errors in the XML class reference...") + file_list = [] # type: List[str] for path in args.path: @@ -345,7 +347,10 @@ def main(): # type: () -> None state.current_class = class_name make_rst_class(class_def, state, args.dry_run, args.output) - if state.errored: + if not state.errored: + print("No errors found.") + else: + print("Errors were found in the class reference XML. Please check the messages above.") exit(1) @@ -561,71 +566,6 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S index += 1 -def make_class_list(class_list, columns): # type: (List[str], int) -> None - # This function is no longer used. - f = open("class_list.rst", "w", encoding="utf-8") - col_max = len(class_list) // columns + 1 - print(("col max is ", col_max)) - fit_columns = [] # type: List[List[str]] - - for _ in range(0, columns): - fit_columns.append([]) - - indexers = [] # type List[str] - last_initial = "" - - for idx, name in enumerate(class_list): - col = idx // col_max - if col >= columns: - col = columns - 1 - fit_columns[col].append(name) - idx += 1 - if name[:1] != last_initial: - indexers.append(name) - last_initial = name[:1] - - row_max = 0 - f.write("\n") - - for n in range(0, columns): - if len(fit_columns[n]) > row_max: - row_max = len(fit_columns[n]) - - f.write("| ") - for n in range(0, columns): - f.write(" | |") - - f.write("\n") - f.write("+") - for n in range(0, columns): - f.write("--+-------+") - f.write("\n") - - for r in range(0, row_max): - s = "+ " - for c in range(0, columns): - if r >= len(fit_columns[c]): - continue - - classname = fit_columns[c][r] - initial = classname[0] - if classname in indexers: - s += "**" + initial + "** | " - else: - s += " | " - - s += "[" + classname + "](class_" + classname.lower() + ") | " - - s += "\n" - f.write(s) - - for n in range(0, columns): - f.write("--+-------+") - f.write("\n") - - f.close() - - def escape_rst(text, until_pos=-1): # type: (str) -> str # Escape \ character, otherwise it ends up as an escape character in rst pos = 0 diff --git a/doc/translations/classes.pot b/doc/translations/classes.pot index 28db7e4e04..c18fecc6f8 100644 --- a/doc/translations/classes.pot +++ b/doc/translations/classes.pot @@ -35842,7 +35842,7 @@ msgstr "" #: doc/classes/PhysicsServer2D.xml:888 msgid "" -"Sets a damped spring joint parameter. See [enum DampedStringParam] for a " +"Sets a damped spring joint parameter. See [enum DampedSpringParam] for a " "list of available parameters." msgstr "" diff --git a/doc/translations/fr.po b/doc/translations/fr.po index 57466d0b04..8010437cf7 100644 --- a/doc/translations/fr.po +++ b/doc/translations/fr.po @@ -35852,7 +35852,7 @@ msgstr "" #: doc/classes/PhysicsServer2D.xml:888 msgid "" -"Sets a damped spring joint parameter. See [enum DampedStringParam] for a " +"Sets a damped spring joint parameter. See [enum DampedSpringParam] for a " "list of available parameters." msgstr "" diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index 9b57f417cb..2e36de5c79 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -262,7 +262,7 @@ void ShaderCompilerGLES2::_dump_function_deps(SL::ShaderNode *p_node, const Stri } } -String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning) { +String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_use_scope) { StringBuilder code; switch (p_node->type) { @@ -626,7 +626,7 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener if (arr_node->call_expression != nullptr) { code += "."; - code += _dump_node_code(arr_node->call_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += _dump_node_code(arr_node->call_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning, false); } if (arr_node->index_expression != nullptr) { @@ -822,13 +822,17 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener } break; default: { - code += "("; + if (p_use_scope) { + code += "("; + } code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); code += " "; code += _opstr(op_node->op); code += " "; code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ")"; + if (p_use_scope) { + code += ")"; + } } break; } } break; diff --git a/drivers/gles2/shader_compiler_gles2.h b/drivers/gles2/shader_compiler_gles2.h index 369bf7877b..66a3af0739 100644 --- a/drivers/gles2/shader_compiler_gles2.h +++ b/drivers/gles2/shader_compiler_gles2.h @@ -75,7 +75,7 @@ private: }; void _dump_function_deps(ShaderLanguage::ShaderNode *p_node, const StringName &p_for_func, const Map<StringName, String> &p_func_code, StringBuilder &r_to_add, Set<StringName> &r_added); - String _dump_node_code(ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning); + String _dump_node_code(ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_use_scope = true); StringName current_func_name; StringName vertex_name; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index efd4f057fd..c9e78678a8 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -35,6 +35,7 @@ #include "core/os/os.h" #include "core/project_settings.h" #include "drivers/vulkan/vulkan_context.h" + #include "thirdparty/spirv-reflect/spirv_reflect.h" //#define FORCE_FULL_BARRIER @@ -5905,7 +5906,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI if (pipeline->push_constant_size) { dl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; #ifdef DEBUG_ENABLED - dl->validation.pipeline_push_constant_suppplied = false; + dl->validation.pipeline_push_constant_supplied = false; #endif } @@ -6036,7 +6037,7 @@ void RenderingDeviceVulkan::draw_list_set_push_constant(DrawListID p_list, const #endif vkCmdPushConstants(dl->command_buffer, dl->state.pipeline_layout, dl->state.pipeline_push_constant_stages, 0, p_data_size, p_data); #ifdef DEBUG_ENABLED - dl->validation.pipeline_push_constant_suppplied = true; + dl->validation.pipeline_push_constant_supplied = true; #endif } @@ -6064,7 +6065,7 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices if (dl->validation.pipeline_push_constant_size > 0) { //using push constants, check that they were supplied - ERR_FAIL_COND_MSG(!dl->validation.pipeline_push_constant_suppplied, + ERR_FAIL_COND_MSG(!dl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } @@ -6300,7 +6301,7 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l if (pipeline->push_constant_size) { cl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; #ifdef DEBUG_ENABLED - cl->validation.pipeline_push_constant_suppplied = false; + cl->validation.pipeline_push_constant_supplied = false; #endif } @@ -6433,7 +6434,7 @@ void RenderingDeviceVulkan::compute_list_set_push_constant(ComputeListID p_list, #endif vkCmdPushConstants(cl->command_buffer, cl->state.pipeline_layout, cl->state.pipeline_push_constant_stages, 0, p_data_size, p_data); #ifdef DEBUG_ENABLED - cl->validation.pipeline_push_constant_suppplied = true; + cl->validation.pipeline_push_constant_supplied = true; #endif } @@ -6460,7 +6461,7 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t if (cl->validation.pipeline_push_constant_size > 0) { //using push constants, check that they were supplied - ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_suppplied, + ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 6b75154a8d..eac3250bf7 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -42,7 +42,9 @@ #endif #endif #include "vk_mem_alloc.h" + #include <vulkan/vulkan.h> + //todo: //compute //push constants @@ -202,15 +204,10 @@ class RenderingDeviceVulkan : public RenderingDevice { Error _insert_staging_block(); struct Buffer { - uint32_t size; - VkBuffer buffer; - VmaAllocation allocation; + uint32_t size = 0; + VkBuffer buffer = VK_NULL_HANDLE; + VmaAllocation allocation = nullptr; VkDescriptorBufferInfo buffer_info; //used for binding - Buffer() { - size = 0; - buffer = VK_NULL_HANDLE; - allocation = nullptr; - } }; Error _buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mapping); @@ -570,7 +567,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct DescriptorPoolKey { union { struct { - uint16_t uniform_type[UNIFORM_TYPE_MAX]; //using 16 bits because, for sending arrays, each element is a pool set. + uint16_t uniform_type[UNIFORM_TYPE_MAX]; // Using 16 bits because, for sending arrays, each element is a pool set. }; struct { uint64_t key1; @@ -712,106 +709,67 @@ class RenderingDeviceVulkan : public RenderingDevice { Vector<SplitDrawListAllocator> split_draw_list_allocators; struct DrawList { - VkCommandBuffer command_buffer; //if persistent, this is owned, otherwise it's shared with the ringbuffer + VkCommandBuffer command_buffer; // If persistent, this is owned, otherwise it's shared with the ringbuffer. Rect2i viewport; struct SetState { - uint32_t pipeline_expected_format; - uint32_t uniform_set_format; - VkDescriptorSet descriptor_set; + uint32_t pipeline_expected_format = 0; + uint32_t uniform_set_format = 0; + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; RID uniform_set; - bool bound; - SetState() { - bound = false; - pipeline_expected_format = 0; - uniform_set_format = 0; - descriptor_set = VK_NULL_HANDLE; - } + bool bound = false; }; struct State { SetState sets[MAX_UNIFORM_SETS]; - uint32_t set_count; + uint32_t set_count = 0; RID pipeline; RID pipeline_shader; - VkPipelineLayout pipeline_layout; + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; RID vertex_array; RID index_array; - uint32_t pipeline_push_constant_stages; - - State() { - set_count = 0; - pipeline_layout = VK_NULL_HANDLE; - pipeline_push_constant_stages = 0; - } + uint32_t pipeline_push_constant_stages = 0; } state; -#ifdef DEBUG_ENABLED +#ifdef DEBUG_ENABLED struct Validation { - bool active; //means command buffer was not closes, so you can keep adding things - FramebufferFormatID framebuffer_format; - //actual render pass values - uint32_t dynamic_state; - VertexFormatID vertex_format; //INVALID_ID if not set - uint32_t vertex_array_size; //0 if not set - uint32_t vertex_max_instances_allowed; - bool index_buffer_uses_restart_indices; - uint32_t index_array_size; //0 if index buffer not set - uint32_t index_array_max_index; + bool active = true; // Means command buffer was not closed, so you can keep adding things. + FramebufferFormatID framebuffer_format = INVALID_ID; + // Actual render pass values. + uint32_t dynamic_state = 0; + VertexFormatID vertex_format = INVALID_ID; + uint32_t vertex_array_size = 0; + uint32_t vertex_max_instances_allowed = 0xFFFFFFFF; + bool index_buffer_uses_restart_indices = false; + uint32_t index_array_size = 0; + uint32_t index_array_max_index = 0; uint32_t index_array_offset; Vector<uint32_t> set_formats; Vector<bool> set_bound; Vector<RID> set_rids; - //last pipeline set values - bool pipeline_active; - uint32_t pipeline_dynamic_state; - VertexFormatID pipeline_vertex_format; + // Last pipeline set values. + bool pipeline_active = false; + uint32_t pipeline_dynamic_state = 0; + VertexFormatID pipeline_vertex_format = INVALID_ID; RID pipeline_shader; - uint32_t invalid_set_from; - bool pipeline_uses_restart_indices; + uint32_t invalid_set_from = 0; + bool pipeline_uses_restart_indices = false; uint32_t pipeline_primitive_divisor; uint32_t pipeline_primitive_minimum; Vector<uint32_t> pipeline_set_formats; - uint32_t pipeline_push_constant_size; - bool pipeline_push_constant_suppplied; - - Validation() { - active = true; - dynamic_state = 0; - vertex_format = INVALID_ID; - vertex_array_size = 0; - vertex_max_instances_allowed = 0xFFFFFFFF; - framebuffer_format = INVALID_ID; - index_array_size = 0; //not sent - index_array_max_index = 0; //not set - index_buffer_uses_restart_indices = false; - invalid_set_from = 0; - - //pipeline state initalize - pipeline_active = false; - pipeline_dynamic_state = 0; - pipeline_vertex_format = INVALID_ID; - pipeline_uses_restart_indices = false; - pipeline_push_constant_size = 0; - pipeline_push_constant_suppplied = false; - } + uint32_t pipeline_push_constant_size = 0; + bool pipeline_push_constant_supplied = false; } validation; #else struct Validation { - uint32_t vertex_array_size; //0 if not set - uint32_t index_array_size; //0 if index buffer not set + uint32_t vertex_array_size = 0; + uint32_t index_array_size = 0; uint32_t index_array_offset; - - Validation() { - vertex_array_size = 0; - index_array_size = 0; //not sent - } } validation; - #endif }; - DrawList *draw_list; //one for regular draw lists, multiple for split. + DrawList *draw_list; // One for regular draw lists, multiple for split. uint32_t draw_list_count; bool draw_list_split; Vector<RID> draw_list_bound_textures; @@ -828,62 +786,39 @@ class RenderingDeviceVulkan : public RenderingDevice { /**********************/ struct ComputeList { - VkCommandBuffer command_buffer; //if persistent, this is owned, otherwise it's shared with the ringbuffer + VkCommandBuffer command_buffer; // If persistent, this is owned, otherwise it's shared with the ringbuffer. struct SetState { - uint32_t pipeline_expected_format; - uint32_t uniform_set_format; - VkDescriptorSet descriptor_set; + uint32_t pipeline_expected_format = 0; + uint32_t uniform_set_format = 0; + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; RID uniform_set; - bool bound; - SetState() { - bound = false; - pipeline_expected_format = 0; - uniform_set_format = 0; - descriptor_set = VK_NULL_HANDLE; - } + bool bound = false; }; struct State { Set<Texture *> textures_to_sampled_layout; - SetState sets[MAX_UNIFORM_SETS]; - uint32_t set_count; + uint32_t set_count = 0; RID pipeline; RID pipeline_shader; - VkPipelineLayout pipeline_layout; - uint32_t pipeline_push_constant_stages; - - State() { - set_count = 0; - pipeline_layout = VK_NULL_HANDLE; - pipeline_push_constant_stages = 0; - } + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; + uint32_t pipeline_push_constant_stages = 0; } state; -#ifdef DEBUG_ENABLED +#ifdef DEBUG_ENABLED struct Validation { - bool active; //means command buffer was not closes, so you can keep adding things + bool active = true; // Means command buffer was not closed, so you can keep adding things. Vector<uint32_t> set_formats; Vector<bool> set_bound; Vector<RID> set_rids; - //last pipeline set values - bool pipeline_active; + // Last pipeline set values. + bool pipeline_active = false; RID pipeline_shader; - uint32_t invalid_set_from; + uint32_t invalid_set_from = 0; Vector<uint32_t> pipeline_set_formats; - uint32_t pipeline_push_constant_size; - bool pipeline_push_constant_suppplied; - - Validation() { - active = true; - invalid_set_from = 0; - - //pipeline state initalize - pipeline_active = false; - pipeline_push_constant_size = 0; - pipeline_push_constant_suppplied = false; - } + uint32_t pipeline_push_constant_size = 0; + bool pipeline_push_constant_supplied = false; } validation; #endif }; diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index ff1215ff5d..3f4cfac123 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -80,29 +80,15 @@ class VulkanContext { } SwapchainImageResources; struct Window { - bool is_minimzed; - VkSurfaceKHR surface; - VkSwapchainKHR swapchain; - SwapchainImageResources *swapchain_image_resources; - VkPresentModeKHR presentMode; - uint32_t current_buffer; - int width; - int height; + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkSwapchainKHR swapchain = VK_NULL_HANDLE; + SwapchainImageResources *swapchain_image_resources = VK_NULL_HANDLE; + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + uint32_t current_buffer = 0; + int width = 0; + int height = 0; VkCommandPool present_cmd_pool; //for separate present queue - - VkRenderPass render_pass; - - Window() { - width = 0; - height = 0; - render_pass = VK_NULL_HANDLE; - current_buffer = 0; - surface = VK_NULL_HANDLE; - swapchain_image_resources = VK_NULL_HANDLE; - swapchain = VK_NULL_HANDLE; - is_minimzed = false; - presentMode = VK_PRESENT_MODE_FIFO_KHR; - } + VkRenderPass render_pass = VK_NULL_HANDLE; }; struct LocalDevice { diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index d0f06ae4c4..2653ac1cdb 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -286,10 +286,6 @@ Error DirAccessWindows::remove(String p_path) { p_path = fix_path(p_path); - printf("erasing %s\n", p_path.utf8().get_data()); - //WIN32_FILE_ATTRIBUTE_DATA fileInfo; - //DWORD fileAttr = GetFileAttributesExW(p_path.c_str(), GetFileExInfoStandard, &fileInfo); - DWORD fileAttr; fileAttr = GetFileAttributesW(p_path.c_str()); diff --git a/editor/SCsub b/editor/SCsub index 13ae85bbf0..651dd5fffd 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -6,6 +6,7 @@ env.editor_sources = [] import os import os.path +import glob from platform_methods import run_in_subprocess import editor_builders @@ -40,20 +41,21 @@ if env["tools"]: f.write(reg_exporters_inc) f.write(reg_exporters) - # API documentation + # Core API documentation. docs = [] - doc_dirs = ["doc/classes"] + docs += Glob("#doc/classes/*.xml") - for p in env.doc_class_path.values(): - if p not in doc_dirs: - doc_dirs.append(p) + # Module API documentation. + module_dirs = [] + for d in env.doc_class_path.values(): + if d not in module_dirs: + module_dirs.append(d) - for d in doc_dirs: - try: - for f in os.listdir(os.path.join(env.Dir("#").abspath, d)): - docs.append("#" + os.path.join(d, f)) - except OSError: - pass + for d in module_dirs: + if not os.path.isabs(d): + docs += Glob("#" + d + "/*.xml") # Built-in. + else: + docs += Glob(d + "/*.xml") # Custom. _make_doc_data_class_path(os.path.join(env.Dir("#").abspath, "editor")) @@ -61,8 +63,6 @@ if env["tools"]: env.Depends("#editor/doc_data_compressed.gen.h", docs) env.CommandNoCache("#editor/doc_data_compressed.gen.h", docs, run_in_subprocess(editor_builders.make_doc_header)) - import glob - path = env.Dir(".").abspath # Editor translations diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 2a8e0d856e..8fd1f5951e 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -3199,7 +3199,7 @@ void AnimationTrackEditor::update_keying() { } keying = keying_enabled; - //_update_menu(); + emit_signal("keying_changed"); } @@ -4882,12 +4882,12 @@ void AnimationTrackEditor::_box_selection_draw() { void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && mb->is_pressed() && mb->get_command() && mb->get_button_index() == BUTTON_WHEEL_DOWN) { + if (mb.is_valid() && mb->is_pressed() && mb->get_command() && mb->get_button_index() == BUTTON_WHEEL_UP) { timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); scroll->accept_event(); } - if (mb.is_valid() && mb->is_pressed() && mb->get_command() && mb->get_button_index() == BUTTON_WHEEL_UP) { + if (mb.is_valid() && mb->is_pressed() && mb->get_command() && mb->get_button_index() == BUTTON_WHEEL_DOWN) { timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); scroll->accept_event(); } diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 3ea970a0f0..9888013ce6 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -657,7 +657,7 @@ FindReplaceBar::FindReplaceBar() { // be handled too late if they weren't handled here. void CodeTextEditor::_input(const Ref<InputEvent> &event) { const Ref<InputEventKey> key_event = event; - if (!key_event.is_valid() || !key_event->is_pressed()) { + if (!key_event.is_valid() || !key_event->is_pressed() || !text_editor->has_focus()) { return; } diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 62b5911ac1..6507956d07 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -984,7 +984,7 @@ void ConnectionsDock::update_tree() { while (F && descr == String()) { for (int i = 0; i < F->get().signals.size(); i++) { if (F->get().signals[i].name == signal_name.operator String()) { - descr = DTR(F->get().signals[i].description.strip_edges()); + descr = DTR(F->get().signals[i].description); break; } } diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 75fff03297..e4a853dbd7 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -53,13 +53,15 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const St if (f) { TreeItem *root = recent->create_item(); + String icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; + while (!f->eof_reached()) { String l = f->get_line().strip_edges(); String name = l.split(" ")[0]; if ((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)) { TreeItem *ti = recent->create_item(root); ti->set_text(0, l); - ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type)); + ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); } } @@ -248,7 +250,8 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description); item->set_tooltip(0, description); - item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, base_type)); + String icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; + item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback)); p_types[p_type] = item; } @@ -305,9 +308,8 @@ void CreateDialog::_update_search() { EditorData &ed = EditorNode::get_editor_data(); root->set_text(0, base_type); - if (search_options->has_theme_icon(base_type, "EditorIcons")) { - root->set_icon(0, search_options->get_theme_icon(base_type, "EditorIcons")); - } + String base_icon = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; + root->set_icon(0, search_options->get_theme_icon(base_icon, "EditorIcons")); TreeItem *to_select = search_box->get_text() == base_type ? root : nullptr; @@ -393,9 +395,7 @@ void CreateDialog::_update_search() { TreeItem *item = search_options->create_item(ti); item->set_metadata(0, type); item->set_text(0, ct[i].name); - if (ct[i].icon.is_valid()) { - item->set_icon(0, ct[i].icon); - } + item->set_icon(0, ct[i].icon.is_valid() ? ct[i].icon : search_options->get_theme_icon(base_icon, "EditorIcons")); if (!to_select || ct[i].name == search_box->get_text()) { to_select = item; @@ -598,16 +598,21 @@ void CreateDialog::_save_favorite_list() { void CreateDialog::_update_favorite_list() { favorites->clear(); + TreeItem *root = favorites->create_item(); + + String icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; + for (int i = 0; i < favorite_list.size(); i++) { String l = favorite_list[i]; String name = l.split(" ")[0]; if (!((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name))) { continue; } + TreeItem *ti = favorites->create_item(root); ti->set_text(0, l); - ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type)); + ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); } emit_signal("favorites_updated"); } diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index 125439d09b..dcd7220ed0 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -153,12 +153,9 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) { if (path.find("::") != -1) { // built-in resource String base_path = path.get_slice("::", 0); - if (ResourceLoader::get_resource_type(base_path) == "PackedScene") { - if (!EditorNode::get_singleton()->is_scene_open(base_path)) { - EditorNode::get_singleton()->load_scene(base_path); - } - } else { - EditorNode::get_singleton()->load_resource(base_path); + RES dependency = ResourceLoader::load(base_path); + if (dependency.is_valid()) { + remote_dependencies.insert(dependency); } } var = ResourceLoader::load(path); @@ -211,6 +208,7 @@ void EditorDebuggerInspector::clear_cache() { memdelete(E->value()); } remote_objects.clear(); + remote_dependencies.clear(); } Object *EditorDebuggerInspector::get_object(ObjectID p_id) { diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h index 638dee3c3f..7d13a4c362 100644 --- a/editor/debugger/editor_debugger_inspector.h +++ b/editor/debugger/editor_debugger_inspector.h @@ -69,6 +69,7 @@ class EditorDebuggerInspector : public EditorInspector { private: ObjectID inspected_object_id; Map<ObjectID, EditorDebuggerRemoteObject *> remote_objects; + Set<RES> remote_dependencies; EditorDebuggerRemoteObject *variables; void _object_selected(ObjectID p_object); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index 200ae7045f..da0ff9f18f 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -674,18 +674,18 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_ String error; if (!_autoload_name_is_valid(name, &error)) { - EditorNode::get_singleton()->show_warning(error); + EditorNode::get_singleton()->show_warning(TTR("Can't add autoload:") + "\n" + error); return false; } const String &path = p_path; if (!FileAccess::exists(path)) { - EditorNode::get_singleton()->show_warning(TTR("Invalid path.") + "\n" + TTR("File does not exist.")); + EditorNode::get_singleton()->show_warning(TTR("Can't add autoload:") + "\n" + TTR(vformat("%s is an invalid path. File does not exist.", path))); return false; } if (!path.begins_with("res://")) { - EditorNode::get_singleton()->show_warning(TTR("Invalid path.") + "\n" + TTR("Not in resource path.")); + EditorNode::get_singleton()->show_warning(TTR("Can't add autoload:") + "\n" + TTR(vformat("%s is an invalid path. Not in resource path (res://).", path))); return false; } @@ -912,4 +912,4 @@ void EditorAutoloadSettings::_set_autoload_add_path(const String &p_text) { void EditorAutoloadSettings::_browse_autoload_add_path() { file_dialog->popup_centered_ratio(); -}
\ No newline at end of file +} diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index d3749477cc..2a410c03e7 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "editor_feature_profile.h" + #include "core/io/json.h" #include "core/os/dir_access.h" #include "editor/editor_settings.h" @@ -353,7 +354,7 @@ void EditorFeatureProfileManager::_update_profile_list(const String &p_select_pr } if (name == current_profile) { - name += " (current)"; + name += " " + TTR("(current)"); } profile_list->add_item(name); int index = profile_list->get_item_count() - 1; @@ -363,12 +364,15 @@ void EditorFeatureProfileManager::_update_profile_list(const String &p_select_pr } } + class_list_vbc->set_visible(selected_profile != String()); + property_list_vbc->set_visible(selected_profile != String()); + no_profile_selected_help->set_visible(selected_profile == String()); profile_actions[PROFILE_CLEAR]->set_disabled(current_profile == String()); profile_actions[PROFILE_ERASE]->set_disabled(selected_profile == String()); profile_actions[PROFILE_EXPORT]->set_disabled(selected_profile == String()); profile_actions[PROFILE_SET]->set_disabled(selected_profile == String()); - current_profile_name->set_text(current_profile); + current_profile_name->set_text(current_profile != String() ? current_profile : TTR("(none)")); _update_selected_profile(); } @@ -451,6 +455,10 @@ void EditorFeatureProfileManager::_create_new_profile() { new_profile->save_to_file(file); _update_profile_list(name); + // The newly created profile is the first one, make it the current profile automatically. + if (profile_list->get_item_count() == 1) { + _profile_action(PROFILE_SET); + } } void EditorFeatureProfileManager::_profile_selected(int p_what) { @@ -730,6 +738,10 @@ void EditorFeatureProfileManager::_import_profiles(const Vector<String> &p_paths } _update_profile_list(); + // The newly imported profile is the first one, make it the current profile automatically. + if (profile_list->get_item_count() == 1) { + _profile_action(PROFILE_SET); + } } void EditorFeatureProfileManager::_export_profile(const String &p_path) { @@ -779,6 +791,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() { HBoxContainer *name_hbc = memnew(HBoxContainer); current_profile_name = memnew(LineEdit); name_hbc->add_child(current_profile_name); + current_profile_name->set_text(TTR("(none)")); current_profile_name->set_editable(false); current_profile_name->set_h_size_flags(Control::SIZE_EXPAND_FILL); profile_actions[PROFILE_CLEAR] = memnew(Button(TTR("Unset"))); @@ -827,7 +840,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() { h_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); main_vbc->add_child(h_split); - VBoxContainer *class_list_vbc = memnew(VBoxContainer); + class_list_vbc = memnew(VBoxContainer); h_split->add_child(class_list_vbc); class_list_vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -837,17 +850,30 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() { class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true); class_list->connect("cell_selected", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_selected)); class_list->connect("item_edited", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_edited), varray(), CONNECT_DEFERRED); + // It will be displayed once the user creates or chooses a profile. + class_list_vbc->hide(); - VBoxContainer *property_list_vbc = memnew(VBoxContainer); + property_list_vbc = memnew(VBoxContainer); h_split->add_child(property_list_vbc); property_list_vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); property_list = memnew(Tree); - property_list_vbc->add_margin_child(TTR("Class Options"), property_list, true); + property_list_vbc->add_margin_child(TTR("Class Options:"), property_list, true); property_list->set_hide_root(true); property_list->set_hide_folding(true); property_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true); property_list->connect("item_edited", callable_mp(this, &EditorFeatureProfileManager::_property_item_edited), varray(), CONNECT_DEFERRED); + // It will be displayed once the user creates or chooses a profile. + property_list_vbc->hide(); + + no_profile_selected_help = memnew(Label(TTR("Create or import a profile to edit available classes and properties."))); + // Add some spacing above the help label. + Ref<StyleBoxEmpty> sb = memnew(StyleBoxEmpty); + sb->set_default_margin(MARGIN_TOP, 20 * EDSCALE); + no_profile_selected_help->add_theme_style_override("normal", sb); + no_profile_selected_help->set_align(Label::ALIGN_CENTER); + no_profile_selected_help->set_v_size_flags(Control::SIZE_EXPAND_FILL); + h_split->add_child(no_profile_selected_help); new_profile_dialog = memnew(ConfirmationDialog); new_profile_dialog->set_title(TTR("New profile name:")); diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h index 4036ec7ec6..38413e35a2 100644 --- a/editor/editor_feature_profile.h +++ b/editor/editor_feature_profile.h @@ -120,8 +120,11 @@ class EditorFeatureProfileManager : public AcceptDialog { HSplitContainer *h_split; + VBoxContainer *class_list_vbc; Tree *class_list; + VBoxContainer *property_list_vbc; Tree *property_list; + Label *no_profile_selected_help; EditorFileDialog *import_profiles; EditorFileDialog *export_profile; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 1b423f69b7..a8ded44323 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1583,9 +1583,9 @@ void EditorInspector::update_tree() { DocData *dd = EditorHelp::get_doc_data(); Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(type2); if (E) { - descr = E->get().brief_description; + descr = DTR(E->get().brief_description); } - class_descr_cache[type2] = DTR(descr); + class_descr_cache[type2] = descr; } category->set_tooltip(p.name + "::" + (class_descr_cache[type2] == "" ? "" : class_descr_cache[type2])); @@ -1755,7 +1755,7 @@ void EditorInspector::update_tree() { while (F && descr == String()) { for (int i = 0; i < F->get().properties.size(); i++) { if (F->get().properties[i].name == propname.operator String()) { - descr = DTR(F->get().properties[i].description.strip_edges()); + descr = DTR(F->get().properties[i].description); break; } } @@ -1765,7 +1765,7 @@ void EditorInspector::update_tree() { // Likely a theme property. for (int i = 0; i < F->get().theme_properties.size(); i++) { if (F->get().theme_properties[i].name == slices[1]) { - descr = DTR(F->get().theme_properties[i].description.strip_edges()); + descr = DTR(F->get().theme_properties[i].description); break; } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8b7014fabe..14a03c5377 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4782,7 +4782,7 @@ void EditorNode::set_distraction_free_mode(bool p_enter) { } } -bool EditorNode::get_distraction_free_mode() const { +bool EditorNode::is_distraction_free_mode_enabled() const { return distraction_free->is_pressed(); } @@ -6568,6 +6568,7 @@ EditorNode::EditorNode() { gui_base->add_child(load_error_dialog); execute_outputs = memnew(RichTextLabel); + execute_outputs->set_selection_enabled(true); execute_output_dialog = memnew(AcceptDialog); execute_output_dialog->add_child(execute_outputs); execute_output_dialog->set_title(""); diff --git a/editor/editor_node.h b/editor/editor_node.h index dfe3d91c07..7c9cf44d6c 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -693,7 +693,7 @@ public: bool get_docks_visible() const; void set_distraction_free_mode(bool p_enter); - bool get_distraction_free_mode() const; + bool is_distraction_free_mode_enabled() const; void add_control_to_dock(DockSlot p_slot, Control *p_control); void remove_control_from_dock(Control *p_control); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 2365090f03..6d93e92555 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -270,6 +270,10 @@ void EditorInterface::set_distraction_free_mode(bool p_enter) { EditorNode::get_singleton()->set_distraction_free_mode(p_enter); } +bool EditorInterface::is_distraction_free_mode_enabled() const { + return EditorNode::get_singleton()->is_distraction_free_mode_enabled(); +} + EditorInterface *EditorInterface::singleton = nullptr; void EditorInterface::_bind_methods() { @@ -302,6 +306,9 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_main_screen_editor", "name"), &EditorInterface::set_main_screen_editor); ClassDB::bind_method(D_METHOD("set_distraction_free_mode", "enter"), &EditorInterface::set_distraction_free_mode); + ClassDB::bind_method(D_METHOD("is_distraction_free_mode_enabled"), &EditorInterface::is_distraction_free_mode_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distraction_free_mode"), "set_distraction_free_mode", "is_distraction_free_mode_enabled"); } EditorInterface::EditorInterface() { diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 2792c8bf19..aac36bfdfd 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -105,6 +105,7 @@ public: void set_main_screen_editor(const String &p_name); void set_distraction_free_mode(bool p_enter); + bool is_distraction_free_mode_enabled() const; EditorInterface(); }; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 81c4e48974..cfa0c7a063 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -581,6 +581,7 @@ public: Vector<Rect2> flag_rects; Vector<String> names; Vector<String> tooltips; + int hovered_index; virtual Size2 get_minimum_size() const { Ref<Font> font = get_theme_font("font", "Label"); @@ -596,56 +597,79 @@ public: return String(); } void _gui_input(const Ref<InputEvent> &p_ev) { - Ref<InputEventMouseButton> mb = p_ev; - if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { + const Ref<InputEventMouseMotion> mm = p_ev; + + if (mm.is_valid()) { for (int i = 0; i < flag_rects.size(); i++) { - if (flag_rects[i].has_point(mb->get_position())) { - //toggle - if (value & (1 << i)) { - value &= ~(1 << i); - } else { - value |= (1 << i); - } - emit_signal("flag_changed", value); + if (flag_rects[i].has_point(mm->get_position())) { + // Used to highlight the hovered flag in the layers grid. + hovered_index = i; update(); + break; } } } - } - void _notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - Rect2 rect; - rect.size = get_size(); - flag_rects.clear(); + const Ref<InputEventMouseButton> mb = p_ev; - int bsize = (rect.size.height * 80 / 100) / 2; + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { + // Toggle the flag. + // We base our choice on the hovered flag, so that it always matches the hovered flag. + if (value & (1 << hovered_index)) { + value &= ~(1 << hovered_index); + } else { + value |= (1 << hovered_index); + } - int h = bsize * 2 + 1; - int vofs = (rect.size.height - h) / 2; + emit_signal("flag_changed", value); + update(); + } + } - Color color = get_theme_color("highlight_color", "Editor"); - for (int i = 0; i < 2; i++) { - Point2 ofs(4, vofs); - if (i == 1) { - ofs.y += bsize + 1; - } + void _notification(int p_what) { + switch (p_what) { + case NOTIFICATION_DRAW: { + Rect2 rect; + rect.size = get_size(); + flag_rects.clear(); + + const int bsize = (rect.size.height * 80 / 100) / 2; + const int h = bsize * 2 + 1; + const int vofs = (rect.size.height - h) / 2; + + Color color = get_theme_color("highlight_color", "Editor"); + for (int i = 0; i < 2; i++) { + Point2 ofs(4, vofs); + if (i == 1) + ofs.y += bsize + 1; + + ofs += rect.position; + for (int j = 0; j < 10; j++) { + Point2 o = ofs + Point2(j * (bsize + 1), 0); + if (j >= 5) + o.x += 1; + + const int idx = i * 10 + j; + const bool on = value & (1 << idx); + Rect2 rect2 = Rect2(o, Size2(bsize, bsize)); + + color.a = on ? 0.6 : 0.2; + if (idx == hovered_index) { + // Add visual feedback when hovering a flag. + color.a += 0.15; + } - ofs += rect.position; - for (int j = 0; j < 10; j++) { - Point2 o = ofs + Point2(j * (bsize + 1), 0); - if (j >= 5) { - o.x += 1; + draw_rect(rect2, color); + flag_rects.push_back(rect2); } - - uint32_t idx = i * 10 + j; - bool on = value & (1 << idx); - Rect2 rect2 = Rect2(o, Size2(bsize, bsize)); - color.a = on ? 0.6 : 0.2; - draw_rect(rect2, color); - flag_rects.push_back(rect2); } - } + } break; + case NOTIFICATION_MOUSE_EXIT: { + hovered_index = -1; + update(); + } break; + default: + break; } } @@ -661,6 +685,7 @@ public: EditorPropertyLayersGrid() { value = 0; + hovered_index = -1; // Nothing is hovered. } }; void EditorPropertyLayers::_grid_changed(uint32_t p_grid) { @@ -752,7 +777,7 @@ EditorPropertyLayers::EditorPropertyLayers() { hb->add_child(grid); button = memnew(Button); button->set_toggle_mode(true); - button->set_text(".."); + button->set_text("..."); button->connect("pressed", callable_mp(this, &EditorPropertyLayers::_button_pressed)); hb->add_child(button); set_bottom_editor(hb); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index a16605ab44..5f293f1fb3 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -503,17 +503,38 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("editors/grid_map/pick_distance", 5000.0); // 3D - _initial_set("editors/3d/primary_grid_color", Color(0.56, 0.56, 0.56)); - hints["editors/3d/primary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/primary_grid_color", PROPERTY_HINT_COLOR_NO_ALPHA, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + _initial_set("editors/3d/primary_grid_color", Color(0.56, 0.56, 0.56, 0.5)); + hints["editors/3d/primary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/primary_grid_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); - _initial_set("editors/3d/secondary_grid_color", Color(0.38, 0.38, 0.38)); - hints["editors/3d/secondary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/secondary_grid_color", PROPERTY_HINT_COLOR_NO_ALPHA, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); - - _initial_set("editors/3d/grid_size", 50); - hints["editors/3d/grid_size"] = PropertyInfo(Variant::INT, "editors/3d/grid_size", PROPERTY_HINT_RANGE, "1,500,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + _initial_set("editors/3d/secondary_grid_color", Color(0.38, 0.38, 0.38, 0.5)); + hints["editors/3d/secondary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/secondary_grid_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + // If a line is a multiple of this, it uses the primary grid color. _initial_set("editors/3d/primary_grid_steps", 10); - hints["editors/3d/primary_grid_steps"] = PropertyInfo(Variant::INT, "editors/3d/primary_grid_steps", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["editors/3d/primary_grid_steps"] = PropertyInfo(Variant::INT, "editors/3d/primary_grid_steps", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT); + + // At 1000, the grid mostly looks like it has no edge. + _initial_set("editors/3d/grid_size", 200); + hints["editors/3d/grid_size"] = PropertyInfo(Variant::INT, "editors/3d/grid_size", PROPERTY_HINT_RANGE, "1,2000,1", PROPERTY_USAGE_DEFAULT); + + // Default largest grid size is 100m, 10^2 (primary grid lines are 1km apart when primary_grid_steps is 10). + _initial_set("editors/3d/grid_division_level_max", 2); + // Higher values produce graphical artifacts when far away unless View Z-Far + // is increased significantly more than it really should need to be. + hints["editors/3d/grid_division_level_max"] = PropertyInfo(Variant::INT, "editors/3d/grid_division_level_max", PROPERTY_HINT_RANGE, "-1,3,1", PROPERTY_USAGE_DEFAULT); + + // Default smallest grid size is 1cm, 10^-2. + _initial_set("editors/3d/grid_division_level_min", -2); + // Lower values produce graphical artifacts regardless of view clipping planes, so limit to -2 as a lower bound. + hints["editors/3d/grid_division_level_min"] = PropertyInfo(Variant::INT, "editors/3d/grid_division_level_min", PROPERTY_HINT_RANGE, "-2,2,1", PROPERTY_USAGE_DEFAULT); + + // -0.2 seems like a sensible default. -1.0 gives Blender-like behavior, 0.5 gives huge grids. + _initial_set("editors/3d/grid_division_level_bias", -0.2); + hints["editors/3d/grid_division_level_bias"] = PropertyInfo(Variant::FLOAT, "editors/3d/grid_division_level_bias", PROPERTY_HINT_RANGE, "-1.0,0.5,0.1", PROPERTY_USAGE_DEFAULT); + + _initial_set("editors/3d/grid_xz_plane", true); + _initial_set("editors/3d/grid_xy_plane", false); + _initial_set("editors/3d/grid_yz_plane", false); _initial_set("editors/3d/default_fov", 70.0); _initial_set("editors/3d/default_z_near", 0.05); diff --git a/editor/icons/SCsub b/editor/icons/SCsub index f0d51999f0..e143276259 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -2,6 +2,8 @@ Import("env") +import os + from platform_methods import run_in_subprocess import editor_icons_builders @@ -15,7 +17,10 @@ env["BUILDERS"]["MakeEditorIconsBuilder"] = make_editor_icons_builder icon_sources = Glob("*.svg") # Module icons -for module_icons in env.module_icons_paths: - icon_sources += Glob("#" + module_icons + "/*.svg") +for path in env.module_icons_paths: + if not os.path.isabs(path): + icon_sources += Glob("#" + path + "/*.svg") # Built-in. + else: + icon_sources += Glob(path + "/*.svg") # Custom. env.Alias("editor_icons", [env.MakeEditorIconsBuilder("#editor/editor_icons.gen.h", icon_sources)]) diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 1f39a12c25..b57ea3745d 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -134,7 +134,7 @@ String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { } void ResourceImporterLayeredTexture::get_import_options(List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,Video RAM,Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless (PNG),Lossy (WebP),Video RAM (S3TC/ETC/BPTC),Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0)); diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index 0818655c4c..2423553d22 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -33,6 +33,7 @@ #include "atlas_import_failed.xpm" #include "core/io/image_loader.h" #include "core/io/resource_saver.h" +#include "core/math/geometry_2d.h" #include "core/os/file_access.h" #include "editor/editor_atlas_packer.h" #include "scene/resources/mesh.h" @@ -129,7 +130,8 @@ static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_tr double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1); double xf = x[0]; double xt = x[0] + dx_upper; // if y[0] == y[1], special case - for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) { + int max_y = MIN(y[2], height - p_offset.y - 1); + for (int yi = y[0]; yi <= max_y; yi++) { if (yi >= 0) { for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) { int px = xi, py = yi; @@ -247,7 +249,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file chart.vertices = polygons[j]; chart.can_transpose = true; - Vector<int> poly = Geometry::triangulate_polygon(polygons[j]); + Vector<int> poly = Geometry2D::triangulate_polygon(polygons[j]); for (int i = 0; i < poly.size(); i += 3) { EditorAtlasPacker::Chart::Face f; f.vertex[0] = poly[i + 0]; diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp index 0eb4e6036a..2ddcf3d877 100644 --- a/editor/node_3d_editor_gizmos.cpp +++ b/editor/node_3d_editor_gizmos.cpp @@ -30,7 +30,8 @@ #include "node_3d_editor_gizmos.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" +#include "core/math/geometry_3d.h" #include "core/math/quick_hull.h" #include "scene/3d/audio_stream_player_3d.h" #include "scene/3d/baked_lightmap.h" @@ -482,7 +483,7 @@ bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector transformed_frustum.push_back(it.xform(p_frustum[i])); } - Vector<Vector3> convex_points = Geometry::compute_convex_mesh_points(p_frustum.ptr(), p_frustum.size()); + Vector<Vector3> convex_points = Geometry3D::compute_convex_mesh_points(p_frustum.ptr(), p_frustum.size()); if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), transformed_frustum.size(), convex_points.ptr(), convex_points.size(), mesh_scale)) { return true; } @@ -616,7 +617,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, s[0] = p_camera->unproject_position(a); s[1] = p_camera->unproject_position(b); - Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, s); + Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s); float pd = p.distance_to(p_point); @@ -831,7 +832,7 @@ static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vec Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb); + Geometry3D::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb); float d = ra.distance_to(rb); if (d < min_d) { @@ -857,7 +858,7 @@ void Light3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camer if (p_idx == 0) { if (Object::cast_to<SpotLight3D>(light)) { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); float d = -ra.z; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -1100,7 +1101,7 @@ void AudioStreamPlayer3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int Vector3 to(Math::sin(an), 0, -Math::cos(an)); Vector3 r1, r2; - Geometry::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2); + Geometry3D::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2); float d = r1.distance_to(r2); if (d < closest_dist) { closest_dist = d; @@ -1238,7 +1239,7 @@ void Camera3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Came camera->set("fov", CLAMP(a * 2.0, 1, 179)); } else { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); float d = ra.x * 2.0; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -2169,7 +2170,7 @@ void VisibilityNotifier3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int if (move) { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2181,7 +2182,7 @@ void VisibilityNotifier3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int } else { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx] - ofs[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2360,7 +2361,7 @@ void GPUParticles3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx if (move) { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2372,7 +2373,7 @@ void GPUParticles3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx } else { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx] - ofs[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2523,7 +2524,7 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_id axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -2550,7 +2551,7 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_id axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb); // Adjust the actual position to account for the gizmo handle position float d = ra[p_idx] + 0.25; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2701,7 +2702,7 @@ void DecalGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3 axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -2842,7 +2843,7 @@ void GIProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camer axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3354,7 +3355,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i if (Object::cast_to<SphereShape3D>(*s)) { Ref<SphereShape3D> ss = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); float d = ra.x; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3370,7 +3371,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i if (Object::cast_to<RayShape3D>(*s)) { Ref<RayShape3D> rs = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); float d = ra.z; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3388,7 +3389,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i axis[p_idx] = 1.0; Ref<BoxShape3D> bs = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3408,7 +3409,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i axis[p_idx == 0 ? 0 : 2] = 1.0; Ref<CapsuleShape3D> cs2 = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); if (p_idx == 1) { d -= cs2->get_radius(); @@ -3434,7 +3435,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i axis[p_idx == 0 ? 0 : 1] = 1.0; Ref<CylinderShape3D> cs2 = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3802,7 +3803,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (points.size() > 3) { Vector<Vector3> varr = Variant(points); - Geometry::MeshData md; + Geometry3D::MeshData md; Error err = QuickHull::build(varr, md); if (err == OK) { Vector<Vector3> points2; diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 1abba45f9e..b905c8db12 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -31,6 +31,7 @@ #include "abstract_polygon_2d_editor.h" #include "canvas_item_editor_plugin.h" +#include "core/math/geometry_2d.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" @@ -671,7 +672,7 @@ AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(c Vector2 segment[2] = { xform.xform(points[i] + offset), xform.xform(points[(i + 1) % n_points] + offset) }; - Vector2 cp = Geometry::get_closest_point_to_segment_2d(p_pos, segment); + Vector2 cp = Geometry2D::get_closest_point_to_segment(p_pos, segment); if (cp.distance_squared_to(segment[0]) < eps2 || cp.distance_squared_to(segment[1]) < eps2) { continue; //not valid to reuse point diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index df67482dfb..a7d5c2207b 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -32,7 +32,7 @@ #include "core/input/input.h" #include "core/io/resource_loader.h" -#include "core/math/delaunay_2d.h" +#include "core/math/geometry_2d.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_scale.h" @@ -165,7 +165,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven triangle.push_back(points[idx]); } - if (Geometry::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) { + if (Geometry2D::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) { selected_triangle = i; _update_tool_erase(); return; diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 652754a146..a435b1c482 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -32,7 +32,7 @@ #include "core/input/input.h" #include "core/io/resource_loader.h" -#include "core/math/delaunay_2d.h" +#include "core/math/geometry_2d.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_scale.h" @@ -191,7 +191,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv transition_lines[i].from, transition_lines[i].to }; - Vector2 cpoint = Geometry::get_closest_point_to_segment_2d(mb->get_position(), s); + Vector2 cpoint = Geometry2D::get_closest_point_to_segment(mb->get_position(), s); float d = cpoint.distance_to(mb->get_position()); if (d > transition_lines[i].width) { continue; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 8f88612ffa..2f7080b1a5 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "canvas_item_editor_plugin.h" #include "core/input/input.h" +#include "core/math/geometry_2d.h" #include "core/os/keyboard.h" #include "core/print_string.h" #include "core/project_settings.h" @@ -53,8 +54,10 @@ #include "scene/main/window.h" #include "scene/resources/packed_scene.h" -#define MIN_ZOOM 0.01 -#define MAX_ZOOM 100 +// Min and Max are power of two in order to play nicely with successive increment. +// That way, we can naturally reach a 100% zoom from boundaries. +#define MIN_ZOOM 1. / 128 +#define MAX_ZOOM 128 #define RULER_WIDTH (15 * EDSCALE) #define SCALE_HANDLE_DISTANCE 25 @@ -86,7 +89,6 @@ public: GridContainer *child_container; set_title(TTR("Configure Snap")); - get_ok()->set_text(TTR("Close")); container = memnew(VBoxContainer); add_child(container); @@ -672,7 +674,7 @@ void CanvasItemEditor::_get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResu } // Check if the point is inside the Polygon2D - if (Geometry::is_point_in_polygon(screen_pos, bone_shape)) { + if (Geometry2D::is_point_in_polygon(screen_pos, bone_shape)) { // Check if the item is already in the list bool duplicate = false; for (int i = 0; i < r_items.size(); i++) { @@ -1213,7 +1215,11 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); update_viewport(); } else { - _zoom_on_position(zoom * (1 - (0.05 * b->get_factor())), b->get_position()); + float new_zoom = _get_next_zoom_value(-1); + if (b->get_factor() != 1.f) { + new_zoom = zoom * ((new_zoom / zoom - 1.f) * b->get_factor() + 1.f); + } + _zoom_on_position(new_zoom, b->get_position()); } return true; } @@ -1224,7 +1230,11 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); update_viewport(); } else { - _zoom_on_position(zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95), b->get_position()); + float new_zoom = _get_next_zoom_value(1); + if (b->get_factor() != 1.f) { + new_zoom = zoom * ((new_zoom / zoom - 1.f) * b->get_factor() + 1.f); + } + _zoom_on_position(new_zoom, b->get_position()); } return true; } @@ -4333,8 +4343,37 @@ void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) { undo_redo->commit_action(); } +float CanvasItemEditor::_get_next_zoom_value(int p_increment_count) const { + // Base increment factor defined as the twelveth root of two. + // This allow a smooth geometric evolution of the zoom, with the advantage of + // visiting all integer power of two scale factors. + // note: this is analogous to the 'semitones' interval in the music world + // In order to avoid numerical imprecisions, we compute and edit a zoom index + // with the following relation: zoom = 2 ^ (index / 12) + + if (zoom < CMP_EPSILON || p_increment_count == 0) { + return 1.f; + } + + // Remove Editor scale from the index computation + float zoom_noscale = zoom / MAX(1, EDSCALE); + + // zoom = 2**(index/12) => log2(zoom) = index/12 + float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f)); + + float new_zoom_index = closest_zoom_index + p_increment_count; + float new_zoom = Math::pow(2.f, new_zoom_index / 12.f); + + // Restore Editor scale transformation + new_zoom *= MAX(1, EDSCALE); + + return new_zoom; +} + void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) { - if (p_zoom < MIN_ZOOM || p_zoom > MAX_ZOOM) { + p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM); + + if (p_zoom == zoom) { return; } @@ -4376,7 +4415,7 @@ void CanvasItemEditor::_update_zoom_label() { } void CanvasItemEditor::_button_zoom_minus() { - _zoom_on_position(zoom / Math_SQRT2, viewport_scrollable->get_size() / 2.0); + _zoom_on_position(_get_next_zoom_value(-6), viewport_scrollable->get_size() / 2.0); } void CanvasItemEditor::_button_zoom_reset() { @@ -4384,7 +4423,7 @@ void CanvasItemEditor::_button_zoom_reset() { } void CanvasItemEditor::_button_zoom_plus() { - _zoom_on_position(zoom * Math_SQRT2, viewport_scrollable->get_size() / 2.0); + _zoom_on_position(_get_next_zoom_value(6), viewport_scrollable->get_size() / 2.0); } void CanvasItemEditor::_button_toggle_smart_snap(bool p_status) { @@ -5610,6 +5649,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->add_child(pan_button); pan_button->set_toggle_mode(true); pan_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_PAN)); + pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), KEY_G)); pan_button->set_tooltip(TTR("Pan Mode")); ruler_button = memnew(ToolButton); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index a686c98f65..765d5f81d0 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -528,6 +528,7 @@ private: VBoxContainer *controls_vb; HBoxContainer *zoom_hb; + float _get_next_zoom_value(int p_increment_count) const; void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); void _update_zoom_label(); void _button_zoom_minus(); diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/collision_polygon_3d_editor_plugin.cpp index c61d410d38..d9d9cf6a87 100644 --- a/editor/plugins/collision_polygon_3d_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_3d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "core/input/input.h" +#include "core/math/geometry_2d.h" #include "core/os/file_access.h" #include "core/os/keyboard.h" #include "editor/editor_settings.h" @@ -196,7 +197,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, poly[(i + 1) % poly.size()].y, depth))) }; - Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint, points); + Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, points); if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) { continue; //not valid to reuse point } diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 0f381c06b4..596629f8e8 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -36,6 +36,7 @@ #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/line_shape_2d.h" +#include "scene/resources/ray_shape_2d.h" #include "scene/resources/rectangle_shape_2d.h" #include "scene/resources/segment_shape_2d.h" diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp index 0ca479555d..0a4d173923 100644 --- a/editor/plugins/debugger_editor_plugin.cpp +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -68,13 +68,10 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(EditorNode *p_editor, MenuButton *p_d p->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION); p->set_item_tooltip(p->get_item_count() - 1, TTR("Navigation meshes and polygons will be visible on the running game if this option is turned on.")); p->add_separator(); - //those are now on by default, since they are harmless p->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Sync Scene Changes")), RUN_LIVE_DEBUG); p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any changes made to the scene in the editor will be replicated in the running game.\nWhen used remotely on a device, this is more efficient with network filesystem.")); - p->set_item_checked(p->get_item_count() - 1, true); p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Sync Script Changes")), RUN_RELOAD_SCRIPTS); p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem.")); - p->set_item_checked(p->get_item_count() - 1, true); // Multi-instance, start/stop instances_menu = memnew(PopupMenu); @@ -174,8 +171,8 @@ void DebuggerEditorPlugin::_update_debug_options() { bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false); bool check_debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisons", false); bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); - bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", false); - bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", false); + bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true); + bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true); int instances = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_instances", 1); if (check_deploy_remote) { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 3c12022854..b8209fe50d 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -328,17 +328,13 @@ void Node3DEditorViewport::_update_camera(float p_interp_delta) { //------- // Apply camera transform - float tolerance = 0.001; + real_t tolerance = 0.001; bool equal = true; - if (Math::abs(old_camera_cursor.x_rot - camera_cursor.x_rot) > tolerance || Math::abs(old_camera_cursor.y_rot - camera_cursor.y_rot) > tolerance) { + if (!Math::is_equal_approx(old_camera_cursor.x_rot, camera_cursor.x_rot, tolerance) || !Math::is_equal_approx(old_camera_cursor.y_rot, camera_cursor.y_rot, tolerance)) { equal = false; - } - - if (equal && old_camera_cursor.pos.distance_squared_to(camera_cursor.pos) > tolerance * tolerance) { + } else if (!old_camera_cursor.pos.is_equal_approx(camera_cursor.pos)) { equal = false; - } - - if (equal && Math::abs(old_camera_cursor.distance - camera_cursor.distance) > tolerance) { + } else if (!Math::is_equal_approx(old_camera_cursor.distance, camera_cursor.distance, tolerance)) { equal = false; } @@ -356,6 +352,7 @@ void Node3DEditorViewport::_update_camera(float p_interp_delta) { update_transform_gizmo_view(); rotation_control->update(); } + spatial_editor->update_grid(); } Transform Node3DEditorViewport::to_camera_transform(const Cursor &p_cursor) const { @@ -835,7 +832,7 @@ bool Node3DEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_high Vector3 r; - if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { + if (Geometry3D::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { float d = r.distance_to(ray_pos); if (d < col_d) { col_d = d; @@ -932,7 +929,7 @@ bool Node3DEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_high Vector3 r; - if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { + if (Geometry3D::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { float d = r.distance_to(ray_pos); if (d < col_d) { col_d = d; @@ -3661,15 +3658,19 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name); editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); - Transform global_transform; - Node3D *parent_spatial = Object::cast_to<Node3D>(parent); - if (parent_spatial) { - global_transform = parent_spatial->get_global_gizmo_transform(); - } + Node3D *node3d = Object::cast_to<Node3D>(instanced_scene); + if (node3d) { + Transform global_transform; + Node3D *parent_node3d = Object::cast_to<Node3D>(parent); + if (parent_node3d) { + global_transform = parent_node3d->get_global_gizmo_transform(); + } - global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point)); + global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point)); + global_transform.basis *= node3d->get_transform().basis; - editor_data->get_undo_redo().add_do_method(instanced_scene, "set_global_transform", global_transform); + editor_data->get_undo_redo().add_do_method(instanced_scene, "set_global_transform", global_transform); + } return true; } @@ -4929,8 +4930,10 @@ void Node3DEditor::_menu_item_pressed(int p_option) { for (int i = 0; i < 3; ++i) { if (grid_enable[i]) { - RenderingServer::get_singleton()->instance_set_visible(grid_instance[i], grid_enabled); grid_visible[i] = grid_enabled; + if (grid_instance[i].is_valid()) { + RenderingServer::get_singleton()->instance_set_visible(grid_instance[i], grid_enabled); + } } } @@ -5054,6 +5057,7 @@ void Node3DEditor::_init_indicators() { indicator_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); indicator_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); indicator_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + indicator_mat->set_transparency(StandardMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); Vector<Color> origin_colors; Vector<Vector3> origin_points; @@ -5082,12 +5086,27 @@ void Node3DEditor::_init_indicators() { origin_colors.push_back(origin_color); origin_colors.push_back(origin_color); - origin_points.push_back(axis * 4096); - origin_points.push_back(axis * -4096); - } - - grid_enable[1] = true; - grid_visible[1] = true; + origin_colors.push_back(origin_color); + origin_colors.push_back(origin_color); + origin_colors.push_back(origin_color); + origin_colors.push_back(origin_color); + // To both allow having a large origin size and avoid jitter + // at small scales, we should segment the line into pieces. + // 3 pieces seems to do the trick, and let's use powers of 2. + origin_points.push_back(axis * 1048576); + origin_points.push_back(axis * 1024); + origin_points.push_back(axis * 1024); + origin_points.push_back(axis * -1024); + origin_points.push_back(axis * -1024); + origin_points.push_back(axis * -1048576); + } + + grid_enable[0] = EditorSettings::get_singleton()->get("editors/3d/grid_xy_plane"); + grid_enable[1] = EditorSettings::get_singleton()->get("editors/3d/grid_yz_plane"); + grid_enable[2] = EditorSettings::get_singleton()->get("editors/3d/grid_xz_plane"); + grid_visible[0] = grid_enable[0]; + grid_visible[1] = grid_enable[1]; + grid_visible[2] = grid_enable[2]; _init_grid(); @@ -5382,6 +5401,9 @@ void Node3DEditor::_update_gizmos_menu() { const int plugin_state = gizmo_plugins_by_name[i]->get_state(); gizmos_menu->add_multistate_item(TTR(plugin_name), 3, plugin_state, i); const int idx = gizmos_menu->get_item_index(i); + gizmos_menu->set_item_tooltip( + idx, + TTR("Click to toggle between visibility states.\n\nOpen eye: Gizmo is visible.\nClosed eye: Gizmo is hidden.\nHalf-open eye: Gizmo is also visible through opaque surfaces (\"x-ray\").")); switch (plugin_state) { case EditorNode3DGizmoPlugin::VISIBLE: gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_visible")); @@ -5418,6 +5440,15 @@ void Node3DEditor::_update_gizmos_menu_theme() { } void Node3DEditor::_init_grid() { + if (!grid_enabled) { + return; + } + Camera3D *camera = get_editor_viewport(0)->camera; + Vector3 camera_position = camera->get_translation(); + if (camera_position == Vector3()) { + return; // Camera3D is invalid, don't draw the grid. + } + Vector<Color> grid_colors[3]; Vector<Vector3> grid_points[3]; @@ -5426,52 +5457,111 @@ void Node3DEditor::_init_grid() { int grid_size = EditorSettings::get_singleton()->get("editors/3d/grid_size"); int primary_grid_steps = EditorSettings::get_singleton()->get("editors/3d/primary_grid_steps"); - for (int i = 0; i < 3; i++) { - Vector3 axis; - axis[i] = 1; - Vector3 axis_n1; - axis_n1[(i + 1) % 3] = 1; - Vector3 axis_n2; - axis_n2[(i + 2) % 3] = 1; - - for (int j = -grid_size; j <= grid_size; j++) { - Vector3 p1 = axis_n1 * j + axis_n2 * -grid_size; - Vector3 p1_dest = p1 * (-axis_n2 + axis_n1); - Vector3 p2 = axis_n2 * j + axis_n1 * -grid_size; - Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); - - Color line_color = secondary_grid_color; - if (origin_enabled && j == 0) { - // Don't draw the center lines of the grid if the origin is enabled - // The origin would overlap the grid lines in this case, causing flickering - continue; - } else if (j % primary_grid_steps == 0) { - line_color = primary_grid_color; + // Which grid planes are enabled? Which should we generate? + grid_enable[0] = grid_visible[0] = EditorSettings::get_singleton()->get("editors/3d/grid_xy_plane"); + grid_enable[1] = grid_visible[1] = EditorSettings::get_singleton()->get("editors/3d/grid_yz_plane"); + grid_enable[2] = grid_visible[2] = EditorSettings::get_singleton()->get("editors/3d/grid_xz_plane"); + + // Offsets division_level for bigger or smaller grids. + // Default value is -0.2. -1.0 gives Blender-like behavior, 0.5 gives huge grids. + real_t division_level_bias = EditorSettings::get_singleton()->get("editors/3d/grid_division_level_bias"); + // Default largest grid size is 100m, 10^2 (default value is 2). + int division_level_max = EditorSettings::get_singleton()->get("editors/3d/grid_division_level_max"); + // Default smallest grid size is 1cm, 10^-2 (default value is -2). + int division_level_min = EditorSettings::get_singleton()->get("editors/3d/grid_division_level_min"); + ERR_FAIL_COND_MSG(division_level_max < division_level_min, "The 3D grid's maximum division level cannot be lower than its minimum division level."); + + if (primary_grid_steps != 10) { // Log10 of 10 is 1. + // Change of base rule, divide by ln(10). + real_t div = Math::log((real_t)primary_grid_steps) / (real_t)2.302585092994045901094; + // Trucation (towards zero) is intentional. + division_level_max = (int)(division_level_max / div); + division_level_min = (int)(division_level_min / div); + } + + for (int a = 0; a < 3; a++) { + if (!grid_enable[a]) { + continue; // If this grid plane is disabled, skip generation. + } + int b = (a + 1) % 3; + int c = (a + 2) % 3; + + real_t division_level = Math::log(Math::abs(camera_position[c])) / Math::log((double)primary_grid_steps) + division_level_bias; + division_level = CLAMP(division_level, division_level_min, division_level_max); + real_t division_level_floored = Math::floor(division_level); + real_t division_level_decimals = division_level - division_level_floored; + + real_t small_step_size = Math::pow(primary_grid_steps, division_level_floored); + real_t large_step_size = small_step_size * primary_grid_steps; + real_t center_a = large_step_size * (int)(camera_position[a] / large_step_size); + real_t center_b = large_step_size * (int)(camera_position[b] / large_step_size); + + real_t bgn_a = center_a - grid_size * small_step_size; + real_t end_a = center_a + grid_size * small_step_size; + real_t bgn_b = center_b - grid_size * small_step_size; + real_t end_b = center_b + grid_size * small_step_size; + + // In each iteration of this loop, draw one line in each direction (so two lines per loop, in each if statement). + for (int i = -grid_size; i <= grid_size; i++) { + Color line_color; + // Is this a primary line? Set the appropriate color. + if (i % primary_grid_steps == 0) { + line_color = primary_grid_color.lerp(secondary_grid_color, division_level_decimals); + } else { + line_color = secondary_grid_color; + line_color.a = line_color.a * (1 - division_level_decimals); + } + // Makes lines farther from the center fade out. + // Due to limitations of lines, any that come near the camera have full opacity always. + // This should eventually be replaced by some kind of "distance fade" system, outside of this function. + // But the effect is still somewhat convincing... + line_color.a *= 1 - (1 - division_level_decimals * 0.9) * (Math::abs(i / (float)grid_size)); + + real_t position_a = center_a + i * small_step_size; + real_t position_b = center_b + i * small_step_size; + + // Don't draw lines over the origin if it's enabled. + if (!(origin_enabled && Math::is_zero_approx(position_a))) { + Vector3 line_bgn = Vector3(); + Vector3 line_end = Vector3(); + line_bgn[a] = position_a; + line_end[a] = position_a; + line_bgn[b] = bgn_b; + line_end[b] = end_b; + grid_points[c].push_back(line_bgn); + grid_points[c].push_back(line_end); + grid_colors[c].push_back(line_color); + grid_colors[c].push_back(line_color); } - grid_points[i].push_back(p1); - grid_points[i].push_back(p1_dest); - grid_colors[i].push_back(line_color); - grid_colors[i].push_back(line_color); - - grid_points[i].push_back(p2); - grid_points[i].push_back(p2_dest); - grid_colors[i].push_back(line_color); - grid_colors[i].push_back(line_color); + if (!(origin_enabled && Math::is_zero_approx(position_b))) { + Vector3 line_bgn = Vector3(); + Vector3 line_end = Vector3(); + line_bgn[b] = position_b; + line_end[b] = position_b; + line_bgn[a] = bgn_a; + line_end[a] = end_a; + grid_points[c].push_back(line_bgn); + grid_points[c].push_back(line_end); + grid_colors[c].push_back(line_color); + grid_colors[c].push_back(line_color); + } } - grid[i] = RenderingServer::get_singleton()->mesh_create(); + // Create a mesh from the pushed vector points and colors. + grid[c] = RenderingServer::get_singleton()->mesh_create(); Array d; d.resize(RS::ARRAY_MAX); - d[RenderingServer::ARRAY_VERTEX] = grid_points[i]; - d[RenderingServer::ARRAY_COLOR] = grid_colors[i]; - RenderingServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], RenderingServer::PRIMITIVE_LINES, d); - RenderingServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid()); - grid_instance[i] = RenderingServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world_3d()->get_scenario()); + d[RenderingServer::ARRAY_VERTEX] = grid_points[c]; + d[RenderingServer::ARRAY_COLOR] = grid_colors[c]; + RenderingServer::get_singleton()->mesh_add_surface_from_arrays(grid[c], RenderingServer::PRIMITIVE_LINES, d); + RenderingServer::get_singleton()->mesh_surface_set_material(grid[c], 0, indicator_mat->get_rid()); + grid_instance[c] = RenderingServer::get_singleton()->instance_create2(grid[c], get_tree()->get_root()->get_world_3d()->get_scenario()); - RenderingServer::get_singleton()->instance_set_visible(grid_instance[i], grid_visible[i]); - RenderingServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], RS::SHADOW_CASTING_SETTING_OFF); - RS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << Node3DEditorViewport::GIZMO_GRID_LAYER); + // Yes, the end of this line is supposed to be a. + RenderingServer::get_singleton()->instance_set_visible(grid_instance[c], grid_visible[a]); + RenderingServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[c], RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_set_layer_mask(grid_instance[c], 1 << Node3DEditorViewport::GIZMO_GRID_LAYER); } } @@ -5489,6 +5579,11 @@ void Node3DEditor::_finish_grid() { } } +void Node3DEditor::update_grid() { + _finish_grid(); + _init_grid(); +} + bool Node3DEditor::is_any_freelook_active() const { for (unsigned int i = 0; i < VIEWPORTS_COUNT; ++i) { if (viewports[i]->is_freelook_active()) { diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 3d92e7e7e1..32b087c372 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -766,6 +766,7 @@ public: Ref<ArrayMesh> get_scale_gizmo(int idx) const { return scale_gizmo[idx]; } Ref<ArrayMesh> get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; } + void update_grid(); void update_transform_gizmo(); void update_all_gizmos(Node *p_node = nullptr); void snap_selected_nodes_to_floor(); diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index a44fe69ff6..25cffa3d6c 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -30,6 +30,8 @@ #include "path_3d_editor_plugin.h" +#include "core/math/geometry_2d.h" +#include "core/math/geometry_3d.h" #include "core/os/keyboard.h" #include "node_3d_editor_plugin.h" #include "scene/resources/curve.h" @@ -344,7 +346,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref Vector2 s[2]; s[0] = p_camera->unproject_position(from); s[1] = p_camera->unproject_position(to); - Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos, s); + Vector2 inters = Geometry2D::get_closest_point_to_segment(mbpos, s); float d = inters.distance_to(mbpos); if (d < 10 && d < closest_d) { @@ -354,7 +356,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref Vector3 ray_dir = p_camera->project_ray_normal(mbpos); Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ray_from, ray_from + ray_dir * 4096, from, to, ra, rb); + Geometry3D::get_closest_points_between_segments(ray_from, ray_from + ray_dir * 4096, from, to, ra, rb); closest_seg_point = it.xform(rb); } diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 7ee695b9fe..1f98c0139b 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "core/input/input.h" +#include "core/math/geometry_2d.h" #include "core/os/file_access.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" @@ -693,7 +694,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { polys.write[j] = mtx.xform(points_prev[idx]); } - if (Geometry::is_point_in_polygon(Vector2(mb->get_position().x, mb->get_position().y), polys)) { + if (Geometry2D::is_point_in_polygon(Vector2(mb->get_position().x, mb->get_position().y), polys)) { erase_index = i; break; } diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index e7f8a56e5e..4b79d8c344 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -609,6 +609,18 @@ void ScriptTextEditor::_validate_script() { for (List<ScriptLanguage::Warning>::Element *E = warnings.front(); E; E = E->next()) { ScriptLanguage::Warning w = E->get(); + Dictionary ignore_meta; + ignore_meta["line"] = w.line; + ignore_meta["code"] = w.string_code.to_lower(); + warnings_panel->push_cell(); + warnings_panel->push_meta(ignore_meta); + warnings_panel->push_color( + warnings_panel->get_theme_color("accent_color", "Editor").lerp(warnings_panel->get_theme_color("mono_color", "Editor"), 0.5)); + warnings_panel->add_text(TTR("[Ignore]")); + warnings_panel->pop(); // Color. + warnings_panel->pop(); // Meta ignore. + warnings_panel->pop(); // Cell. + warnings_panel->push_cell(); warnings_panel->push_meta(w.line - 1); warnings_panel->push_color(warnings_panel->get_theme_color("warning_color", "Editor")); @@ -621,15 +633,6 @@ void ScriptTextEditor::_validate_script() { warnings_panel->push_cell(); warnings_panel->add_text(w.message); warnings_panel->pop(); // Cell. - - Dictionary ignore_meta; - ignore_meta["line"] = w.line; - ignore_meta["code"] = w.string_code.to_lower(); - warnings_panel->push_cell(); - warnings_panel->push_meta(ignore_meta); - warnings_panel->add_text(TTR("(ignore)")); - warnings_panel->pop(); // Meta ignore. - warnings_panel->pop(); // Cell. } warnings_panel->pop(); // Table. @@ -1747,6 +1750,8 @@ ScriptTextEditor::ScriptTextEditor() { warnings_panel = memnew(RichTextLabel); editor_box->add_child(warnings_panel); + warnings_panel->add_theme_font_override( + "normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font("main", "EditorFonts")); warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL); warnings_panel->set_meta_underline(true); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index c256acd17b..321b4432ab 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -30,13 +30,385 @@ #include "skeleton_3d_editor_plugin.h" +#include "core/io/resource_saver.h" +#include "editor/editor_file_dialog.h" +#include "editor/editor_properties.h" +#include "editor/editor_scale.h" +#include "editor/plugins/animation_player_editor_plugin.h" #include "node_3d_editor_plugin.h" #include "scene/3d/collision_shape_3d.h" +#include "scene/3d/mesh_instance_3d.h" #include "scene/3d/physics_body_3d.h" #include "scene/3d/physics_joint_3d.h" #include "scene/resources/capsule_shape_3d.h" #include "scene/resources/sphere_shape_3d.h" +void BoneTransformEditor::create_editors() { + const Color section_color = get_theme_color("prop_subsection", "Editor"); + + section = memnew(EditorInspectorSection); + section->setup("trf_properties", label, this, section_color, true); + add_child(section); + + key_button = memnew(Button); + key_button->set_text(TTR("Key Transform")); + key_button->set_visible(keyable); + key_button->set_icon(get_theme_icon("Key", "EditorIcons")); + key_button->set_flat(true); + section->get_vbox()->add_child(key_button); + + enabled_checkbox = memnew(CheckBox(TTR("Pose Enabled"))); + enabled_checkbox->set_flat(true); + enabled_checkbox->set_visible(toggle_enabled); + section->get_vbox()->add_child(enabled_checkbox); + + Label *l1 = memnew(Label(TTR("Translation"))); + section->get_vbox()->add_child(l1); + + translation_grid = memnew(GridContainer()); + translation_grid->set_columns(TRANSLATION_COMPONENTS); + section->get_vbox()->add_child(translation_grid); + + Label *l2 = memnew(Label(TTR("Rotation Degrees"))); + section->get_vbox()->add_child(l2); + + rotation_grid = memnew(GridContainer()); + rotation_grid->set_columns(ROTATION_DEGREES_COMPONENTS); + section->get_vbox()->add_child(rotation_grid); + + Label *l3 = memnew(Label(TTR("Scale"))); + section->get_vbox()->add_child(l3); + + scale_grid = memnew(GridContainer()); + scale_grid->set_columns(SCALE_COMPONENTS); + section->get_vbox()->add_child(scale_grid); + + Label *l4 = memnew(Label(TTR("Transform"))); + section->get_vbox()->add_child(l4); + + transform_grid = memnew(GridContainer()); + transform_grid->set_columns(TRANSFORM_CONTROL_COMPONENTS); + section->get_vbox()->add_child(transform_grid); + + static const char *desc[TRANSFORM_COMPONENTS] = { "x", "y", "z", "x", "y", "z", "x", "y", "z", "x", "y", "z" }; + + for (int i = 0; i < TRANSFORM_CONTROL_COMPONENTS; ++i) { + translation_slider[i] = memnew(EditorSpinSlider()); + translation_slider[i]->set_label(desc[i]); + setup_spinner(translation_slider[i], false); + translation_grid->add_child(translation_slider[i]); + + rotation_slider[i] = memnew(EditorSpinSlider()); + rotation_slider[i]->set_label(desc[i]); + setup_spinner(rotation_slider[i], false); + rotation_grid->add_child(rotation_slider[i]); + + scale_slider[i] = memnew(EditorSpinSlider()); + scale_slider[i]->set_label(desc[i]); + setup_spinner(scale_slider[i], false); + scale_grid->add_child(scale_slider[i]); + } + + for (int i = 0; i < TRANSFORM_COMPONENTS; ++i) { + transform_slider[i] = memnew(EditorSpinSlider()); + transform_slider[i]->set_label(desc[i]); + setup_spinner(transform_slider[i], true); + transform_grid->add_child(transform_slider[i]); + } +} + +void BoneTransformEditor::setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner) { + spinner->set_flat(true); + spinner->set_min(-10000); + spinner->set_max(10000); + spinner->set_step(0.001f); + spinner->set_hide_slider(true); + spinner->set_allow_greater(true); + spinner->set_allow_lesser(true); + spinner->set_h_size_flags(SIZE_EXPAND_FILL); + + spinner->connect_compat("value_changed", this, "_value_changed", varray(is_transform_spinner)); +} + +void BoneTransformEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + create_editors(); + key_button->connect_compat("pressed", this, "_key_button_pressed"); + enabled_checkbox->connect_compat("toggled", this, "_checkbox_toggled"); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + const Color base = get_theme_color("accent_color", "Editor"); + const Color bg_color = get_theme_color("property_color", "Editor"); + const Color bg_lbl_color(bg_color.r, bg_color.g, bg_color.b, 0.5); + + for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { + Color c = base; + c.set_hsv(float(i % TRANSLATION_COMPONENTS) / TRANSLATION_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); + if (!translation_slider[i]) { + continue; + } + translation_slider[i]->set_custom_label_color(true, c); + } + + for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) { + Color c = base; + c.set_hsv(float(i % ROTATION_DEGREES_COMPONENTS) / ROTATION_DEGREES_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); + if (!rotation_slider[i]) { + continue; + } + rotation_slider[i]->set_custom_label_color(true, c); + } + + for (int i = 0; i < SCALE_COMPONENTS; i++) { + Color c = base; + c.set_hsv(float(i % SCALE_COMPONENTS) / SCALE_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); + if (!scale_slider[i]) { + continue; + } + scale_slider[i]->set_custom_label_color(true, c); + } + + for (int i = 0; i < TRANSFORM_COMPONENTS; i++) { + Color c = base; + c.set_hsv(float(i % TRANSFORM_COMPONENTS) / TRANSFORM_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); + if (!transform_slider[i]) { + continue; + } + transform_slider[i]->set_custom_label_color(true, c); + } + + break; + } + case NOTIFICATION_SORT_CHILDREN: { + const Ref<Font> font = get_theme_font("font", "Tree"); + + Point2 buffer; + buffer.x += get_theme_constant("inspector_margin", "Editor"); + buffer.y += font->get_height(); + buffer.y += get_theme_constant("vseparation", "Tree"); + + const float vector_height = translation_grid->get_size().y; + const float transform_height = transform_grid->get_size().y; + const float button_height = key_button->get_size().y; + + const float width = get_size().x - get_theme_constant("inspector_margin", "Editor"); + Vector<Rect2> input_rects; + if (keyable && section->get_vbox()->is_visible()) { + input_rects.push_back(Rect2(key_button->get_position() + buffer, Size2(width, button_height))); + } else { + input_rects.push_back(Rect2(0, 0, 0, 0)); + } + + if (section->get_vbox()->is_visible()) { + input_rects.push_back(Rect2(translation_grid->get_position() + buffer, Size2(width, vector_height))); + input_rects.push_back(Rect2(rotation_grid->get_position() + buffer, Size2(width, vector_height))); + input_rects.push_back(Rect2(scale_grid->get_position() + buffer, Size2(width, vector_height))); + input_rects.push_back(Rect2(transform_grid->get_position() + buffer, Size2(width, transform_height))); + } else { + const int32_t start = input_rects.size(); + const int32_t empty_input_rect_elements = 4; + const int32_t end = start + empty_input_rect_elements; + for (int i = start; i < end; ++i) { + input_rects.push_back(Rect2(0, 0, 0, 0)); + } + } + + for (int32_t i = 0; i < input_rects.size(); i++) { + background_rects[i] = input_rects[i]; + } + + update(); + break; + } + case NOTIFICATION_DRAW: { + const Color dark_color = get_theme_color("dark_color_2", "Editor"); + + for (int i = 0; i < 5; ++i) { + draw_rect(background_rects[i], dark_color); + } + + break; + } + } +} + +void BoneTransformEditor::_value_changed(const double p_value, const bool p_from_transform) { + if (updating) + return; + + if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") { + const Transform tform = compute_transform(p_from_transform); + + undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS); + undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int())); + undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), tform); + undo_redo->commit_action(); + } else if (property.get_slicec('/', 0) == "bones") { + const Transform tform = compute_transform(p_from_transform); + + undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS); + undo_redo->add_undo_property(skeleton, property, skeleton->get(property)); + undo_redo->add_do_property(skeleton, property, tform); + undo_redo->commit_action(); + } +} + +Transform BoneTransformEditor::compute_transform(const bool p_from_transform) const { + // Last modified was a raw transform column... + if (p_from_transform) { + Transform tform; + + for (int i = 0; i < BASIS_COMPONENTS; ++i) { + tform.basis[i / BASIS_SPLIT_COMPONENTS][i % BASIS_SPLIT_COMPONENTS] = transform_slider[i]->get_value(); + } + + for (int i = 0; i < TRANSLATION_COMPONENTS; ++i) { + tform.origin[i] = transform_slider[i + BASIS_COMPONENTS]->get_value(); + } + + return tform; + } + + return Transform( + Basis(Vector3(Math::deg2rad(rotation_slider[0]->get_value()), Math::deg2rad(rotation_slider[1]->get_value()), Math::deg2rad(rotation_slider[2]->get_value())), + Vector3(scale_slider[0]->get_value(), scale_slider[1]->get_value(), scale_slider[2]->get_value())), + Vector3(translation_slider[0]->get_value(), translation_slider[1]->get_value(), translation_slider[2]->get_value())); +} + +void BoneTransformEditor::update_enabled_checkbox() { + if (enabled_checkbox) { + const String path = "bones/" + property.get_slicec('/', 1) + "/enabled"; + const bool is_enabled = skeleton->get(path); + enabled_checkbox->set_pressed(is_enabled); + } +} + +void BoneTransformEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_value_changed", "value"), &BoneTransformEditor::_value_changed); + ClassDB::bind_method(D_METHOD("_key_button_pressed"), &BoneTransformEditor::_key_button_pressed); + ClassDB::bind_method(D_METHOD("_checkbox_toggled", "toggled"), &BoneTransformEditor::_checkbox_toggled); +} + +void BoneTransformEditor::_update_properties() { + if (updating) + return; + + if (skeleton == nullptr) + return; + + updating = true; + + Transform tform = skeleton->get(property); + _update_transform_properties(tform); +} + +void BoneTransformEditor::_update_custom_pose_properties() { + if (updating) + return; + + if (skeleton == nullptr) + return; + + updating = true; + + Transform tform = skeleton->get_bone_custom_pose(property.to_int()); + _update_transform_properties(tform); +} + +void BoneTransformEditor::_update_transform_properties(Transform tform) { + Quat rot = tform.get_basis(); + Vector3 rot_rad = rot.get_euler(); + Vector3 rot_degrees = Vector3(Math::rad2deg(rot_rad.x), Math::rad2deg(rot_rad.y), Math::rad2deg(rot_rad.z)); + Vector3 tr = tform.get_origin(); + Vector3 scale = tform.basis.get_scale(); + + for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { + translation_slider[i]->set_value(tr[i]); + } + + for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) { + rotation_slider[i]->set_value(rot_degrees[i]); + } + + for (int i = 0; i < SCALE_COMPONENTS; i++) { + scale_slider[i]->set_value(scale[i]); + } + + transform_slider[0]->set_value(tform.get_basis()[Vector3::AXIS_X].x); + transform_slider[1]->set_value(tform.get_basis()[Vector3::AXIS_X].y); + transform_slider[2]->set_value(tform.get_basis()[Vector3::AXIS_X].z); + transform_slider[3]->set_value(tform.get_basis()[Vector3::AXIS_Y].x); + transform_slider[4]->set_value(tform.get_basis()[Vector3::AXIS_Y].y); + transform_slider[5]->set_value(tform.get_basis()[Vector3::AXIS_Y].z); + transform_slider[6]->set_value(tform.get_basis()[Vector3::AXIS_Z].x); + transform_slider[7]->set_value(tform.get_basis()[Vector3::AXIS_Z].y); + transform_slider[8]->set_value(tform.get_basis()[Vector3::AXIS_Z].z); + + for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { + transform_slider[BASIS_COMPONENTS + i]->set_value(tform.get_origin()[i]); + } + + update_enabled_checkbox(); + updating = false; +} + +BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) : + translation_slider(), + rotation_slider(), + scale_slider(), + transform_slider(), + skeleton(p_skeleton), + key_button(nullptr), + enabled_checkbox(nullptr), + keyable(false), + toggle_enabled(false), + updating(false) { + undo_redo = EditorNode::get_undo_redo(); +} + +void BoneTransformEditor::set_target(const String &p_prop) { + property = p_prop; +} + +void BoneTransformEditor::set_keyable(const bool p_keyable) { + keyable = p_keyable; + if (key_button) { + key_button->set_visible(p_keyable); + } +} + +void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) { + toggle_enabled = p_enabled; + if (enabled_checkbox) { + enabled_checkbox->set_visible(p_enabled); + } +} + +void BoneTransformEditor::_key_button_pressed() { + if (skeleton == nullptr) + return; + + const BoneId bone_id = property.get_slicec('/', 1).to_int(); + const String name = skeleton->get_bone_name(bone_id); + + if (name.empty()) + return; + + // Need to normalize the basis before you key it + Transform tform = compute_transform(true); + tform.orthonormalize(); + AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform); +} + +void BoneTransformEditor::_checkbox_toggled(const bool p_toggled) { + if (enabled_checkbox) { + const String path = "bones/" + property.get_slicec('/', 1) + "/enabled"; + skeleton->set(path, p_toggled); + } +} + void Skeleton3DEditor::_on_click_option(int p_option) { if (!skeleton) { return; @@ -45,12 +417,14 @@ void Skeleton3DEditor::_on_click_option(int p_option) { switch (p_option) { case MENU_OPTION_CREATE_PHYSICAL_SKELETON: { create_physical_skeleton(); - } break; + break; + } } } void Skeleton3DEditor::create_physical_skeleton() { UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ERR_FAIL_COND(!get_tree()); Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner(); const int bc = skeleton->get_bone_count(); @@ -124,28 +498,164 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi return physical_bone; } -void Skeleton3DEditor::edit(Skeleton3D *p_node) { - skeleton = p_node; +Variant Skeleton3DEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + TreeItem *selected = joint_tree->get_selected(); + + if (!selected) + return Variant(); + + Ref<Texture> icon = selected->get_icon(0); + + VBoxContainer *vb = memnew(VBoxContainer); + HBoxContainer *hb = memnew(HBoxContainer); + TextureRect *tf = memnew(TextureRect); + tf->set_texture(icon); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hb->add_child(tf); + Label *label = memnew(Label(selected->get_text(0))); + hb->add_child(label); + vb->add_child(hb); + hb->set_modulate(Color(1, 1, 1, 1)); + + set_drag_preview(vb); + Dictionary drag_data; + drag_data["type"] = "nodes"; + drag_data["node"] = selected; + + return drag_data; } -void Skeleton3DEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed)); +bool Skeleton3DEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + TreeItem *target = joint_tree->get_item_at_position(p_point); + if (!target) + return false; + + const String path = target->get_metadata(0); + if (!path.begins_with("bones/")) + return false; + + TreeItem *selected = Object::cast_to<TreeItem>(Dictionary(p_data)["node"]); + if (target == selected) + return false; + + const String path2 = target->get_metadata(0); + if (!path2.begins_with("bones/")) + return false; + + return true; +} + +void Skeleton3DEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + if (!can_drop_data_fw(p_point, p_data, p_from)) + return; + + TreeItem *target = joint_tree->get_item_at_position(p_point); + TreeItem *selected = Object::cast_to<TreeItem>(Dictionary(p_data)["node"]); + + const BoneId target_boneidx = String(target->get_metadata(0)).get_slicec('/', 1).to_int(); + const BoneId selected_boneidx = String(selected->get_metadata(0)).get_slicec('/', 1).to_int(); + + move_skeleton_bone(skeleton->get_path(), selected_boneidx, target_boneidx); +} + +void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx) { + Node *node = get_node_or_null(p_skeleton_path); + Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node); + ERR_FAIL_NULL(skeleton); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Set Bone Parentage")); + // If the target is a child of ourselves, we move only *us* and not our children + if (skeleton->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) { + const BoneId parent_idx = skeleton->get_bone_parent(p_selected_boneidx); + const int bone_count = skeleton->get_bone_count(); + for (BoneId i = 0; i < bone_count; ++i) { + if (skeleton->get_bone_parent(i) == p_selected_boneidx) { + ur->add_undo_method(skeleton, "set_bone_parent", i, skeleton->get_bone_parent(i)); + ur->add_do_method(skeleton, "set_bone_parent", i, parent_idx); + skeleton->set_bone_parent(i, parent_idx); + } + } } + ur->add_undo_method(skeleton, "set_bone_parent", p_selected_boneidx, skeleton->get_bone_parent(p_selected_boneidx)); + ur->add_do_method(skeleton, "set_bone_parent", p_selected_boneidx, p_target_boneidx); + skeleton->set_bone_parent(p_selected_boneidx, p_target_boneidx); + + update_joint_tree(); + ur->commit_action(); } -void Skeleton3DEditor::_node_removed(Node *p_node) { - if (p_node == skeleton) { - skeleton = nullptr; - options->hide(); +void Skeleton3DEditor::_joint_tree_selection_changed() { + TreeItem *selected = joint_tree->get_selected(); + const String path = selected->get_metadata(0); + + if (path.begins_with("bones/")) { + const int b_idx = path.get_slicec('/', 1).to_int(); + const String bone_path = "bones/" + itos(b_idx) + "/"; + + pose_editor->set_target(bone_path + "pose"); + rest_editor->set_target(bone_path + "rest"); + custom_pose_editor->set_target(bone_path + "custom_pose"); + + pose_editor->set_visible(true); + rest_editor->set_visible(true); + custom_pose_editor->set_visible(true); } } -void Skeleton3DEditor::_bind_methods() { +void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos) { +} + +void Skeleton3DEditor::_update_properties() { + if (rest_editor) + rest_editor->_update_properties(); + if (pose_editor) + pose_editor->_update_properties(); + if (custom_pose_editor) + custom_pose_editor->_update_custom_pose_properties(); +} + +void Skeleton3DEditor::update_joint_tree() { + joint_tree->clear(); + + if (skeleton == nullptr) + return; + + TreeItem *root = joint_tree->create_item(); + + Map<int, TreeItem *> items; + + items.insert(-1, root); + + const Vector<int> &joint_porder = skeleton->get_bone_process_orders(); + + Ref<Texture> bone_icon = get_theme_icon("Skeleton3D", "EditorIcons"); + + for (int i = 0; i < joint_porder.size(); ++i) { + const int b_idx = joint_porder[i]; + + const int p_idx = skeleton->get_bone_parent(b_idx); + TreeItem *p_item = items.find(p_idx)->get(); + + TreeItem *joint_item = joint_tree->create_item(p_item); + items.insert(b_idx, joint_item); + + joint_item->set_text(0, skeleton->get_bone_name(b_idx)); + joint_item->set_icon(0, bone_icon); + joint_item->set_selectable(0, true); + joint_item->set_metadata(0, "bones/" + itos(b_idx)); + } +} + +void Skeleton3DEditor::update_editors() { } -Skeleton3DEditor::Skeleton3DEditor() { - skeleton = nullptr; +void Skeleton3DEditor::create_editors() { + set_h_size_flags(SIZE_EXPAND_FILL); + add_theme_constant_override("separation", 0); + + set_focus_mode(FOCUS_ALL); + + // Create Top Menu Bar options = memnew(MenuButton); Node3DEditor::get_singleton()->add_control_to_menu_panel(options); @@ -156,31 +666,119 @@ Skeleton3DEditor::Skeleton3DEditor() { options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_option)); options->hide(); + + const Color section_color = get_theme_color("prop_subsection", "Editor"); + + EditorInspectorSection *bones_section = memnew(EditorInspectorSection); + bones_section->setup("bones", "Bones", skeleton, section_color, true); + add_child(bones_section); + bones_section->unfold(); + + ScrollContainer *s_con = memnew(ScrollContainer); + s_con->set_h_size_flags(SIZE_EXPAND_FILL); + s_con->set_custom_minimum_size(Size2(1, 350) * EDSCALE); + bones_section->get_vbox()->add_child(s_con); + + joint_tree = memnew(Tree); + joint_tree->set_columns(1); + joint_tree->set_focus_mode(Control::FocusMode::FOCUS_NONE); + joint_tree->set_select_mode(Tree::SELECT_SINGLE); + joint_tree->set_hide_root(true); + joint_tree->set_v_size_flags(SIZE_EXPAND_FILL); + joint_tree->set_h_size_flags(SIZE_EXPAND_FILL); + joint_tree->set_allow_rmb_select(true); + joint_tree->set_drag_forwarding(this); + s_con->add_child(joint_tree); + + pose_editor = memnew(BoneTransformEditor(skeleton)); + pose_editor->set_label(TTR("Bone Pose")); + pose_editor->set_keyable(AnimationPlayerEditor::singleton->get_track_editor()->has_keying()); + pose_editor->set_toggle_enabled(true); + pose_editor->set_visible(false); + add_child(pose_editor); + + rest_editor = memnew(BoneTransformEditor(skeleton)); + rest_editor->set_label(TTR("Bone Rest")); + rest_editor->set_visible(false); + add_child(rest_editor); + + custom_pose_editor = memnew(BoneTransformEditor(skeleton)); + custom_pose_editor->set_label(TTR("Bone Custom Pose")); + custom_pose_editor->set_visible(false); + add_child(custom_pose_editor); +} + +void Skeleton3DEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + create_editors(); + update_joint_tree(); + update_editors(); + + get_tree()->connect_compat("node_removed", this, "_node_removed", Vector<Variant>(), Object::CONNECT_ONESHOT); + joint_tree->connect_compat("item_selected", this, "_joint_tree_selection_changed"); + joint_tree->connect_compat("item_rmb_selected", this, "_joint_tree_rmb_select"); +#ifdef TOOLS_ENABLED + skeleton->connect_compat("pose_updated", this, "_update_properties"); +#endif // TOOLS_ENABLED + + break; + } + } } -Skeleton3DEditor::~Skeleton3DEditor() {} +void Skeleton3DEditor::_node_removed(Node *p_node) { + if (skeleton && p_node == skeleton) { + skeleton = nullptr; + options->hide(); + } -void Skeleton3DEditorPlugin::edit(Object *p_object) { - skeleton_editor->edit(Object::cast_to<Skeleton3D>(p_object)); + _update_properties(); } -bool Skeleton3DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("Skeleton3D"); +void Skeleton3DEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_node_removed"), &Skeleton3DEditor::_node_removed); + ClassDB::bind_method(D_METHOD("_joint_tree_selection_changed"), &Skeleton3DEditor::_joint_tree_selection_changed); + ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &Skeleton3DEditor::_joint_tree_rmb_select); + ClassDB::bind_method(D_METHOD("_update_properties"), &Skeleton3DEditor::_update_properties); + ClassDB::bind_method(D_METHOD("_on_click_option"), &Skeleton3DEditor::_on_click_option); + + ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &Skeleton3DEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("move_skeleton_bone"), &Skeleton3DEditor::move_skeleton_bone); +} + +Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *p_skeleton) : + editor(p_editor), + editor_plugin(e_plugin), + skeleton(p_skeleton) { } -void Skeleton3DEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - skeleton_editor->options->show(); - } else { - skeleton_editor->options->hide(); - skeleton_editor->edit(nullptr); +Skeleton3DEditor::~Skeleton3DEditor() { + if (options) { + Node3DEditor::get_singleton()->remove_control_from_menu_panel(options); } } +bool EditorInspectorPluginSkeleton::can_handle(Object *p_object) { + return Object::cast_to<Skeleton3D>(p_object) != nullptr; +} + +void EditorInspectorPluginSkeleton::parse_begin(Object *p_object) { + Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_object); + ERR_FAIL_COND(!skeleton); + + Skeleton3DEditor *skel_editor = memnew(Skeleton3DEditor(this, editor, skeleton)); + add_custom_control(skel_editor); +} + Skeleton3DEditorPlugin::Skeleton3DEditorPlugin(EditorNode *p_node) { editor = p_node; - skeleton_editor = memnew(Skeleton3DEditor); - editor->get_viewport()->add_child(skeleton_editor); -} -Skeleton3DEditorPlugin::~Skeleton3DEditorPlugin() {} + Ref<EditorInspectorPluginSkeleton> skeleton_plugin; + skeleton_plugin.instance(); + skeleton_plugin->editor = editor; + + EditorInspector::add_inspector_plugin(skeleton_plugin); +} diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index af9ebb6246..8b0639ed92 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -35,11 +35,97 @@ #include "editor/editor_plugin.h" #include "scene/3d/skeleton_3d.h" +class EditorInspectorPluginSkeleton; +class Joint; class PhysicalBone3D; -class Joint3D; +class Skeleton3DEditorPlugin; +class Button; +class CheckBox; -class Skeleton3DEditor : public Node { - GDCLASS(Skeleton3DEditor, Node); +class BoneTransformEditor : public VBoxContainer { + GDCLASS(BoneTransformEditor, VBoxContainer); + + static const int32_t TRANSLATION_COMPONENTS = 3; + static const int32_t ROTATION_DEGREES_COMPONENTS = 3; + static const int32_t SCALE_COMPONENTS = 3; + static const int32_t BASIS_COMPONENTS = 9; + static const int32_t BASIS_SPLIT_COMPONENTS = 3; + static const int32_t TRANSFORM_COMPONENTS = 12; + static const int32_t TRANSFORM_SPLIT_COMPONENTS = 3; + static const int32_t TRANSFORM_CONTROL_COMPONENTS = 3; + + EditorInspectorSection *section; + + GridContainer *translation_grid; + GridContainer *rotation_grid; + GridContainer *scale_grid; + GridContainer *transform_grid; + + EditorSpinSlider *translation_slider[TRANSLATION_COMPONENTS]; + EditorSpinSlider *rotation_slider[ROTATION_DEGREES_COMPONENTS]; + EditorSpinSlider *scale_slider[SCALE_COMPONENTS]; + EditorSpinSlider *transform_slider[TRANSFORM_COMPONENTS]; + + Rect2 background_rects[5]; + + Skeleton3D *skeleton; + String property; + + UndoRedo *undo_redo; + + Button *key_button; + CheckBox *enabled_checkbox; + + bool keyable; + bool toggle_enabled; + bool updating; + + String label; + + void create_editors(); + void setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner); + + void _value_changed(const double p_value, const bool p_from_transform); + + Transform compute_transform(const bool p_from_transform) const; + + void update_enabled_checkbox(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + BoneTransformEditor(Skeleton3D *p_skeleton); + + // Which transform target to modify + void set_target(const String &p_prop); + void set_label(const String &p_label) { label = p_label; } + + void _update_properties(); + void _update_custom_pose_properties(); + void _update_transform_properties(Transform p_transform); + + // Can/cannot modify the spinner values for the Transform + void set_read_only(const bool p_read_only); + + // Transform can be keyed, whether or not to show the button + void set_keyable(const bool p_keyable); + + // Bone can be toggled enabled or disabled, whether or not to show the checkbox + void set_toggle_enabled(const bool p_enabled); + + // Key Transform Button pressed + void _key_button_pressed(); + + // Bone Enabled Checkbox toggled + void _checkbox_toggled(const bool p_toggled); +}; + +class Skeleton3DEditor : public VBoxContainer { + GDCLASS(Skeleton3DEditor, VBoxContainer); + + friend class Skeleton3DEditorPlugin; enum Menu { MENU_OPTION_CREATE_PHYSICAL_SKELETON @@ -51,44 +137,78 @@ class Skeleton3DEditor : public Node { BoneInfo() {} }; + EditorNode *editor; + EditorInspectorPluginSkeleton *editor_plugin; + Skeleton3D *skeleton; + Tree *joint_tree; + BoneTransformEditor *rest_editor; + BoneTransformEditor *pose_editor; + BoneTransformEditor *custom_pose_editor; + MenuButton *options; + EditorFileDialog *file_dialog; + + UndoRedo *undo_redo; void _on_click_option(int p_option); + void _file_selected(const String &p_file); - friend class Skeleton3DEditorPlugin; + EditorFileDialog *file_export_lib; + + void update_joint_tree(); + void update_editors(); + + void create_editors(); + + void create_physical_skeleton(); + PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos); + + Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); + bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); protected: void _notification(int p_what); void _node_removed(Node *p_node); static void _bind_methods(); - void create_physical_skeleton(); - PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos); - public: - void edit(Skeleton3D *p_node); + void move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx); - Skeleton3DEditor(); + Skeleton3D *get_skeleton() const { return skeleton; }; + + void _joint_tree_selection_changed(); + void _joint_tree_rmb_select(const Vector2 &p_pos); + + void _update_properties(); + + Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *skeleton); ~Skeleton3DEditor(); }; +class EditorInspectorPluginSkeleton : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginSkeleton, EditorInspectorPlugin); + + friend class Skeleton3DEditorPlugin; + + EditorNode *editor; + +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); +}; + class Skeleton3DEditorPlugin : public EditorPlugin { GDCLASS(Skeleton3DEditorPlugin, EditorPlugin); EditorNode *editor; - Skeleton3DEditor *skeleton_editor; public: - virtual String get_name() const { return "Skeleton3D"; } - virtual bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - Skeleton3DEditorPlugin(EditorNode *p_node); - ~Skeleton3DEditorPlugin(); + + virtual String get_name() const { return "Skeleton3D"; } }; #endif // SKELETON_3D_EDITOR_PLUGIN_H diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index b21586a6b0..f5fafb68a5 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "sprite_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/math/geometry_2d.h" #include "editor/editor_scale.h" #include "scene/2d/collision_polygon_2d.h" #include "scene/2d/light_occluder_2d.h" @@ -233,7 +234,7 @@ void Sprite2DEditor::_update_mesh_data() { computed_vertices.push_back(vtx); } - Vector<int> poly = Geometry::triangulate_polygon(lines[j]); + Vector<int> poly = Geometry2D::triangulate_polygon(lines[j]); for (int i = 0; i < poly.size(); i += 3) { for (int k = 0; k < 3; k++) { diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 3010d72d81..3281a59c1c 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -194,6 +194,21 @@ void TileMapEditor::_palette_multi_selected(int index, bool selected) { _update_palette(); } +void TileMapEditor::_palette_input(const Ref<InputEvent> &p_event) { + const Ref<InputEventMouseButton> mb = p_event; + + // Zoom in/out using Ctrl + mouse wheel. + if (mb.is_valid() && mb->is_pressed() && mb->get_command()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) { + size_slider->set_value(size_slider->get_value() + 0.2); + } + + if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) { + size_slider->set_value(size_slider->get_value() - 0.2); + } + } +} + void TileMapEditor::_canvas_mouse_enter() { mouse_over = true; CanvasItemEditor::get_singleton()->update_viewport(); @@ -1913,6 +1928,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { palette->add_theme_constant_override("vseparation", 8 * EDSCALE); palette->connect("item_selected", callable_mp(this, &TileMapEditor::_palette_selected)); palette->connect("multi_selected", callable_mp(this, &TileMapEditor::_palette_multi_selected)); + palette->connect("gui_input", callable_mp(this, &TileMapEditor::_palette_input)); palette_container->add_child(palette); // Add message for when no texture is selected. @@ -1951,7 +1967,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { toolbar->add_child(paint_button); bucket_fill_button = memnew(ToolButton); - bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G)); + bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_B)); bucket_fill_button->connect("pressed", callable_mp(this, &TileMapEditor::_button_tool_select), make_binds(TOOL_BUCKET)); bucket_fill_button->set_toggle_mode(true); toolbar->add_child(bucket_fill_button); diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index 5f82d7bfb8..e25e2d2add 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -182,6 +182,7 @@ class TileMapEditor : public VBoxContainer { void _menu_option(int p_option); void _palette_selected(int index); void _palette_multi_selected(int index, bool selected); + void _palette_input(const Ref<InputEvent> &p_event); Dictionary _create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord); void _start_undo(const String &p_action); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 644facd5bd..b3d4b391d3 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -569,6 +569,7 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { scroll = memnew(ScrollContainer); main_vb->add_child(scroll); scroll->set_v_size_flags(SIZE_EXPAND_FILL); + scroll->connect("gui_input", callable_mp(this, &TileSetEditor::_on_scroll_container_input)); scroll->set_clip_contents(true); empty_message = memnew(Label); @@ -1198,6 +1199,27 @@ bool TileSetEditor::is_within_grabbing_distance_of_first_point(const Vector2 &p_ return distance < p_grab_threshold; } +void TileSetEditor::_on_scroll_container_input(const Ref<InputEvent> &p_event) { + const Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid()) { + // Zoom in/out using Ctrl + mouse wheel. This is done on the ScrollContainer + // to allow performing this action anywhere, even if the cursor isn't + // hovering the texture in the workspace. + if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed() && mb->get_control()) { + print_line("zooming in"); + _zoom_in(); + // Don't scroll up after zooming in. + accept_event(); + } else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed() && mb->get_control()) { + print_line("zooming out"); + _zoom_out(); + // Don't scroll down after zooming out. + accept_event(); + } + } +} + void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if (tileset.is_null() || !get_current_texture().is_valid()) { return; @@ -1214,8 +1236,8 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } current_tile_region.position += WORKSPACE_MARGIN; - Ref<InputEventMouseButton> mb = p_ie; - Ref<InputEventMouseMotion> mm = p_ie; + const Ref<InputEventMouseButton> mb = p_ie; + const Ref<InputEventMouseMotion> mm = p_ie; if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && !creating_shape) { @@ -1239,13 +1261,6 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { delete tiles; } } - - // Mouse Wheel Event - if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed() && mb->get_control()) { - _zoom_in(); - } else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed() && mb->get_control()) { - _zoom_out(); - } } // Drag Middle Mouse if (mm.is_valid()) { diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 827325cfd7..2955dda244 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -200,6 +200,7 @@ private: void _on_workspace_overlay_draw(); void _on_workspace_draw(); void _on_workspace_process(); + void _on_scroll_container_input(const Ref<InputEvent> &p_event); void _on_workspace_input(const Ref<InputEvent> &p_ie); void _on_tool_clicked(int p_tool); void _on_priority_changed(float val); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index dc5ff6a5eb..499f7d4017 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -916,6 +916,8 @@ public: icon = nullptr; icon_needs_reload = true; hover = false; + + set_focus_mode(FocusMode::FOCUS_ALL); } void set_is_favorite(bool fav) { @@ -1739,6 +1741,10 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) { select_project(clicked_index); } + if (_selected_project_keys.has(clicked_project.project_key)) { + clicked_project.control->grab_focus(); + } + emit_signal(SIGNAL_SELECTION_CHANGED); if (!mb->get_control() && mb->is_doubleclick()) { diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index 6888ebdc71..c6c93fae83 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -357,7 +357,7 @@ void PropertySelector::_item_selected() { if (E) { for (int i = 0; i < E->get().properties.size(); i++) { if (E->get().properties[i].name == name) { - text = E->get().properties[i].description; + text = DTR(E->get().properties[i].description); } } } @@ -372,7 +372,7 @@ void PropertySelector::_item_selected() { if (E) { for (int i = 0; i < E->get().methods.size(); i++) { if (E->get().methods[i].name == name) { - text = E->get().methods[i].description; + text = DTR(E->get().methods[i].description); } } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index a81a2ff4e9..c37d32b26b 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2557,6 +2557,11 @@ void SceneTreeDock::_focus_node() { } void SceneTreeDock::attach_script_to_selected(bool p_extend) { + if (ScriptServer::get_language_count() == 0) { + EditorNode::get_singleton()->show_warning(TTR("Cannot attach a script: there are no languages registered.\nThis is probably because this editor was built with all language modules disabled.")); + return; + } + if (!profile_allow_script_editing) { return; } diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 04fbfdff9d..ae5229b628 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -783,7 +783,7 @@ ScriptCreateDialog::ScriptCreateDialog() { gc->add_child(memnew(Label(TTR("Language:")))); gc->add_child(language_menu); - default_language = 0; + default_language = -1; for (int i = 0; i < ScriptServer::get_language_count(); i++) { String lang = ScriptServer::get_language(i)->get_name(); language_menu->add_item(lang); @@ -791,8 +791,9 @@ ScriptCreateDialog::ScriptCreateDialog() { default_language = i; } } - - language_menu->select(default_language); + if (default_language >= 0) { + language_menu->select(default_language); + } current_language = default_language; language_menu->connect("item_selected", callable_mp(this, &ScriptCreateDialog::_lang_changed)); diff --git a/main/main.cpp b/main/main.cpp index f05032e68c..94dd895a26 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1645,7 +1645,11 @@ bool Main::start() { print_line("Loading docs..."); for (int i = 0; i < _doc_data_class_path_count; i++) { - String path = doc_tool.plus_file(_doc_data_class_paths[i].path); + // Custom modules are always located by absolute path. + String path = _doc_data_class_paths[i].path; + if (path.is_rel_path()) { + path = doc_tool.plus_file(path); + } String name = _doc_data_class_paths[i].name; doc_data_classes[name] = path; if (!checked_paths.has(path)) { diff --git a/main/tests/test_math.cpp b/main/tests/test_math.cpp index 11aa164709..5f84bad4e9 100644 --- a/main/tests/test_math.cpp +++ b/main/tests/test_math.cpp @@ -33,6 +33,7 @@ #include "core/math/basis.h" #include "core/math/camera_matrix.h" #include "core/math/delaunay_3d.h" +#include "core/math/geometry_2d.h" #include "core/math/math_funcs.h" #include "core/math/transform.h" #include "core/method_ptrcall.h" @@ -482,7 +483,7 @@ MainLoop *test() { float gb = (rgbe >> 9) & 0x1ff; float bb = (rgbe >> 18) & 0x1ff; float eb = (rgbe >> 27); - float mb = Math::pow(2, eb - 15.0 - 9.0); + float mb = Math::pow(2.0, eb - 15.0 - 9.0); float rd = rb * mb; float gd = gb * mb; float bd = bb * mb; @@ -635,7 +636,7 @@ MainLoop *test() { b["44"] = 4; } - print_line("inters: " + rtos(Geometry::segment_intersects_circle(Vector2(-5, 0), Vector2(-2, 0), Vector2(), 1.0))); + print_line("inters: " + rtos(Geometry2D::segment_intersects_circle(Vector2(-5, 0), Vector2(-2, 0), Vector2(), 1.0))); print_line("cross: " + Vector3(1, 2, 3).cross(Vector3(4, 5, 7))); print_line("dot: " + rtos(Vector3(1, 2, 3).dot(Vector3(4, 5, 7)))); diff --git a/main/tests/test_oa_hash_map.cpp b/main/tests/test_oa_hash_map.cpp index 719817baf4..9182f66b61 100644 --- a/main/tests/test_oa_hash_map.cpp +++ b/main/tests/test_oa_hash_map.cpp @@ -210,6 +210,89 @@ MainLoop *test() { } } + // Test map with 0 capacity. + { + OAHashMap<int, String> original_map(0); + original_map.set(1, "1"); + OS::get_singleton()->print("OAHashMap 0 capacity initialization passed.\n"); + } + + // Test copy constructor. + { + OAHashMap<int, String> original_map; + original_map.set(1, "1"); + original_map.set(2, "2"); + original_map.set(3, "3"); + original_map.set(4, "4"); + original_map.set(5, "5"); + + OAHashMap<int, String> map_copy(original_map); + + bool pass = true; + for ( + OAHashMap<int, String>::Iterator it = original_map.iter(); + it.valid; + it = original_map.next_iter(it)) { + if (map_copy.lookup_ptr(*it.key) == nullptr) { + pass = false; + } + if (*it.value != *map_copy.lookup_ptr(*it.key)) { + pass = false; + } + } + if (pass) { + OS::get_singleton()->print("OAHashMap copy constructor test passed.\n"); + } else { + OS::get_singleton()->print("OAHashMap copy constructor test FAILED.\n"); + } + + map_copy.set(1, "Random String"); + if (*map_copy.lookup_ptr(1) == *original_map.lookup_ptr(1)) { + OS::get_singleton()->print("OAHashMap copy constructor, atomic copy test FAILED.\n"); + } else { + OS::get_singleton()->print("OAHashMap copy constructor, atomic copy test passed.\n"); + } + } + + // Test assign operator. + { + OAHashMap<int, String> original_map; + original_map.set(1, "1"); + original_map.set(2, "2"); + original_map.set(3, "3"); + original_map.set(4, "4"); + original_map.set(5, "5"); + + OAHashMap<int, String> map_copy(100000); + map_copy.set(1, "Just a string."); + map_copy = original_map; + + bool pass = true; + for ( + OAHashMap<int, String>::Iterator it = map_copy.iter(); + it.valid; + it = map_copy.next_iter(it)) { + if (original_map.lookup_ptr(*it.key) == nullptr) { + pass = false; + } + if (*it.value != *original_map.lookup_ptr(*it.key)) { + pass = false; + } + } + if (pass) { + OS::get_singleton()->print("OAHashMap assign operation test passed.\n"); + } else { + OS::get_singleton()->print("OAHashMap assign operation test FAILED.\n"); + } + + map_copy.set(1, "Random String"); + if (*map_copy.lookup_ptr(1) == *original_map.lookup_ptr(1)) { + OS::get_singleton()->print("OAHashMap assign operation atomic copy test FAILED.\n"); + } else { + OS::get_singleton()->print("OAHashMap assign operation atomic copy test passed.\n"); + } + } + return nullptr; } diff --git a/main/tests/test_physics_3d.cpp b/main/tests/test_physics_3d.cpp index fe54ece98e..dfe2e946cf 100644 --- a/main/tests/test_physics_3d.cpp +++ b/main/tests/test_physics_3d.cpp @@ -136,9 +136,9 @@ protected: /* BOX SHAPE */ - Vector<Plane> box_planes = Geometry::build_box_planes(Vector3(0.5, 0.5, 0.5)); + Vector<Plane> box_planes = Geometry3D::build_box_planes(Vector3(0.5, 0.5, 0.5)); RID box_mesh = vs->mesh_create(); - Geometry::MeshData box_data = Geometry::build_convex_mesh(box_planes); + Geometry3D::MeshData box_data = Geometry3D::build_convex_mesh(box_planes); vs->mesh_add_surface_from_mesh_data(box_mesh, box_data); type_mesh_map[PhysicsServer3D::SHAPE_BOX] = box_mesh; @@ -148,10 +148,10 @@ protected: /* CAPSULE SHAPE */ - Vector<Plane> capsule_planes = Geometry::build_capsule_planes(0.5, 0.7, 12, Vector3::AXIS_Z); + Vector<Plane> capsule_planes = Geometry3D::build_capsule_planes(0.5, 0.7, 12, Vector3::AXIS_Z); RID capsule_mesh = vs->mesh_create(); - Geometry::MeshData capsule_data = Geometry::build_convex_mesh(capsule_planes); + Geometry3D::MeshData capsule_data = Geometry3D::build_convex_mesh(capsule_planes); vs->mesh_add_surface_from_mesh_data(capsule_mesh, capsule_data); type_mesh_map[PhysicsServer3D::SHAPE_CAPSULE] = capsule_mesh; @@ -165,10 +165,10 @@ protected: /* CONVEX SHAPE */ - Vector<Plane> convex_planes = Geometry::build_cylinder_planes(0.5, 0.7, 5, Vector3::AXIS_Z); + Vector<Plane> convex_planes = Geometry3D::build_cylinder_planes(0.5, 0.7, 5, Vector3::AXIS_Z); RID convex_mesh = vs->mesh_create(); - Geometry::MeshData convex_data = Geometry::build_convex_mesh(convex_planes); + Geometry3D::MeshData convex_data = Geometry3D::build_convex_mesh(convex_planes); QuickHull::build(convex_data.vertices, convex_data); vs->mesh_add_surface_from_mesh_data(convex_mesh, convex_data); @@ -341,10 +341,10 @@ public: RenderingServer *vs = RenderingServer::get_singleton(); PhysicsServer3D *ps = PhysicsServer3D::get_singleton(); - Vector<Plane> capsule_planes = Geometry::build_capsule_planes(0.5, 1, 12, 5, Vector3::AXIS_Y); + Vector<Plane> capsule_planes = Geometry3D::build_capsule_planes(0.5, 1, 12, 5, Vector3::AXIS_Y); RID capsule_mesh = vs->mesh_create(); - Geometry::MeshData capsule_data = Geometry::build_convex_mesh(capsule_planes); + Geometry3D::MeshData capsule_data = Geometry3D::build_convex_mesh(capsule_planes); vs->mesh_add_surface_from_mesh_data(capsule_mesh, capsule_data); type_mesh_map[PhysicsServer3D::SHAPE_CAPSULE] = capsule_mesh; diff --git a/main/tests/test_render.cpp b/main/tests/test_render.cpp index afc09374b9..d936dd72e7 100644 --- a/main/tests/test_render.cpp +++ b/main/tests/test_render.cpp @@ -79,8 +79,8 @@ public: Vector<Vector3> vts; /* - Vector<Plane> sp = Geometry::build_sphere_planes(2,5,5); - Geometry::MeshData md2 = Geometry::build_convex_mesh(sp); + Vector<Plane> sp = Geometry3D::build_sphere_planes(2,5,5); + Geometry3D::MeshData md2 = Geometry3D::build_convex_mesh(sp); vts=md2.vertices; */ /* @@ -118,7 +118,7 @@ public: vts.push_back(Vector3(-1, 1, -1)); vts.push_back(Vector3(-1, -1, -1)); - Geometry::MeshData md; + Geometry3D::MeshData md; Error err = QuickHull::build(vts, md); print_line("ERR: " + itos(err)); test_cube = vs->mesh_create(); diff --git a/methods.py b/methods.py index 805ae256c3..756db19e9a 100644 --- a/methods.py +++ b/methods.py @@ -2,6 +2,7 @@ import os import re import glob import subprocess +from collections import OrderedDict def add_source_files(self, sources, files, warn_duplicates=True): @@ -137,37 +138,47 @@ def parse_cg_file(fname, uniforms, sizes, conditionals): fs.close() -def detect_modules(): +def detect_modules(at_path): + module_list = OrderedDict() # name : path - module_list = [] + modules_glob = os.path.join(at_path, "*") + files = glob.glob(modules_glob) + files.sort() # so register_module_types does not change that often, and also plugins are registered in alphabetic order + + for x in files: + if not is_module(x): + continue + name = os.path.basename(x) + path = x.replace("\\", "/") # win32 + module_list[name] = path + + return module_list + + +def is_module(path): + return os.path.isdir(path) and os.path.exists(os.path.join(path, "SCsub")) + + +def write_modules(module_list): includes_cpp = "" + preregister_cpp = "" register_cpp = "" unregister_cpp = "" - preregister_cpp = "" - files = glob.glob("modules/*") - files.sort() # so register_module_types does not change that often, and also plugins are registered in alphabetic order - for x in files: - if not os.path.isdir(x): - continue - if not os.path.exists(x + "/config.py"): - continue - x = x.replace("modules/", "") # rest of world - x = x.replace("modules\\", "") # win32 - module_list.append(x) + for name, path in module_list.items(): try: - with open("modules/" + x + "/register_types.h"): - includes_cpp += '#include "modules/' + x + '/register_types.h"\n' - register_cpp += "#ifdef MODULE_" + x.upper() + "_ENABLED\n" - register_cpp += "\tregister_" + x + "_types();\n" - register_cpp += "#endif\n" - preregister_cpp += "#ifdef MODULE_" + x.upper() + "_ENABLED\n" - preregister_cpp += "#ifdef MODULE_" + x.upper() + "_HAS_PREREGISTER\n" - preregister_cpp += "\tpreregister_" + x + "_types();\n" + with open(os.path.join(path, "register_types.h")): + includes_cpp += '#include "' + path + '/register_types.h"\n' + preregister_cpp += "#ifdef MODULE_" + name.upper() + "_ENABLED\n" + preregister_cpp += "#ifdef MODULE_" + name.upper() + "_HAS_PREREGISTER\n" + preregister_cpp += "\tpreregister_" + name + "_types();\n" preregister_cpp += "#endif\n" preregister_cpp += "#endif\n" - unregister_cpp += "#ifdef MODULE_" + x.upper() + "_ENABLED\n" - unregister_cpp += "\tunregister_" + x + "_types();\n" + register_cpp += "#ifdef MODULE_" + name.upper() + "_ENABLED\n" + register_cpp += "\tregister_" + name + "_types();\n" + register_cpp += "#endif\n" + unregister_cpp += "#ifdef MODULE_" + name.upper() + "_ENABLED\n" + unregister_cpp += "\tunregister_" + name + "_types();\n" unregister_cpp += "#endif\n" except IOError: pass @@ -202,13 +213,48 @@ void unregister_module_types() { with open("modules/register_module_types.gen.cpp", "w") as f: f.write(modules_cpp) - return module_list + +def convert_custom_modules_path(path): + if not path: + return path + err_msg = "Build option 'custom_modules' must %s" + if not os.path.isdir(path): + raise ValueError(err_msg % "point to an existing directory.") + if os.path.realpath(path) == os.path.realpath("modules"): + raise ValueError(err_msg % "be a directory other than built-in `modules` directory.") + if is_module(path): + raise ValueError(err_msg % "point to a directory with modules, not a single module.") + return os.path.realpath(os.path.expanduser(path)) def disable_module(self): self.disabled_modules.append(self.current_module) +def module_check_dependencies(self, module, dependencies): + """ + Checks if module dependencies are enabled for a given module, + and prints a warning if they aren't. + Meant to be used in module `can_build` methods. + Returns a boolean (True if dependencies are satisfied). + """ + missing_deps = [] + for dep in dependencies: + opt = "module_{}_enabled".format(dep) + if not opt in self or not self[opt]: + missing_deps.append(dep) + + if missing_deps != []: + print( + "Disabling '{}' module as the following dependencies are not satisfied: {}".format( + module, ", ".join(missing_deps) + ) + ) + return False + else: + return True + + def use_windows_spawn_fix(self, platform=None): if os.name != "nt": @@ -634,3 +680,126 @@ def using_gcc(env): def using_clang(env): return "clang" in os.path.basename(env["CC"]) + + +def show_progress(env): + import sys + from SCons.Script import Progress, Command, AlwaysBuild + + screen = sys.stdout + # Progress reporting is not available in non-TTY environments since it + # messes with the output (for example, when writing to a file) + show_progress = env["progress"] and sys.stdout.isatty() + node_count = 0 + node_count_max = 0 + node_count_interval = 1 + node_count_fname = str(env.Dir("#")) + "/.scons_node_count" + + import time, math + + class cache_progress: + # The default is 1 GB cache and 12 hours half life + def __init__(self, path=None, limit=1073741824, half_life=43200): + self.path = path + self.limit = limit + self.exponent_scale = math.log(2) / half_life + if env["verbose"] and path != None: + screen.write( + "Current cache limit is {} (used: {})\n".format( + self.convert_size(limit), self.convert_size(self.get_size(path)) + ) + ) + self.delete(self.file_list()) + + def __call__(self, node, *args, **kw): + nonlocal node_count, node_count_max, node_count_interval, node_count_fname, show_progress + if show_progress: + # Print the progress percentage + node_count += node_count_interval + if node_count_max > 0 and node_count <= node_count_max: + screen.write("\r[%3d%%] " % (node_count * 100 / node_count_max)) + screen.flush() + elif node_count_max > 0 and node_count > node_count_max: + screen.write("\r[100%] ") + screen.flush() + else: + screen.write("\r[Initial build] ") + screen.flush() + + def delete(self, files): + if len(files) == 0: + return + if env["verbose"]: + # Utter something + screen.write("\rPurging %d %s from cache...\n" % (len(files), len(files) > 1 and "files" or "file")) + [os.remove(f) for f in files] + + def file_list(self): + if self.path is None: + # Nothing to do + return [] + # Gather a list of (filename, (size, atime)) within the + # cache directory + file_stat = [(x, os.stat(x)[6:8]) for x in glob.glob(os.path.join(self.path, "*", "*"))] + if file_stat == []: + # Nothing to do + return [] + # Weight the cache files by size (assumed to be roughly + # proportional to the recompilation time) times an exponential + # decay since the ctime, and return a list with the entries + # (filename, size, weight). + current_time = time.time() + file_stat = [(x[0], x[1][0], (current_time - x[1][1])) for x in file_stat] + # Sort by the most recently accessed files (most sensible to keep) first + file_stat.sort(key=lambda x: x[2]) + # Search for the first entry where the storage limit is + # reached + sum, mark = 0, None + for i, x in enumerate(file_stat): + sum += x[1] + if sum > self.limit: + mark = i + break + if mark is None: + return [] + else: + return [x[0] for x in file_stat[mark:]] + + def convert_size(self, size_bytes): + if size_bytes == 0: + return "0 bytes" + size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") + i = int(math.floor(math.log(size_bytes, 1024))) + p = math.pow(1024, i) + s = round(size_bytes / p, 2) + return "%s %s" % (int(s) if i == 0 else s, size_name[i]) + + def get_size(self, start_path="."): + total_size = 0 + for dirpath, dirnames, filenames in os.walk(start_path): + for f in filenames: + fp = os.path.join(dirpath, f) + total_size += os.path.getsize(fp) + return total_size + + def progress_finish(target, source, env): + nonlocal node_count, progressor + with open(node_count_fname, "w") as f: + f.write("%d\n" % node_count) + progressor.delete(progressor.file_list()) + + try: + with open(node_count_fname) as f: + node_count_max = int(f.readline()) + except: + pass + + cache_directory = os.environ.get("SCONS_CACHE") + # Simple cache pruning, attached to SCons' progress callback. Trim the + # cache directory to a size not larger than cache_limit. + cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024 + progressor = cache_progress(cache_directory, cache_limit) + Progress(progressor, interval=node_count_interval) + + progress_finish_command = Command("progress_finish", [], progress_finish) + AlwaysBuild(progress_finish_command) diff --git a/modules/SCsub b/modules/SCsub index fb46c5f877..9155a53eaf 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -3,6 +3,7 @@ Import("env") import modules_builders +import os env_modules = env.Clone() @@ -13,16 +14,20 @@ env.CommandNoCache("modules_enabled.gen.h", Value(env.module_list), modules_buil vs_sources = [] # libmodule_<name>.a for each active module. -for module in env.module_list: +for name, path in env.module_list.items(): env.modules_sources = [] - SConscript(module + "/SCsub") + + if not os.path.isabs(path): + SConscript(name + "/SCsub") # Built-in. + else: + SConscript(path + "/SCsub") # Custom. # Some modules are not linked automatically but can be enabled optionally # on iOS, so we handle those specially. - if env["platform"] == "iphone" and module in ["arkit", "camera"]: + if env["platform"] == "iphone" and name in ["arkit", "camera"]: continue - lib = env_modules.add_library("module_%s" % module, env.modules_sources) + lib = env_modules.add_library("module_%s" % name, env.modules_sources) env.Prepend(LIBS=[lib]) if env["vsproj"]: vs_sources += env.modules_sources diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp index 9cd2870852..abbd08ae4e 100644 --- a/modules/assimp/editor_scene_importer_assimp.cpp +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -145,7 +145,8 @@ Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_f // aiProcess_EmbedTextures | //aiProcess_SplitByBoneCount | 0; - aiScene *scene = (aiScene *)importer.ReadFile(s_path.c_str(), post_process_Steps); + String g_path = ProjectSettings::get_singleton()->globalize_path(p_path); + aiScene *scene = (aiScene *)importer.ReadFile(g_path.utf8().ptr(), post_process_Steps); ERR_FAIL_COND_V_MSG(scene == nullptr, nullptr, String("Open Asset Import failed to open: ") + String(importer.GetErrorString())); diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h index cde889c1ba..c0bcc858fe 100644 --- a/modules/bullet/area_bullet.h +++ b/modules/bullet/area_bullet.h @@ -93,7 +93,7 @@ private: Vector3 spOv_gravityVec = Vector3(0, -1, 0); real_t spOv_gravityMag = 10; real_t spOv_linearDump = 0.1; - real_t spOv_angularDump = 1; + real_t spOv_angularDump = 0.1; int spOv_priority = 0; bool isScratched = false; diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index b24ded574f..a35a1d8a18 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -31,7 +31,7 @@ #ifndef SHAPE_BULLET_H #define SHAPE_BULLET_H -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #include "core/variant.h" #include "rid_bullet.h" #include "servers/physics_server_3d.h" diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index df798623f9..ded0b970dc 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -30,7 +30,7 @@ #include "csg.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" #include "core/math/math_funcs.h" #include "core/sort_array.h" @@ -964,7 +964,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_ face_points[face_edge_idx], face_points[(face_edge_idx + 1) % 3] }; - Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(point_2D, edge_points); + Vector2 closest_point = Geometry2D::get_closest_point_to_segment(point_2D, edge_points); if ((closest_point - point_2D).length_squared() < vertex_snap2) { int opposite_vertex_idx = face.vertex_idx[(face_edge_idx + 2) % 3]; @@ -1028,7 +1028,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s // First check if the ends of the segment are on the edge. bool on_edge = false; for (int edge_point_idx = 0; edge_point_idx < 2; ++edge_point_idx) { - intersection_point = Geometry::get_closest_point_to_segment_2d(p_segment_points[edge_point_idx], edge_points); + intersection_point = Geometry2D::get_closest_point_to_segment(p_segment_points[edge_point_idx], edge_points); if ((intersection_point - p_segment_points[edge_point_idx]).length_squared() < vertex_snap2) { on_edge = true; break; @@ -1036,7 +1036,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s } // Else check if the segment intersects the edge. - if (on_edge || Geometry::segment_intersects_segment_2d(p_segment_points[0], p_segment_points[1], edge_points[0], edge_points[1], &intersection_point)) { + if (on_edge || Geometry2D::segment_intersects_segment(p_segment_points[0], p_segment_points[1], edge_points[0], edge_points[1], &intersection_point)) { // Check if intersection point is an edge point. if ((intersection_point - edge_points[0]).length_squared() < vertex_snap2 || (intersection_point - edge_points[1]).length_squared() < vertex_snap2) { @@ -1074,7 +1074,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s } // If opposite point is on the segemnt, add its index to segment indices too. - Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(vertices[opposite_vertex_idx].point, p_segment_points); + Vector2 closest_point = Geometry2D::get_closest_point_to_segment(vertices[opposite_vertex_idx].point, p_segment_points); if ((closest_point - vertices[opposite_vertex_idx].point).length_squared() < vertex_snap2) { _add_vertex_idx_sorted(r_segment_indices, opposite_vertex_idx); } @@ -1141,7 +1141,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { uvs[(face_edge_idx + 1) % 3] }; - Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(p_point, edge_points); + Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, edge_points); if ((closest_point - p_point).length_squared() < vertex_snap2) { on_edge = true; @@ -1192,7 +1192,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { } // If not on an edge, check if the point is inside the face. - if (!on_edge && Geometry::is_point_in_triangle(p_point, face_vertices[0].point, face_vertices[1].point, face_vertices[2].point)) { + if (!on_edge && Geometry2D::is_point_in_triangle(p_point, face_vertices[0].point, face_vertices[1].point, face_vertices[2].point)) { // Add the point as a new vertex. Vertex2D new_vertex; new_vertex.point = p_point; diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index 9fa7dc1340..cce72770f5 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -120,7 +120,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca CSGSphere3D *s = Object::cast_to<CSGSphere3D>(cs); Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); float d = ra.x; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -139,7 +139,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca Vector3 axis; axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -168,7 +168,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca Vector3 axis; axis[p_idx == 0 ? 0 : 1] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -191,7 +191,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca Vector3 axis; axis[0] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 7df65b04c4..cea006364f 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "csg_shape.h" +#include "core/math/geometry_2d.h" #include "scene/3d/path_3d.h" void CSGShape3D::set_use_collision(bool p_enable) { @@ -1728,7 +1729,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { final_polygon.invert(); } - Vector<int> triangles = Geometry::triangulate_polygon(final_polygon); + Vector<int> triangles = Geometry2D::triangulate_polygon(final_polygon); if (triangles.size() < 3) { return nullptr; diff --git a/modules/denoise/config.py b/modules/denoise/config.py index 53b8f2f2e3..091d7643c0 100644 --- a/modules/denoise/config.py +++ b/modules/denoise/config.py @@ -1,5 +1,11 @@ def can_build(env, platform): - return env["tools"] + # Thirdparty dependency OpenImage Denoise includes oneDNN library + # which only supports 64-bit architectures. + # It's also only relevant for tools build and desktop platforms, + # as doing lightmap generation and denoising on Android or HTML5 + # would be a bit far-fetched. + desktop_platforms = ["linuxbsd", "osx", "windows"] + return env["tools"] and platform in desktop_platforms and env["bits"] == "64" def configure(env): diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp index 675d66056a..fc71d50289 100644 --- a/modules/gdnative/gdnative/packed_arrays.cpp +++ b/modules/gdnative/gdnative/packed_arrays.cpp @@ -78,6 +78,16 @@ void GDAPI godot_packed_byte_array_new_with_array(godot_packed_byte_array *r_des } } +const uint8_t GDAPI *godot_packed_byte_array_ptr(const godot_packed_byte_array *p_self) { + const Vector<uint8_t> *self = (const Vector<uint8_t> *)p_self; + return self->ptr(); +} + +uint8_t GDAPI *godot_packed_byte_array_ptrw(godot_packed_byte_array *p_self) { + Vector<uint8_t> *self = (Vector<uint8_t> *)p_self; + return self->ptrw(); +} + void GDAPI godot_packed_byte_array_append(godot_packed_byte_array *p_self, const uint8_t p_data) { Vector<uint8_t> *self = (Vector<uint8_t> *)p_self; self->push_back(p_data); @@ -162,6 +172,16 @@ void GDAPI godot_packed_int32_array_new_with_array(godot_packed_int32_array *r_d } } +const int32_t GDAPI *godot_packed_int32_array_ptr(const godot_packed_int32_array *p_self) { + const Vector<int32_t> *self = (const Vector<int32_t> *)p_self; + return self->ptr(); +} + +int32_t GDAPI *godot_packed_int32_array_ptrw(godot_packed_int32_array *p_self) { + Vector<int32_t> *self = (Vector<int32_t> *)p_self; + return self->ptrw(); +} + void GDAPI godot_packed_int32_array_append(godot_packed_int32_array *p_self, const int32_t p_data) { Vector<int32_t> *self = (Vector<int32_t> *)p_self; self->push_back(p_data); @@ -246,6 +266,16 @@ void GDAPI godot_packed_int64_array_new_with_array(godot_packed_int64_array *r_d } } +const int64_t GDAPI *godot_packed_int64_array_ptr(const godot_packed_int64_array *p_self) { + const Vector<int64_t> *self = (const Vector<int64_t> *)p_self; + return self->ptr(); +} + +int64_t GDAPI *godot_packed_int64_array_ptrw(godot_packed_int64_array *p_self) { + Vector<int64_t> *self = (Vector<int64_t> *)p_self; + return self->ptrw(); +} + void GDAPI godot_packed_int64_array_append(godot_packed_int64_array *p_self, const int64_t p_data) { Vector<int64_t> *self = (Vector<int64_t> *)p_self; self->push_back(p_data); @@ -330,6 +360,16 @@ void GDAPI godot_packed_float32_array_new_with_array(godot_packed_float32_array } } +const float GDAPI *godot_packed_float32_array_ptr(const godot_packed_float32_array *p_self) { + const Vector<float> *self = (const Vector<float> *)p_self; + return self->ptr(); +} + +float GDAPI *godot_packed_float32_array_ptrw(godot_packed_float32_array *p_self) { + Vector<float> *self = (Vector<float> *)p_self; + return self->ptrw(); +} + void GDAPI godot_packed_float32_array_append(godot_packed_float32_array *p_self, const float p_data) { Vector<float> *self = (Vector<float> *)p_self; self->push_back(p_data); @@ -414,6 +454,16 @@ void GDAPI godot_packed_float64_array_new_with_array(godot_packed_float64_array } } +const double GDAPI *godot_packed_float64_array_ptr(const godot_packed_float64_array *p_self) { + const Vector<double> *self = (const Vector<double> *)p_self; + return self->ptr(); +} + +double GDAPI *godot_packed_float64_array_ptrw(godot_packed_float64_array *p_self) { + Vector<double> *self = (Vector<double> *)p_self; + return self->ptrw(); +} + void GDAPI godot_packed_float64_array_append(godot_packed_float64_array *p_self, const double p_data) { Vector<double> *self = (Vector<double> *)p_self; self->push_back(p_data); @@ -498,6 +548,16 @@ void GDAPI godot_packed_string_array_new_with_array(godot_packed_string_array *r } } +const godot_string GDAPI *godot_packed_string_array_ptr(const godot_packed_string_array *p_self) { + const Vector<String> *self = (const Vector<String> *)p_self; + return (const godot_string *)self->ptr(); +} + +godot_string GDAPI *godot_packed_string_array_ptrw(godot_packed_string_array *p_self) { + Vector<String> *self = (Vector<String> *)p_self; + return (godot_string *)self->ptrw(); +} + void GDAPI godot_packed_string_array_append(godot_packed_string_array *p_self, const godot_string *p_data) { Vector<String> *self = (Vector<String> *)p_self; String &s = *(String *)p_data; @@ -590,6 +650,16 @@ void GDAPI godot_packed_vector2_array_new_with_array(godot_packed_vector2_array } } +const godot_vector2 GDAPI *godot_packed_vector2_array_ptr(const godot_packed_vector2_array *p_self) { + const Vector<Vector2> *self = (const Vector<Vector2> *)p_self; + return (const godot_vector2 *)self->ptr(); +} + +godot_vector2 GDAPI *godot_packed_vector2_array_ptrw(godot_packed_vector2_array *p_self) { + Vector<Vector2> *self = (Vector<Vector2> *)p_self; + return (godot_vector2 *)self->ptrw(); +} + void GDAPI godot_packed_vector2_array_append(godot_packed_vector2_array *p_self, const godot_vector2 *p_data) { Vector<Vector2> *self = (Vector<Vector2> *)p_self; Vector2 &s = *(Vector2 *)p_data; @@ -681,6 +751,16 @@ void GDAPI godot_packed_vector3_array_new_with_array(godot_packed_vector3_array } } +const godot_vector3 GDAPI *godot_packed_vector3_array_ptr(const godot_packed_vector3_array *p_self) { + const Vector<Vector3> *self = (const Vector<Vector3> *)p_self; + return (const godot_vector3 *)self->ptr(); +} + +godot_vector3 GDAPI *godot_packed_vector3_array_ptrw(godot_packed_vector3_array *p_self) { + Vector<Vector3> *self = (Vector<Vector3> *)p_self; + return (godot_vector3 *)self->ptrw(); +} + void GDAPI godot_packed_vector3_array_append(godot_packed_vector3_array *p_self, const godot_vector3 *p_data) { Vector<Vector3> *self = (Vector<Vector3> *)p_self; Vector3 &s = *(Vector3 *)p_data; @@ -772,6 +852,16 @@ void GDAPI godot_packed_color_array_new_with_array(godot_packed_color_array *r_d } } +const godot_color GDAPI *godot_packed_color_array_ptr(const godot_packed_color_array *p_self) { + const Vector<Color> *self = (const Vector<Color> *)p_self; + return (const godot_color *)self->ptr(); +} + +godot_color GDAPI *godot_packed_color_array_ptrw(godot_packed_color_array *p_self) { + Vector<Color> *self = (Vector<Color> *)p_self; + return (godot_color *)self->ptrw(); +} + void GDAPI godot_packed_color_array_append(godot_packed_color_array *p_self, const godot_color *p_data) { Vector<Color> *self = (Vector<Color> *)p_self; Color &s = *(Color *)p_data; diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 9c38c8b58d..d174b936a8 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -519,6 +519,34 @@ ] }, { + "name": "godot_packed_int64_array_ptr", + "return_type": "const int64_t *", + "arguments": [ + ["const godot_packed_int64_array *", "p_self"] + ] + }, + { + "name": "godot_packed_int64_array_ptrw", + "return_type": "int64_t *", + "arguments": [ + ["godot_packed_int64_array *", "p_self"] + ] + }, + { + "name": "godot_packed_float64_array_ptr", + "return_type": "const double *", + "arguments": [ + ["const godot_packed_float64_array *", "p_self"] + ] + }, + { + "name": "godot_packed_float64_array_ptrw", + "return_type": "double *", + "arguments": [ + ["godot_packed_float64_array *", "p_self"] + ] + }, + { "name": "godot_rect2_as_rect2i", "return_type": "godot_rect2i", "arguments": [ @@ -2854,6 +2882,20 @@ ] }, { + "name": "godot_packed_byte_array_ptr", + "return_type": "const uint8_t *", + "arguments": [ + ["const godot_packed_byte_array *", "p_self"] + ] + }, + { + "name": "godot_packed_byte_array_ptrw", + "return_type": "uint8_t *", + "arguments": [ + ["godot_packed_byte_array *", "p_self"] + ] + }, + { "name": "godot_packed_byte_array_set", "return_type": "void", "arguments": [ @@ -2964,6 +3006,20 @@ ] }, { + "name": "godot_packed_int32_array_ptr", + "return_type": "const int32_t *", + "arguments": [ + ["const godot_packed_int32_array *", "p_self"] + ] + }, + { + "name": "godot_packed_int32_array_ptrw", + "return_type": "int32_t *", + "arguments": [ + ["godot_packed_int32_array *", "p_self"] + ] + }, + { "name": "godot_packed_int32_array_set", "return_type": "void", "arguments": [ @@ -3074,6 +3130,20 @@ ] }, { + "name": "godot_packed_float32_array_ptr", + "return_type": "const float *", + "arguments": [ + ["const godot_packed_float32_array *", "p_self"] + ] + }, + { + "name": "godot_packed_float32_array_ptrw", + "return_type": "float *", + "arguments": [ + ["godot_packed_float32_array *", "p_self"] + ] + }, + { "name": "godot_packed_float32_array_set", "return_type": "void", "arguments": [ @@ -3184,6 +3254,20 @@ ] }, { + "name": "godot_packed_string_array_ptr", + "return_type": "const godot_string *", + "arguments": [ + ["const godot_packed_string_array *", "p_self"] + ] + }, + { + "name": "godot_packed_string_array_ptrw", + "return_type": "godot_string *", + "arguments": [ + ["godot_packed_string_array *", "p_self"] + ] + }, + { "name": "godot_packed_string_array_set", "return_type": "void", "arguments": [ @@ -3294,6 +3378,20 @@ ] }, { + "name": "godot_packed_vector2_array_ptr", + "return_type": "const godot_vector2 *", + "arguments": [ + ["const godot_packed_vector2_array *", "p_self"] + ] + }, + { + "name": "godot_packed_vector2_array_ptrw", + "return_type": "godot_vector2 *", + "arguments": [ + ["godot_packed_vector2_array *", "p_self"] + ] + }, + { "name": "godot_packed_vector2_array_set", "return_type": "void", "arguments": [ @@ -3404,6 +3502,20 @@ ] }, { + "name": "godot_packed_vector3_array_ptr", + "return_type": "const godot_vector3 *", + "arguments": [ + ["const godot_packed_vector3_array *", "p_self"] + ] + }, + { + "name": "godot_packed_vector3_array_ptrw", + "return_type": "godot_vector3 *", + "arguments": [ + ["godot_packed_vector3_array *", "p_self"] + ] + }, + { "name": "godot_packed_vector3_array_set", "return_type": "void", "arguments": [ @@ -3512,6 +3624,20 @@ ["godot_packed_color_array *", "p_self"], ["const godot_int", "p_size"] ] + }, + { + "name": "godot_packed_color_array_ptr", + "return_type": "const godot_color *", + "arguments": [ + ["const godot_packed_color_array *", "p_self"] + ] + }, + { + "name": "godot_packed_color_array_ptrw", + "return_type": "godot_color *", + "arguments": [ + ["godot_packed_color_array *", "p_self"] + ] }, { "name": "godot_packed_color_array_set", diff --git a/modules/gdnative/include/gdnative/packed_arrays.h b/modules/gdnative/include/gdnative/packed_arrays.h index d5bad70bdc..8cff6d49a5 100644 --- a/modules/gdnative/include/gdnative/packed_arrays.h +++ b/modules/gdnative/include/gdnative/packed_arrays.h @@ -158,6 +158,9 @@ void GDAPI godot_packed_byte_array_new(godot_packed_byte_array *r_dest); void GDAPI godot_packed_byte_array_new_copy(godot_packed_byte_array *r_dest, const godot_packed_byte_array *p_src); void GDAPI godot_packed_byte_array_new_with_array(godot_packed_byte_array *r_dest, const godot_array *p_a); +const uint8_t GDAPI *godot_packed_byte_array_ptr(const godot_packed_byte_array *p_self); +uint8_t GDAPI *godot_packed_byte_array_ptrw(godot_packed_byte_array *p_self); + void GDAPI godot_packed_byte_array_append(godot_packed_byte_array *p_self, const uint8_t p_data); void GDAPI godot_packed_byte_array_append_array(godot_packed_byte_array *p_self, const godot_packed_byte_array *p_array); @@ -187,6 +190,9 @@ void GDAPI godot_packed_int32_array_new(godot_packed_int32_array *r_dest); void GDAPI godot_packed_int32_array_new_copy(godot_packed_int32_array *r_dest, const godot_packed_int32_array *p_src); void GDAPI godot_packed_int32_array_new_with_array(godot_packed_int32_array *r_dest, const godot_array *p_a); +const int32_t GDAPI *godot_packed_int32_array_ptr(const godot_packed_int32_array *p_self); +int32_t GDAPI *godot_packed_int32_array_ptrw(godot_packed_int32_array *p_self); + void GDAPI godot_packed_int32_array_append(godot_packed_int32_array *p_self, const int32_t p_data); void GDAPI godot_packed_int32_array_append_array(godot_packed_int32_array *p_self, const godot_packed_int32_array *p_array); @@ -216,6 +222,9 @@ void GDAPI godot_packed_int64_array_new(godot_packed_int64_array *r_dest); void GDAPI godot_packed_int64_array_new_copy(godot_packed_int64_array *r_dest, const godot_packed_int64_array *p_src); void GDAPI godot_packed_int64_array_new_with_array(godot_packed_int64_array *r_dest, const godot_array *p_a); +const int64_t GDAPI *godot_packed_int64_array_ptr(const godot_packed_int64_array *p_self); +int64_t GDAPI *godot_packed_int64_array_ptrw(godot_packed_int64_array *p_self); + void GDAPI godot_packed_int64_array_append(godot_packed_int64_array *p_self, const int64_t p_data); void GDAPI godot_packed_int64_array_append_array(godot_packed_int64_array *p_self, const godot_packed_int64_array *p_array); @@ -245,6 +254,9 @@ void GDAPI godot_packed_float32_array_new(godot_packed_float32_array *r_dest); void GDAPI godot_packed_float32_array_new_copy(godot_packed_float32_array *r_dest, const godot_packed_float32_array *p_src); void GDAPI godot_packed_float32_array_new_with_array(godot_packed_float32_array *r_dest, const godot_array *p_a); +const float GDAPI *godot_packed_float32_array_ptr(const godot_packed_float32_array *p_self); +float GDAPI *godot_packed_float32_array_ptrw(godot_packed_float32_array *p_self); + void GDAPI godot_packed_float32_array_append(godot_packed_float32_array *p_self, const float p_data); void GDAPI godot_packed_float32_array_append_array(godot_packed_float32_array *p_self, const godot_packed_float32_array *p_array); @@ -274,6 +286,9 @@ void GDAPI godot_packed_float64_array_new(godot_packed_float64_array *r_dest); void GDAPI godot_packed_float64_array_new_copy(godot_packed_float64_array *r_dest, const godot_packed_float64_array *p_src); void GDAPI godot_packed_float64_array_new_with_array(godot_packed_float64_array *r_dest, const godot_array *p_a); +const double GDAPI *godot_packed_float64_array_ptr(const godot_packed_float64_array *p_self); +double GDAPI *godot_packed_float64_array_ptrw(godot_packed_float64_array *p_self); + void GDAPI godot_packed_float64_array_append(godot_packed_float64_array *p_self, const double p_data); void GDAPI godot_packed_float64_array_append_array(godot_packed_float64_array *p_self, const godot_packed_float64_array *p_array); @@ -303,6 +318,9 @@ void GDAPI godot_packed_string_array_new(godot_packed_string_array *r_dest); void GDAPI godot_packed_string_array_new_copy(godot_packed_string_array *r_dest, const godot_packed_string_array *p_src); void GDAPI godot_packed_string_array_new_with_array(godot_packed_string_array *r_dest, const godot_array *p_a); +const godot_string GDAPI *godot_packed_string_array_ptr(const godot_packed_string_array *p_self); +godot_string GDAPI *godot_packed_string_array_ptrw(godot_packed_string_array *p_self); + void GDAPI godot_packed_string_array_append(godot_packed_string_array *p_self, const godot_string *p_data); void GDAPI godot_packed_string_array_append_array(godot_packed_string_array *p_self, const godot_packed_string_array *p_array); @@ -332,6 +350,9 @@ void GDAPI godot_packed_vector2_array_new(godot_packed_vector2_array *r_dest); void GDAPI godot_packed_vector2_array_new_copy(godot_packed_vector2_array *r_dest, const godot_packed_vector2_array *p_src); void GDAPI godot_packed_vector2_array_new_with_array(godot_packed_vector2_array *r_dest, const godot_array *p_a); +const godot_vector2 GDAPI *godot_packed_vector2_array_ptr(const godot_packed_vector2_array *p_self); +godot_vector2 GDAPI *godot_packed_vector2_array_ptrw(godot_packed_vector2_array *p_self); + void GDAPI godot_packed_vector2_array_append(godot_packed_vector2_array *p_self, const godot_vector2 *p_data); void GDAPI godot_packed_vector2_array_append_array(godot_packed_vector2_array *p_self, const godot_packed_vector2_array *p_array); @@ -361,6 +382,9 @@ void GDAPI godot_packed_vector3_array_new(godot_packed_vector3_array *r_dest); void GDAPI godot_packed_vector3_array_new_copy(godot_packed_vector3_array *r_dest, const godot_packed_vector3_array *p_src); void GDAPI godot_packed_vector3_array_new_with_array(godot_packed_vector3_array *r_dest, const godot_array *p_a); +const godot_vector3 GDAPI *godot_packed_vector3_array_ptr(const godot_packed_vector3_array *p_self); +godot_vector3 GDAPI *godot_packed_vector3_array_ptrw(godot_packed_vector3_array *p_self); + void GDAPI godot_packed_vector3_array_append(godot_packed_vector3_array *p_self, const godot_vector3 *p_data); void GDAPI godot_packed_vector3_array_append_array(godot_packed_vector3_array *p_self, const godot_packed_vector3_array *p_array); @@ -390,6 +414,9 @@ void GDAPI godot_packed_color_array_new(godot_packed_color_array *r_dest); void GDAPI godot_packed_color_array_new_copy(godot_packed_color_array *r_dest, const godot_packed_color_array *p_src); void GDAPI godot_packed_color_array_new_with_array(godot_packed_color_array *r_dest, const godot_array *p_a); +const godot_color GDAPI *godot_packed_color_array_ptr(const godot_packed_color_array *p_self); +godot_color GDAPI *godot_packed_color_array_ptrw(godot_packed_color_array *p_self); + void GDAPI godot_packed_color_array_append(godot_packed_color_array *p_self, const godot_color *p_data); void GDAPI godot_packed_color_array_append_array(godot_packed_color_array *p_self, const godot_packed_color_array *p_array); diff --git a/modules/gdnavigation/nav_map.cpp b/modules/gdnavigation/nav_map.cpp index 4bea007104..7919e6a01f 100644 --- a/modules/gdnavigation/nav_map.cpp +++ b/modules/gdnavigation/nav_map.cpp @@ -155,7 +155,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p least_cost_poly->poly->points[(i + 1) % least_cost_poly->poly->points.size()].pos }; - const Vector3 new_entry = Geometry::get_closest_point_to_segment(least_cost_poly->entry, edge_line); + const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, edge_line); const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance; #else const float new_distance = least_cost_poly->poly->center.distance_to(edge.other_polygon->center) + least_cost_poly->traveled_distance; @@ -413,7 +413,7 @@ Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector for (size_t point_id = 0; point_id < p.points.size(); point_id += 1) { Vector3 a, b; - Geometry::get_closest_points_between_segments( + Geometry3D::get_closest_points_between_segments( p_from, p_to, p.points[point_id].pos, diff --git a/modules/gdnavigation/navigation_mesh_generator.cpp b/modules/gdnavigation/navigation_mesh_generator.cpp index 7dc08fbf29..5329600e39 100644 --- a/modules/gdnavigation/navigation_mesh_generator.cpp +++ b/modules/gdnavigation/navigation_mesh_generator.cpp @@ -218,7 +218,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s); if (convex_polygon) { Vector<Vector3> varr = Variant(convex_polygon->get_points()); - Geometry::MeshData md; + Geometry3D::MeshData md; Error err = QuickHull::build(varr, md); @@ -226,7 +226,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, PackedVector3Array faces; for (int j = 0; j < md.faces.size(); ++j) { - Geometry::MeshData::Face face = md.faces[j]; + Geometry3D::MeshData::Face face = md.faces[j]; for (int k = 2; k < face.indices.size(); ++k) { faces.push_back(md.vertices[face.indices[0]]); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 7433c4a5bc..50d8289fd1 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2082,7 +2082,11 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (!p_only_functions) { List<PropertyInfo> members; - tmp.get_property_list(&members); + if (p_base.value.get_type() != Variant::NIL) { + p_base.value.get_property_list(&members); + } else { + tmp.get_property_list(&members); + } for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) { if (String(E->get().name).find("/") == -1) { diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index fc0c4b3138..1aab71d161 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -363,6 +363,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } } + static_ref = script; + String err_text; #ifdef DEBUG_ENABLED @@ -1430,11 +1432,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED GET_VARIANT_PTR(test, 1); - GET_VARIANT_PTR(message, 2); bool result = test->booleanize(); if (!result) { - const String &message_str = *message; + String message_str; + if (_code_ptr[ip + 2] != 0) { + GET_VARIANT_PTR(message, 2); + message_str = *message; + } if (message_str.empty()) { err_text = "Assertion failed."; } else { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 0e498f6895..a06ecb1ea8 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3179,6 +3179,15 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { IdentifierNode *id = alloc_node<IdentifierNode>(); id->name = tokenizer->get_token_identifier(); + BlockNode *check_block = p_block; + while (check_block) { + if (check_block->variables.has(id->name)) { + _set_error("Variable \"" + String(id->name) + "\" already defined in the scope (at line " + itos(check_block->variables[id->name]->line) + ")."); + return; + } + check_block = check_block->parent_block; + } + tokenizer->advance(); if (tokenizer->get_token() != GDScriptTokenizer::TK_OP_IN) { @@ -7336,8 +7345,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat } else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) { // Supertypes are acceptable for dynamic compliance if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) { - _set_error("At \"" + callee_name + "()\" call, argument " + itos(i - arg_diff + 1) + ". Assigned type (" + - par_type.to_string() + ") doesn't match the function argument's type (" + + _set_error("At \"" + callee_name + "()\" call, argument " + itos(i - arg_diff + 1) + ". The passed argument's type (" + + par_type.to_string() + ") doesn't match the function's expected argument type (" + arg_types[i - arg_diff].to_string() + ").", p_call->line); return DataType(); diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 2bae43510a..3b0e78546d 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -35,7 +35,6 @@ #include "editor/plugins/node_3d_editor_plugin.h" #include "scene/3d/camera_3d.h" -#include "core/math/geometry.h" #include "core/os/keyboard.h" #include "scene/main/window.h" diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index b55c73e9bc..4de523baa0 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "lightmapper_rd.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" #include "core/project_settings.h" #include "lm_blendseams.glsl.gen.h" #include "lm_compute.glsl.gen.h" @@ -137,7 +137,7 @@ void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Ve { Vector3 qsize = aabb.size * 0.5; //quarter size, for fast aabb test - if (!Geometry::triangle_box_overlap(aabb.position + qsize, qsize, p_points)) { + if (!Geometry3D::triangle_box_overlap(aabb.position + qsize, qsize, p_points)) { //does not fit in child, go on continue; } @@ -198,7 +198,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ int slices = 0; while (source_sizes.size() > 0) { - Vector<Vector3i> offsets = Geometry::partial_pack_rects(source_sizes, atlas_size); + Vector<Vector3i> offsets = Geometry2D::partial_pack_rects(source_sizes, atlas_size); Vector<int> new_indices; Vector<Vector2i> new_sources; for (int i = 0; i < offsets.size(); i++) { @@ -488,9 +488,9 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i } //generate SDF for raytracing - Vector<uint32_t> euclidean_pos = Geometry::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), false); - Vector<uint32_t> euclidean_neg = Geometry::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), true); - Vector<int8_t> sdf8 = Geometry::generate_sdf8(euclidean_pos, euclidean_neg); + Vector<uint32_t> euclidean_pos = Geometry3D::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), false); + Vector<uint32_t> euclidean_neg = Geometry3D::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), true); + Vector<int8_t> sdf8 = Geometry3D::generate_sdf8(euclidean_pos, euclidean_neg); /*****************************/ /*** CREATE GPU STRUCTURES ***/ diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 958d72adb1..ae25bd3544 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2688,7 +2688,9 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect return true; } +#ifdef TOOLS_ENABLED MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); +#endif PropertyHint hint = PROPERTY_HINT_NONE; String hint_string; diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index eb7696685f..c874025be0 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -421,7 +421,7 @@ namespace GodotTools aboutLabel.Text = "C# support in Godot Engine is in late alpha stage and, while already usable, " + "it is not meant for use in production.\n\n" + - "Projects can be exported to Linux, macOS, Windows and Android, but not yet to iOS, HTML5 or UWP. " + + "Projects can be exported to Linux, macOS, Windows, Android, iOS and HTML5, but not yet to UWP. " + "Bugs and usability issues will be addressed gradually over future releases, " + "potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" + "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" + diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 5f518d0613..39c3bd8934 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -422,10 +422,10 @@ void GDMono::initialize_load_assemblies() { #if defined(TOOLS_ENABLED) bool tool_assemblies_loaded = _load_tools_assemblies(); CRASH_COND_MSG(!tool_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies."); -#endif if (Main::is_project_manager()) return; +#endif // Load the project's main assembly. This doesn't necessarily need to succeed. // The game may not be using .NET at all, or if the project does use .NET and diff --git a/modules/opus/config.py b/modules/opus/config.py index d22f9454ed..9ff7b2dece 100644 --- a/modules/opus/config.py +++ b/modules/opus/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return True + return env.module_check_dependencies("opus", ["ogg"]) def configure(env): diff --git a/modules/theora/config.py b/modules/theora/config.py index 413acce2df..b063ed51f9 100644 --- a/modules/theora/config.py +++ b/modules/theora/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return True + return env.module_check_dependencies("theora", ["ogg", "vorbis"]) def configure(env): diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index fea7d151df..b7ca3c882b 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -3216,6 +3216,7 @@ void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from, { List<VisualScript::DataConnection> data_connections; script->get_data_connection_list(p_func_from, &data_connections); + int func_from_node_id = script->get_function_node_id(p_func_from); HashMap<int, Map<int, Pair<int, int>>> connections; @@ -3225,6 +3226,11 @@ void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from, int out_p = E->get().from_port; int in_p = E->get().to_port; + // skip if the from_node is a function node + if (from == func_from_node_id) { + continue; + } + if (!connections.has(to)) { connections.set(to, Map<int, Pair<int, int>>()); } diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index f14c9ce49d..3c44faab90 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -447,7 +447,7 @@ void VisualScriptPropertySelector::_item_selected() { if (E) { for (int i = 0; i < E->get().properties.size(); i++) { if (E->get().properties[i].name == name) { - text = E->get().properties[i].description; + text = DTR(E->get().properties[i].description); } } } @@ -461,7 +461,7 @@ void VisualScriptPropertySelector::_item_selected() { if (C) { for (int i = 0; i < C->get().methods.size(); i++) { if (C->get().methods[i].name == name) { - text = C->get().methods[i].description; + text = DTR(C->get().methods[i].description); } } } @@ -473,7 +473,7 @@ void VisualScriptPropertySelector::_item_selected() { for (int i = 0; i < T->get().methods.size(); i++) { Vector<String> functions = name.rsplit("/", false, 1); if (T->get().methods[i].name == functions[functions.size() - 1]) { - text = T->get().methods[i].description; + text = DTR(T->get().methods[i].description); } } } @@ -492,7 +492,7 @@ void VisualScriptPropertySelector::_item_selected() { if (typecast_node.is_valid()) { Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(typecast_node->get_class_name()); if (F) { - text = F->get().description; + text = DTR(F->get().description); } } @@ -502,7 +502,7 @@ void VisualScriptPropertySelector::_item_selected() { if (F) { for (int i = 0; i < F->get().constants.size(); i++) { if (F->get().constants[i].value.to_int() == int(builtin_node->get_func())) { - text = F->get().constants[i].description; + text = DTR(F->get().constants[i].description); } } } diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py index d22f9454ed..8a384e3066 100644 --- a/modules/vorbis/config.py +++ b/modules/vorbis/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return True + return env.module_check_dependencies("vorbis", ["ogg"]) def configure(env): diff --git a/modules/webm/config.py b/modules/webm/config.py index 93b49d177a..99f8ace114 100644 --- a/modules/webm/config.py +++ b/modules/webm/config.py @@ -1,5 +1,8 @@ def can_build(env, platform): - return platform not in ["iphone"] + if platform in ["iphone"]: + return False + + return env.module_check_dependencies("webm", ["ogg", "opus", "vorbis"]) def configure(env): diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index 504b4705d8..2054276655 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -120,7 +120,7 @@ </argument> <description> Sets the SDP description of the local peer. This should be called in response to [signal session_description_created]. - If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created]. + After calling this function the peer will start emitting [signal ice_candidate_created] (unless an [enum Error] different from [constant OK] is returned). </description> </method> <method name="set_remote_description"> diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index c5b06827b2..1436d832de 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -367,6 +367,25 @@ void DisplayServerAndroid::register_android_driver() { register_create_function("android", create_func, get_rendering_drivers_func); } +void DisplayServerAndroid::reset_window() { +#if defined(VULKAN_ENABLED) + if (rendering_driver == "vulkan") { + ANativeWindow *native_window = OS_Android::get_singleton()->get_native_window(); + ERR_FAIL_COND(!native_window); + + ERR_FAIL_COND(!context_vulkan); + context_vulkan->window_destroy(MAIN_WINDOW_ID); + + Size2i display_size = OS_Android::get_singleton()->get_display_size(); + if (context_vulkan->window_create(native_window, display_size.width, display_size.height) == -1) { + memdelete(context_vulkan); + context_vulkan = nullptr; + ERR_FAIL_MSG("Failed to reset Vulkan window."); + } + } +#endif +} + DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { rendering_driver = p_rendering_driver; diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 199371d08d..d64542df58 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -166,6 +166,8 @@ public: static Vector<String> get_rendering_drivers_func(); static void register_android_driver(); + void reset_window(); + DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); ~DisplayServerAndroid(); }; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index dfaaf68b69..a663a847c2 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -254,6 +254,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { }; Vector<PluginConfig> plugins; + String last_plugin_names; + uint64_t last_custom_build_time = 0; volatile bool plugins_changed; Mutex plugins_lock; Vector<Device> devices; @@ -770,6 +772,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); int xr_mode_index = p_preset->get("xr_features/xr_mode"); + bool focus_awareness = p_preset->get("xr_features/focus_awareness"); String plugins_names = get_plugins_names(get_enabled_plugins(p_preset)); @@ -926,6 +929,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } } + if (tname == "meta-data" && attrname == "value" && value == "oculus_focus_aware_value") { + // Update the focus awareness meta-data value + string_table.write[attr_value] = xr_mode_index == /* XRMode.OVR */ 1 && focus_awareness ? "true" : "false"; + } + if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.empty()) { // Update the meta-data 'android:value' attribute with the list of enabled plugins. string_table.write[attr_value] = plugins_names; @@ -1447,6 +1455,7 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "xr_features/focus_awareness"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); @@ -1727,23 +1736,32 @@ public: // Look for export templates (first official, and if defined custom templates). if (!bool(p_preset->get("custom_template/use_custom_build"))) { - bool dvalid = exists_export_template("android_debug.apk", &err); - bool rvalid = exists_export_template("android_release.apk", &err); + String template_err; + bool dvalid = false; + bool rvalid = false; if (p_preset->get("custom_template/debug") != "") { dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; + template_err += TTR("Custom debug template not found.") + "\n"; } + } else { + dvalid = exists_export_template("android_debug.apk", &template_err); } + if (p_preset->get("custom_template/release") != "") { rvalid = FileAccess::exists(p_preset->get("custom_template/release")); if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; + template_err += TTR("Custom release template not found.") + "\n"; } + } else { + rvalid = exists_export_template("android_release.apk", &template_err); } valid = dvalid || rvalid; + if (!valid) { + err += template_err; + } } else { valid = exists_export_template("android_source.zip", &err); } @@ -1775,6 +1793,13 @@ public: } } + String rk = p_preset->get("keystore/release"); + + if (!rk.empty() && !FileAccess::exists(rk)) { + valid = false; + err += TTR("Release keystore incorrectly configured in the export preset.") + "\n"; + } + if (bool(p_preset->get("custom_template/use_custom_build"))) { String sdk_path = EditorSettings::get_singleton()->get("export/android/custom_build_sdk_path"); if (sdk_path == "") { @@ -1821,6 +1846,40 @@ public: err += etc_error; } + // Ensure that `Use Custom Build` is enabled if a plugin is selected. + String enabled_plugins_names = get_plugins_names(get_enabled_plugins(p_preset)); + bool custom_build_enabled = p_preset->get("custom_template/use_custom_build"); + if (!enabled_plugins_names.empty() && !custom_build_enabled) { + valid = false; + err += TTR("\"Use Custom Build\" must be enabled to use the plugins."); + err += "\n"; + } + + // Validate the Xr features are properly populated + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + int degrees_of_freedom = p_preset->get("xr_features/degrees_of_freedom"); + int hand_tracking = p_preset->get("xr_features/hand_tracking"); + bool focus_awareness = p_preset->get("xr_features/focus_awareness"); + if (xr_mode_index != /* XRMode.OVR*/ 1) { + if (degrees_of_freedom > 0) { + valid = false; + err += TTR("\"Degrees Of Freedom\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\"."); + err += "\n"; + } + + if (hand_tracking > 0) { + valid = false; + err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\"."); + err += "\n"; + } + + if (focus_awareness) { + valid = false; + err += TTR("\"Focus Awareness\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\"."); + err += "\n"; + } + } + r_error = err; return valid; } @@ -1831,6 +1890,29 @@ public: return list; } + inline bool is_clean_build_required(Vector<PluginConfig> enabled_plugins) { + String plugin_names = get_plugins_names(enabled_plugins); + bool first_build = last_custom_build_time == 0; + bool have_plugins_changed = false; + + if (!first_build) { + have_plugins_changed = plugin_names != last_plugin_names; + if (!have_plugins_changed) { + for (int i = 0; i < enabled_plugins.size(); i++) { + if (enabled_plugins.get(i).last_updated > last_custom_build_time) { + have_plugins_changed = true; + break; + } + } + } + } + + last_custom_build_time = OS::get_singleton()->get_unix_time(); + last_plugin_names = plugin_names; + + return have_plugins_changed || first_build; + } + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); @@ -1877,8 +1959,12 @@ public: String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins); String remote_plugins_binaries = get_plugins_binaries(BINARY_TYPE_REMOTE, enabled_plugins); String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins); + bool clean_build_required = is_clean_build_required(enabled_plugins); List<String> cmdline; + if (clean_build_required) { + cmdline.push_back("clean"); + } cmdline.push_back("build"); cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name. cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies. diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index f5b1d29f22..dbf1dc0f3c 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -45,6 +45,9 @@ android:resizeableActivity="false" tools:ignore="UnusedAttribute" > + <!-- Focus awareness metadata populated at export time if the user enables it in the 'Xr Features' section. --> + <meta-data android:name="com.oculus.vr.focusaware" android:value="oculus_focus_aware_value" /> + <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index ea341b37b1..19202d2310 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -75,6 +75,11 @@ android { } defaultConfig { + // The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects. + aaptOptions { + ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~" + } + // Feel free to modify the application id to your own. applicationId getExportPackageName() minSdkVersion versions.minSdk diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 5251bc3066..acfdef531e 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -72,17 +72,12 @@ ext.getGodotPluginsRemoteBinaries = { -> /** * Parse the project properties for the 'plugins_local_binaries' property and return * their binaries for inclusion in the build dependencies. - * - * Returns the prebuilt plugins if the 'plugins_local_binaries' property is unavailable. */ ext.getGodotPluginsLocalBinaries = { -> - // Set the prebuilt plugins as default. If custom build is enabled, - // the 'plugins_local_binaries' will be defined so we can use it instead. - Set<String> binDeps = ["libs/plugins/GodotPayment.release.aar"] + Set<String> binDeps = [] // Retrieve the list of local plugins binaries. if (project.hasProperty("plugins_local_binaries")) { - binDeps.clear() String pluginsList = project.property("plugins_local_binaries") if (pluginsList != null && !pluginsList.trim().isEmpty()) { for (String plugin : pluginsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) { diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 01a3607b20..80c6be0fae 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -193,4 +193,6 @@ task cleanGodotTemplates(type: Delete) { delete("$binDir/android_source.zip") delete("$binDir/godot-lib.debug.aar") delete("$binDir/godot-lib.release.aar") + + finalizedBy getTasksByName("clean", true) } diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index f27d8620ec..8ba9b0400f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -345,7 +345,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe return deviceInfo.reqGlEsVersion; } - private String[] getCommandLine() { + @CallSuper + protected String[] getCommandLine() { InputStream is; try { is = getAssets().open("_cl_"); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 72198ba123..3693f36557 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -66,11 +66,12 @@ public class GodotLib { /** * Invoked on the GL thread when the underlying Android surface has changed size. - * @param width - * @param height + * @param p_surface + * @param p_width + * @param p_height * @see android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int) */ - public static native void resize(int width, int height); + public static native void resize(Surface p_surface, int p_width, int p_height); /** * Invoked on the render thread when the underlying Android surface is created or recreated. @@ -189,7 +190,7 @@ public class GodotLib { * @param p_method Name of the method to invoke * @param p_params Parameters to use for method invocation */ - public static native void callobject(int p_id, String p_method, Object[] p_params); + public static native void callobject(long p_id, String p_method, Object[] p_params); /** * Invoke method |p_method| on the Godot object specified by |p_id| during idle time. @@ -197,7 +198,7 @@ public class GodotLib { * @param p_method Name of the method to invoke * @param p_params Parameters to use for method invocation */ - public static native void calldeferred(int p_id, String p_method, Object[] p_params); + public static native void calldeferred(long p_id, String p_method, Object[] p_params); /** * Forward the results from a permission request. diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java index 99d3662317..64395f7d1e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java @@ -64,7 +64,7 @@ class GodotRenderer implements GLSurfaceView.Renderer { } public void onSurfaceChanged(GL10 gl, int width, int height) { - GodotLib.resize(width, height); + GodotLib.resize(null, width, height); for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onGLSurfaceChanged(gl, width, height); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt index 608ad48df9..aeb4628d5d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt @@ -59,9 +59,7 @@ internal class VkRenderer { * Called when the surface is created and signals the beginning of rendering. */ fun onVkSurfaceCreated(surface: Surface) { - // TODO: properly implement surface re-creation: - // GodotLib.newcontext should be called here once it's done. - //GodotLib.newcontext(surface, false) + GodotLib.newcontext(surface, false) for (plugin in pluginRegistry.getAllPlugins()) { plugin.onVkSurfaceCreated(surface) @@ -72,12 +70,7 @@ internal class VkRenderer { * Called after the surface is created and whenever its size changes. */ fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) { - GodotLib.resize(width, height) - - // TODO: properly implement surface re-creation: - // Update the native renderer instead of restarting the app. - // GodotLib.newcontext should not be called here once it's done. - GodotLib.newcontext(surface, false) + GodotLib.resize(surface, width, height) for (plugin in pluginRegistry.getAllPlugins()) { plugin.onVkSurfaceChanged(surface, width, height) diff --git a/platform/android/java/plugins/godotpayment/build.gradle b/platform/android/java/plugins/godotpayment/build.gradle index ffab86e26e..fb3aa8bba2 100644 --- a/platform/android/java/plugins/godotpayment/build.gradle +++ b/platform/android/java/plugins/godotpayment/build.gradle @@ -3,7 +3,6 @@ apply plugin: 'com.android.library' android { compileSdkVersion versions.compileSdk buildToolsVersion versions.buildTools - useLibrary 'org.apache.http.legacy' defaultConfig { minSdkVersion versions.minSdk @@ -21,6 +20,7 @@ android { dependencies { implementation libraries.supportCoreUtils implementation libraries.v4Support + implementation 'com.android.billingclient:billing:2.2.1' if (rootProject.findProject(":lib")) { compileOnly project(":lib") diff --git a/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl b/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl deleted file mode 100644 index 0f2bcae338..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.vending.billing; - -import android.os.Bundle; - -/** - * InAppBillingService is the service that provides in-app billing version 3 and beyond. - * This service provides the following features: - * 1. Provides a new API to get details of in-app items published for the app including - * price, type, title and description. - * 2. The purchase flow is synchronous and purchase information is available immediately - * after it completes. - * 3. Purchase information of in-app purchases is maintained within the Google Play system - * till the purchase is consumed. - * 4. An API to consume a purchase of an inapp item. All purchases of one-time - * in-app items are consumable and thereafter can be purchased again. - * 5. An API to get current purchases of the user immediately. This will not contain any - * consumed purchases. - * - * All calls will give a response code with the following possible values - * RESULT_OK = 0 - success - * RESULT_USER_CANCELED = 1 - User pressed back or canceled a dialog - * RESULT_SERVICE_UNAVAILABLE = 2 - The network connection is down - * RESULT_BILLING_UNAVAILABLE = 3 - This billing API version is not supported for the type requested - * RESULT_ITEM_UNAVAILABLE = 4 - Requested SKU is not available for purchase - * RESULT_DEVELOPER_ERROR = 5 - Invalid arguments provided to the API - * RESULT_ERROR = 6 - Fatal error during the API action - * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned - * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned - */ -interface IInAppBillingService { - /** - * Checks support for the requested billing API version, package and in-app type. - * Minimum API version supported by this interface is 3. - * @param apiVersion billing API version that the app is using - * @param packageName the package name of the calling app - * @param type type of the in-app item being purchased ("inapp" for one-time purchases - * and "subs" for subscriptions) - * @return RESULT_OK(0) on success and appropriate response code on failures. - */ - int isBillingSupported(int apiVersion, String packageName, String type); - - /** - * Provides details of a list of SKUs - * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle - * with a list JSON strings containing the productId, price, title and description. - * This API can be called with a maximum of 20 SKUs. - * @param apiVersion billing API version that the app is using - * @param packageName the package name of the calling app - * @param type of the in-app items ("inapp" for one-time purchases - * and "subs" for subscriptions) - * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST" - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes - * on failures. - * "DETAILS_LIST" with a StringArrayList containing purchase information - * in JSON format similar to: - * '{ "productId" : "exampleSku", - * "type" : "inapp", - * "price" : "$5.00", - * "price_currency": "USD", - * "price_amount_micros": 5000000, - * "title : "Example Title", - * "description" : "This is an example description" }' - */ - Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle); - - /** - * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU, - * the type, a unique purchase token and an optional developer payload. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param sku the SKU of the in-app item as published in the developer console - * @param type of the in-app item being purchased ("inapp" for one-time purchases - * and "subs" for subscriptions) - * @param developerPayload optional argument to be sent back with the purchase information - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes - * on failures. - * "BUY_INTENT" - PendingIntent to start the purchase flow - * - * The Pending intent should be launched with startIntentSenderForResult. When purchase flow - * has completed, the onActivityResult() will give a resultCode of OK or CANCELED. - * If the purchase is successful, the result data will contain the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response - * codes on failures. - * "INAPP_PURCHASE_DATA" - String in JSON format similar to - * '{"orderId":"12999763169054705758.1371079406387615", - * "packageName":"com.example.app", - * "productId":"exampleSku", - * "purchaseTime":1345678900000, - * "purchaseToken" : "122333444455555", - * "developerPayload":"example developer payload" }' - * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that - * was signed with the private key of the developer - */ - Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type, - String developerPayload); - - /** - * Returns the current SKUs owned by the user of the type and package name specified along with - * purchase information and a signature of the data to be validated. - * This will return all SKUs that have been purchased in V3 and managed items purchased using - * V1 and V2 that have not been consumed. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param type of the in-app items being requested ("inapp" for one-time purchases - * and "subs" for subscriptions) - * @param continuationToken to be set as null for the first call, if the number of owned - * skus are too many, a continuationToken is returned in the response bundle. - * This method can be called again with the continuation token to get the next set of - * owned skus. - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes - on failures. - * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs - * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information - * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures - * of the purchase information - * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the - * next set of in-app purchases. Only set if the - * user has more owned skus than the current list. - */ - Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken); - - /** - * Consume the last purchase of the given SKU. This will result in this item being removed - * from all subsequent responses to getPurchases() and allow re-purchase of this item. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param purchaseToken token in the purchase information JSON that identifies the purchase - * to be consumed - * @return RESULT_OK(0) if consumption succeeded, appropriate response codes on failures. - */ - int consumePurchase(int apiVersion, String packageName, String purchaseToken); - - /** - * This API is currently under development. - */ - int stub(int apiVersion, String packageName, String type); - - /** - * Returns a pending intent to launch the purchase flow for upgrading or downgrading a - * subscription. The existing owned SKU(s) should be provided along with the new SKU that - * the user is upgrading or downgrading to. - * @param apiVersion billing API version that the app is using, must be 5 or later - * @param packageName package name of the calling app - * @param oldSkus the SKU(s) that the user is upgrading or downgrading from, - * if null or empty this method will behave like {@link #getBuyIntent} - * @param newSku the SKU that the user is upgrading or downgrading to - * @param type of the item being purchased, currently must be "subs" - * @param developerPayload optional argument to be sent back with the purchase information - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes - * on failures. - * "BUY_INTENT" - PendingIntent to start the purchase flow - * - * The Pending intent should be launched with startIntentSenderForResult. When purchase flow - * has completed, the onActivityResult() will give a resultCode of OK or CANCELED. - * If the purchase is successful, the result data will contain the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response - * codes on failures. - * "INAPP_PURCHASE_DATA" - String in JSON format similar to - * '{"orderId":"12999763169054705758.1371079406387615", - * "packageName":"com.example.app", - * "productId":"exampleSku", - * "purchaseTime":1345678900000, - * "purchaseToken" : "122333444455555", - * "developerPayload":"example developer payload" }' - * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that - * was signed with the private key of the developer - */ - Bundle getBuyIntentToReplaceSkus(int apiVersion, String packageName, - in List<String> oldSkus, String newSku, String type, String developerPayload); - - /** - * Returns a pending intent to launch the purchase flow for an in-app item. This method is - * a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams} - * parameter. This parameter is a Bundle of optional keys and values that affect the - * operation of the method. - * @param apiVersion billing API version that the app is using, must be 6 or later - * @param packageName package name of the calling app - * @param sku the SKU of the in-app item as published in the developer console - * @param type of the in-app item being purchased ("inapp" for one-time purchases - * and "subs" for subscriptions) - * @param developerPayload optional argument to be sent back with the purchase information - * @extraParams a Bundle with the following optional keys: - * "skusToReplace" - List<String> - an optional list of SKUs that the user is - * upgrading or downgrading from. - * Pass this field if the purchase is upgrading or downgrading - * existing subscriptions. - * The specified SKUs are replaced with the SKUs that the user is - * purchasing. Google Play replaces the specified SKUs at the start of - * the next billing cycle. - * "replaceSkusProration" - Boolean - whether the user should be credited for any unused - * subscription time on the SKUs they are upgrading or downgrading. - * If you set this field to true, Google Play swaps out the old SKUs - * and credits the user with the unused value of their subscription - * time on a pro-rated basis. - * Google Play applies this credit to the new subscription, and does - * not begin billing the user for the new subscription until after - * the credit is used up. - * If you set this field to false, the user does not receive credit for - * any unused subscription time and the recurrence date does not - * change. - * Default value is true. Ignored if you do not pass skusToReplace. - * "accountId" - String - an optional obfuscated string that is uniquely - * associated with the user's account in your app. - * If you pass this value, Google Play can use it to detect irregular - * activity, such as many devices making purchases on the same - * account in a short period of time. - * Do not use the developer ID or the user's Google ID for this field. - * In addition, this field should not contain the user's ID in - * cleartext. - * We recommend that you use a one-way hash to generate a string from - * the user's ID, and store the hashed string in this field. - * "vr" - Boolean - an optional flag indicating whether the returned intent - * should start a VR purchase flow. The apiVersion must also be 7 or - * later to use this flag. - */ - Bundle getBuyIntentExtraParams(int apiVersion, String packageName, String sku, - String type, String developerPayload, in Bundle extraParams); - - /** - * Returns the most recent purchase made by the user for each SKU, even if that purchase is - * expired, canceled, or consumed. - * @param apiVersion billing API version that the app is using, must be 6 or later - * @param packageName package name of the calling app - * @param type of the in-app items being requested ("inapp" for one-time purchases - * and "subs" for subscriptions) - * @param continuationToken to be set as null for the first call, if the number of owned - * skus is too large, a continuationToken is returned in the response bundle. - * This method can be called again with the continuation token to get the next set of - * owned skus. - * @param extraParams a Bundle with extra params that would be appended into http request - * query string. Not used at this moment. Reserved for future functionality. - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value: RESULT_OK(0) if success, - * {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures. - * - * "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs - * "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information - * "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures - * of the purchase information - * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the - * next set of in-app purchases. Only set if the - * user has more owned skus than the current list. - */ - Bundle getPurchaseHistory(int apiVersion, String packageName, String type, - String continuationToken, in Bundle extraParams); - - /** - * This method is a variant of {@link #isBillingSupported}} that takes an additional - * {@code extraParams} parameter. - * @param apiVersion billing API version that the app is using, must be 7 or later - * @param packageName package name of the calling app - * @param type of the in-app item being purchased ("inapp" for one-time purchases and "subs" - * for subscriptions) - * @param extraParams a Bundle with the following optional keys: - * "vr" - Boolean - an optional flag to indicate whether {link #getBuyIntentExtraParams} - * supports returning a VR purchase flow. - * @return RESULT_OK(0) on success and appropriate response code on failures. - */ - int isBillingSupportedExtraParams(int apiVersion, String packageName, String type, - in Bundle extraParams); -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java deleted file mode 100644 index de009f6d16..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java +++ /dev/null @@ -1,116 +0,0 @@ -/*************************************************************************/ -/* ConsumeTask.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment; - -import android.content.Context; -import android.os.AsyncTask; -import android.os.RemoteException; - -import com.android.vending.billing.IInAppBillingService; - -import java.lang.ref.WeakReference; - -abstract public class ConsumeTask { - private Context context; - private IInAppBillingService mService; - - private String mSku; - private String mToken; - - private static class ConsumeAsyncTask extends AsyncTask<String, String, String> { - private WeakReference<ConsumeTask> mTask; - - ConsumeAsyncTask(ConsumeTask consume) { - mTask = new WeakReference<>(consume); - } - - @Override - protected String doInBackground(String... strings) { - ConsumeTask consume = mTask.get(); - if (consume != null) { - return consume.doInBackground(strings); - } - return null; - } - - @Override - protected void onPostExecute(String param) { - ConsumeTask consume = mTask.get(); - if (consume != null) { - consume.onPostExecute(param); - } - } - } - - public ConsumeTask(IInAppBillingService mService, Context context) { - this.context = context; - this.mService = mService; - } - - public void consume(final String sku) { - mSku = sku; - PaymentsCache pc = new PaymentsCache(context); - Boolean isBlocked = pc.getConsumableFlag("block", sku); - mToken = pc.getConsumableValue("token", sku); - if (!isBlocked && mToken == null) { - // Consuming task is processing - } else if (!isBlocked) { - return; - } else if (mToken == null) { - this.error("No token for sku:" + sku); - return; - } - new ConsumeAsyncTask(this).execute(); - } - - private String doInBackground(String... params) { - try { - int response = mService.consumePurchase(3, context.getPackageName(), mToken); - if (response == 0 || response == 8) { - return null; - } - } catch (RemoteException e) { - return e.getMessage(); - } - return "Some error"; - } - - private void onPostExecute(String param) { - if (param == null) { - success(new PaymentsCache(context).getConsumableValue("ticket", mSku)); - } else { - error(param); - } - } - - abstract protected void success(String ticket); - abstract protected void error(String message); -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java index ded7f0a9aa..6447a9ec3f 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java @@ -32,214 +32,177 @@ package org.godotengine.godot.plugin.payment; import org.godotengine.godot.Dictionary; import org.godotengine.godot.Godot; -import org.godotengine.godot.GodotLib; import org.godotengine.godot.plugin.GodotPlugin; - -import android.content.Intent; -import android.util.Log; +import org.godotengine.godot.plugin.SignalInfo; +import org.godotengine.godot.plugin.payment.utils.GodotPaymentUtils; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.collection.ArraySet; + +import com.android.billingclient.api.AcknowledgePurchaseParams; +import com.android.billingclient.api.AcknowledgePurchaseResponseListener; +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.BillingClientStateListener; +import com.android.billingclient.api.BillingFlowParams; +import com.android.billingclient.api.BillingResult; +import com.android.billingclient.api.ConsumeParams; +import com.android.billingclient.api.ConsumeResponseListener; +import com.android.billingclient.api.Purchase; +import com.android.billingclient.api.PurchasesUpdatedListener; +import com.android.billingclient.api.SkuDetails; +import com.android.billingclient.api.SkuDetailsParams; +import com.android.billingclient.api.SkuDetailsResponseListener; -import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Set; -import org.json.JSONException; -import org.json.JSONObject; - -public class GodotPayment extends GodotPlugin { - private Integer purchaseCallbackId = 0; - private String accessToken; - private String purchaseValidationUrlPrefix; - private String transactionId; - private final PaymentsManager mPaymentManager; - private final Dictionary mSkuDetails = new Dictionary(); +public class GodotPayment extends GodotPlugin implements PurchasesUpdatedListener, BillingClientStateListener { + private final BillingClient billingClient; + private final HashMap<String, SkuDetails> skuDetailsCache = new HashMap<>(); // sku → SkuDetails public GodotPayment(Godot godot) { super(godot); - mPaymentManager = new PaymentsManager(godot, this); - mPaymentManager.initService(); - } - @Override - public void onMainActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) { - mPaymentManager.processPurchaseResponse(resultCode, data); - } + billingClient = BillingClient + .newBuilder(getGodot()) + .enablePendingPurchases() + .setListener(this) + .build(); } - @Override - public void onMainDestroy() { - super.onMainDestroy(); - if (mPaymentManager != null) { - mPaymentManager.destroy(); - } + public void startConnection() { + billingClient.startConnection(this); } - public void purchase(final String sku, final String transactionId) { - runOnUiThread(new Runnable() { - @Override - public void run() { - mPaymentManager.requestPurchase(sku, transactionId); - } - }); - } - - public void consumeUnconsumedPurchases() { - runOnUiThread(new Runnable() { - @Override - public void run() { - mPaymentManager.consumeUnconsumedPurchases(); - } - }); + public void endConnection() { + billingClient.endConnection(); } - private String signature; - - public String getSignature() { - return this.signature; + public boolean isReady() { + return this.billingClient.isReady(); } - public void callbackSuccess(String ticket, String signature, String sku) { - GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku }); - } + public Dictionary queryPurchases(String type) { + Purchase.PurchasesResult result = billingClient.queryPurchases(type); - public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) { - Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku); - GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku }); - } - - public void callbackSuccessNoUnconsumedPurchases() { - GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {}); - } - - public void callbackFailConsume(String message) { - GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message }); - } - - public void callbackFail(String message) { - GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message }); - } - - public void callbackCancel() { - GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {}); - } - - public void callbackAlreadyOwned(String sku) { - GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku }); - } - - public int getPurchaseCallbackId() { - return purchaseCallbackId; - } - - public void setPurchaseCallbackId(int purchaseCallbackId) { - this.purchaseCallbackId = purchaseCallbackId; - } + Dictionary returnValue = new Dictionary(); + if (result.getBillingResult().getResponseCode() == BillingClient.BillingResponseCode.OK) { + returnValue.put("status", 0); // OK = 0 + returnValue.put("purchases", GodotPaymentUtils.convertPurchaseListToDictionaryObjectArray(result.getPurchasesList())); + } else { + returnValue.put("status", 1); // FAILED = 1 + returnValue.put("response_code", result.getBillingResult().getResponseCode()); + returnValue.put("debug_message", result.getBillingResult().getDebugMessage()); + } - public String getPurchaseValidationUrlPrefix() { - return this.purchaseValidationUrlPrefix; + return returnValue; } - public void setPurchaseValidationUrlPrefix(String url) { - this.purchaseValidationUrlPrefix = url; - } + public void querySkuDetails(final String[] list, String type) { + List<String> skuList = Arrays.asList(list); - public String getAccessToken() { - return accessToken; - } + SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder() + .setSkusList(skuList) + .setType(type); - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; + billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() { + @Override + public void onSkuDetailsResponse(BillingResult billingResult, + List<SkuDetails> skuDetailsList) { + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { + for (SkuDetails skuDetails : skuDetailsList) { + skuDetailsCache.put(skuDetails.getSku(), skuDetails); + } + emitSignal("sku_details_query_completed", (Object)GodotPaymentUtils.convertSkuDetailsListToDictionaryObjectArray(skuDetailsList)); + } else { + emitSignal("sku_details_query_error", billingResult.getResponseCode(), billingResult.getDebugMessage(), list); + } + } + }); } - public void setTransactionId(String transactionId) { - this.transactionId = transactionId; + public void acknowledgePurchase(final String purchaseToken) { + AcknowledgePurchaseParams acknowledgePurchaseParams = + AcknowledgePurchaseParams.newBuilder() + .setPurchaseToken(purchaseToken) + .build(); + billingClient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() { + @Override + public void onAcknowledgePurchaseResponse(BillingResult billingResult) { + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { + emitSignal("purchase_acknowledged", purchaseToken); + } else { + emitSignal("purchase_acknowledgement_error", billingResult.getResponseCode(), billingResult.getDebugMessage(), purchaseToken); + } + } + }); } - public String getTransactionId() { - return this.transactionId; - } + public void consumePurchase(String purchaseToken) { + ConsumeParams consumeParams = ConsumeParams.newBuilder() + .setPurchaseToken(purchaseToken) + .build(); - // request purchased items are not consumed - public void requestPurchased() { - runOnUiThread(new Runnable() { + billingClient.consumeAsync(consumeParams, new ConsumeResponseListener() { @Override - public void run() { - mPaymentManager.requestPurchased(); + public void onConsumeResponse(BillingResult billingResult, String purchaseToken) { + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { + emitSignal("purchase_consumed", purchaseToken); + } else { + emitSignal("purchase_consumption_error", billingResult.getResponseCode(), billingResult.getDebugMessage(), purchaseToken); + } } }); } - // callback for requestPurchased() - public void callbackPurchased(String receipt, String signature, String sku) { - GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku }); - } - - public void callbackDisconnected() { - GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {}); + @Override + public void onBillingSetupFinished(BillingResult billingResult) { + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { + emitSignal("connected"); + } else { + emitSignal("connect_error", billingResult.getResponseCode(), billingResult.getDebugMessage()); + } } - public void callbackConnected() { - GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {}); + @Override + public void onBillingServiceDisconnected() { + emitSignal("disconnected"); } - // true if connected, false otherwise - public boolean isConnected() { - return mPaymentManager.isConnected(); - } + public Dictionary purchase(String sku) { + if (!skuDetailsCache.containsKey(sku)) { + emitSignal("purchase_error", null, "You must query the sku details and wait for the result before purchasing!"); + } - // consume item automatically after purchase. default is true. - public void setAutoConsume(boolean autoConsume) { - mPaymentManager.setAutoConsume(autoConsume); - } + SkuDetails skuDetails = skuDetailsCache.get(sku); + BillingFlowParams purchaseParams = BillingFlowParams.newBuilder() + .setSkuDetails(skuDetails) + .build(); - // consume a specific item - public void consume(String sku) { - mPaymentManager.consume(sku); - } + BillingResult result = billingClient.launchBillingFlow(getGodot(), purchaseParams); - // query in app item detail info - public void querySkuDetails(String[] list) { - List<String> nKeys = Arrays.asList(list); - List<String> cKeys = Arrays.asList(mSkuDetails.get_keys()); - ArrayList<String> fKeys = new ArrayList<String>(); - for (String key : nKeys) { - if (!cKeys.contains(key)) { - fKeys.add(key); - } - } - if (fKeys.size() > 0) { - mPaymentManager.querySkuDetails(fKeys.toArray(new String[0])); + Dictionary returnValue = new Dictionary(); + if (result.getResponseCode() == BillingClient.BillingResponseCode.OK) { + returnValue.put("status", 0); // OK = 0 } else { - completeSkuDetail(); - } - } - - public void addSkuDetail(String itemJson) { - JSONObject o = null; - try { - o = new JSONObject(itemJson); - Dictionary item = new Dictionary(); - item.put("type", o.optString("type")); - item.put("product_id", o.optString("productId")); - item.put("title", o.optString("title")); - item.put("description", o.optString("description")); - item.put("price", o.optString("price")); - item.put("price_currency_code", o.optString("price_currency_code")); - item.put("price_amount", 0.000001d * o.optLong("price_amount_micros")); - mSkuDetails.put(item.get("product_id").toString(), item); - } catch (JSONException e) { - e.printStackTrace(); + returnValue.put("status", 1); // FAILED = 1 + returnValue.put("response_code", result.getResponseCode()); + returnValue.put("debug_message", result.getDebugMessage()); } - } - public void completeSkuDetail() { - GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails }); + return returnValue; } - public void errorSkuDetail(String errorMessage) { - GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { errorMessage }); + @Override + public void onPurchasesUpdated(final BillingResult billingResult, @Nullable final List<Purchase> list) { + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null) { + emitSignal("purchases_updated", (Object)GodotPaymentUtils.convertPurchaseListToDictionaryObjectArray(list)); + } else { + emitSignal("purchase_error", billingResult.getResponseCode(), billingResult.getDebugMessage()); + } } @NonNull @@ -251,8 +214,26 @@ public class GodotPayment extends GodotPlugin { @NonNull @Override public List<String> getPluginMethods() { - return Arrays.asList("purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", - "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", - "setAutoConsume", "consume", "querySkuDetails", "isConnected"); + return Arrays.asList("startConnection", "endConnection", "purchase", "querySkuDetails", "isReady", "queryPurchases", "acknowledgePurchase", "consumePurchase"); + } + + @NonNull + @Override + public Set<SignalInfo> getPluginSignals() { + Set<SignalInfo> signals = new ArraySet<>(); + + signals.add(new SignalInfo("connected")); + signals.add(new SignalInfo("disconnected")); + signals.add(new SignalInfo("connect_error", Integer.class, String.class)); + signals.add(new SignalInfo("purchases_updated", Object[].class)); + signals.add(new SignalInfo("purchase_error", Integer.class, String.class)); + signals.add(new SignalInfo("sku_details_query_completed", Object[].class)); + signals.add(new SignalInfo("sku_details_query_error", Integer.class, String.class, String[].class)); + signals.add(new SignalInfo("purchase_acknowledged", String.class)); + signals.add(new SignalInfo("purchase_acknowledgement_error", Integer.class, String.class, String.class)); + signals.add(new SignalInfo("purchase_consumed", String.class)); + signals.add(new SignalInfo("purchase_consumption_error", Integer.class, String.class, String.class)); + + return signals; } } diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java deleted file mode 100644 index 00e216e8c0..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java +++ /dev/null @@ -1,93 +0,0 @@ -/*************************************************************************/ -/* HandlePurchaseTask.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment; - -import android.app.Activity; -import android.content.Intent; - -import org.json.JSONException; -import org.json.JSONObject; - -abstract public class HandlePurchaseTask { - private Activity context; - - public HandlePurchaseTask(Activity context) { - this.context = context; - } - - public void handlePurchaseRequest(int resultCode, Intent data) { - //Log.d("XXX", "Handling purchase response"); - if (resultCode == Activity.RESULT_OK) { - try { - //int responseCode = data.getIntExtra("RESPONSE_CODE", 0); - PaymentsCache pc = new PaymentsCache(context); - - String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); - //Log.d("XXX", "Purchase data:" + purchaseData); - String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); - //Log.d("XXX", "Purchase signature:" + dataSignature); - //Log.d("SARLANGA", purchaseData); - - JSONObject jo = new JSONObject(purchaseData); - //String sku = jo.getString("productId"); - //alert("You have bought the " + sku + ". Excellent choice, aventurer!"); - //String orderId = jo.getString("orderId"); - //String packageName = jo.getString("packageName"); - String productId = jo.getString("productId"); - //Long purchaseTime = jo.getLong("purchaseTime"); - //Integer state = jo.getInt("purchaseState"); - String developerPayload = jo.getString("developerPayload"); - String purchaseToken = jo.getString("purchaseToken"); - - if (!pc.getConsumableValue("validation_hash", productId).equals(developerPayload)) { - error("Untrusted callback"); - return; - } - //Log.d("XXX", "Este es el product ID:" + productId); - pc.setConsumableValue("ticket_signautre", productId, dataSignature); - pc.setConsumableValue("ticket", productId, purchaseData); - pc.setConsumableFlag("block", productId, true); - pc.setConsumableValue("token", productId, purchaseToken); - - success(productId, dataSignature, purchaseData); - return; - } catch (JSONException e) { - error(e.getMessage()); - } - } else if (resultCode == Activity.RESULT_CANCELED) { - canceled(); - } - } - - abstract protected void success(String sku, String signature, String ticket); - abstract protected void error(String message); - abstract protected void canceled(); -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java deleted file mode 100644 index 435f43c49d..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java +++ /dev/null @@ -1,70 +0,0 @@ -/*************************************************************************/ -/* PaymentsCache.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment; - -import android.content.Context; -import android.content.SharedPreferences; - -public class PaymentsCache { - public Context context; - - public PaymentsCache(Context context) { - this.context = context; - } - - public void setConsumableFlag(String set, String sku, Boolean flag) { - SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putBoolean(sku, flag); - editor.apply(); - } - - public boolean getConsumableFlag(String set, String sku) { - SharedPreferences sharedPref = context.getSharedPreferences( - "consumables_" + set, Context.MODE_PRIVATE); - return sharedPref.getBoolean(sku, false); - } - - public void setConsumableValue(String set, String sku, String value) { - SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putString(sku, value); - //Log.d("XXX", "Setting asset: consumables_" + set + ":" + sku); - editor.apply(); - } - - public String getConsumableValue(String set, String sku) { - SharedPreferences sharedPref = context.getSharedPreferences( - "consumables_" + set, Context.MODE_PRIVATE); - //Log.d("XXX", "Getting asset: consumables_" + set + ":" + sku); - return sharedPref.getString(sku, null); - } -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java deleted file mode 100644 index 9b3a338866..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java +++ /dev/null @@ -1,403 +0,0 @@ -/*************************************************************************/ -/* PaymentsManager.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.text.TextUtils; -import android.util.Log; - -import com.android.vending.billing.IInAppBillingService; - -import java.util.ArrayList; -import java.util.Arrays; - -import org.json.JSONException; -import org.json.JSONObject; - -public class PaymentsManager { - public static final int BILLING_RESPONSE_RESULT_OK = 0; - public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001; - private static boolean auto_consume = true; - - private final Activity activity; - private final GodotPayment godotPayment; - IInAppBillingService mService; - - PaymentsManager(Activity activity, GodotPayment godotPayment) { - this.activity = activity; - this.godotPayment = godotPayment; - } - - public PaymentsManager initService() { - Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); - intent.setPackage("com.android.vending"); - activity.bindService( - intent, - mServiceConn, - Context.BIND_AUTO_CREATE); - return this; - } - - public void destroy() { - if (mService != null) { - activity.unbindService(mServiceConn); - } - } - - ServiceConnection mServiceConn = new ServiceConnection() { - @Override - public void onServiceDisconnected(ComponentName name) { - mService = null; - - // At this stage, godotPayment might not have been initialized yet. - if (godotPayment != null) { - godotPayment.callbackDisconnected(); - } - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - mService = IInAppBillingService.Stub.asInterface(service); - - // At this stage, godotPayment might not have been initialized yet. - if (godotPayment != null) { - godotPayment.callbackConnected(); - } - } - }; - - public void requestPurchase(final String sku, String transactionId) { - new PurchaseTask(mService, activity) { - @Override - protected void error(String message) { - godotPayment.callbackFail(message); - } - - @Override - protected void canceled() { - godotPayment.callbackCancel(); - } - - @Override - protected void alreadyOwned() { - godotPayment.callbackAlreadyOwned(sku); - } - } - .purchase(sku, transactionId); - } - - public boolean isConnected() { - return mService != null; - } - - public void consumeUnconsumedPurchases() { - new ReleaseAllConsumablesTask(mService, activity) { - @Override - protected void success(String sku, String receipt, String signature, String token) { - godotPayment.callbackSuccessProductMassConsumed(receipt, signature, sku); - } - - @Override - protected void error(String message) { - Log.d("godot", "consumeUnconsumedPurchases :" + message); - godotPayment.callbackFailConsume(message); - } - - @Override - protected void notRequired() { - Log.d("godot", "callbackSuccessNoUnconsumedPurchases :"); - godotPayment.callbackSuccessNoUnconsumedPurchases(); - } - } - .consumeItAll(); - } - - public void requestPurchased() { - try { - PaymentsCache pc = new PaymentsCache(activity); - - String continueToken = null; - - do { - Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp", continueToken); - - if (bundle.getInt("RESPONSE_CODE") == 0) { - final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); - final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); - - if (myPurchases == null || myPurchases.size() == 0) { - godotPayment.callbackPurchased("", "", ""); - return; - } - - for (int i = 0; i < myPurchases.size(); i++) { - try { - String receipt = myPurchases.get(i); - JSONObject inappPurchaseData = new JSONObject(receipt); - String sku = inappPurchaseData.getString("productId"); - String token = inappPurchaseData.getString("purchaseToken"); - String signature = mySignatures.get(i); - - pc.setConsumableValue("ticket_signautre", sku, signature); - pc.setConsumableValue("ticket", sku, receipt); - pc.setConsumableFlag("block", sku, true); - pc.setConsumableValue("token", sku, token); - - godotPayment.callbackPurchased(receipt, signature, sku); - } catch (JSONException e) { - } - } - } - continueToken = bundle.getString("INAPP_CONTINUATION_TOKEN"); - Log.d("godot", "continue token = " + continueToken); - } while (!TextUtils.isEmpty(continueToken)); - } catch (Exception e) { - Log.d("godot", "Error requesting purchased products:" + e.getClass().getName() + ":" + e.getMessage()); - } - } - - public void processPurchaseResponse(int resultCode, Intent data) { - new HandlePurchaseTask(activity) { - @Override - protected void success(final String sku, final String signature, final String ticket) { - godotPayment.callbackSuccess(ticket, signature, sku); - - if (auto_consume) { - new ConsumeTask(mService, activity) { - @Override - protected void success(String ticket) { - } - - @Override - protected void error(String message) { - godotPayment.callbackFail(message); - } - } - .consume(sku); - } - } - - @Override - protected void error(String message) { - godotPayment.callbackFail(message); - } - - @Override - protected void canceled() { - godotPayment.callbackCancel(); - } - } - .handlePurchaseRequest(resultCode, data); - } - - public void validatePurchase(String purchaseToken, final String sku) { - new ValidateTask(activity, godotPayment) { - @Override - protected void success() { - new ConsumeTask(mService, activity) { - @Override - protected void success(String ticket) { - godotPayment.callbackSuccess(ticket, null, sku); - } - - @Override - protected void error(String message) { - godotPayment.callbackFail(message); - } - } - .consume(sku); - } - - @Override - protected void error(String message) { - godotPayment.callbackFail(message); - } - - @Override - protected void canceled() { - godotPayment.callbackCancel(); - } - } - .validatePurchase(sku); - } - - public void setAutoConsume(boolean autoConsume) { - auto_consume = autoConsume; - } - - public void consume(final String sku) { - new ConsumeTask(mService, activity) { - @Override - protected void success(String ticket) { - godotPayment.callbackSuccessProductMassConsumed(ticket, "", sku); - } - - @Override - protected void error(String message) { - godotPayment.callbackFailConsume(message); - } - } - .consume(sku); - } - - // Workaround to bug where sometimes response codes come as Long instead of Integer - int getResponseCodeFromBundle(Bundle b) { - Object o = b.get("RESPONSE_CODE"); - if (o == null) { - //logDebug("Bundle with null response code, assuming OK (known issue)"); - return BILLING_RESPONSE_RESULT_OK; - } else if (o instanceof Integer) - return ((Integer)o).intValue(); - else if (o instanceof Long) - return (int)((Long)o).longValue(); - else { - //logError("Unexpected type for bundle response code."); - //logError(o.getClass().getName()); - throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName()); - } - } - - /** - * Returns a human-readable description for the given response code. - * - * @param code The response code - * @return A human-readable string explaining the result code. - * It also includes the result code numerically. - */ - public static String getResponseDesc(int code) { - String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" - + - "3:Billing Unavailable/4:Item unavailable/" - + - "5:Developer Error/6:Error/7:Item Already Owned/" - + - "8:Item not owned") - .split("/"); - String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" - + - "-1002:Bad response received/" - + - "-1003:Purchase signature verification failed/" - + - "-1004:Send intent failed/" - + - "-1005:User cancelled/" - + - "-1006:Unknown purchase response/" - + - "-1007:Missing token/" - + - "-1008:Unknown error/" - + - "-1009:Subscriptions not available/" - + - "-1010:Invalid consumption attempt") - .split("/"); - - if (code <= -1000) { - int index = -1000 - code; - if (index >= 0 && index < iabhelper_msgs.length) - return iabhelper_msgs[index]; - else - return String.valueOf(code) + ":Unknown IAB Helper Error"; - } else if (code < 0 || code >= iab_msgs.length) - return String.valueOf(code) + ":Unknown"; - else - return iab_msgs[code]; - } - - public void querySkuDetails(final String[] list) { - (new Thread(new Runnable() { - @Override - public void run() { - ArrayList<String> skuList = new ArrayList<String>(Arrays.asList(list)); - if (skuList.size() == 0) { - return; - } - // Split the sku list in blocks of no more than 20 elements. - ArrayList<ArrayList<String>> packs = new ArrayList<ArrayList<String>>(); - ArrayList<String> tempList; - int n = skuList.size() / 20; - int mod = skuList.size() % 20; - for (int i = 0; i < n; i++) { - tempList = new ArrayList<String>(); - for (String s : skuList.subList(i * 20, i * 20 + 20)) { - tempList.add(s); - } - packs.add(tempList); - } - if (mod != 0) { - tempList = new ArrayList<String>(); - for (String s : skuList.subList(n * 20, n * 20 + mod)) { - tempList.add(s); - } - packs.add(tempList); - } - for (ArrayList<String> skuPartList : packs) { - Bundle querySkus = new Bundle(); - querySkus.putStringArrayList("ITEM_ID_LIST", skuPartList); - Bundle skuDetails = null; - try { - skuDetails = mService.getSkuDetails(3, activity.getPackageName(), "inapp", querySkus); - if (!skuDetails.containsKey("DETAILS_LIST")) { - int response = getResponseCodeFromBundle(skuDetails); - if (response != BILLING_RESPONSE_RESULT_OK) { - godotPayment.errorSkuDetail(getResponseDesc(response)); - } else { - godotPayment.errorSkuDetail("No error but no detail list."); - } - return; - } - - ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST"); - - for (String thisResponse : responseList) { - Log.d("godot", "response = " + thisResponse); - godotPayment.addSkuDetail(thisResponse); - } - } catch (RemoteException e) { - e.printStackTrace(); - godotPayment.errorSkuDetail("RemoteException error!"); - } - } - godotPayment.completeSkuDetail(); - } - })) - .start(); - } -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java deleted file mode 100644 index f894bd5132..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java +++ /dev/null @@ -1,118 +0,0 @@ -/*************************************************************************/ -/* PurchaseTask.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Intent; -import android.content.IntentSender.SendIntentException; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.Log; - -import com.android.vending.billing.IInAppBillingService; - -abstract public class PurchaseTask { - private Activity context; - - private IInAppBillingService mService; - public PurchaseTask(IInAppBillingService mService, Activity context) { - this.context = context; - this.mService = mService; - } - - private boolean isLooping = false; - - public void purchase(final String sku, final String transactionId) { - Log.d("XXX", "Starting purchase for: " + sku); - PaymentsCache pc = new PaymentsCache(context); - Boolean isBlocked = pc.getConsumableFlag("block", sku); - /* - if(isBlocked) { - Log.d("XXX", "Is awaiting payment confirmation"); - error("Awaiting payment confirmation"); - return; - } - */ - final String hash = transactionId; - - Bundle buyIntentBundle; - try { - buyIntentBundle = mService.getBuyIntent(3, context.getApplicationContext().getPackageName(), sku, "inapp", hash); - } catch (RemoteException e) { - //Log.d("XXX", "Error: " + e.getMessage()); - error(e.getMessage()); - return; - } - Object rc = buyIntentBundle.get("RESPONSE_CODE"); - int responseCode = 0; - if (rc == null) { - responseCode = PaymentsManager.BILLING_RESPONSE_RESULT_OK; - } else if (rc instanceof Integer) { - responseCode = ((Integer)rc).intValue(); - } else if (rc instanceof Long) { - responseCode = (int)((Long)rc).longValue(); - } - //Log.d("XXX", "Buy intent response code: " + responseCode); - if (responseCode == 1 || responseCode == 3 || responseCode == 4) { - canceled(); - return; - } - if (responseCode == 7) { - alreadyOwned(); - return; - } - - PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT"); - pc.setConsumableValue("validation_hash", sku, hash); - try { - if (context == null) { - //Log.d("XXX", "No context!"); - } - if (pendingIntent == null) { - //Log.d("XXX", "No pending intent"); - } - //Log.d("XXX", "Starting activity for purchase!"); - context.startIntentSenderForResult( - pendingIntent.getIntentSender(), - PaymentsManager.REQUEST_CODE_FOR_PURCHASE, - new Intent(), - Integer.valueOf(0), Integer.valueOf(0), - Integer.valueOf(0)); - } catch (SendIntentException e) { - error(e.getMessage()); - } - } - - abstract protected void error(String message); - abstract protected void canceled(); - abstract protected void alreadyOwned(); -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java deleted file mode 100644 index 1d52cf0fa5..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java +++ /dev/null @@ -1,140 +0,0 @@ -/*************************************************************************/ -/* ReleaseAllConsumablesTask.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment; - -import android.content.Context; -import android.os.AsyncTask; -import android.os.Bundle; -import android.util.Log; - -import com.android.vending.billing.IInAppBillingService; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -import org.json.JSONException; -import org.json.JSONObject; - -abstract public class ReleaseAllConsumablesTask { - private Context context; - private IInAppBillingService mService; - - private static class ReleaseAllConsumablesAsyncTask extends AsyncTask<String, String, String> { - private WeakReference<ReleaseAllConsumablesTask> mTask; - private String mSku; - private String mReceipt; - private String mSignature; - private String mToken; - - ReleaseAllConsumablesAsyncTask(ReleaseAllConsumablesTask task, String sku, String receipt, String signature, String token) { - mTask = new WeakReference<ReleaseAllConsumablesTask>(task); - - mSku = sku; - mReceipt = receipt; - mSignature = signature; - mToken = token; - } - - @Override - protected String doInBackground(String... params) { - ReleaseAllConsumablesTask consume = mTask.get(); - if (consume != null) { - return consume.doInBackground(mToken); - } - return null; - } - - @Override - protected void onPostExecute(String param) { - ReleaseAllConsumablesTask consume = mTask.get(); - if (consume != null) { - consume.success(mSku, mReceipt, mSignature, mToken); - } - } - } - - public ReleaseAllConsumablesTask(IInAppBillingService mService, Context context) { - this.context = context; - this.mService = mService; - } - - public void consumeItAll() { - try { - //Log.d("godot", "consumeItall for " + context.getPackageName()); - Bundle bundle = mService.getPurchases(3, context.getPackageName(), "inapp", null); - - if (bundle.getInt("RESPONSE_CODE") == 0) { - final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); - final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); - - if (myPurchases == null || myPurchases.size() == 0) { - //Log.d("godot", "No purchases!"); - notRequired(); - return; - } - - //Log.d("godot", "# products to be consumed:" + myPurchases.size()); - for (int i = 0; i < myPurchases.size(); i++) { - try { - String receipt = myPurchases.get(i); - JSONObject inappPurchaseData = new JSONObject(receipt); - String sku = inappPurchaseData.getString("productId"); - String token = inappPurchaseData.getString("purchaseToken"); - String signature = mySignatures.get(i); - //Log.d("godot", "A punto de consumir un item con token:" + token + "\n" + receipt); - new ReleaseAllConsumablesAsyncTask(this, sku, receipt, signature, token).execute(); - } catch (JSONException e) { - } - } - } - } catch (Exception e) { - Log.d("godot", "Error releasing products:" + e.getClass().getName() + ":" + e.getMessage()); - } - } - - private String doInBackground(String token) { - try { - //Log.d("godot", "Requesting to consume an item with token ." + token); - int response = mService.consumePurchase(3, context.getPackageName(), token); - //Log.d("godot", "consumePurchase response: " + response); - if (response == 0 || response == 8) { - return null; - } - } catch (Exception e) { - Log.d("godot", "Error " + e.getClass().getName() + ":" + e.getMessage()); - } - return null; - } - - abstract protected void success(String sku, String receipt, String signature, String token); - abstract protected void error(String message); - abstract protected void notRequired(); -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java deleted file mode 100644 index a7156152ce..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************/ -/* ValidateTask.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment; - -import org.godotengine.godot.plugin.payment.utils.HttpRequester; -import org.godotengine.godot.plugin.payment.utils.RequestParams; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.os.AsyncTask; - -import java.lang.ref.WeakReference; - -import org.json.JSONException; -import org.json.JSONObject; - -abstract public class ValidateTask { - private Activity context; - private GodotPayment godotPayments; - private ProgressDialog dialog; - private String mSku; - - private static class ValidateAsyncTask extends AsyncTask<String, String, String> { - private WeakReference<ValidateTask> mTask; - - ValidateAsyncTask(ValidateTask task) { - mTask = new WeakReference<>(task); - } - - @Override - protected void onPreExecute() { - ValidateTask task = mTask.get(); - if (task != null) { - task.onPreExecute(); - } - } - - @Override - protected String doInBackground(String... params) { - ValidateTask task = mTask.get(); - if (task != null) { - return task.doInBackground(params); - } - return null; - } - - @Override - protected void onPostExecute(String response) { - ValidateTask task = mTask.get(); - if (task != null) { - task.onPostExecute(response); - } - } - } - - public ValidateTask(Activity context, GodotPayment godotPayments) { - this.context = context; - this.godotPayments = godotPayments; - } - - public void validatePurchase(final String sku) { - mSku = sku; - new ValidateAsyncTask(this).execute(); - } - - private void onPreExecute() { - dialog = ProgressDialog.show(context, null, "Please wait..."); - } - - private String doInBackground(String... params) { - PaymentsCache pc = new PaymentsCache(context); - String url = godotPayments.getPurchaseValidationUrlPrefix(); - RequestParams param = new RequestParams(); - param.setUrl(url); - param.put("ticket", pc.getConsumableValue("ticket", mSku)); - param.put("purchaseToken", pc.getConsumableValue("token", mSku)); - param.put("sku", mSku); - //Log.d("XXX", "Haciendo request a " + url); - //Log.d("XXX", "ticket: " + pc.getConsumableValue("ticket", sku)); - //Log.d("XXX", "purchaseToken: " + pc.getConsumableValue("token", sku)); - //Log.d("XXX", "sku: " + sku); - param.put("package", context.getApplicationContext().getPackageName()); - HttpRequester requester = new HttpRequester(); - String jsonResponse = requester.post(param); - //Log.d("XXX", "Validation response:\n"+jsonResponse); - return jsonResponse; - } - - private void onPostExecute(String response) { - if (dialog != null) { - dialog.dismiss(); - dialog = null; - } - JSONObject j; - try { - j = new JSONObject(response); - if (j.getString("status").equals("OK")) { - success(); - return; - } else if (j.getString("status") != null) { - error(j.getString("message")); - } else { - error("Connection error"); - } - } catch (JSONException e) { - error(e.getMessage()); - } catch (Exception e) { - error(e.getMessage()); - } - } - - abstract protected void success(); - abstract protected void error(String message); - abstract protected void canceled(); -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java deleted file mode 100644 index 55b87b49e5..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************/ -/* CustomSSLSocketFactory.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment.utils; - -import java.io.IOException; -import java.net.Socket; -import java.net.UnknownHostException; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; - -import org.apache.http.conn.ssl.SSLSocketFactory; - -/** - * - * @author Luis Linietsky <luis.linietsky@gmail.com> - */ -public class CustomSSLSocketFactory extends SSLSocketFactory { - SSLContext sslContext = SSLContext.getInstance("TLS"); - - public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - super(truststore); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); - tmf.init(truststore); - - sslContext.init(null, tmf.getTrustManagers(), null); - } - - @Override - public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { - return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); - } - - @Override - public Socket createSocket() throws IOException { - return sslContext.getSocketFactory().createSocket(); - } -} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/GodotPaymentUtils.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/GodotPaymentUtils.java new file mode 100644 index 0000000000..f569c1b8bf --- /dev/null +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/GodotPaymentUtils.java @@ -0,0 +1,66 @@ +package org.godotengine.godot.plugin.payment.utils; + +import org.godotengine.godot.Dictionary; + +import com.android.billingclient.api.Purchase; +import com.android.billingclient.api.SkuDetails; + +import java.util.List; + +public class GodotPaymentUtils { + public static Dictionary convertPurchaseToDictionary(Purchase purchase) { + Dictionary dictionary = new Dictionary(); + dictionary.put("order_id", purchase.getOrderId()); + dictionary.put("package_name", purchase.getPackageName()); + dictionary.put("purchase_state", Integer.valueOf(purchase.getPurchaseState())); + dictionary.put("purchase_time", Long.valueOf(purchase.getPurchaseTime())); + dictionary.put("purchase_token", purchase.getPurchaseToken()); + dictionary.put("signature", purchase.getSignature()); + dictionary.put("sku", purchase.getSku()); + dictionary.put("is_acknowledged", Boolean.valueOf(purchase.isAcknowledged())); + dictionary.put("is_auto_renewing", Boolean.valueOf(purchase.isAutoRenewing())); + return dictionary; + } + + public static Dictionary convertSkuDetailsToDictionary(SkuDetails details) { + Dictionary dictionary = new Dictionary(); + dictionary.put("sku", details.getSku()); + dictionary.put("title", details.getTitle()); + dictionary.put("description", details.getDescription()); + dictionary.put("price", details.getPrice()); + dictionary.put("price_currency_code", details.getPriceCurrencyCode()); + dictionary.put("price_amount_micros", Long.valueOf(details.getPriceAmountMicros())); + dictionary.put("free_trial_period", details.getFreeTrialPeriod()); + dictionary.put("icon_url", details.getIconUrl()); + dictionary.put("introductory_price", details.getIntroductoryPrice()); + dictionary.put("introductory_price_amount_micros", Long.valueOf(details.getIntroductoryPriceAmountMicros())); + dictionary.put("introductory_price_cycles", details.getIntroductoryPriceCycles()); + dictionary.put("introductory_price_period", details.getIntroductoryPricePeriod()); + dictionary.put("original_price", details.getOriginalPrice()); + dictionary.put("original_price_amount_micros", Long.valueOf(details.getOriginalPriceAmountMicros())); + dictionary.put("subscription_period", details.getSubscriptionPeriod()); + dictionary.put("type", details.getType()); + dictionary.put("is_rewarded", Boolean.valueOf(details.isRewarded())); + return dictionary; + } + + public static Object[] convertPurchaseListToDictionaryObjectArray(List<Purchase> purchases) { + Object[] purchaseDictionaries = new Object[purchases.size()]; + + for (int i = 0; i < purchases.size(); i++) { + purchaseDictionaries[i] = GodotPaymentUtils.convertPurchaseToDictionary(purchases.get(i)); + } + + return purchaseDictionaries; + } + + public static Object[] convertSkuDetailsListToDictionaryObjectArray(List<SkuDetails> skuDetails) { + Object[] skuDetailsDictionaries = new Object[skuDetails.size()]; + + for (int i = 0; i < skuDetails.size(); i++) { + skuDetailsDictionaries[i] = GodotPaymentUtils.convertSkuDetailsToDictionary(skuDetails.get(i)); + } + + return skuDetailsDictionaries; + } +} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java deleted file mode 100644 index 0afcf60f38..0000000000 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java +++ /dev/null @@ -1,230 +0,0 @@ -/*************************************************************************/ -/* HttpRequester.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 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. */ -/*************************************************************************/ - -package org.godotengine.godot.plugin.payment.utils; - -import org.godotengine.godot.utils.Crypt; - -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.security.KeyStore; -import java.util.Date; - -import org.apache.http.HttpResponse; -import org.apache.http.HttpVersion; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.protocol.HTTP; -import org.apache.http.util.EntityUtils; - -/** - * - * @author Luis Linietsky <luis.linietsky@gmail.com> - */ -public class HttpRequester { - private Context context; - private static final int TTL = 600000; // 10 minutos - private long cttl = 0; - - public HttpRequester() { - //Log.d("XXX", "Creando http request sin contexto"); - } - - public HttpRequester(Context context) { - this.context = context; - //Log.d("XXX", "Creando http request con contexto"); - } - - public String post(RequestParams params) { - HttpPost httppost = new HttpPost(params.getUrl()); - try { - httppost.setEntity(new UrlEncodedFormEntity(params.toPairsList())); - return request(httppost); - } catch (UnsupportedEncodingException e) { - return null; - } - } - - public String get(RequestParams params) { - String response = getResponseFromCache(params.getUrl()); - if (response == null) { - //Log.d("XXX", "Cache miss!"); - HttpGet httpget = new HttpGet(params.getUrl()); - long timeInit = new Date().getTime(); - response = request(httpget); - long delay = new Date().getTime() - timeInit; - Log.d("HttpRequest::get(url)", "Url: " + params.getUrl() + " downloaded in " + String.format("%.03f", delay / 1000.0f) + " seconds"); - if (response == null || response.length() == 0) { - response = ""; - } else { - saveResponseIntoCache(params.getUrl(), response); - } - } - Log.d("XXX", "Req: " + params.getUrl()); - Log.d("XXX", "Resp: " + response); - return response; - } - - private String request(HttpUriRequest request) { - //Log.d("XXX", "Haciendo request a: " + request.getURI() ); - Log.d("PPP", "Haciendo request a: " + request.getURI()); - long init = new Date().getTime(); - HttpClient httpclient = getNewHttpClient(); - HttpParams httpParameters = httpclient.getParams(); - HttpConnectionParams.setConnectionTimeout(httpParameters, 0); - HttpConnectionParams.setSoTimeout(httpParameters, 0); - HttpConnectionParams.setTcpNoDelay(httpParameters, true); - try { - HttpResponse response = httpclient.execute(request); - Log.d("PPP", "Fin de request (" + (new Date().getTime() - init) + ") a: " + request.getURI()); - //Log.d("XXX1", "Status:" + response.getStatusLine().toString()); - if (response.getStatusLine().getStatusCode() == 200) { - String strResponse = EntityUtils.toString(response.getEntity()); - //Log.d("XXX2", strResponse); - return strResponse; - } else { - Log.d("XXX3", "Response status code:" + response.getStatusLine().getStatusCode() + "\n" + EntityUtils.toString(response.getEntity())); - return null; - } - - } catch (ClientProtocolException e) { - Log.d("XXX3", e.getMessage()); - } catch (IOException e) { - Log.d("XXX4", e.getMessage()); - } - return null; - } - - private HttpClient getNewHttpClient() { - try { - KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - trustStore.load(null, null); - - SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore); - sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); - - HttpParams params = new BasicHttpParams(); - HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); - HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); - - SchemeRegistry registry = new SchemeRegistry(); - registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); - registry.register(new Scheme("https", sf, 443)); - - ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); - - return new DefaultHttpClient(ccm, params); - } catch (Exception e) { - return new DefaultHttpClient(); - } - } - - private static String convertStreamToString(InputStream is) { - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - StringBuilder sb = new StringBuilder(); - String line = null; - try { - while ((line = reader.readLine()) != null) { - sb.append((line + "\n")); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return sb.toString(); - } - - public void saveResponseIntoCache(String request, String response) { - if (context == null) { - //Log.d("XXX", "No context, cache failed!"); - return; - } - SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putString("request_" + Crypt.md5(request), response); - editor.putLong("request_" + Crypt.md5(request) + "_ttl", new Date().getTime() + getTtl()); - editor.apply(); - } - - public String getResponseFromCache(String request) { - if (context == null) { - Log.d("XXX", "No context, cache miss"); - return null; - } - SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE); - long ttl = getResponseTtl(request); - if (ttl == 0l || (new Date().getTime() - ttl) > 0l) { - Log.d("XXX", "Cache invalid ttl:" + ttl + " vs now:" + new Date().getTime()); - return null; - } - return sharedPref.getString("request_" + Crypt.md5(request), null); - } - - public long getResponseTtl(String request) { - SharedPreferences sharedPref = context.getSharedPreferences( - "http_get_cache", Context.MODE_PRIVATE); - return sharedPref.getLong("request_" + Crypt.md5(request) + "_ttl", 0l); - } - - public long getTtl() { - return cttl > 0 ? cttl : TTL; - } - - public void setTtl(long ttl) { - this.cttl = (ttl * 1000) + new Date().getTime(); - } -} diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 23e0d24b57..1f61c4a805 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -164,9 +164,20 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc ClassDB::register_class<JNISingleton>(); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height) { - if (os_android) - os_android->set_display_size(Size2i(width, height)); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height) { + if (os_android) { + os_android->set_display_size(Size2i(p_width, p_height)); + + // No need to reset the surface during startup + if (step > 0) { + if (p_surface) { + ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface); + os_android->set_native_window(native_window); + + DisplayServerAndroid::get_singleton()->reset_window(); + } + } + } } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits) { @@ -375,8 +386,8 @@ JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv * return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data()); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) { - Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID)); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) { + Object *obj = ObjectDB::get_instance(ObjectID(ID)); ERR_FAIL_COND(!obj); int res = env->PushLocalFrame(16); @@ -405,8 +416,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en env->PopLocalFrame(nullptr); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) { - Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID)); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) { + Object *obj = ObjectDB::get_instance(ObjectID(ID)); ERR_FAIL_COND(!obj); int res = env->PushLocalFrame(16); diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 221d701e2b..e8be7be0d0 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -40,7 +40,7 @@ extern "C" { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); @@ -61,8 +61,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz); JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz); diff --git a/platform/android/plugin/godot_plugin_config.h b/platform/android/plugin/godot_plugin_config.h index 9ad7de1202..5bc0fc3a58 100644 --- a/platform/android/plugin/godot_plugin_config.h +++ b/platform/android/plugin/godot_plugin_config.h @@ -70,6 +70,8 @@ The `dependencies` section and fields are optional and defined as follow: struct PluginConfig { // Set to true when the config file is properly loaded. bool valid_config = false; + // Unix timestamp of last change to this plugin. + uint64_t last_updated = 0; // Required config section String name; @@ -87,11 +89,12 @@ struct PluginConfig { */ static const PluginConfig GODOT_PAYMENT = { /*.valid_config =*/true, + /*.last_updated =*/0, /*.name =*/"GodotPayment", /*.binary_type =*/"local", /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar", /*.local_dependencies =*/{}, - /*.remote_dependencies =*/{}, + /*.remote_dependencies =*/String("com.android.billingclient:billing:2.2.1").split("|"), /*.custom_maven_repos =*/{} }; @@ -150,6 +153,18 @@ static inline bool is_plugin_config_valid(PluginConfig plugin_config) { return valid_name && valid_binary && valid_binary_type && valid_local_dependencies; } +static inline uint64_t get_plugin_modification_time(const PluginConfig &plugin_config, const String &config_path) { + uint64_t last_updated = FileAccess::get_modified_time(config_path); + last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary)); + + for (int i = 0; i < plugin_config.local_dependencies.size(); i++) { + String binary = plugin_config.local_dependencies.get(i); + last_updated = MAX(last_updated, FileAccess::get_modified_time(binary)); + } + + return last_updated; +} + static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const String &path) { PluginConfig plugin_config = {}; @@ -177,6 +192,7 @@ static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const } plugin_config.valid_config = is_plugin_config_valid(plugin_config); + plugin_config.last_updated = get_plugin_modification_time(plugin_config, path); } } diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index 557743fa73..d2528bebeb 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -114,12 +114,15 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS String signal_name = jstring_to_string(j_signal_name, env); int count = env->GetArrayLength(j_signal_params); - const Variant *args[count]; + ERR_FAIL_COND_MSG(count > VARIANT_ARG_MAX, "Maximum argument count exceeded!"); + + Variant variant_params[VARIANT_ARG_MAX]; + const Variant *args[VARIANT_ARG_MAX]; for (int i = 0; i < count; i++) { jobject j_param = env->GetObjectArrayElement(j_signal_params, i); - Variant variant = _jobject_to_variant(env, j_param); - args[i] = &variant; + variant_params[i] = _jobject_to_variant(env, j_param); + args[i] = &variant_params[i]; env->DeleteLocalRef(j_param); }; diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index b52bd4ce60..b8914414e6 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -70,14 +70,14 @@ Error AudioDriverJavaScript::init() { /* clang-format off */ _driver_id = EM_ASM_INT({ const MIX_RATE = $0; - const LATENCY = $1; + const LATENCY = $1 / 1000; return Module.IDHandler.add({ 'context': new (window.AudioContext || window.webkitAudioContext)({ sampleRate: MIX_RATE, latencyHint: LATENCY}), 'input': null, 'stream': null, 'script': null }); - }); + }, mix_rate, latency); /* clang-format on */ int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode()); diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp index b95674efc3..0312efb377 100644 --- a/platform/javascript/display_server_javascript.cpp +++ b/platform/javascript/display_server_javascript.cpp @@ -143,8 +143,8 @@ static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscrip ev.instance(); ev->set_echo(emscripten_event->repeat); dom2godot_mod(emscripten_event, ev); - ev->set_keycode(dom2godot_keycode(emscripten_event->keyCode)); - ev->set_physical_keycode(dom2godot_keycode(emscripten_event->keyCode)); + ev->set_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, false)); + ev->set_physical_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, true)); String unicode = String::utf8(emscripten_event->key); // Check if empty or multi-character (e.g. `CapsLock`). diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc index 882e943471..e3f2ce42b4 100644 --- a/platform/javascript/dom_keys.inc +++ b/platform/javascript/dom_keys.inc @@ -30,400 +30,203 @@ #include "core/os/keyboard.h" -// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value -#define DOM_VK_CANCEL 0x03 -#define DOM_VK_HELP 0x06 -#define DOM_VK_BACK_SPACE 0x08 -#define DOM_VK_TAB 0x09 -#define DOM_VK_CLEAR 0x0C -#define DOM_VK_RETURN 0x0D -#define DOM_VK_ENTER 0x0E // "Reserved, but not used." -#define DOM_VK_SHIFT 0x10 -#define DOM_VK_CONTROL 0x11 -#define DOM_VK_ALT 0x12 -#define DOM_VK_PAUSE 0x13 -#define DOM_VK_CAPS_LOCK 0x14 -#define DOM_VK_KANA 0x15 -#define DOM_VK_HANGUL 0x15 -#define DOM_VK_EISU 0x16 -#define DOM_VK_JUNJA 0x17 -#define DOM_VK_FINAL 0x18 -#define DOM_VK_HANJA 0x19 -#define DOM_VK_KANJI 0x19 -#define DOM_VK_ESCAPE 0x1B -#define DOM_VK_CONVERT 0x1C -#define DOM_VK_NONCONVERT 0x1D -#define DOM_VK_ACCEPT 0x1E -#define DOM_VK_MODECHANGE 0x1F -#define DOM_VK_SPACE 0x20 -#define DOM_VK_PAGE_UP 0x21 -#define DOM_VK_PAGE_DOWN 0x22 -#define DOM_VK_END 0x23 -#define DOM_VK_HOME 0x24 -#define DOM_VK_LEFT 0x25 -#define DOM_VK_UP 0x26 -#define DOM_VK_RIGHT 0x27 -#define DOM_VK_DOWN 0x28 -#define DOM_VK_SELECT 0x29 -#define DOM_VK_PRINT 0x2A -#define DOM_VK_EXECUTE 0x2B -#define DOM_VK_PRINTSCREEN 0x2C -#define DOM_VK_INSERT 0x2D -#define DOM_VK_DELETE 0x2E -#define DOM_VK_0 0x30 -#define DOM_VK_1 0x31 -#define DOM_VK_2 0x32 -#define DOM_VK_3 0x33 -#define DOM_VK_4 0x34 -#define DOM_VK_5 0x35 -#define DOM_VK_6 0x36 -#define DOM_VK_7 0x37 -#define DOM_VK_8 0x38 -#define DOM_VK_9 0x39 -#define DOM_VK_COLON 0x3A -#define DOM_VK_SEMICOLON 0x3B -#define DOM_VK_LESS_THAN 0x3C -#define DOM_VK_EQUALS 0x3D -#define DOM_VK_GREATER_THAN 0x3E -#define DOM_VK_QUESTION_MARK 0x3F -#define DOM_VK_AT 0x40 -#define DOM_VK_A 0x41 -#define DOM_VK_B 0x42 -#define DOM_VK_C 0x43 -#define DOM_VK_D 0x44 -#define DOM_VK_E 0x45 -#define DOM_VK_F 0x46 -#define DOM_VK_G 0x47 -#define DOM_VK_H 0x48 -#define DOM_VK_I 0x49 -#define DOM_VK_J 0x4A -#define DOM_VK_K 0x4B -#define DOM_VK_L 0x4C -#define DOM_VK_M 0x4D -#define DOM_VK_N 0x4E -#define DOM_VK_O 0x4F -#define DOM_VK_P 0x50 -#define DOM_VK_Q 0x51 -#define DOM_VK_R 0x52 -#define DOM_VK_S 0x53 -#define DOM_VK_T 0x54 -#define DOM_VK_U 0x55 -#define DOM_VK_V 0x56 -#define DOM_VK_W 0x57 -#define DOM_VK_X 0x58 -#define DOM_VK_Y 0x59 -#define DOM_VK_Z 0x5A -#define DOM_VK_WIN 0x5B -#define DOM_VK_CONTEXT_MENU 0x5D -#define DOM_VK_SLEEP 0x5F -#define DOM_VK_NUMPAD0 0x60 -#define DOM_VK_NUMPAD1 0x61 -#define DOM_VK_NUMPAD2 0x62 -#define DOM_VK_NUMPAD3 0x63 -#define DOM_VK_NUMPAD4 0x64 -#define DOM_VK_NUMPAD5 0x65 -#define DOM_VK_NUMPAD6 0x66 -#define DOM_VK_NUMPAD7 0x67 -#define DOM_VK_NUMPAD8 0x68 -#define DOM_VK_NUMPAD9 0x69 -#define DOM_VK_MULTIPLY 0x6A -#define DOM_VK_ADD 0x6B -#define DOM_VK_SEPARATOR 0x6C -#define DOM_VK_SUBTRACT 0x6D -#define DOM_VK_DECIMAL 0x6E -#define DOM_VK_DIVIDE 0x6F -#define DOM_VK_F1 0x70 -#define DOM_VK_F2 0x71 -#define DOM_VK_F3 0x72 -#define DOM_VK_F4 0x73 -#define DOM_VK_F5 0x74 -#define DOM_VK_F6 0x75 -#define DOM_VK_F7 0x76 -#define DOM_VK_F8 0x77 -#define DOM_VK_F9 0x78 -#define DOM_VK_F10 0x79 -#define DOM_VK_F11 0x7A -#define DOM_VK_F12 0x7B -#define DOM_VK_F13 0x7C -#define DOM_VK_F14 0x7D -#define DOM_VK_F15 0x7E -#define DOM_VK_F16 0x7F -#define DOM_VK_F17 0x80 -#define DOM_VK_F18 0x81 -#define DOM_VK_F19 0x82 -#define DOM_VK_F20 0x83 -#define DOM_VK_F21 0x84 -#define DOM_VK_F22 0x85 -#define DOM_VK_F23 0x86 -#define DOM_VK_F24 0x87 -#define DOM_VK_NUM_LOCK 0x90 -#define DOM_VK_SCROLL_LOCK 0x91 -#define DOM_VK_WIN_OEM_FJ_JISHO 0x92 -#define DOM_VK_WIN_OEM_FJ_MASSHOU 0x93 -#define DOM_VK_WIN_OEM_FJ_TOUROKU 0x94 -#define DOM_VK_WIN_OEM_FJ_LOYA 0x95 -#define DOM_VK_WIN_OEM_FJ_ROYA 0x96 -#define DOM_VK_CIRCUMFLEX 0xA0 -#define DOM_VK_EXCLAMATION 0xA1 -#define DOM_VK_DOUBLE_QUOTE 0xA2 -#define DOM_VK_HASH 0xA3 -#define DOM_VK_DOLLAR 0xA4 -#define DOM_VK_PERCENT 0xA5 -#define DOM_VK_AMPERSAND 0xA6 -#define DOM_VK_UNDERSCORE 0xA7 -#define DOM_VK_OPEN_PAREN 0xA8 -#define DOM_VK_CLOSE_PAREN 0xA9 -#define DOM_VK_ASTERISK 0xAA -#define DOM_VK_PLUS 0xAB -#define DOM_VK_PIPE 0xAC -#define DOM_VK_HYPHEN_MINUS 0xAD -#define DOM_VK_OPEN_CURLY_BRACKET 0xAE -#define DOM_VK_CLOSE_CURLY_BRACKET 0xAF -#define DOM_VK_TILDE 0xB0 -#define DOM_VK_VOLUME_MUTE 0xB5 -#define DOM_VK_VOLUME_DOWN 0xB6 -#define DOM_VK_VOLUME_UP 0xB7 -#define DOM_VK_COMMA 0xBC -#define DOM_VK_PERIOD 0xBE -#define DOM_VK_SLASH 0xBF -#define DOM_VK_BACK_QUOTE 0xC0 -#define DOM_VK_OPEN_BRACKET 0xDB -#define DOM_VK_BACK_SLASH 0xDC -#define DOM_VK_CLOSE_BRACKET 0xDD -#define DOM_VK_QUOTE 0xDE -#define DOM_VK_META 0xE0 -#define DOM_VK_ALTGR 0xE1 -#define DOM_VK_WIN_ICO_HELP 0xE3 -#define DOM_VK_WIN_ICO_00 0xE4 -#define DOM_VK_WIN_ICO_CLEAR 0xE6 -#define DOM_VK_WIN_OEM_RESET 0xE9 -#define DOM_VK_WIN_OEM_JUMP 0xEA -#define DOM_VK_WIN_OEM_PA1 0xEB -#define DOM_VK_WIN_OEM_PA2 0xEC -#define DOM_VK_WIN_OEM_PA3 0xED -#define DOM_VK_WIN_OEM_WSCTRL 0xEE -#define DOM_VK_WIN_OEM_CUSEL 0xEF -#define DOM_VK_WIN_OEM_ATTN 0xF0 -#define DOM_VK_WIN_OEM_FINISH 0xF1 -#define DOM_VK_WIN_OEM_COPY 0xF2 -#define DOM_VK_WIN_OEM_AUTO 0xF3 -#define DOM_VK_WIN_OEM_ENLW 0xF4 -#define DOM_VK_WIN_OEM_BACKTAB 0xF5 -#define DOM_VK_ATTN 0xF6 -#define DOM_VK_CRSEL 0xF7 -#define DOM_VK_EXSEL 0xF8 -#define DOM_VK_EREOF 0xF9 -#define DOM_VK_PLAY 0xFA -#define DOM_VK_ZOOM 0xFB -#define DOM_VK_PA1 0xFD -#define DOM_VK_WIN_OEM_CLEAR 0xFE - -int dom2godot_keycode(int dom_keycode) { - if (DOM_VK_0 <= dom_keycode && dom_keycode <= DOM_VK_Z) { - // ASCII intersection - return dom_keycode; - } - - if (DOM_VK_NUMPAD0 <= dom_keycode && dom_keycode <= DOM_VK_NUMPAD9) { - // Numpad numbers - return KEY_KP_0 + (dom_keycode - DOM_VK_NUMPAD0); +// See https://w3c.github.io/uievents-code/#code-value-tables +int dom_code2godot_scancode(EM_UTF8 const p_code[32], EM_UTF8 const p_key[32], bool p_physical) { +#define DOM2GODOT(p_str, p_godot_code) \ + if (memcmp((const void *)p_str, (void *)p_code, strlen(p_str) + 1) == 0) { \ + return KEY_##p_godot_code; \ } - if (DOM_VK_F1 <= dom_keycode && dom_keycode <= DOM_VK_F16) { - // F1-F16 - return KEY_F1 + (dom_keycode - DOM_VK_F1); + // Numpad section. + DOM2GODOT("NumLock", NUMLOCK); + DOM2GODOT("Numpad0", KP_0); + DOM2GODOT("Numpad1", KP_1); + DOM2GODOT("Numpad2", KP_2); + DOM2GODOT("Numpad3", KP_3); + DOM2GODOT("Numpad4", KP_4); + DOM2GODOT("Numpad5", KP_5); + DOM2GODOT("Numpad6", KP_6); + DOM2GODOT("Numpad7", KP_7); + DOM2GODOT("Numpad8", KP_8); + DOM2GODOT("Numpad9", KP_9); + DOM2GODOT("NumpadAdd", KP_ADD); + DOM2GODOT("NumpadBackspace", BACKSPACE); + DOM2GODOT("NumpadClear", CLEAR); + DOM2GODOT("NumpadClearEntry", CLEAR); + //DOM2GODOT("NumpadComma", UNKNOWN); + DOM2GODOT("NumpadDecimal", KP_PERIOD); + DOM2GODOT("NumpadDivide", KP_DIVIDE); + DOM2GODOT("NumpadEnter", KP_ENTER); + DOM2GODOT("NumpadEqual", EQUAL); + //DOM2GODOT("NumpadHash", UNKNOWN); + //DOM2GODOT("NumpadMemoryAdd", UNKNOWN); + //DOM2GODOT("NumpadMemoryClear", UNKNOWN); + //DOM2GODOT("NumpadMemoryRecall", UNKNOWN); + //DOM2GODOT("NumpadMemoryStore", UNKNOWN); + //DOM2GODOT("NumpadMemorySubtract", UNKNOWN); + DOM2GODOT("NumpadMultiply", KP_MULTIPLY); + DOM2GODOT("NumpadParenLeft", PARENLEFT); + DOM2GODOT("NumpadParenRight", PARENRIGHT); + DOM2GODOT("NumpadStar", KP_MULTIPLY); // or ASTERISK ? + DOM2GODOT("NumpadSubtract", KP_SUBTRACT); + + // Printable ASCII. + if (!p_physical) { + uint8_t b0 = (uint8_t)p_key[0]; + uint8_t b1 = (uint8_t)p_key[1]; + uint8_t b2 = (uint8_t)p_key[2]; + if (b1 == 0 && b0 > 0x1F && b0 < 0x7F) { // ASCII. + if (b0 > 0x60 && b0 < 0x7B) { // Lowercase ASCII. + b0 -= 32; + } + return b0; + } + +#define _U_2BYTES_MASK 0xE0 +#define _U_2BYTES 0xC0 + // Latin-1 codes. + if (b2 == 0 && (b0 & _U_2BYTES_MASK) == _U_2BYTES) { // 2-bytes utf8, only known latin. + uint32_t key = ((b0 & ~_U_2BYTES_MASK) << 6) | (b1 & 0x3F); + if (key >= 0xA0 && key <= 0xDF) { + return key; + } + if (key >= 0xE0 && key <= 0xFF) { // Lowercase known latin. + key -= 0x20; + return key; + } + } +#undef _U_2BYTES_MASK +#undef _U_2BYTES } - switch (dom_keycode) { - //case DOM_VK_CANCEL: return KEY_UNKNOWN; - case DOM_VK_HELP: - return KEY_HELP; - case DOM_VK_BACK_SPACE: - return KEY_BACKSPACE; - case DOM_VK_TAB: - return KEY_TAB; - - case DOM_VK_CLEAR: - case DOM_VK_WIN_OEM_CLEAR: // OEM duplicate - return KEY_CLEAR; - - case DOM_VK_RETURN: - case DOM_VK_ENTER: // unused according to MDN - return KEY_ENTER; - - case DOM_VK_SHIFT: - return KEY_SHIFT; - case DOM_VK_CONTROL: - return KEY_CONTROL; - - case DOM_VK_ALT: - case DOM_VK_ALTGR: - return KEY_ALT; - - case DOM_VK_PAUSE: - return KEY_PAUSE; - case DOM_VK_CAPS_LOCK: - return KEY_CAPSLOCK; - - /* - case DOM_VK_KANA: return KEY_UNKNOWN; - case DOM_VK_HANGUL: return KEY_UNKNOWN; - case DOM_VK_EISU: return KEY_UNKNOWN; - case DOM_VK_JUNJA: return KEY_UNKNOWN; - case DOM_VK_FINAL: return KEY_UNKNOWN; - case DOM_VK_HANJA: return KEY_UNKNOWN; - case DOM_VK_KANJI: return KEY_UNKNOWN; - */ - - case DOM_VK_ESCAPE: - return KEY_ESCAPE; - /* - case DOM_VK_CONVERT: return KEY_UNKNOWN; - case DOM_VK_NONCONVERT: return KEY_UNKNOWN; - case DOM_VK_ACCEPT: return KEY_UNKNOWN; - case DOM_VK_MODECHANGE: return KEY_UNKNOWN; - */ - - case DOM_VK_SPACE: - return KEY_SPACE; - case DOM_VK_PAGE_UP: - return KEY_PAGEUP; - case DOM_VK_PAGE_DOWN: - return KEY_PAGEDOWN; - case DOM_VK_END: - return KEY_END; - case DOM_VK_HOME: - return KEY_HOME; - case DOM_VK_LEFT: - return KEY_LEFT; - case DOM_VK_UP: - return KEY_UP; - case DOM_VK_RIGHT: - return KEY_RIGHT; - case DOM_VK_DOWN: - return KEY_DOWN; - - //case DOM_VK_SELECT: return KEY_UNKNOWN; - - case DOM_VK_PRINTSCREEN: - case DOM_VK_PRINT: - return KEY_PRINT; - - //case DOM_VK_EXECUTE: return KEY_UNKNOWN; - case DOM_VK_INSERT: - return KEY_INSERT; - case DOM_VK_DELETE: - return KEY_DELETE; - - case DOM_VK_META: - case DOM_VK_WIN: - return KEY_META; - - case DOM_VK_CONTEXT_MENU: - return KEY_MENU; - case DOM_VK_SLEEP: - return KEY_STANDBY; - - // Numpad keys - case DOM_VK_MULTIPLY: - return KEY_KP_MULTIPLY; - case DOM_VK_ADD: - return KEY_KP_ADD; - case DOM_VK_SEPARATOR: - return KEY_KP_PERIOD; // Good enough? - case DOM_VK_SUBTRACT: - return KEY_KP_SUBTRACT; - case DOM_VK_DECIMAL: - return KEY_KP_PERIOD; - case DOM_VK_DIVIDE: - return KEY_KP_DIVIDE; - - /* - case DOM_VK_F17: return KEY_UNKNOWN; - case DOM_VK_F18: return KEY_UNKNOWN; - case DOM_VK_F19: return KEY_UNKNOWN; - case DOM_VK_F20: return KEY_UNKNOWN; - case DOM_VK_F21: return KEY_UNKNOWN; - case DOM_VK_F22: return KEY_UNKNOWN; - case DOM_VK_F23: return KEY_UNKNOWN; - case DOM_VK_F24: return KEY_UNKNOWN; - */ - - case DOM_VK_NUM_LOCK: - return KEY_NUMLOCK; - case DOM_VK_SCROLL_LOCK: - return KEY_SCROLLLOCK; - - /* - case DOM_VK_WIN_OEM_FJ_JISHO: return KEY_UNKNOWN; - case DOM_VK_WIN_OEM_FJ_MASSHOU: return KEY_UNKNOWN; - case DOM_VK_WIN_OEM_FJ_TOUROKU: return KEY_UNKNOWN; - case DOM_VK_WIN_OEM_FJ_LOYA: return KEY_UNKNOWN; - case DOM_VK_WIN_OEM_FJ_ROYA: return KEY_UNKNOWN; - */ - - case DOM_VK_CIRCUMFLEX: - return KEY_ASCIICIRCUM; - case DOM_VK_EXCLAMATION: - return KEY_EXCLAM; - case DOM_VK_DOUBLE_QUOTE: - return KEY_QUOTEDBL; - case DOM_VK_HASH: - return KEY_NUMBERSIGN; - case DOM_VK_DOLLAR: - return KEY_DOLLAR; - case DOM_VK_PERCENT: - return KEY_PERCENT; - case DOM_VK_AMPERSAND: - return KEY_AMPERSAND; - case DOM_VK_UNDERSCORE: - return KEY_UNDERSCORE; - case DOM_VK_OPEN_PAREN: - return KEY_PARENLEFT; - case DOM_VK_CLOSE_PAREN: - return KEY_PARENRIGHT; - case DOM_VK_ASTERISK: - return KEY_ASTERISK; - case DOM_VK_PLUS: - return KEY_PLUS; - case DOM_VK_PIPE: - return KEY_BAR; - case DOM_VK_HYPHEN_MINUS: - return KEY_MINUS; - case DOM_VK_OPEN_CURLY_BRACKET: - return KEY_BRACELEFT; - case DOM_VK_CLOSE_CURLY_BRACKET: - return KEY_BRACERIGHT; - case DOM_VK_TILDE: - return KEY_ASCIITILDE; - - case DOM_VK_VOLUME_MUTE: - return KEY_VOLUMEMUTE; - case DOM_VK_VOLUME_DOWN: - return KEY_VOLUMEDOWN; - case DOM_VK_VOLUME_UP: - return KEY_VOLUMEUP; - - case DOM_VK_COMMA: - return KEY_COMMA; - case DOM_VK_PERIOD: - return KEY_PERIOD; - case DOM_VK_SLASH: - return KEY_SLASH; - case DOM_VK_BACK_QUOTE: - return KEY_QUOTELEFT; - case DOM_VK_OPEN_BRACKET: - return KEY_BRACKETLEFT; - case DOM_VK_BACK_SLASH: - return KEY_BACKSLASH; - case DOM_VK_CLOSE_BRACKET: - return KEY_BRACKETRIGHT; - case DOM_VK_QUOTE: - return KEY_APOSTROPHE; - - // The rest is OEM/unusual. - - default: - return KEY_UNKNOWN; - }; + // Alphanumeric section. + DOM2GODOT("Backquote", QUOTELEFT); + DOM2GODOT("Backslash", BACKSLASH); + DOM2GODOT("BracketLeft", BRACKETLEFT); + DOM2GODOT("BracketRight", BRACKETRIGHT); + DOM2GODOT("Comma", COMMA); + DOM2GODOT("Digit0", 0); + DOM2GODOT("Digit1", 1); + DOM2GODOT("Digit2", 2); + DOM2GODOT("Digit3", 3); + DOM2GODOT("Digit4", 4); + DOM2GODOT("Digit5", 5); + DOM2GODOT("Digit6", 6); + DOM2GODOT("Digit7", 7); + DOM2GODOT("Digit8", 8); + DOM2GODOT("Digit9", 9); + DOM2GODOT("Equal", EQUAL); + DOM2GODOT("IntlBackslash", BACKSLASH); + //DOM2GODOT("IntlRo", UNKNOWN); + DOM2GODOT("IntlYen", YEN); + + DOM2GODOT("KeyA", A); + DOM2GODOT("KeyB", B); + DOM2GODOT("KeyC", C); + DOM2GODOT("KeyD", D); + DOM2GODOT("KeyE", E); + DOM2GODOT("KeyF", F); + DOM2GODOT("KeyG", G); + DOM2GODOT("KeyH", H); + DOM2GODOT("KeyI", I); + DOM2GODOT("KeyJ", J); + DOM2GODOT("KeyK", K); + DOM2GODOT("KeyL", L); + DOM2GODOT("KeyM", M); + DOM2GODOT("KeyN", N); + DOM2GODOT("KeyO", O); + DOM2GODOT("KeyP", P); + DOM2GODOT("KeyQ", Q); + DOM2GODOT("KeyR", R); + DOM2GODOT("KeyS", S); + DOM2GODOT("KeyT", T); + DOM2GODOT("KeyU", U); + DOM2GODOT("KeyV", V); + DOM2GODOT("KeyW", W); + DOM2GODOT("KeyX", X); + DOM2GODOT("KeyY", Y); + DOM2GODOT("KeyZ", Z); + + DOM2GODOT("Minus", MINUS); + DOM2GODOT("Period", PERIOD); + DOM2GODOT("Quote", APOSTROPHE); + DOM2GODOT("Semicolon", SEMICOLON); + DOM2GODOT("Slash", SLASH); + + // Functional keys in the Alphanumeric section. + DOM2GODOT("AltLeft", ALT); + DOM2GODOT("AltRight", ALT); + DOM2GODOT("Backspace", BACKSPACE); + DOM2GODOT("CapsLock", CAPSLOCK); + DOM2GODOT("ContextMenu", MENU); + DOM2GODOT("ControlLeft", CONTROL); + DOM2GODOT("ControlRight", CONTROL); + DOM2GODOT("Enter", ENTER); + DOM2GODOT("MetaLeft", SUPER_L); + DOM2GODOT("MetaRight", SUPER_R); + DOM2GODOT("ShiftLeft", SHIFT); + DOM2GODOT("ShiftRight", SHIFT); + DOM2GODOT("Space", SPACE); + DOM2GODOT("Tab", TAB); + + // ControlPad section. + DOM2GODOT("Delete", DELETE); + DOM2GODOT("End", END); + DOM2GODOT("Help", HELP); + DOM2GODOT("Home", HOME); + DOM2GODOT("Insert", INSERT); + DOM2GODOT("PageDown", PAGEDOWN); + DOM2GODOT("PageUp", PAGEUP); + + // ArrowPad section. + DOM2GODOT("ArrowDown", DOWN); + DOM2GODOT("ArrowLeft", LEFT); + DOM2GODOT("ArrowRight", RIGHT); + DOM2GODOT("ArrowUp", UP); + + // Function section. + DOM2GODOT("Escape", ESCAPE); + DOM2GODOT("F1", F1); + DOM2GODOT("F2", F2); + DOM2GODOT("F3", F3); + DOM2GODOT("F4", F4); + DOM2GODOT("F5", F5); + DOM2GODOT("F6", F6); + DOM2GODOT("F7", F7); + DOM2GODOT("F8", F8); + DOM2GODOT("F9", F9); + DOM2GODOT("F10", F10); + DOM2GODOT("F11", F11); + DOM2GODOT("F12", F12); + //DOM2GODOT("Fn", UNKNOWN); // never actually fired, but included in the standard draft. + //DOM2GODOT("FnLock", UNKNOWN); + DOM2GODOT("PrintScreen", PRINT); + DOM2GODOT("ScrollLock", SCROLLLOCK); + DOM2GODOT("Pause", PAUSE); + + // Media keys section. + DOM2GODOT("BrowserBack", BACK); + DOM2GODOT("BrowserFavorites", FAVORITES); + DOM2GODOT("BrowserForward", FORWARD); + DOM2GODOT("BrowserHome", OPENURL); + DOM2GODOT("BrowserRefresh", REFRESH); + DOM2GODOT("BrowserSearch", SEARCH); + DOM2GODOT("BrowserStop", STOP); + //DOM2GODOT("Eject", UNKNOWN); + DOM2GODOT("LaunchApp1", LAUNCH0); + DOM2GODOT("LaunchApp2", LAUNCH1); + DOM2GODOT("LaunchMail", LAUNCHMAIL); + DOM2GODOT("MediaPlayPause", MEDIAPLAY); + DOM2GODOT("MediaSelect", LAUNCHMEDIA); + DOM2GODOT("MediaStop", MEDIASTOP); + DOM2GODOT("MediaTrackNext", MEDIANEXT); + DOM2GODOT("MediaTrackPrevious", MEDIAPREVIOUS); + //DOM2GODOT("Power", UNKNOWN); + //DOM2GODOT("Sleep", UNKNOWN); + DOM2GODOT("AudioVolumeDown", VOLUMEDOWN); + DOM2GODOT("AudioVolumeMute", VOLUMEMUTE); + DOM2GODOT("AudioVolumeUp", VOLUMEUP); + //DOM2GODOT("WakeUp", UNKNOWN); + return KEY_UNKNOWN; +#undef DOM2GODOT } diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp index d342d30097..cfc371710b 100644 --- a/platform/osx/joypad_osx.cpp +++ b/platform/osx/joypad_osx.cpp @@ -311,9 +311,16 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) { if (refCF) { CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id); } + + int version = 0; + refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVersionNumberKey)); + if (refCF) { + CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &version); + } + if (vendor && product_id) { char uid[128]; - sprintf(uid, "%04x%08x%04x%08x", OSSwapHostToBigInt32(vendor), 0, OSSwapHostToBigInt32(product_id), 0); + sprintf(uid, "%08x%08x%08x%08x", OSSwapHostToBigInt32(3), OSSwapHostToBigInt32(vendor), OSSwapHostToBigInt32(product_id), OSSwapHostToBigInt32(version)); input->joy_connection_changed(id, true, name, uid); } else { //bluetooth device diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index 0cff12ca8c..271a4e41bc 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -33,10 +33,6 @@ #include <oleauto.h> #include <wbemidl.h> -#ifndef __GNUC__ -#define __builtin_bswap32 _byteswap_ulong -#endif - #if defined(__GNUC__) // Workaround GCC warning from -Wcast-function-type. #define GetProcAddress (void *)GetProcAddress @@ -67,17 +63,25 @@ JoypadWindows::JoypadWindows(HWND *hwnd) { for (int i = 0; i < JOYPADS_MAX; i++) attached_joypads[i] = false; - HRESULT result; - result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr); - if (FAILED(result)) { - printf("failed init DINPUT: %ld\n", result); + HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr); + if (result == DI_OK) { + probe_joypads(); + } else { + ERR_PRINT("Couldn't initialize DirectInput. Error: " + itos(result)); + if (result == DIERR_OUTOFMEMORY) { + ERR_PRINT("The Windows DirectInput subsystem could not allocate sufficient memory."); + ERR_PRINT("Rebooting your PC may solve this issue."); + } + // Ensure dinput is still a nullptr. + dinput = nullptr; } - probe_joypads(); } JoypadWindows::~JoypadWindows() { close_joypad(); - dinput->Release(); + if (dinput) { + dinput->Release(); + } unload_xinput(); } @@ -135,6 +139,7 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) { } bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { + ERR_FAIL_NULL_V_MSG(dinput, false, "DirectInput not initialized. Rebooting your PC may solve this issue."); HRESULT hr; int num = input->get_unused_joy_id(); @@ -158,10 +163,13 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { const GUID &guid = instance->guidProduct; char uid[128]; - sprintf_s(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", - __builtin_bswap32(guid.Data1), guid.Data2, guid.Data3, - guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], - guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); + + ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognised."); + WORD type = BSWAP16(0x03); + WORD vendor = BSWAP16(LOWORD(guid.Data1)); + WORD product = BSWAP16(HIWORD(guid.Data1)); + WORD version = 0; + sprintf_s(uid, "%04x%04x%04x%04x%04x%04x%04x%04x", type, 0, vendor, 0, product, 0, version, 0); id_to_change = joypad_count; @@ -266,6 +274,7 @@ void JoypadWindows::close_joypad(int id) { } void JoypadWindows::probe_joypads() { + ERR_FAIL_NULL_MSG(dinput, "DirectInput not initialized. Rebooting your PC may solve this issue."); DWORD dwResult; for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) { ZeroMemory(&x_joypads[i].state, sizeof(XINPUT_STATE)); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index f11888b26c..0dab0c601a 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -795,6 +795,9 @@ String OS_Windows::get_current_tablet_driver() const { } void OS_Windows::set_current_tablet_driver(const String &p_driver) { + if (get_tablet_driver_count() == 0) { + return; + } bool found = false; for (int i = 0; i < get_tablet_driver_count(); i++) { if (p_driver == get_tablet_driver_name(i)) { diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 4919ef8304..d23398713a 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -32,6 +32,7 @@ #include "collision_object_2d.h" #include "core/engine.h" +#include "core/math/geometry_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -75,7 +76,7 @@ void CollisionPolygon2D::_build_polygon() { } Vector<Vector<Vector2>> CollisionPolygon2D::_decompose_in_convex() { - Vector<Vector<Vector2>> decomp = Geometry::decompose_polygon_in_convex(polygon); + Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(polygon); return decomp; } @@ -224,7 +225,7 @@ bool CollisionPolygon2D::_edit_use_rect() const { } bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - return Geometry::is_point_in_polygon(p_point, Variant(polygon)); + return Geometry2D::is_point_in_polygon(p_point, Variant(polygon)); } #endif diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index 0d126b949d..8df72d7aac 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -309,10 +309,10 @@ RID DampedSpringJoint2D::_configure_joint(PhysicsBody2D *body_a, PhysicsBody2D * RID dsj = PhysicsServer2D::get_singleton()->damped_spring_joint_create(anchor_A, anchor_B, body_a->get_rid(), body_b->get_rid()); if (rest_length) { - PhysicsServer2D::get_singleton()->damped_string_joint_set_param(dsj, PhysicsServer2D::DAMPED_STRING_REST_LENGTH, rest_length); + PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(dsj, PhysicsServer2D::DAMPED_SPRING_REST_LENGTH, rest_length); } - PhysicsServer2D::get_singleton()->damped_string_joint_set_param(dsj, PhysicsServer2D::DAMPED_STRING_STIFFNESS, stiffness); - PhysicsServer2D::get_singleton()->damped_string_joint_set_param(dsj, PhysicsServer2D::DAMPED_STRING_DAMPING, damping); + PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(dsj, PhysicsServer2D::DAMPED_SPRING_STIFFNESS, stiffness); + PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(dsj, PhysicsServer2D::DAMPED_SPRING_DAMPING, damping); return dsj; } @@ -330,7 +330,7 @@ void DampedSpringJoint2D::set_rest_length(real_t p_rest_length) { rest_length = p_rest_length; update(); if (get_joint().is_valid()) { - PhysicsServer2D::get_singleton()->damped_string_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_STRING_REST_LENGTH, p_rest_length ? p_rest_length : length); + PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_REST_LENGTH, p_rest_length ? p_rest_length : length); } } @@ -342,7 +342,7 @@ void DampedSpringJoint2D::set_stiffness(real_t p_stiffness) { stiffness = p_stiffness; update(); if (get_joint().is_valid()) { - PhysicsServer2D::get_singleton()->damped_string_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_STRING_STIFFNESS, p_stiffness); + PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_STIFFNESS, p_stiffness); } } @@ -354,7 +354,7 @@ void DampedSpringJoint2D::set_damping(real_t p_damping) { damping = p_damping; update(); if (get_joint().is_valid()) { - PhysicsServer2D::get_singleton()->damped_string_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_STRING_DAMPING, p_damping); + PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_DAMPING, p_damping); } } diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 0f4880f69a..023cfa6d03 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "light_occluder_2d.h" +#include "core/math/geometry_2d.h" #include "core/engine.h" @@ -68,12 +69,12 @@ Rect2 OccluderPolygon2D::_edit_get_rect() const { bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { if (closed) { - return Geometry::is_point_in_polygon(p_point, Variant(polygon)); + return Geometry2D::is_point_in_polygon(p_point, Variant(polygon)); } else { const real_t d = LINE_GRAB_WIDTH / 2 + p_tolerance; const Vector2 *points = polygon.ptr(); for (int i = 0; i < polygon.size() - 1; i++) { - Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]); + Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, &points[i]); if (p.distance_to(p_point) <= d) { return true; } diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 28183403f2..b120b115b0 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -31,6 +31,7 @@ #include "line_2d.h" #include "core/core_string_names.h" +#include "core/math/geometry_2d.h" #include "line_builder.h" // Needed so we can bind functions @@ -72,7 +73,7 @@ bool Line2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc const real_t d = _width / 2 + p_tolerance; const Vector2 *points = _points.ptr(); for (int i = 0; i < _points.size() - 1; i++) { - Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]); + Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, &points[i]); if (p.distance_to(p_point) <= d) { return true; } diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index cb2dbba0fe..e5cdade4a4 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -31,6 +31,7 @@ #include "navigation_agent_2d.h" #include "core/engine.h" +#include "core/math/geometry_2d.h" #include "scene/2d/navigation_2d.h" #include "servers/navigation_server_2d.h" @@ -304,7 +305,7 @@ void NavigationAgent2D::update_navigation() { Vector2 segment[2]; segment[0] = navigation_path[nav_path_index - 1]; segment[1] = navigation_path[nav_path_index]; - Vector2 p = Geometry::get_closest_point_to_segment_2d(o, segment); + Vector2 p = Geometry2D::get_closest_point_to_segment(o, segment); if (o.distance_to(p) >= path_max_distance) { // To faraway, reload path reload_path = true; diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 72bde17428..671bda558d 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -32,6 +32,7 @@ #include "core/core_string_names.h" #include "core/engine.h" +#include "core/math/geometry_2d.h" #include "core/os/mutex.h" #include "navigation_2d.h" #include "servers/navigation_server_2d.h" @@ -73,7 +74,7 @@ bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double if (outline_size < 3) { continue; } - if (Geometry::is_point_in_polygon(p_point, Variant(outline))) { + if (Geometry2D::is_point_in_polygon(p_point, Variant(outline))) { return true; } } @@ -269,7 +270,7 @@ void NavigationPolygon::make_polygons_from_outlines() { const Vector2 *r2 = ol2.ptr(); for (int l = 0; l < olsize2; l++) { - if (Geometry::segment_intersects_segment_2d(r[0], outside_point, r2[l], r2[(l + 1) % olsize2], nullptr)) { + if (Geometry2D::segment_intersects_segment(r[0], outside_point, r2[l], r2[(l + 1) % olsize2], nullptr)) { interscount++; } } diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 046e4dbd41..00e9af3bb7 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -31,6 +31,7 @@ #include "path_2d.h" #include "core/engine.h" +#include "core/math/geometry_2d.h" #include "scene/scene_string_names.h" #ifdef TOOLS_ENABLED @@ -73,7 +74,7 @@ bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc real_t frac = j / 8.0; s[1] = curve->interpolate(i, frac); - Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, s); + Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s); if (p.distance_to(p_point) <= p_tolerance) { return true; } diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 62c66dbb29..13b62816a4 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -30,7 +30,7 @@ #include "polygon_2d.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" #include "skeleton_2d.h" #ifdef TOOLS_ENABLED @@ -86,7 +86,7 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler if (internal_vertices > 0) { polygon2d.resize(polygon2d.size() - internal_vertices); } - return Geometry::is_point_in_polygon(p_point - get_offset(), polygon2d); + return Geometry2D::is_point_in_polygon(p_point - get_offset(), polygon2d); } #endif @@ -300,7 +300,7 @@ void Polygon2D::_notification(int p_what) { } if (invert || polygons.size() == 0) { - Vector<int> indices = Geometry::triangulate_polygon(points); + Vector<int> indices = Geometry2D::triangulate_polygon(points); if (indices.size()) { RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID(), -1, normal_map.is_valid() ? normal_map->get_rid() : RID(), specular_map.is_valid() ? specular_map->get_rid() : RID(), Color(specular_color.r, specular_color.g, specular_color.b, shininess)); } @@ -323,7 +323,7 @@ void Polygon2D::_notification(int p_what) { ERR_CONTINUE(idx < 0 || idx >= points.size()); tmp_points.write[j] = points[r[j]]; } - Vector<int> indices = Geometry::triangulate_polygon(tmp_points); + Vector<int> indices = Geometry2D::triangulate_polygon(tmp_points); int ic2 = indices.size(); const int *r2 = indices.ptr(); diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index e2f1b3807d..a41eaf9da0 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -559,7 +559,7 @@ void BakedLightmap::_plot_triangle_into_octree(GenProbesOctree *p_cell, float p_ subcell.position = Vector3(pos) * p_cell_size; subcell.size = Vector3(half_size, half_size, half_size) * p_cell_size; - if (!Geometry::triangle_box_overlap(subcell.position + subcell.size * 0.5, subcell.size * 0.5, p_triangle)) { + if (!Geometry3D::triangle_box_overlap(subcell.position + subcell.size * 0.5, subcell.size * 0.5, p_triangle)) { continue; } diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index bad4a1fddd..e2d11c740a 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -31,6 +31,7 @@ #include "collision_polygon_3d.h" #include "collision_object_3d.h" +#include "core/math/geometry_2d.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" @@ -45,7 +46,7 @@ void CollisionPolygon3D::_build_polygon() { return; } - Vector<Vector<Vector2>> decomp = Geometry::decompose_polygon_in_convex(polygon); + Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(polygon); if (decomp.size() == 0) { return; } diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 814e911372..ef24676d69 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -402,7 +402,7 @@ void DirectionalLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight3D::is_blend_splits_enabled); ADD_GROUP("Directional Shadow", "directional_shadow_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal,PSSM 2 Splits,PSSM 4 Splits"), "set_shadow_mode", "get_shadow_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 4 Splits (Slow)"), "set_shadow_mode", "get_shadow_mode"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_1", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_1_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_2", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_2_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_3", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_3_OFFSET); diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index f8d44506b6..e179261002 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -323,7 +323,7 @@ void NavigationAgent3D::update_navigation() { segment[1] = navigation_path[nav_path_index]; segment[0].y -= navigation_height_offset; segment[1].y -= navigation_height_offset; - Vector3 p = Geometry::get_closest_point_to_segment(o, segment); + Vector3 p = Geometry3D::get_closest_point_to_segment(o, segment); if (o.distance_to(p) >= path_max_distance) { // To faraway, reload path reload_path = true; diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 7516cf95b0..a09424fa17 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -36,6 +36,7 @@ #include "core/type_info.h" #include "scene/3d/physics_body_3d.h" #include "scene/resources/surface_tool.h" +#include "scene/scene_string_names.h" void SkinReference::_skin_changed() { if (skeleton_node) { @@ -157,12 +158,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < bones.size(); i++) { String prep = "bones/" + itos(i) + "/"; - p_list->push_back(PropertyInfo(Variant::STRING, prep + "name")); - p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest")); - p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_children")); + p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } } @@ -214,7 +215,7 @@ void Skeleton3D::_update_process_order() { } if (pass_count == len * len) { - ERR_PRINT("Skeleton parenthood graph is cyclic"); + ERR_PRINT("Skeleton3D parenthood graph is cyclic"); } process_order_dirty = false; @@ -223,7 +224,7 @@ void Skeleton3D::_update_process_order() { void Skeleton3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_UPDATE_SKELETON: { - RenderingServer *vs = RenderingServer::get_singleton(); + RenderingServer *rs = RenderingServer::get_singleton(); Bone *bonesptr = bones.ptrw(); int len = bones.size(); @@ -288,9 +289,9 @@ void Skeleton3D::_notification(int p_what) { for (List<ObjectID>::Element *E = b.nodes_bound.front(); E; E = E->next()) { Object *obj = ObjectDB::get_instance(E->get()); ERR_CONTINUE(!obj); - Node3D *sp = Object::cast_to<Node3D>(obj); - ERR_CONTINUE(!sp); - sp->set_transform(b.pose_global); + Node3D *node_3d = Object::cast_to<Node3D>(obj); + ERR_CONTINUE(!node_3d); + node_3d->set_transform(b.pose_global); } } @@ -323,7 +324,7 @@ void Skeleton3D::_notification(int p_what) { } if (!found) { - ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton has no bone by that name."); + ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton3D has no bone by that name."); E->get()->skin_bone_indices_ptrs[i] = 0; } } else if (skin->get_bind_bone(i) >= 0) { @@ -346,11 +347,16 @@ void Skeleton3D::_notification(int p_what) { for (uint32_t i = 0; i < bind_count; i++) { uint32_t bone_index = E->get()->skin_bone_indices_ptrs[i]; ERR_CONTINUE(bone_index >= (uint32_t)len); - vs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); + rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); } } dirty = false; + +#ifdef TOOLS_ENABLED + emit_signal(SceneStringNames::get_singleton()->pose_updated); +#endif // TOOLS_ENABLED + } break; #ifndef _3D_DISABLED @@ -603,6 +609,11 @@ int Skeleton3D::get_process_order(int p_idx) { return process_order[p_idx]; } +Vector<int> Skeleton3D::get_bone_process_orders() { + _update_process_order(); + return process_order; +} + void Skeleton3D::localize_rests() { _update_process_order(); @@ -843,6 +854,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { } void Skeleton3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_bone_process_orders"), &Skeleton3D::get_bone_process_orders); ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone); ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone); ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name); @@ -893,6 +905,10 @@ void Skeleton3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones"); #endif // _3D_DISABLED +#ifdef TOOLS_ENABLED + ADD_SIGNAL(MethodInfo("pose_updated")); +#endif // TOOLS_ENABLED + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 1e864c1c48..66706a9450 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -196,6 +196,7 @@ public: void localize_rests(); // used for loaders and tools int get_process_order(int p_idx); + Vector<int> get_bone_process_orders(); Ref<SkinReference> register_skin(const Ref<Skin> &p_skin); diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 9fc3feb49a..de5496ee35 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "voxelizer.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #include "core/os/os.h" #include "core/os/threaded_array_processor.h" @@ -124,7 +124,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector3 half = (to - from) * 0.5; //is in this cell? - if (!Geometry::triangle_box_overlap(from + half, half, p_vtx)) { + if (!Geometry3D::triangle_box_overlap(from + half, half, p_vtx)) { continue; //face does not span this cell } @@ -267,7 +267,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co //test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time Vector3 qsize = test_aabb.size * 0.5; //quarter size, for fast aabb test - if (!Geometry::triangle_box_overlap(test_aabb.position + qsize, qsize, p_vtx)) { + if (!Geometry3D::triangle_box_overlap(test_aabb.position + qsize, qsize, p_vtx)) { //if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) { //does not fit in child, go on continue; @@ -439,7 +439,7 @@ void Voxelizer::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vec } //test against original bounds - if (!Geometry::triangle_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs)) { + if (!Geometry3D::triangle_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs)) { continue; } //plot @@ -471,7 +471,7 @@ void Voxelizer::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vec } //test against original bounds - if (!Geometry::triangle_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs)) { + if (!Geometry3D::triangle_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs)) { continue; } //plot face diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 003a4fad90..5a42e2af7a 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -30,7 +30,7 @@ #include "animation_blend_space_2d.h" -#include "core/math/delaunay_2d.h" +#include "core/math/geometry_2d.h" void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const { r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position)); @@ -366,7 +366,7 @@ Vector2 AnimationNodeBlendSpace2D::get_closest_point(const Vector2 &p_point) { points[j] = get_blend_point_position(get_triangle_point(i, j)); } - if (Geometry::is_point_in_triangle(p_point, points[0], points[1], points[2])) { + if (Geometry2D::is_point_in_triangle(p_point, points[0], points[1], points[2])) { return p_point; } @@ -375,7 +375,7 @@ Vector2 AnimationNodeBlendSpace2D::get_closest_point(const Vector2 &p_point) { points[j], points[(j + 1) % 3] }; - Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, s); + Vector2 closest = Geometry2D::get_closest_point_to_segment(p_point, s); if (first || closest.distance_to(p_point) < best_point.distance_to(p_point)) { best_point = closest; first = false; @@ -455,7 +455,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { points[j] = get_blend_point_position(get_triangle_point(i, j)); } - if (Geometry::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) { + if (Geometry2D::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) { blend_triangle = i; _blend_triangle(blend_pos, points, blend_weights); break; @@ -466,7 +466,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { points[j], points[(j + 1) % 3] }; - Vector2 closest2 = Geometry::get_closest_point_to_segment_2d(blend_pos, s); + Vector2 closest2 = Geometry2D::get_closest_point_to_segment(blend_pos, s); if (first || closest2.distance_to(blend_pos) < best_point.distance_to(blend_pos)) { best_point = closest2; blend_triangle = i; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 4e56f1acf0..319d0171b3 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -396,7 +396,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float Animation::UpdateMode update_mode = a->value_track_get_update_mode(i); if (update_mode == Animation::UPDATE_CAPTURE) { - if (p_started) { + if (p_started || pa->capture == Variant()) { pa->capture = pa->object->get_indexed(pa->subpath); } diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 0222585948..f57c8e58db 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -361,15 +361,6 @@ void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) { RES res = var; - if (var.get_type() == Variant::OBJECT && var.is_ref()) { - REF r = var; - if (r.is_valid()) { - res = *r; - } else { - res = RES(); - } - } - Array prop; prop.push_back(pi.name); prop.push_back(pi.type); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 88710289c7..84170a65d1 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -66,7 +66,7 @@ void ColorPicker::_notification(int p_what) { } break; case NOTIFICATION_PARENTED: { for (int i = 0; i < 4; i++) { - set_margin((Margin)i, get_theme_constant("margin")); + set_margin((Margin)i, get_margin((Margin)i) + get_theme_constant("margin")); } } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 96aaec6ae9..97daeceda9 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -30,6 +30,7 @@ #include "control.h" +#include "core/math/geometry_2d.h" #include "core/message_queue.h" #include "core/os/keyboard.h" #include "core/os/os.h" @@ -2293,8 +2294,8 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con Vector2 fb = points[(j + 1) % 4]; Vector2 pa, pb; - float d = Geometry::get_closest_points_between_segments(la, lb, fa, fb, pa, pb); - //float d = Geometry::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0)); + float d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb); + //float d = Geometry2D::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0)); if (d < r_closest_dist) { r_closest_dist = d; *r_closest = c; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index a57408b83b..2f5af0eda0 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -256,6 +256,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & lh = line < l.height_caches.size() ? l.height_caches[line] : 1; \ line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; \ line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; \ + if (p_mode == PROCESS_DRAW) { \ + if (line < l.offset_caches.size()) { \ + wofs = l.offset_caches[line]; \ + } \ + } \ } \ if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x < p_ofs.x + wofs) { \ if (r_outside) \ @@ -646,7 +651,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & } if (p_mode == PROCESS_DRAW && visible) { - img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size)); + img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size), false, img->color); } p_char_count++; @@ -873,7 +878,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & } void RichTextLabel::_scroll_changed(double) { - if (updating_scroll || !scroll_active) { + if (updating_scroll) { return; } @@ -1443,6 +1448,46 @@ void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack } } +Color RichTextLabel::_get_color_from_string(const String &p_color_str, const Color &p_default_color) { + if (p_color_str.begins_with("#")) { + return Color::html(p_color_str); + } else if (p_color_str == "aqua") { + return Color(0, 1, 1); + } else if (p_color_str == "black") { + return Color(0, 0, 0); + } else if (p_color_str == "blue") { + return Color(0, 0, 1); + } else if (p_color_str == "fuchsia") { + return Color(1, 0, 1); + } else if (p_color_str == "gray" || p_color_str == "grey") { + return Color(0.5, 0.5, 0.5); + } else if (p_color_str == "green") { + return Color(0, 0.5, 0); + } else if (p_color_str == "lime") { + return Color(0, 1, 0); + } else if (p_color_str == "maroon") { + return Color(0.5, 0, 0); + } else if (p_color_str == "navy") { + return Color(0, 0, 0.5); + } else if (p_color_str == "olive") { + return Color(0.5, 0.5, 0); + } else if (p_color_str == "purple") { + return Color(0.5, 0, 0.5); + } else if (p_color_str == "red") { + return Color(1, 0, 0); + } else if (p_color_str == "silver") { + return Color(0.75, 0.75, 0.75); + } else if (p_color_str == "teal") { + return Color(0, 0.5, 0.5); + } else if (p_color_str == "white") { + return Color(1, 1, 1); + } else if (p_color_str == "yellow") { + return Color(1, 1, 0); + } else { + return p_default_color; + } +} + bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { Item *item = p_item; @@ -1636,7 +1681,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub } } -void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height) { +void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color) { if (current->type == ITEM_TABLE) { return; } @@ -1645,6 +1690,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, ItemImage *item = memnew(ItemImage); item->image = p_image; + item->color = p_color; if (p_width > 0) { // custom width @@ -1967,6 +2013,7 @@ void RichTextLabel::set_scroll_active(bool p_active) { } scroll_active = p_active; + vscroll->set_drag_node_enabled(p_active); update(); } @@ -2034,7 +2081,32 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1); Vector<String> split_tag_block = tag.split(" ", false); - String bbcode = !split_tag_block.empty() ? split_tag_block[0] : ""; + + // Find optional parameters. + String bbcode_name; + typedef Map<String, String> OptionMap; + OptionMap bbcode_options; + if (!split_tag_block.empty()) { + bbcode_name = split_tag_block[0]; + for (int i = 1; i < split_tag_block.size(); i++) { + const String &expr = split_tag_block[i]; + int value_pos = expr.find("="); + if (value_pos > -1) { + bbcode_options[expr.substr(0, value_pos)] = expr.substr(value_pos + 1); + } + } + } else { + bbcode_name = tag; + } + + // Find main parameter. + String bbcode_value; + int main_value_pos = bbcode_name.find("="); + if (main_value_pos > -1) { + bbcode_value = bbcode_name.substr(main_value_pos + 1); + bbcode_name = bbcode_name.substr(0, main_value_pos); + } + if (tag.begins_with("/") && tag_stack.size()) { bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length()); @@ -2160,7 +2232,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { push_meta(url); pos = brk_end + 1; tag_stack.push_front("url"); - } else if (tag == "img") { + } else if (bbcode_name == "img") { int end = p_bbcode.find("[", brk_end); if (end == -1) { end = p_bbcode.length(); @@ -2170,80 +2242,42 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { Ref<Texture2D> texture = ResourceLoader::load(image, "Texture2D"); if (texture.is_valid()) { - add_image(texture); - } - - pos = end; - tag_stack.push_front(tag); - } else if (tag.begins_with("img=")) { - int width = 0; - int height = 0; - - String params = tag.substr(4, tag.length()); - int sep = params.find("x"); - if (sep == -1) { - width = params.to_int(); - } else { - width = params.substr(0, sep).to_int(); - height = params.substr(sep + 1, params.length()).to_int(); - } + Color color = Color(1.0, 1.0, 1.0); + OptionMap::Element *color_option = bbcode_options.find("color"); + if (color_option) { + color = _get_color_from_string(color_option->value(), color); + } - int end = p_bbcode.find("[", brk_end); - if (end == -1) { - end = p_bbcode.length(); - } + int width = 0; + int height = 0; + if (!bbcode_value.empty()) { + int sep = bbcode_value.find("x"); + if (sep == -1) { + width = bbcode_value.to_int(); + } else { + width = bbcode_value.substr(0, sep).to_int(); + height = bbcode_value.substr(sep + 1).to_int(); + } + } else { + OptionMap::Element *width_option = bbcode_options.find("width"); + if (width_option) { + width = width_option->value().to_int(); + } - String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1); + OptionMap::Element *height_option = bbcode_options.find("height"); + if (height_option) { + height = height_option->value().to_int(); + } + } - Ref<Texture2D> texture = ResourceLoader::load(image, "Texture"); - if (texture.is_valid()) { - add_image(texture, width, height); + add_image(texture, width, height, color); } pos = end; - tag_stack.push_front("img"); + tag_stack.push_front(bbcode_name); } else if (tag.begins_with("color=")) { - String col = tag.substr(6, tag.length()); - Color color; - - if (col.begins_with("#")) { - color = Color::html(col); - } else if (col == "aqua") { - color = Color(0, 1, 1); - } else if (col == "black") { - color = Color(0, 0, 0); - } else if (col == "blue") { - color = Color(0, 0, 1); - } else if (col == "fuchsia") { - color = Color(1, 0, 1); - } else if (col == "gray" || col == "grey") { - color = Color(0.5, 0.5, 0.5); - } else if (col == "green") { - color = Color(0, 0.5, 0); - } else if (col == "lime") { - color = Color(0, 1, 0); - } else if (col == "maroon") { - color = Color(0.5, 0, 0); - } else if (col == "navy") { - color = Color(0, 0, 0.5); - } else if (col == "olive") { - color = Color(0.5, 0.5, 0); - } else if (col == "purple") { - color = Color(0.5, 0, 0.5); - } else if (col == "red") { - color = Color(1, 0, 0); - } else if (col == "silver") { - color = Color(0.75, 0.75, 0.75); - } else if (col == "teal") { - color = Color(0, 0.5, 0.5); - } else if (col == "white") { - color = Color(1, 1, 1); - } else if (col == "yellow") { - color = Color(1, 1, 0); - } else { - color = base_color; - } - + String color_str = tag.substr(6, tag.length()); + Color color = _get_color_from_string(color_str, base_color); push_color(color); pos = brk_end + 1; tag_stack.push_front("color"); @@ -2261,113 +2295,90 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front("font"); - } else if (bbcode == "fade") { - int startIndex = 0; - int length = 10; + } else if (bbcode_name == "fade") { + int start_index = 0; + OptionMap::Element *start_option = bbcode_options.find("start"); + if (start_option) { + start_index = start_option->value().to_int(); + } - if (split_tag_block.size() > 1) { - split_tag_block.remove(0); - for (int i = 0; i < split_tag_block.size(); i++) { - String expr = split_tag_block[i]; - if (expr.begins_with("start=")) { - String start_str = expr.substr(6, expr.length()); - startIndex = start_str.to_int(); - } else if (expr.begins_with("length=")) { - String end_str = expr.substr(7, expr.length()); - length = end_str.to_int(); - } - } + int length = 10; + OptionMap::Element *length_option = bbcode_options.find("length"); + if (length_option) { + length = length_option->value().to_int(); } - push_fade(startIndex, length); + push_fade(start_index, length); pos = brk_end + 1; tag_stack.push_front("fade"); - } else if (bbcode == "shake") { + } else if (bbcode_name == "shake") { int strength = 5; - float rate = 20.0f; + OptionMap::Element *strength_option = bbcode_options.find("level"); + if (strength_option) { + strength = strength_option->value().to_int(); + } - if (split_tag_block.size() > 1) { - split_tag_block.remove(0); - for (int i = 0; i < split_tag_block.size(); i++) { - String expr = split_tag_block[i]; - if (expr.begins_with("level=")) { - String str_str = expr.substr(6, expr.length()); - strength = str_str.to_int(); - } else if (expr.begins_with("rate=")) { - String rate_str = expr.substr(5, expr.length()); - rate = rate_str.to_float(); - } - } + float rate = 20.0f; + OptionMap::Element *rate_option = bbcode_options.find("rate"); + if (rate_option) { + rate = rate_option->value().to_float(); } push_shake(strength, rate); pos = brk_end + 1; tag_stack.push_front("shake"); set_process_internal(true); - } else if (bbcode == "wave") { + } else if (bbcode_name == "wave") { float amplitude = 20.0f; - float period = 5.0f; + OptionMap::Element *amplitude_option = bbcode_options.find("amp"); + if (amplitude_option) { + amplitude = amplitude_option->value().to_float(); + } - if (split_tag_block.size() > 1) { - split_tag_block.remove(0); - for (int i = 0; i < split_tag_block.size(); i++) { - String expr = split_tag_block[i]; - if (expr.begins_with("amp=")) { - String amp_str = expr.substr(4, expr.length()); - amplitude = amp_str.to_float(); - } else if (expr.begins_with("freq=")) { - String period_str = expr.substr(5, expr.length()); - period = period_str.to_float(); - } - } + float period = 5.0f; + OptionMap::Element *period_option = bbcode_options.find("freq"); + if (period_option) { + period = period_option->value().to_float(); } push_wave(period, amplitude); pos = brk_end + 1; tag_stack.push_front("wave"); set_process_internal(true); - } else if (bbcode == "tornado") { + } else if (bbcode_name == "tornado") { float radius = 10.0f; - float frequency = 1.0f; + OptionMap::Element *radius_option = bbcode_options.find("radius"); + if (radius_option) { + radius = radius_option->value().to_float(); + } - if (split_tag_block.size() > 1) { - split_tag_block.remove(0); - for (int i = 0; i < split_tag_block.size(); i++) { - String expr = split_tag_block[i]; - if (expr.begins_with("radius=")) { - String amp_str = expr.substr(7, expr.length()); - radius = amp_str.to_float(); - } else if (expr.begins_with("freq=")) { - String period_str = expr.substr(5, expr.length()); - frequency = period_str.to_float(); - } - } + float frequency = 1.0f; + OptionMap::Element *frequency_option = bbcode_options.find("freq"); + if (frequency_option) { + frequency = frequency_option->value().to_float(); } push_tornado(frequency, radius); pos = brk_end + 1; tag_stack.push_front("tornado"); set_process_internal(true); - } else if (bbcode == "rainbow") { + } else if (bbcode_name == "rainbow") { float saturation = 0.8f; + OptionMap::Element *saturation_option = bbcode_options.find("sat"); + if (saturation_option) { + saturation = saturation_option->value().to_float(); + } + float value = 0.8f; - float frequency = 1.0f; + OptionMap::Element *value_option = bbcode_options.find("val"); + if (value_option) { + value = value_option->value().to_float(); + } - if (split_tag_block.size() > 1) { - split_tag_block.remove(0); - for (int i = 0; i < split_tag_block.size(); i++) { - String expr = split_tag_block[i]; - if (expr.begins_with("sat=")) { - String sat_str = expr.substr(4, expr.length()); - saturation = sat_str.to_float(); - } else if (expr.begins_with("val=")) { - String val_str = expr.substr(4, expr.length()); - value = val_str.to_float(); - } else if (expr.begins_with("freq=")) { - String freq_str = expr.substr(5, expr.length()); - frequency = freq_str.to_float(); - } - } + float frequency = 1.0f; + OptionMap::Element *frequency_option = bbcode_options.find("freq"); + if (frequency_option) { + frequency = frequency_option->value().to_float(); } push_rainbow(saturation, value, frequency); @@ -2634,7 +2645,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); - ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0))); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 019edf5d45..4cec435568 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -149,6 +149,7 @@ private: struct ItemImage : public Item { Ref<Texture2D> image; Size2 size; + Color color; ItemImage() { type = ITEM_IMAGE; } }; @@ -381,6 +382,8 @@ private: bool _find_by_type(Item *p_item, ItemType p_type); void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack); + static Color _get_color_from_string(const String &p_color_str, const Color &p_default_color); + void _update_scroll(); void _update_fx(ItemFrame *p_frame, float p_delta_time); void _scroll_changed(double); @@ -406,7 +409,7 @@ protected: public: String get_text(); void add_text(const String &p_text); - void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0); + void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0)); void add_newline(); bool remove_line(const int p_line); void push_font(const Ref<Font> &p_font); diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index e7950bec98..4db6ca2949 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -506,6 +506,10 @@ void ScrollBar::_drag_node_exit() { } void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) { + if (!drag_node_enabled) { + return; + } + Ref<InputEventMouseButton> mb = p_input; if (mb.is_valid()) { @@ -590,6 +594,10 @@ NodePath ScrollBar::get_drag_node() const { return drag_node_path; } +void ScrollBar::set_drag_node_enabled(bool p_enable) { + drag_node_enabled = p_enable; +} + void ScrollBar::set_smooth_scroll_enabled(bool p_enable) { smooth_scroll_enabled = p_enable; } @@ -610,19 +618,6 @@ void ScrollBar::_bind_methods() { ScrollBar::ScrollBar(Orientation p_orientation) { orientation = p_orientation; - highlight = HIGHLIGHT_NONE; - custom_step = -1; - drag_node = nullptr; - - drag.active = false; - - drag_node_speed = Vector2(); - drag_node_touching = false; - drag_node_touching_deaccel = false; - - scrolling = false; - target_scroll = 0; - smooth_scroll_enabled = false; if (focus_by_default) { set_focus_mode(FOCUS_ALL); diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index d2641b14f3..23ee61d9e1 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -47,12 +47,12 @@ class ScrollBar : public Range { Orientation orientation; Size2 size; - float custom_step; + float custom_step = -1; - HighlightStatus highlight; + HighlightStatus highlight = HIGHLIGHT_NONE; struct Drag { - bool active; + bool active = false; float pos_at_click; float value_at_click; } drag; @@ -66,22 +66,23 @@ class ScrollBar : public Range { static void set_can_focus_by_default(bool p_can_focus); - Node *drag_node; + Node *drag_node = nullptr; NodePath drag_node_path; + bool drag_node_enabled = true; - Vector2 drag_node_speed; + Vector2 drag_node_speed = Vector2(); Vector2 drag_node_accum; Vector2 drag_node_from; Vector2 last_drag_node_accum; float last_drag_node_time; float time_since_motion; - bool drag_node_touching; - bool drag_node_touching_deaccel; + bool drag_node_touching = false; + bool drag_node_touching_deaccel = false; bool click_handled; - bool scrolling; - double target_scroll; - bool smooth_scroll_enabled; + bool scrolling = false; + double target_scroll = 0; + bool smooth_scroll_enabled = false; void _drag_node_exit(); void _drag_node_input(const Ref<InputEvent> &p_input); @@ -99,6 +100,7 @@ public: void set_drag_node(const NodePath &p_path); NodePath get_drag_node() const; + void set_drag_node_enabled(bool p_enable); void set_smooth_scroll_enabled(bool p_enable); bool is_smooth_scroll_enabled() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 5588d1bd97..2d0e2daf41 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -154,6 +154,7 @@ #include "scene/resources/physics_material.h" #include "scene/resources/polygon_path_finder.h" #include "scene/resources/primitive_meshes.h" +#include "scene/resources/ray_shape_2d.h" #include "scene/resources/ray_shape_3d.h" #include "scene/resources/rectangle_shape_2d.h" #include "scene/resources/resource_format_text.h" diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index c9fc68233d..014b773298 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -31,7 +31,7 @@ #include "animation.h" #include "scene/scene_string_names.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #define ANIM_MIN_LENGTH 0.001 @@ -2730,7 +2730,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons } Vector3 s[2] = { v0, v2 }; - real_t d = Geometry::get_closest_point_to_segment(v1, s).distance_to(v1); + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); if (d > pd.length() * p_alowed_linear_err) { return false; //beyond allowed error for colinearity @@ -2820,7 +2820,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons } Vector3 s[2] = { v0, v2 }; - real_t d = Geometry::get_closest_point_to_segment(v1, s).distance_to(v1); + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); if (d > pd.length() * p_alowed_linear_err) { return false; //beyond allowed error for colinearity diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index 0e784e04ff..e519970f38 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -30,6 +30,7 @@ #include "capsule_shape_2d.h" +#include "core/math/geometry_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" @@ -48,7 +49,7 @@ Vector<Vector2> CapsuleShape2D::_get_points() const { } bool CapsuleShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - return Geometry::is_point_in_polygon(p_point, _get_points()); + return Geometry2D::is_point_in_polygon(p_point, _get_points()); } void CapsuleShape2D::_update_shape() { diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp index 2154633111..eecf8afa8f 100644 --- a/scene/resources/concave_polygon_shape_2d.cpp +++ b/scene/resources/concave_polygon_shape_2d.cpp @@ -30,6 +30,7 @@ #include "concave_polygon_shape_2d.h" +#include "core/math/geometry_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" @@ -42,7 +43,7 @@ bool ConcavePolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, do const Vector2 *r = s.ptr(); for (int i = 0; i < len; i += 2) { - Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, &r[i]); + Vector2 closest = Geometry2D::get_closest_point_to_segment(p_point, &r[i]); if (p_point.distance_to(closest) < p_tolerance) { return true; } diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index 7df7c3ac72..2b7531c630 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -30,17 +30,17 @@ #include "convex_polygon_shape_2d.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" bool ConvexPolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - return Geometry::is_point_in_polygon(p_point, points); + return Geometry2D::is_point_in_polygon(p_point, points); } void ConvexPolygonShape2D::_update_shape() { Vector<Vector2> final_points = points; - if (Geometry::is_polygon_clockwise(final_points)) { //needs to be counter clockwise + if (Geometry2D::is_polygon_clockwise(final_points)) { //needs to be counter clockwise final_points.invert(); } PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), final_points); @@ -48,7 +48,7 @@ void ConvexPolygonShape2D::_update_shape() { } void ConvexPolygonShape2D::set_point_cloud(const Vector<Vector2> &p_points) { - Vector<Point2> hull = Geometry::convex_hull_2d(p_points); + Vector<Point2> hull = Geometry2D::convex_hull(p_points); ERR_FAIL_COND(hull.size() < 3); set_points(hull); } diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp index e52df73663..affeb05a8f 100644 --- a/scene/resources/convex_polygon_shape_3d.cpp +++ b/scene/resources/convex_polygon_shape_3d.cpp @@ -37,7 +37,7 @@ Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() { if (points.size() > 3) { Vector<Vector3> varr = Variant(points); - Geometry::MeshData md; + Geometry3D::MeshData md; Error err = QuickHull::build(varr, md); if (err == OK) { Vector<Vector3> lines; diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index 3b4e4b73f8..3d99556a10 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -567,7 +567,7 @@ DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(CharType p_ch if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { goto cleanup_glyph; } - if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, 1) != 0) { + if (FT_Glyph_To_Bitmap(&glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { goto cleanup_glyph; } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 1878b174e8..ccab88a153 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -93,6 +93,7 @@ void Font::_bind_methods() { ClassDB::bind_method(D_METHOD("get_descent"), &Font::get_descent); ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height); ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint); + ClassDB::bind_method(D_METHOD("get_char_size", "char", "next"), &Font::get_char_size, DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size); ClassDB::bind_method(D_METHOD("get_wordwrap_string_size", "string", "width"), &Font::get_wordwrap_string_size); ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline); @@ -596,8 +597,6 @@ void BitmapFont::_bind_methods() { ClassDB::bind_method(D_METHOD("get_texture_count"), &BitmapFont::get_texture_count); ClassDB::bind_method(D_METHOD("get_texture", "idx"), &BitmapFont::get_texture); - ClassDB::bind_method(D_METHOD("get_char_size", "char", "next"), &BitmapFont::get_char_size, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("set_distance_field_hint", "enable"), &BitmapFont::set_distance_field_hint); ClassDB::bind_method(D_METHOD("clear"), &BitmapFont::clear); diff --git a/scene/resources/line_shape_2d.cpp b/scene/resources/line_shape_2d.cpp index 802ccaaee6..58653c5f4a 100644 --- a/scene/resources/line_shape_2d.cpp +++ b/scene/resources/line_shape_2d.cpp @@ -30,6 +30,7 @@ #include "line_shape_2d.h" +#include "core/math/geometry_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" @@ -38,7 +39,7 @@ bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tol Vector2 l[2][2] = { { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }, { point, point + get_normal() * 30 } }; for (int i = 0; i < 2; i++) { - Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, l[i]); + Vector2 closest = Geometry2D::get_closest_point_to_segment(p_point, l[i]); if (p_point.distance_to(closest) < p_tolerance) { return true; } diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 0546c92948..df98d4cfd4 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "polygon_path_finder.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" bool PolygonPathFinder::_is_point_inside(const Vector2 &p_point) const { int crosses = 0; @@ -40,7 +40,7 @@ bool PolygonPathFinder::_is_point_inside(const Vector2 &p_point) const { Vector2 a = points[e.points[0]].pos; Vector2 b = points[e.points[1]].pos; - if (Geometry::segment_intersects_segment_2d(a, b, p_point, outside_point, nullptr)) { + if (Geometry2D::segment_intersects_segment(a, b, p_point, outside_point, nullptr)) { crosses++; } } @@ -114,7 +114,7 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> Vector2 a = points[e.points[0]].pos; Vector2 b = points[e.points[1]].pos; - if (Geometry::segment_intersects_segment_2d(a, b, from, to, nullptr)) { + if (Geometry2D::segment_intersects_segment(a, b, from, to, nullptr)) { valid = false; break; } @@ -147,7 +147,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector points[e.points[1]].pos }; - Vector2 closest = Geometry::get_closest_point_to_segment_2d(from, seg); + Vector2 closest = Geometry2D::get_closest_point_to_segment(from, seg); float d = from.distance_squared_to(closest); if (d < closest_dist) { @@ -171,7 +171,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector points[e.points[1]].pos }; - Vector2 closest = Geometry::get_closest_point_to_segment_2d(to, seg); + Vector2 closest = Geometry2D::get_closest_point_to_segment(to, seg); float d = to.distance_squared_to(closest); if (d < closest_dist) { @@ -200,7 +200,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector Vector2 a = points[e.points[0]].pos; Vector2 b = points[e.points[1]].pos; - if (Geometry::segment_intersects_segment_2d(a, b, from, to, nullptr)) { + if (Geometry2D::segment_intersects_segment(a, b, from, to, nullptr)) { can_see_eachother = false; break; } @@ -255,7 +255,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector e.points[1] != ignore_from_edge.points[1] && e.points[0] != ignore_from_edge.points[0] && e.points[1] != ignore_from_edge.points[0]) { - if (Geometry::segment_intersects_segment_2d(a, b, from, points[i].pos, nullptr)) { + if (Geometry2D::segment_intersects_segment(a, b, from, points[i].pos, nullptr)) { valid_a = false; } } @@ -266,7 +266,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector e.points[1] != ignore_to_edge.points[1] && e.points[0] != ignore_to_edge.points[0] && e.points[1] != ignore_to_edge.points[0]) { - if (Geometry::segment_intersects_segment_2d(a, b, to, points[i].pos, nullptr)) { + if (Geometry2D::segment_intersects_segment(a, b, to, points[i].pos, nullptr)) { valid_b = false; } } @@ -499,7 +499,7 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { points[e.points[1]].pos }; - Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, seg); + Vector2 closest = Geometry2D::get_closest_point_to_segment(p_point, seg); float d = p_point.distance_squared_to(closest); if (d < closest_dist) { @@ -521,7 +521,7 @@ Vector<Vector2> PolygonPathFinder::get_intersections(const Vector2 &p_from, cons Vector2 b = points[E->get().points[1]].pos; Vector2 res; - if (Geometry::segment_intersects_segment_2d(a, b, p_from, p_to, &res)) { + if (Geometry2D::segment_intersects_segment(a, b, p_from, p_to, &res)) { inters.push_back(res); } } diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 99edf26dc1..8d9c5f07b2 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -204,7 +204,7 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces); ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces"); } diff --git a/scene/resources/ray_shape_2d.cpp b/scene/resources/ray_shape_2d.cpp new file mode 100644 index 0000000000..67c4f84749 --- /dev/null +++ b/scene/resources/ray_shape_2d.cpp @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* ray_shape_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 "ray_shape_2d.h" + +#include "servers/physics_server_2d.h" +#include "servers/rendering_server.h" + +void RayShape2D::_update_shape() { + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), d); + emit_changed(); +} + +void RayShape2D::draw(const RID &p_to_rid, const Color &p_color) { + Vector2 tip = Vector2(0, get_length()); + RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), tip, p_color, 3); + Vector<Vector2> pts; + float tsize = 4; + pts.push_back(tip + Vector2(0, tsize)); + pts.push_back(tip + Vector2(Math_SQRT12 * tsize, 0)); + pts.push_back(tip + Vector2(-Math_SQRT12 * tsize, 0)); + Vector<Color> cols; + for (int i = 0; i < 3; i++) { + cols.push_back(p_color); + } + RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID()); +} + +Rect2 RayShape2D::get_rect() const { + Rect2 rect; + rect.position = Vector2(); + rect.expand_to(Vector2(0, length)); + rect = rect.grow(Math_SQRT12 * 4); + return rect; +} + +real_t RayShape2D::get_enclosing_radius() const { + return length; +} + +void RayShape2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length); + ClassDB::bind_method(D_METHOD("get_length"), &RayShape2D::get_length); + + ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape2D::set_slips_on_slope); + ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape2D::get_slips_on_slope); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope"); +} + +void RayShape2D::set_length(real_t p_length) { + length = p_length; + _update_shape(); +} + +real_t RayShape2D::get_length() const { + return length; +} + +void RayShape2D::set_slips_on_slope(bool p_active) { + slips_on_slope = p_active; + _update_shape(); +} + +bool RayShape2D::get_slips_on_slope() const { + return slips_on_slope; +} + +RayShape2D::RayShape2D() : + Shape2D(PhysicsServer2D::get_singleton()->ray_shape_create()) { + length = 20; + slips_on_slope = false; + _update_shape(); +} diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java b/scene/resources/ray_shape_2d.h index 6b66c7e474..9a209d2907 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java +++ b/scene/resources/ray_shape_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* RequestParams.java */ +/* ray_shape_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,57 +28,34 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.plugin.payment.utils; +#ifndef RAY_SHAPE_2D_H +#define RAY_SHAPE_2D_H -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +#include "scene/resources/shape_2d.h" -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicNameValuePair; +class RayShape2D : public Shape2D { + GDCLASS(RayShape2D, Shape2D); -/** - * - * @author Luis Linietsky <luis.linietsky@gmail.com> - */ -public class RequestParams { - private HashMap<String, String> params; - private String url; + real_t length; + bool slips_on_slope; - public RequestParams() { - params = new HashMap<String, String>(); - } + void _update_shape(); - public void put(String key, String value) { - params.put(key, value); - } +protected: + static void _bind_methods(); - public String get(String key) { - return params.get(key); - } +public: + void set_length(real_t p_length); + real_t get_length() const; - public void remove(Object key) { - params.remove(key); - } + void set_slips_on_slope(bool p_active); + bool get_slips_on_slope() const; - public boolean has(String key) { - return params.containsKey(key); - } + virtual void draw(const RID &p_to_rid, const Color &p_color); + virtual Rect2 get_rect() const; + virtual real_t get_enclosing_radius() const; - public List<NameValuePair> toPairsList() { - List<NameValuePair> fields = new ArrayList<NameValuePair>(); + RayShape2D(); +}; - for (String key : params.keySet()) { - fields.add(new BasicNameValuePair(key, this.get(key))); - } - return fields; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } -} +#endif // RAY_SHAPE_2D_H diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp index 7b409eebbb..b1001203a1 100644 --- a/scene/resources/segment_shape_2d.cpp +++ b/scene/resources/segment_shape_2d.cpp @@ -30,12 +30,13 @@ #include "segment_shape_2d.h" +#include "core/math/geometry_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" bool SegmentShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { Vector2 l[2] = { a, b }; - Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, l); + Vector2 closest = Geometry2D::get_closest_point_to_segment(p_point, l); return p_point.distance_to(closest) < p_tolerance; } @@ -97,77 +98,3 @@ SegmentShape2D::SegmentShape2D() : b = Vector2(0, 10); _update_shape(); } - -//////////////////////////////////////////////////////////// - -void RayShape2D::_update_shape() { - Dictionary d; - d["length"] = length; - d["slips_on_slope"] = slips_on_slope; - PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), d); - emit_changed(); -} - -void RayShape2D::draw(const RID &p_to_rid, const Color &p_color) { - Vector2 tip = Vector2(0, get_length()); - RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), tip, p_color, 3); - Vector<Vector2> pts; - float tsize = 4; - pts.push_back(tip + Vector2(0, tsize)); - pts.push_back(tip + Vector2(Math_SQRT12 * tsize, 0)); - pts.push_back(tip + Vector2(-Math_SQRT12 * tsize, 0)); - Vector<Color> cols; - for (int i = 0; i < 3; i++) { - cols.push_back(p_color); - } - - RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID()); -} - -Rect2 RayShape2D::get_rect() const { - Rect2 rect; - rect.position = Vector2(); - rect.expand_to(Vector2(0, length)); - rect = rect.grow(Math_SQRT12 * 4); - return rect; -} - -real_t RayShape2D::get_enclosing_radius() const { - return length; -} - -void RayShape2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length); - ClassDB::bind_method(D_METHOD("get_length"), &RayShape2D::get_length); - - ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape2D::set_slips_on_slope); - ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape2D::get_slips_on_slope); - - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope"); -} - -void RayShape2D::set_length(real_t p_length) { - length = p_length; - _update_shape(); -} - -real_t RayShape2D::get_length() const { - return length; -} - -void RayShape2D::set_slips_on_slope(bool p_active) { - slips_on_slope = p_active; - _update_shape(); -} - -bool RayShape2D::get_slips_on_slope() const { - return slips_on_slope; -} - -RayShape2D::RayShape2D() : - Shape2D(PhysicsServer2D::get_singleton()->ray_shape_create()) { - length = 20; - slips_on_slope = false; - _update_shape(); -} diff --git a/scene/resources/segment_shape_2d.h b/scene/resources/segment_shape_2d.h index ca10c24f07..39c297b040 100644 --- a/scene/resources/segment_shape_2d.h +++ b/scene/resources/segment_shape_2d.h @@ -60,29 +60,4 @@ public: SegmentShape2D(); }; -class RayShape2D : public Shape2D { - GDCLASS(RayShape2D, Shape2D); - - real_t length; - bool slips_on_slope; - - void _update_shape(); - -protected: - static void _bind_methods(); - -public: - void set_length(real_t p_length); - real_t get_length() const; - - void set_slips_on_slope(bool p_active); - bool get_slips_on_slope() const; - - virtual void draw(const RID &p_to_rid, const Color &p_color); - virtual Rect2 get_rect() const; - virtual real_t get_enclosing_radius() const; - - RayShape2D(); -}; - #endif // SEGMENT_SHAPE_2D_H diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 4ca8032d65..4249542567 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -125,7 +125,7 @@ bool Shader::is_text_shader() const { } bool Shader::has_param(const StringName &p_param) const { - return params_cache.has(p_param); + return params_cache.has("shader_param/" + p_param); } void Shader::_update_shader() const { diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index c17b6f8817..6992360df7 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -29,8 +29,10 @@ /*************************************************************************/ #include "tile_set.h" + #include "core/array.h" #include "core/engine.h" +#include "core/math/geometry_2d.h" bool TileSet::_set(const StringName &p_name, const Variant &p_value) { String n = p_name; @@ -1033,7 +1035,7 @@ void TileSet::_decompose_convex_shape(Ref<Shape2D> p_shape) { if (!convex.is_valid()) { return; } - Vector<Vector<Vector2>> decomp = Geometry::decompose_polygon_in_convex(convex->get_points()); + Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(convex->get_points()); if (decomp.size() > 1) { Array sub_shapes; for (int i = 0; i < decomp.size(); i++) { diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 7f87da3724..7cf9a4fedd 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -62,6 +62,8 @@ SceneStringNames::SceneStringNames() { animation_changed = StaticCString::create("animation_changed"); animation_started = StaticCString::create("animation_started"); + pose_updated = StaticCString::create("pose_updated"); + mouse_entered = StaticCString::create("mouse_entered"); mouse_exited = StaticCString::create("mouse_exited"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 1a6ffbd5dd..1ae244492e 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -95,6 +95,8 @@ public: StringName animation_changed; StringName animation_started; + StringName pose_updated; + StringName body_shape_entered; StringName body_entered; StringName body_shape_exited; diff --git a/servers/physics_2d/collision_solver_2d_sat.cpp b/servers/physics_2d/collision_solver_2d_sat.cpp index 2cada2bf50..d993754fee 100644 --- a/servers/physics_2d/collision_solver_2d_sat.cpp +++ b/servers/physics_2d/collision_solver_2d_sat.cpp @@ -30,7 +30,7 @@ #include "collision_solver_2d_sat.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" struct _CollectorCallback2D { CollisionSolver2DSW::CallbackResult callback; @@ -70,7 +70,7 @@ _FORCE_INLINE_ static void _generate_contacts_point_edge(const Vector2 *p_points ERR_FAIL_COND(p_point_count_B != 2); #endif - Vector2 closest_B = Geometry::get_closest_point_to_segment_uncapped_2d(*p_points_A, p_points_B); + Vector2 closest_B = Geometry2D::get_closest_point_to_segment_uncapped(*p_points_A, p_points_B); p_collector->call(*p_points_A, closest_B); } diff --git a/servers/physics_2d/joints_2d_sw.cpp b/servers/physics_2d/joints_2d_sw.cpp index eda0b923a2..81e961e90d 100644 --- a/servers/physics_2d/joints_2d_sw.cpp +++ b/servers/physics_2d/joints_2d_sw.cpp @@ -390,29 +390,29 @@ void DampedSpringJoint2DSW::solve(real_t p_step) { B->apply_impulse(rB, j); } -void DampedSpringJoint2DSW::set_param(PhysicsServer2D::DampedStringParam p_param, real_t p_value) { +void DampedSpringJoint2DSW::set_param(PhysicsServer2D::DampedSpringParam p_param, real_t p_value) { switch (p_param) { - case PhysicsServer2D::DAMPED_STRING_REST_LENGTH: { + case PhysicsServer2D::DAMPED_SPRING_REST_LENGTH: { rest_length = p_value; } break; - case PhysicsServer2D::DAMPED_STRING_DAMPING: { + case PhysicsServer2D::DAMPED_SPRING_DAMPING: { damping = p_value; } break; - case PhysicsServer2D::DAMPED_STRING_STIFFNESS: { + case PhysicsServer2D::DAMPED_SPRING_STIFFNESS: { stiffness = p_value; } break; } } -real_t DampedSpringJoint2DSW::get_param(PhysicsServer2D::DampedStringParam p_param) const { +real_t DampedSpringJoint2DSW::get_param(PhysicsServer2D::DampedSpringParam p_param) const { switch (p_param) { - case PhysicsServer2D::DAMPED_STRING_REST_LENGTH: { + case PhysicsServer2D::DAMPED_SPRING_REST_LENGTH: { return rest_length; } break; - case PhysicsServer2D::DAMPED_STRING_DAMPING: { + case PhysicsServer2D::DAMPED_SPRING_DAMPING: { return damping; } break; - case PhysicsServer2D::DAMPED_STRING_STIFFNESS: { + case PhysicsServer2D::DAMPED_SPRING_STIFFNESS: { return stiffness; } break; } diff --git a/servers/physics_2d/joints_2d_sw.h b/servers/physics_2d/joints_2d_sw.h index 87556ccea1..3c8aab77c8 100644 --- a/servers/physics_2d/joints_2d_sw.h +++ b/servers/physics_2d/joints_2d_sw.h @@ -151,8 +151,8 @@ public: virtual bool setup(real_t p_step); virtual void solve(real_t p_step); - void set_param(PhysicsServer2D::DampedStringParam p_param, real_t p_value); - real_t get_param(PhysicsServer2D::DampedStringParam p_param) const; + void set_param(PhysicsServer2D::DampedSpringParam p_param, real_t p_value); + real_t get_param(PhysicsServer2D::DampedSpringParam p_param) const; DampedSpringJoint2DSW(const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, Body2DSW *p_body_a, Body2DSW *p_body_b); ~DampedSpringJoint2DSW(); diff --git a/servers/physics_2d/physics_server_2d_sw.cpp b/servers/physics_2d/physics_server_2d_sw.cpp index a686903763..6983225668 100644 --- a/servers/physics_2d/physics_server_2d_sw.cpp +++ b/servers/physics_2d/physics_server_2d_sw.cpp @@ -1113,7 +1113,7 @@ real_t PhysicsServer2DSW::pin_joint_get_param(RID p_joint, PinJointParam p_param return pin_joint->get_param(p_param); } -void PhysicsServer2DSW::damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value) { +void PhysicsServer2DSW::damped_spring_joint_set_param(RID p_joint, DampedSpringParam p_param, real_t p_value) { Joint2DSW *j = joint_owner.getornull(p_joint); ERR_FAIL_COND(!j); ERR_FAIL_COND(j->get_type() != JOINT_DAMPED_SPRING); @@ -1122,7 +1122,7 @@ void PhysicsServer2DSW::damped_string_joint_set_param(RID p_joint, DampedStringP dsj->set_param(p_param, p_value); } -real_t PhysicsServer2DSW::damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const { +real_t PhysicsServer2DSW::damped_spring_joint_get_param(RID p_joint, DampedSpringParam p_param) const { Joint2DSW *j = joint_owner.getornull(p_joint); ERR_FAIL_COND_V(!j, 0); ERR_FAIL_COND_V(j->get_type() != JOINT_DAMPED_SPRING, 0); diff --git a/servers/physics_2d/physics_server_2d_sw.h b/servers/physics_2d/physics_server_2d_sw.h index f9b0bc716c..093c775cb5 100644 --- a/servers/physics_2d/physics_server_2d_sw.h +++ b/servers/physics_2d/physics_server_2d_sw.h @@ -266,8 +266,8 @@ public: virtual RID damped_spring_joint_create(const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, RID p_body_a, RID p_body_b = RID()); virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value); virtual real_t pin_joint_get_param(RID p_joint, PinJointParam p_param) const; - virtual void damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value); - virtual real_t damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const; + virtual void damped_spring_joint_set_param(RID p_joint, DampedSpringParam p_param, real_t p_value); + virtual real_t damped_spring_joint_get_param(RID p_joint, DampedSpringParam p_param) const; virtual JointType joint_get_type(RID p_joint) const; diff --git a/servers/physics_2d/physics_server_2d_wrap_mt.h b/servers/physics_2d/physics_server_2d_wrap_mt.h index c2ae288f95..bc918b20f4 100644 --- a/servers/physics_2d/physics_server_2d_wrap_mt.h +++ b/servers/physics_2d/physics_server_2d_wrap_mt.h @@ -287,8 +287,8 @@ public: FUNC3(pin_joint_set_param, RID, PinJointParam, real_t); FUNC2RC(real_t, pin_joint_get_param, RID, PinJointParam); - FUNC3(damped_string_joint_set_param, RID, DampedStringParam, real_t); - FUNC2RC(real_t, damped_string_joint_get_param, RID, DampedStringParam); + FUNC3(damped_spring_joint_set_param, RID, DampedSpringParam, real_t); + FUNC2RC(real_t, damped_spring_joint_get_param, RID, DampedSpringParam); FUNC1RC(JointType, joint_get_type, RID); diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp index c0589b9804..87e22ef1c9 100644 --- a/servers/physics_2d/shape_2d_sw.cpp +++ b/servers/physics_2d/shape_2d_sw.cpp @@ -30,7 +30,7 @@ #include "shape_2d_sw.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" #include "core/sort_array.h" void Shape2DSW::configure(const Rect2 &p_aabb) { @@ -205,7 +205,7 @@ bool SegmentShape2DSW::contains_point(const Vector2 &p_point) const { } bool SegmentShape2DSW::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const { - if (!Geometry::segment_intersects_segment_2d(p_begin, p_end, a, b, &r_point)) { + if (!Geometry2D::segment_intersects_segment(p_begin, p_end, a, b, &r_point)) { return false; } @@ -556,7 +556,7 @@ bool ConvexPolygonShape2DSW::intersect_segment(const Vector2 &p_begin, const Vec Vector2 res; - if (!Geometry::segment_intersects_segment_2d(p_begin, p_end, points[i].pos, points[(i + 1) % point_count].pos, &res)) { + if (!Geometry2D::segment_intersects_segment(p_begin, p_end, points[i].pos, points[(i + 1) % point_count].pos, &res)) { continue; } @@ -735,7 +735,7 @@ bool ConcavePolygonShape2DSW::intersect_segment(const Vector2 &p_begin, const Ve Vector2 res; - if (Geometry::segment_intersects_segment_2d(p_begin, p_end, a, b, &res)) { + if (Geometry2D::segment_intersects_segment(p_begin, p_end, a, b, &res)) { real_t nd = n.dot(res); if (nd < d) { d = nd; diff --git a/servers/physics_3d/collision_solver_3d_sat.cpp b/servers/physics_3d/collision_solver_3d_sat.cpp index 736222c7d9..85f55ad66d 100644 --- a/servers/physics_3d/collision_solver_3d_sat.cpp +++ b/servers/physics_3d/collision_solver_3d_sat.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "collision_solver_3d_sat.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.02 @@ -67,7 +67,7 @@ static void _generate_contacts_point_edge(const Vector3 *p_points_A, int p_point ERR_FAIL_COND(p_point_count_B != 2); #endif - Vector3 closest_B = Geometry::get_closest_point_to_segment_uncapped(*p_points_A, p_points_B); + Vector3 closest_B = Geometry3D::get_closest_point_to_segment_uncapped(*p_points_A, p_points_B); p_callback->call(*p_points_A, closest_B); } @@ -124,7 +124,7 @@ static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_ } Vector3 closest_A = p_points_A[0] + rel_A * d; - Vector3 closest_B = Geometry::get_closest_point_to_segment_uncapped(closest_A, p_points_B); + Vector3 closest_B = Geometry3D::get_closest_point_to_segment_uncapped(closest_A, p_points_B); p_callback->call(closest_A, closest_B); } @@ -542,11 +542,11 @@ static void _collision_sphere_convex_polygon(const Shape3DSW *p_a, const Transfo return; } - const Geometry::MeshData &mesh = convex_polygon_B->get_mesh(); + const Geometry3D::MeshData &mesh = convex_polygon_B->get_mesh(); - const Geometry::MeshData::Face *faces = mesh.faces.ptr(); + const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int face_count = mesh.faces.size(); - const Geometry::MeshData::Edge *edges = mesh.edges.ptr(); + const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr(); int edge_count = mesh.edges.size(); const Vector3 *vertices = mesh.vertices.ptr(); int vertex_count = mesh.vertices.size(); @@ -839,11 +839,11 @@ static void _collision_box_convex_polygon(const Shape3DSW *p_a, const Transform return; } - const Geometry::MeshData &mesh = convex_polygon_B->get_mesh(); + const Geometry3D::MeshData &mesh = convex_polygon_B->get_mesh(); - const Geometry::MeshData::Face *faces = mesh.faces.ptr(); + const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int face_count = mesh.faces.size(); - const Geometry::MeshData::Edge *edges = mesh.edges.ptr(); + const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr(); int edge_count = mesh.edges.size(); const Vector3 *vertices = mesh.vertices.ptr(); int vertex_count = mesh.vertices.size(); @@ -1124,11 +1124,11 @@ static void _collision_capsule_convex_polygon(const Shape3DSW *p_a, const Transf return; } - const Geometry::MeshData &mesh = convex_polygon_B->get_mesh(); + const Geometry3D::MeshData &mesh = convex_polygon_B->get_mesh(); - const Geometry::MeshData::Face *faces = mesh.faces.ptr(); + const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int face_count = mesh.faces.size(); - const Geometry::MeshData::Edge *edges = mesh.edges.ptr(); + const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr(); int edge_count = mesh.edges.size(); const Vector3 *vertices = mesh.vertices.ptr(); @@ -1257,20 +1257,20 @@ static void _collision_convex_polygon_convex_polygon(const Shape3DSW *p_a, const return; } - const Geometry::MeshData &mesh_A = convex_polygon_A->get_mesh(); + const Geometry3D::MeshData &mesh_A = convex_polygon_A->get_mesh(); - const Geometry::MeshData::Face *faces_A = mesh_A.faces.ptr(); + const Geometry3D::MeshData::Face *faces_A = mesh_A.faces.ptr(); int face_count_A = mesh_A.faces.size(); - const Geometry::MeshData::Edge *edges_A = mesh_A.edges.ptr(); + const Geometry3D::MeshData::Edge *edges_A = mesh_A.edges.ptr(); int edge_count_A = mesh_A.edges.size(); const Vector3 *vertices_A = mesh_A.vertices.ptr(); int vertex_count_A = mesh_A.vertices.size(); - const Geometry::MeshData &mesh_B = convex_polygon_B->get_mesh(); + const Geometry3D::MeshData &mesh_B = convex_polygon_B->get_mesh(); - const Geometry::MeshData::Face *faces_B = mesh_B.faces.ptr(); + const Geometry3D::MeshData::Face *faces_B = mesh_B.faces.ptr(); int face_count_B = mesh_B.faces.size(); - const Geometry::MeshData::Edge *edges_B = mesh_B.edges.ptr(); + const Geometry3D::MeshData::Edge *edges_B = mesh_B.edges.ptr(); int edge_count_B = mesh_B.edges.size(); const Vector3 *vertices_B = mesh_B.vertices.ptr(); int vertex_count_B = mesh_B.vertices.size(); @@ -1362,11 +1362,11 @@ static void _collision_convex_polygon_face(const Shape3DSW *p_a, const Transform SeparatorAxisTest<ConvexPolygonShape3DSW, FaceShape3DSW, withMargin> separator(convex_polygon_A, p_transform_a, face_B, p_transform_b, p_collector, p_margin_a, p_margin_b); - const Geometry::MeshData &mesh = convex_polygon_A->get_mesh(); + const Geometry3D::MeshData &mesh = convex_polygon_A->get_mesh(); - const Geometry::MeshData::Face *faces = mesh.faces.ptr(); + const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int face_count = mesh.faces.size(); - const Geometry::MeshData::Edge *edges = mesh.edges.ptr(); + const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr(); int edge_count = mesh.edges.size(); const Vector3 *vertices = mesh.vertices.ptr(); int vertex_count = mesh.vertices.size(); diff --git a/servers/physics_3d/shape_3d_sw.cpp b/servers/physics_3d/shape_3d_sw.cpp index eb0e87cec0..ca33241d29 100644 --- a/servers/physics_3d/shape_3d_sw.cpp +++ b/servers/physics_3d/shape_3d_sw.cpp @@ -30,7 +30,7 @@ #include "shape_3d_sw.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #include "core/math/quick_hull.h" #include "core/sort_array.h" @@ -195,7 +195,7 @@ Vector3 RayShape3DSW::get_closest_point_to(const Vector3 &p_point) const { Vector3(0, 0, length) }; - return Geometry::get_closest_point_to_segment(p_point, s); + return Geometry3D::get_closest_point_to_segment(p_point, s); } Vector3 RayShape3DSW::get_moment_of_inertia(real_t p_mass) const { @@ -252,7 +252,7 @@ void SphereShape3DSW::get_supports(const Vector3 &p_normal, int p_max, Vector3 * } bool SphereShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const { - return Geometry::segment_intersects_sphere(p_begin, p_end, Vector3(), radius, &r_result, &r_normal); + return Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(), radius, &r_result, &r_normal); } bool SphereShape3DSW::intersect_point(const Vector3 &p_point) const { @@ -441,7 +441,7 @@ Vector3 BoxShape3DSW::get_closest_point_to(const Vector3 &p_point) const { s[1] = closest_vertex; s[1][i] = -s[1][i]; //edge - Vector3 closest_edge = Geometry::get_closest_point_to_segment(p_point, s); + Vector3 closest_edge = Geometry3D::get_closest_point_to_segment(p_point, s); float d = p_point.distance_to(closest_edge); if (d < min_distance) { @@ -540,7 +540,7 @@ bool CapsuleShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 & // test against cylinder and spheres :-| - collided = Geometry::segment_intersects_cylinder(p_begin, p_end, height, radius, &auxres, &auxn); + collided = Geometry3D::segment_intersects_cylinder(p_begin, p_end, height, radius, &auxres, &auxn); if (collided) { real_t d = norm.dot(auxres); @@ -552,7 +552,7 @@ bool CapsuleShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 & } } - collided = Geometry::segment_intersects_sphere(p_begin, p_end, Vector3(0, 0, height * 0.5), radius, &auxres, &auxn); + collided = Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(0, 0, height * 0.5), radius, &auxres, &auxn); if (collided) { real_t d = norm.dot(auxres); @@ -564,7 +564,7 @@ bool CapsuleShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 & } } - collided = Geometry::segment_intersects_sphere(p_begin, p_end, Vector3(0, 0, height * -0.5), radius, &auxres, &auxn); + collided = Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(0, 0, height * -0.5), radius, &auxres, &auxn); if (collided) { real_t d = norm.dot(auxres); @@ -600,7 +600,7 @@ Vector3 CapsuleShape3DSW::get_closest_point_to(const Vector3 &p_point) const { Vector3(0, 0, height * 0.5), }; - Vector3 p = Geometry::get_closest_point_to_segment(p_point, s); + Vector3 p = Geometry3D::get_closest_point_to_segment(p_point, s); if (p.distance_to(p_point) < radius) { return p_point; @@ -691,10 +691,10 @@ Vector3 ConvexPolygonShape3DSW::get_support(const Vector3 &p_normal) const { } void ConvexPolygonShape3DSW::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount) const { - const Geometry::MeshData::Face *faces = mesh.faces.ptr(); + const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int fc = mesh.faces.size(); - const Geometry::MeshData::Edge *edges = mesh.edges.ptr(); + const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr(); int ec = mesh.edges.size(); const Vector3 *vertices = mesh.vertices.ptr(); @@ -755,7 +755,7 @@ void ConvexPolygonShape3DSW::get_supports(const Vector3 &p_normal, int p_max, Ve } bool ConvexPolygonShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const { - const Geometry::MeshData::Face *faces = mesh.faces.ptr(); + const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int fc = mesh.faces.size(); const Vector3 *vertices = mesh.vertices.ptr(); @@ -793,7 +793,7 @@ bool ConvexPolygonShape3DSW::intersect_segment(const Vector3 &p_begin, const Vec } bool ConvexPolygonShape3DSW::intersect_point(const Vector3 &p_point) const { - const Geometry::MeshData::Face *faces = mesh.faces.ptr(); + const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int fc = mesh.faces.size(); for (int i = 0; i < fc; i++) { @@ -806,7 +806,7 @@ bool ConvexPolygonShape3DSW::intersect_point(const Vector3 &p_point) const { } Vector3 ConvexPolygonShape3DSW::get_closest_point_to(const Vector3 &p_point) const { - const Geometry::MeshData::Face *faces = mesh.faces.ptr(); + const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int fc = mesh.faces.size(); const Vector3 *vertices = mesh.vertices.ptr(); @@ -844,7 +844,7 @@ Vector3 ConvexPolygonShape3DSW::get_closest_point_to(const Vector3 &p_point) con Vector3 min_point; //check edges - const Geometry::MeshData::Edge *edges = mesh.edges.ptr(); + const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr(); int ec = mesh.edges.size(); for (int i = 0; i < ec; i++) { Vector3 s[2] = { @@ -852,7 +852,7 @@ Vector3 ConvexPolygonShape3DSW::get_closest_point_to(const Vector3 &p_point) con vertices[edges[i].b] }; - Vector3 closest = Geometry::get_closest_point_to_segment(p_point, s); + Vector3 closest = Geometry3D::get_closest_point_to_segment(p_point, s); float d = closest.distance_to(p_point); if (d < min_distance) { min_distance = d; @@ -986,7 +986,7 @@ void FaceShape3DSW::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_ } bool FaceShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const { - bool c = Geometry::segment_intersects_triangle(p_begin, p_end, vertex[0], vertex[1], vertex[2], &r_result); + bool c = Geometry3D::segment_intersects_triangle(p_begin, p_end, vertex[0], vertex[1], vertex[2], &r_result); if (c) { r_normal = Plane(vertex[0], vertex[1], vertex[2]).normal; if (r_normal.dot(p_end - p_begin) > 0) { @@ -1095,7 +1095,7 @@ void ConcavePolygonShape3DSW::_cull_segment(int p_idx, _SegmentCullParams *p_par p_params->vertices[p_params->faces[bvh->face_index].indices[2]] }; - if (Geometry::segment_intersects_triangle( + if (Geometry3D::segment_intersects_triangle( p_params->from, p_params->to, vertices[0], diff --git a/servers/physics_3d/shape_3d_sw.h b/servers/physics_3d/shape_3d_sw.h index 848a1135ad..2a2cd42255 100644 --- a/servers/physics_3d/shape_3d_sw.h +++ b/servers/physics_3d/shape_3d_sw.h @@ -31,7 +31,7 @@ #ifndef SHAPE_SW_H #define SHAPE_SW_H -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #include "servers/physics_server_3d.h" /* @@ -252,12 +252,12 @@ public: }; struct ConvexPolygonShape3DSW : public Shape3DSW { - Geometry::MeshData mesh; + Geometry3D::MeshData mesh; void _setup(const Vector<Vector3> &p_vertices); public: - const Geometry::MeshData &get_mesh() const { return mesh; } + const Geometry3D::MeshData &get_mesh() const { return mesh; } virtual PhysicsServer3D::ShapeType get_type() const { return PhysicsServer3D::SHAPE_CONVEX_POLYGON; } diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index c2de7fbd00..080b8c61ad 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -654,8 +654,8 @@ void PhysicsServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("groove_joint_create", "groove1_a", "groove2_a", "anchor_b", "body_a", "body_b"), &PhysicsServer2D::groove_joint_create, DEFVAL(RID()), DEFVAL(RID())); ClassDB::bind_method(D_METHOD("damped_spring_joint_create", "anchor_a", "anchor_b", "body_a", "body_b"), &PhysicsServer2D::damped_spring_joint_create, DEFVAL(RID())); - ClassDB::bind_method(D_METHOD("damped_string_joint_set_param", "joint", "param", "value"), &PhysicsServer2D::damped_string_joint_set_param); - ClassDB::bind_method(D_METHOD("damped_string_joint_get_param", "joint", "param"), &PhysicsServer2D::damped_string_joint_get_param); + ClassDB::bind_method(D_METHOD("damped_spring_joint_set_param", "joint", "param", "value"), &PhysicsServer2D::damped_spring_joint_set_param); + ClassDB::bind_method(D_METHOD("damped_spring_joint_get_param", "joint", "param"), &PhysicsServer2D::damped_spring_joint_get_param); ClassDB::bind_method(D_METHOD("joint_get_type", "joint"), &PhysicsServer2D::joint_get_type); @@ -727,9 +727,9 @@ void PhysicsServer2D::_bind_methods() { BIND_ENUM_CONSTANT(JOINT_PARAM_MAX_BIAS); BIND_ENUM_CONSTANT(JOINT_PARAM_MAX_FORCE); - BIND_ENUM_CONSTANT(DAMPED_STRING_REST_LENGTH); - BIND_ENUM_CONSTANT(DAMPED_STRING_STIFFNESS); - BIND_ENUM_CONSTANT(DAMPED_STRING_DAMPING); + BIND_ENUM_CONSTANT(DAMPED_SPRING_REST_LENGTH); + BIND_ENUM_CONSTANT(DAMPED_SPRING_STIFFNESS); + BIND_ENUM_CONSTANT(DAMPED_SPRING_DAMPING); BIND_ENUM_CONSTANT(CCD_MODE_DISABLED); BIND_ENUM_CONSTANT(CCD_MODE_CAST_RAY); diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index 3553ec11a1..549a78aa1f 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -552,13 +552,13 @@ public: virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value) = 0; virtual real_t pin_joint_get_param(RID p_joint, PinJointParam p_param) const = 0; - enum DampedStringParam { - DAMPED_STRING_REST_LENGTH, - DAMPED_STRING_STIFFNESS, - DAMPED_STRING_DAMPING + enum DampedSpringParam { + DAMPED_SPRING_REST_LENGTH, + DAMPED_SPRING_STIFFNESS, + DAMPED_SPRING_DAMPING }; - virtual void damped_string_joint_set_param(RID p_joint, DampedStringParam p_param, real_t p_value) = 0; - virtual real_t damped_string_joint_get_param(RID p_joint, DampedStringParam p_param) const = 0; + virtual void damped_spring_joint_set_param(RID p_joint, DampedSpringParam p_param, real_t p_value) = 0; + virtual real_t damped_spring_joint_get_param(RID p_joint, DampedSpringParam p_param) const = 0; virtual JointType joint_get_type(RID p_joint) const = 0; @@ -678,7 +678,7 @@ VARIANT_ENUM_CAST(PhysicsServer2D::BodyState); VARIANT_ENUM_CAST(PhysicsServer2D::CCDMode); VARIANT_ENUM_CAST(PhysicsServer2D::JointParam); VARIANT_ENUM_CAST(PhysicsServer2D::JointType); -VARIANT_ENUM_CAST(PhysicsServer2D::DampedStringParam); +VARIANT_ENUM_CAST(PhysicsServer2D::DampedSpringParam); //VARIANT_ENUM_CAST( PhysicsServer2D::ObjectType ); VARIANT_ENUM_CAST(PhysicsServer2D::AreaBodyStatus); VARIANT_ENUM_CAST(PhysicsServer2D::ProcessInfo); diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index e0d4432fd5..026725bf01 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -814,7 +814,7 @@ public: item_mask = 1; scale = 1.0; energy = 1.0; - item_shadow_mask = -1; + item_shadow_mask = 1; mode = RS::CANVAS_LIGHT_MODE_ADD; // texture_cache = nullptr; next_ptr = nullptr; diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index 4f216d7cb5..a5151d1ff8 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -4295,7 +4295,7 @@ void RasterizerStorageRD::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p uint32_t *tetrahedron = (uint32_t *)&lm->tetrahedra[node * 4]; Vector3 points[4] = { lm->points[tetrahedron[0]], lm->points[tetrahedron[1]], lm->points[tetrahedron[2]], lm->points[tetrahedron[3]] }; const Color *sh_colors[4]{ &lm->point_sh[tetrahedron[0] * 9], &lm->point_sh[tetrahedron[1] * 9], &lm->point_sh[tetrahedron[2] * 9], &lm->point_sh[tetrahedron[3] * 9] }; - Color barycentric = Geometry::tetrahedron_get_barycentric_coords(points[0], points[1], points[2], points[3], p_point); + Color barycentric = Geometry3D::tetrahedron_get_barycentric_coords(points[0], points[1], points[2], points[3], p_point); for (int i = 0; i < 4; i++) { float c = CLAMP(barycentric[i], 0.0, 1.0); diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp index d0b91df470..32321164a1 100644 --- a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp @@ -479,7 +479,7 @@ static String _get_global_variable_from_type_and_index(const String &p_buffer, c } } -String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning) { +String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_use_scope) { String code; switch (p_node->type) { @@ -967,7 +967,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge if (anode->call_expression != nullptr) { code += "."; - code += _dump_node_code(anode->call_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += _dump_node_code(anode->call_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning, false); } if (anode->index_expression != nullptr) { @@ -1113,7 +1113,13 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge } break; default: { - code = "(" + _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + _opstr(onode->op) + _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ")"; + if (p_use_scope) { + code += "("; + } + code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + _opstr(onode->op) + _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + if (p_use_scope) { + code += ")"; + } break; } } diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.h b/servers/rendering/rasterizer_rd/shader_compiler_rd.h index eaad34b8cd..ce94fb743f 100644 --- a/servers/rendering/rasterizer_rd/shader_compiler_rd.h +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.h @@ -96,7 +96,7 @@ private: String _get_sampler_name(ShaderLanguage::TextureFilter p_filter, ShaderLanguage::TextureRepeat p_repeat); void _dump_function_deps(const ShaderLanguage::ShaderNode *p_node, const StringName &p_for_func, const Map<StringName, String> &p_func_code, String &r_to_add, Set<StringName> &added); - String _dump_node_code(const ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning); + String _dump_node_code(const ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_scope = true); const ShaderLanguage::ShaderNode *shader; const ShaderLanguage::FunctionNode *function; diff --git a/servers/rendering/rendering_device_binds.cpp b/servers/rendering/rendering_device_binds.cpp index 291f2ff705..0400cebfdc 100644 --- a/servers/rendering/rendering_device_binds.cpp +++ b/servers/rendering/rendering_device_binds.cpp @@ -103,7 +103,7 @@ Error RDShaderFile::parse_versions_from_text(const String &p_text, const String base_error = "Missing `=` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`."; break; } - if (l.find(";") != -1) { + if (l.find(";") == -1) { // We don't require a semicolon per se, but it's needed for clang-format to handle things properly. base_error = "Missing `;` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`."; break; diff --git a/servers/rendering/rendering_server_canvas.cpp b/servers/rendering/rendering_server_canvas.cpp index 0c9290a765..5c0741bb3b 100644 --- a/servers/rendering/rendering_server_canvas.cpp +++ b/servers/rendering/rendering_server_canvas.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "rendering_server_canvas.h" + +#include "core/math/geometry_2d.h" #include "rendering_server_globals.h" #include "rendering_server_raster.h" #include "rendering_server_viewport.h" @@ -774,7 +776,7 @@ void RenderingServerCanvas::canvas_item_add_polygon(RID p_item, const Vector<Poi ERR_FAIL_COND(color_size != 0 && color_size != 1 && color_size != pointcount); ERR_FAIL_COND(uv_size != 0 && (uv_size != pointcount)); #endif - Vector<int> indices = Geometry::triangulate_polygon(p_points); + Vector<int> indices = Geometry2D::triangulate_polygon(p_points); ERR_FAIL_COND_MSG(indices.empty(), "Invalid polygon data, triangulation failed."); Item::CommandPolygon *polygon = canvas_item->alloc_command<Item::CommandPolygon>(); diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h index f59028603f..f2e2918f21 100644 --- a/servers/rendering/rendering_server_scene.h +++ b/servers/rendering/rendering_server_scene.h @@ -33,7 +33,6 @@ #include "servers/rendering/rasterizer.h" -#include "core/math/geometry.h" #include "core/math/octree.h" #include "core/os/semaphore.h" #include "core/os/thread.h" diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 906946f074..4f12ec5b02 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2245,12 +2245,12 @@ void RenderingServer::_camera_set_orthogonal(RID p_camera, float p_size, float p camera_set_orthogonal(p_camera, p_size, p_z_near, p_z_far); } -void RenderingServer::mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry::MeshData &p_mesh_data) { +void RenderingServer::mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry3D::MeshData &p_mesh_data) { Vector<Vector3> vertices; Vector<Vector3> normals; for (int i = 0; i < p_mesh_data.faces.size(); i++) { - const Geometry::MeshData::Face &f = p_mesh_data.faces[i]; + const Geometry3D::MeshData::Face &f = p_mesh_data.faces[i]; for (int j = 2; j < f.indices.size(); j++) { #define _ADD_VERTEX(m_idx) \ @@ -2271,7 +2271,7 @@ void RenderingServer::mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry } void RenderingServer::mesh_add_surface_from_planes(RID p_mesh, const Vector<Plane> &p_planes) { - Geometry::MeshData mdata = Geometry::build_convex_mesh(p_planes); + Geometry3D::MeshData mdata = Geometry3D::build_convex_mesh(p_planes); mesh_add_surface_from_mesh_data(p_mesh, mdata); } diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 92a27b7fe6..3ce63585a8 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -32,7 +32,7 @@ #define RENDERING_SERVER_H #include "core/image.h" -#include "core/math/geometry.h" +#include "core/math/geometry_3d.h" #include "core/math/transform_2d.h" #include "core/object.h" #include "core/rid.h" @@ -1208,7 +1208,7 @@ public: virtual RID make_sphere_mesh(int p_lats, int p_lons, float p_radius); - virtual void mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry::MeshData &p_mesh_data); + virtual void mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry3D::MeshData &p_mesh_data); virtual void mesh_add_surface_from_planes(RID p_mesh, const Vector<Plane> &p_planes); virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) = 0; diff --git a/thirdparty/README.md b/thirdparty/README.md index 64c2ce336d..c69ca17fd0 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -446,12 +446,32 @@ Files extracted from the upstream source: ## oidn - Upstream: https://github.com/OpenImageDenoise/oidn -- Version: TBD +- Version: 1.1.0 (c58c5216db05ceef4cde5a096862f2eeffd14c06, 2019) - License: Apache 2.0 Files extracted from upstream source: -- TBD +common/* (except tasking.* and CMakeLists.txt) +core/* +include/OpenImageDenoise/* (except version.h.in) +LICENSE.txt +mkl-dnn/include/* +mkl-dnn/src/* (except CMakeLists.txt) +weights/rtlightmap_hdr.tza +scripts/resource_to_cpp.py + +Modified files: +Modifications are marked with `// -- GODOT start --` and `// -- GODOT end --`. +Patch files are provided in `oidn/patches/`. + +core/autoencoder.cpp +core/autoencoder.h +core/common.h +core/device.cpp +core/device.h +core/transfer_function.cpp + +scripts/resource_to_cpp.py (used in modules/denoise/resource_to_cpp.py) ## opus diff --git a/thirdparty/oidn/0001-window.h-case-sensitive.patch b/thirdparty/oidn/0001-window.h-case-sensitive.patch deleted file mode 100644 index 7b9c8e96c1..0000000000 --- a/thirdparty/oidn/0001-window.h-case-sensitive.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/thirdparty/oidn/common/platform.h b/thirdparty/oidn/common/platform.h -index 205ac8981d..9373b617b5 100644 ---- a/thirdparty/oidn/common/platform.h -+++ b/thirdparty/oidn/common/platform.h -@@ -19,7 +19,7 @@ - #if defined(_WIN32) - #define WIN32_LEAN_AND_MEAN - #define NOMINMAX -- #include <Windows.h> -+ #include <windows.h> - #elif defined(__APPLE__) - #include <sys/sysctl.h> - #endif diff --git a/thirdparty/oidn/core/autoencoder.cpp b/thirdparty/oidn/core/autoencoder.cpp index 8ae2421fa6..d8da684cb8 100644 --- a/thirdparty/oidn/core/autoencoder.cpp +++ b/thirdparty/oidn/core/autoencoder.cpp @@ -90,12 +90,19 @@ namespace oidn { if (!dirty) return; - { + // -- GODOT start -- + //device->executeTask([&]() + //{ + // GODOT end -- + if (mayiuse(avx512_common)) net = buildNet<16>(); else net = buildNet<8>(); - } + + // GODOT start -- + //}); + // GODOT end -- dirty = false; } @@ -107,8 +114,10 @@ namespace oidn { if (!net) return; - - { + // -- GODOT start -- + //device->executeTask([&]() + //{ + // -- GODOT end -- Progress progress; progress.func = progressFunc; progress.userPtr = progressUserPtr; @@ -154,7 +163,9 @@ namespace oidn { tileIndex++; } } - } + // -- GODOT start -- + //}); + // -- GODOT end -- } void AutoencoderFilter::computeTileSize() @@ -462,8 +473,11 @@ namespace oidn { return std::make_shared<GammaTransferFunction>(); } +// -- GODOT start -- // Godot doesn't need Raytracing filters. Removing them saves space in the weights files. #if 0 +// -- GODOT end -- + // -------------------------------------------------------------------------- // RTFilter // -------------------------------------------------------------------------- @@ -491,7 +505,9 @@ namespace oidn { weightData.hdr_alb = weights::rt_hdr_alb; weightData.hdr_alb_nrm = weights::rt_hdr_alb_nrm; } +// -- GODOT start -- #endif +// -- GODOT end -- // -------------------------------------------------------------------------- // RTLightmapFilter diff --git a/thirdparty/oidn/core/autoencoder.h b/thirdparty/oidn/core/autoencoder.h index 97432f2bbd..98b610844e 100644 --- a/thirdparty/oidn/core/autoencoder.h +++ b/thirdparty/oidn/core/autoencoder.h @@ -93,14 +93,18 @@ namespace oidn { // RTFilter - Generic ray tracing denoiser // -------------------------------------------------------------------------- +// -- GODOT start -- // Godot doesn't need Raytracing filters. Removing them saves space in the weights files. #if 0 +// -- GODOT end -- class RTFilter : public AutoencoderFilter { public: explicit RTFilter(const Ref<Device>& device); }; +// -- GODOT start -- #endif +// -- GODOT end -- // -------------------------------------------------------------------------- // RTLightmapFilter - Ray traced lightmap denoiser diff --git a/thirdparty/oidn/core/common.h b/thirdparty/oidn/core/common.h index 6c87f377bc..a35dd908b4 100644 --- a/thirdparty/oidn/core/common.h +++ b/thirdparty/oidn/core/common.h @@ -27,6 +27,9 @@ #include "common/ref.h" #include "common/exception.h" #include "common/thread.h" +// -- GODOT start -- +//#include "common/tasking.h" +// -- GODOT end -- #include "math.h" namespace oidn { diff --git a/thirdparty/oidn/core/device.cpp b/thirdparty/oidn/core/device.cpp index 0812624bb5..3cd658b9c8 100644 --- a/thirdparty/oidn/core/device.cpp +++ b/thirdparty/oidn/core/device.cpp @@ -29,6 +29,9 @@ namespace oidn { Device::~Device() { + // -- GODOT start -- + //observer.reset(); + // -- GODOT end -- } void Device::setError(Device* device, Error code, const std::string& message) @@ -140,10 +143,29 @@ namespace oidn { if (isCommitted()) throw Exception(Error::InvalidOperation, "device can be committed only once"); + // -- GODOT start -- + #if 0 + // -- GODOT end -- + // Get the optimal thread affinities + if (setAffinity) + { + affinity = std::make_shared<ThreadAffinity>(1, verbose); // one thread per core + if (affinity->getNumThreads() == 0) + affinity.reset(); + } + // Create the task arena - const int maxNumThreads = 1; //affinity ? affinity->getNumThreads() : tbb::this_task_arena::max_concurrency(); + const int maxNumThreads = affinity ? affinity->getNumThreads() : tbb::this_task_arena::max_concurrency(); numThreads = (numThreads > 0) ? min(numThreads, maxNumThreads) : maxNumThreads; - + arena = std::make_shared<tbb::task_arena>(numThreads); + + // Automatically set the thread affinities + if (affinity) + observer = std::make_shared<PinningObserver>(affinity, *arena); + // -- GODOT start -- + #endif + numThreads = 1; + // -- GODOT end -- dirty = false; if (isVerbose()) @@ -177,12 +199,17 @@ namespace oidn { Ref<Filter> filter; +// -- GODOT start -- // Godot doesn't need Raytracing filters. Removing them saves space in the weights files. #if 0 +// -- GODOT end -- if (type == "RT") filter = makeRef<RTFilter>(Ref<Device>(this)); +// -- GODOT start -- +// Godot doesn't need Raytracing filters. Removing them saves space in the weights files. #endif - if (type == "RTLightmap") + if (type == "RTLightmap") +// -- GODOT end -- filter = makeRef<RTLightmapFilter>(Ref<Device>(this)); else throw Exception(Error::InvalidArgument, "unknown filter type"); @@ -199,6 +226,12 @@ namespace oidn { std::cout << " Build : " << getBuildName() << std::endl; std::cout << " Platform: " << getPlatformName() << std::endl; +// -- GODOT start -- +// std::cout << " Tasking :"; +// std::cout << " TBB" << TBB_VERSION_MAJOR << "." << TBB_VERSION_MINOR; +// std::cout << " TBB_header_interface_" << TBB_INTERFACE_VERSION << " TBB_lib_interface_" << tbb::TBB_runtime_interface_version(); +// std::cout << std::endl; +// -- GODOT end -- std::cout << std::endl; } diff --git a/thirdparty/oidn/core/device.h b/thirdparty/oidn/core/device.h index 93a83eb731..d9cfd8541a 100644 --- a/thirdparty/oidn/core/device.h +++ b/thirdparty/oidn/core/device.h @@ -41,6 +41,13 @@ namespace oidn { ErrorFunction errorFunc = nullptr; void* errorUserPtr = nullptr; +// -- GODOT start -- +// // Tasking +// std::shared_ptr<tbb::task_arena> arena; +// std::shared_ptr<PinningObserver> observer; +// std::shared_ptr<ThreadAffinity> affinity; +// -- GODOT end -- + // Parameters int numThreads = 0; // autodetect by default bool setAffinity = true; @@ -61,6 +68,20 @@ namespace oidn { void commit(); +// -- GODOT start -- +// template<typename F> +// void executeTask(F& f) +// { +// arena->execute(f); +// } + +// template<typename F> +// void executeTask(const F& f) +// { +// arena->execute(f); +// } +// -- GODOT end -- + Ref<Buffer> newBuffer(size_t byteSize); Ref<Buffer> newBuffer(void* ptr, size_t byteSize); Ref<Filter> newFilter(const std::string& type); @@ -69,7 +90,10 @@ namespace oidn { __forceinline std::mutex& getMutex() { return mutex; } private: - bool isCommitted() const { return false; } +// -- GODOT start -- + //bool isCommitted() const { return bool(arena); } + bool isCommitted() const { return false; } +// -- GODOT end -- void checkCommitted(); void print(); diff --git a/thirdparty/oidn/core/network.cpp b/thirdparty/oidn/core/network.cpp index 4da32073cd..ed8328c954 100644 --- a/thirdparty/oidn/core/network.cpp +++ b/thirdparty/oidn/core/network.cpp @@ -14,10 +14,12 @@ // limitations under the License. // // ======================================================================== // -#include "network.h" #include "upsample.h" #include "weights_reorder.h" +#include "network.h" +// -- GODOT start -- #include <cstring> +// -- GODOT end -- namespace oidn { diff --git a/thirdparty/oidn/core/transfer_function.cpp b/thirdparty/oidn/core/transfer_function.cpp index a33e3c84bc..487f0a9f75 100644 --- a/thirdparty/oidn/core/transfer_function.cpp +++ b/thirdparty/oidn/core/transfer_function.cpp @@ -24,9 +24,12 @@ namespace oidn { float AutoexposureNode::autoexposure(const Image& color) { assert(color.format == Format::Float3); - return 1.0f; +// -- GODOT start -- +// We don't want to mess with TTB and we don't use autoexposure, so we disable this code +#if 0 +// -- GODOT end -- - /*constexpr float key = 0.18f; + constexpr float key = 0.18f; constexpr float eps = 1e-8f; constexpr int K = 16; // downsampling amount @@ -89,7 +92,11 @@ namespace oidn { tbb::static_partitioner() ); - return (sum.second > 0) ? (key / exp2(sum.first / float(sum.second))) : 1.f;*/ + return (sum.second > 0) ? (key / exp2(sum.first / float(sum.second))) : 1.f; +// -- GODOT start -- +#endif + return 1.0; +// -- GODOT end -- } } // namespace oidn diff --git a/thirdparty/oidn/mkl-dnn/src/cpu/rnn/rnn_reorders.hpp b/thirdparty/oidn/mkl-dnn/src/cpu/rnn/rnn_reorders.hpp index 597c63e3f8..78cdedbae4 100644 --- a/thirdparty/oidn/mkl-dnn/src/cpu/rnn/rnn_reorders.hpp +++ b/thirdparty/oidn/mkl-dnn/src/cpu/rnn/rnn_reorders.hpp @@ -131,7 +131,7 @@ struct rnn_weights_reorder_t : public cpu_primitive_t { return status::success; } - format_tag_t itag_; + format_tag_t itag_ = mkldnn_format_tag_undef; private: void init_scratchpad() { diff --git a/thirdparty/oidn/mkl-dnn/src/cpu/simple_concat.hpp b/thirdparty/oidn/mkl-dnn/src/cpu/simple_concat.hpp index 5177275452..057cc3c4c7 100644 --- a/thirdparty/oidn/mkl-dnn/src/cpu/simple_concat.hpp +++ b/thirdparty/oidn/mkl-dnn/src/cpu/simple_concat.hpp @@ -96,9 +96,9 @@ struct simple_concat_t: public cpu_primitive_t { return status::success; } - int perm_[MKLDNN_MAX_NDIMS]; - int iperm_[MKLDNN_MAX_NDIMS]; - dims_t blocks_; + int perm_[MKLDNN_MAX_NDIMS] {}; + int iperm_[MKLDNN_MAX_NDIMS] {}; + dims_t blocks_ {}; dim_t nelems_to_concat(const memory_desc_wrapper &data_d) const { const int ndims = data_d.ndims(); diff --git a/thirdparty/oidn/patches/godot-changes-c58c5216.patch b/thirdparty/oidn/patches/godot-changes-c58c5216.patch new file mode 100644 index 0000000000..6a54703064 --- /dev/null +++ b/thirdparty/oidn/patches/godot-changes-c58c5216.patch @@ -0,0 +1,307 @@ +diff --git a/common/platform.h b/common/platform.h +index be14bc7..9373b61 100644 +--- a/common/platform.h ++++ b/common/platform.h +@@ -19,7 +19,7 @@ + #if defined(_WIN32) + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX +- #include <Windows.h> ++ #include <windows.h> + #elif defined(__APPLE__) + #include <sys/sysctl.h> + #endif +@@ -129,4 +129,3 @@ namespace oidn { + std::string getBuildName(); + + } // namespace oidn +- +diff --git a/core/autoencoder.cpp b/core/autoencoder.cpp +index d6915e6..d8da684 100644 +--- a/core/autoencoder.cpp ++++ b/core/autoencoder.cpp +@@ -90,13 +90,19 @@ namespace oidn { + if (!dirty) + return; + +- device->executeTask([&]() +- { ++ // -- GODOT start -- ++ //device->executeTask([&]() ++ //{ ++ // GODOT end -- ++ + if (mayiuse(avx512_common)) + net = buildNet<16>(); + else + net = buildNet<8>(); +- }); ++ ++ // GODOT start -- ++ //}); ++ // GODOT end -- + + dirty = false; + } +@@ -108,9 +114,10 @@ namespace oidn { + + if (!net) + return; +- +- device->executeTask([&]() +- { ++ // -- GODOT start -- ++ //device->executeTask([&]() ++ //{ ++ // -- GODOT end -- + Progress progress; + progress.func = progressFunc; + progress.userPtr = progressUserPtr; +@@ -156,7 +163,9 @@ namespace oidn { + tileIndex++; + } + } +- }); ++ // -- GODOT start -- ++ //}); ++ // -- GODOT end -- + } + + void AutoencoderFilter::computeTileSize() +@@ -464,6 +473,11 @@ namespace oidn { + return std::make_shared<GammaTransferFunction>(); + } + ++// -- GODOT start -- ++// Godot doesn't need Raytracing filters. Removing them saves space in the weights files. ++#if 0 ++// -- GODOT end -- ++ + // -------------------------------------------------------------------------- + // RTFilter + // -------------------------------------------------------------------------- +@@ -491,6 +505,9 @@ namespace oidn { + weightData.hdr_alb = weights::rt_hdr_alb; + weightData.hdr_alb_nrm = weights::rt_hdr_alb_nrm; + } ++// -- GODOT start -- ++#endif ++// -- GODOT end -- + + // -------------------------------------------------------------------------- + // RTLightmapFilter +diff --git a/core/autoencoder.h b/core/autoencoder.h +index c199052..98b6108 100644 +--- a/core/autoencoder.h ++++ b/core/autoencoder.h +@@ -93,11 +93,18 @@ namespace oidn { + // RTFilter - Generic ray tracing denoiser + // -------------------------------------------------------------------------- + ++// -- GODOT start -- ++// Godot doesn't need Raytracing filters. Removing them saves space in the weights files. ++#if 0 ++// -- GODOT end -- + class RTFilter : public AutoencoderFilter + { + public: + explicit RTFilter(const Ref<Device>& device); + }; ++// -- GODOT start -- ++#endif ++// -- GODOT end -- + + // -------------------------------------------------------------------------- + // RTLightmapFilter - Ray traced lightmap denoiser +diff --git a/core/common.h b/core/common.h +index a3a7e8a..a35dd90 100644 +--- a/core/common.h ++++ b/core/common.h +@@ -27,7 +27,9 @@ + #include "common/ref.h" + #include "common/exception.h" + #include "common/thread.h" +-#include "common/tasking.h" ++// -- GODOT start -- ++//#include "common/tasking.h" ++// -- GODOT end -- + #include "math.h" + + namespace oidn { +diff --git a/core/device.cpp b/core/device.cpp +index c455695..3cd658b 100644 +--- a/core/device.cpp ++++ b/core/device.cpp +@@ -29,7 +29,9 @@ namespace oidn { + + Device::~Device() + { +- observer.reset(); ++ // -- GODOT start -- ++ //observer.reset(); ++ // -- GODOT end -- + } + + void Device::setError(Device* device, Error code, const std::string& message) +@@ -141,6 +143,9 @@ namespace oidn { + if (isCommitted()) + throw Exception(Error::InvalidOperation, "device can be committed only once"); + ++ // -- GODOT start -- ++ #if 0 ++ // -- GODOT end -- + // Get the optimal thread affinities + if (setAffinity) + { +@@ -157,7 +162,10 @@ namespace oidn { + // Automatically set the thread affinities + if (affinity) + observer = std::make_shared<PinningObserver>(affinity, *arena); +- ++ // -- GODOT start -- ++ #endif ++ numThreads = 1; ++ // -- GODOT end -- + dirty = false; + + if (isVerbose()) +@@ -191,9 +199,17 @@ namespace oidn { + + Ref<Filter> filter; + ++// -- GODOT start -- ++// Godot doesn't need Raytracing filters. Removing them saves space in the weights files. ++#if 0 ++// -- GODOT end -- + if (type == "RT") + filter = makeRef<RTFilter>(Ref<Device>(this)); +- else if (type == "RTLightmap") ++// -- GODOT start -- ++// Godot doesn't need Raytracing filters. Removing them saves space in the weights files. ++#endif ++ if (type == "RTLightmap") ++// -- GODOT end -- + filter = makeRef<RTLightmapFilter>(Ref<Device>(this)); + else + throw Exception(Error::InvalidArgument, "unknown filter type"); +@@ -210,11 +226,12 @@ namespace oidn { + std::cout << " Build : " << getBuildName() << std::endl; + std::cout << " Platform: " << getPlatformName() << std::endl; + +- std::cout << " Tasking :"; +- std::cout << " TBB" << TBB_VERSION_MAJOR << "." << TBB_VERSION_MINOR; +- std::cout << " TBB_header_interface_" << TBB_INTERFACE_VERSION << " TBB_lib_interface_" << tbb::TBB_runtime_interface_version(); +- std::cout << std::endl; +- ++// -- GODOT start -- ++// std::cout << " Tasking :"; ++// std::cout << " TBB" << TBB_VERSION_MAJOR << "." << TBB_VERSION_MINOR; ++// std::cout << " TBB_header_interface_" << TBB_INTERFACE_VERSION << " TBB_lib_interface_" << tbb::TBB_runtime_interface_version(); ++// std::cout << std::endl; ++// -- GODOT end -- + std::cout << std::endl; + } + +diff --git a/core/device.h b/core/device.h +index c2df714..d9cfd85 100644 +--- a/core/device.h ++++ b/core/device.h +@@ -41,10 +41,12 @@ namespace oidn { + ErrorFunction errorFunc = nullptr; + void* errorUserPtr = nullptr; + +- // Tasking +- std::shared_ptr<tbb::task_arena> arena; +- std::shared_ptr<PinningObserver> observer; +- std::shared_ptr<ThreadAffinity> affinity; ++// -- GODOT start -- ++// // Tasking ++// std::shared_ptr<tbb::task_arena> arena; ++// std::shared_ptr<PinningObserver> observer; ++// std::shared_ptr<ThreadAffinity> affinity; ++// -- GODOT end -- + + // Parameters + int numThreads = 0; // autodetect by default +@@ -66,17 +68,19 @@ namespace oidn { + + void commit(); + +- template<typename F> +- void executeTask(F& f) +- { +- arena->execute(f); +- } ++// -- GODOT start -- ++// template<typename F> ++// void executeTask(F& f) ++// { ++// arena->execute(f); ++// } + +- template<typename F> +- void executeTask(const F& f) +- { +- arena->execute(f); +- } ++// template<typename F> ++// void executeTask(const F& f) ++// { ++// arena->execute(f); ++// } ++// -- GODOT end -- + + Ref<Buffer> newBuffer(size_t byteSize); + Ref<Buffer> newBuffer(void* ptr, size_t byteSize); +@@ -86,7 +90,10 @@ namespace oidn { + __forceinline std::mutex& getMutex() { return mutex; } + + private: +- bool isCommitted() const { return bool(arena); } ++// -- GODOT start -- ++ //bool isCommitted() const { return bool(arena); } ++ bool isCommitted() const { return false; } ++// -- GODOT end -- + void checkCommitted(); + + void print(); +diff --git a/core/network.cpp b/core/network.cpp +index 8c2de09..ed8328c 100644 +--- a/core/network.cpp ++++ b/core/network.cpp +@@ -17,6 +17,9 @@ + #include "upsample.h" + #include "weights_reorder.h" + #include "network.h" ++// -- GODOT start -- ++#include <cstring> ++// -- GODOT end -- + + namespace oidn { + +diff --git a/core/transfer_function.cpp b/core/transfer_function.cpp +index 601f814..487f0a9 100644 +--- a/core/transfer_function.cpp ++++ b/core/transfer_function.cpp +@@ -24,6 +24,10 @@ namespace oidn { + float AutoexposureNode::autoexposure(const Image& color) + { + assert(color.format == Format::Float3); ++// -- GODOT start -- ++// We don't want to mess with TTB and we don't use autoexposure, so we disable this code ++#if 0 ++// -- GODOT end -- + + constexpr float key = 0.18f; + constexpr float eps = 1e-8f; +@@ -89,6 +93,10 @@ namespace oidn { + ); + + return (sum.second > 0) ? (key / exp2(sum.first / float(sum.second))) : 1.f; ++// -- GODOT start -- ++#endif ++ return 1.0; ++// -- GODOT end -- + } + + } // namespace oidn diff --git a/thirdparty/oidn/patches/mkl-dnn-fix-vs2017-build.patch b/thirdparty/oidn/patches/mkl-dnn-fix-vs2017-build.patch new file mode 100644 index 0000000000..50d94ebffa --- /dev/null +++ b/thirdparty/oidn/patches/mkl-dnn-fix-vs2017-build.patch @@ -0,0 +1,45 @@ +Rediffed by @akien-mga to match oidn 1.1.0 source. + +From 1e42e6db81e1a5270ecc0191c5385ce7e7d978e9 Mon Sep 17 00:00:00 2001 +From: Jeremy Wong <jmw@netvigator.com> +Date: Wed, 11 Sep 2019 04:46:53 +0800 +Subject: [PATCH] src: initialize members in some structures to prevent compile + errors with VS2017 + +addresses "error C3615: constexpr function '...' cannot result in a constant expression" with VS2017 +--- + src/cpu/rnn/rnn_reorders.hpp | 2 +- + src/cpu/simple_concat.hpp | 6 +++--- + src/cpu/simple_sum.hpp | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/thirdparty/oidn/mkl-dnn/src/cpu/rnn/rnn_reorders.hpp b/thirdparty/oidn/mkl-dnn/src/cpu/rnn/rnn_reorders.hpp +index 597c63e3f8..ae1551390a 100644 +--- a/thirdparty/oidn/mkl-dnn/src/cpu/rnn/rnn_reorders.hpp ++++ b/thirdparty/oidn/mkl-dnn/src/cpu/rnn/rnn_reorders.hpp +@@ -131,7 +131,7 @@ struct rnn_weights_reorder_t : public cpu_primitive_t { + return status::success; + } + +- format_tag_t itag_; ++ format_tag_t itag_ = mkldnn_format_tag_undef; + + private: + void init_scratchpad() { +diff --git a/thirdparty/oidn/mkl-dnn/src/cpu/simple_concat.hpp b/thirdparty/oidn/mkl-dnn/src/cpu/simple_concat.hpp +index 5177275452..057cc3c4c7 100644 +--- a/thirdparty/oidn/mkl-dnn/src/cpu/simple_concat.hpp ++++ b/thirdparty/oidn/mkl-dnn/src/cpu/simple_concat.hpp +@@ -96,9 +96,9 @@ struct simple_concat_t: public cpu_primitive_t { + return status::success; + } + +- int perm_[MKLDNN_MAX_NDIMS]; +- int iperm_[MKLDNN_MAX_NDIMS]; +- dims_t blocks_; ++ int perm_[MKLDNN_MAX_NDIMS] {}; ++ int iperm_[MKLDNN_MAX_NDIMS] {}; ++ dims_t blocks_ {}; + + dim_t nelems_to_concat(const memory_desc_wrapper &data_d) const { + const int ndims = data_d.ndims(); diff --git a/thirdparty/oidn/weights/LICENSE.txt b/thirdparty/oidn/weights/LICENSE.txt new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/thirdparty/oidn/weights/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. |