diff options
Diffstat (limited to 'core')
326 files changed, 30731 insertions, 24759 deletions
diff --git a/core/SCsub b/core/SCsub index 0ab4f11d87..21829553a7 100644 --- a/core/SCsub +++ b/core/SCsub @@ -3,8 +3,6 @@ Import("env") import core_builders -import make_binders -from platform_methods import run_in_subprocess env.core_sources = [] @@ -27,7 +25,7 @@ if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ: txts = "0x" + e[i * 2 : i * 2 + 2] try: int(txts, 16) - except: + except Exception: ec_valid = False txt += txts if not ec_valid: @@ -36,10 +34,13 @@ if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ: # NOTE: It is safe to generate this file here, since this is still executed serially with open("script_encryption_key.gen.cpp", "w") as f: - f.write('#include "core/project_settings.h"\nuint8_t script_encryption_key[32]={' + txt + "};\n") + f.write('#include "core/config/project_settings.h"\nuint8_t script_encryption_key[32]={' + txt + "};\n") # Add required thirdparty code. + +thirdparty_obj = [] + env_thirdparty = env.Clone() env_thirdparty.disable_warnings() @@ -49,15 +50,15 @@ thirdparty_misc_dir = "#thirdparty/misc/" thirdparty_misc_sources = [ # C sources "fastlz.c", + "r128.c", "smaz.c", # C++ sources - "hq2x.cpp", "pcg.cpp", - "triangulator.cpp", + "polypartition.cpp", "clipper.cpp", ] thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources] -env_thirdparty.add_source_files(env.core_sources, thirdparty_misc_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_misc_sources) # Zlib library, can be unbundled if env["builtin_zlib"]: @@ -83,18 +84,14 @@ if env["builtin_zlib"]: if env["target"] == "debug": env_thirdparty.Append(CPPDEFINES=["ZLIB_DEBUG"]) - env_thirdparty.add_source_files(env.core_sources, thirdparty_zlib_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_zlib_sources) # Minizip library, could be unbundled in theory # However, our version has some custom modifications, so it won't compile with the system one thirdparty_minizip_dir = "#thirdparty/minizip/" -thirdparty_minizip_sources = [ - "ioapi.c", - "unzip.c", - "zip.c", -] +thirdparty_minizip_sources = ["ioapi.c", "unzip.c", "zip.c"] thirdparty_minizip_sources = [thirdparty_minizip_dir + file for file in thirdparty_minizip_sources] -env_thirdparty.add_source_files(env.core_sources, thirdparty_minizip_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_minizip_sources) # Zstd library, can be unbundled in theory # though we currently use some private symbols @@ -122,6 +119,7 @@ if env["builtin_zstd"]: "compress/zstdmt_compress.c", "compress/zstd_compress_literals.c", "compress/zstd_compress_sequences.c", + "compress/zstd_compress_superblock.c", "decompress/huf_decompress.c", "decompress/zstd_ddict.c", "decompress/zstd_decompress_block.c", @@ -135,10 +133,14 @@ if env["builtin_zstd"]: # Also needed in main env includes will trigger warnings env.Append(CPPDEFINES=["ZSTD_STATIC_LINKING_ONLY"]) - env_thirdparty.add_source_files(env.core_sources, thirdparty_zstd_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_zstd_sources) + +env.core_sources += thirdparty_obj + + +# Godot source files -# Godot's own sources env.add_source_files(env.core_sources, "*.cpp") # Certificates @@ -149,28 +151,27 @@ env.Depends( env.CommandNoCache( "#core/io/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", - run_in_subprocess(core_builders.make_certs_header), -) - -# Make binders -env.CommandNoCache( - ["method_bind.gen.inc", "method_bind_ext.gen.inc", "method_bind_free_func.gen.inc"], - "make_binders.py", - run_in_subprocess(make_binders.run), + env.Run(core_builders.make_certs_header, "Building ca-certificates header."), ) # Authors env.Depends("#core/authors.gen.h", "../AUTHORS.md") -env.CommandNoCache("#core/authors.gen.h", "../AUTHORS.md", run_in_subprocess(core_builders.make_authors_header)) +env.CommandNoCache( + "#core/authors.gen.h", "../AUTHORS.md", env.Run(core_builders.make_authors_header, "Generating authors header.") +) # Donors env.Depends("#core/donors.gen.h", "../DONORS.md") -env.CommandNoCache("#core/donors.gen.h", "../DONORS.md", run_in_subprocess(core_builders.make_donors_header)) +env.CommandNoCache( + "#core/donors.gen.h", "../DONORS.md", env.Run(core_builders.make_donors_header, "Generating donors header.") +) # License env.Depends("#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"]) env.CommandNoCache( - "#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"], run_in_subprocess(core_builders.make_license_header) + "#core/license.gen.h", + ["../COPYRIGHT.txt", "../LICENSE.txt"], + env.Run(core_builders.make_license_header, "Generating license header."), ) # Chain load SCsubs @@ -180,9 +181,17 @@ SConscript("crypto/SCsub") SConscript("io/SCsub") SConscript("debugger/SCsub") SConscript("input/SCsub") -SConscript("bind/SCsub") +SConscript("variant/SCsub") +SConscript("object/SCsub") +SConscript("templates/SCsub") +SConscript("string/SCsub") +SConscript("config/SCsub") +SConscript("error/SCsub") # Build it all as a library lib = env.add_library("core", env.core_sources) env.Prepend(LIBS=[lib]) + +# Needed to force rebuilding the core files when the thirdparty code is updated. +env.Depends(lib, thirdparty_obj) diff --git a/core/bind/SCsub b/core/bind/SCsub deleted file mode 100644 index 19a6549225..0000000000 --- a/core/bind/SCsub +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -env.add_source_files(env.core_sources, "*.cpp") diff --git a/core/color_names.inc b/core/color_names.inc deleted file mode 100644 index 428a8473fe..0000000000 --- a/core/color_names.inc +++ /dev/null @@ -1,153 +0,0 @@ -// Names from https://en.wikipedia.org/wiki/X11_color_names -#include "core/map.h" - -static Map<String, Color> _named_colors; -static void _populate_named_colors() { - if (!_named_colors.empty()) return; - _named_colors.insert("aliceblue", Color(0.94, 0.97, 1.00)); - _named_colors.insert("antiquewhite", Color(0.98, 0.92, 0.84)); - _named_colors.insert("aqua", Color(0.00, 1.00, 1.00)); - _named_colors.insert("aquamarine", Color(0.50, 1.00, 0.83)); - _named_colors.insert("azure", Color(0.94, 1.00, 1.00)); - _named_colors.insert("beige", Color(0.96, 0.96, 0.86)); - _named_colors.insert("bisque", Color(1.00, 0.89, 0.77)); - _named_colors.insert("black", Color(0.00, 0.00, 0.00)); - _named_colors.insert("blanchedalmond", Color(1.00, 0.92, 0.80)); - _named_colors.insert("blue", Color(0.00, 0.00, 1.00)); - _named_colors.insert("blueviolet", Color(0.54, 0.17, 0.89)); - _named_colors.insert("brown", Color(0.65, 0.16, 0.16)); - _named_colors.insert("burlywood", Color(0.87, 0.72, 0.53)); - _named_colors.insert("cadetblue", Color(0.37, 0.62, 0.63)); - _named_colors.insert("chartreuse", Color(0.50, 1.00, 0.00)); - _named_colors.insert("chocolate", Color(0.82, 0.41, 0.12)); - _named_colors.insert("coral", Color(1.00, 0.50, 0.31)); - _named_colors.insert("cornflower", Color(0.39, 0.58, 0.93)); - _named_colors.insert("cornsilk", Color(1.00, 0.97, 0.86)); - _named_colors.insert("crimson", Color(0.86, 0.08, 0.24)); - _named_colors.insert("cyan", Color(0.00, 1.00, 1.00)); - _named_colors.insert("darkblue", Color(0.00, 0.00, 0.55)); - _named_colors.insert("darkcyan", Color(0.00, 0.55, 0.55)); - _named_colors.insert("darkgoldenrod", Color(0.72, 0.53, 0.04)); - _named_colors.insert("darkgray", Color(0.66, 0.66, 0.66)); - _named_colors.insert("darkgreen", Color(0.00, 0.39, 0.00)); - _named_colors.insert("darkkhaki", Color(0.74, 0.72, 0.42)); - _named_colors.insert("darkmagenta", Color(0.55, 0.00, 0.55)); - _named_colors.insert("darkolivegreen", Color(0.33, 0.42, 0.18)); - _named_colors.insert("darkorange", Color(1.00, 0.55, 0.00)); - _named_colors.insert("darkorchid", Color(0.60, 0.20, 0.80)); - _named_colors.insert("darkred", Color(0.55, 0.00, 0.00)); - _named_colors.insert("darksalmon", Color(0.91, 0.59, 0.48)); - _named_colors.insert("darkseagreen", Color(0.56, 0.74, 0.56)); - _named_colors.insert("darkslateblue", Color(0.28, 0.24, 0.55)); - _named_colors.insert("darkslategray", Color(0.18, 0.31, 0.31)); - _named_colors.insert("darkturquoise", Color(0.00, 0.81, 0.82)); - _named_colors.insert("darkviolet", Color(0.58, 0.00, 0.83)); - _named_colors.insert("deeppink", Color(1.00, 0.08, 0.58)); - _named_colors.insert("deepskyblue", Color(0.00, 0.75, 1.00)); - _named_colors.insert("dimgray", Color(0.41, 0.41, 0.41)); - _named_colors.insert("dodgerblue", Color(0.12, 0.56, 1.00)); - _named_colors.insert("firebrick", Color(0.70, 0.13, 0.13)); - _named_colors.insert("floralwhite", Color(1.00, 0.98, 0.94)); - _named_colors.insert("forestgreen", Color(0.13, 0.55, 0.13)); - _named_colors.insert("fuchsia", Color(1.00, 0.00, 1.00)); - _named_colors.insert("gainsboro", Color(0.86, 0.86, 0.86)); - _named_colors.insert("ghostwhite", Color(0.97, 0.97, 1.00)); - _named_colors.insert("gold", Color(1.00, 0.84, 0.00)); - _named_colors.insert("goldenrod", Color(0.85, 0.65, 0.13)); - _named_colors.insert("gray", Color(0.75, 0.75, 0.75)); - _named_colors.insert("webgray", Color(0.50, 0.50, 0.50)); - _named_colors.insert("green", Color(0.00, 1.00, 0.00)); - _named_colors.insert("webgreen", Color(0.00, 0.50, 0.00)); - _named_colors.insert("greenyellow", Color(0.68, 1.00, 0.18)); - _named_colors.insert("honeydew", Color(0.94, 1.00, 0.94)); - _named_colors.insert("hotpink", Color(1.00, 0.41, 0.71)); - _named_colors.insert("indianred", Color(0.80, 0.36, 0.36)); - _named_colors.insert("indigo", Color(0.29, 0.00, 0.51)); - _named_colors.insert("ivory", Color(1.00, 1.00, 0.94)); - _named_colors.insert("khaki", Color(0.94, 0.90, 0.55)); - _named_colors.insert("lavender", Color(0.90, 0.90, 0.98)); - _named_colors.insert("lavenderblush", Color(1.00, 0.94, 0.96)); - _named_colors.insert("lawngreen", Color(0.49, 0.99, 0.00)); - _named_colors.insert("lemonchiffon", Color(1.00, 0.98, 0.80)); - _named_colors.insert("lightblue", Color(0.68, 0.85, 0.90)); - _named_colors.insert("lightcoral", Color(0.94, 0.50, 0.50)); - _named_colors.insert("lightcyan", Color(0.88, 1.00, 1.00)); - _named_colors.insert("lightgoldenrod", Color(0.98, 0.98, 0.82)); - _named_colors.insert("lightgray", Color(0.83, 0.83, 0.83)); - _named_colors.insert("lightgreen", Color(0.56, 0.93, 0.56)); - _named_colors.insert("lightpink", Color(1.00, 0.71, 0.76)); - _named_colors.insert("lightsalmon", Color(1.00, 0.63, 0.48)); - _named_colors.insert("lightseagreen", Color(0.13, 0.70, 0.67)); - _named_colors.insert("lightskyblue", Color(0.53, 0.81, 0.98)); - _named_colors.insert("lightslategray", Color(0.47, 0.53, 0.60)); - _named_colors.insert("lightsteelblue", Color(0.69, 0.77, 0.87)); - _named_colors.insert("lightyellow", Color(1.00, 1.00, 0.88)); - _named_colors.insert("lime", Color(0.00, 1.00, 0.00)); - _named_colors.insert("limegreen", Color(0.20, 0.80, 0.20)); - _named_colors.insert("linen", Color(0.98, 0.94, 0.90)); - _named_colors.insert("magenta", Color(1.00, 0.00, 1.00)); - _named_colors.insert("maroon", Color(0.69, 0.19, 0.38)); - _named_colors.insert("webmaroon", Color(0.50, 0.00, 0.00)); - _named_colors.insert("mediumaquamarine", Color(0.40, 0.80, 0.67)); - _named_colors.insert("mediumblue", Color(0.00, 0.00, 0.80)); - _named_colors.insert("mediumorchid", Color(0.73, 0.33, 0.83)); - _named_colors.insert("mediumpurple", Color(0.58, 0.44, 0.86)); - _named_colors.insert("mediumseagreen", Color(0.24, 0.70, 0.44)); - _named_colors.insert("mediumslateblue", Color(0.48, 0.41, 0.93)); - _named_colors.insert("mediumspringgreen", Color(0.00, 0.98, 0.60)); - _named_colors.insert("mediumturquoise", Color(0.28, 0.82, 0.80)); - _named_colors.insert("mediumvioletred", Color(0.78, 0.08, 0.52)); - _named_colors.insert("midnightblue", Color(0.10, 0.10, 0.44)); - _named_colors.insert("mintcream", Color(0.96, 1.00, 0.98)); - _named_colors.insert("mistyrose", Color(1.00, 0.89, 0.88)); - _named_colors.insert("moccasin", Color(1.00, 0.89, 0.71)); - _named_colors.insert("navajowhite", Color(1.00, 0.87, 0.68)); - _named_colors.insert("navyblue", Color(0.00, 0.00, 0.50)); - _named_colors.insert("oldlace", Color(0.99, 0.96, 0.90)); - _named_colors.insert("olive", Color(0.50, 0.50, 0.00)); - _named_colors.insert("olivedrab", Color(0.42, 0.56, 0.14)); - _named_colors.insert("orange", Color(1.00, 0.65, 0.00)); - _named_colors.insert("orangered", Color(1.00, 0.27, 0.00)); - _named_colors.insert("orchid", Color(0.85, 0.44, 0.84)); - _named_colors.insert("palegoldenrod", Color(0.93, 0.91, 0.67)); - _named_colors.insert("palegreen", Color(0.60, 0.98, 0.60)); - _named_colors.insert("paleturquoise", Color(0.69, 0.93, 0.93)); - _named_colors.insert("palevioletred", Color(0.86, 0.44, 0.58)); - _named_colors.insert("papayawhip", Color(1.00, 0.94, 0.84)); - _named_colors.insert("peachpuff", Color(1.00, 0.85, 0.73)); - _named_colors.insert("peru", Color(0.80, 0.52, 0.25)); - _named_colors.insert("pink", Color(1.00, 0.75, 0.80)); - _named_colors.insert("plum", Color(0.87, 0.63, 0.87)); - _named_colors.insert("powderblue", Color(0.69, 0.88, 0.90)); - _named_colors.insert("purple", Color(0.63, 0.13, 0.94)); - _named_colors.insert("webpurple", Color(0.50, 0.00, 0.50)); - _named_colors.insert("rebeccapurple", Color(0.40, 0.20, 0.60)); - _named_colors.insert("red", Color(1.00, 0.00, 0.00)); - _named_colors.insert("rosybrown", Color(0.74, 0.56, 0.56)); - _named_colors.insert("royalblue", Color(0.25, 0.41, 0.88)); - _named_colors.insert("saddlebrown", Color(0.55, 0.27, 0.07)); - _named_colors.insert("salmon", Color(0.98, 0.50, 0.45)); - _named_colors.insert("sandybrown", Color(0.96, 0.64, 0.38)); - _named_colors.insert("seagreen", Color(0.18, 0.55, 0.34)); - _named_colors.insert("seashell", Color(1.00, 0.96, 0.93)); - _named_colors.insert("sienna", Color(0.63, 0.32, 0.18)); - _named_colors.insert("silver", Color(0.75, 0.75, 0.75)); - _named_colors.insert("skyblue", Color(0.53, 0.81, 0.92)); - _named_colors.insert("slateblue", Color(0.42, 0.35, 0.80)); - _named_colors.insert("slategray", Color(0.44, 0.50, 0.56)); - _named_colors.insert("snow", Color(1.00, 0.98, 0.98)); - _named_colors.insert("springgreen", Color(0.00, 1.00, 0.50)); - _named_colors.insert("steelblue", Color(0.27, 0.51, 0.71)); - _named_colors.insert("tan", Color(0.82, 0.71, 0.55)); - _named_colors.insert("teal", Color(0.00, 0.50, 0.50)); - _named_colors.insert("thistle", Color(0.85, 0.75, 0.85)); - _named_colors.insert("tomato", Color(1.00, 0.39, 0.28)); - _named_colors.insert("turquoise", Color(0.25, 0.88, 0.82)); - _named_colors.insert("transparent", Color(1.00, 1.00, 1.00, 0.00)); - _named_colors.insert("violet", Color(0.93, 0.51, 0.93)); - _named_colors.insert("wheat", Color(0.96, 0.87, 0.70)); - _named_colors.insert("white", Color(1.00, 1.00, 1.00)); - _named_colors.insert("whitesmoke", Color(0.96, 0.96, 0.96)); - _named_colors.insert("yellow", Color(1.00, 1.00, 0.00)); - _named_colors.insert("yellowgreen", Color(0.60, 0.80, 0.20)); -} diff --git a/core/config/SCsub b/core/config/SCsub new file mode 100644 index 0000000000..bf70285490 --- /dev/null +++ b/core/config/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +env_config = env.Clone() + +env_config.add_source_files(env.core_sources, "*.cpp") diff --git a/core/engine.cpp b/core/config/engine.cpp index 5361e09a8a..2360d66438 100644 --- a/core/engine.cpp +++ b/core/config/engine.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -37,18 +37,18 @@ #include "core/version_hash.gen.h" void Engine::set_iterations_per_second(int p_ips) { - ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0."); ips = p_ips; } -int Engine::get_iterations_per_second() const { +int Engine::get_iterations_per_second() const { return ips; } void Engine::set_physics_jitter_fix(float p_threshold) { - if (p_threshold < 0) + if (p_threshold < 0) { p_threshold = 0; + } physics_jitter_fix = p_threshold; } @@ -65,32 +65,26 @@ int Engine::get_target_fps() const { } uint64_t Engine::get_frames_drawn() { - return frames_drawn; } void Engine::set_frame_delay(uint32_t p_msec) { - _frame_delay = p_msec; } uint32_t Engine::get_frame_delay() const { - return _frame_delay; } void Engine::set_time_scale(float p_scale) { - _time_scale = p_scale; } float Engine::get_time_scale() const { - return _time_scale; } Dictionary Engine::get_version_info() const { - Dictionary dict; dict["major"] = VERSION_MAJOR; dict["minor"] = VERSION_MINOR; @@ -104,8 +98,9 @@ Dictionary Engine::get_version_info() const { dict["hash"] = hash.length() == 0 ? String("unknown") : hash; String stringver = String(dict["major"]) + "." + String(dict["minor"]); - if ((int)dict["patch"] != 0) + if ((int)dict["patch"] != 0) { stringver += "." + String(dict["patch"]); + } stringver += "-" + String(dict["status"]) + " (" + String(dict["build"]) + ")"; dict["string"] = stringver; @@ -163,8 +158,10 @@ Array Engine::get_copyright_info() const { Dictionary Engine::get_donor_info() const { Dictionary donors; - donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLAT); + donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLATINUM); donors["gold_sponsors"] = array_from_info(DONORS_SPONSOR_GOLD); + donors["silver_sponsors"] = array_from_info(DONORS_SPONSOR_SILVER); + donors["bronze_sponsors"] = array_from_info(DONORS_SPONSOR_BRONZE); donors["mini_sponsors"] = array_from_info(DONORS_SPONSOR_MINI); donors["gold_donors"] = array_from_info(DONORS_GOLD); donors["silver_donors"] = array_from_info(DONORS_SILVER); @@ -184,28 +181,33 @@ String Engine::get_license_text() const { return String(GODOT_LICENSE_TEXT); } -void Engine::add_singleton(const Singleton &p_singleton) { +bool Engine::is_abort_on_gpu_errors_enabled() const { + return abort_on_gpu_errors; +} +bool Engine::is_validation_layers_enabled() const { + return use_validation_layers; +} + +void Engine::add_singleton(const Singleton &p_singleton) { singletons.push_back(p_singleton); singleton_ptrs[p_singleton.name] = p_singleton.ptr; } Object *Engine::get_singleton_object(const String &p_name) const { - const Map<StringName, Object *>::Element *E = singleton_ptrs.find(p_name); ERR_FAIL_COND_V_MSG(!E, nullptr, "Failed to retrieve non-existent singleton '" + p_name + "'."); return E->get(); -}; +} bool Engine::has_singleton(const String &p_name) const { - return singleton_ptrs.has(p_name); -}; +} void Engine::get_singletons(List<Singleton> *p_singletons) { - - for (List<Singleton>::Element *E = singletons.front(); E; E = E->next()) + for (List<Singleton>::Element *E = singletons.front(); E; E = E->next()) { p_singletons->push_back(E->get()); + } } Engine *Engine::singleton = nullptr; @@ -214,26 +216,17 @@ Engine *Engine::get_singleton() { return singleton; } -bool Engine::is_abort_on_gpu_errors_enabled() const { - return abort_on_gpu_errors; -} Engine::Engine() { - singleton = this; - frames_drawn = 0; - ips = 60; - physics_jitter_fix = 0.5; - _physics_interpolation_fraction = 0.0f; - _frame_delay = 0; - _fps = 1; - _target_fps = 0; - _time_scale = 1.0; - _pixel_snap = false; - _physics_frames = 0; - _idle_frames = 0; - _in_physics = false; - _frame_ticks = 0; - _frame_step = 0; - editor_hint = false; - abort_on_gpu_errors = false; +} + +Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr) : + name(p_name), + ptr(p_ptr) { +#ifdef DEBUG_ENABLED + Reference *ref = Object::cast_to<Reference>(p_ptr); + if (ref && !ref->is_referenced()) { + WARN_PRINT("You must use Ref<> to ensure the lifetime of a Reference object intended to be used as a singleton."); + } +#endif } diff --git a/core/engine.h b/core/config/engine.h index 8512779d4c..a9080e3dfd 100644 --- a/core/engine.h +++ b/core/config/engine.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,48 +31,44 @@ #ifndef ENGINE_H #define ENGINE_H -#include "core/list.h" #include "core/os/main_loop.h" -#include "core/ustring.h" -#include "core/vector.h" +#include "core/string/ustring.h" +#include "core/templates/list.h" +#include "core/templates/vector.h" class Engine { - public: struct Singleton { StringName name; Object *ptr; - Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr) : - name(p_name), - ptr(p_ptr) { - } + Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr); }; private: friend class Main; - uint64_t frames_drawn; - uint32_t _frame_delay; - uint64_t _frame_ticks; - float _frame_step; + uint64_t frames_drawn = 0; + uint32_t _frame_delay = 0; + uint64_t _frame_ticks = 0; + float _process_step = 0; - int ips; - float physics_jitter_fix; - float _fps; - int _target_fps; - float _time_scale; - bool _pixel_snap; - uint64_t _physics_frames; - float _physics_interpolation_fraction; - bool abort_on_gpu_errors; + int ips = 60; + float physics_jitter_fix = 0.5; + float _fps = 1; + int _target_fps = 0; + float _time_scale = 1.0; + uint64_t _physics_frames = 0; + float _physics_interpolation_fraction = 0.0f; + bool abort_on_gpu_errors = false; + bool use_validation_layers = false; - uint64_t _idle_frames; - bool _in_physics; + uint64_t _process_frames = 0; + bool _in_physics = false; List<Singleton> singletons; Map<StringName, Object *> singleton_ptrs; - bool editor_hint; + bool editor_hint = false; static Engine *singleton; @@ -93,10 +89,10 @@ public: uint64_t get_frames_drawn(); uint64_t get_physics_frames() const { return _physics_frames; } - uint64_t get_idle_frames() const { return _idle_frames; } + uint64_t get_process_frames() const { return _process_frames; } bool is_in_physics_frame() const { return _in_physics; } - uint64_t get_idle_frame_ticks() const { return _frame_ticks; } - float get_idle_frame_step() const { return _frame_step; } + uint64_t get_frame_ticks() const { return _frame_ticks; } + float get_process_step() const { return _process_step; } float get_physics_interpolation_fraction() const { return _physics_interpolation_fraction; } void set_time_scale(float p_scale); @@ -110,8 +106,6 @@ public: bool has_singleton(const String &p_name) const; Object *get_singleton_object(const String &p_name) const; - _FORCE_INLINE_ bool get_use_pixel_snap() const { return _pixel_snap; } - #ifdef TOOLS_ENABLED _FORCE_INLINE_ void set_editor_hint(bool p_enabled) { editor_hint = p_enabled; } _FORCE_INLINE_ bool is_editor_hint() const { return editor_hint; } @@ -128,6 +122,7 @@ public: String get_license_text() const; bool is_abort_on_gpu_errors_enabled() const; + bool is_validation_layers_enabled() const; Engine(); virtual ~Engine() {} diff --git a/core/project_settings.cpp b/core/config/project_settings.cpp index 8829181489..70e8133eaa 100644 --- a/core/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "project_settings.h" -#include "core/bind/core_bind.h" +#include "core/core_bind.h" #include "core/core_string_names.h" #include "core/io/file_access_network.h" #include "core/io/file_access_pack.h" @@ -39,37 +39,37 @@ #include "core/os/file_access.h" #include "core/os/keyboard.h" #include "core/os/os.h" -#include "core/variant_parser.h" +#include "core/variant/variant_parser.h" #include <zlib.h> ProjectSettings *ProjectSettings::singleton = nullptr; ProjectSettings *ProjectSettings::get_singleton() { - return singleton; } String ProjectSettings::get_resource_path() const { - return resource_path; -}; +} -String ProjectSettings::localize_path(const String &p_path) const { +const String ProjectSettings::IMPORTED_FILES_PATH("res://.godot/imported"); - if (resource_path == "") +String ProjectSettings::localize_path(const String &p_path) const { + if (resource_path == "") { return p_path; //not initialized yet + } if (p_path.begins_with("res://") || p_path.begins_with("user://") || - (p_path.is_abs_path() && !p_path.begins_with(resource_path))) + (p_path.is_abs_path() && !p_path.begins_with(resource_path))) { return p_path.simplify_path(); + } DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String path = p_path.replace("\\", "/").simplify_path(); if (dir->change_dir(path) == OK) { - String cwd = dir->get_current_dir(); cwd = cwd.replace("\\", "/"); @@ -89,59 +89,68 @@ String ProjectSettings::localize_path(const String &p_path) const { if (!cwd.begins_with(res_path)) { return p_path; - }; + } return cwd.replace_first(res_path, "res://"); } else { - memdelete(dir); - int sep = path.find_last("/"); + int sep = path.rfind("/"); if (sep == -1) { return "res://" + path; - }; + } String parent = path.substr(0, sep); String plocal = localize_path(parent); if (plocal == "") { return ""; - }; + } // Only strip the starting '/' from 'path' if its parent ('plocal') ends with '/' if (plocal[plocal.length() - 1] == '/') { sep += 1; } return plocal + path.substr(sep, path.size() - sep); - }; + } } void ProjectSettings::set_initial_value(const String &p_name, const Variant &p_value) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].initial = p_value; } -void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) { +void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) { ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].restart_if_changed = p_restart; } -String ProjectSettings::globalize_path(const String &p_path) const { +void ProjectSettings::set_ignore_value_in_docs(const String &p_name, bool p_ignore) { + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); +#ifdef DEBUG_METHODS_ENABLED + props[p_name].ignore_value_in_docs = p_ignore; +#endif +} - if (p_path.begins_with("res://")) { +bool ProjectSettings::get_ignore_value_in_docs(const String &p_name) const { + ERR_FAIL_COND_V_MSG(!props.has(p_name), false, "Request for nonexistent project setting: " + p_name + "."); +#ifdef DEBUG_METHODS_ENABLED + return props[p_name].ignore_value_in_docs; +#else + return false; +#endif +} +String ProjectSettings::globalize_path(const String &p_path) const { + if (p_path.begins_with("res://")) { if (resource_path != "") { - return p_path.replace("res:/", resource_path); - }; + } return p_path.replace("res://", ""); } else if (p_path.begins_with("user://")) { - String data_dir = OS::get_singleton()->get_user_data_dir(); if (data_dir != "") { - return p_path.replace("user:/", data_dir); - }; + } return p_path.replace("user://", ""); } @@ -149,17 +158,20 @@ String ProjectSettings::globalize_path(const String &p_path) const { } bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { - _THREAD_SAFE_METHOD_ - if (p_value.get_type() == Variant::NIL) + if (p_value.get_type() == Variant::NIL) { props.erase(p_name); - else { - + if (p_name.operator String().begins_with("autoload/")) { + String node_name = p_name.operator String().split("/")[1]; + if (autoloads.has(node_name)) { + remove_autoload(node_name); + } + } + } else { if (p_name == CoreStringNames::get_singleton()->_custom_features) { Vector<String> custom_feature_array = String(p_value).split(","); for (int i = 0; i < custom_feature_array.size(); i++) { - custom_features.insert(custom_feature_array[i]); } return true; @@ -180,25 +192,38 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { } if (override_valid) { - feature_overrides[s[0]] = p_name; } } } if (props.has(p_name)) { - if (!props[p_name].overridden) + if (!props[p_name].overridden) { props[p_name].variant = p_value; + } } else { props[p_name] = VariantContainer(p_value, last_order++); } + if (p_name.operator String().begins_with("autoload/")) { + String node_name = p_name.operator String().split("/")[1]; + AutoloadInfo autoload; + autoload.name = node_name; + String path = p_value; + if (path.begins_with("*")) { + autoload.is_singleton = true; + autoload.path = path.substr(1); + } else { + autoload.path = path; + } + add_autoload(autoload); + } } return true; } -bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const { +bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const { _THREAD_SAFE_METHOD_ StringName name = p_name; @@ -214,7 +239,6 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const { } struct _VCSort { - String name; Variant::Type type; int order; @@ -224,26 +248,26 @@ struct _VCSort { }; void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { - _THREAD_SAFE_METHOD_ Set<_VCSort> vclist; for (Map<StringName, VariantContainer>::Element *E = props.front(); E; E = E->next()) { - const VariantContainer *v = &E->get(); - if (v->hide_from_editor) + if (v->hide_from_editor) { continue; + } _VCSort vc; vc.name = E->key(); vc.order = v->order; vc.type = v->variant.get_type(); - if (vc.name.begins_with("input/") || vc.name.begins_with("import/") || vc.name.begins_with("export/") || vc.name.begins_with("/remap") || vc.name.begins_with("/locale") || vc.name.begins_with("/autoload")) + if (vc.name.begins_with("input/") || vc.name.begins_with("import/") || vc.name.begins_with("export/") || vc.name.begins_with("/remap") || vc.name.begins_with("/locale") || vc.name.begins_with("/autoload")) { vc.flags = PROPERTY_USAGE_STORAGE; - else + } else { vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; + } if (v->restart_if_changed) { vc.flags |= PROPERTY_USAGE_RESTART_IF_CHANGED; @@ -252,31 +276,33 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { } for (Set<_VCSort>::Element *E = vclist.front(); E; E = E->next()) { - String prop_info_name = E->get().name; int dot = prop_info_name.find("."); - if (dot != -1) + if (dot != -1) { prop_info_name = prop_info_name.substr(0, dot); + } if (custom_prop_info.has(prop_info_name)) { PropertyInfo pi = custom_prop_info[prop_info_name]; pi.name = E->get().name; pi.usage = E->get().flags; p_list->push_back(pi); - } else + } else { p_list->push_back(PropertyInfo(E->get().type, E->get().name, PROPERTY_HINT_NONE, "", E->get().flags)); + } } } -bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files) { - - if (PackedData::get_singleton()->is_disabled()) +bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) { + if (PackedData::get_singleton()->is_disabled()) { return false; + } - bool ok = PackedData::get_singleton()->add_pack(p_pack, p_replace_files) == OK; + bool ok = PackedData::get_singleton()->add_pack(p_pack, p_replace_files, p_offset) == OK; - if (!ok) + if (!ok) { return false; + } //if data.pck is found, all directory access will be from here DirAccess::make_default<DirAccessPack>(DirAccess::ACCESS_RESOURCES); @@ -286,7 +312,6 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f } void ProjectSettings::_convert_to_last_version(int p_from_version) { - if (p_from_version <= 3) { // Converts the actions from array to dictionary (array of events to dictionary with deadzone + events) for (Map<StringName, ProjectSettings::VariantContainer>::Element *E = props.front(); E; E = E->next()) { @@ -307,10 +332,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 @@ -322,11 +353,9 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) { * If nothing was found, error out. */ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, bool p_upwards) { - // If looking for files in a network client, use it directly if (FileAccessNetworkClient::get_singleton()) { - Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { // Optional, we don't mind if it fails @@ -338,7 +367,6 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b // Attempt with a user-defined main pack first if (p_main_pack != "") { - bool ok = _load_resource_pack(p_main_pack); ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, "Cannot open resource pack '" + p_main_pack + "'."); @@ -354,65 +382,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 - found = _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().plus_file(exec_basename + ".pck")); + // 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")) || _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().plus_file(exec_filename + ".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; @@ -433,7 +464,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; @@ -443,8 +474,9 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b if (p_upwards) { // Try to load settings ascending through parent directories d->change_dir(".."); - if (d->get_current_dir() == current_dir) + if (d->get_current_dir() == current_dir) { break; // not doing anything useful + } current_dir = d->get_current_dir(); } else { break; @@ -452,14 +484,16 @@ 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) + if (!found) { return err; + } - if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') - resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end + if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') { + resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end. + } return OK; } @@ -472,24 +506,25 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo _load_settings_text(custom_settings); } } + // Using GLOBAL_GET on every block for compressing can be slow, so assigning here. + Compression::zstd_long_distance_matching = GLOBAL_GET("compression/formats/zstd/long_distance_matching"); + Compression::zstd_level = GLOBAL_GET("compression/formats/zstd/compression_level"); + Compression::zstd_window_log_size = GLOBAL_GET("compression/formats/zstd/window_log_size"); + + Compression::zlib_level = GLOBAL_GET("compression/formats/zlib/compression_level"); + + Compression::gzip_level = GLOBAL_GET("compression/formats/gzip/compression_level"); return err; } bool ProjectSettings::has_setting(String p_var) const { - _THREAD_SAFE_METHOD_ return props.has(p_var); } -void ProjectSettings::set_registering_order(bool p_enable) { - - registering_order = p_enable; -} - Error ProjectSettings::_load_settings_binary(const String &p_path) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); if (err != OK) { @@ -499,7 +534,6 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) { uint8_t hdr[4]; f->get_buffer(hdr, 4); if (hdr[0] != 'E' || hdr[1] != 'C' || hdr[2] != 'F' || hdr[3] != 'G') { - memdelete(f); ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Corrupted header in binary project.binary (not ECFG)."); } @@ -507,7 +541,6 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) { uint32_t count = f->get_32(); for (uint32_t i = 0; i < count; i++) { - uint32_t slen = f->get_32(); CharString cs; cs.resize(slen + 1); @@ -532,7 +565,6 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) { } Error ProjectSettings::_load_settings_text(const String &p_path) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -555,7 +587,6 @@ Error ProjectSettings::_load_settings_text(const String &p_path) { int config_version = 0; while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); @@ -594,55 +625,61 @@ Error ProjectSettings::_load_settings_text(const String &p_path) { } Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path, const String &p_bin_path) { + // Attempt first to load the binary project.godot file. + Error err = _load_settings_binary(p_bin_path); + if (err == OK) { + return OK; + } else if (err != ERR_FILE_NOT_FOUND) { + // If the file exists but can't be loaded, we want to know it. + ERR_PRINT("Couldn't load file '" + p_bin_path + "', error code " + itos(err) + "."); + return err; + } - // Attempt first to load the text-based project.godot file - Error err_text = _load_settings_text(p_text_path); - if (err_text == OK) { + // Fallback to text-based project.godot file if binary was not found. + err = _load_settings_text(p_text_path); + if (err == OK) { return OK; - } else if (err_text != ERR_FILE_NOT_FOUND) { - // If the text-based file exists but can't be loaded, we want to know it - ERR_PRINT("Couldn't load file '" + p_text_path + "', error code " + itos(err_text) + "."); - return err_text; + } else if (err != ERR_FILE_NOT_FOUND) { + ERR_PRINT("Couldn't load file '" + p_text_path + "', error code " + itos(err) + "."); + return err; } - // Fallback to binary project.binary file if text-based was not found - Error err_bin = _load_settings_binary(p_bin_path); - return err_bin; + return err; } int ProjectSettings::get_order(const String &p_name) const { - ERR_FAIL_COND_V_MSG(!props.has(p_name), -1, "Request for nonexistent project setting: " + p_name + "."); return props[p_name].order; } void ProjectSettings::set_order(const String &p_name, int p_order) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].order = p_order; } void ProjectSettings::set_builtin_order(const String &p_name) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); if (props[p_name].order >= NO_BUILTIN_ORDER_BASE) { props[p_name].order = last_builtin_order++; } } -void ProjectSettings::clear(const String &p_name) { +bool ProjectSettings::is_builtin_setting(const String &p_name) const { + // Return true because a false negative is worse than a false positive. + ERR_FAIL_COND_V_MSG(!props.has(p_name), true, "Request for nonexistent project setting: " + p_name + "."); + return props[p_name].order < NO_BUILTIN_ORDER_BASE; +} +void ProjectSettings::clear(const String &p_name) { ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props.erase(p_name); } Error ProjectSettings::save() { - return save_custom(get_resource_path().plus_file("project.godot")); } Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<String, List<String>> &props, const CustomMap &p_custom, const String &p_custom_features) { - Error err; FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err); ERR_FAIL_COND_V_MSG(err != OK, err, "Couldn't save project.binary at " + p_file + "."); @@ -653,9 +690,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str int count = 0; for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { - for (List<String>::Element *F = E->get().front(); F; F = F->next()) { - count++; } } @@ -690,33 +725,35 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str } for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { - for (List<String>::Element *F = E->get().front(); F; F = F->next()) { - String key = F->get(); - if (E->key() != "") + if (E->key() != "") { key = E->key() + "/" + key; + } Variant value; - if (p_custom.has(key)) + if (p_custom.has(key)) { value = p_custom[key]; - else + } else { value = get(key); + } file->store_32(key.length()); file->store_string(key); int len; err = encode_variant(value, nullptr, len, true); - if (err != OK) + if (err != OK) { memdelete(file); + } ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant."); Vector<uint8_t> buff; buff.resize(len); err = encode_variant(value, buff.ptrw(), len, true); - if (err != OK) + if (err != OK) { memdelete(file); + } ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant."); file->store_32(len); file->store_buffer(buff.ptr(), buff.size()); @@ -730,7 +767,6 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str } Error ProjectSettings::_save_settings_text(const String &p_file, const Map<String, List<String>> &props, const CustomMap &p_custom, const String &p_custom_features) { - Error err; FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err); @@ -746,27 +782,30 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin file->store_line(""); file->store_string("config_version=" + itos(CONFIG_VERSION) + "\n"); - if (p_custom_features != String()) + if (p_custom_features != String()) { file->store_string("custom_features=\"" + p_custom_features + "\"\n"); + } file->store_string("\n"); for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { - - if (E != props.front()) + if (E != props.front()) { file->store_string("\n"); + } - if (E->key() != "") + if (E->key() != "") { file->store_string("[" + E->key() + "]\n\n"); + } for (List<String>::Element *F = E->get().front(); F; F = F->next()) { - String key = F->get(); - if (E->key() != "") + if (E->key() != "") { key = E->key() + "/" + key; + } Variant value; - if (p_custom.has(key)) + if (p_custom.has(key)) { value = p_custom[key]; - else + } else { value = get(key); + } String vstr; VariantWriter::write_to_string(value, vstr); @@ -783,39 +822,39 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other params as dictionary and array? return save_custom(p_file); -}; +} Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) { - ERR_FAIL_COND_V_MSG(p_path == "", ERR_INVALID_PARAMETER, "Project settings save path cannot be empty."); Set<_VCSort> vclist; if (p_merge_with_current) { for (Map<StringName, VariantContainer>::Element *G = props.front(); G; G = G->next()) { - const VariantContainer *v = &G->get(); - if (v->hide_from_editor) + if (v->hide_from_editor) { continue; + } - if (p_custom.has(G->key())) + if (p_custom.has(G->key())) { continue; + } _VCSort vc; vc.name = G->key(); //*k; vc.order = v->order; vc.type = v->variant.get_type(); vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; - if (v->variant == v->initial) + if (v->variant == v->initial) { continue; + } vclist.insert(vc); } } for (const Map<String, Variant>::Element *E = p_custom.front(); E; E = E->next()) { - // Lookup global prop to store in the same order Map<StringName, VariantContainer>::Element *global_prop = props.find(E->key()); @@ -830,16 +869,14 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust Map<String, List<String>> props; for (Set<_VCSort>::Element *E = vclist.front(); E; E = E->next()) { - String category = E->get().name; String name = E->get().name; int div = category.find("/"); - if (div < 0) + if (div < 0) { category = ""; - else { - + } else { category = category.substr(0, div); name = name.substr(div + 1, name.size()); } @@ -849,25 +886,24 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust String custom_features; for (int i = 0; i < p_custom_features.size(); i++) { - if (i > 0) + if (i > 0) { custom_features += ","; + } String f = p_custom_features[i].strip_edges().replace("\"", ""); custom_features += f; } - if (p_path.ends_with(".godot")) + if (p_path.ends_with(".godot")) { return _save_settings_text(p_path, props, p_custom, custom_features); - else if (p_path.ends_with(".binary")) + } else if (p_path.ends_with(".binary")) { return _save_settings_binary(p_path, props, p_custom, custom_features); - else { - + } else { ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown config file format: " + p_path + "."); } } -Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed) { - +Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed, bool p_ignore_value_in_docs) { Variant ret; if (!ProjectSettings::get_singleton()->has_setting(p_var)) { ProjectSettings::get_singleton()->set(p_var, p_default); @@ -877,19 +913,19 @@ Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restar ProjectSettings::get_singleton()->set_initial_value(p_var, p_default); ProjectSettings::get_singleton()->set_builtin_order(p_var); ProjectSettings::get_singleton()->set_restart_if_changed(p_var, p_restart_if_changed); + ProjectSettings::get_singleton()->set_ignore_value_in_docs(p_var, p_ignore_value_in_docs); return ret; } Vector<String> ProjectSettings::get_optimizer_presets() const { - List<PropertyInfo> pi; ProjectSettings::get_singleton()->get_property_list(&pi); Vector<String> names; for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { - - if (!E->get().name.begins_with("optimizer_presets/")) + if (!E->get().name.begins_with("optimizer_presets/")) { continue; + } names.push_back(E->get().name.get_slicec('/', 1)); } @@ -899,7 +935,6 @@ Vector<String> ProjectSettings::get_optimizer_presets() const { } void ProjectSettings::_add_property_info_bind(const Dictionary &p_info) { - ERR_FAIL_COND(!p_info.has("name")); ERR_FAIL_COND(!p_info.has("type")); @@ -909,16 +944,17 @@ void ProjectSettings::_add_property_info_bind(const Dictionary &p_info) { pinfo.type = Variant::Type(p_info["type"].operator int()); ERR_FAIL_INDEX(pinfo.type, Variant::VARIANT_MAX); - if (p_info.has("hint")) + if (p_info.has("hint")) { pinfo.hint = PropertyHint(p_info["hint"].operator int()); - if (p_info.has("hint_string")) + } + if (p_info.has("hint_string")) { pinfo.hint_string = p_info["hint_string"]; + } set_custom_property_info(pinfo.name, pinfo); } void ProjectSettings::set_custom_property_info(const String &p_prop, const PropertyInfo &p_info) { - ERR_FAIL_COND(!props.has(p_prop)); custom_prop_info[p_prop] = p_info; custom_prop_info[p_prop].name = p_prop; @@ -929,27 +965,25 @@ const Map<StringName, PropertyInfo> &ProjectSettings::get_custom_property_info() } void ProjectSettings::set_disable_feature_overrides(bool p_disable) { - disable_feature_overrides = p_disable; } bool ProjectSettings::is_using_datapack() const { - return using_datapack; } bool ProjectSettings::property_can_revert(const String &p_name) { - - if (!props.has(p_name)) + if (!props.has(p_name)) { return false; + } return props[p_name].initial != props[p_name].variant; } Variant ProjectSettings::property_get_revert(const String &p_name) { - - if (!props.has(p_name)) + if (!props.has(p_name)) { return Variant(); + } return props[p_name].initial; } @@ -966,8 +1000,30 @@ bool ProjectSettings::has_custom_feature(const String &p_feature) const { return custom_features.has(p_feature); } -void ProjectSettings::_bind_methods() { +Map<StringName, ProjectSettings::AutoloadInfo> ProjectSettings::get_autoload_list() const { + return autoloads; +} + +void ProjectSettings::add_autoload(const AutoloadInfo &p_autoload) { + ERR_FAIL_COND_MSG(p_autoload.name == StringName(), "Trying to add autoload with no name."); + autoloads[p_autoload.name] = p_autoload; +} +void ProjectSettings::remove_autoload(const StringName &p_autoload) { + ERR_FAIL_COND_MSG(!autoloads.has(p_autoload), "Trying to remove non-existent autoload."); + autoloads.erase(p_autoload); +} + +bool ProjectSettings::has_autoload(const StringName &p_autoload) const { + return autoloads.has(p_autoload); +} + +ProjectSettings::AutoloadInfo ProjectSettings::get_autoload(const StringName &p_name) const { + ERR_FAIL_COND_V_MSG(!autoloads.has(p_name), AutoloadInfo(), "Trying to get non-existent autoload."); + return autoloads[p_name]; +} + +void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting); ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting); ClassDB::bind_method(D_METHOD("get_setting", "name"), &ProjectSettings::get_setting); @@ -979,7 +1035,7 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("localize_path", "path"), &ProjectSettings::localize_path); ClassDB::bind_method(D_METHOD("globalize_path", "path"), &ProjectSettings::globalize_path); ClassDB::bind_method(D_METHOD("save"), &ProjectSettings::save); - ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files"), &ProjectSettings::_load_resource_pack, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0)); ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &ProjectSettings::property_can_revert); ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &ProjectSettings::property_get_revert); @@ -987,12 +1043,10 @@ void ProjectSettings::_bind_methods() { } ProjectSettings::ProjectSettings() { + // Initialization of engine variables should be done in the setup() method, + // so that the values can be overridden from project.godot or project.binary. singleton = this; - last_order = NO_BUILTIN_ORDER_BASE; - last_builtin_order = 0; - disable_feature_overrides = false; - registering_order = true; Array events; Dictionary action; @@ -1014,8 +1068,9 @@ ProjectSettings::ProjectSettings() { PackedStringArray extensions = PackedStringArray(); extensions.push_back("gd"); - if (Engine::get_singleton()->has_singleton("GodotSharp")) + if (Engine::get_singleton()->has_singleton("GodotSharp")) { extensions.push_back("cs"); + } extensions.push_back("shader"); GLOBAL_DEF("editor/search_in_file_extensions", extensions); @@ -1037,7 +1092,7 @@ ProjectSettings::ProjectSettings() { key->set_keycode(KEY_SPACE); events.push_back(key); joyb.instance(); - joyb->set_button_index(JOY_BUTTON_0); + joyb->set_button_index(JOY_BUTTON_A); events.push_back(joyb); action["events"] = events; GLOBAL_DEF("input/ui_accept", action); @@ -1050,7 +1105,7 @@ ProjectSettings::ProjectSettings() { key->set_keycode(KEY_SPACE); events.push_back(key); joyb.instance(); - joyb->set_button_index(JOY_BUTTON_3); + joyb->set_button_index(JOY_BUTTON_Y); events.push_back(joyb); action["events"] = events; GLOBAL_DEF("input/ui_select", action); @@ -1063,7 +1118,7 @@ ProjectSettings::ProjectSettings() { key->set_keycode(KEY_ESCAPE); events.push_back(key); joyb.instance(); - joyb->set_button_index(JOY_BUTTON_1); + joyb->set_button_index(JOY_BUTTON_B); events.push_back(joyb); action["events"] = events; GLOBAL_DEF("input/ui_cancel", action); @@ -1097,7 +1152,7 @@ ProjectSettings::ProjectSettings() { key->set_keycode(KEY_LEFT); events.push_back(key); joyb.instance(); - joyb->set_button_index(JOY_DPAD_LEFT); + joyb->set_button_index(JOY_BUTTON_DPAD_LEFT); events.push_back(joyb); action["events"] = events; GLOBAL_DEF("input/ui_left", action); @@ -1110,7 +1165,7 @@ ProjectSettings::ProjectSettings() { key->set_keycode(KEY_RIGHT); events.push_back(key); joyb.instance(); - joyb->set_button_index(JOY_DPAD_RIGHT); + joyb->set_button_index(JOY_BUTTON_DPAD_RIGHT); events.push_back(joyb); action["events"] = events; GLOBAL_DEF("input/ui_right", action); @@ -1123,7 +1178,7 @@ ProjectSettings::ProjectSettings() { key->set_keycode(KEY_UP); events.push_back(key); joyb.instance(); - joyb->set_button_index(JOY_DPAD_UP); + joyb->set_button_index(JOY_BUTTON_DPAD_UP); events.push_back(joyb); action["events"] = events; GLOBAL_DEF("input/ui_up", action); @@ -1136,7 +1191,7 @@ ProjectSettings::ProjectSettings() { key->set_keycode(KEY_DOWN); events.push_back(key); joyb.instance(); - joyb->set_button_index(JOY_DPAD_DOWN); + joyb->set_button_index(JOY_BUTTON_DPAD_DOWN); events.push_back(joyb); action["events"] = events; GLOBAL_DEF("input/ui_down", action); @@ -1190,24 +1245,20 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"); - //assigning here, because using GLOBAL_GET on every block for compressing can be slow - Compression::zstd_long_distance_matching = GLOBAL_DEF("compression/formats/zstd/long_distance_matching", false); + GLOBAL_DEF("compression/formats/zstd/long_distance_matching", Compression::zstd_long_distance_matching); custom_prop_info["compression/formats/zstd/long_distance_matching"] = PropertyInfo(Variant::BOOL, "compression/formats/zstd/long_distance_matching"); - Compression::zstd_level = GLOBAL_DEF("compression/formats/zstd/compression_level", 3); + GLOBAL_DEF("compression/formats/zstd/compression_level", Compression::zstd_level); custom_prop_info["compression/formats/zstd/compression_level"] = PropertyInfo(Variant::INT, "compression/formats/zstd/compression_level", PROPERTY_HINT_RANGE, "1,22,1"); - Compression::zstd_window_log_size = GLOBAL_DEF("compression/formats/zstd/window_log_size", 27); + GLOBAL_DEF("compression/formats/zstd/window_log_size", Compression::zstd_window_log_size); custom_prop_info["compression/formats/zstd/window_log_size"] = PropertyInfo(Variant::INT, "compression/formats/zstd/window_log_size", PROPERTY_HINT_RANGE, "10,30,1"); - Compression::zlib_level = GLOBAL_DEF("compression/formats/zlib/compression_level", Z_DEFAULT_COMPRESSION); + GLOBAL_DEF("compression/formats/zlib/compression_level", Compression::zlib_level); custom_prop_info["compression/formats/zlib/compression_level"] = PropertyInfo(Variant::INT, "compression/formats/zlib/compression_level", PROPERTY_HINT_RANGE, "-1,9,1"); - Compression::gzip_level = GLOBAL_DEF("compression/formats/gzip/compression_level", Z_DEFAULT_COMPRESSION); + GLOBAL_DEF("compression/formats/gzip/compression_level", Compression::gzip_level); custom_prop_info["compression/formats/gzip/compression_level"] = PropertyInfo(Variant::INT, "compression/formats/gzip/compression_level", PROPERTY_HINT_RANGE, "-1,9,1"); - - using_datapack = false; } ProjectSettings::~ProjectSettings() { - singleton = nullptr; } diff --git a/core/project_settings.h b/core/config/project_settings.h index 7b3ca18c62..59c56c23c2 100644 --- a/core/project_settings.h +++ b/core/config/project_settings.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,62 +31,65 @@ #ifndef PROJECT_SETTINGS_H #define PROJECT_SETTINGS_H -#include "core/object.h" +#include "core/object/class_db.h" #include "core/os/thread_safe.h" -#include "core/set.h" +#include "core/templates/set.h" class ProjectSettings : public Object { - GDCLASS(ProjectSettings, Object); _THREAD_SAFE_CLASS_ public: typedef Map<String, Variant> CustomMap; + static const String IMPORTED_FILES_PATH; enum { //properties that are not for built in values begin from this value, so builtin ones are displayed first NO_BUILTIN_ORDER_BASE = 1 << 16 }; + struct AutoloadInfo { + StringName name; + String path; + bool is_singleton = false; + }; + protected: struct VariantContainer { - int order; - bool persist; + int order = 0; + bool persist = false; Variant variant; Variant initial; - bool hide_from_editor; - bool overridden; - bool restart_if_changed; - VariantContainer() : - order(0), - persist(false), - hide_from_editor(false), - overridden(false), - restart_if_changed(false) { - } + bool hide_from_editor = false; + bool overridden = false; + bool restart_if_changed = false; +#ifdef DEBUG_METHODS_ENABLED + bool ignore_value_in_docs = false; +#endif + + VariantContainer() {} + VariantContainer(const Variant &p_variant, int p_order, bool p_persist = false) : order(p_order), persist(p_persist), - variant(p_variant), - hide_from_editor(false), - overridden(false), - restart_if_changed(false) { + variant(p_variant) { } }; - bool registering_order; - int last_order; - int last_builtin_order; + int last_order = NO_BUILTIN_ORDER_BASE; + int last_builtin_order = 0; Map<StringName, VariantContainer> props; String resource_path; Map<StringName, PropertyInfo> custom_prop_info; - bool disable_feature_overrides; - bool using_datapack; + bool disable_feature_overrides = false; + bool using_datapack = false; List<String> input_presets; Set<String> custom_features; Map<StringName, StringName> feature_overrides; + Map<StringName, AutoloadInfo> autoloads; + bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; @@ -104,7 +107,7 @@ protected: void _convert_to_last_version(int p_from_version); - bool _load_resource_pack(const String &p_pack, bool p_replace_files = true); + bool _load_resource_pack(const String &p_pack, bool p_replace_files = true, int p_offset = 0); void _add_property_info_bind(const Dictionary &p_info); @@ -125,6 +128,9 @@ public: void set_initial_value(const String &p_name, const Variant &p_value); void set_restart_if_changed(const String &p_name, bool p_restart); + void set_ignore_value_in_docs(const String &p_name, bool p_ignore); + bool get_ignore_value_in_docs(const String &p_name) const; + bool property_can_revert(const String &p_name); Variant property_get_revert(const String &p_name); @@ -136,6 +142,7 @@ public: int get_order(const String &p_name) const; void set_order(const String &p_name, int p_order); void set_builtin_order(const String &p_name); + bool is_builtin_setting(const String &p_name) const; Error setup(const String &p_path, const String &p_main_pack, bool p_upwards = false); @@ -152,18 +159,24 @@ public: bool is_using_datapack() const; - void set_registering_order(bool p_enable); - bool has_custom_feature(const String &p_feature) const; + Map<StringName, AutoloadInfo> get_autoload_list() const; + void add_autoload(const AutoloadInfo &p_autoload); + void remove_autoload(const StringName &p_autoload); + bool has_autoload(const StringName &p_autoload) const; + AutoloadInfo get_autoload(const StringName &p_name) const; + ProjectSettings(); ~ProjectSettings(); }; //not a macro any longer -Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false); +Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false, bool p_ignore_value_in_docs = false); #define GLOBAL_DEF(m_var, m_value) _GLOBAL_DEF(m_var, m_value) #define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true) +#define GLOBAL_DEF_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true) +#define GLOBAL_DEF_RST_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true) #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var) #endif // PROJECT_SETTINGS_H diff --git a/core/bind/core_bind.cpp b/core/core_bind.cpp index e2774deb3c..000b628ba7 100644 --- a/core/bind/core_bind.cpp +++ b/core/core_bind.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,15 +30,17 @@ #include "core_bind.h" +#include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" +#include "core/debugger/engine_debugger.h" #include "core/io/file_access_compressed.h" #include "core/io/file_access_encrypted.h" #include "core/io/json.h" #include "core/io/marshalls.h" -#include "core/math/geometry.h" +#include "core/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" /** * Time constants borrowed from loc_time.h @@ -62,12 +64,14 @@ static const unsigned int MONTH_DAYS_TABLE[2][12] = { { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; +////// _ResourceLoader ////// + _ResourceLoader *_ResourceLoader::singleton = nullptr; Error _ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads) { - return ResourceLoader::load_threaded_request(p_path, p_type_hint, p_use_sub_threads); } + _ResourceLoader::ThreadLoadStatus _ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) { float progress = 0; ResourceLoader::ThreadLoadStatus tls = ResourceLoader::load_threaded_get_status(p_path, &progress); @@ -75,6 +79,7 @@ _ResourceLoader::ThreadLoadStatus _ResourceLoader::load_threaded_get_status(cons r_progress[0] = progress; return (ThreadLoadStatus)tls; } + RES _ResourceLoader::load_threaded_get(const String &p_path) { Error error; RES res = ResourceLoader::load_threaded_get(p_path, &error); @@ -82,7 +87,6 @@ RES _ResourceLoader::load_threaded_get(const String &p_path) { } RES _ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache) { - Error err = OK; RES ret = ResourceLoader::load(p_path, p_type_hint, p_no_cache, &err); @@ -91,12 +95,10 @@ RES _ResourceLoader::load(const String &p_path, const String &p_type_hint, bool } Vector<String> _ResourceLoader::get_recognized_extensions_for_type(const String &p_type) { - List<String> exts; ResourceLoader::get_recognized_extensions_for_type(p_type, &exts); Vector<String> ret; for (List<String>::Element *E = exts.front(); E; E = E->next()) { - ret.push_back(E->get()); } @@ -104,12 +106,10 @@ Vector<String> _ResourceLoader::get_recognized_extensions_for_type(const String } void _ResourceLoader::set_abort_on_missing_resources(bool p_abort) { - ResourceLoader::set_abort_on_missing_resources(p_abort); } PackedStringArray _ResourceLoader::get_dependencies(const String &p_path) { - List<String> deps; ResourceLoader::get_dependencies(p_path, &deps); @@ -119,10 +119,9 @@ PackedStringArray _ResourceLoader::get_dependencies(const String &p_path) { } return ret; -}; +} bool _ResourceLoader::has_cached(const String &p_path) { - String local_path = ProjectSettings::get_singleton()->localize_path(p_path); return ResourceCache::has(local_path); } @@ -132,7 +131,6 @@ bool _ResourceLoader::exists(const String &p_path, const String &p_type_hint) { } void _ResourceLoader::_bind_methods() { - ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads"), &_ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false)); ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &_ResourceLoader::load_threaded_get_status, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &_ResourceLoader::load_threaded_get); @@ -150,10 +148,7 @@ void _ResourceLoader::_bind_methods() { BIND_ENUM_CONSTANT(THREAD_LOAD_LOADED); } -_ResourceLoader::_ResourceLoader() { - - singleton = this; -} +////// _ResourceSaver ////// Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) { ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + String(p_path) + "'."); @@ -161,13 +156,11 @@ Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFla } Vector<String> _ResourceSaver::get_recognized_extensions(const RES &p_resource) { - ERR_FAIL_COND_V_MSG(p_resource.is_null(), Vector<String>(), "It's not a reference to a valid Resource object."); List<String> exts; ResourceSaver::get_recognized_extensions(p_resource, &exts); Vector<String> ret; for (List<String>::Element *E = exts.front(); E; E = E->next()) { - ret.push_back(E->get()); } return ret; @@ -176,7 +169,6 @@ Vector<String> _ResourceSaver::get_recognized_extensions(const RES &p_resource) _ResourceSaver *_ResourceSaver::singleton = nullptr; void _ResourceSaver::_bind_methods() { - ClassDB::bind_method(D_METHOD("save", "path", "resource", "flags"), &_ResourceSaver::save, DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_recognized_extensions", "type"), &_ResourceSaver::get_recognized_extensions); @@ -189,10 +181,7 @@ void _ResourceSaver::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_REPLACE_SUBRESOURCE_PATHS); } -_ResourceSaver::_ResourceSaver() { - - singleton = this; -} +////// _OS ////// PackedStringArray _OS::get_connected_midi_inputs() { return OS::get_singleton()->get_connected_midi_inputs(); @@ -207,92 +196,90 @@ void _OS::close_midi_inputs() { } void _OS::set_use_file_access_save_and_swap(bool p_enable) { - FileAccess::set_backup_save(p_enable); } void _OS::set_low_processor_usage_mode(bool p_enabled) { - OS::get_singleton()->set_low_processor_usage_mode(p_enabled); } -bool _OS::is_in_low_processor_usage_mode() const { +bool _OS::is_in_low_processor_usage_mode() const { return OS::get_singleton()->is_in_low_processor_usage_mode(); } void _OS::set_low_processor_usage_mode_sleep_usec(int p_usec) { - OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(p_usec); } int _OS::get_low_processor_usage_mode_sleep_usec() const { - return OS::get_singleton()->get_low_processor_usage_mode_sleep_usec(); } String _OS::get_executable_path() const { - return OS::get_singleton()->get_executable_path(); } Error _OS::shell_open(String p_uri) { - if (p_uri.begins_with("res://")) { WARN_PRINT("Attempting to open an URL with the \"res://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_open()`."); } else if (p_uri.begins_with("user://")) { WARN_PRINT("Attempting to open an URL with the \"user://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_open()`."); } return OS::get_singleton()->shell_open(p_uri); -}; - -int _OS::execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking, Array p_output, bool p_read_stderr) { +} - OS::ProcessID pid = -2; - int exitcode = 0; +int _OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr) { List<String> args; - for (int i = 0; i < p_arguments.size(); i++) + for (int i = 0; i < p_arguments.size(); i++) { args.push_back(p_arguments[i]); + } String pipe; - Error err = OS::get_singleton()->execute(p_path, args, p_blocking, &pid, &pipe, &exitcode, p_read_stderr); - p_output.clear(); - p_output.push_back(pipe); - if (err != OK) + int exitcode = 0; + Error err = OS::get_singleton()->execute(p_path, args, &pipe, &exitcode, p_read_stderr); + r_output.push_back(pipe); + if (err != OK) { return -1; - else if (p_blocking) - return exitcode; - else - return pid; + } + return exitcode; } -Error _OS::kill(int p_pid) { +int _OS::create_process(const String &p_path, const Vector<String> &p_arguments) { + List<String> args; + for (int i = 0; i < p_arguments.size(); i++) { + args.push_back(p_arguments[i]); + } + OS::ProcessID pid = 0; + Error err = OS::get_singleton()->create_process(p_path, args, &pid); + if (err != OK) { + return -1; + } + return pid; +} +Error _OS::kill(int p_pid) { return OS::get_singleton()->kill(p_pid); } int _OS::get_process_id() const { - return OS::get_singleton()->get_process_id(); -}; +} bool _OS::has_environment(const String &p_var) const { - return OS::get_singleton()->has_environment(p_var); } -String _OS::get_environment(const String &p_var) const { +String _OS::get_environment(const String &p_var) const { return OS::get_singleton()->get_environment(p_var); } String _OS::get_name() const { - return OS::get_singleton()->get_name(); } -Vector<String> _OS::get_cmdline_args() { +Vector<String> _OS::get_cmdline_args() { List<String> cmdline = OS::get_singleton()->get_cmdline_args(); Vector<String> cmdlinev; for (List<String>::Element *E = cmdline.front(); E; E = E->next()) { - cmdlinev.push_back(E->get()); } @@ -300,86 +287,34 @@ Vector<String> _OS::get_cmdline_args() { } String _OS::get_locale() const { - return OS::get_singleton()->get_locale(); } String _OS::get_model_name() const { - return OS::get_singleton()->get_model_name(); } Error _OS::set_thread_name(const String &p_name) { - return Thread::set_name(p_name); -}; +} bool _OS::has_feature(const String &p_feature) const { - return OS::get_singleton()->has_feature(p_feature); } -/* -enum Weekday { - DAY_SUNDAY, - DAY_MONDAY, - DAY_TUESDAY, - DAY_WEDNESDAY, - DAY_THURSDAY, - DAY_FRIDAY, - DAY_SATURDAY -}; - -enum Month { - MONTH_JANUARY, - MONTH_FEBRUARY, - MONTH_MARCH, - MONTH_APRIL, - MONTH_MAY, - MONTH_JUNE, - MONTH_JULY, - MONTH_AUGUST, - MONTH_SEPTEMBER, - MONTH_OCTOBER, - MONTH_NOVEMBER, - MONTH_DECEMBER -}; -*/ -/* -struct Date { - - int year; - Month month; - int day; - Weekday weekday; - bool dst; -}; - -struct Time { - - int hour; - int min; - int sec; -}; -*/ - uint64_t _OS::get_static_memory_usage() const { - return OS::get_singleton()->get_static_memory_usage(); } uint64_t _OS::get_static_memory_peak_usage() const { - return OS::get_singleton()->get_static_memory_peak_usage(); } int _OS::get_exit_code() const { - return OS::get_singleton()->get_exit_code(); } void _OS::set_exit_code(int p_code) { - if (p_code < 0 || p_code > 125) { WARN_PRINT("For portability reasons, the exit code should be set between 0 and 125 (inclusive)."); } @@ -392,7 +327,6 @@ void _OS::set_exit_code(int p_code) { * dst */ Dictionary _OS::get_datetime(bool utc) const { - Dictionary dated = get_date(utc); Dictionary timed = get_time(utc); @@ -407,7 +341,6 @@ Dictionary _OS::get_datetime(bool utc) const { } Dictionary _OS::get_date(bool utc) const { - OS::Date date = OS::get_singleton()->get_date(utc); Dictionary dated; dated[YEAR_KEY] = date.year; @@ -419,7 +352,6 @@ Dictionary _OS::get_date(bool utc) const { } Dictionary _OS::get_time(bool utc) const { - OS::Time time = OS::get_singleton()->get_time(utc); Dictionary timed; timed[HOUR_KEY] = time.hour; @@ -441,7 +373,6 @@ Dictionary _OS::get_time(bool utc) const { * @return epoch calculated */ int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { - // Bunch of conversion constants static const unsigned int SECONDS_PER_MINUTE = 60; static const unsigned int MINUTES_PER_HOUR = 60; @@ -514,7 +445,6 @@ int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { * @return dictionary of date and time values */ Dictionary _OS::get_datetime_from_unix_time(int64_t unix_time_val) const { - OS::Date date; OS::Time time; @@ -533,7 +463,7 @@ Dictionary _OS::get_datetime_from_unix_time(int64_t unix_time_val) const { } else { dayno = (unix_time_val - SECS_DAY + 1) / SECS_DAY; dayclock = unix_time_val - dayno * SECS_DAY; - date.weekday = static_cast<OS::Weekday>((dayno - 3) % 7 + 7); + date.weekday = static_cast<OS::Weekday>(((dayno % 7) + 11) % 7); do { year--; dayno += YEARSIZE(year); @@ -577,81 +507,56 @@ Dictionary _OS::get_time_zone_info() const { return infod; } -uint64_t _OS::get_unix_time() const { - +double _OS::get_unix_time() const { return OS::get_singleton()->get_unix_time(); } -uint64_t _OS::get_system_time_secs() const { - return OS::get_singleton()->get_system_time_secs(); -} - -uint64_t _OS::get_system_time_msecs() const { - return OS::get_singleton()->get_system_time_msecs(); -} - void _OS::delay_usec(uint32_t p_usec) const { - OS::get_singleton()->delay_usec(p_usec); } void _OS::delay_msec(uint32_t p_msec) const { - OS::get_singleton()->delay_usec(int64_t(p_msec) * 1000); } uint32_t _OS::get_ticks_msec() const { - return OS::get_singleton()->get_ticks_msec(); } uint64_t _OS::get_ticks_usec() const { - return OS::get_singleton()->get_ticks_usec(); } -uint32_t _OS::get_splash_tick_msec() const { - - return OS::get_singleton()->get_splash_tick_msec(); -} - bool _OS::can_use_threads() const { - return OS::get_singleton()->can_use_threads(); } bool _OS::is_userfs_persistent() const { - return OS::get_singleton()->is_userfs_persistent(); } int _OS::get_processor_count() const { - return OS::get_singleton()->get_processor_count(); } bool _OS::is_stdout_verbose() const { - return OS::get_singleton()->is_stdout_verbose(); } void _OS::dump_memory_to_file(const String &p_file) { - OS::get_singleton()->dump_memory_to_file(p_file.utf8().get_data()); } struct _OSCoreBindImg { - String path; Size2 size; - int fmt; + int fmt = 0; ObjectID id; - int vram; + int vram = 0; bool operator<(const _OSCoreBindImg &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; } }; void _OS::print_all_textures_by_size() { - List<_OSCoreBindImg> imgs; int total = 0; { @@ -659,9 +564,9 @@ void _OS::print_all_textures_by_size() { ResourceCache::get_cached_resources(&rsrc); for (List<Ref<Resource>>::Element *E = rsrc.front(); E; E = E->next()) { - - if (!E->get()->is_class("ImageTexture")) + if (!E->get()->is_class("ImageTexture")) { continue; + } Size2 size = E->get()->call("get_size"); int fmt = E->get()->call("get_format"); @@ -680,33 +585,29 @@ void _OS::print_all_textures_by_size() { imgs.sort(); for (List<_OSCoreBindImg>::Element *E = imgs.front(); E; E = E->next()) { - total -= E->get().vram; } } void _OS::print_resources_by_type(const Vector<String> &p_types) { - Map<String, int> type_count; List<Ref<Resource>> resources; ResourceCache::get_cached_resources(&resources); - List<Ref<Resource>> rsrc; - ResourceCache::get_cached_resources(&rsrc); - - for (List<Ref<Resource>>::Element *E = rsrc.front(); E; E = E->next()) { - + for (List<Ref<Resource>>::Element *E = resources.front(); E; E = E->next()) { Ref<Resource> r = E->get(); bool found = false; for (int i = 0; i < p_types.size(); i++) { - if (r->is_class(p_types[i])) + if (r->is_class(p_types[i])) { found = true; + } } - if (!found) + if (!found) { continue; + } if (!type_count.has(r->get_class())) { type_count[r->get_class()] = 0; @@ -714,30 +615,25 @@ void _OS::print_resources_by_type(const Vector<String> &p_types) { type_count[r->get_class()]++; } -}; +} void _OS::print_all_resources(const String &p_to_file) { - OS::get_singleton()->print_all_resources(p_to_file); } void _OS::print_resources_in_use(bool p_short) { - OS::get_singleton()->print_resources_in_use(p_short); } void _OS::dump_resources_to_file(const String &p_file) { - OS::get_singleton()->dump_resources_to_file(p_file.utf8().get_data()); } String _OS::get_user_data_dir() const { - return OS::get_singleton()->get_user_data_dir(); -}; +} bool _OS::is_debug_build() const { - #ifdef DEBUG_ENABLED return true; #else @@ -746,47 +642,56 @@ bool _OS::is_debug_build() const { } String _OS::get_system_dir(SystemDir p_dir) const { - return OS::get_singleton()->get_system_dir(OS::SystemDir(p_dir)); } String _OS::get_keycode_string(uint32_t p_code) const { - return keycode_get_string(p_code); } bool _OS::is_keycode_unicode(uint32_t p_unicode) const { - return keycode_has_unicode(p_unicode); } int _OS::find_keycode_from_string(const String &p_code) const { - return find_keycode(p_code); } bool _OS::request_permission(const String &p_name) { - return OS::get_singleton()->request_permission(p_name); } bool _OS::request_permissions() { - return OS::get_singleton()->request_permissions(); } Vector<String> _OS::get_granted_permissions() const { - return OS::get_singleton()->get_granted_permissions(); } String _OS::get_unique_id() const { return OS::get_singleton()->get_unique_id(); } + +int _OS::get_tablet_driver_count() const { + return OS::get_singleton()->get_tablet_driver_count(); +} + +String _OS::get_tablet_driver_name(int p_driver) const { + return OS::get_singleton()->get_tablet_driver_name(p_driver); +} + +String _OS::get_current_tablet_driver() const { + return OS::get_singleton()->get_current_tablet_driver(); +} + +void _OS::set_current_tablet_driver(const String &p_driver) { + OS::get_singleton()->set_current_tablet_driver(p_driver); +} + _OS *_OS::singleton = nullptr; void _OS::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs); ClassDB::bind_method(D_METHOD("open_midi_inputs"), &_OS::open_midi_inputs); ClassDB::bind_method(D_METHOD("close_midi_inputs"), &_OS::close_midi_inputs); @@ -800,7 +705,8 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_processor_count"), &_OS::get_processor_count); ClassDB::bind_method(D_METHOD("get_executable_path"), &_OS::get_executable_path); - ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "blocking", "output", "read_stderr"), &_OS::execute, DEFVAL(true), DEFVAL(Array()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr"), &_OS::execute, DEFVAL(Array()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("create_process", "path", "arguments"), &_OS::create_process); ClassDB::bind_method(D_METHOD("kill", "pid"), &_OS::kill); ClassDB::bind_method(D_METHOD("shell_open", "uri"), &_OS::shell_open); ClassDB::bind_method(D_METHOD("get_process_id"), &_OS::get_process_id); @@ -818,8 +724,6 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_unix_time"), &_OS::get_unix_time); ClassDB::bind_method(D_METHOD("get_datetime_from_unix_time", "unix_time_val"), &_OS::get_datetime_from_unix_time); ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime", "datetime"), &_OS::get_unix_time_from_datetime); - ClassDB::bind_method(D_METHOD("get_system_time_secs"), &_OS::get_system_time_secs); - ClassDB::bind_method(D_METHOD("get_system_time_msecs"), &_OS::get_system_time_msecs); ClassDB::bind_method(D_METHOD("get_exit_code"), &_OS::get_exit_code); ClassDB::bind_method(D_METHOD("set_exit_code", "code"), &_OS::set_exit_code); @@ -828,7 +732,6 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &_OS::delay_msec); ClassDB::bind_method(D_METHOD("get_ticks_msec"), &_OS::get_ticks_msec); ClassDB::bind_method(D_METHOD("get_ticks_usec"), &_OS::get_ticks_usec); - ClassDB::bind_method(D_METHOD("get_splash_tick_msec"), &_OS::get_splash_tick_msec); ClassDB::bind_method(D_METHOD("get_locale"), &_OS::get_locale); ClassDB::bind_method(D_METHOD("get_model_name"), &_OS::get_model_name); @@ -839,8 +742,6 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("is_debug_build"), &_OS::is_debug_build); - //ClassDB::bind_method(D_METHOD("get_mouse_button_state"),&_OS::get_mouse_button_state); - ClassDB::bind_method(D_METHOD("dump_memory_to_file", "file"), &_OS::dump_memory_to_file); ClassDB::bind_method(D_METHOD("dump_resources_to_file", "file"), &_OS::dump_resources_to_file); ClassDB::bind_method(D_METHOD("print_resources_in_use", "short"), &_OS::print_resources_in_use, DEFVAL(false)); @@ -870,12 +771,19 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions); ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions); + ClassDB::bind_method(D_METHOD("get_tablet_driver_count"), &_OS::get_tablet_driver_count); + ClassDB::bind_method(D_METHOD("get_tablet_driver_name", "idx"), &_OS::get_tablet_driver_name); + ClassDB::bind_method(D_METHOD("get_current_tablet_driver"), &_OS::get_current_tablet_driver); + ClassDB::bind_method(D_METHOD("set_current_tablet_driver", "name"), &_OS::set_current_tablet_driver); + ADD_PROPERTY(PropertyInfo(Variant::INT, "exit_code"), "set_exit_code", "get_exit_code"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tablet_driver"), "set_current_tablet_driver", "get_current_tablet_driver"); // Those default values need to be specified for the docs generator, // to avoid using values from the documentation writer's own OS instance. + ADD_PROPERTY_DEFAULT("tablet_driver", ""); ADD_PROPERTY_DEFAULT("exit_code", 0); ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false); ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900); @@ -914,69 +822,43 @@ void _OS::_bind_methods() { BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES); } -_OS::_OS() { - - singleton = this; -} +////// _Geometry2D ////// -///////////////////// GEOMETRY - -_Geometry *_Geometry::singleton = nullptr; - -_Geometry *_Geometry::get_singleton() { +_Geometry2D *_Geometry2D::singleton = nullptr; +_Geometry2D *_Geometry2D::get_singleton() { return singleton; } -Vector<Plane> _Geometry::build_box_planes(const Vector3 &p_extents) { - - return Geometry::build_box_planes(p_extents); -} - -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); +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_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 _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); } -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); @@ -984,128 +866,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); + return Geometry2D::get_closest_point_to_segment_uncapped(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); +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); } -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(); +bool _Geometry2D::is_polygon_clockwise(const Vector<Vector2> &p_polygon) { + return Geometry2D::is_polygon_clockwise(p_polygon); } -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 _Geometry2D::is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon) { + return Geometry2D::is_point_in_polygon(p_point, p_polygon); } -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<int> _Geometry2D::triangulate_polygon(const Vector<Vector2> &p_polygon) { + return Geometry2D::triangulate_polygon(p_polygon); } -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; +Vector<int> _Geometry2D::triangulate_delaunay(const Vector<Vector2> &p_points) { + return Geometry2D::triangulate_delaunay(p_points); } -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; -} -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); -} - -Vector<int> _Geometry::triangulate_polygon(const Vector<Vector2> &p_polygon) { - - return Geometry::triangulate_polygon(p_polygon); -} - -Vector<int> _Geometry::triangulate_delaunay_2d(const Vector<Vector2> &p_points) { - - return Geometry::triangulate_delaunay_2d(p_points); -} - -Vector<Point2> _Geometry::convex_hull_2d(const Vector<Point2> &p_points) { - - return Geometry::convex_hull_2d(p_points); +Vector<Point2> _Geometry2D::convex_hull(const Vector<Point2> &p_points) { + return Geometry2D::convex_hull(p_points); } -Vector<Vector3> _Geometry::clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane) { - - return Geometry::clip_polygon(p_points, p_plane); -} - -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; @@ -1115,9 +911,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; @@ -1127,9 +922,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; @@ -1139,9 +933,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; @@ -1151,9 +944,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; @@ -1163,9 +955,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; @@ -1175,9 +966,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; @@ -1187,9 +977,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; @@ -1199,86 +988,62 @@ 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; for (int i = 0; i < p_rects.size(); i++) { - rects.push_back(p_rects[i]); - }; + } 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; for (int i = 0; i < result.size(); i++) { - r_result.push_back(result[i]); - }; + } ret["points"] = r_result; ret["size"] = r_size; 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); @@ -1296,17 +1061,134 @@ void _Geometry::_bind_methods() { BIND_ENUM_CONSTANT(END_ROUND); } -_Geometry::_Geometry() { - singleton = this; +////// _Geometry3D ////// + +_Geometry3D *_Geometry3D::singleton = nullptr; + +_Geometry3D *_Geometry3D::get_singleton() { + return singleton; } -///////////////////////// FILE +Vector<Plane> _Geometry3D::build_box_planes(const Vector3 &p_extents) { + return Geometry3D::build_box_planes(p_extents); +} -Error _File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { +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); +} + +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("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) { Error err = open(p_path, p_mode_flags); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse(f, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ); @@ -1320,10 +1202,10 @@ Error _File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const } Error _File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) { - Error err = open(p_path, p_mode_flags); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse_password(f, p_pass, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ); @@ -1338,7 +1220,6 @@ Error _File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, c } Error _File::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) { - FileAccessCompressed *fac = memnew(FileAccessCompressed); fac->configure("GCPF", (Compression::Mode)p_compress_mode); @@ -1355,110 +1236,104 @@ Error _File::open_compressed(const String &p_path, ModeFlags p_mode_flags, Compr } Error _File::open(const String &p_path, ModeFlags p_mode_flags) { - close(); Error err; f = FileAccess::open(p_path, p_mode_flags, &err); - if (f) + if (f) { f->set_endian_swap(eswap); + } return err; } void _File::close() { - - if (f) + if (f) { memdelete(f); + } f = nullptr; } -bool _File::is_open() const { +bool _File::is_open() const { return f != nullptr; } -String _File::get_path() const { +String _File::get_path() const { ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_path(); } String _File::get_path_absolute() const { - ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_path_absolute(); } void _File::seek(int64_t p_position) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->seek(p_position); } -void _File::seek_end(int64_t p_position) { +void _File::seek_end(int64_t p_position) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->seek_end(p_position); } -int64_t _File::get_position() const { +int64_t _File::get_position() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_position(); } int64_t _File::get_len() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_len(); } bool _File::eof_reached() const { - ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); return f->eof_reached(); } uint8_t _File::get_8() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_8(); } -uint16_t _File::get_16() const { +uint16_t _File::get_16() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_16(); } -uint32_t _File::get_32() const { +uint32_t _File::get_32() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_32(); } -uint64_t _File::get_64() const { +uint64_t _File::get_64() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_64(); } float _File::get_float() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_float(); } -double _File::get_double() const { +double _File::get_double() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_double(); } -real_t _File::get_real() const { +real_t _File::get_real() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_real(); } Vector<uint8_t> _File::get_buffer(int p_length) const { - Vector<uint8_t> data; ERR_FAIL_COND_V_MSG(!f, data, "File must be opened before use."); ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0."); - if (p_length == 0) + if (p_length == 0) { return data; + } Error err = data.resize(p_length); ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements."); @@ -1467,14 +1342,14 @@ Vector<uint8_t> _File::get_buffer(int p_length) const { int len = f->get_buffer(&w[0], p_length); ERR_FAIL_COND_V(len < 0, Vector<uint8_t>()); - if (len < p_length) + if (len < p_length) { data.resize(p_length); + } return data; } String _File::get_as_text() const { - ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); String text; @@ -1494,17 +1369,14 @@ String _File::get_as_text() const { } String _File::get_md5(const String &p_path) const { - return FileAccess::get_md5(p_path); } String _File::get_sha256(const String &p_path) const { - return FileAccess::get_sha256(p_path); } String _File::get_line() const { - ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); return f->get_line(); } @@ -1520,90 +1392,84 @@ Vector<String> _File::get_csv_line(const String &p_delim) const { */ void _File::set_endian_swap(bool p_swap) { - eswap = p_swap; - if (f) + if (f) { f->set_endian_swap(p_swap); + } } -bool _File::get_endian_swap() { +bool _File::get_endian_swap() { return eswap; } Error _File::get_error() const { - - if (!f) + if (!f) { return ERR_UNCONFIGURED; + } return f->get_error(); } void _File::store_8(uint8_t p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_8(p_dest); } -void _File::store_16(uint16_t p_dest) { +void _File::store_16(uint16_t p_dest) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_16(p_dest); } -void _File::store_32(uint32_t p_dest) { +void _File::store_32(uint32_t p_dest) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_32(p_dest); } -void _File::store_64(uint64_t p_dest) { +void _File::store_64(uint64_t p_dest) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_64(p_dest); } void _File::store_float(float p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_float(p_dest); } -void _File::store_double(double p_dest) { +void _File::store_double(double p_dest) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_double(p_dest); } -void _File::store_real(real_t p_real) { +void _File::store_real(real_t p_real) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_real(p_real); } void _File::store_string(const String &p_string) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_string(p_string); } void _File::store_pascal_string(const String &p_string) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_pascal_string(p_string); -}; +} String _File::get_pascal_string() { - ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_pascal_string(); -}; +} void _File::store_line(const String &p_string) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_line(p_string); } @@ -1614,12 +1480,12 @@ void _File::store_csv_line(const Vector<String> &p_values, const String &p_delim } void _File::store_buffer(const Vector<uint8_t> &p_buffer) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); int len = p_buffer.size(); - if (len == 0) + if (len == 0) { return; + } const uint8_t *r = p_buffer.ptr(); @@ -1627,12 +1493,10 @@ void _File::store_buffer(const Vector<uint8_t> &p_buffer) { } bool _File::file_exists(const String &p_name) const { - return FileAccess::exists(p_name); } void _File::store_var(const Variant &p_var, bool p_full_objects) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); int len; Error err = encode_variant(p_var, nullptr, len, p_full_objects); @@ -1650,7 +1514,6 @@ void _File::store_var(const Variant &p_var, bool p_full_objects) { } Variant _File::get_var(bool p_allow_objects) const { - ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use."); uint32_t len = get_32(); Vector<uint8_t> buff = get_buffer(len); @@ -1666,12 +1529,10 @@ Variant _File::get_var(bool p_allow_objects) const { } uint64_t _File::get_modified_time(const String &p_file) const { - return FileAccess::get_modified_time(p_file); } void _File::_bind_methods() { - ClassDB::bind_method(D_METHOD("open_encrypted", "path", "mode_flags", "key"), &_File::open_encrypted); ClassDB::bind_method(D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &_File::open_encrypted_pass); ClassDB::bind_method(D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &_File::open_compressed, DEFVAL(0)); @@ -1736,36 +1597,36 @@ void _File::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESSION_GZIP); } -_File::_File() { - - f = nullptr; - eswap = false; -} - _File::~_File() { - - if (f) + if (f) { memdelete(f); + } } -/////////////////////////////////////////////////////// +////// _Directory ////// Error _Directory::open(const String &p_path) { Error err; DirAccess *alt = DirAccess::open(p_path, &err); - if (!alt) + if (!alt) { return err; - if (d) + } + if (d) { memdelete(d); + } d = alt; + dir_open = true; return OK; } -Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { +bool _Directory::is_open() const { + return d && dir_open; +} - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); +Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); _list_skip_navigational = p_skip_navigational; _list_skip_hidden = p_skip_hidden; @@ -1774,56 +1635,59 @@ Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { } String _Directory::get_next() { - - ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); String next = d->get_next(); while (next != "" && ((_list_skip_navigational && (next == "." || next == "..")) || (_list_skip_hidden && d->current_is_hidden()))) { - next = d->get_next(); } return next; } -bool _Directory::current_is_dir() const { - ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); +bool _Directory::current_is_dir() const { + ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use."); return d->current_is_dir(); } void _Directory::list_dir_end() { - - ERR_FAIL_COND_MSG(!d, "Directory must be opened before use."); + ERR_FAIL_COND_MSG(!is_open(), "Directory must be opened before use."); d->list_dir_end(); } int _Directory::get_drive_count() { - - ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); return d->get_drive_count(); } -String _Directory::get_drive(int p_drive) { - ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); +String _Directory::get_drive(int p_drive) { + ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); return d->get_drive(p_drive); } + int _Directory::get_current_drive() { - ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); return d->get_current_drive(); } Error _Directory::change_dir(String p_dir) { + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly."); + Error err = d->change_dir(p_dir); - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); - return d->change_dir(p_dir); + if (err != OK) { + return err; + } + dir_open = true; + + return OK; } -String _Directory::get_current_dir() { - ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); +String _Directory::get_current_dir() { + ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); return d->get_current_dir(); } -Error _Directory::make_dir(String p_dir) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); +Error _Directory::make_dir(String p_dir) { + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir(p_dir); @@ -1832,9 +1696,9 @@ Error _Directory::make_dir(String p_dir) { } return d->make_dir(p_dir); } -Error _Directory::make_dir_recursive(String p_dir) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); +Error _Directory::make_dir_recursive(String p_dir) { + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir_recursive(p_dir); @@ -1845,9 +1709,7 @@ Error _Directory::make_dir_recursive(String p_dir) { } bool _Directory::file_exists(String p_file) { - - ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); - + ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly."); if (!p_file.is_rel_path()) { return FileAccess::exists(p_file); } @@ -1856,45 +1718,43 @@ bool _Directory::file_exists(String p_file) { } bool _Directory::dir_exists(String p_dir) { - ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly."); if (!p_dir.is_rel_path()) { - DirAccess *d = DirAccess::create_for_path(p_dir); bool exists = d->dir_exists(p_dir); memdelete(d); return exists; - - } else { - return d->dir_exists(p_dir); } + + return d->dir_exists(p_dir); } int _Directory::get_space_left() { - - ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); return d->get_space_left() / 1024 * 1024; //return value in megabytes, given binding is int } Error _Directory::copy(String p_from, String p_to) { - - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); return d->copy(p_from, p_to); } -Error _Directory::rename(String p_from, String p_to) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); +Error _Directory::rename(String p_from, String p_to) { + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_from.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_from); + ERR_FAIL_COND_V_MSG(!d->file_exists(p_from), ERR_DOES_NOT_EXIST, "File does not exist."); Error err = d->rename(p_from, p_to); memdelete(d); return err; } + ERR_FAIL_COND_V_MSG(!d->file_exists(p_from), ERR_DOES_NOT_EXIST, "File does not exist."); return d->rename(p_from, p_to); } -Error _Directory::remove(String p_name) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); +Error _Directory::remove(String p_name) { + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_name.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_name); Error err = d->remove(p_name); @@ -1906,7 +1766,6 @@ Error _Directory::remove(String p_name) { } void _Directory::_bind_methods() { - ClassDB::bind_method(D_METHOD("open", "path"), &_Directory::open); ClassDB::bind_method(D_METHOD("list_dir_begin", "skip_navigational", "skip_hidden"), &_Directory::list_dir_begin, DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_next"), &_Directory::get_next); @@ -1929,16 +1788,17 @@ void _Directory::_bind_methods() { } _Directory::_Directory() { - d = DirAccess::create(DirAccess::ACCESS_RESOURCES); } _Directory::~_Directory() { - - if (d) + if (d) { memdelete(d); + } } +////// _Marshalls ////// + _Marshalls *_Marshalls::singleton = nullptr; _Marshalls *_Marshalls::get_singleton() { @@ -1946,7 +1806,6 @@ _Marshalls *_Marshalls::get_singleton() { } String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) { - int len; Error err = encode_variant(p_var, nullptr, len, p_full_objects); ERR_FAIL_COND_V_MSG(err != OK, "", "Error when trying to encode Variant."); @@ -1962,10 +1821,9 @@ String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) ERR_FAIL_COND_V(ret == "", ret); return ret; -}; +} Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) { - int strlen = p_str.length(); CharString cstr = p_str.ascii(); @@ -1981,17 +1839,15 @@ Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); return v; -}; +} String _Marshalls::raw_to_base64(const Vector<uint8_t> &p_arr) { - String ret = CryptoCore::b64_encode_str(p_arr.ptr(), p_arr.size()); ERR_FAIL_COND_V(ret == "", ret); return ret; -}; +} Vector<uint8_t> _Marshalls::base64_to_raw(const String &p_str) { - int strlen = p_str.length(); CharString cstr = p_str.ascii(); @@ -2006,18 +1862,16 @@ Vector<uint8_t> _Marshalls::base64_to_raw(const String &p_str) { buf.resize(arr_len); return buf; -}; +} String _Marshalls::utf8_to_base64(const String &p_str) { - CharString cstr = p_str.utf8(); String ret = CryptoCore::b64_encode_str((unsigned char *)cstr.get_data(), cstr.length()); ERR_FAIL_COND_V(ret == "", ret); return ret; -}; +} String _Marshalls::base64_to_utf8(const String &p_str) { - int strlen = p_str.length(); CharString cstr = p_str.ascii(); @@ -2032,10 +1886,9 @@ String _Marshalls::base64_to_utf8(const String &p_str) { String ret = String::utf8((char *)&w[0]); return ret; -}; +} void _Marshalls::_bind_methods() { - ClassDB::bind_method(D_METHOD("variant_to_base64", "variant", "full_objects"), &_Marshalls::variant_to_base64, DEFVAL(false)); ClassDB::bind_method(D_METHOD("base64_to_variant", "base64_str", "allow_objects"), &_Marshalls::base64_to_variant, DEFVAL(false)); @@ -2044,60 +1897,51 @@ void _Marshalls::_bind_methods() { ClassDB::bind_method(D_METHOD("utf8_to_base64", "utf8_str"), &_Marshalls::utf8_to_base64); ClassDB::bind_method(D_METHOD("base64_to_utf8", "base64_str"), &_Marshalls::base64_to_utf8); -}; +} -//////////////// +////// _Semaphore ////// void _Semaphore::wait() { - semaphore.wait(); } Error _Semaphore::try_wait() { - return semaphore.try_wait() ? OK : ERR_BUSY; } void _Semaphore::post() { - semaphore.post(); } void _Semaphore::_bind_methods() { - ClassDB::bind_method(D_METHOD("wait"), &_Semaphore::wait); ClassDB::bind_method(D_METHOD("try_wait"), &_Semaphore::try_wait); ClassDB::bind_method(D_METHOD("post"), &_Semaphore::post); } -/////////////// +////// _Mutex ////// void _Mutex::lock() { - mutex.lock(); } Error _Mutex::try_lock() { - return mutex.try_lock(); } void _Mutex::unlock() { - mutex.unlock(); } void _Mutex::_bind_methods() { - ClassDB::bind_method(D_METHOD("lock"), &_Mutex::lock); ClassDB::bind_method(D_METHOD("try_lock"), &_Mutex::try_lock); ClassDB::bind_method(D_METHOD("unlock"), &_Mutex::unlock); } -/////////////// +////// _Thread ////// void _Thread::_start_func(void *ud) { - Ref<_Thread> *tud = (Ref<_Thread> *)ud; Ref<_Thread> t = *tud; memdelete(tud); @@ -2108,23 +1952,18 @@ void _Thread::_start_func(void *ud) { t->ret = t->target_instance->call(t->target_method, arg, 1, ce); if (ce.error != Callable::CallError::CALL_OK) { - String reason; switch (ce.error) { case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { - reason = "Invalid Argument #" + itos(ce.argument); } break; case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: { - reason = "Too Many Arguments"; } break; case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: { - reason = "Too Few Arguments"; } break; case Callable::CallError::CALL_ERROR_INVALID_METHOD: { - reason = "Method Not Found"; } break; default: { @@ -2136,7 +1975,6 @@ void _Thread::_start_func(void *ud) { } Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { - ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "Thread already started."); ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); @@ -2165,19 +2003,18 @@ Error _Thread::start(Object *p_instance, const StringName &p_method, const Varia } String _Thread::get_id() const { - - if (!thread) + if (!thread) { return String(); + } return itos(thread->get_id()); } bool _Thread::is_active() const { - return active; } -Variant _Thread::wait_to_finish() { +Variant _Thread::wait_to_finish() { ERR_FAIL_COND_V_MSG(!thread, Variant(), "Thread must exist to wait for its completion."); ERR_FAIL_COND_V_MSG(!active, Variant(), "Thread must be active to wait for its completion."); Thread::wait_to_finish(thread); @@ -2186,15 +2023,15 @@ Variant _Thread::wait_to_finish() { target_method = StringName(); target_instance = nullptr; userdata = Variant(); - if (thread) + if (thread) { memdelete(thread); + } thread = nullptr; return r; } void _Thread::_bind_methods() { - ClassDB::bind_method(D_METHOD("start", "instance", "method", "userdata", "priority"), &_Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); ClassDB::bind_method(D_METHOD("get_id"), &_Thread::get_id); ClassDB::bind_method(D_METHOD("is_active"), &_Thread::is_active); @@ -2204,22 +2041,14 @@ void _Thread::_bind_methods() { BIND_ENUM_CONSTANT(PRIORITY_NORMAL); BIND_ENUM_CONSTANT(PRIORITY_HIGH); } -_Thread::_Thread() { - - active = false; - thread = nullptr; - target_instance = nullptr; -} _Thread::~_Thread() { - ERR_FAIL_COND_MSG(active, "Reference to a Thread object was lost while the thread is still running..."); } -///////////////////////////////////// +////// _ClassDB ////// PackedStringArray _ClassDB::get_class_list() const { - List<StringName> classes; ClassDB::get_class_list(&classes); @@ -2232,8 +2061,8 @@ PackedStringArray _ClassDB::get_class_list() const { return ret; } -PackedStringArray _ClassDB::get_inheriters_from_class(const StringName &p_class) const { +PackedStringArray _ClassDB::get_inheriters_from_class(const StringName &p_class) const { List<StringName> classes; ClassDB::get_inheriters_from_class(p_class, &classes); @@ -2246,27 +2075,28 @@ PackedStringArray _ClassDB::get_inheriters_from_class(const StringName &p_class) return ret; } -StringName _ClassDB::get_parent_class(const StringName &p_class) const { +StringName _ClassDB::get_parent_class(const StringName &p_class) const { return ClassDB::get_parent_class(p_class); } -bool _ClassDB::class_exists(const StringName &p_class) const { +bool _ClassDB::class_exists(const StringName &p_class) const { return ClassDB::class_exists(p_class); } -bool _ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inherits) const { +bool _ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inherits) const { return ClassDB::is_parent_class(p_class, p_inherits); } -bool _ClassDB::can_instance(const StringName &p_class) const { +bool _ClassDB::can_instance(const StringName &p_class) const { return ClassDB::can_instance(p_class); } -Variant _ClassDB::instance(const StringName &p_class) const { +Variant _ClassDB::instance(const StringName &p_class) const { Object *obj = ClassDB::instance(p_class); - if (!obj) + if (!obj) { return Variant(); + } Reference *r = Object::cast_to<Reference>(obj); if (r) { @@ -2277,11 +2107,10 @@ Variant _ClassDB::instance(const StringName &p_class) const { } bool _ClassDB::has_signal(StringName p_class, StringName p_signal) const { - return ClassDB::has_signal(p_class, p_signal); } -Dictionary _ClassDB::get_signal(StringName p_class, StringName p_signal) const { +Dictionary _ClassDB::get_signal(StringName p_class, StringName p_signal) const { MethodInfo signal; if (ClassDB::get_signal(p_class, p_signal, &signal)) { return signal.operator Dictionary(); @@ -2289,8 +2118,8 @@ Dictionary _ClassDB::get_signal(StringName p_class, StringName p_signal) const { return Dictionary(); } } -Array _ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const { +Array _ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const { List<MethodInfo> signals; ClassDB::get_signal_list(p_class, &signals, p_no_inheritance); Array ret; @@ -2303,7 +2132,6 @@ Array _ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const } Array _ClassDB::get_property_list(StringName p_class, bool p_no_inheritance) const { - List<PropertyInfo> plist; ClassDB::get_property_list(p_class, &plist, p_no_inheritance); Array ret; @@ -2332,12 +2160,10 @@ Error _ClassDB::set_property(Object *p_object, const StringName &p_property, con } bool _ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const { - return ClassDB::has_method(p_class, p_method, p_no_inheritance); } Array _ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const { - List<MethodInfo> methods; ClassDB::get_method_list(p_class, &methods, p_no_inheritance); Array ret; @@ -2356,7 +2182,6 @@ Array _ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const } PackedStringArray _ClassDB::get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const { - List<String> constants; ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance); @@ -2371,31 +2196,27 @@ PackedStringArray _ClassDB::get_integer_constant_list(const StringName &p_class, } bool _ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name) const { - bool success; ClassDB::get_integer_constant(p_class, p_name, &success); return success; } int _ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name) const { - bool found; int c = ClassDB::get_integer_constant(p_class, p_name, &found); ERR_FAIL_COND_V(!found, 0); return c; } -StringName _ClassDB::get_category(const StringName &p_node) const { +StringName _ClassDB::get_category(const StringName &p_node) const { return ClassDB::get_category(p_node); } bool _ClassDB::is_class_enabled(StringName p_class) const { - return ClassDB::is_class_enabled(p_class); } void _ClassDB::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_class_list"), &_ClassDB::get_class_list); ClassDB::bind_method(D_METHOD("get_inheriters_from_class", "class"), &_ClassDB::get_inheriters_from_class); ClassDB::bind_method(D_METHOD("get_parent_class", "class"), &_ClassDB::get_parent_class); @@ -2425,18 +2246,13 @@ void _ClassDB::_bind_methods() { ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &_ClassDB::is_class_enabled); } -_ClassDB::_ClassDB() { -} -_ClassDB::~_ClassDB() { -} -/////////////////////////////// +////// _Engine ////// void _Engine::set_iterations_per_second(int p_ips) { - Engine::get_singleton()->set_iterations_per_second(p_ips); } -int _Engine::get_iterations_per_second() const { +int _Engine::get_iterations_per_second() const { return Engine::get_singleton()->get_iterations_per_second(); } @@ -2461,18 +2277,15 @@ int _Engine::get_target_fps() const { } float _Engine::get_frames_per_second() const { - return Engine::get_singleton()->get_frames_per_second(); } uint64_t _Engine::get_physics_frames() const { - return Engine::get_singleton()->get_physics_frames(); } -uint64_t _Engine::get_idle_frames() const { - - return Engine::get_singleton()->get_idle_frames(); +uint64_t _Engine::get_process_frames() const { + return Engine::get_singleton()->get_process_frames(); } void _Engine::set_time_scale(float p_scale) { @@ -2480,23 +2293,19 @@ void _Engine::set_time_scale(float p_scale) { } float _Engine::get_time_scale() { - return Engine::get_singleton()->get_time_scale(); } int _Engine::get_frames_drawn() { - return Engine::get_singleton()->get_frames_drawn(); } MainLoop *_Engine::get_main_loop() const { - //needs to remain in OS, since it's actually OS that interacts with it, but it's better exposed here return OS::get_singleton()->get_main_loop(); } Dictionary _Engine::get_version_info() const { - return Engine::get_singleton()->get_version_info(); } @@ -2525,27 +2334,22 @@ bool _Engine::is_in_physics_frame() const { } bool _Engine::has_singleton(const String &p_name) const { - return Engine::get_singleton()->has_singleton(p_name); } Object *_Engine::get_singleton_object(const String &p_name) const { - return Engine::get_singleton()->get_singleton_object(p_name); } void _Engine::set_editor_hint(bool p_enabled) { - Engine::get_singleton()->set_editor_hint(p_enabled); } bool _Engine::is_editor_hint() const { - return Engine::get_singleton()->is_editor_hint(); } void _Engine::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_iterations_per_second", "iterations_per_second"), &_Engine::set_iterations_per_second); ClassDB::bind_method(D_METHOD("get_iterations_per_second"), &_Engine::get_iterations_per_second); ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &_Engine::set_physics_jitter_fix); @@ -2560,7 +2364,7 @@ void _Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_frames_drawn"), &_Engine::get_frames_drawn); ClassDB::bind_method(D_METHOD("get_frames_per_second"), &_Engine::get_frames_per_second); ClassDB::bind_method(D_METHOD("get_physics_frames"), &_Engine::get_physics_frames); - ClassDB::bind_method(D_METHOD("get_idle_frames"), &_Engine::get_idle_frames); + ClassDB::bind_method(D_METHOD("get_process_frames"), &_Engine::get_process_frames); ClassDB::bind_method(D_METHOD("get_main_loop"), &_Engine::get_main_loop); @@ -2588,9 +2392,7 @@ void _Engine::_bind_methods() { _Engine *_Engine::singleton = nullptr; -_Engine::_Engine() { - singleton = this; -} +////// _JSON ////// void JSONParseResult::_bind_methods() { ClassDB::bind_method(D_METHOD("get_error"), &JSONParseResult::get_error); @@ -2664,6 +2466,153 @@ Ref<JSONParseResult> _JSON::parse(const String &p_json) { _JSON *_JSON::singleton = nullptr; -_JSON::_JSON() { - singleton = this; +////// _EngineDebugger ////// + +void _EngineDebugger::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_active"), &_EngineDebugger::is_active); + + ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &_EngineDebugger::register_profiler); + ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &_EngineDebugger::unregister_profiler); + ClassDB::bind_method(D_METHOD("is_profiling", "name"), &_EngineDebugger::is_profiling); + ClassDB::bind_method(D_METHOD("has_profiler", "name"), &_EngineDebugger::has_profiler); + + ClassDB::bind_method(D_METHOD("profiler_add_frame_data", "name", "data"), &_EngineDebugger::profiler_add_frame_data); + ClassDB::bind_method(D_METHOD("profiler_enable", "name", "enable", "arguments"), &_EngineDebugger::profiler_enable, DEFVAL(Array())); + + ClassDB::bind_method(D_METHOD("register_message_capture", "name", "callable"), &_EngineDebugger::register_message_capture); + ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &_EngineDebugger::unregister_message_capture); + ClassDB::bind_method(D_METHOD("has_capture", "name"), &_EngineDebugger::has_capture); + + ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &_EngineDebugger::send_message); +} + +bool _EngineDebugger::is_active() { + return EngineDebugger::is_active(); +} + +void _EngineDebugger::register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) { + ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler already registered: " + p_name); + profilers.insert(p_name, ProfilerCallable(p_toggle, p_add, p_tick)); + ProfilerCallable &p = profilers[p_name]; + EngineDebugger::Profiler profiler( + &p, + &_EngineDebugger::call_toggle, + &_EngineDebugger::call_add, + &_EngineDebugger::call_tick); + EngineDebugger::register_profiler(p_name, profiler); +} + +void _EngineDebugger::unregister_profiler(const StringName &p_name) { + ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name); + EngineDebugger::unregister_profiler(p_name); + profilers.erase(p_name); } + +bool _EngineDebugger::_EngineDebugger::is_profiling(const StringName &p_name) { + return EngineDebugger::is_profiling(p_name); +} + +bool _EngineDebugger::has_profiler(const StringName &p_name) { + return EngineDebugger::has_profiler(p_name); +} + +void _EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) { + EngineDebugger::profiler_add_frame_data(p_name, p_data); +} + +void _EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) { + if (EngineDebugger::get_singleton()) { + EngineDebugger::get_singleton()->profiler_enable(p_name, p_enabled, p_opts); + } +} + +void _EngineDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) { + ERR_FAIL_COND_MSG(captures.has(p_name) || has_capture(p_name), "Capture already registered: " + p_name); + captures.insert(p_name, p_callable); + Callable &c = captures[p_name]; + EngineDebugger::Capture capture(&c, &_EngineDebugger::call_capture); + EngineDebugger::register_message_capture(p_name, capture); +} + +void _EngineDebugger::unregister_message_capture(const StringName &p_name) { + ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name); + EngineDebugger::unregister_message_capture(p_name); + captures.erase(p_name); +} + +bool _EngineDebugger::has_capture(const StringName &p_name) { + return EngineDebugger::has_capture(p_name); +} + +void _EngineDebugger::send_message(const String &p_msg, const Array &p_data) { + ERR_FAIL_COND_MSG(!EngineDebugger::is_active(), "Can't send message. No active debugger"); + EngineDebugger::get_singleton()->send_message(p_msg, p_data); +} + +void _EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_opts) { + Callable &toggle = ((ProfilerCallable *)p_user)->callable_toggle; + if (toggle.is_null()) { + return; + } + Variant enable = p_enable, opts = p_opts; + const Variant *args[2] = { &enable, &opts }; + Variant retval; + Callable::CallError err; + toggle.call(args, 2, retval, err); + ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'toggle' to callable: " + Variant::get_callable_error_text(toggle, args, 2, err)); +} + +void _EngineDebugger::call_add(void *p_user, const Array &p_data) { + Callable &add = ((ProfilerCallable *)p_user)->callable_add; + if (add.is_null()) { + return; + } + Variant data = p_data; + const Variant *args[1] = { &data }; + Variant retval; + Callable::CallError err; + add.call(args, 1, retval, err); + ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'add' to callable: " + Variant::get_callable_error_text(add, args, 1, err)); +} + +void _EngineDebugger::call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + Callable &tick = ((ProfilerCallable *)p_user)->callable_tick; + if (tick.is_null()) { + return; + } + Variant frame_time = p_frame_time, idle_time = p_idle_time, physics_time = p_physics_time, physics_frame_time = p_physics_frame_time; + const Variant *args[4] = { &frame_time, &idle_time, &physics_time, &physics_frame_time }; + Variant retval; + Callable::CallError err; + tick.call(args, 4, retval, err); + ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'tick' to callable: " + Variant::get_callable_error_text(tick, args, 4, err)); +} + +Error _EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) { + Callable &capture = *(Callable *)p_user; + if (capture.is_null()) { + return FAILED; + } + Variant cmd = p_cmd, data = p_data; + const Variant *args[2] = { &cmd, &data }; + Variant retval; + Callable::CallError err; + capture.call(args, 2, retval, err); + ERR_FAIL_COND_V_MSG(err.error != Callable::CallError::CALL_OK, FAILED, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(capture, args, 2, err)); + ERR_FAIL_COND_V_MSG(retval.get_type() != Variant::BOOL, FAILED, "Error calling 'capture' to callable: " + String(capture) + ". Return type is not bool."); + r_captured = retval; + return OK; +} + +_EngineDebugger::~_EngineDebugger() { + for (Map<StringName, Callable>::Element *E = captures.front(); E; E = E->next()) { + EngineDebugger::unregister_message_capture(E->key()); + } + captures.clear(); + for (Map<StringName, ProfilerCallable>::Element *E = profilers.front(); E; E = E->next()) { + EngineDebugger::unregister_profiler(E->key()); + } + profilers.clear(); +} + +_EngineDebugger *_EngineDebugger::singleton = nullptr; diff --git a/core/bind/core_bind.h b/core/core_bind.h index d5f44cdc44..665858cd26 100644 --- a/core/bind/core_bind.h +++ b/core/core_bind.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,8 +31,8 @@ #ifndef CORE_BIND_H #define CORE_BIND_H -#include "core/image.h" #include "core/io/compression.h" +#include "core/io/image.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/os/dir_access.h" @@ -69,7 +69,7 @@ public: bool has_cached(const String &p_path); bool exists(const String &p_path, const String &p_type_hint = ""); - _ResourceLoader(); + _ResourceLoader() { singleton = this; } }; VARIANT_ENUM_CAST(_ResourceLoader::ThreadLoadStatus); @@ -83,7 +83,6 @@ protected: public: enum SaverFlags { - FLAG_RELATIVE_PATHS = 1, FLAG_BUNDLE_RESOURCES = 2, FLAG_CHANGE_PATH = 4, @@ -98,7 +97,7 @@ public: Error save(const String &p_path, const RES &p_resource, SaverFlags p_flags); Vector<String> get_recognized_extensions(const RES &p_resource); - _ResourceSaver(); + _ResourceSaver() { singleton = this; } }; VARIANT_ENUM_CAST(_ResourceSaver::SaverFlags); @@ -156,8 +155,8 @@ public: int get_low_processor_usage_mode_sleep_usec() const; String get_executable_path() const; - int execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking = true, Array p_output = Array(), bool p_read_stderr = false); - + int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false); + int create_process(const String &p_path, const Vector<String> &p_arguments); Error kill(int p_pid); Error shell_open(String p_uri); @@ -199,9 +198,7 @@ public: Dictionary get_datetime_from_unix_time(int64_t unix_time_val) const; int64_t get_unix_time_from_datetime(Dictionary datetime) const; Dictionary get_time_zone_info() const; - uint64_t get_unix_time() const; - uint64_t get_system_time_secs() const; - uint64_t get_system_time_msecs() const; + double get_unix_time() const; uint64_t get_static_memory_usage() const; uint64_t get_static_memory_peak_usage() const; @@ -210,7 +207,6 @@ public: void delay_msec(uint32_t p_msec) const; uint32_t get_ticks_msec() const; uint64_t get_ticks_usec() const; - uint32_t get_splash_tick_msec() const; bool can_use_threads() const; @@ -243,9 +239,14 @@ public: bool request_permissions(); Vector<String> get_granted_permissions() const; + int get_tablet_driver_count() const; + String get_tablet_driver_name(int p_driver) const; + String get_current_tablet_driver() const; + void set_current_tablet_driver(const String &p_driver); + static _OS *get_singleton() { return singleton; } - _OS(); + _OS() { singleton = this; } }; VARIANT_ENUM_CAST(_OS::VideoDriver); @@ -253,45 +254,31 @@ VARIANT_ENUM_CAST(_OS::Weekday); VARIANT_ENUM_CAST(_OS::Month); VARIANT_ENUM_CAST(_OS::SystemDir); -class _Geometry : public Object { +class _Geometry2D : public Object { + GDCLASS(_Geometry2D, Object); - GDCLASS(_Geometry, 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, @@ -300,14 +287,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 { @@ -322,30 +309,57 @@ 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(); + _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 _File : public Reference { +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); + Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane); + + _Geometry3D() { singleton = this; } +}; + +class _File : public Reference { GDCLASS(_File, Reference); - FileAccess *f; - bool eswap; + + FileAccess *f = nullptr; + bool eswap = false; protected: static void _bind_methods(); public: enum ModeFlags { - READ = 1, WRITE = 2, READ_WRITE = 3, @@ -429,7 +443,7 @@ public: uint64_t get_modified_time(const String &p_file) const; - _File(); + _File() {} virtual ~_File(); }; @@ -437,9 +451,9 @@ VARIANT_ENUM_CAST(_File::ModeFlags); VARIANT_ENUM_CAST(_File::CompressionMode); class _Directory : public Reference { - GDCLASS(_Directory, Reference); DirAccess *d; + bool dir_open = false; protected: static void _bind_methods(); @@ -447,6 +461,8 @@ protected: public: Error open(const String &p_path); + bool is_open() const; + Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); // This starts dir listing. String get_next(); bool current_is_dir() const; @@ -476,12 +492,11 @@ public: virtual ~_Directory(); private: - bool _list_skip_navigational; - bool _list_skip_hidden; + bool _list_skip_navigational = false; + bool _list_skip_hidden = false; }; class _Marshalls : public Object { - GDCLASS(_Marshalls, Object); static _Marshalls *singleton; @@ -506,7 +521,6 @@ public: }; class _Mutex : public Reference { - GDCLASS(_Mutex, Reference); Mutex mutex; @@ -519,7 +533,6 @@ public: }; class _Semaphore : public Reference { - GDCLASS(_Semaphore, Reference); Semaphore semaphore; @@ -532,22 +545,20 @@ public: }; class _Thread : public Reference { - GDCLASS(_Thread, Reference); protected: Variant ret; Variant userdata; - volatile bool active; - Object *target_instance; + volatile bool active = false; + Object *target_instance = nullptr; StringName target_method; - Thread *thread; + Thread *thread = nullptr; static void _bind_methods(); static void _start_func(void *ud); public: enum Priority { - PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH, @@ -559,14 +570,13 @@ public: bool is_active() const; Variant wait_to_finish(); - _Thread(); + _Thread() {} ~_Thread(); }; VARIANT_ENUM_CAST(_Thread::Priority); class _ClassDB : public Object { - GDCLASS(_ClassDB, Object); protected: @@ -600,8 +610,8 @@ public: bool is_class_enabled(StringName p_class) const; - _ClassDB(); - ~_ClassDB(); + _ClassDB() {} + ~_ClassDB() {} }; class _Engine : public Object { @@ -625,7 +635,7 @@ public: float get_frames_per_second() const; uint64_t get_physics_frames() const; - uint64_t get_idle_frames() const; + uint64_t get_process_frames() const; int get_frames_drawn(); @@ -649,7 +659,7 @@ public: void set_editor_hint(bool p_enabled); bool is_editor_hint() const; - _Engine(); + _Engine() { singleton = this; } }; class _JSON; @@ -661,7 +671,7 @@ class JSONParseResult : public Reference { Error error; String error_string; - int error_line; + int error_line = -1; Variant result; @@ -681,8 +691,7 @@ public: void set_result(const Variant &p_result); Variant get_result() const; - JSONParseResult() : - error_line(-1) {} + JSONParseResult() {} }; class _JSON : public Object { @@ -698,7 +707,61 @@ public: String print(const Variant &p_value, const String &p_indent = "", bool p_sort_keys = false); Ref<JSONParseResult> parse(const String &p_json); - _JSON(); + _JSON() { singleton = this; } +}; + +class _EngineDebugger : public Object { + GDCLASS(_EngineDebugger, Object); + + class ProfilerCallable { + friend class _EngineDebugger; + + Callable callable_toggle; + Callable callable_add; + Callable callable_tick; + + public: + ProfilerCallable() {} + + ProfilerCallable(const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) { + callable_toggle = p_toggle; + callable_add = p_add; + callable_tick = p_tick; + } + }; + + Map<StringName, Callable> captures; + Map<StringName, ProfilerCallable> profilers; + +protected: + static void _bind_methods(); + static _EngineDebugger *singleton; + +public: + static _EngineDebugger *get_singleton() { return singleton; } + + bool is_active(); + + void register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick); + void unregister_profiler(const StringName &p_name); + bool is_profiling(const StringName &p_name); + bool has_profiler(const StringName &p_name); + void profiler_add_frame_data(const StringName &p_name, const Array &p_data); + void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array()); + + void register_message_capture(const StringName &p_name, const Callable &p_callable); + void unregister_message_capture(const StringName &p_name); + bool has_capture(const StringName &p_name); + + void send_message(const String &p_msg, const Array &p_data); + + static void call_toggle(void *p_user, bool p_enable, const Array &p_opts); + static void call_add(void *p_user, const Array &p_data); + static void call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time); + static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured); + + _EngineDebugger() { singleton = this; } + ~_EngineDebugger(); }; #endif // CORE_BIND_H diff --git a/core/core_builders.py b/core/core_builders.py index d03874608e..004475faa7 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -117,14 +117,18 @@ def make_donors_header(target, source, env): sections = [ "Platinum sponsors", "Gold sponsors", + "Silver sponsors", + "Bronze sponsors", "Mini sponsors", "Gold donors", "Silver donors", "Bronze donors", ] sections_id = [ - "DONORS_SPONSOR_PLAT", + "DONORS_SPONSOR_PLATINUM", "DONORS_SPONSOR_GOLD", + "DONORS_SPONSOR_SILVER", + "DONORS_SPONSOR_BRONZE", "DONORS_SPONSOR_MINI", "DONORS_GOLD", "DONORS_SILVER", diff --git a/core/core_constants.cpp b/core/core_constants.cpp new file mode 100644 index 0000000000..3df121b9cb --- /dev/null +++ b/core/core_constants.cpp @@ -0,0 +1,659 @@ +/*************************************************************************/ +/* core_constants.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core_constants.h" + +#include "core/input/input_event.h" +#include "core/object/class_db.h" +#include "core/os/keyboard.h" +#include "core/variant/variant.h" + +struct _CoreConstant { +#ifdef DEBUG_METHODS_ENABLED + StringName enum_name; + bool ignore_value_in_docs = false; +#endif + const char *name; + int value = 0; + + _CoreConstant() {} + +#ifdef DEBUG_METHODS_ENABLED + _CoreConstant(const StringName &p_enum_name, const char *p_name, int p_value, bool p_ignore_value_in_docs = false) : + enum_name(p_enum_name), + ignore_value_in_docs(p_ignore_value_in_docs), + name(p_name), + value(p_value) { + } +#else + _CoreConstant(const char *p_name, int p_value) : + name(p_name), + value(p_value) { + } +#endif +}; + +static Vector<_CoreConstant> _global_constants; + +#ifdef DEBUG_METHODS_ENABLED + +#define BIND_CORE_CONSTANT(m_constant) \ + _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); + +#define BIND_CORE_ENUM_CONSTANT(m_constant) \ + _global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant)); + +#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \ + _global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant)); + +#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \ + _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant, true)); + +#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \ + _global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant, true)); + +#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ + _global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant, true)); + +#else + +#define BIND_CORE_CONSTANT(m_constant) \ + _global_constants.push_back(_CoreConstant(#m_constant, m_constant)); + +#define BIND_CORE_ENUM_CONSTANT(m_constant) \ + _global_constants.push_back(_CoreConstant(#m_constant, m_constant)); + +#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \ + _global_constants.push_back(_CoreConstant(m_custom_name, m_constant)); + +#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \ + _global_constants.push_back(_CoreConstant(#m_constant, m_constant)); + +#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \ + _global_constants.push_back(_CoreConstant(#m_constant, m_constant)); + +#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ + _global_constants.push_back(_CoreConstant(m_custom_name, m_constant)); + +#endif + +VARIANT_ENUM_CAST(KeyList); +VARIANT_ENUM_CAST(KeyModifierMask); +VARIANT_ENUM_CAST(ButtonList); +VARIANT_ENUM_CAST(JoyButtonList); +VARIANT_ENUM_CAST(JoyAxisList); +VARIANT_ENUM_CAST(MidiMessageList); + +void register_global_constants() { + BIND_CORE_ENUM_CONSTANT(SIDE_LEFT); + BIND_CORE_ENUM_CONSTANT(SIDE_TOP); + BIND_CORE_ENUM_CONSTANT(SIDE_RIGHT); + BIND_CORE_ENUM_CONSTANT(SIDE_BOTTOM); + + BIND_CORE_ENUM_CONSTANT(CORNER_TOP_LEFT); + BIND_CORE_ENUM_CONSTANT(CORNER_TOP_RIGHT); + BIND_CORE_ENUM_CONSTANT(CORNER_BOTTOM_RIGHT); + BIND_CORE_ENUM_CONSTANT(CORNER_BOTTOM_LEFT); + + BIND_CORE_ENUM_CONSTANT(VERTICAL); + BIND_CORE_ENUM_CONSTANT(HORIZONTAL); + + BIND_CORE_ENUM_CONSTANT(HALIGN_LEFT); + BIND_CORE_ENUM_CONSTANT(HALIGN_CENTER); + BIND_CORE_ENUM_CONSTANT(HALIGN_RIGHT); + BIND_CORE_ENUM_CONSTANT(HALIGN_FILL); + + BIND_CORE_ENUM_CONSTANT(VALIGN_TOP); + BIND_CORE_ENUM_CONSTANT(VALIGN_CENTER); + BIND_CORE_ENUM_CONSTANT(VALIGN_BOTTOM); + + // huge list of keys + BIND_CORE_CONSTANT(SPKEY); + + BIND_CORE_ENUM_CONSTANT(KEY_ESCAPE); + BIND_CORE_ENUM_CONSTANT(KEY_TAB); + BIND_CORE_ENUM_CONSTANT(KEY_BACKTAB); + BIND_CORE_ENUM_CONSTANT(KEY_BACKSPACE); + BIND_CORE_ENUM_CONSTANT(KEY_ENTER); + BIND_CORE_ENUM_CONSTANT(KEY_KP_ENTER); + BIND_CORE_ENUM_CONSTANT(KEY_INSERT); + BIND_CORE_ENUM_CONSTANT(KEY_DELETE); + BIND_CORE_ENUM_CONSTANT(KEY_PAUSE); + BIND_CORE_ENUM_CONSTANT(KEY_PRINT); + BIND_CORE_ENUM_CONSTANT(KEY_SYSREQ); + BIND_CORE_ENUM_CONSTANT(KEY_CLEAR); + BIND_CORE_ENUM_CONSTANT(KEY_HOME); + BIND_CORE_ENUM_CONSTANT(KEY_END); + BIND_CORE_ENUM_CONSTANT(KEY_LEFT); + BIND_CORE_ENUM_CONSTANT(KEY_UP); + BIND_CORE_ENUM_CONSTANT(KEY_RIGHT); + BIND_CORE_ENUM_CONSTANT(KEY_DOWN); + BIND_CORE_ENUM_CONSTANT(KEY_PAGEUP); + BIND_CORE_ENUM_CONSTANT(KEY_PAGEDOWN); + BIND_CORE_ENUM_CONSTANT(KEY_SHIFT); + BIND_CORE_ENUM_CONSTANT(KEY_CONTROL); + BIND_CORE_ENUM_CONSTANT(KEY_META); + BIND_CORE_ENUM_CONSTANT(KEY_ALT); + BIND_CORE_ENUM_CONSTANT(KEY_CAPSLOCK); + BIND_CORE_ENUM_CONSTANT(KEY_NUMLOCK); + BIND_CORE_ENUM_CONSTANT(KEY_SCROLLLOCK); + BIND_CORE_ENUM_CONSTANT(KEY_F1); + BIND_CORE_ENUM_CONSTANT(KEY_F2); + BIND_CORE_ENUM_CONSTANT(KEY_F3); + BIND_CORE_ENUM_CONSTANT(KEY_F4); + BIND_CORE_ENUM_CONSTANT(KEY_F5); + BIND_CORE_ENUM_CONSTANT(KEY_F6); + BIND_CORE_ENUM_CONSTANT(KEY_F7); + BIND_CORE_ENUM_CONSTANT(KEY_F8); + BIND_CORE_ENUM_CONSTANT(KEY_F9); + BIND_CORE_ENUM_CONSTANT(KEY_F10); + BIND_CORE_ENUM_CONSTANT(KEY_F11); + BIND_CORE_ENUM_CONSTANT(KEY_F12); + BIND_CORE_ENUM_CONSTANT(KEY_F13); + BIND_CORE_ENUM_CONSTANT(KEY_F14); + BIND_CORE_ENUM_CONSTANT(KEY_F15); + BIND_CORE_ENUM_CONSTANT(KEY_F16); + BIND_CORE_ENUM_CONSTANT(KEY_KP_MULTIPLY); + BIND_CORE_ENUM_CONSTANT(KEY_KP_DIVIDE); + BIND_CORE_ENUM_CONSTANT(KEY_KP_SUBTRACT); + BIND_CORE_ENUM_CONSTANT(KEY_KP_PERIOD); + BIND_CORE_ENUM_CONSTANT(KEY_KP_ADD); + BIND_CORE_ENUM_CONSTANT(KEY_KP_0); + BIND_CORE_ENUM_CONSTANT(KEY_KP_1); + BIND_CORE_ENUM_CONSTANT(KEY_KP_2); + BIND_CORE_ENUM_CONSTANT(KEY_KP_3); + BIND_CORE_ENUM_CONSTANT(KEY_KP_4); + BIND_CORE_ENUM_CONSTANT(KEY_KP_5); + BIND_CORE_ENUM_CONSTANT(KEY_KP_6); + BIND_CORE_ENUM_CONSTANT(KEY_KP_7); + BIND_CORE_ENUM_CONSTANT(KEY_KP_8); + BIND_CORE_ENUM_CONSTANT(KEY_KP_9); + BIND_CORE_ENUM_CONSTANT(KEY_SUPER_L); + BIND_CORE_ENUM_CONSTANT(KEY_SUPER_R); + BIND_CORE_ENUM_CONSTANT(KEY_MENU); + BIND_CORE_ENUM_CONSTANT(KEY_HYPER_L); + BIND_CORE_ENUM_CONSTANT(KEY_HYPER_R); + BIND_CORE_ENUM_CONSTANT(KEY_HELP); + BIND_CORE_ENUM_CONSTANT(KEY_DIRECTION_L); + BIND_CORE_ENUM_CONSTANT(KEY_DIRECTION_R); + BIND_CORE_ENUM_CONSTANT(KEY_BACK); + BIND_CORE_ENUM_CONSTANT(KEY_FORWARD); + BIND_CORE_ENUM_CONSTANT(KEY_STOP); + BIND_CORE_ENUM_CONSTANT(KEY_REFRESH); + BIND_CORE_ENUM_CONSTANT(KEY_VOLUMEDOWN); + BIND_CORE_ENUM_CONSTANT(KEY_VOLUMEMUTE); + BIND_CORE_ENUM_CONSTANT(KEY_VOLUMEUP); + BIND_CORE_ENUM_CONSTANT(KEY_BASSBOOST); + BIND_CORE_ENUM_CONSTANT(KEY_BASSUP); + BIND_CORE_ENUM_CONSTANT(KEY_BASSDOWN); + BIND_CORE_ENUM_CONSTANT(KEY_TREBLEUP); + BIND_CORE_ENUM_CONSTANT(KEY_TREBLEDOWN); + BIND_CORE_ENUM_CONSTANT(KEY_MEDIAPLAY); + BIND_CORE_ENUM_CONSTANT(KEY_MEDIASTOP); + BIND_CORE_ENUM_CONSTANT(KEY_MEDIAPREVIOUS); + BIND_CORE_ENUM_CONSTANT(KEY_MEDIANEXT); + BIND_CORE_ENUM_CONSTANT(KEY_MEDIARECORD); + BIND_CORE_ENUM_CONSTANT(KEY_HOMEPAGE); + BIND_CORE_ENUM_CONSTANT(KEY_FAVORITES); + BIND_CORE_ENUM_CONSTANT(KEY_SEARCH); + BIND_CORE_ENUM_CONSTANT(KEY_STANDBY); + BIND_CORE_ENUM_CONSTANT(KEY_OPENURL); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHMAIL); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHMEDIA); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH0); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH1); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH2); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH3); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH4); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH5); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH6); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH7); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH8); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH9); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHA); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHB); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHC); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHD); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHE); + BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHF); + + BIND_CORE_ENUM_CONSTANT(KEY_UNKNOWN); + BIND_CORE_ENUM_CONSTANT(KEY_SPACE); + BIND_CORE_ENUM_CONSTANT(KEY_EXCLAM); + BIND_CORE_ENUM_CONSTANT(KEY_QUOTEDBL); + BIND_CORE_ENUM_CONSTANT(KEY_NUMBERSIGN); + BIND_CORE_ENUM_CONSTANT(KEY_DOLLAR); + BIND_CORE_ENUM_CONSTANT(KEY_PERCENT); + BIND_CORE_ENUM_CONSTANT(KEY_AMPERSAND); + BIND_CORE_ENUM_CONSTANT(KEY_APOSTROPHE); + BIND_CORE_ENUM_CONSTANT(KEY_PARENLEFT); + BIND_CORE_ENUM_CONSTANT(KEY_PARENRIGHT); + BIND_CORE_ENUM_CONSTANT(KEY_ASTERISK); + BIND_CORE_ENUM_CONSTANT(KEY_PLUS); + BIND_CORE_ENUM_CONSTANT(KEY_COMMA); + BIND_CORE_ENUM_CONSTANT(KEY_MINUS); + BIND_CORE_ENUM_CONSTANT(KEY_PERIOD); + BIND_CORE_ENUM_CONSTANT(KEY_SLASH); + BIND_CORE_ENUM_CONSTANT(KEY_0); + BIND_CORE_ENUM_CONSTANT(KEY_1); + BIND_CORE_ENUM_CONSTANT(KEY_2); + BIND_CORE_ENUM_CONSTANT(KEY_3); + BIND_CORE_ENUM_CONSTANT(KEY_4); + BIND_CORE_ENUM_CONSTANT(KEY_5); + BIND_CORE_ENUM_CONSTANT(KEY_6); + BIND_CORE_ENUM_CONSTANT(KEY_7); + BIND_CORE_ENUM_CONSTANT(KEY_8); + BIND_CORE_ENUM_CONSTANT(KEY_9); + BIND_CORE_ENUM_CONSTANT(KEY_COLON); + BIND_CORE_ENUM_CONSTANT(KEY_SEMICOLON); + BIND_CORE_ENUM_CONSTANT(KEY_LESS); + BIND_CORE_ENUM_CONSTANT(KEY_EQUAL); + BIND_CORE_ENUM_CONSTANT(KEY_GREATER); + BIND_CORE_ENUM_CONSTANT(KEY_QUESTION); + BIND_CORE_ENUM_CONSTANT(KEY_AT); + BIND_CORE_ENUM_CONSTANT(KEY_A); + BIND_CORE_ENUM_CONSTANT(KEY_B); + BIND_CORE_ENUM_CONSTANT(KEY_C); + BIND_CORE_ENUM_CONSTANT(KEY_D); + BIND_CORE_ENUM_CONSTANT(KEY_E); + BIND_CORE_ENUM_CONSTANT(KEY_F); + BIND_CORE_ENUM_CONSTANT(KEY_G); + BIND_CORE_ENUM_CONSTANT(KEY_H); + BIND_CORE_ENUM_CONSTANT(KEY_I); + BIND_CORE_ENUM_CONSTANT(KEY_J); + BIND_CORE_ENUM_CONSTANT(KEY_K); + BIND_CORE_ENUM_CONSTANT(KEY_L); + BIND_CORE_ENUM_CONSTANT(KEY_M); + BIND_CORE_ENUM_CONSTANT(KEY_N); + BIND_CORE_ENUM_CONSTANT(KEY_O); + BIND_CORE_ENUM_CONSTANT(KEY_P); + BIND_CORE_ENUM_CONSTANT(KEY_Q); + BIND_CORE_ENUM_CONSTANT(KEY_R); + BIND_CORE_ENUM_CONSTANT(KEY_S); + BIND_CORE_ENUM_CONSTANT(KEY_T); + BIND_CORE_ENUM_CONSTANT(KEY_U); + BIND_CORE_ENUM_CONSTANT(KEY_V); + BIND_CORE_ENUM_CONSTANT(KEY_W); + BIND_CORE_ENUM_CONSTANT(KEY_X); + BIND_CORE_ENUM_CONSTANT(KEY_Y); + BIND_CORE_ENUM_CONSTANT(KEY_Z); + BIND_CORE_ENUM_CONSTANT(KEY_BRACKETLEFT); + BIND_CORE_ENUM_CONSTANT(KEY_BACKSLASH); + BIND_CORE_ENUM_CONSTANT(KEY_BRACKETRIGHT); + BIND_CORE_ENUM_CONSTANT(KEY_ASCIICIRCUM); + BIND_CORE_ENUM_CONSTANT(KEY_UNDERSCORE); + BIND_CORE_ENUM_CONSTANT(KEY_QUOTELEFT); + BIND_CORE_ENUM_CONSTANT(KEY_BRACELEFT); + BIND_CORE_ENUM_CONSTANT(KEY_BAR); + BIND_CORE_ENUM_CONSTANT(KEY_BRACERIGHT); + BIND_CORE_ENUM_CONSTANT(KEY_ASCIITILDE); + BIND_CORE_ENUM_CONSTANT(KEY_NOBREAKSPACE); + BIND_CORE_ENUM_CONSTANT(KEY_EXCLAMDOWN); + BIND_CORE_ENUM_CONSTANT(KEY_CENT); + BIND_CORE_ENUM_CONSTANT(KEY_STERLING); + BIND_CORE_ENUM_CONSTANT(KEY_CURRENCY); + BIND_CORE_ENUM_CONSTANT(KEY_YEN); + BIND_CORE_ENUM_CONSTANT(KEY_BROKENBAR); + BIND_CORE_ENUM_CONSTANT(KEY_SECTION); + BIND_CORE_ENUM_CONSTANT(KEY_DIAERESIS); + BIND_CORE_ENUM_CONSTANT(KEY_COPYRIGHT); + BIND_CORE_ENUM_CONSTANT(KEY_ORDFEMININE); + BIND_CORE_ENUM_CONSTANT(KEY_GUILLEMOTLEFT); + BIND_CORE_ENUM_CONSTANT(KEY_NOTSIGN); + BIND_CORE_ENUM_CONSTANT(KEY_HYPHEN); + BIND_CORE_ENUM_CONSTANT(KEY_REGISTERED); + BIND_CORE_ENUM_CONSTANT(KEY_MACRON); + BIND_CORE_ENUM_CONSTANT(KEY_DEGREE); + BIND_CORE_ENUM_CONSTANT(KEY_PLUSMINUS); + BIND_CORE_ENUM_CONSTANT(KEY_TWOSUPERIOR); + BIND_CORE_ENUM_CONSTANT(KEY_THREESUPERIOR); + BIND_CORE_ENUM_CONSTANT(KEY_ACUTE); + BIND_CORE_ENUM_CONSTANT(KEY_MU); + BIND_CORE_ENUM_CONSTANT(KEY_PARAGRAPH); + BIND_CORE_ENUM_CONSTANT(KEY_PERIODCENTERED); + BIND_CORE_ENUM_CONSTANT(KEY_CEDILLA); + BIND_CORE_ENUM_CONSTANT(KEY_ONESUPERIOR); + BIND_CORE_ENUM_CONSTANT(KEY_MASCULINE); + BIND_CORE_ENUM_CONSTANT(KEY_GUILLEMOTRIGHT); + BIND_CORE_ENUM_CONSTANT(KEY_ONEQUARTER); + BIND_CORE_ENUM_CONSTANT(KEY_ONEHALF); + BIND_CORE_ENUM_CONSTANT(KEY_THREEQUARTERS); + BIND_CORE_ENUM_CONSTANT(KEY_QUESTIONDOWN); + BIND_CORE_ENUM_CONSTANT(KEY_AGRAVE); + BIND_CORE_ENUM_CONSTANT(KEY_AACUTE); + BIND_CORE_ENUM_CONSTANT(KEY_ACIRCUMFLEX); + BIND_CORE_ENUM_CONSTANT(KEY_ATILDE); + BIND_CORE_ENUM_CONSTANT(KEY_ADIAERESIS); + BIND_CORE_ENUM_CONSTANT(KEY_ARING); + BIND_CORE_ENUM_CONSTANT(KEY_AE); + BIND_CORE_ENUM_CONSTANT(KEY_CCEDILLA); + BIND_CORE_ENUM_CONSTANT(KEY_EGRAVE); + BIND_CORE_ENUM_CONSTANT(KEY_EACUTE); + BIND_CORE_ENUM_CONSTANT(KEY_ECIRCUMFLEX); + BIND_CORE_ENUM_CONSTANT(KEY_EDIAERESIS); + BIND_CORE_ENUM_CONSTANT(KEY_IGRAVE); + BIND_CORE_ENUM_CONSTANT(KEY_IACUTE); + BIND_CORE_ENUM_CONSTANT(KEY_ICIRCUMFLEX); + BIND_CORE_ENUM_CONSTANT(KEY_IDIAERESIS); + BIND_CORE_ENUM_CONSTANT(KEY_ETH); + BIND_CORE_ENUM_CONSTANT(KEY_NTILDE); + BIND_CORE_ENUM_CONSTANT(KEY_OGRAVE); + BIND_CORE_ENUM_CONSTANT(KEY_OACUTE); + BIND_CORE_ENUM_CONSTANT(KEY_OCIRCUMFLEX); + BIND_CORE_ENUM_CONSTANT(KEY_OTILDE); + BIND_CORE_ENUM_CONSTANT(KEY_ODIAERESIS); + BIND_CORE_ENUM_CONSTANT(KEY_MULTIPLY); + BIND_CORE_ENUM_CONSTANT(KEY_OOBLIQUE); + BIND_CORE_ENUM_CONSTANT(KEY_UGRAVE); + BIND_CORE_ENUM_CONSTANT(KEY_UACUTE); + BIND_CORE_ENUM_CONSTANT(KEY_UCIRCUMFLEX); + BIND_CORE_ENUM_CONSTANT(KEY_UDIAERESIS); + BIND_CORE_ENUM_CONSTANT(KEY_YACUTE); + BIND_CORE_ENUM_CONSTANT(KEY_THORN); + BIND_CORE_ENUM_CONSTANT(KEY_SSHARP); + + BIND_CORE_ENUM_CONSTANT(KEY_DIVISION); + BIND_CORE_ENUM_CONSTANT(KEY_YDIAERESIS); + + BIND_CORE_ENUM_CONSTANT(KEY_CODE_MASK); + BIND_CORE_ENUM_CONSTANT(KEY_MODIFIER_MASK); + + BIND_CORE_ENUM_CONSTANT(KEY_MASK_SHIFT); + BIND_CORE_ENUM_CONSTANT(KEY_MASK_ALT); + BIND_CORE_ENUM_CONSTANT(KEY_MASK_META); + BIND_CORE_ENUM_CONSTANT(KEY_MASK_CTRL); + BIND_CORE_ENUM_CONSTANT_NO_VAL(KEY_MASK_CMD); + BIND_CORE_ENUM_CONSTANT(KEY_MASK_KPAD); + BIND_CORE_ENUM_CONSTANT(KEY_MASK_GROUP_SWITCH); + + // mouse + BIND_CORE_ENUM_CONSTANT(BUTTON_LEFT); + BIND_CORE_ENUM_CONSTANT(BUTTON_RIGHT); + BIND_CORE_ENUM_CONSTANT(BUTTON_MIDDLE); + BIND_CORE_ENUM_CONSTANT(BUTTON_XBUTTON1); + BIND_CORE_ENUM_CONSTANT(BUTTON_XBUTTON2); + BIND_CORE_ENUM_CONSTANT(BUTTON_WHEEL_UP); + BIND_CORE_ENUM_CONSTANT(BUTTON_WHEEL_DOWN); + BIND_CORE_ENUM_CONSTANT(BUTTON_WHEEL_LEFT); + BIND_CORE_ENUM_CONSTANT(BUTTON_WHEEL_RIGHT); + BIND_CORE_ENUM_CONSTANT(BUTTON_MASK_LEFT); + BIND_CORE_ENUM_CONSTANT(BUTTON_MASK_RIGHT); + BIND_CORE_ENUM_CONSTANT(BUTTON_MASK_MIDDLE); + BIND_CORE_ENUM_CONSTANT(BUTTON_MASK_XBUTTON1); + BIND_CORE_ENUM_CONSTANT(BUTTON_MASK_XBUTTON2); + + // Joypad buttons + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_INVALID); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_A); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_B); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_X); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_Y); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_BACK); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_GUIDE); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_START); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_LEFT_STICK); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_RIGHT_STICK); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_LEFT_SHOULDER); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_RIGHT_SHOULDER); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_DPAD_UP); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_DPAD_DOWN); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_DPAD_LEFT); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_DPAD_RIGHT); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_SDL_MAX); + BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_MAX); + + // Joypad axes + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_INVALID); + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_LEFT_X); + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_LEFT_Y); + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_RIGHT_X); + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_RIGHT_Y); + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_TRIGGER_LEFT); + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_TRIGGER_RIGHT); + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_SDL_MAX); + BIND_CORE_ENUM_CONSTANT(JOY_AXIS_MAX); + + // midi + BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); + BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); + BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH); + BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE); + BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE); + BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE); + BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND); + + // error list + + BIND_CORE_ENUM_CONSTANT(OK); // (0) + BIND_CORE_ENUM_CONSTANT(FAILED); + BIND_CORE_ENUM_CONSTANT(ERR_UNAVAILABLE); + BIND_CORE_ENUM_CONSTANT(ERR_UNCONFIGURED); + BIND_CORE_ENUM_CONSTANT(ERR_UNAUTHORIZED); + BIND_CORE_ENUM_CONSTANT(ERR_PARAMETER_RANGE_ERROR); // (5) + BIND_CORE_ENUM_CONSTANT(ERR_OUT_OF_MEMORY); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_NOT_FOUND); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_BAD_DRIVE); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_BAD_PATH); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_NO_PERMISSION); // (10) + BIND_CORE_ENUM_CONSTANT(ERR_FILE_ALREADY_IN_USE); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_CANT_OPEN); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_CANT_WRITE); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_CANT_READ); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_UNRECOGNIZED); // (15) + BIND_CORE_ENUM_CONSTANT(ERR_FILE_CORRUPT); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_MISSING_DEPENDENCIES); + BIND_CORE_ENUM_CONSTANT(ERR_FILE_EOF); + BIND_CORE_ENUM_CONSTANT(ERR_CANT_OPEN); + BIND_CORE_ENUM_CONSTANT(ERR_CANT_CREATE); // (20) + BIND_CORE_ENUM_CONSTANT(ERR_QUERY_FAILED); + BIND_CORE_ENUM_CONSTANT(ERR_ALREADY_IN_USE); + BIND_CORE_ENUM_CONSTANT(ERR_LOCKED); + BIND_CORE_ENUM_CONSTANT(ERR_TIMEOUT); + BIND_CORE_ENUM_CONSTANT(ERR_CANT_CONNECT); // (25) + BIND_CORE_ENUM_CONSTANT(ERR_CANT_RESOLVE); + BIND_CORE_ENUM_CONSTANT(ERR_CONNECTION_ERROR); + BIND_CORE_ENUM_CONSTANT(ERR_CANT_ACQUIRE_RESOURCE); + BIND_CORE_ENUM_CONSTANT(ERR_CANT_FORK); + BIND_CORE_ENUM_CONSTANT(ERR_INVALID_DATA); // (30) + BIND_CORE_ENUM_CONSTANT(ERR_INVALID_PARAMETER); + BIND_CORE_ENUM_CONSTANT(ERR_ALREADY_EXISTS); + BIND_CORE_ENUM_CONSTANT(ERR_DOES_NOT_EXIST); + BIND_CORE_ENUM_CONSTANT(ERR_DATABASE_CANT_READ); + BIND_CORE_ENUM_CONSTANT(ERR_DATABASE_CANT_WRITE); // (35) + BIND_CORE_ENUM_CONSTANT(ERR_COMPILATION_FAILED); + BIND_CORE_ENUM_CONSTANT(ERR_METHOD_NOT_FOUND); + BIND_CORE_ENUM_CONSTANT(ERR_LINK_FAILED); + BIND_CORE_ENUM_CONSTANT(ERR_SCRIPT_FAILED); + BIND_CORE_ENUM_CONSTANT(ERR_CYCLIC_LINK); // (40) + BIND_CORE_ENUM_CONSTANT(ERR_INVALID_DECLARATION); + BIND_CORE_ENUM_CONSTANT(ERR_DUPLICATE_SYMBOL); + BIND_CORE_ENUM_CONSTANT(ERR_PARSE_ERROR); + BIND_CORE_ENUM_CONSTANT(ERR_BUSY); + BIND_CORE_ENUM_CONSTANT(ERR_SKIP); // (45) + BIND_CORE_ENUM_CONSTANT(ERR_HELP); + BIND_CORE_ENUM_CONSTANT(ERR_BUG); + BIND_CORE_ENUM_CONSTANT(ERR_PRINTER_ON_FIRE); + + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NONE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RANGE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXP_RANGE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ENUM); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXP_EASING); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LENGTH); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_KEY_ACCEL); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_FLAGS); + + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_RENDER); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_PHYSICS); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_RENDER); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_PHYSICS); + + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_FILE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_DIR); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_FILE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TYPE_STRING); + + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_STORAGE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NETWORK); + + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_HELPER); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CHECKABLE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CHECKED); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_INTERNATIONALIZED); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_GROUP); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CATEGORY); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SUBGROUP); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NO_INSTANCE_STATE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE); + + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NOEDITOR); + + BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_NORMAL); + BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_EDITOR); + BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_NOSCRIPT); + BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_CONST); + BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_REVERSE); + BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_VIRTUAL); + BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_FROM_SCRIPT); + BIND_CORE_ENUM_CONSTANT(METHOD_FLAGS_DEFAULT); + + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_FLOAT", Variant::FLOAT); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_STRING", Variant::STRING); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR2", Variant::VECTOR2); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR2I", Variant::VECTOR2I); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_RECT2", Variant::RECT2); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_RECT2I", Variant::RECT2I); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3", Variant::VECTOR3); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3I", Variant::VECTOR3I); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM2D", Variant::TRANSFORM2D); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PLANE", Variant::PLANE); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_QUAT", Variant::QUAT); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_AABB", Variant::AABB); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BASIS", Variant::BASIS); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM", Variant::TRANSFORM); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_COLOR", Variant::COLOR); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_STRING_NAME", Variant::STRING_NAME); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NODE_PATH", Variant::NODE_PATH); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_RID", Variant::RID); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_OBJECT", Variant::OBJECT); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_CALLABLE", Variant::CALLABLE); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_SIGNAL", Variant::SIGNAL); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_DICTIONARY", Variant::DICTIONARY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_ARRAY", Variant::ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_RAW_ARRAY", Variant::PACKED_BYTE_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT32_ARRAY", Variant::PACKED_INT32_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT64_ARRAY", Variant::PACKED_INT64_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_FLOAT32_ARRAY", Variant::PACKED_FLOAT32_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_FLOAT64_ARRAY", Variant::PACKED_FLOAT64_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_STRING_ARRAY", Variant::PACKED_STRING_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR2_ARRAY", Variant::PACKED_VECTOR2_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3_ARRAY", Variant::PACKED_VECTOR3_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_COLOR_ARRAY", Variant::PACKED_COLOR_ARRAY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_MAX", Variant::VARIANT_MAX); + + //comparison + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_EQUAL", Variant::OP_EQUAL); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_NOT_EQUAL", Variant::OP_NOT_EQUAL); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_LESS", Variant::OP_LESS); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_LESS_EQUAL", Variant::OP_LESS_EQUAL); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_GREATER", Variant::OP_GREATER); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_GREATER_EQUAL", Variant::OP_GREATER_EQUAL); + //mathematic + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_ADD", Variant::OP_ADD); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SUBTRACT", Variant::OP_SUBTRACT); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_MULTIPLY", Variant::OP_MULTIPLY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_DIVIDE", Variant::OP_DIVIDE); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_NEGATE", Variant::OP_NEGATE); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_POSITIVE", Variant::OP_POSITIVE); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_MODULE", Variant::OP_MODULE); + //bitwise + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SHIFT_LEFT", Variant::OP_SHIFT_LEFT); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SHIFT_RIGHT", Variant::OP_SHIFT_RIGHT); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_BIT_AND", Variant::OP_BIT_AND); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_BIT_OR", Variant::OP_BIT_OR); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_BIT_XOR", Variant::OP_BIT_XOR); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_BIT_NEGATE", Variant::OP_BIT_NEGATE); + //logic + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_AND", Variant::OP_AND); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_OR", Variant::OP_OR); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_XOR", Variant::OP_XOR); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_NOT", Variant::OP_NOT); + //containment + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_IN", Variant::OP_IN); + BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_MAX", Variant::OP_MAX); +} + +void unregister_global_constants() { + _global_constants.clear(); +} + +int CoreConstants::get_global_constant_count() { + return _global_constants.size(); +} + +#ifdef DEBUG_METHODS_ENABLED +StringName CoreConstants::get_global_constant_enum(int p_idx) { + return _global_constants[p_idx].enum_name; +} + +bool CoreConstants::get_ignore_value_in_docs(int p_idx) { + return _global_constants[p_idx].ignore_value_in_docs; +} +#else +StringName CoreConstants::get_global_constant_enum(int p_idx) { + return StringName(); +} + +bool CoreConstants::get_ignore_value_in_docs(int p_idx) { + return false; +} +#endif + +const char *CoreConstants::get_global_constant_name(int p_idx) { + return _global_constants[p_idx].name; +} + +int CoreConstants::get_global_constant_value(int p_idx) { + return _global_constants[p_idx].value; +} diff --git a/core/global_constants.h b/core/core_constants.h index a20b5ecd9a..deaf9ec60f 100644 --- a/core/global_constants.h +++ b/core/core_constants.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* global_constants.h */ +/* core_constants.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,15 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GLOBAL_CONSTANTS_H -#define GLOBAL_CONSTANTS_H +#ifndef CORE_CONSTANTS_H +#define CORE_CONSTANTS_H -#include "core/string_name.h" +#include "core/string/string_name.h" -class GlobalConstants { +class CoreConstants { public: static int get_global_constant_count(); static StringName get_global_constant_enum(int p_idx); + static bool get_ignore_value_in_docs(int p_idx); static const char *get_global_constant_name(int p_idx); static int get_global_constant_value(int p_idx); }; diff --git a/core/core_string_names.cpp b/core/core_string_names.cpp index 1d3b333efc..18ac2a2d43 100644 --- a/core/core_string_names.cpp +++ b/core/core_string_names.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -73,6 +73,8 @@ CoreStringNames::CoreStringNames() : a8(StaticCString::create("a8")), call(StaticCString::create("call")), call_deferred(StaticCString::create("call_deferred")), + bind(StaticCString::create("bind")), + unbind(StaticCString::create("unbind")), emit(StaticCString::create("emit")), notification(StaticCString::create("notification")) { } diff --git a/core/core_string_names.h b/core/core_string_names.h index 2ade44f4e0..b4e386f3bc 100644 --- a/core/core_string_names.h +++ b/core/core_string_names.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,10 +31,9 @@ #ifndef CORE_STRING_NAMES_H #define CORE_STRING_NAMES_H -#include "core/string_name.h" +#include "core/string/string_name.h" class CoreStringNames { - friend void register_core_types(); friend void unregister_core_types(); @@ -93,6 +92,8 @@ public: StringName call; StringName call_deferred; + StringName bind; + StringName unbind; StringName emit; StringName notification; }; diff --git a/core/crypto/SCsub b/core/crypto/SCsub index da4a9c9381..4f3104d84b 100644 --- a/core/crypto/SCsub +++ b/core/crypto/SCsub @@ -6,6 +6,7 @@ env_crypto = env.Clone() is_builtin = env["builtin_mbedtls"] has_module = env["module_mbedtls_enabled"] +thirdparty_obj = [] if is_builtin or not has_module: # Use our headers for builtin or if the module is not going to be compiled. @@ -35,6 +36,16 @@ if not has_module: "godot_core_mbedtls_platform.c", ] thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources] - env_thirdparty.add_source_files(env.core_sources, thirdparty_mbedtls_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_mbedtls_sources) + env.core_sources += thirdparty_obj -env_crypto.add_source_files(env.core_sources, "*.cpp") + +# Godot source files + +core_obj = [] + +env_crypto.add_source_files(core_obj, "*.cpp") +env.core_sources += core_obj + +# Needed to force rebuilding the core files when the thirdparty library is updated. +env.Depends(core_obj, thirdparty_obj) diff --git a/core/crypto/aes_context.cpp b/core/crypto/aes_context.cpp new file mode 100644 index 0000000000..b387aeb27d --- /dev/null +++ b/core/crypto/aes_context.cpp @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* aes_context.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/crypto/aes_context.h" + +Error AESContext::start(Mode p_mode, PackedByteArray p_key, PackedByteArray p_iv) { + ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AESContext already started. Call 'finish' before starting a new one."); + ERR_FAIL_COND_V_MSG(p_mode < 0 || p_mode >= MODE_MAX, ERR_INVALID_PARAMETER, "Invalid mode requested."); + // Key check. + int key_bits = p_key.size() << 3; + ERR_FAIL_COND_V_MSG(key_bits != 128 && key_bits != 256, ERR_INVALID_PARAMETER, "AES key must be either 16 or 32 bytes"); + // Initialization vector. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_CBC_DECRYPT) { + ERR_FAIL_COND_V_MSG(p_iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes."); + iv.resize(0); + iv.append_array(p_iv); + } + // Encryption/decryption key. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_ECB_ENCRYPT) { + ctx.set_encode_key(p_key.ptr(), key_bits); + } else { + ctx.set_decode_key(p_key.ptr(), key_bits); + } + mode = p_mode; + return OK; +} + +PackedByteArray AESContext::update(PackedByteArray p_src) { + ERR_FAIL_COND_V_MSG(mode < 0 || mode >= MODE_MAX, PackedByteArray(), "AESContext not started. Call 'start' before calling 'update'."); + int len = p_src.size(); + ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); + PackedByteArray out; + out.resize(len); + const uint8_t *src_ptr = p_src.ptr(); + uint8_t *out_ptr = out.ptrw(); + switch (mode) { + case MODE_ECB_ENCRYPT: { + for (int i = 0; i < len; i += 16) { + Error err = ctx.encrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_ECB_DECRYPT: { + for (int i = 0; i < len; i += 16) { + Error err = ctx.decrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_CBC_ENCRYPT: { + Error err = ctx.encrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CBC_DECRYPT: { + Error err = ctx.decrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + default: + ERR_FAIL_V_MSG(PackedByteArray(), "Bug!"); + } + return out; +} + +PackedByteArray AESContext::get_iv_state() { + ERR_FAIL_COND_V_MSG(mode != MODE_CBC_ENCRYPT && mode != MODE_CBC_DECRYPT, PackedByteArray(), "Calling 'get_iv_state' only makes sense when the context is started in CBC mode."); + PackedByteArray out; + out.append_array(iv); + return out; +} + +void AESContext::finish() { + mode = MODE_MAX; + iv.resize(0); +} + +void AESContext::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "mode", "key", "iv"), &AESContext::start, DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("update", "src"), &AESContext::update); + ClassDB::bind_method(D_METHOD("get_iv_state"), &AESContext::get_iv_state); + ClassDB::bind_method(D_METHOD("finish"), &AESContext::finish); + BIND_ENUM_CONSTANT(MODE_ECB_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_ECB_DECRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_DECRYPT); + BIND_ENUM_CONSTANT(MODE_MAX); +} + +AESContext::AESContext() { +} diff --git a/core/func_ref.h b/core/crypto/aes_context.h index 8cb3be6e61..cc00b18fd2 100644 --- a/core/func_ref.h +++ b/core/crypto/aes_context.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* func_ref.h */ +/* aes_context.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,27 +28,41 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef FUNC_REF_H -#define FUNC_REF_H +#ifndef AES_CONTEXT_H +#define AES_CONTEXT_H -#include "core/reference.h" +#include "core/crypto/crypto_core.h" +#include "core/object/reference.h" -class FuncRef : public Reference { +class AESContext : public Reference { + GDCLASS(AESContext, Reference); - GDCLASS(FuncRef, Reference); - ObjectID id; - StringName function; +public: + enum Mode { + MODE_ECB_ENCRYPT, + MODE_ECB_DECRYPT, + MODE_CBC_ENCRYPT, + MODE_CBC_DECRYPT, + MODE_MAX + }; + +private: + Mode mode = MODE_MAX; + CryptoCore::AESContext ctx; + PackedByteArray iv; protected: static void _bind_methods(); public: - Variant call_func(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - Variant call_funcv(const Array &p_args); - void set_instance(Object *p_obj); - void set_function(const StringName &p_func); - bool is_valid() const; - FuncRef(); + Error start(Mode p_mode, PackedByteArray p_key, PackedByteArray p_iv = PackedByteArray()); + PackedByteArray update(PackedByteArray p_src); + PackedByteArray get_iv_state(); + void finish(); + + AESContext(); }; -#endif // FUNC_REF_H +VARIANT_ENUM_CAST(AESContext::Mode); + +#endif // AES_CONTEXT_H diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp index 233f62bd15..99f4fb232d 100644 --- a/core/crypto/crypto.cpp +++ b/core/crypto/crypto.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "crypto.h" -#include "core/engine.h" +#include "core/config/engine.h" #include "core/io/certs_compressed.gen.h" #include "core/io/compression.h" @@ -38,20 +38,25 @@ CryptoKey *(*CryptoKey::_create)() = nullptr; CryptoKey *CryptoKey::create() { - if (_create) + if (_create) { return _create(); + } return nullptr; } void CryptoKey::_bind_methods() { - ClassDB::bind_method(D_METHOD("save", "path"), &CryptoKey::save); - ClassDB::bind_method(D_METHOD("load", "path"), &CryptoKey::load); + ClassDB::bind_method(D_METHOD("save", "path", "public_only"), &CryptoKey::save, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("load", "path", "public_only"), &CryptoKey::load, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_public_only"), &CryptoKey::is_public_only); + ClassDB::bind_method(D_METHOD("save_to_string", "public_only"), &CryptoKey::save_to_string, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("load_from_string", "string_key", "public_only"), &CryptoKey::load_from_string, DEFVAL(false)); } X509Certificate *(*X509Certificate::_create)() = nullptr; X509Certificate *X509Certificate::create() { - if (_create) + if (_create) { return _create(); + } return nullptr; } @@ -60,92 +65,134 @@ void X509Certificate::_bind_methods() { ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load); } +/// HMACContext + +void HMACContext::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "hash_type", "key"), &HMACContext::start); + ClassDB::bind_method(D_METHOD("update", "data"), &HMACContext::update); + ClassDB::bind_method(D_METHOD("finish"), &HMACContext::finish); +} + +HMACContext *(*HMACContext::_create)() = nullptr; +HMACContext *HMACContext::create() { + if (_create) { + return _create(); + } + ERR_FAIL_V_MSG(nullptr, "HMACContext is not available when the mbedtls module is disabled."); +} + /// Crypto void (*Crypto::_load_default_certificates)(String p_path) = nullptr; Crypto *(*Crypto::_create)() = nullptr; Crypto *Crypto::create() { - if (_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) { - - if (_load_default_certificates) + if (_load_default_certificates) { _load_default_certificates(p_path); + } +} + +PackedByteArray Crypto::hmac_digest(HashingContext::HashType p_hash_type, PackedByteArray p_key, PackedByteArray p_msg) { + Ref<HMACContext> ctx = Ref<HMACContext>(HMACContext::create()); + ERR_FAIL_COND_V_MSG(ctx.is_null(), PackedByteArray(), "HMAC is not available witout mbedtls module."); + Error err = ctx->start(p_hash_type, p_key); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + err = ctx->update(p_msg); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + return ctx->finish(); +} + +// Compares two HMACS for equality without leaking timing information in order to prevent timing attakcs. +// @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy +bool Crypto::constant_time_compare(PackedByteArray p_trusted, PackedByteArray p_received) { + const uint8_t *t = p_trusted.ptr(); + const uint8_t *r = p_received.ptr(); + int tlen = p_trusted.size(); + int rlen = p_received.size(); + // If the lengths are different then nothing else matters. + if (tlen != rlen) { + return false; + } + + uint8_t v = 0; + for (int i = 0; i < tlen; i++) { + v |= t[i] ^ r[i]; + } + return v == 0; } void Crypto::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_random_bytes", "size"), &Crypto::generate_random_bytes); ClassDB::bind_method(D_METHOD("generate_rsa", "size"), &Crypto::generate_rsa); 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."); -} - -Crypto::Crypto() { + ClassDB::bind_method(D_METHOD("sign", "hash_type", "hash", "key"), &Crypto::sign); + ClassDB::bind_method(D_METHOD("verify", "hash_type", "hash", "signature", "key"), &Crypto::verify); + ClassDB::bind_method(D_METHOD("encrypt", "key", "plaintext"), &Crypto::encrypt); + ClassDB::bind_method(D_METHOD("decrypt", "key", "ciphertext"), &Crypto::decrypt); + ClassDB::bind_method(D_METHOD("hmac_digest", "hash_type", "key", "msg"), &Crypto::hmac_digest); + ClassDB::bind_method(D_METHOD("constant_time_compare", "trusted", "received"), &Crypto::constant_time_compare); } /// 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) { - String el = p_path.get_extension().to_lower(); if (el == "crt") { X509Certificate *cert = X509Certificate::create(); - if (cert) + if (cert) { cert->load(p_path); + } return cert; } else if (el == "key") { CryptoKey *key = CryptoKey::create(); + if (key) { + key->load(p_path, false); + } + return key; + } else if (el == "pub") { + CryptoKey *key = CryptoKey::create(); if (key) - key->load(p_path); + key->load(p_path, true); return key; } return nullptr; } void ResourceFormatLoaderCrypto::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("crt"); p_extensions->push_back("key"); + p_extensions->push_back("pub"); } bool ResourceFormatLoaderCrypto::handles_type(const String &p_type) const { - return p_type == "X509Certificate" || p_type == "CryptoKey"; } String ResourceFormatLoaderCrypto::get_resource_type(const String &p_path) const { - String el = p_path.get_extension().to_lower(); - if (el == "crt") + if (el == "crt") { return "X509Certificate"; - else if (el == "key") + } else if (el == "key" || el == "pub") { return "CryptoKey"; + } return ""; } Error ResourceFormatSaverCrypto::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - Error err; Ref<X509Certificate> cert = p_resource; Ref<CryptoKey> key = p_resource; if (cert.is_valid()) { err = cert->save(p_path); } else if (key.is_valid()) { - err = key->save(p_path); + String el = p_path.get_extension().to_lower(); + err = key->save(p_path, el == "pub"); } else { ERR_FAIL_V(ERR_INVALID_PARAMETER); } @@ -154,17 +201,19 @@ Error ResourceFormatSaverCrypto::save(const String &p_path, const RES &p_resourc } void ResourceFormatSaverCrypto::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { - const X509Certificate *cert = Object::cast_to<X509Certificate>(*p_resource); const CryptoKey *key = Object::cast_to<CryptoKey>(*p_resource); if (cert) { p_extensions->push_back("crt"); } if (key) { - p_extensions->push_back("key"); + if (!key->is_public_only()) { + p_extensions->push_back("key"); + } + p_extensions->push_back("pub"); } } -bool ResourceFormatSaverCrypto::recognize(const RES &p_resource) const { +bool ResourceFormatSaverCrypto::recognize(const RES &p_resource) const { return Object::cast_to<X509Certificate>(*p_resource) || Object::cast_to<CryptoKey>(*p_resource); } diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h index d9becab958..30d2129e3d 100644 --- a/core/crypto/crypto.h +++ b/core/crypto/crypto.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,11 +31,11 @@ #ifndef CRYPTO_H #define CRYPTO_H -#include "core/reference.h" -#include "core/resource.h" - +#include "core/crypto/hashing_context.h" +#include "core/io/resource.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" +#include "core/object/reference.h" class CryptoKey : public Resource { GDCLASS(CryptoKey, Resource); @@ -46,8 +46,11 @@ protected: public: static CryptoKey *create(); - virtual Error load(String p_path) = 0; - virtual Error save(String p_path) = 0; + virtual Error load(String p_path, bool p_public_only = false) = 0; + virtual Error save(String p_path, bool p_public_only = false) = 0; + virtual String save_to_string(bool p_public_only = false) = 0; + virtual Error load_from_string(String p_string_key, bool p_public_only = false) = 0; + virtual bool is_public_only() const = 0; }; class X509Certificate : public Resource { @@ -64,6 +67,23 @@ public: virtual Error save(String p_path) = 0; }; +class HMACContext : public Reference { + GDCLASS(HMACContext, Reference); + +protected: + static void _bind_methods(); + static HMACContext *(*_create)(); + +public: + static HMACContext *create(); + + virtual Error start(HashingContext::HashType p_hash_type, PackedByteArray p_key) = 0; + virtual Error update(PackedByteArray p_data) = 0; + virtual PackedByteArray finish() = 0; + + HMACContext() {} +}; + class Crypto : public Reference { GDCLASS(Crypto, Reference); @@ -76,11 +96,22 @@ 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; + + virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) = 0; + virtual bool verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) = 0; + virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_plaintext) = 0; + virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_ciphertext) = 0; + + PackedByteArray hmac_digest(HashingContext::HashType p_hash_type, PackedByteArray p_key, PackedByteArray p_msg); + + // Compares two PackedByteArrays for equality without leaking timing information in order to prevent timing attacks. + // @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy + bool constant_time_compare(PackedByteArray p_trusted, PackedByteArray p_received); - Crypto(); + Crypto() {} }; class ResourceFormatLoaderCrypto : public ResourceFormatLoader { diff --git a/core/crypto/crypto_core.cpp b/core/crypto/crypto_core.cpp index ec25ee0d38..f90092056e 100644 --- a/core/crypto/crypto_core.cpp +++ b/core/crypto/crypto_core.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -140,11 +140,33 @@ Error CryptoCore::AESContext::encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst return ret ? FAILED : OK; } +Error CryptoCore::AESContext::encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_length, r_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCore::AESContext::encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + size_t iv_off = 0; // Ignore and assume 16-byte alignment. + int ret = mbedtls_aes_crypt_cfb128((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_length, &iv_off, p_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + Error CryptoCore::AESContext::decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { int ret = mbedtls_aes_crypt_ecb((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_src, r_dst); return ret ? FAILED : OK; } +Error CryptoCore::AESContext::decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_length, r_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCore::AESContext::decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + size_t iv_off = 0; // Ignore and assume 16-byte alignment. + int ret = mbedtls_aes_crypt_cfb128((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_length, &iv_off, p_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + // CryptoCore String CryptoCore::b64_encode_str(const uint8_t *p_src, int p_src_len) { int b64len = p_src_len / 3 * 4 + 4 + 1; diff --git a/core/crypto/crypto_core.h b/core/crypto/crypto_core.h index b3be58e8ee..27b380e838 100644 --- a/core/crypto/crypto_core.h +++ b/core/crypto/crypto_core.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,15 +31,13 @@ #ifndef CRYPTO_CORE_H #define CRYPTO_CORE_H -#include "core/reference.h" +#include "core/object/reference.h" class CryptoCore { - public: class MD5Context { - private: - void *ctx; // To include, or not to include... + void *ctx = nullptr; // To include, or not to include... public: MD5Context(); @@ -51,9 +49,8 @@ public: }; class SHA1Context { - private: - void *ctx; // To include, or not to include... + void *ctx = nullptr; // To include, or not to include... public: SHA1Context(); @@ -65,9 +62,8 @@ public: }; class SHA256Context { - private: - void *ctx; // To include, or not to include... + void *ctx = nullptr; // To include, or not to include... public: SHA256Context(); @@ -79,9 +75,8 @@ public: }; class AESContext { - private: - void *ctx; // To include, or not to include... + void *ctx = nullptr; // To include, or not to include... public: AESContext(); @@ -91,6 +86,10 @@ public: Error set_decode_key(const uint8_t *p_key, size_t p_bits); Error encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); Error decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); + Error encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst); }; static String b64_encode_str(const uint8_t *p_src, int p_src_len); diff --git a/core/crypto/hashing_context.cpp b/core/crypto/hashing_context.cpp index af43bc9bad..070d2d4dd7 100644 --- a/core/crypto/hashing_context.cpp +++ b/core/crypto/hashing_context.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -104,7 +104,6 @@ void HashingContext::_create_ctx(HashType p_type) { } void HashingContext::_delete_ctx() { - switch (type) { case HASH_MD5: memdelete((CryptoCore::MD5Context *)ctx); @@ -128,11 +127,8 @@ void HashingContext::_bind_methods() { BIND_ENUM_CONSTANT(HASH_SHA256); } -HashingContext::HashingContext() { - ctx = nullptr; -} - HashingContext::~HashingContext() { - if (ctx != nullptr) + if (ctx != nullptr) { _delete_ctx(); + } } diff --git a/core/crypto/hashing_context.h b/core/crypto/hashing_context.h index 230ba7ee85..892a48a4e8 100644 --- a/core/crypto/hashing_context.h +++ b/core/crypto/hashing_context.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef HASHING_CONTEXT_H #define HASHING_CONTEXT_H -#include "core/reference.h" +#include "core/object/reference.h" class HashingContext : public Reference { GDCLASS(HashingContext, Reference); @@ -44,8 +44,8 @@ public: }; private: - void *ctx; - HashType type; + void *ctx = nullptr; + HashType type = HASH_MD5; protected: static void _bind_methods(); @@ -57,7 +57,7 @@ public: Error update(PackedByteArray p_chunk); PackedByteArray finish(); - HashingContext(); + HashingContext() {} ~HashingContext(); }; diff --git a/core/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp index 410c55129d..26f82c2658 100644 --- a/core/debugger/debugger_marshalls.cpp +++ b/core/debugger/debugger_marshalls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -171,7 +171,7 @@ bool DebuggerMarshalls::ServersProfilerFrame::deserialize(const Array &p_arr) { } servers.push_back(si); } - CHECK_SIZE(p_arr, idx + 3, "ServersProfilerFrame"); + CHECK_SIZE(p_arr, idx + 1, "ServersProfilerFrame"); int func_size = p_arr[idx]; idx += 1; CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame"); @@ -228,8 +228,9 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) { int len = 0; Error err = encode_variant(var, nullptr, len, true); - if (err != OK) + if (err != OK) { ERR_PRINT("Failed to encode variant."); + } if (len > max_size) { arr.push_back(Variant()); diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h index 04229c0afc..3e8c34d84b 100644 --- a/core/debugger/debugger_marshalls.h +++ b/core/debugger/debugger_marshalls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,22 +31,18 @@ #ifndef DEBUGGER_MARSHARLLS_H #define DEBUGGER_MARSHARLLS_H -#include "core/script_language.h" +#include "core/object/script_language.h" #include "servers/rendering_server.h" struct DebuggerMarshalls { - // Memory usage struct ResourceInfo { String path; String format; String type; RID id; - int vram; + int vram = 0; bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; } - ResourceInfo() { - vram = 0; - } }; struct ResourceUsage { @@ -119,10 +115,7 @@ struct DebuggerMarshalls { struct ScriptStackVariable { String name; Variant value; - int type; - ScriptStackVariable() { - type = -1; - } + int type = -1; Array serialize(int max_size = 1 << 20); // 1 MiB default. bool deserialize(const Array &p_arr); @@ -137,34 +130,25 @@ struct DebuggerMarshalls { }; struct OutputError { - int hr; - int min; - int sec; - int msec; + int hr = -1; + int min = -1; + int sec = -1; + int msec = -1; String source_file; String source_func; - int source_line; + int source_line = -1; String error; String error_descr; - bool warning; + bool warning = false; Vector<ScriptLanguage::StackInfo> callstack; - OutputError() { - hr = -1; - min = -1; - sec = -1; - msec = -1; - source_line = -1; - warning = false; - } - Array serialize(); bool deserialize(const Array &p_arr); }; // Visual Profiler struct VisualProfilerFrame { - uint64_t frame_number; + uint64_t frame_number = 0; Vector<RS::FrameProfileArea> areas; Array serialize(); diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index bfe38d0f4a..895b8c23a9 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,6 +32,7 @@ #include "core/debugger/local_debugger.h" #include "core/debugger/remote_debugger.h" +#include "core/debugger/remote_debugger_peer.h" #include "core/debugger/script_debugger.h" #include "core/os/os.h" @@ -40,6 +41,7 @@ ScriptDebugger *EngineDebugger::script_debugger = nullptr; Map<StringName, EngineDebugger::Profiler> EngineDebugger::profilers; Map<StringName, EngineDebugger::Capture> EngineDebugger::captures; +Map<String, EngineDebugger::CreatePeerFunc> EngineDebugger::protocols; void EngineDebugger::register_profiler(const StringName &p_name, const Profiler &p_func) { ERR_FAIL_COND_MSG(profilers.has(p_name), "Profiler already registered: " + p_name); @@ -66,6 +68,11 @@ void EngineDebugger::unregister_message_capture(const StringName &p_name) { captures.erase(p_name); } +void EngineDebugger::register_uri_handler(const String &p_protocol, CreatePeerFunc p_func) { + ERR_FAIL_COND_MSG(protocols.has(p_protocol), "Protocol handler already registered: " + p_protocol); + protocols.insert(p_protocol, p_func); +} + void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) { ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't change profiler state, no profiler: " + p_name); Profiler &p = profilers[p_name]; @@ -104,55 +111,65 @@ Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_ms void EngineDebugger::line_poll() { // The purpose of this is just processing events every now and then when the script might get too busy otherwise bugs like infinite loops can't be caught - if (poll_every % 2048 == 0) + if (poll_every % 2048 == 0) { poll_events(false); + } poll_every++; } -void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_idle_ticks, uint64_t p_physics_ticks, float p_physics_frame_time) { +void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, float p_physics_frame_time) { frame_time = USEC_TO_SEC(p_frame_ticks); - idle_time = USEC_TO_SEC(p_idle_ticks); + process_time = USEC_TO_SEC(p_process_ticks); physics_time = USEC_TO_SEC(p_physics_ticks); physics_frame_time = p_physics_frame_time; // Notify tick to running profilers for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) { Profiler &p = E->get(); - if (!p.active || !p.tick) + if (!p.active || !p.tick) { continue; - p.tick(p.data, frame_time, idle_time, physics_time, physics_frame_time); + } + p.tick(p.data, frame_time, process_time, physics_time, physics_frame_time); } singleton->poll_events(true); } void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Vector<String> p_breakpoints) { - if (p_uri.empty()) + register_uri_handler("tcp://", RemoteDebuggerPeerTCP::create); // TCP is the default protocol. Platforms/modules can add more. + if (p_uri.is_empty()) { return; + } if (p_uri == "local://") { singleton = memnew(LocalDebugger); script_debugger = memnew(ScriptDebugger); // Tell the OS that we want to handle termination signals. OS::get_singleton()->initialize_debugging(); - } else { - singleton = RemoteDebugger::create_for_uri(p_uri); - if (!singleton) + } else if (p_uri.find("://") >= 0) { + const String proto = p_uri.substr(0, p_uri.find("://") + 3); + if (!protocols.has(proto)) { return; + } + RemoteDebuggerPeer *peer = protocols[proto](p_uri); + if (!peer) { + return; + } + singleton = memnew(RemoteDebugger(Ref<RemoteDebuggerPeer>(peer))); script_debugger = memnew(ScriptDebugger); // Notify editor of our pid (to allow focus stealing). Array msg; msg.push_back(OS::get_singleton()->get_process_id()); singleton->send_message("set_pid", msg); } - if (!singleton) + if (!singleton) { return; + } // There is a debugger, parse breakpoints. ScriptDebugger *singleton_script_debugger = singleton->get_script_debugger(); singleton_script_debugger->set_skip_breakpoints(p_skip_breakpoints); for (int i = 0; i < p_breakpoints.size(); i++) { - String bp = p_breakpoints[i]; - int sp = bp.find_last(":"); + int sp = bp.rfind(":"); ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format."); singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp)); @@ -160,27 +177,31 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Ve } void EngineDebugger::deinitialize() { - if (!singleton) - return; - - // Stop all profilers - for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) { - if (E->get().active) - singleton->profiler_enable(E->key(), false); + if (singleton) { + // Stop all profilers + for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) { + if (E->get().active) { + singleton->profiler_enable(E->key(), false); + } + } + + // Flush any remaining message + singleton->poll_events(false); + + memdelete(singleton); + singleton = nullptr; } - // Flush any remaining message - singleton->poll_events(false); - - memdelete(singleton); - singleton = nullptr; + // Clear profilers/captuers/protocol handlers. profilers.clear(); captures.clear(); + protocols.clear(); } EngineDebugger::~EngineDebugger() { - if (script_debugger) + if (script_debugger) { memdelete(script_debugger); + } script_debugger = nullptr; singleton = nullptr; } diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h index 7b6b77ca9b..c6daea6e2f 100644 --- a/core/debugger/engine_debugger.h +++ b/core/debugger/engine_debugger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,22 +31,26 @@ #ifndef ENGINE_DEBUGGER_H #define ENGINE_DEBUGGER_H -#include "core/array.h" -#include "core/map.h" -#include "core/string_name.h" -#include "core/ustring.h" -#include "core/variant.h" -#include "core/vector.h" +#include "core/string/string_name.h" +#include "core/string/ustring.h" +#include "core/templates/map.h" +#include "core/templates/vector.h" +#include "core/variant/array.h" +#include "core/variant/variant.h" +class RemoteDebuggerPeer; class ScriptDebugger; class EngineDebugger { public: typedef void (*ProfilingToggle)(void *p_user, bool p_enable, const Array &p_opts); - typedef void (*ProfilingTick)(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time); + typedef void (*ProfilingTick)(void *p_user, float p_frame_time, float p_process_time, float p_physics_time, float p_physics_frame_time); typedef void (*ProfilingAdd)(void *p_user, const Array &p_arr); + typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured); + typedef RemoteDebuggerPeer *(*CreatePeerFunc)(const String &p_uri); + class Profiler { friend class EngineDebugger; @@ -82,7 +86,7 @@ public: private: float frame_time = 0.0; - float idle_time = 0.0; + float process_time = 0.0; float physics_time = 0.0; float physics_frame_time = 0.0; @@ -94,6 +98,7 @@ protected: static Map<StringName, Profiler> profilers; static Map<StringName, Capture> captures; + static Map<String, CreatePeerFunc> protocols; public: _FORCE_INLINE_ static EngineDebugger *get_singleton() { return singleton; } @@ -113,7 +118,9 @@ public: static void unregister_message_capture(const StringName &p_name); static bool has_capture(const StringName &p_name); - void iteration(uint64_t p_frame_ticks, uint64_t p_idle_ticks, uint64_t p_physics_ticks, float p_physics_frame_time); + static void register_uri_handler(const String &p_protocol, CreatePeerFunc p_func); + + void iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, float p_physics_frame_time); void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array()); Error capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured); diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp index 6d88ceb2c1..1dd7e268a5 100644 --- a/core/debugger/local_debugger.cpp +++ b/core/debugger/local_debugger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -36,7 +36,6 @@ struct LocalDebugger::ScriptsProfiler { struct ProfileInfoSort { - bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const { return A.total_time > B.total_time; } @@ -70,17 +69,19 @@ struct LocalDebugger::ScriptsProfiler { void _print_frame_data(bool p_accumulated) { uint64_t diff = OS::get_singleton()->get_ticks_usec() - idle_accum; - if (!p_accumulated && diff < 1000000) //show every one second + if (!p_accumulated && diff < 1000000) { //show every one second return; + } idle_accum = OS::get_singleton()->get_ticks_usec(); int ofs = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { - if (p_accumulated) + if (p_accumulated) { ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs); - else + } else { ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs); + } } SortArray<ScriptLanguage::ProfilingInfo, ProfileInfoSort> sort; @@ -89,7 +90,6 @@ struct LocalDebugger::ScriptsProfiler { // compute total script frame time uint64_t script_time_us = 0; for (int i = 0; i < ofs; i++) { - script_time_us += pinfo[i].self_time; } float script_time = USEC_TO_SEC(script_time_us); @@ -102,7 +102,6 @@ struct LocalDebugger::ScriptsProfiler { } for (int i = 0; i < ofs; i++) { - print_line(itos(i) + ":" + pinfo[i].signature); float tt = USEC_TO_SEC(pinfo[i].total_time); float st = USEC_TO_SEC(pinfo[i].self_time); @@ -116,10 +115,9 @@ struct LocalDebugger::ScriptsProfiler { }; void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { - ScriptLanguage *script_lang = script_debugger->get_break_language(); - if (!target_function.empty()) { + if (!target_function.is_empty()) { String current_function = script_lang->debug_get_stack_level_function(0); if (current_function != target_function) { script_debugger->set_depth(0); @@ -135,7 +133,6 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { int current_frame = 0; int total_frames = script_lang->debug_get_stack_level_count(); while (true) { - OS::get_singleton()->print("debug> "); String line = OS::get_singleton()->get_stdin_string().strip_edges(); @@ -146,18 +143,15 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'"); print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'"); print_line("Enter \"help\" for assistance."); - } else if (line == "c" || line == "continue") + } else if (line == "c" || line == "continue") { break; - else if (line == "bt" || line == "breakpoint") { - + } else if (line == "bt" || line == "breakpoint") { for (int i = 0; i < total_frames; i++) { - String cfi = (current_frame == i) ? "*" : " "; //current frame indicator print_line(cfi + "Frame " + itos(i) + " - " + script_lang->debug_get_stack_level_source(i) + ":" + itos(script_lang->debug_get_stack_level_line(i)) + " in function '" + script_lang->debug_get_stack_level_function(i) + "'"); } } else if (line.begins_with("fr") || line.begins_with("frame")) { - if (line.get_slice_count(" ") == 1) { print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'"); } else { @@ -171,9 +165,7 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } } else if (line.begins_with("set")) { - if (line.get_slice_count(" ") == 1) { - for (Map<String, String>::Element *E = options.front(); E; E = E->next()) { print_line("\t" + E->key() + "=" + E->value()); } @@ -185,13 +177,11 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { if (value_pos < 0) { print_line("Error: Invalid set format. Use: set key=value"); } else { - String key = key_value.left(value_pos); if (!options.has(key)) { print_line("Error: Unknown option " + key); } else { - // Allow explicit tab character String value = key_value.right(value_pos + 1).replace("\\t", "\t"); @@ -201,49 +191,41 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } } else if (line == "lv" || line == "locals") { - List<String> locals; List<Variant> values; script_lang->debug_get_stack_level_locals(current_frame, &locals, &values); print_variables(locals, values, variable_prefix); } else if (line == "gv" || line == "globals") { - List<String> globals; List<Variant> values; script_lang->debug_get_globals(&globals, &values); print_variables(globals, values, variable_prefix); } else if (line == "mv" || line == "members") { - List<String> members; List<Variant> values; script_lang->debug_get_stack_level_members(current_frame, &members, &values); print_variables(members, values, variable_prefix); } else if (line.begins_with("p") || line.begins_with("print")) { - if (line.get_slice_count(" ") <= 1) { print_line("Usage: print <expre>"); } else { - String expr = line.get_slicec(' ', 2); String res = script_lang->debug_parse_stack_level_expression(current_frame, expr); print_line(res); } } else if (line == "s" || line == "step") { - script_debugger->set_depth(-1); script_debugger->set_lines_left(1); break; } else if (line == "n" || line == "next") { - script_debugger->set_depth(0); script_debugger->set_lines_left(1); break; } else if (line == "fin" || line == "finish") { - String current_function = script_lang->debug_get_stack_level_function(0); for (int i = 0; i < total_frames; i++) { @@ -259,9 +241,7 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { target_function = ""; } else if (line.begins_with("br") || line.begins_with("break")) { - if (line.get_slice_count(" ") <= 1) { - const Map<int, Set<StringName>> &breakpoints = script_debugger->get_breakpoints(); if (breakpoints.size() == 0) { print_line("No Breakpoints."); @@ -274,14 +254,14 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } } else { - Pair<String, int> breakpoint = to_breakpoint(line); String source = breakpoint.first; int linenr = breakpoint.second; - if (source.empty()) + if (source.is_empty()) { continue; + } script_debugger->insert_breakpoint(linenr, source); @@ -289,7 +269,6 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } } else if (line == "q" || line == "quit") { - // Do not stop again on quit script_debugger->clear_breakpoints(); script_debugger->set_depth(-1); @@ -298,18 +277,17 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { SceneTree::get_singleton()->quit(); break; } else if (line.begins_with("delete")) { - if (line.get_slice_count(" ") <= 1) { script_debugger->clear_breakpoints(); } else { - Pair<String, int> breakpoint = to_breakpoint(line); String source = breakpoint.first; int linenr = breakpoint.second; - if (source.empty()) + if (source.is_empty()) { continue; + } script_debugger->remove_breakpoint(linenr, source); @@ -317,7 +295,6 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } } else if (line == "h" || line == "help") { - print_line("Built-In Debugger command list:\n"); print_line("\tc,continue\t\t Continue execution."); print_line("\tbt,backtrace\t\t Show stack trace (frames)."); @@ -340,18 +317,15 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } void LocalDebugger::print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix) { - String value; Vector<String> value_lines; const List<Variant>::Element *V = values.front(); for (const List<String>::Element *E = names.front(); E; E = E->next()) { - value = String(V->get()); - if (variable_prefix.empty()) { + if (variable_prefix.is_empty()) { print_line(E->get() + ": " + String(V->get())); } else { - print_line(E->get() + ":"); value_lines = value.split("\n"); for (int i = 0; i < value_lines.size(); ++i) { @@ -364,7 +338,6 @@ void LocalDebugger::print_variables(const List<String> &names, const List<Varian } Pair<String, int> LocalDebugger::to_breakpoint(const String &p_line) { - String breakpoint_part = p_line.get_slicec(' ', 1); Pair<String, int> breakpoint; @@ -381,18 +354,15 @@ Pair<String, int> LocalDebugger::to_breakpoint(const String &p_line) { } void LocalDebugger::send_message(const String &p_message, const Array &p_args) { - // This needs to be cleaned up entirely. // print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args))); } void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) { - - print_line("ERROR: '" + (p_descr.empty() ? p_err : p_descr) + "'"); + print_line("ERROR: '" + (p_descr.is_empty() ? p_err : p_descr) + "'"); } LocalDebugger::LocalDebugger() { - options["variable_prefix"] = ""; // Bind scripts profiler. @@ -411,6 +381,7 @@ LocalDebugger::LocalDebugger() { LocalDebugger::~LocalDebugger() { unregister_profiler("scripts"); - if (scripts_profiler) + if (scripts_profiler) { memdelete(scripts_profiler); + } } diff --git a/core/debugger/local_debugger.h b/core/debugger/local_debugger.h index 2c4302f4da..e793b2a859 100644 --- a/core/debugger/local_debugger.h +++ b/core/debugger/local_debugger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,11 +32,10 @@ #define LOCAL_DEBUGGER_H #include "core/debugger/engine_debugger.h" -#include "core/list.h" -#include "core/script_language.h" +#include "core/object/script_language.h" +#include "core/templates/list.h" class LocalDebugger : public EngineDebugger { - private: struct ScriptsProfiler; diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 97d5c71b6f..7392e6b9a6 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,13 +30,13 @@ #include "remote_debugger.h" +#include "core/config/project_settings.h" #include "core/debugger/debugger_marshalls.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" #include "core/input/input.h" +#include "core/object/script_language.h" #include "core/os/os.h" -#include "core/project_settings.h" -#include "core/script_language.h" #include "scene/main/node.h" #include "servers/display_server.h" @@ -57,7 +57,6 @@ void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) { } struct RemoteDebugger::NetworkProfiler { - public: typedef DebuggerMarshalls::MultiplayerNodeInfo NodeInfo; struct BandwidthFrame { @@ -97,8 +96,9 @@ public: } void init_node(const ObjectID p_node) { - if (multiplayer_node_data.has(p_node)) + if (multiplayer_node_data.has(p_node)) { return; + } multiplayer_node_data.insert(p_node, DebuggerMarshalls::MultiplayerNodeInfo()); multiplayer_node_data[p_node].node = p_node; multiplayer_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path(); @@ -191,7 +191,6 @@ struct RemoteDebugger::ScriptsProfiler { typedef DebuggerMarshalls::ScriptFunctionSignature FunctionSignature; typedef DebuggerMarshalls::ScriptFunctionInfo FunctionInfo; struct ProfileInfoSort { - bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const { return A->total_time < B->total_time; } @@ -220,10 +219,11 @@ struct RemoteDebugger::ScriptsProfiler { void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) { int ofs = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { - if (p_accumulated) + if (p_accumulated) { ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs); - else + } else { ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs); + } } for (int i = 0; i < ofs; i++) { @@ -270,7 +270,6 @@ struct RemoteDebugger::ScriptsProfiler { }; struct RemoteDebugger::ServersProfiler { - bool skip_profile_frame = false; typedef DebuggerMarshalls::ServerInfo ServerInfo; typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo; @@ -318,7 +317,7 @@ struct RemoteDebugger::ServersProfiler { void _send_frame_data(bool p_final) { DebuggerMarshalls::ServersProfilerFrame frame; - frame.frame_number = Engine::get_singleton()->get_idle_frames(); + frame.frame_number = Engine::get_singleton()->get_process_frames(); frame.frame_time = frame_time; frame.idle_time = idle_time; frame.physics_time = physics_time; @@ -347,7 +346,6 @@ struct RemoteDebugger::ServersProfiler { }; struct RemoteDebugger::VisualProfiler { - typedef DebuggerMarshalls::ServerInfo ServerInfo; typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo; @@ -362,8 +360,9 @@ struct RemoteDebugger::VisualProfiler { void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile(); DebuggerMarshalls::VisualProfilerFrame frame; - if (!profile_areas.size()) + if (!profile_areas.size()) { return; + } frame.frame_number = RS::get_singleton()->get_frame_profile_frame(); frame.areas.append_array(profile_areas); @@ -372,26 +371,47 @@ struct RemoteDebugger::VisualProfiler { }; struct RemoteDebugger::PerformanceProfiler { - Object *performance = nullptr; int last_perf_time = 0; + uint64_t last_monitor_modification_time = 0; void toggle(bool p_enable, const Array &p_opts) {} void add(const Array &p_data) {} void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { - if (!performance) + if (!performance) { return; + } uint64_t pt = OS::get_singleton()->get_ticks_msec(); - if (pt - last_perf_time < 1000) + if (pt - last_perf_time < 1000) { return; + } last_perf_time = pt; + + Array custom_monitor_names = performance->call("get_custom_monitor_names"); + + uint64_t monitor_modification_time = performance->call("get_monitor_modification_time"); + if (monitor_modification_time > last_monitor_modification_time) { + last_monitor_modification_time = monitor_modification_time; + EngineDebugger::get_singleton()->send_message("performance:profile_names", custom_monitor_names); + } + int max = performance->get("MONITOR_MAX"); Array arr; - arr.resize(max); + arr.resize(max + custom_monitor_names.size()); for (int i = 0; i < max; i++) { arr[i] = performance->call("get_monitor", i); } + + for (int i = 0; i < custom_monitor_names.size(); i++) { + Variant monitor_value = performance->call("get_custom_monitor", custom_monitor_names[i]); + if (!monitor_value.is_num()) { + ERR_PRINT("Value of custom monitor '" + String(custom_monitor_names[i]) + "' is not a number"); + arr[i + max] = Variant(); + } + arr[i + max] = monitor_value; + } + EngineDebugger::get_singleton()->send_message("performance:profile_frame", arr); } @@ -401,14 +421,12 @@ struct RemoteDebugger::PerformanceProfiler { }; void RemoteDebugger::_send_resource_usage() { - DebuggerMarshalls::ResourceUsage usage; List<RS::TextureInfo> tinfo; RS::get_singleton()->texture_debug_usage(&tinfo); for (List<RS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) { - DebuggerMarshalls::ResourceInfo info; info.path = E->get().path; info.vram = E->get().bytes; @@ -430,26 +448,29 @@ Error RemoteDebugger::_put_msg(String p_message, Array p_data) { msg.push_back(p_message); msg.push_back(p_data); Error err = peer->put_message(msg); - if (err != OK) + if (err != OK) { n_messages_dropped++; + } return err; } void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) { - - if (p_type == ERR_HANDLER_SCRIPT) + if (p_type == ERR_HANDLER_SCRIPT) { return; //ignore script errors, those go through debugger + } RemoteDebugger *rd = (RemoteDebugger *)p_this; - if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) // Can't handle recursive errors during flush. + if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) { // Can't handle recursive errors during flush. return; + } Vector<ScriptLanguage::StackInfo> si; for (int i = 0; i < ScriptServer::get_language_count(); i++) { si = ScriptServer::get_language(i)->debug_get_current_stack_info(); - if (si.size()) + if (si.size()) { break; + } } // send_error will lock internally. @@ -457,17 +478,18 @@ void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char * } void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) { - RemoteDebugger *rd = (RemoteDebugger *)p_this; - if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) // Can't handle recursive prints during flush. + if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) { // Can't handle recursive prints during flush. return; + } String s = p_string; int allowed_chars = MIN(MAX(rd->max_chars_per_second - rd->char_count, 0), s.length()); - if (allowed_chars == 0 && s.length() > 0) + if (allowed_chars == 0 && s.length() > 0) { return; + } if (allowed_chars < s.length()) { s = s.substr(0, allowed_chars); @@ -478,8 +500,9 @@ void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p rd->char_count += allowed_chars; bool overflowed = rd->char_count >= rd->max_chars_per_second; if (rd->is_peer_connected()) { - if (overflowed) + if (overflowed) { s += "[...]"; + } OutputString output_string; output_string.message = s; @@ -511,17 +534,18 @@ void RemoteDebugger::flush_output() { flush_thread = Thread::get_caller_id(); flushing = true; MutexLock lock(mutex); - if (!is_peer_connected()) + if (!is_peer_connected()) { return; + } if (n_messages_dropped > 0) { ErrorMessage err_msg = _create_overflow_error("TOO_MANY_MESSAGES", "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped. Profiling might misbheave, try raising 'network/limits/debugger/max_queued_messages' in project setting."); - if (_put_msg("error", err_msg.serialize()) == OK) + if (_put_msg("error", err_msg.serialize()) == OK) { n_messages_dropped = 0; + } } if (output_strings.size()) { - // Join output strings so we generate less messages. Vector<String> joined_log_strings; Vector<String> strings; @@ -529,7 +553,7 @@ void RemoteDebugger::flush_output() { for (int i = 0; i < output_strings.size(); i++) { const OutputString &output_string = output_strings[i]; if (output_string.type == MESSAGE_TYPE_ERROR) { - if (!joined_log_strings.empty()) { + if (!joined_log_strings.is_empty()) { strings.push_back(String("\n").join(joined_log_strings)); types.push_back(MESSAGE_TYPE_LOG); joined_log_strings.clear(); @@ -541,7 +565,7 @@ void RemoteDebugger::flush_output() { } } - if (!joined_log_strings.empty()) { + if (!joined_log_strings.is_empty()) { strings.push_back(String("\n").join(joined_log_strings)); types.push_back(MESSAGE_TYPE_LOG); } @@ -574,7 +598,6 @@ void RemoteDebugger::flush_output() { } void RemoteDebugger::send_message(const String &p_message, const Array &p_args) { - MutexLock lock(mutex); if (is_peer_connected()) { _put_msg(p_message, p_args); @@ -582,7 +605,6 @@ void RemoteDebugger::send_message(const String &p_message, const Array &p_args) } void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) { - ErrorMessage oe; oe.error = p_err; oe.error_descr = p_descr; @@ -597,8 +619,9 @@ void RemoteDebugger::send_error(const String &p_func, const String &p_file, int oe.msec = time % 1000; oe.callstack.append_array(script_debugger->get_error_stack_info()); - if (flushing && Thread::get_caller_id() == flush_thread) // Can't handle recursive errors during flush. + if (flushing && Thread::get_caller_id() == flush_thread) { // Can't handle recursive errors during flush. return; + } MutexLock lock(mutex); @@ -609,7 +632,6 @@ void RemoteDebugger::send_error(const String &p_func, const String &p_file, int } if (is_peer_connected()) { - if (oe.warning) { if (warn_count > max_warnings_per_second) { n_warnings_dropped++; @@ -657,22 +679,27 @@ Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, boo return OK; } const String cap = p_msg.substr(0, idx); - if (!has_capture(cap)) + if (!has_capture(cap)) { return ERR_UNAVAILABLE; // Unknown message... + } const String msg = p_msg.substr(idx + 1); return capture_parse(cap, msg, p_data, r_captured); } void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { - //this function is called when there is a debugger break (bug on script) //or when execution is paused from editor - if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint) + if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint) { return; + } ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway."); + if (!peer->can_block()) { + return; // Peer does not support blocking IO. We could at least send the error though. + } + ScriptLanguage *script_lang = script_debugger->get_break_language(); const String error_str = script_lang ? script_lang->debug_get_error() : ""; Array msg; @@ -683,8 +710,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug. Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode(); - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) + if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + } uint64_t loop_begin_usec = 0; uint64_t loop_time_sec = 0; @@ -695,7 +723,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { peer->poll(); if (peer->has_message()) { - Array cmd = peer->get_message(); ERR_CONTINUE(cmd.size() != 2); @@ -773,10 +800,11 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else if (command == "breakpoint") { ERR_FAIL_COND(data.size() < 3); bool set = data[2]; - if (set) + if (set) { script_debugger->insert_breakpoint(data[1], data[0]); - else + } else { script_debugger->remove_breakpoint(data[1], data[0]); + } } else if (command == "set_skip_breakpoints") { ERR_FAIL_COND(data.size() < 1); @@ -784,8 +812,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else { bool captured = false; ERR_CONTINUE(_try_capture(command, data, captured) != OK); - if (!captured) + if (!captured) { WARN_PRINT("Unknown message received from debugger: " + command); + } } } else { OS::get_singleton()->delay_usec(10000); @@ -802,18 +831,19 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { send_message("debug_exit", Array()); - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) + if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { Input::get_singleton()->set_mouse_mode(mouse_mode); + } } void RemoteDebugger::poll_events(bool p_is_idle) { - if (peer.is_null()) + if (peer.is_null()) { return; + } flush_output(); peer->poll(); while (peer->has_message()) { - Array arr = peer->get_message(); ERR_CONTINUE(arr.size() != 2); @@ -829,8 +859,9 @@ void RemoteDebugger::poll_events(bool p_is_idle) { } const String cap = cmd.substr(0, idx); - if (!has_capture(cap)) + if (!has_capture(cap)) { continue; // Unknown message... + } const String msg = cmd.substr(idx + 1); capture_parse(cap, msg, arr[1], parsed); @@ -853,10 +884,11 @@ Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bo } else if (p_cmd == "breakpoint") { ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA); bool set = p_data[2]; - if (set) + if (set) { script_debugger->insert_breakpoint(p_data[1], p_data[0]); - else + } else { script_debugger->remove_breakpoint(p_data[1], p_data[0]); + } } else if (p_cmd == "set_skip_breakpoints") { ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); @@ -886,13 +918,6 @@ Error RemoteDebugger::_profiler_capture(const String &p_cmd, const Array &p_data return OK; } -RemoteDebugger *RemoteDebugger::create_for_uri(const String &p_uri) { - Ref<RemoteDebuggerPeer> peer = RemoteDebuggerPeer::create_from_uri(p_uri); - if (peer.is_valid()) - return memnew(RemoteDebugger(peer)); - return nullptr; -} - RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) { peer = p_peer; max_chars_per_second = GLOBAL_GET("network/limits/debugger/max_chars_per_second"); @@ -954,6 +979,7 @@ RemoteDebugger::~RemoteDebugger() { memdelete(servers_profiler); memdelete(network_profiler); memdelete(visual_profiler); - if (performance_profiler) + if (performance_profiler) { memdelete(performance_profiler); + } } diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index cac0bc3730..28e670747e 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,13 +31,13 @@ #ifndef REMOTE_DEBUGGER_H #define REMOTE_DEBUGGER_H -#include "core/array.h" #include "core/debugger/debugger_marshalls.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/remote_debugger_peer.h" -#include "core/object.h" -#include "core/string_name.h" -#include "core/ustring.h" +#include "core/object/class_db.h" +#include "core/string/string_name.h" +#include "core/string/ustring.h" +#include "core/variant/array.h" class RemoteDebugger : public EngineDebugger { public: @@ -108,8 +108,6 @@ private: Error _try_capture(const String &p_name, const Array &p_data, bool &r_captured); public: - static RemoteDebugger *create_for_uri(const String &p_uri); - // Overrides void poll_events(bool p_is_idle); void send_message(const String &p_message, const Array &p_args); diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index ed04431177..2e388d5934 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,9 @@ #include "remote_debugger_peer.h" +#include "core/config/project_settings.h" #include "core/io/marshalls.h" #include "core/os/os.h" -#include "core/project_settings.h" bool RemoteDebuggerPeerTCP::is_peer_connected() { return connected; @@ -52,8 +52,9 @@ Array RemoteDebuggerPeerTCP::get_message() { Error RemoteDebuggerPeerTCP::put_message(const Array &p_arr) { MutexLock lock(mutex); - if (out_queue.size() >= max_queued_messages) + if (out_queue.size() >= max_queued_messages) { return ERR_OUT_OF_MEMORY; + } out_queue.push_back(p_arr); return OK; @@ -99,8 +100,9 @@ void RemoteDebuggerPeerTCP::_write_out() { while (tcp_client->poll(NetSocket::POLL_TYPE_OUT) == OK) { uint8_t *buf = out_buf.ptrw(); if (out_left <= 0) { - if (out_queue.size() == 0) + if (out_queue.size() == 0) { break; // Nothing left to send + } mutex.lock(); Variant var = out_queue[0]; out_queue.pop_front(); @@ -154,12 +156,12 @@ void RemoteDebuggerPeerTCP::_read_in() { } Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_port) { - IP_Address ip; - if (p_host.is_valid_ip_address()) + if (p_host.is_valid_ip_address()) { ip = p_host; - else + } else { ip = IP::get_singleton()->resolve_hostname(p_host); + } int port = p_port; @@ -169,23 +171,20 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po tcp_client->connect_to_host(ip, port); for (int i = 0; i < tries; i++) { - if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) { print_verbose("Remote Debugger: Connected!"); break; } else { - const int ms = waits[i]; OS::get_singleton()->delay_usec(ms * 1000); print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); - }; - }; + } + } if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + "."); return FAILED; - }; + } connected = true; #ifndef NO_THREADS running = true; @@ -198,8 +197,9 @@ void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) { RemoteDebuggerPeerTCP *peer = (RemoteDebuggerPeerTCP *)p_ud; while (peer->running && peer->is_peer_connected()) { peer->_poll(); - if (!peer->is_peer_connected()) + if (!peer->is_peer_connected()) { break; + } peer->tcp_client->poll(NetSocket::POLL_TYPE_IN_OUT, 1); } } @@ -218,22 +218,24 @@ void RemoteDebuggerPeerTCP::_poll() { } } -Ref<RemoteDebuggerPeer> RemoteDebuggerPeer::create_from_uri(const String p_uri) { - if (!p_uri.begins_with("tcp://")) - return Ref<RemoteDebuggerPeer>(); // Only TCP supported for now, more to come. +RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) { + ERR_FAIL_COND_V(!p_uri.begins_with("tcp://"), nullptr); String debug_host = p_uri.replace("tcp://", ""); uint16_t debug_port = 6007; if (debug_host.find(":") != -1) { - int sep_pos = debug_host.find_last(":"); + int sep_pos = debug_host.rfind(":"); debug_port = debug_host.substr(sep_pos + 1).to_int(); debug_host = debug_host.substr(0, sep_pos); } - Ref<RemoteDebuggerPeerTCP> peer = Ref<RemoteDebuggerPeer>(memnew(RemoteDebuggerPeerTCP)); + + RemoteDebuggerPeerTCP *peer = memnew(RemoteDebuggerPeerTCP); Error err = peer->connect_to_host(debug_host, debug_port); - if (err != OK) - return Ref<RemoteDebuggerPeer>(); + if (err != OK) { + memdelete(peer); + return nullptr; + } return peer; } diff --git a/core/debugger/remote_debugger_peer.h b/core/debugger/remote_debugger_peer.h index e4b838f145..c759c65568 100644 --- a/core/debugger/remote_debugger_peer.h +++ b/core/debugger/remote_debugger_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,17 +32,16 @@ #define REMOTE_DEBUGGER_PEER_H #include "core/io/stream_peer_tcp.h" +#include "core/object/reference.h" #include "core/os/mutex.h" #include "core/os/thread.h" -#include "core/reference.h" -#include "core/ustring.h" +#include "core/string/ustring.h" class RemoteDebuggerPeer : public Reference { protected: int max_queued_messages = 4096; public: - static Ref<RemoteDebuggerPeer> create_from_uri(const String p_uri); virtual bool is_peer_connected() = 0; virtual bool has_message() = 0; virtual Error put_message(const Array &p_arr) = 0; @@ -50,6 +49,7 @@ public: virtual void close() = 0; virtual void poll() = 0; virtual int get_max_message_size() const = 0; + virtual bool can_block() const { return true; } // If blocking io is allowed on main thread (debug). RemoteDebuggerPeer(); }; @@ -77,6 +77,8 @@ private: void _read_in(); public: + static RemoteDebuggerPeer *create(const String &p_uri); + Error connect_to_host(const String &p_host, uint16_t p_port); void poll(); diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp index 935ad01d80..6d1e4ed101 100644 --- a/core/debugger/script_debugger.cpp +++ b/core/debugger/script_debugger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,69 +33,63 @@ #include "core/debugger/engine_debugger.h" void ScriptDebugger::set_lines_left(int p_left) { - lines_left = p_left; } int ScriptDebugger::get_lines_left() const { - return lines_left; } void ScriptDebugger::set_depth(int p_depth) { - depth = p_depth; } int ScriptDebugger::get_depth() const { - return depth; } void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) { - - if (!breakpoints.has(p_line)) + if (!breakpoints.has(p_line)) { breakpoints[p_line] = Set<StringName>(); + } breakpoints[p_line].insert(p_source); } void ScriptDebugger::remove_breakpoint(int p_line, const StringName &p_source) { - - if (!breakpoints.has(p_line)) + if (!breakpoints.has(p_line)) { return; + } breakpoints[p_line].erase(p_source); - if (breakpoints[p_line].size() == 0) + if (breakpoints[p_line].size() == 0) { breakpoints.erase(p_line); + } } -bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const { - if (!breakpoints.has(p_line)) +bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const { + if (!breakpoints.has(p_line)) { return false; + } return breakpoints[p_line].has(p_source); } -bool ScriptDebugger::is_breakpoint_line(int p_line) const { +bool ScriptDebugger::is_breakpoint_line(int p_line) const { return breakpoints.has(p_line); } String ScriptDebugger::breakpoint_find_source(const String &p_source) const { - return p_source; } void ScriptDebugger::clear_breakpoints() { - breakpoints.clear(); } void ScriptDebugger::set_skip_breakpoints(bool p_skip_breakpoints) { - skip_breakpoints = p_skip_breakpoints; } bool ScriptDebugger::is_skipping_breakpoints() { - return skip_breakpoints; } @@ -118,6 +112,5 @@ Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const { } ScriptLanguage *ScriptDebugger::get_break_language() const { - return break_lang; } diff --git a/core/debugger/script_debugger.h b/core/debugger/script_debugger.h index e5066273d2..9f034a5e5d 100644 --- a/core/debugger/script_debugger.h +++ b/core/debugger/script_debugger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,14 +31,13 @@ #ifndef SCRIPT_DEBUGGER_H #define SCRIPT_DEBUGGER_H -#include "core/map.h" -#include "core/script_language.h" -#include "core/set.h" -#include "core/string_name.h" -#include "core/vector.h" +#include "core/object/script_language.h" +#include "core/string/string_name.h" +#include "core/templates/map.h" +#include "core/templates/set.h" +#include "core/templates/vector.h" class ScriptDebugger { - typedef ScriptLanguage::StackInfo StackInfo; int lines_left = -1; diff --git a/core/doc_data.cpp b/core/doc_data.cpp new file mode 100644 index 0000000000..45450bf97a --- /dev/null +++ b/core/doc_data.cpp @@ -0,0 +1,126 @@ +/*************************************************************************/ +/* doc_data.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "doc_data.h" + +void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) { + if (p_retinfo.type == Variant::INT && p_retinfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + p_method.return_enum = p_retinfo.class_name; + if (p_method.return_enum.begins_with("_")) { //proxy class + p_method.return_enum = p_method.return_enum.substr(1, p_method.return_enum.length()); + } + p_method.return_type = "int"; + } else if (p_retinfo.class_name != StringName()) { + p_method.return_type = p_retinfo.class_name; + } else if (p_retinfo.type == Variant::ARRAY && p_retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) { + p_method.return_type = p_retinfo.hint_string + "[]"; + } else if (p_retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { + p_method.return_type = p_retinfo.hint_string; + } else if (p_retinfo.type == Variant::NIL && p_retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + p_method.return_type = "Variant"; + } else if (p_retinfo.type == Variant::NIL) { + p_method.return_type = "void"; + } else { + p_method.return_type = Variant::get_type_name(p_retinfo.type); + } +} + +void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo) { + p_argument.name = p_arginfo.name; + + if (p_arginfo.type == Variant::INT && p_arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + p_argument.enumeration = p_arginfo.class_name; + if (p_argument.enumeration.begins_with("_")) { //proxy class + p_argument.enumeration = p_argument.enumeration.substr(1, p_argument.enumeration.length()); + } + p_argument.type = "int"; + } else if (p_arginfo.class_name != StringName()) { + p_argument.type = p_arginfo.class_name; + } else if (p_arginfo.type == Variant::ARRAY && p_arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { + p_argument.type = p_arginfo.hint_string + "[]"; + } else if (p_arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { + p_argument.type = p_arginfo.hint_string; + } else if (p_arginfo.type == Variant::NIL) { + // Parameters cannot be void, so PROPERTY_USAGE_NIL_IS_VARIANT is not necessary + p_argument.type = "Variant"; + } else { + p_argument.type = Variant::get_type_name(p_arginfo.type); + } +} + +void DocData::property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo) { + p_property.name = p_memberinfo.propinfo.name; + p_property.description = p_memberinfo.doc_string; + + if (p_memberinfo.propinfo.type == Variant::OBJECT) { + p_property.type = p_memberinfo.propinfo.class_name; + } else if (p_memberinfo.propinfo.type == Variant::NIL && p_memberinfo.propinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + p_property.type = "Variant"; + } else { + p_property.type = Variant::get_type_name(p_memberinfo.propinfo.type); + } + + p_property.setter = p_memberinfo.setter; + p_property.getter = p_memberinfo.getter; + + if (p_memberinfo.has_default_value && p_memberinfo.default_value.get_type() != Variant::OBJECT) { + p_property.default_value = p_memberinfo.default_value.get_construct_string().replace("\n", ""); + } + + p_property.overridden = false; +} + +void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc) { + p_method.name = p_methodinfo.name; + p_method.description = p_desc; + + return_doc_from_retinfo(p_method, p_methodinfo.return_val); + + for (int i = 0; i < p_methodinfo.arguments.size(); i++) { + DocData::ArgumentDoc argument; + argument_doc_from_arginfo(argument, p_methodinfo.arguments[i]); + int default_arg_index = i - (p_methodinfo.arguments.size() - p_methodinfo.default_arguments.size()); + if (default_arg_index >= 0) { + Variant default_arg = p_methodinfo.default_arguments[default_arg_index]; + argument.default_value = default_arg.get_construct_string(); + } + p_method.arguments.push_back(argument); + } +} + +void DocData::constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc) { + p_const.name = p_name; + p_const.value = p_value; + p_const.description = p_desc; +} + +void DocData::signal_doc_from_methodinfo(DocData::MethodDoc &p_signal, const MethodInfo &p_methodinfo, const String &p_desc) { + return method_doc_from_methodinfo(p_signal, p_methodinfo, p_desc); +} diff --git a/core/doc_data.h b/core/doc_data.h new file mode 100644 index 0000000000..46ab697768 --- /dev/null +++ b/core/doc_data.h @@ -0,0 +1,152 @@ +/*************************************************************************/ +/* doc_data.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 DOC_DATA_H +#define DOC_DATA_H + +#include "core/io/xml_parser.h" +#include "core/templates/map.h" +#include "core/variant/variant.h" + +struct ScriptMemberInfo { + PropertyInfo propinfo; + String doc_string; + StringName setter; + StringName getter; + + bool has_default_value = false; + Variant default_value; +}; + +class DocData { +public: + struct ArgumentDoc { + String name; + String type; + String enumeration; + String default_value; + bool operator<(const ArgumentDoc &p_arg) const { + if (name == p_arg.name) { + return type < p_arg.type; + } + return name < p_arg.name; + } + }; + + struct MethodDoc { + String name; + String return_type; + String return_enum; + String qualifiers; + String description; + Vector<ArgumentDoc> arguments; + bool operator<(const MethodDoc &p_method) const { + if (name == p_method.name) { + // Must be a constructor since there is no overloading. + // We want this arbitrary order for a class "Foo": + // - 1. Default constructor: Foo() + // - 2. Copy constructor: Foo(Foo) + // - 3+. Other constructors Foo(Bar, ...) based on first argument's name + if (arguments.size() == 0 || p_method.arguments.size() == 0) { // 1. + return arguments.size() < p_method.arguments.size(); + } + if (arguments[0].type == return_type || p_method.arguments[0].type == p_method.return_type) { // 2. + return (arguments[0].type == return_type) || (p_method.arguments[0].type != p_method.return_type); + } + return arguments[0] < p_method.arguments[0]; + } + return name < p_method.name; + } + }; + + struct ConstantDoc { + String name; + String value; + bool is_value_valid = false; + String enumeration; + String description; + bool operator<(const ConstantDoc &p_const) const { + return name < p_const.name; + } + }; + + struct EnumDoc { + String name = "@unnamed_enum"; + String description; + Vector<DocData::ConstantDoc> values; + }; + + struct PropertyDoc { + String name; + String type; + String enumeration; + String description; + String setter, getter; + String default_value; + bool overridden = false; + bool operator<(const PropertyDoc &p_prop) const { + return name < p_prop.name; + } + }; + + struct TutorialDoc { + String link; + String title; + }; + + struct ClassDoc { + String name; + String inherits; + String category; + String brief_description; + String description; + Vector<TutorialDoc> tutorials; + Vector<MethodDoc> methods; + Vector<MethodDoc> signals; + Vector<ConstantDoc> constants; + Map<String, String> enums; + Vector<PropertyDoc> properties; + Vector<PropertyDoc> theme_properties; + bool is_script_doc = false; + String script_path; + bool operator<(const ClassDoc &p_class) const { + return name < p_class.name; + } + }; + + static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo); + static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo); + static void property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo); + static void method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc); + static void constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc); + static void signal_doc_from_methodinfo(DocData::MethodDoc &p_signal, const MethodInfo &p_methodinfo, const String &p_desc); +}; + +#endif // DOC_DATA_H diff --git a/core/error/SCsub b/core/error/SCsub new file mode 100644 index 0000000000..dfd6248a94 --- /dev/null +++ b/core/error/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +env_error = env.Clone() + +env_error.add_source_files(env.core_sources, "*.cpp") diff --git a/core/error_list.h b/core/error/error_list.h index a0218cf045..f032f44c1f 100644 --- a/core/error_list.h +++ b/core/error/error_list.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/error_macros.cpp b/core/error/error_macros.cpp index 5de070844a..272dda97d8 100644 --- a/core/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,13 +31,12 @@ #include "error_macros.h" #include "core/io/logger.h" -#include "core/ustring.h" -#include "os/os.h" +#include "core/os/os.h" +#include "core/string/ustring.h" static ErrorHandlerList *error_handler_list = nullptr; void add_error_handler(ErrorHandlerList *p_handler) { - _global_lock(); p_handler->next = error_handler_list; error_handler_list = p_handler; @@ -45,20 +44,18 @@ void add_error_handler(ErrorHandlerList *p_handler) { } void remove_error_handler(ErrorHandlerList *p_handler) { - _global_lock(); ErrorHandlerList *prev = nullptr; ErrorHandlerList *l = error_handler_list; while (l) { - if (l == p_handler) { - - if (prev) + if (prev) { prev->next = l->next; - else + } else { error_handler_list = l->next; + } break; } prev = l; @@ -77,13 +74,11 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co } void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type) { - OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, (Logger::ErrorType)p_type); _global_lock(); ErrorHandlerList *l = error_handler_list; while (l) { - l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_type); l = l->next; } @@ -104,7 +99,6 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co } void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool fatal) { - String fstr(fatal ? "FATAL: " : ""); String err(fstr + "Index " + p_index_str + " = " + itos(p_index) + " is out of bounds (" + p_size_str + " = " + itos(p_size) + ")."); _err_print_error(p_function, p_file, p_line, err.utf8().get_data(), p_message); diff --git a/core/error_macros.h b/core/error/error_macros.h index 18c46c9e7d..8eb6217ce8 100644 --- a/core/error_macros.h +++ b/core/error/error_macros.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -47,17 +47,12 @@ enum ErrorHandlerType { typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type); struct ErrorHandlerList { + ErrorHandlerFunc errfunc = nullptr; + void *userdata = nullptr; - ErrorHandlerFunc errfunc; - void *userdata; + ErrorHandlerList *next = nullptr; - ErrorHandlerList *next; - - ErrorHandlerList() { - errfunc = 0; - next = 0; - userdata = 0; - } + ErrorHandlerList() {} }; void add_error_handler(ErrorHandlerList *p_handler); @@ -108,6 +103,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * running application to fail or crash. * Always try to return processable data, so the engine can keep running well. * Use the _MSG versions to print a meaningful message to help with debugging. + * + * The `((void)0)` no-op statement is used as a trick to force us to put a semicolon after + * those macros, making them look like proper statements. + * The if wrappers are used to ensure that the macro replacement does not trigger unexpected + * issues when expanded e.g. after an `if (cond) ERR_FAIL();` without braces. */ // Index out of bounds error macros. @@ -285,7 +285,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If it is null, the current function returns. */ #define ERR_FAIL_NULL(m_param) \ - if (unlikely(!m_param)) { \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ return; \ } else \ @@ -296,7 +296,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If it is null, prints `m_msg` and the current function returns. */ #define ERR_FAIL_NULL_MSG(m_param, m_msg) \ - if (unlikely(!m_param)) { \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ return; \ } else \ @@ -310,7 +310,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If it is null, the current function returns `m_retval`. */ #define ERR_FAIL_NULL_V(m_param, m_retval) \ - if (unlikely(!m_param)) { \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ return m_retval; \ } else \ @@ -321,7 +321,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If it is null, prints `m_msg` and the current function returns `m_retval`. */ #define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ - if (unlikely(!m_param)) { \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ return m_retval; \ } else \ @@ -366,11 +366,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures `m_cond` is false. * If `m_cond` is true, the current function returns `m_retval`. */ -#define ERR_FAIL_COND_V(m_cond, m_retval) \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. returned: " _STR(m_retval)); \ - return m_retval; \ - } else \ +#define ERR_FAIL_COND_V(m_cond, m_retval) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval)); \ + return m_retval; \ + } else \ ((void)0) /** @@ -380,11 +380,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If checking for null use ERR_FAIL_NULL_V_MSG instead. * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. */ -#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. returned: " _STR(m_retval), DEBUG_STR(m_msg)); \ - return m_retval; \ - } else \ +#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \ + return m_retval; \ + } else \ ((void)0) /** @@ -476,8 +476,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * The current function returns. */ #define ERR_FAIL() \ - if (1) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed."); \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed."); \ return; \ } else \ ((void)0) @@ -489,8 +489,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Prints `m_msg`, and the current function returns. */ #define ERR_FAIL_MSG(m_msg) \ - if (1) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed.", DEBUG_STR(m_msg)); \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", DEBUG_STR(m_msg)); \ return; \ } else \ ((void)0) @@ -503,8 +503,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * The current function returns `m_retval`. */ #define ERR_FAIL_V(m_retval) \ - if (1) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " _STR(m_retval)); \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \ return m_retval; \ } else \ ((void)0) @@ -516,8 +516,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Prints `m_msg`, and the current function returns `m_retval`. */ #define ERR_FAIL_V_MSG(m_retval, m_msg) \ - if (1) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " _STR(m_retval), DEBUG_STR(m_msg)); \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \ return m_retval; \ } else \ ((void)0) @@ -530,19 +530,19 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Prints `m_msg`. */ #define ERR_PRINT(m_msg) \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg)) + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg) /** * Prints `m_msg` once during the application lifetime. */ -#define ERR_PRINT_ONCE(m_msg) \ - if (1) { \ - static bool first_print = true; \ - if (first_print) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg)); \ - first_print = false; \ - } \ - } else \ +#define ERR_PRINT_ONCE(m_msg) \ + if (true) { \ + static bool first_print = true; \ + if (first_print) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \ + first_print = false; \ + } \ + } else \ ((void)0) // Print warning message macros. @@ -553,21 +553,21 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. */ #define WARN_PRINT(m_msg) \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg), ERR_HANDLER_WARNING) + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING) /** * Prints `m_msg` once during the application lifetime. * * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. */ -#define WARN_PRINT_ONCE(m_msg) \ - if (1) { \ - static bool first_print = true; \ - if (first_print) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \ - first_print = false; \ - } \ - } else \ +#define WARN_PRINT_ONCE(m_msg) \ + if (true) { \ + static bool first_print = true; \ + if (first_print) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING); \ + first_print = false; \ + } \ + } else \ ((void)0) // Print deprecated warning message macros. @@ -576,7 +576,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Warns that the current function is deprecated. */ #define WARN_DEPRECATED \ - if (1) { \ + if (true) { \ static volatile bool warning_shown = false; \ if (!warning_shown) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \ @@ -589,7 +589,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Warns that the current function is deprecated and prints `m_msg`. */ #define WARN_DEPRECATED_MSG(m_msg) \ - if (1) { \ + if (true) { \ static volatile bool warning_shown = false; \ if (!warning_shown) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \ @@ -605,8 +605,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * The application crashes. */ #define CRASH_NOW() \ - if (1) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/Function Failed."); \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \ GENERATE_TRAP(); \ } else \ ((void)0) @@ -617,8 +617,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Prints `m_msg`, and then the application crashes. */ #define CRASH_NOW_MSG(m_msg) \ - if (1) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/Function Failed.", DEBUG_STR(m_msg)); \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", DEBUG_STR(m_msg)); \ GENERATE_TRAP(); \ } else \ ((void)0) diff --git a/core/func_ref.cpp b/core/func_ref.cpp deleted file mode 100644 index 338c17946b..0000000000 --- a/core/func_ref.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/*************************************************************************/ -/* func_ref.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 "func_ref.h" - -Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - - if (id.is_null()) { - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - return Variant(); - } - Object *obj = ObjectDB::get_instance(id); - - if (!obj) { - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - return Variant(); - } - - return obj->call(function, p_args, p_argcount, r_error); -} - -Variant FuncRef::call_funcv(const Array &p_args) { - - ERR_FAIL_COND_V(id.is_null(), Variant()); - - Object *obj = ObjectDB::get_instance(id); - - ERR_FAIL_COND_V(!obj, Variant()); - - return obj->callv(function, p_args); -} - -void FuncRef::set_instance(Object *p_obj) { - - ERR_FAIL_NULL(p_obj); - id = p_obj->get_instance_id(); -} - -void FuncRef::set_function(const StringName &p_func) { - - function = p_func; -} - -bool FuncRef::is_valid() const { - if (id.is_null()) - return false; - - Object *obj = ObjectDB::get_instance(id); - if (!obj) - return false; - - return obj->has_method(function); -} - -void FuncRef::_bind_methods() { - - { - MethodInfo mi; - mi.name = "call_func"; - Vector<Variant> defargs; - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_func", &FuncRef::call_func, mi, defargs); - } - - ClassDB::bind_method(D_METHOD("call_funcv", "arg_array"), &FuncRef::call_funcv); - - ClassDB::bind_method(D_METHOD("set_instance", "instance"), &FuncRef::set_instance); - ClassDB::bind_method(D_METHOD("set_function", "name"), &FuncRef::set_function); - ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid); -} - -FuncRef::FuncRef() { -} diff --git a/core/global_constants.cpp b/core/global_constants.cpp deleted file mode 100644 index afcb283e05..0000000000 --- a/core/global_constants.cpp +++ /dev/null @@ -1,689 +0,0 @@ -/*************************************************************************/ -/* global_constants.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 "global_constants.h" - -#include "core/input/input_event.h" -#include "core/object.h" -#include "core/os/keyboard.h" -#include "core/variant.h" - -struct _GlobalConstant { - -#ifdef DEBUG_METHODS_ENABLED - StringName enum_name; -#endif - const char *name; - int value; - - _GlobalConstant() {} - -#ifdef DEBUG_METHODS_ENABLED - _GlobalConstant(const StringName &p_enum_name, const char *p_name, int p_value) : - enum_name(p_enum_name), - name(p_name), - value(p_value) { - } -#else - _GlobalConstant(const char *p_name, int p_value) : - name(p_name), - value(p_value) { - } -#endif -}; - -static Vector<_GlobalConstant> _global_constants; - -#ifdef DEBUG_METHODS_ENABLED - -#define BIND_GLOBAL_CONSTANT(m_constant) \ - _global_constants.push_back(_GlobalConstant(StringName(), #m_constant, m_constant)); - -#define BIND_GLOBAL_ENUM_CONSTANT(m_constant) \ - _global_constants.push_back(_GlobalConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant)); - -#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \ - _global_constants.push_back(_GlobalConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant)); - -#else - -#define BIND_GLOBAL_CONSTANT(m_constant) \ - _global_constants.push_back(_GlobalConstant(#m_constant, m_constant)); - -#define BIND_GLOBAL_ENUM_CONSTANT(m_constant) \ - _global_constants.push_back(_GlobalConstant(#m_constant, m_constant)); - -#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \ - _global_constants.push_back(_GlobalConstant(m_custom_name, m_constant)); - -#endif - -VARIANT_ENUM_CAST(KeyList); -VARIANT_ENUM_CAST(KeyModifierMask); -VARIANT_ENUM_CAST(ButtonList); -VARIANT_ENUM_CAST(JoystickList); -VARIANT_ENUM_CAST(MidiMessageList); - -void register_global_constants() { - BIND_GLOBAL_ENUM_CONSTANT(MARGIN_LEFT); - BIND_GLOBAL_ENUM_CONSTANT(MARGIN_TOP); - BIND_GLOBAL_ENUM_CONSTANT(MARGIN_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT(MARGIN_BOTTOM); - - BIND_GLOBAL_ENUM_CONSTANT(CORNER_TOP_LEFT); - BIND_GLOBAL_ENUM_CONSTANT(CORNER_TOP_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT(CORNER_BOTTOM_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT(CORNER_BOTTOM_LEFT); - - BIND_GLOBAL_ENUM_CONSTANT(VERTICAL); - BIND_GLOBAL_ENUM_CONSTANT(HORIZONTAL); - - BIND_GLOBAL_ENUM_CONSTANT(HALIGN_LEFT); - BIND_GLOBAL_ENUM_CONSTANT(HALIGN_CENTER); - BIND_GLOBAL_ENUM_CONSTANT(HALIGN_RIGHT); - - BIND_GLOBAL_ENUM_CONSTANT(VALIGN_TOP); - BIND_GLOBAL_ENUM_CONSTANT(VALIGN_CENTER); - BIND_GLOBAL_ENUM_CONSTANT(VALIGN_BOTTOM); - - // huge list of keys - BIND_GLOBAL_CONSTANT(SPKEY); - - BIND_GLOBAL_ENUM_CONSTANT(KEY_ESCAPE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_TAB); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BACKTAB); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BACKSPACE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ENTER); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_ENTER); - BIND_GLOBAL_ENUM_CONSTANT(KEY_INSERT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_DELETE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PAUSE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PRINT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SYSREQ); - BIND_GLOBAL_ENUM_CONSTANT(KEY_CLEAR); - BIND_GLOBAL_ENUM_CONSTANT(KEY_HOME); - BIND_GLOBAL_ENUM_CONSTANT(KEY_END); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LEFT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_UP); - BIND_GLOBAL_ENUM_CONSTANT(KEY_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_DOWN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PAGEUP); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PAGEDOWN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SHIFT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_CONTROL); - BIND_GLOBAL_ENUM_CONSTANT(KEY_META); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ALT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_CAPSLOCK); - BIND_GLOBAL_ENUM_CONSTANT(KEY_NUMLOCK); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SCROLLLOCK); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F1); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F2); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F3); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F4); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F5); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F6); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F7); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F8); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F9); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F10); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F11); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F12); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F13); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F14); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F15); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F16); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_MULTIPLY); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_DIVIDE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_SUBTRACT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_PERIOD); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_ADD); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_0); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_1); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_2); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_3); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_4); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_5); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_6); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_7); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_8); - BIND_GLOBAL_ENUM_CONSTANT(KEY_KP_9); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SUPER_L); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SUPER_R); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MENU); - BIND_GLOBAL_ENUM_CONSTANT(KEY_HYPER_L); - BIND_GLOBAL_ENUM_CONSTANT(KEY_HYPER_R); - BIND_GLOBAL_ENUM_CONSTANT(KEY_HELP); - BIND_GLOBAL_ENUM_CONSTANT(KEY_DIRECTION_L); - BIND_GLOBAL_ENUM_CONSTANT(KEY_DIRECTION_R); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BACK); - BIND_GLOBAL_ENUM_CONSTANT(KEY_FORWARD); - BIND_GLOBAL_ENUM_CONSTANT(KEY_STOP); - BIND_GLOBAL_ENUM_CONSTANT(KEY_REFRESH); - BIND_GLOBAL_ENUM_CONSTANT(KEY_VOLUMEDOWN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_VOLUMEMUTE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_VOLUMEUP); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BASSBOOST); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BASSUP); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BASSDOWN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_TREBLEUP); - BIND_GLOBAL_ENUM_CONSTANT(KEY_TREBLEDOWN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MEDIAPLAY); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MEDIASTOP); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MEDIAPREVIOUS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MEDIANEXT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MEDIARECORD); - BIND_GLOBAL_ENUM_CONSTANT(KEY_HOMEPAGE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_FAVORITES); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SEARCH); - BIND_GLOBAL_ENUM_CONSTANT(KEY_STANDBY); - BIND_GLOBAL_ENUM_CONSTANT(KEY_OPENURL); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCHMAIL); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCHMEDIA); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH0); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH1); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH2); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH3); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH4); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH5); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH6); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH7); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH8); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCH9); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCHA); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCHB); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCHC); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCHD); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCHE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LAUNCHF); - - BIND_GLOBAL_ENUM_CONSTANT(KEY_UNKNOWN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SPACE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_EXCLAM); - BIND_GLOBAL_ENUM_CONSTANT(KEY_QUOTEDBL); - BIND_GLOBAL_ENUM_CONSTANT(KEY_NUMBERSIGN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_DOLLAR); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PERCENT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_AMPERSAND); - BIND_GLOBAL_ENUM_CONSTANT(KEY_APOSTROPHE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PARENLEFT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PARENRIGHT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ASTERISK); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PLUS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_COMMA); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MINUS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PERIOD); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SLASH); - BIND_GLOBAL_ENUM_CONSTANT(KEY_0); - BIND_GLOBAL_ENUM_CONSTANT(KEY_1); - BIND_GLOBAL_ENUM_CONSTANT(KEY_2); - BIND_GLOBAL_ENUM_CONSTANT(KEY_3); - BIND_GLOBAL_ENUM_CONSTANT(KEY_4); - BIND_GLOBAL_ENUM_CONSTANT(KEY_5); - BIND_GLOBAL_ENUM_CONSTANT(KEY_6); - BIND_GLOBAL_ENUM_CONSTANT(KEY_7); - BIND_GLOBAL_ENUM_CONSTANT(KEY_8); - BIND_GLOBAL_ENUM_CONSTANT(KEY_9); - BIND_GLOBAL_ENUM_CONSTANT(KEY_COLON); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SEMICOLON); - BIND_GLOBAL_ENUM_CONSTANT(KEY_LESS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_EQUAL); - BIND_GLOBAL_ENUM_CONSTANT(KEY_GREATER); - BIND_GLOBAL_ENUM_CONSTANT(KEY_QUESTION); - BIND_GLOBAL_ENUM_CONSTANT(KEY_AT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_A); - BIND_GLOBAL_ENUM_CONSTANT(KEY_B); - BIND_GLOBAL_ENUM_CONSTANT(KEY_C); - BIND_GLOBAL_ENUM_CONSTANT(KEY_D); - BIND_GLOBAL_ENUM_CONSTANT(KEY_E); - BIND_GLOBAL_ENUM_CONSTANT(KEY_F); - BIND_GLOBAL_ENUM_CONSTANT(KEY_G); - BIND_GLOBAL_ENUM_CONSTANT(KEY_H); - BIND_GLOBAL_ENUM_CONSTANT(KEY_I); - BIND_GLOBAL_ENUM_CONSTANT(KEY_J); - BIND_GLOBAL_ENUM_CONSTANT(KEY_K); - BIND_GLOBAL_ENUM_CONSTANT(KEY_L); - BIND_GLOBAL_ENUM_CONSTANT(KEY_M); - BIND_GLOBAL_ENUM_CONSTANT(KEY_N); - BIND_GLOBAL_ENUM_CONSTANT(KEY_O); - BIND_GLOBAL_ENUM_CONSTANT(KEY_P); - BIND_GLOBAL_ENUM_CONSTANT(KEY_Q); - BIND_GLOBAL_ENUM_CONSTANT(KEY_R); - BIND_GLOBAL_ENUM_CONSTANT(KEY_S); - BIND_GLOBAL_ENUM_CONSTANT(KEY_T); - BIND_GLOBAL_ENUM_CONSTANT(KEY_U); - BIND_GLOBAL_ENUM_CONSTANT(KEY_V); - BIND_GLOBAL_ENUM_CONSTANT(KEY_W); - BIND_GLOBAL_ENUM_CONSTANT(KEY_X); - BIND_GLOBAL_ENUM_CONSTANT(KEY_Y); - BIND_GLOBAL_ENUM_CONSTANT(KEY_Z); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BRACKETLEFT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BACKSLASH); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BRACKETRIGHT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ASCIICIRCUM); - BIND_GLOBAL_ENUM_CONSTANT(KEY_UNDERSCORE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_QUOTELEFT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BRACELEFT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BAR); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BRACERIGHT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ASCIITILDE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_NOBREAKSPACE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_EXCLAMDOWN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_CENT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_STERLING); - BIND_GLOBAL_ENUM_CONSTANT(KEY_CURRENCY); - BIND_GLOBAL_ENUM_CONSTANT(KEY_YEN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_BROKENBAR); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SECTION); - BIND_GLOBAL_ENUM_CONSTANT(KEY_DIAERESIS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_COPYRIGHT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ORDFEMININE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_GUILLEMOTLEFT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_NOTSIGN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_HYPHEN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_REGISTERED); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MACRON); - BIND_GLOBAL_ENUM_CONSTANT(KEY_DEGREE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PLUSMINUS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_TWOSUPERIOR); - BIND_GLOBAL_ENUM_CONSTANT(KEY_THREESUPERIOR); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ACUTE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MU); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PARAGRAPH); - BIND_GLOBAL_ENUM_CONSTANT(KEY_PERIODCENTERED); - BIND_GLOBAL_ENUM_CONSTANT(KEY_CEDILLA); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ONESUPERIOR); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MASCULINE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_GUILLEMOTRIGHT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ONEQUARTER); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ONEHALF); - BIND_GLOBAL_ENUM_CONSTANT(KEY_THREEQUARTERS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_QUESTIONDOWN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_AGRAVE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_AACUTE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ACIRCUMFLEX); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ATILDE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ADIAERESIS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ARING); - BIND_GLOBAL_ENUM_CONSTANT(KEY_AE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_CCEDILLA); - BIND_GLOBAL_ENUM_CONSTANT(KEY_EGRAVE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_EACUTE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ECIRCUMFLEX); - BIND_GLOBAL_ENUM_CONSTANT(KEY_EDIAERESIS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_IGRAVE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_IACUTE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ICIRCUMFLEX); - BIND_GLOBAL_ENUM_CONSTANT(KEY_IDIAERESIS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ETH); - BIND_GLOBAL_ENUM_CONSTANT(KEY_NTILDE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_OGRAVE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_OACUTE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_OCIRCUMFLEX); - BIND_GLOBAL_ENUM_CONSTANT(KEY_OTILDE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_ODIAERESIS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MULTIPLY); - BIND_GLOBAL_ENUM_CONSTANT(KEY_OOBLIQUE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_UGRAVE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_UACUTE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_UCIRCUMFLEX); - BIND_GLOBAL_ENUM_CONSTANT(KEY_UDIAERESIS); - BIND_GLOBAL_ENUM_CONSTANT(KEY_YACUTE); - BIND_GLOBAL_ENUM_CONSTANT(KEY_THORN); - BIND_GLOBAL_ENUM_CONSTANT(KEY_SSHARP); - - BIND_GLOBAL_ENUM_CONSTANT(KEY_DIVISION); - BIND_GLOBAL_ENUM_CONSTANT(KEY_YDIAERESIS); - - BIND_GLOBAL_ENUM_CONSTANT(KEY_CODE_MASK); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MODIFIER_MASK); - - BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_SHIFT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_ALT); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_META); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_CTRL); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_CMD); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_KPAD); - BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_GROUP_SWITCH); - - // mouse - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_LEFT); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MIDDLE); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_XBUTTON1); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_XBUTTON2); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_UP); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_DOWN); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_LEFT); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_LEFT); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_MIDDLE); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON1); - BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON2); - - //joypads - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_0); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_1); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_2); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_3); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_4); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_5); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_6); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_7); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_8); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_9); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_10); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_11); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_12); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_13); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_14); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_15); - BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_MAX); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_SONY_CIRCLE); - BIND_GLOBAL_ENUM_CONSTANT(JOY_SONY_X); - BIND_GLOBAL_ENUM_CONSTANT(JOY_SONY_SQUARE); - BIND_GLOBAL_ENUM_CONSTANT(JOY_SONY_TRIANGLE); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_XBOX_B); - BIND_GLOBAL_ENUM_CONSTANT(JOY_XBOX_A); - BIND_GLOBAL_ENUM_CONSTANT(JOY_XBOX_X); - BIND_GLOBAL_ENUM_CONSTANT(JOY_XBOX_Y); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_DS_A); - BIND_GLOBAL_ENUM_CONSTANT(JOY_DS_B); - BIND_GLOBAL_ENUM_CONSTANT(JOY_DS_X); - BIND_GLOBAL_ENUM_CONSTANT(JOY_DS_Y); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_GRIP); - BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_PAD); - BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_TRIGGER); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_AX); - BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_BY); - BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_MENU); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_MENU); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_SELECT); - BIND_GLOBAL_ENUM_CONSTANT(JOY_START); - BIND_GLOBAL_ENUM_CONSTANT(JOY_DPAD_UP); - BIND_GLOBAL_ENUM_CONSTANT(JOY_DPAD_DOWN); - BIND_GLOBAL_ENUM_CONSTANT(JOY_DPAD_LEFT); - BIND_GLOBAL_ENUM_CONSTANT(JOY_DPAD_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT(JOY_L); - BIND_GLOBAL_ENUM_CONSTANT(JOY_L2); - BIND_GLOBAL_ENUM_CONSTANT(JOY_L3); - BIND_GLOBAL_ENUM_CONSTANT(JOY_R); - BIND_GLOBAL_ENUM_CONSTANT(JOY_R2); - BIND_GLOBAL_ENUM_CONSTANT(JOY_R3); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_0); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_1); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_2); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_3); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_4); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_5); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_6); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_7); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_8); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_9); - BIND_GLOBAL_ENUM_CONSTANT(JOY_AXIS_MAX); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_LX); - BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_LY); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_RX); - BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_RY); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2); - BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_ANALOG_TRIGGER); - BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_ANALOG_GRIP); - - BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_TOUCHPADX); - BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_TOUCHPADY); - - // midi - BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); - BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); - BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH); - BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE); - BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE); - BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE); - BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND); - - // error list - - BIND_GLOBAL_ENUM_CONSTANT(OK); // (0) - BIND_GLOBAL_ENUM_CONSTANT(FAILED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_UNAVAILABLE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_UNCONFIGURED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_UNAUTHORIZED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_PARAMETER_RANGE_ERROR); // (5) - BIND_GLOBAL_ENUM_CONSTANT(ERR_OUT_OF_MEMORY); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_NOT_FOUND); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_BAD_DRIVE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_BAD_PATH); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_NO_PERMISSION); // (10) - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_ALREADY_IN_USE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_CANT_OPEN); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_CANT_WRITE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_CANT_READ); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_UNRECOGNIZED); // (15) - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_CORRUPT); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_MISSING_DEPENDENCIES); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_EOF); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_OPEN); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_CREATE); // (20) - BIND_GLOBAL_ENUM_CONSTANT(ERR_QUERY_FAILED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_ALREADY_IN_USE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_LOCKED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_TIMEOUT); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_CONNECT); // (25) - BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_RESOLVE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CONNECTION_ERROR); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_ACQUIRE_RESOURCE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_FORK); - BIND_GLOBAL_ENUM_CONSTANT(ERR_INVALID_DATA); // (30) - BIND_GLOBAL_ENUM_CONSTANT(ERR_INVALID_PARAMETER); - BIND_GLOBAL_ENUM_CONSTANT(ERR_ALREADY_EXISTS); - BIND_GLOBAL_ENUM_CONSTANT(ERR_DOES_NOT_EXIST); - BIND_GLOBAL_ENUM_CONSTANT(ERR_DATABASE_CANT_READ); - BIND_GLOBAL_ENUM_CONSTANT(ERR_DATABASE_CANT_WRITE); // (35) - BIND_GLOBAL_ENUM_CONSTANT(ERR_COMPILATION_FAILED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_METHOD_NOT_FOUND); - BIND_GLOBAL_ENUM_CONSTANT(ERR_LINK_FAILED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_SCRIPT_FAILED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CYCLIC_LINK); // (40) - BIND_GLOBAL_ENUM_CONSTANT(ERR_INVALID_DECLARATION); - BIND_GLOBAL_ENUM_CONSTANT(ERR_DUPLICATE_SYMBOL); - BIND_GLOBAL_ENUM_CONSTANT(ERR_PARSE_ERROR); - BIND_GLOBAL_ENUM_CONSTANT(ERR_BUSY); - BIND_GLOBAL_ENUM_CONSTANT(ERR_SKIP); // (45) - BIND_GLOBAL_ENUM_CONSTANT(ERR_HELP); - BIND_GLOBAL_ENUM_CONSTANT(ERR_BUG); - BIND_GLOBAL_ENUM_CONSTANT(ERR_PRINTER_ON_FIRE); - - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_NONE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_RANGE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_EXP_RANGE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_ENUM); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_EXP_EASING); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LENGTH); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_KEY_ACCEL); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_FLAGS); - - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_RENDER); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_PHYSICS); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_RENDER); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_PHYSICS); - - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_FILE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_DIR); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_FILE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS); - - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORAGE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_NETWORK); - - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_HELPER); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_CHECKABLE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_CHECKED); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_INTERNATIONALIZED); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_GROUP); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_CATEGORY); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_SUBGROUP); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_NO_INSTANCE_STATE); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE); - - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_NOEDITOR); - - BIND_GLOBAL_ENUM_CONSTANT(METHOD_FLAG_NORMAL); - BIND_GLOBAL_ENUM_CONSTANT(METHOD_FLAG_EDITOR); - BIND_GLOBAL_ENUM_CONSTANT(METHOD_FLAG_NOSCRIPT); - BIND_GLOBAL_ENUM_CONSTANT(METHOD_FLAG_CONST); - BIND_GLOBAL_ENUM_CONSTANT(METHOD_FLAG_REVERSE); - BIND_GLOBAL_ENUM_CONSTANT(METHOD_FLAG_VIRTUAL); - BIND_GLOBAL_ENUM_CONSTANT(METHOD_FLAG_FROM_SCRIPT); - BIND_GLOBAL_ENUM_CONSTANT(METHOD_FLAGS_DEFAULT); - - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_REAL", Variant::FLOAT); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_STRING", Variant::STRING); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR2", Variant::VECTOR2); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR2I", Variant::VECTOR2I); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_RECT2", Variant::RECT2); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_RECT2I", Variant::RECT2I); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3", Variant::VECTOR3); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3I", Variant::VECTOR3I); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM2D", Variant::TRANSFORM2D); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_PLANE", Variant::PLANE); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_QUAT", Variant::QUAT); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_AABB", Variant::AABB); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_BASIS", Variant::BASIS); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM", Variant::TRANSFORM); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_COLOR", Variant::COLOR); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_STRING_NAME", Variant::STRING_NAME); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_NODE_PATH", Variant::NODE_PATH); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_RID", Variant::_RID); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_OBJECT", Variant::OBJECT); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_CALLABLE", Variant::CALLABLE); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_SIGNAL", Variant::SIGNAL); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_DICTIONARY", Variant::DICTIONARY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_ARRAY", Variant::ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_RAW_ARRAY", Variant::PACKED_BYTE_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_INT32_ARRAY", Variant::PACKED_INT32_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_INT64_ARRAY", Variant::PACKED_INT64_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_FLOAT32_ARRAY", Variant::PACKED_FLOAT32_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_FLOAT64_ARRAY", Variant::PACKED_FLOAT64_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_STRING_ARRAY", Variant::PACKED_STRING_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR2_ARRAY", Variant::PACKED_VECTOR2_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3_ARRAY", Variant::PACKED_VECTOR3_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_COLOR_ARRAY", Variant::PACKED_COLOR_ARRAY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_MAX", Variant::VARIANT_MAX); - - //comparison - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_EQUAL", Variant::OP_EQUAL); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_NOT_EQUAL", Variant::OP_NOT_EQUAL); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_LESS", Variant::OP_LESS); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_LESS_EQUAL", Variant::OP_LESS_EQUAL); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_GREATER", Variant::OP_GREATER); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_GREATER_EQUAL", Variant::OP_GREATER_EQUAL); - //mathematic - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_ADD", Variant::OP_ADD); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_SUBTRACT", Variant::OP_SUBTRACT); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_MULTIPLY", Variant::OP_MULTIPLY); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_DIVIDE", Variant::OP_DIVIDE); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_NEGATE", Variant::OP_NEGATE); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_POSITIVE", Variant::OP_POSITIVE); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_MODULE", Variant::OP_MODULE); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_STRING_CONCAT", Variant::OP_STRING_CONCAT); - //bitwise - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_SHIFT_LEFT", Variant::OP_SHIFT_LEFT); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_SHIFT_RIGHT", Variant::OP_SHIFT_RIGHT); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_BIT_AND", Variant::OP_BIT_AND); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_BIT_OR", Variant::OP_BIT_OR); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_BIT_XOR", Variant::OP_BIT_XOR); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_BIT_NEGATE", Variant::OP_BIT_NEGATE); - //logic - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_AND", Variant::OP_AND); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_OR", Variant::OP_OR); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_XOR", Variant::OP_XOR); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_NOT", Variant::OP_NOT); - //containment - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_IN", Variant::OP_IN); - BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("OP_MAX", Variant::OP_MAX); -} - -void unregister_global_constants() { - - _global_constants.clear(); -} - -int GlobalConstants::get_global_constant_count() { - - return _global_constants.size(); -} - -#ifdef DEBUG_METHODS_ENABLED -StringName GlobalConstants::get_global_constant_enum(int p_idx) { - - return _global_constants[p_idx].enum_name; -} -#else -StringName GlobalConstants::get_global_constant_enum(int p_idx) { - - return StringName(); -} -#endif - -const char *GlobalConstants::get_global_constant_name(int p_idx) { - - return _global_constants[p_idx].name; -} - -int GlobalConstants::get_global_constant_value(int p_idx) { - - return _global_constants[p_idx].value; -} diff --git a/core/input/SCsub b/core/input/SCsub index c641819698..740398b266 100644 --- a/core/input/SCsub +++ b/core/input/SCsub @@ -2,7 +2,6 @@ Import("env") -from platform_methods import run_in_subprocess import input_builders @@ -16,7 +15,7 @@ env.Depends("#core/input/default_controller_mappings.gen.cpp", controller_databa env.CommandNoCache( "#core/input/default_controller_mappings.gen.cpp", controller_databases, - run_in_subprocess(input_builders.make_default_controller_mappings), + env.Run(input_builders.make_default_controller_mappings, "Generating default controller mappings."), ) env.add_source_files(env.core_sources, "*.cpp") diff --git a/core/input/default_controller_mappings.h b/core/input/default_controller_mappings.h index 9e2a69acec..ba5e650226 100644 --- a/core/input/default_controller_mappings.h +++ b/core/input/default_controller_mappings.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index 90d309c1c8..180708cea6 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -4,37 +4,40 @@ # 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, +03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,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, +03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,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, @@ -51,6 +54,7 @@ 03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, 030000006f0e00003201000000000000,Battlefield 4 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, +03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000bc2000006012000000000000,Betop 2126F,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, 03000000bc2000000055000000000000,Betop BFM Gamepad,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, 03000000bc2000006312000000000000,Betop 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:Windows, @@ -73,6 +77,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, @@ -81,6 +86,8 @@ 03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000008f0e00000f31000000000000,EXEQ,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:b3,y:b2,platform:Windows, 03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows, +030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, 03000000852100000201000000000000,FF-GP1,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, 030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, @@ -88,7 +95,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, @@ -97,7 +103,10 @@ 03000000ac0500003d03000000000000,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, 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, +03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 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, +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, 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 +155,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, @@ -153,6 +164,7 @@ 030000006d04000016c2000000000000,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:Windows, 030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d0400001ac2000000000000,Logitech Precision Gamepad,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,lefttrigger:b6,righttrigger:b7,platform:Windows, 03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, @@ -179,8 +191,11 @@ 03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000002418000000000000,Mega Drive,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows, 03000000380700006382000000000000,MLG GamePad 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, +03000000c62400002a89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,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, +03000000c62400002b89000000000000,MOGA XP5-A Plus,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, 03000000efbe0000edfe000000000000,Monect Virtual Controller,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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000250900006688000000000000,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:Windows, +030000006b140000010c000000000000,NACON GC-400ES,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, 030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows, 03000000152000000182000000000000,NGDS,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:Windows, 03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, @@ -193,6 +208,7 @@ 030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, 03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000901000000000000,PDP Versus Fighting Pad,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, 030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -228,8 +244,11 @@ 03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b13,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4, 03000000321500000507000000000000,Razer Raiju Mobile,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, 03000000321500000707000000000000,Razer Raiju Mobile,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, +03000000321500000011000000000000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -244,6 +263,7 @@ 0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, 030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001e01000000000000,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:Windows, 030000006f0e00002801000000000000,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:Windows, 030000006f0e00002f01000000000000,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:Windows, @@ -271,16 +291,19 @@ 03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,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:b3,y:b4,platform:Windows, +03000000110100003114000000000000,SteelSeries Stratus Duo,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, 03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows, 03000000790000001c18000000000000,STK-7024X,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, 03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows, 03000000d620000011a7000000000000,Switch,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, +03000000457500002211000000000000,SZMY-POWER PC 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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000004f04000007d0000000000000,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:Windows, 030000004f0400000ab1000000000000,T.16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows, 03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 030000004f04000015b3000000000000,Thrustmaster Dual Analog 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:Windows, 030000004f04000023b3000000000000,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:Windows, +030000004f0400000ed0000000000000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows, 030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, @@ -302,6 +325,8 @@ 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, +030000006f0e00000702000000000000,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,leftstick:b10,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,29 +335,39 @@ 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, +03000000120c0000101e000000000000,ZEROPLUS P4 Wired 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: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, +030000008f0e00000300000009010000,2In1 USB Joystick,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X, +03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,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:b4,y:b3,platform:Mac OS X, 03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00005106000000010000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001590000001000000,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:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00006528000000010000,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +030000003512000012ab000001000000,8BitDo NES30 Gamepad,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:Mac OS X, 03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000160000001000000,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:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000161000000010000,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:Mac OS X, 03000000c82d00000260000001000000,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:Mac OS X, 03000000c82d00000261000000010000,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:Mac OS X, 03000000c82d00000031000001000000,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:Mac OS X, +03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00003032000000010000,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:a31,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 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, +03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 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, 030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -351,6 +386,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, @@ -373,11 +409,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X, 03000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, +03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,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:Mac OS X, +03000000c62400002b89000000010000,MOGA XP5-A Plus,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X, 030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired 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:Mac OS X, 030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X, +030000006f0e00000901000002010000,PDP Versus Fighting Pad,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:Mac OS X, 030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, 030000004c050000da0c000000010000,Playstation Classic 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:Mac OS X, 03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -392,12 +432,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,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:Mac OS X, +03000000321500000011000000010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 0300000032150000030a000000000000,Razer Wildcat,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, 03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X, 03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, @@ -406,24 +448,29 @@ 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, 03000000381000002014000001000000,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, 03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, 03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, +03000000457500002211000000010000,SZMY-POWER PC 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: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, 030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, +030000004f0400000ed0000000020000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, 03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000bd12000015d0000000010000,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:Mac OS X, 03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, 030000006f0e00000302000025040000,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:Mac OS X, +030000006f0e00000702000003060000,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,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000791d00000103000009010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,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:Mac OS X, 050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, 050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, 030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000c6240000045d000000000000,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, 030000005e0400000a0b000000000000,Xbox Adaptive 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, +030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, @@ -433,8 +480,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,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:Mac OS X, 03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000120c0000100e000000010000,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:Mac OS X, +03000000120c0000101e000000010000,ZEROPLUS P4 Wired 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:Mac OS X, # Linux +03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001038000000010000,8Bitdo FC30 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, 05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001590000011010000,8BitDo N30 Pro 2,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, @@ -455,12 +504,15 @@ 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, +05000000202800000900000000010000,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,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, +03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,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, @@ -468,10 +520,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000006f0e00003901000000430000,Afterglow Prismatic 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, 030000006f0e00003901000013020000,Afterglow Prismatic Wired Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 05000000491900000204000021000000,Amazon Fire Game Controller,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 03000000120c00000500000010010000,AxisPad,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:Linux, +03000000d62000002a79000011010000,BDA PS4 Fightpad,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, 03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, 03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,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, 03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, @@ -499,28 +553,34 @@ 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, 030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00008500000010010000,HORI Fighting Commander,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, +030000000d0f00008600000002010000,Hori Fighting Commander,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, 030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f0000aa00000011010000,HORI Real Arcade Pro,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, 030000000d0f00001600000000010000,Hori Real Arcade Pro.EX-SE (Xbox 360),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux, +03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, 050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000d80400008200000003000000,IMS PCU#0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux, 03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux, 0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000632500007505000011010000,Ipega PG-9099 - Bluetooth Gamepad,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, 030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, 03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,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:a2,start:b9,x:b3,y:b0,platform:Linux, @@ -529,6 +589,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, @@ -557,7 +619,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000120c00000500000000010000,Manta Dualshock 2,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:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, -03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 03000000242f00007300000011010000,Mayflash Magic NS,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, 0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d620000010a7000011010000,Mayflash Magic NS,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, @@ -578,11 +640,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +03000000c62400002b89000011010000,MOGA XP5-A Plus,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,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, 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, +030000006b140000010c000010010000,NACON GC-400ES,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, 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, 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, +050000007e0500000920000001800000,Nintendo Switch Pro Controller (joycond),a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,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, +030000007e0500000920000011810000,Nintendo Switch Pro Controller Wired (joycond),a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,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, 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, 05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux, @@ -590,17 +658,29 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, 05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, 03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +19000000010000000100000001010000,odroidgo2_joypad,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux, +19000000010000000200000011000000,odroidgo2_joypad_v11,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux, 030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 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, +03000000790000001c18000011010000,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, 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, +030000006f0e0000b802000001010000,PDP AFTERGLOW Wired Xbox One Controller,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, 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, +030000006f0e00008001000011010000,PDP CO. LTD. Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,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, +030000006f0e00008701000011010000,PDP Rock Candy Wired Controller for Nintendo Switch,a:b2,b:b1,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:b3,y:b0,platform:Linux, +030000006f0e00000901000011010000,PDP Versus Fighting Pad,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, 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, +030000006f0e00008501000011010000,PDP Wired Fight Pad Pro for Nintendo Switch,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, +0500000049190000030400001b010000,PG-9099,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:a2,righty:a3,start:b11,x:b3,y:b4,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, 03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux, 030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -625,17 +705,21 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 050000004c050000c405000000810000,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, +050000004c050000c405000001800000,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, 050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 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, +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, 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, +030000008916000000fd000024010000,Razer Onza Tournament Edition,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, 03000000321500000204000011010000,Razer Panthera (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, 03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000010000011010000,Razer RAIJU,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, 03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,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, +03000000321500000011000011010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -646,17 +730,19 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux, 0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, 030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00001e01000011010000,Rock Candy 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, 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, 03000000a306000020f6000011010000,Saitek PS2700 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: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,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux, 03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, 03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, 03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, @@ -667,9 +753,12 @@ 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, +03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,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, +03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, 03000000de280000fc11000001000000,Steam 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, 05000000de2800000212000001000000,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, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, @@ -677,18 +766,25 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000de280000ff11000001000000,Steam Virtual 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, 03000000381000003014000075010000,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, 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, +0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,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, 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,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux, 03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 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, +030000004f0400000ed0000011010000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,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, @@ -699,8 +795,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, 03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, 030000006f0e00000302000011010000,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:Linux, +030000006f0e00000702000011010000,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,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000791d00000103000010010000,Wii Classic Controller,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:Linux, +050000000d0f0000f600000001000000,Wireless HORIPAD 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, 030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -712,42 +810,70 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000d102000002010000,Xbox One 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, 050000005e040000fd02000030110000,Xbox One 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, +050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e040000ea02000000000000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e040000ea02000001030000,Xbox One Wireless Controller (Model 1708),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, +030000005e0400008e02000000010000,xbox360 Wireless EasySMX,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, 03000000450c00002043000010010000,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:Linux, 03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,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, 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, +03000000120c0000101e000011010000,ZEROPLUS P4 Wired 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, # Android +05000000c82d000006500000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000051060000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,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:b3,y:b2,platform:Android, +05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000012900000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000062280000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,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:b3,y:b2,platform:Android, +05000000c82d000002600000ffff0f00,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000002028000009000000ffff3f00,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000003512000020ab000000780f00,8BitDo SNES30 Gamepad,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android, +05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform: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, 05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, +0500000031366332860c44aadfff0f00,GS Gamepad,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b3,y:b2,platform:Android, 64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,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, 050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android, 37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000010720000ffff3f00,NVIDIA Controller v01.03,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005509000014720000df7f3f00,NVIDIA Controller v01.04,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +030000004c050000cc09000000006800,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,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, 050000003215000005070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000007070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, 05000000de2800000611000001000000,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:Android, +050000004f0400000ed00000fffe3f00,ThrustMaster eSwap PRO Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, +30306539356238653637313730656134,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, 050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, 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, +050000001727000044310000ffff3f00,XiaoMi Game Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,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/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt index adc3649d64..51ddda1e4e 100644 --- a/core/input/godotcontrollerdb.txt +++ b/core/input/godotcontrollerdb.txt @@ -4,6 +4,9 @@ # Windows __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpdown:b1,dpleft:b2,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Windows, +# Android +Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android, + # Javascript Default HTML5 Gamepad, Default Mapping,leftx:a0,lefty:a1,dpdown:b13,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:b14,lefttrigger:a6,x:b2,dpup:b12,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:b15,righttrigger:a7,b:b1,platform:Javascript, c2a94d6963726f736f66742058626f78,Wireless X360 Controller,leftx:a0,lefty:a1,dpdown:b14,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:b11,lefttrigger:a2,x:b2,dpup:b13,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:b12,righttrigger:a5,b:b1,platform:Javascript, diff --git a/core/input/input.cpp b/core/input/input.cpp index 357e4c06c1..c96884a7b3 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,15 +30,42 @@ #include "input.h" +#include "core/config/project_settings.h" #include "core/input/default_controller_mappings.h" #include "core/input/input_map.h" #include "core/os/os.h" -#include "core/project_settings.h" #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" #endif +static const char *_joy_buttons[JOY_BUTTON_SDL_MAX] = { + "a", + "b", + "x", + "y", + "back", + "guide", + "start", + "leftstick", + "rightstick", + "leftshoulder", + "rightshoulder", + "dpup", + "dpdown", + "dpleft", + "dpright", +}; + +static const char *_joy_axes[JOY_AXIS_SDL_MAX] = { + "leftx", + "lefty", + "rightx", + "righty", + "lefttrigger", + "righttrigger", +}; + Input *Input::singleton = nullptr; void (*Input::set_mouse_mode_func)(Input::MouseMode) = nullptr; @@ -48,7 +75,6 @@ Input::CursorShape (*Input::get_current_cursor_shape_func)() = nullptr; void (*Input::set_custom_mouse_cursor_func)(const RES &, Input::CursorShape, const Vector2 &) = nullptr; Input *Input::get_singleton() { - return singleton; } @@ -58,12 +84,10 @@ void Input::set_mouse_mode(MouseMode p_mode) { } Input::MouseMode Input::get_mouse_mode() const { - return get_mouse_mode_func(); } void Input::_bind_methods() { - ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed); @@ -71,6 +95,9 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed); ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released); ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength); + ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action"), &Input::get_action_strength); + ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis); + ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f)); ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_joy_mapping", "guid"), &Input::remove_joy_mapping); ClassDB::bind_method(D_METHOD("joy_connection_changed", "device", "connected", "name", "guid"), &Input::joy_connection_changed); @@ -81,10 +108,6 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads); ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); - ClassDB::bind_method(D_METHOD("get_joy_button_string", "button_index"), &Input::get_joy_button_string); - ClassDB::bind_method(D_METHOD("get_joy_button_index_from_string", "button"), &Input::get_joy_button_index_from_string); - ClassDB::bind_method(D_METHOD("get_joy_axis_string", "axis_index"), &Input::get_joy_axis_string); - ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string); ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500)); @@ -137,16 +160,18 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; String pf = p_function; - if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength")) { - + if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || + pf == "is_action_just_pressed" || pf == "is_action_just_released" || + pf == "get_action_strength" || pf == "get_axis" || pf == "get_vector")) { List<PropertyInfo> pinfo; ProjectSettings::get_singleton()->get_property_list(&pinfo); for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { const PropertyInfo &pi = E->get(); - if (!pi.name.begins_with("input/")) + if (!pi.name.begins_with("input/")) { continue; + } String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); r_options->push_back(quote_style + name + quote_style); @@ -156,7 +181,6 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S } void Input::SpeedTrack::update(const Vector2 &p_delta_p) { - uint64_t tick = OS::get_singleton()->get_ticks_usec(); uint32_t tdiff = tick - last_tick; float delta_t = tdiff / 1000000.0; @@ -165,11 +189,11 @@ void Input::SpeedTrack::update(const Vector2 &p_delta_p) { accum += p_delta_p; accum_t += delta_t; - if (accum_t > max_ref_frame * 10) + if (accum_t > max_ref_frame * 10) { accum_t = max_ref_frame * 10; + } while (accum_t >= min_ref_frame) { - float slice_t = min_ref_frame / accum_t; Vector2 slice = accum * slice_t; accum = accum - slice; @@ -186,76 +210,110 @@ void Input::SpeedTrack::reset() { } Input::SpeedTrack::SpeedTrack() { - min_ref_frame = 0.1; max_ref_frame = 0.3; reset(); } bool Input::is_key_pressed(int p_keycode) const { - _THREAD_SAFE_METHOD_ return keys_pressed.has(p_keycode); } bool Input::is_mouse_button_pressed(int p_button) const { - _THREAD_SAFE_METHOD_ return (mouse_button_mask & (1 << (p_button - 1))) != 0; } static int _combine_device(int p_value, int p_device) { - return p_value | (p_device << 20); } bool Input::is_joy_button_pressed(int p_device, int p_button) const { - _THREAD_SAFE_METHOD_ return joy_buttons_pressed.has(_combine_device(p_button, p_device)); } bool Input::is_action_pressed(const StringName &p_action) const { - return action_state.has(p_action) && action_state[p_action].pressed; } bool Input::is_action_just_pressed(const StringName &p_action) const { - const Map<StringName, Action>::Element *E = action_state.find(p_action); - if (!E) + if (!E) { return false; + } if (Engine::get_singleton()->is_in_physics_frame()) { return E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { - return E->get().pressed && E->get().idle_frame == Engine::get_singleton()->get_idle_frames(); + return E->get().pressed && E->get().process_frame == Engine::get_singleton()->get_process_frames(); } } bool Input::is_action_just_released(const StringName &p_action) const { - const Map<StringName, Action>::Element *E = action_state.find(p_action); - if (!E) + if (!E) { return false; + } if (Engine::get_singleton()->is_in_physics_frame()) { return !E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { - return !E->get().pressed && E->get().idle_frame == Engine::get_singleton()->get_idle_frames(); + return !E->get().pressed && E->get().process_frame == Engine::get_singleton()->get_process_frames(); } } float Input::get_action_strength(const StringName &p_action) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); - if (!E) + if (!E) { return 0.0f; + } return E->get().strength; } -float Input::get_joy_axis(int p_device, int p_axis) const { +float Input::get_action_raw_strength(const StringName &p_action) const { + const Map<StringName, Action>::Element *E = action_state.find(p_action); + if (!E) { + return 0.0f; + } + + return E->get().raw_strength; +} +float Input::get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const { + return get_action_strength(p_positive_action) - get_action_strength(p_negative_action); +} + +Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone) const { + Vector2 vector = Vector2( + get_action_raw_strength(p_positive_x) - get_action_raw_strength(p_negative_x), + get_action_raw_strength(p_positive_y) - get_action_raw_strength(p_negative_y)); + + if (p_deadzone < 0.0f) { + // If the deadzone isn't specified, get it from the average of the actions. + p_deadzone = (InputMap::get_singleton()->action_get_deadzone(p_positive_x) + + InputMap::get_singleton()->action_get_deadzone(p_negative_x) + + InputMap::get_singleton()->action_get_deadzone(p_positive_y) + + InputMap::get_singleton()->action_get_deadzone(p_negative_y)) / + 4; + } + + // Circular length limiting and deadzone. + float length = vector.length(); + if (length <= p_deadzone) { + return Vector2(); + } else if (length > 1.0f) { + return vector / length; + } else { + // Inverse lerp length to map (p_deadzone, 1) to (0, 1). + return vector * (Math::inverse_lerp(p_deadzone, 1.0f, length) / length); + } + return vector; +} + +float Input::get_joy_axis(int p_device, int p_axis) const { _THREAD_SAFE_METHOD_ int c = _combine_device(p_axis, p_device); if (_joy_axis.has(c)) { @@ -266,10 +324,9 @@ float Input::get_joy_axis(int p_device, int p_axis) const { } String Input::get_joy_name(int p_idx) { - _THREAD_SAFE_METHOD_ return joy_names[p_idx].name; -}; +} Vector2 Input::get_joy_vibration_strength(int p_device) { if (joy_vibration.has(p_device)) { @@ -296,7 +353,6 @@ float Input::get_joy_vibration_duration(int p_device) { } static String _hex_str(uint8_t p_byte) { - static const char *dict = "0123456789abcdef"; char ret[3]; ret[2] = 0; @@ -305,24 +361,22 @@ static String _hex_str(uint8_t p_byte) { ret[1] = dict[p_byte & 0xf]; return ret; -}; +} void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) { - _THREAD_SAFE_METHOD_ Joypad js; js.name = p_connected ? p_name : ""; js.uid = p_connected ? p_guid : ""; if (p_connected) { - String uidname = p_guid; if (p_guid == "") { int uidlen = MIN(p_name.length(), 16); for (int i = 0; i < uidlen; i++) { uidname = uidname + _hex_str(p_name[i]); - }; - }; + } + } js.uid = uidname; js.connected = true; int mapping = fallback_mapping; @@ -330,56 +384,49 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S if (js.uid == map_db[i].uid) { mapping = i; js.name = map_db[i].name; - }; - }; + } + } js.mapping = mapping; } else { js.connected = false; for (int i = 0; i < JOY_BUTTON_MAX; i++) { - - if (i < JOY_AXIS_MAX) - set_joy_axis(p_idx, i, 0.0f); - int c = _combine_device(i, p_idx); joy_buttons_pressed.erase(c); - }; - }; + } + for (int i = 0; i < JOY_AXIS_MAX; i++) { + set_joy_axis(p_idx, i, 0.0f); + } + } joy_names[p_idx] = js; emit_signal("joy_connection_changed", p_idx, p_connected); -}; +} Vector3 Input::get_gravity() const { - _THREAD_SAFE_METHOD_ return gravity; } Vector3 Input::get_accelerometer() const { - _THREAD_SAFE_METHOD_ return accelerometer; } Vector3 Input::get_magnetometer() const { - _THREAD_SAFE_METHOD_ return magnetometer; } Vector3 Input::get_gyroscope() const { - _THREAD_SAFE_METHOD_ return gyroscope; } void Input::parse_input_event(const Ref<InputEvent> &p_event) { - _parse_input_event_impl(p_event, false); } void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) { - // Notes on mouse-touch emulation: // - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects // as true mouse events. The only difference is the situation is flagged as emulated so they are not @@ -391,16 +438,16 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em Ref<InputEventKey> k = p_event; if (k.is_valid() && !k->is_echo() && k->get_keycode() != 0) { - if (k->is_pressed()) + if (k->is_pressed()) { keys_pressed.insert(k->get_keycode()); - else + } else { keys_pressed.erase(k->get_keycode()); + } } Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->is_pressed()) { mouse_button_mask |= (1 << (mb->get_button_index() - 1)); } else { @@ -424,7 +471,6 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - Point2 pos = mm->get_global_position(); if (mouse_pos != pos) { set_mouse_position(pos); @@ -445,7 +491,6 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em Ref<InputEventScreenTouch> st = p_event; if (st.is_valid()) { - if (st->is_pressed()) { SpeedTrack &track = touch_speed_track[st->get_index()]; track.reset(); @@ -456,7 +501,6 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em } if (emulate_mouse_from_touch) { - bool translate = false; if (st->is_pressed()) { if (mouse_from_touch_index == -1) { @@ -493,13 +537,11 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em Ref<InputEventScreenDrag> sd = p_event; if (sd.is_valid()) { - SpeedTrack &track = touch_speed_track[sd->get_index()]; track.update(sd->get_relative()); sd->set_speed(track.speed); if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) { - Ref<InputEventMouseMotion> motion_event; motion_event.instance(); @@ -517,13 +559,13 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em Ref<InputEventJoypadButton> jb = p_event; if (jb.is_valid()) { - int c = _combine_device(jb->get_button_index(), jb->get_device()); - if (jb->is_pressed()) + if (jb->is_pressed()) { joy_buttons_pressed.insert(c); - else + } else { joy_buttons_pressed.erase(c); + } } Ref<InputEventJoypadMotion> jm = p_event; @@ -535,7 +577,6 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em Ref<InputEventGesture> ge = p_event; if (ge.is_valid()) { - if (event_dispatch_function) { event_dispatch_function(ge); } @@ -543,26 +584,27 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em for (const Map<StringName, InputMap::Action>::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) { if (InputMap::get_singleton()->event_is_action(p_event, E->key())) { - // Save the action's state if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) { Action action; action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.idle_frame = Engine::get_singleton()->get_idle_frames(); + action.process_frame = Engine::get_singleton()->get_process_frames(); action.pressed = p_event->is_action_pressed(E->key()); - action.strength = 0.f; + action.strength = 0.0f; + action.raw_strength = 0.0f; action_state[E->key()] = action; } action_state[E->key()].strength = p_event->get_action_strength(E->key()); + action_state[E->key()].raw_strength = p_event->get_action_raw_strength(E->key()); } } - if (event_dispatch_function) + if (event_dispatch_function) { event_dispatch_function(p_event); + } } void Input::set_joy_axis(int p_device, int p_axis, float p_value) { - _THREAD_SAFE_METHOD_ int c = _combine_device(p_axis, p_device); _joy_axis[c] = p_value; @@ -596,50 +638,43 @@ void Input::vibrate_handheld(int p_duration_ms) { } void Input::set_gravity(const Vector3 &p_gravity) { - _THREAD_SAFE_METHOD_ gravity = p_gravity; } void Input::set_accelerometer(const Vector3 &p_accel) { - _THREAD_SAFE_METHOD_ accelerometer = p_accel; } void Input::set_magnetometer(const Vector3 &p_magnetometer) { - _THREAD_SAFE_METHOD_ magnetometer = p_magnetometer; } void Input::set_gyroscope(const Vector3 &p_gyroscope) { - _THREAD_SAFE_METHOD_ gyroscope = p_gyroscope; } void Input::set_mouse_position(const Point2 &p_posf) { - mouse_speed_track.update(p_posf - mouse_pos); mouse_pos = p_posf; } Point2 Input::get_mouse_position() const { - return mouse_pos; } -Point2 Input::get_last_mouse_speed() const { +Point2 Input::get_last_mouse_speed() const { return mouse_speed_track.speed; } int Input::get_mouse_button_mask() const { - return mouse_button_mask; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state(); } @@ -648,7 +683,6 @@ void Input::warp_mouse_position(const Vector2 &p_to) { } Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect) { - // The relative distance reported for the next event after a warp is in the boundaries of the // size of the rect on that axis, but it may be greater, in which case there's not problem as fmod() // will warp it, but if the pointer has moved in the opposite direction between the pointer relocation @@ -677,11 +711,10 @@ void Input::iteration(float p_step) { } void Input::action_press(const StringName &p_action, float p_strength) { - Action action; action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.idle_frame = Engine::get_singleton()->get_idle_frames(); + action.process_frame = Engine::get_singleton()->get_process_frames(); action.pressed = true; action.strength = p_strength; @@ -689,11 +722,10 @@ void Input::action_press(const StringName &p_action, float p_strength) { } void Input::action_release(const StringName &p_action) { - Action action; action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.idle_frame = Engine::get_singleton()->get_idle_frames(); + action.process_frame = Engine::get_singleton()->get_process_frames(); action.pressed = false; action.strength = 0.f; @@ -701,19 +733,16 @@ void Input::action_release(const StringName &p_action) { } void Input::set_emulate_touch_from_mouse(bool p_emulate) { - emulate_touch_from_mouse = p_emulate; } bool Input::is_emulating_touch_from_mouse() const { - return emulate_touch_from_mouse; } // Calling this whenever the game window is focused helps unstucking the "touch mouse" // if the OS or its abstraction class hasn't properly reported that touch pointers raised void Input::ensure_touch_mouse_raised() { - if (mouse_from_touch_index != -1) { mouse_from_touch_index = -1; @@ -732,24 +761,21 @@ void Input::ensure_touch_mouse_raised() { } void Input::set_emulate_mouse_from_touch(bool p_emulate) { - emulate_mouse_from_touch = p_emulate; } bool Input::is_emulating_mouse_from_touch() const { - return emulate_mouse_from_touch; } Input::CursorShape Input::get_default_cursor_shape() const { - return default_shape; } void Input::set_default_cursor_shape(CursorShape p_shape) { - - if (default_shape == p_shape) + if (default_shape == p_shape) { return; + } default_shape = p_shape; // The default shape is set in Viewport::_gui_input_event. To instantly @@ -762,13 +788,13 @@ void Input::set_default_cursor_shape(CursorShape p_shape) { } Input::CursorShape Input::get_current_cursor_shape() const { - return get_current_cursor_shape_func(); } void Input::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { - if (Engine::get_singleton()->is_editor_hint()) + if (Engine::get_singleton()->is_editor_hint()) { return; + } set_custom_mouse_cursor_func(p_cursor, p_shape, p_hotspot); } @@ -780,14 +806,14 @@ void Input::accumulate_input_event(const Ref<InputEvent> &p_event) { parse_input_event(p_event); return; } - if (!accumulated_events.empty() && accumulated_events.back()->get()->accumulate(p_event)) { + if (!accumulated_events.is_empty() && accumulated_events.back()->get()->accumulate(p_event)) { return; //event was accumulated, exit } accumulated_events.push_back(p_event); } -void Input::flush_accumulated_events() { +void Input::flush_accumulated_events() { while (accumulated_events.front()) { parse_input_event(accumulated_events.front()->get()); accumulated_events.pop_front(); @@ -795,12 +821,10 @@ void Input::flush_accumulated_events() { } void Input::set_use_accumulated_input(bool p_enable) { - use_accumulated_input = p_enable; } void Input::release_pressed_events() { - flush_accumulated_events(); // this is needed to release actions strengths keys_pressed.clear(); @@ -808,8 +832,9 @@ void Input::release_pressed_events() { _joy_axis.clear(); for (Map<StringName, Input::Action>::Element *E = action_state.front(); E; E = E->next()) { - if (E->get().pressed) + if (E->get().pressed) { action_release(E->key()); + } } } @@ -818,7 +843,6 @@ void Input::set_event_dispatch_function(EventDispatchFunc p_function) { } void Input::joy_button(int p_device, int p_button, bool p_pressed) { - _THREAD_SAFE_METHOD_; Joypad &joy = joy_names[p_device]; //printf("got button %i, mapping is %i\n", p_button, joy.mapping); @@ -831,33 +855,20 @@ void Input::joy_button(int p_device, int p_button, bool p_pressed) { return; } - const Map<int, JoyEvent>::Element *el = map_db[joy.mapping].buttons.find(p_button); - if (!el) { - //don't process un-mapped events for now, it could mess things up badly for devices with additional buttons/axis - //return _button_event(p_last_id, p_device, p_button, p_pressed); - return; - } + JoyEvent map = _get_mapped_button_event(map_db[joy.mapping], p_button); - JoyEvent map = el->get(); if (map.type == TYPE_BUTTON) { - //fake additional axis event for triggers - if (map.index == JOY_L2 || map.index == JOY_R2) { - float value = p_pressed ? 1.0f : 0.0f; - int axis = map.index == JOY_L2 ? JOY_ANALOG_L2 : JOY_ANALOG_R2; - _axis_event(p_device, axis, value); - } _button_event(p_device, map.index, p_pressed); return; } if (map.type == TYPE_AXIS) { - _axis_event(p_device, map.index, p_pressed ? 1.0 : 0.0); + _axis_event(p_device, map.index, p_pressed ? map.value : 0.0); } // no event? } void Input::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { - _THREAD_SAFE_METHOD_; ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX); @@ -868,17 +879,6 @@ void Input::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { return; } - if (p_value.value > joy.last_axis[p_axis]) { - - if (p_value.value < joy.last_axis[p_axis] + joy.filter) { - - return; - } - } else if (p_value.value > joy.last_axis[p_axis] - joy.filter) { - - return; - } - //when changing direction quickly, insert fake event to release pending inputmap actions float last = joy.last_axis[p_axis]; if (p_value.min == 0 && (last < 0.25 || last > 0.75) && (last - 0.5) * (p_value.value - 0.5) < 0) { @@ -899,112 +899,97 @@ void Input::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { if (joy.mapping == -1) { _axis_event(p_device, p_axis, val); return; - }; - - const Map<int, JoyEvent>::Element *el = map_db[joy.mapping].axis.find(p_axis); - if (!el) { - //return _axis_event(p_last_id, p_device, p_axis, p_value); - return; - }; + } - JoyEvent map = el->get(); + JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, val); if (map.type == TYPE_BUTTON) { - //send axis event for triggers - if (map.index == JOY_L2 || map.index == JOY_R2) { - float value = p_value.min == 0 ? p_value.value : 0.5f + p_value.value / 2.0f; - int axis = map.index == JOY_L2 ? JOY_ANALOG_L2 : JOY_ANALOG_R2; - _axis_event(p_device, axis, value); + bool pressed = map.value > 0.5; + if (pressed == joy_buttons_pressed.has(_combine_device(map.index, p_device))) { + // Button already pressed or released; so ignore. + return; } + _button_event(p_device, map.index, pressed); - if (map.index == JOY_DPAD_UP || map.index == JOY_DPAD_DOWN) { - bool pressed = p_value.value != 0.0f; - int button = p_value.value < 0 ? JOY_DPAD_UP : JOY_DPAD_DOWN; - - if (!pressed) { - if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_UP, p_device))) { - _button_event(p_device, JOY_DPAD_UP, false); + // Ensure opposite D-Pad button is also released. + switch (map.index) { + case JOY_BUTTON_DPAD_UP: + if (joy_buttons_pressed.has(_combine_device(JOY_BUTTON_DPAD_DOWN, p_device))) { + _button_event(p_device, JOY_BUTTON_DPAD_DOWN, false); } - if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_DOWN, p_device))) { - _button_event(p_device, JOY_DPAD_DOWN, false); + break; + case JOY_BUTTON_DPAD_DOWN: + if (joy_buttons_pressed.has(_combine_device(JOY_BUTTON_DPAD_UP, p_device))) { + _button_event(p_device, JOY_BUTTON_DPAD_UP, false); } - } - if (pressed == joy_buttons_pressed.has(_combine_device(button, p_device))) { - return; - } - _button_event(p_device, button, true); - return; - } - if (map.index == JOY_DPAD_LEFT || map.index == JOY_DPAD_RIGHT) { - bool pressed = p_value.value != 0.0f; - int button = p_value.value < 0 ? JOY_DPAD_LEFT : JOY_DPAD_RIGHT; - - if (!pressed) { - if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_LEFT, p_device))) { - _button_event(p_device, JOY_DPAD_LEFT, false); + break; + case JOY_BUTTON_DPAD_LEFT: + if (joy_buttons_pressed.has(_combine_device(JOY_BUTTON_DPAD_RIGHT, p_device))) { + _button_event(p_device, JOY_BUTTON_DPAD_RIGHT, false); } - if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_RIGHT, p_device))) { - _button_event(p_device, JOY_DPAD_RIGHT, false); + break; + case JOY_BUTTON_DPAD_RIGHT: + if (joy_buttons_pressed.has(_combine_device(JOY_BUTTON_DPAD_LEFT, p_device))) { + _button_event(p_device, JOY_BUTTON_DPAD_LEFT, false); } - } - if (pressed == joy_buttons_pressed.has(_combine_device(button, p_device))) { - return; - } - _button_event(p_device, button, true); - return; + break; + default: + // Nothing to do. + break; } - float deadzone = p_value.min == 0 ? 0.5f : 0.0f; - bool pressed = p_value.value > deadzone; - if (pressed == joy_buttons_pressed.has(_combine_device(map.index, p_device))) { - // button already pressed or released, this is an axis bounce value - return; - } - _button_event(p_device, map.index, pressed); return; } if (map.type == TYPE_AXIS) { - - _axis_event(p_device, map.index, val); + _axis_event(p_device, map.index, map.value); return; } //printf("invalid mapping\n"); } void Input::joy_hat(int p_device, int p_val) { - _THREAD_SAFE_METHOD_; const Joypad &joy = joy_names[p_device]; - const JoyEvent *map; + JoyEvent map[HAT_MAX]; - if (joy.mapping == -1) { - map = hat_map_default; - } else { - map = map_db[joy.mapping].hat; - }; + map[HAT_UP].type = TYPE_BUTTON; + map[HAT_UP].index = JOY_BUTTON_DPAD_UP; + map[HAT_UP].value = 0; - int cur_val = joy_names[p_device].hat_current; + map[HAT_RIGHT].type = TYPE_BUTTON; + map[HAT_RIGHT].index = JOY_BUTTON_DPAD_RIGHT; + map[HAT_RIGHT].value = 0; - if ((p_val & HAT_MASK_UP) != (cur_val & HAT_MASK_UP)) { - _button_event(p_device, map[HAT_UP].index, p_val & HAT_MASK_UP); - } + map[HAT_DOWN].type = TYPE_BUTTON; + map[HAT_DOWN].index = JOY_BUTTON_DPAD_DOWN; + map[HAT_DOWN].value = 0; - if ((p_val & HAT_MASK_RIGHT) != (cur_val & HAT_MASK_RIGHT)) { - _button_event(p_device, map[HAT_RIGHT].index, p_val & HAT_MASK_RIGHT); - } - if ((p_val & HAT_MASK_DOWN) != (cur_val & HAT_MASK_DOWN)) { - _button_event(p_device, map[HAT_DOWN].index, p_val & HAT_MASK_DOWN); + map[HAT_LEFT].type = TYPE_BUTTON; + map[HAT_LEFT].index = JOY_BUTTON_DPAD_LEFT; + map[HAT_LEFT].value = 0; + + if (joy.mapping != -1) { + _get_mapped_hat_events(map_db[joy.mapping], 0, map); } - if ((p_val & HAT_MASK_LEFT) != (cur_val & HAT_MASK_LEFT)) { - _button_event(p_device, map[HAT_LEFT].index, p_val & HAT_MASK_LEFT); + + int cur_val = joy_names[p_device].hat_current; + + for (int hat_direction = 0, hat_mask = 1; hat_direction < HAT_MAX; hat_direction++, hat_mask <<= 1) { + if ((p_val & hat_mask) != (cur_val & hat_mask)) { + if (map[hat_direction].type == TYPE_BUTTON) { + _button_event(p_device, map[hat_direction].index, p_val & hat_mask); + } + if (map[hat_direction].type == TYPE_AXIS) { + _axis_event(p_device, map[hat_direction].index, (p_val & hat_mask) ? map[hat_direction].value : 0.0); + } + } } joy_names[p_device].hat_current = p_val; } void Input::_button_event(int p_device, int p_index, bool p_pressed) { - Ref<InputEventJoypadButton> ievent; ievent.instance(); ievent->set_device(p_device); @@ -1015,7 +1000,6 @@ void Input::_button_event(int p_device, int p_index, bool p_pressed) { } void Input::_axis_event(int p_device, int p_axis, float p_value) { - Ref<InputEventJoypadMotion> ievent; ievent.instance(); ievent->set_device(p_device); @@ -1023,52 +1007,187 @@ void Input::_axis_event(int p_device, int p_axis, float p_value) { ievent->set_axis_value(p_value); parse_input_event(ievent); -}; - -Input::JoyEvent Input::_find_to_event(String p_to) { - - // string names of the SDL buttons in the same order as input_event.h godot buttons - static const char *buttons[] = { "a", "b", "x", "y", "leftshoulder", "rightshoulder", "lefttrigger", "righttrigger", "leftstick", "rightstick", "back", "start", "dpup", "dpdown", "dpleft", "dpright", "guide", nullptr }; +} - static const char *axis[] = { "leftx", "lefty", "rightx", "righty", nullptr }; +Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, int p_button) { + JoyEvent event; + event.type = TYPE_MAX; + + for (int i = 0; i < mapping.bindings.size(); i++) { + const JoyBinding binding = mapping.bindings[i]; + if (binding.inputType == TYPE_BUTTON && binding.input.button == p_button) { + event.type = binding.outputType; + switch (binding.outputType) { + case TYPE_BUTTON: + event.index = binding.output.button; + return event; + case TYPE_AXIS: + event.index = binding.output.axis.axis; + switch (binding.output.axis.range) { + case POSITIVE_HALF_AXIS: + event.value = 1; + break; + case NEGATIVE_HALF_AXIS: + event.value = -1; + break; + case FULL_AXIS: + // It doesn't make sense for a button to map to a full axis, + // but keeping as a default for a trigger with a positive half-axis. + event.value = 1; + break; + } + return event; + default: + ERR_PRINT_ONCE("Joypad button mapping error."); + } + } + } + return event; +} - JoyEvent ret; - ret.type = -1; - ret.index = 0; +Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, int p_axis, float p_value) { + JoyEvent event; + event.type = TYPE_MAX; - int i = 0; - while (buttons[i]) { + for (int i = 0; i < mapping.bindings.size(); i++) { + const JoyBinding binding = mapping.bindings[i]; + if (binding.inputType == TYPE_AXIS && binding.input.axis.axis == p_axis) { + float value = p_value; + if (binding.input.axis.invert) { + value = -value; + } + if (binding.input.axis.range == FULL_AXIS || + (binding.input.axis.range == POSITIVE_HALF_AXIS && value > 0) || + (binding.input.axis.range == NEGATIVE_HALF_AXIS && value < 0)) { + event.type = binding.outputType; + float shifted_positive_value = 0; + switch (binding.input.axis.range) { + case POSITIVE_HALF_AXIS: + shifted_positive_value = value; + break; + case NEGATIVE_HALF_AXIS: + shifted_positive_value = value + 1; + break; + case FULL_AXIS: + shifted_positive_value = (value + 1) / 2; + break; + } + switch (binding.outputType) { + case TYPE_BUTTON: + event.index = binding.output.button; + switch (binding.input.axis.range) { + case POSITIVE_HALF_AXIS: + event.value = shifted_positive_value; + break; + case NEGATIVE_HALF_AXIS: + event.value = 1 - shifted_positive_value; + break; + case FULL_AXIS: + // It doesn't make sense for a full axis to map to a button, + // but keeping as a default for a trigger with a positive half-axis. + event.value = (shifted_positive_value * 2) - 1; + ; + break; + } + return event; + case TYPE_AXIS: + event.index = binding.output.axis.axis; + event.value = value; + if (binding.output.axis.range != binding.input.axis.range) { + switch (binding.output.axis.range) { + case POSITIVE_HALF_AXIS: + event.value = shifted_positive_value; + break; + case NEGATIVE_HALF_AXIS: + event.value = shifted_positive_value - 1; + break; + case FULL_AXIS: + event.value = (shifted_positive_value * 2) - 1; + break; + } + } + return event; + default: + ERR_PRINT_ONCE("Joypad axis mapping error."); + } + } + } + } + return event; +} - if (p_to == buttons[i]) { - ret.type = TYPE_BUTTON; - ret.index = i; - ret.value = 0; - return ret; - }; - ++i; - }; +void Input::_get_mapped_hat_events(const JoyDeviceMapping &mapping, int p_hat, JoyEvent r_events[]) { + for (int i = 0; i < mapping.bindings.size(); i++) { + const JoyBinding binding = mapping.bindings[i]; + if (binding.inputType == TYPE_HAT && binding.input.hat.hat == p_hat) { + int hat_direction; + switch (binding.input.hat.hat_mask) { + case HAT_MASK_UP: + hat_direction = HAT_UP; + break; + case HAT_MASK_RIGHT: + hat_direction = HAT_RIGHT; + break; + case HAT_MASK_DOWN: + hat_direction = HAT_DOWN; + break; + case HAT_MASK_LEFT: + hat_direction = HAT_LEFT; + break; + default: + ERR_PRINT_ONCE("Joypad button mapping error."); + continue; + } - i = 0; - while (axis[i]) { + r_events[hat_direction].type = binding.outputType; + switch (binding.outputType) { + case TYPE_BUTTON: + r_events[hat_direction].index = binding.output.button; + break; + case TYPE_AXIS: + r_events[hat_direction].index = binding.output.axis.axis; + switch (binding.output.axis.range) { + case POSITIVE_HALF_AXIS: + r_events[hat_direction].value = 1; + break; + case NEGATIVE_HALF_AXIS: + r_events[hat_direction].value = -1; + break; + case FULL_AXIS: + // It doesn't make sense for a hat direction to map to a full axis, + // but keeping as a default for a trigger with a positive half-axis. + r_events[hat_direction].value = 1; + break; + } + break; + default: + ERR_PRINT_ONCE("Joypad button mapping error."); + } + } + } +} - if (p_to == axis[i]) { - ret.type = TYPE_AXIS; - ret.index = i; - ret.value = 0; - return ret; - }; - ++i; - }; +JoyButtonList Input::_get_output_button(String output) { + for (int i = 0; i < JOY_BUTTON_SDL_MAX; i++) { + if (output == _joy_buttons[i]) { + return JoyButtonList(i); + } + } + return JoyButtonList::JOY_BUTTON_INVALID; +} - return ret; -}; +JoyAxisList Input::_get_output_axis(String output) { + for (int i = 0; i < JOY_AXIS_SDL_MAX; i++) { + if (output == _joy_axes[i]) { + return JoyAxisList(i); + } + } + return JoyAxisList::JOY_AXIS_INVALID; +} void Input::parse_mapping(String p_mapping) { - _THREAD_SAFE_METHOD_; JoyDeviceMapping mapping; - for (int i = 0; i < HAT_MAX; ++i) - mapping.hat[i].index = 1024 + i; Vector<String> entry = p_mapping.split(","); if (entry.size() < 2) { @@ -1083,50 +1202,88 @@ void Input::parse_mapping(String p_mapping) { int idx = 1; while (++idx < entry.size()) { - - if (entry[idx] == "") + if (entry[idx] == "") { continue; + } - String from = entry[idx].get_slice(":", 1).replace(" ", ""); - String to = entry[idx].get_slice(":", 0).replace(" ", ""); + String output = entry[idx].get_slice(":", 0).replace(" ", ""); + String input = entry[idx].get_slice(":", 1).replace(" ", ""); + ERR_CONTINUE_MSG(output.length() < 1 || input.length() < 2, + String(entry[idx] + "\nInvalid device mapping entry: " + entry[idx])); - JoyEvent to_event = _find_to_event(to); - if (to_event.type == -1) + if (output == "platform" || output == "hint") { continue; + } - String etype = from.substr(0, 1); - if (etype == "a") { + JoyAxisRange output_range = FULL_AXIS; + if (output[0] == '+' || output[0] == '-') { + ERR_CONTINUE_MSG(output.length() < 2, String(entry[idx] + "\nInvalid output: " + entry[idx])); + if (output[0] == '+') { + output_range = POSITIVE_HALF_AXIS; + } else if (output[0] == '-') { + output_range = NEGATIVE_HALF_AXIS; + } + output = output.right(1); + } - int aid = from.substr(1, from.length() - 1).to_int(); - mapping.axis[aid] = to_event; + JoyAxisRange input_range = FULL_AXIS; + if (input[0] == '+') { + input_range = POSITIVE_HALF_AXIS; + input = input.right(1); + } else if (input[0] == '-') { + input_range = NEGATIVE_HALF_AXIS; + input = input.right(1); + } + bool invert_axis = false; + if (input[input.length() - 1] == '~') { + invert_axis = true; + input = input.left(input.length() - 1); + } - } else if (etype == "b") { + JoyButtonList output_button = _get_output_button(output); + JoyAxisList output_axis = _get_output_axis(output); + ERR_CONTINUE_MSG(output_button == JOY_BUTTON_INVALID && output_axis == JOY_AXIS_INVALID, + String(entry[idx] + "\nUnrecognised output string: " + output)); + ERR_CONTINUE_MSG(output_button != JOY_BUTTON_INVALID && output_axis != JOY_AXIS_INVALID, + String("BUG: Output string matched both button and axis: " + output)); + + JoyBinding binding; + if (output_button != JOY_BUTTON_INVALID) { + binding.outputType = TYPE_BUTTON; + binding.output.button = output_button; + } else if (output_axis != JOY_AXIS_INVALID) { + binding.outputType = TYPE_AXIS; + binding.output.axis.axis = output_axis; + binding.output.axis.range = output_range; + } - int bid = from.substr(1, from.length() - 1).to_int(); - mapping.buttons[bid] = to_event; + switch (input[0]) { + case 'b': + binding.inputType = TYPE_BUTTON; + binding.input.button = input.right(1).to_int(); + break; + case 'a': + binding.inputType = TYPE_AXIS; + binding.input.axis.axis = input.right(1).to_int(); + binding.input.axis.range = input_range; + binding.input.axis.invert = invert_axis; + break; + case 'h': + ERR_CONTINUE_MSG(input.length() != 4 || input[2] != '.', + String(entry[idx] + "\nInvalid hat input: " + input)); + binding.inputType = TYPE_HAT; + binding.input.hat.hat = input.substr(1, 1).to_int(); + binding.input.hat.hat_mask = static_cast<HatMask>(input.right(3).to_int()); + break; + default: + ERR_CONTINUE_MSG(true, String(entry[idx] + "\nUnrecognised input string: " + input)); + } - } else if (etype == "h") { + mapping.bindings.push_back(binding); + } - int hat_value = from.get_slice(".", 1).to_int(); - switch (hat_value) { - case 1: - mapping.hat[HAT_UP] = to_event; - break; - case 2: - mapping.hat[HAT_RIGHT] = to_event; - break; - case 4: - mapping.hat[HAT_DOWN] = to_event; - break; - case 8: - mapping.hat[HAT_LEFT] = to_event; - break; - }; - }; - }; map_db.push_back(mapping); - //printf("added mapping with uuid %ls\n", mapping.uid.c_str()); -}; +} void Input::add_joy_mapping(String p_mapping, bool p_update_existing) { parse_mapping(p_mapping); @@ -1155,7 +1312,6 @@ void Input::remove_joy_mapping(String p_guid) { } void Input::set_fallback_mapping(String p_guid) { - for (int i = 0; i < map_db.size(); i++) { if (map_db[i].uid == p_guid) { fallback_mapping = i; @@ -1187,52 +1343,6 @@ Array Input::get_connected_joypads() { return ret; } -static const char *_buttons[JOY_BUTTON_MAX] = { - "Face Button Bottom", - "Face Button Right", - "Face Button Left", - "Face Button Top", - "L", - "R", - "L2", - "R2", - "L3", - "R3", - "Select", - "Start", - "DPAD Up", - "DPAD Down", - "DPAD Left", - "DPAD Right" -}; - -static const char *_axes[JOY_AXIS_MAX] = { - "Left Stick X", - "Left Stick Y", - "Right Stick X", - "Right Stick Y", - "", - "", - "L2", - "R2", - "", - "" -}; - -String Input::get_joy_button_string(int p_button) { - ERR_FAIL_INDEX_V(p_button, JOY_BUTTON_MAX, ""); - return _buttons[p_button]; -} - -int Input::get_joy_button_index_from_string(String p_button) { - for (int i = 0; i < JOY_BUTTON_MAX; i++) { - if (p_button == _buttons[i]) { - return i; - } - } - ERR_FAIL_V(-1); -} - int Input::get_unused_joy_id() { for (int i = 0; i < JOYPADS_MAX; i++) { if (!joy_names.has(i) || !joy_names[i].connected) { @@ -1242,49 +1352,8 @@ int Input::get_unused_joy_id() { return -1; } -String Input::get_joy_axis_string(int p_axis) { - ERR_FAIL_INDEX_V(p_axis, JOY_AXIS_MAX, ""); - return _axes[p_axis]; -} - -int Input::get_joy_axis_index_from_string(String p_axis) { - for (int i = 0; i < JOY_AXIS_MAX; i++) { - if (p_axis == _axes[i]) { - return i; - } - } - ERR_FAIL_V(-1); -} - Input::Input() { - singleton = this; - use_accumulated_input = true; - mouse_button_mask = 0; - mouse_window = 0; - emulate_touch_from_mouse = false; - emulate_mouse_from_touch = false; - mouse_from_touch_index = -1; - event_dispatch_function = nullptr; - default_shape = CURSOR_ARROW; - - hat_map_default[HAT_UP].type = TYPE_BUTTON; - hat_map_default[HAT_UP].index = JOY_DPAD_UP; - hat_map_default[HAT_UP].value = 0; - - hat_map_default[HAT_RIGHT].type = TYPE_BUTTON; - hat_map_default[HAT_RIGHT].index = JOY_DPAD_RIGHT; - hat_map_default[HAT_RIGHT].value = 0; - - hat_map_default[HAT_DOWN].type = TYPE_BUTTON; - hat_map_default[HAT_DOWN].index = JOY_DPAD_DOWN; - hat_map_default[HAT_DOWN].value = 0; - - hat_map_default[HAT_LEFT].type = TYPE_BUTTON; - hat_map_default[HAT_LEFT].index = JOY_DPAD_LEFT; - hat_map_default[HAT_LEFT].value = 0; - - fallback_mapping = -1; // Parse default mappings. { @@ -1299,8 +1368,9 @@ Input::Input() { if (env_mapping != "") { Vector<String> entries = env_mapping.split("\n"); for (int i = 0; i < entries.size(); i++) { - if (entries[i] == "") + if (entries[i] == "") { continue; + } parse_mapping(entries[i]); } } diff --git a/core/input/input.h b/core/input/input.h index 2e136dbf02..445b7ff0cf 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,11 +32,10 @@ #define INPUT_H #include "core/input/input_event.h" -#include "core/object.h" +#include "core/object/object.h" #include "core/os/thread_safe.h" class Input : public Object { - GDCLASS(Input, Object); _THREAD_SAFE_CLASS_ @@ -100,7 +99,7 @@ public: typedef void (*EventDispatchFunc)(const Ref<InputEvent> &p_event); private: - int mouse_button_mask; + int mouse_button_mask = 0; Set<int> keys_pressed; Set<int> joy_buttons_pressed; @@ -111,24 +110,25 @@ private: Vector3 magnetometer; Vector3 gyroscope; Vector2 mouse_pos; - int64_t mouse_window; + int64_t mouse_window = 0; struct Action { uint64_t physics_frame; - uint64_t idle_frame; + uint64_t process_frame; bool pressed; float strength; + float raw_strength; }; Map<StringName, Action> action_state; - bool emulate_touch_from_mouse; - bool emulate_mouse_from_touch; + bool emulate_touch_from_mouse = false; + bool emulate_mouse_from_touch = false; + bool use_accumulated_input = false; - int mouse_from_touch_index; + int mouse_from_touch_index = -1; struct SpeedTrack { - uint64_t last_tick; Vector2 speed; Vector2 accum; @@ -144,37 +144,20 @@ private: struct Joypad { StringName name; StringName uid; - bool connected; - bool last_buttons[JOY_BUTTON_MAX + 19]; //apparently SDL specifies 35 possible buttons on android - float last_axis[JOY_AXIS_MAX]; - float filter; - int last_hat; - int mapping; - int hat_current; - - Joypad() { - for (int i = 0; i < JOY_AXIS_MAX; i++) { - - last_axis[i] = 0.0f; - } - for (int i = 0; i < JOY_BUTTON_MAX + 19; i++) { - - last_buttons[i] = false; - } - connected = false; - last_hat = HAT_MASK_CENTER; - filter = 0.01f; - mapping = -1; - hat_current = 0; - } + bool connected = false; + bool last_buttons[JOY_BUTTON_MAX] = { false }; + float last_axis[JOY_AXIS_MAX] = { 0.0f }; + int last_hat = HAT_MASK_CENTER; + int mapping = -1; + int hat_current = 0; }; SpeedTrack mouse_speed_track; Map<int, SpeedTrack> touch_speed_track; Map<int, Joypad> joy_names; - int fallback_mapping; + int fallback_mapping = -1; - CursorShape default_shape; + CursorShape default_shape = CURSOR_ARROW; enum JoyType { TYPE_BUTTON, @@ -183,34 +166,68 @@ private: TYPE_MAX, }; + enum JoyAxisRange { + NEGATIVE_HALF_AXIS = -1, + FULL_AXIS = 0, + POSITIVE_HALF_AXIS = 1 + }; + struct JoyEvent { int type; int index; - int value; + float value; }; - struct JoyDeviceMapping { + struct JoyBinding { + JoyType inputType; + union { + int button; + + struct { + int axis; + JoyAxisRange range; + bool invert; + } axis; + + struct { + int hat; + HatMask hat_mask; + } hat; + + } input; + JoyType outputType; + union { + JoyButtonList button; + + struct { + JoyAxisList axis; + JoyAxisRange range; + } axis; + + } output; + }; + + struct JoyDeviceMapping { String uid; String name; - Map<int, JoyEvent> buttons; - Map<int, JoyEvent> axis; - JoyEvent hat[HAT_MAX]; + Vector<JoyBinding> bindings; }; - JoyEvent hat_map_default[HAT_MAX]; - Vector<JoyDeviceMapping> map_db; - JoyEvent _find_to_event(String p_to); + JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, int p_button); + JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, int p_axis, float p_value); + void _get_mapped_hat_events(const JoyDeviceMapping &mapping, int p_hat, JoyEvent r_events[HAT_MAX]); + JoyButtonList _get_output_button(String output); + JoyAxisList _get_output_axis(String output); void _button_event(int p_device, int p_index, bool p_pressed); void _axis_event(int p_device, int p_axis, float p_value); - float _handle_deadzone(int p_device, int p_axis, float p_value); void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated); List<Ref<InputEvent>> accumulated_events; - bool use_accumulated_input; + friend class DisplayServer; static void (*set_mouse_mode_func)(MouseMode); @@ -220,7 +237,7 @@ private: static CursorShape (*get_current_cursor_shape_func)(); static void (*set_custom_mouse_cursor_func)(const RES &, CursorShape, const Vector2 &); - EventDispatchFunc event_dispatch_function; + EventDispatchFunc event_dispatch_function = nullptr; protected: struct VibrationInfo { @@ -237,7 +254,7 @@ protected: public: void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; - void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; + void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; static Input *get_singleton(); @@ -248,6 +265,10 @@ public: bool is_action_just_pressed(const StringName &p_action) const; bool is_action_just_released(const StringName &p_action) const; float get_action_strength(const StringName &p_action) const; + float get_action_raw_strength(const StringName &p_action) const; + + float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const; + Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const; float get_joy_axis(int p_device, int p_axis) const; String get_joy_name(int p_idx); @@ -309,11 +330,6 @@ public: void add_joy_mapping(String p_mapping, bool p_update_existing = false); void remove_joy_mapping(String p_guid); - String get_joy_button_string(int p_button); - String get_joy_axis_string(int p_axis); - int get_joy_axis_index_from_string(String p_axis); - int get_joy_button_index_from_string(String p_button); - int get_unused_joy_id(); bool is_joy_known(int p_device); diff --git a/core/input/input_builders.py b/core/input/input_builders.py index 53b90f2073..748ec06133 100644 --- a/core/input/input_builders.py +++ b/core/input/input_builders.py @@ -42,18 +42,7 @@ def make_default_controller_mappings(target, source, env): src_path, current_platform, platform_mappings[current_platform][guid] ) ) - valid_mapping = True - for input_map in line_parts[2:]: - if "+" in input_map or "-" in input_map or "~" in input_map: - g.write( - "// WARNING - DISCARDED UNSUPPORTED MAPPING TYPE FROM DATABASE {}: {} {}\n".format( - src_path, current_platform, line - ) - ) - valid_mapping = False - break - if valid_mapping: - platform_mappings[current_platform][guid] = line + platform_mappings[current_platform][guid] = line platform_variables = { "Linux": "#if X11_ENABLED", diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 4b8c104f39..2771a15b80 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -45,69 +45,58 @@ int InputEvent::get_device() const { } bool InputEvent::is_action(const StringName &p_action) const { - return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action); } bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo) const { - bool pressed; bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); return valid && pressed && (p_allow_echo || !is_echo()); } bool InputEvent::is_action_released(const StringName &p_action) const { - bool pressed; bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); return valid && !pressed; } float InputEvent::get_action_strength(const StringName &p_action) const { - - bool pressed; float strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed, &strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, &strength); return valid ? strength : 0.0f; } -bool InputEvent::is_pressed() const { +float InputEvent::get_action_raw_strength(const StringName &p_action) const { + float raw_strength; + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, nullptr, &raw_strength); + return valid ? raw_strength : 0.0f; +} +bool InputEvent::is_pressed() const { return false; } bool InputEvent::is_echo() const { - return false; } Ref<InputEvent> InputEvent::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - return Ref<InputEvent>((InputEvent *)this); } -String InputEvent::as_text() const { - - return String(); -} - -bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { - +bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { return false; } bool InputEvent::shortcut_match(const Ref<InputEvent> &p_event) const { - return false; } bool InputEvent::is_action_type() const { - return false; } void InputEvent::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device); ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device); @@ -132,14 +121,9 @@ void InputEvent::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "device"), "set_device", "get_device"); } -InputEvent::InputEvent() { - - device = 0; -} -//////////////// +/////////////////////////////////// void InputEventFromWindow::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_window_id", "id"), &InputEventFromWindow::set_window_id); ClassDB::bind_method(D_METHOD("get_window_id"), &InputEventFromWindow::get_window_id); ADD_PROPERTY(PropertyInfo(Variant::INT, "window_id"), "set_window_id", "get_window_id"); @@ -148,71 +132,98 @@ void InputEventFromWindow::_bind_methods() { void InputEventFromWindow::set_window_id(int64_t p_id) { window_id = p_id; } + int64_t InputEventFromWindow::get_window_id() const { return window_id; } -InputEventFromWindow::InputEventFromWindow() { - window_id = 0; +/////////////////////////////////// + +void InputEventWithModifiers::set_store_command(bool p_enabled) { + store_command = p_enabled; } -////////////////// +bool InputEventWithModifiers::is_storing_command() const { + return store_command; +} void InputEventWithModifiers::set_shift(bool p_enabled) { - shift = p_enabled; } bool InputEventWithModifiers::get_shift() const { - return shift; } void InputEventWithModifiers::set_alt(bool p_enabled) { - alt = p_enabled; } -bool InputEventWithModifiers::get_alt() const { +bool InputEventWithModifiers::get_alt() const { return alt; } void InputEventWithModifiers::set_control(bool p_enabled) { - control = p_enabled; } -bool InputEventWithModifiers::get_control() const { +bool InputEventWithModifiers::get_control() const { return control; } void InputEventWithModifiers::set_metakey(bool p_enabled) { - meta = p_enabled; } -bool InputEventWithModifiers::get_metakey() const { +bool InputEventWithModifiers::get_metakey() const { return meta; } void InputEventWithModifiers::set_command(bool p_enabled) { - command = p_enabled; } -bool InputEventWithModifiers::get_command() const { +bool InputEventWithModifiers::get_command() const { return command; } void InputEventWithModifiers::set_modifiers_from_event(const InputEventWithModifiers *event) { - set_alt(event->get_alt()); set_shift(event->get_shift()); set_control(event->get_control()); set_metakey(event->get_metakey()); } +String InputEventWithModifiers::as_text() const { + Vector<String> mod_names; + + if (get_control()) { + mod_names.push_back(find_keycode_name(KEY_CONTROL)); + } + if (get_shift()) { + mod_names.push_back(find_keycode_name(KEY_SHIFT)); + } + if (get_alt()) { + mod_names.push_back(find_keycode_name(KEY_ALT)); + } + if (get_metakey()) { + mod_names.push_back(find_keycode_name(KEY_META)); + } + + if (!mod_names.is_empty()) { + return String("+").join(mod_names); + } else { + return ""; + } +} + +String InputEventWithModifiers::to_string() { + return as_text(); +} + void InputEventWithModifiers::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_store_command", "enable"), &InputEventWithModifiers::set_store_command); + ClassDB::bind_method(D_METHOD("is_storing_command"), &InputEventWithModifiers::is_storing_command); ClassDB::bind_method(D_METHOD("set_alt", "enable"), &InputEventWithModifiers::set_alt); ClassDB::bind_method(D_METHOD("get_alt"), &InputEventWithModifiers::get_alt); @@ -229,6 +240,7 @@ void InputEventWithModifiers::_bind_methods() { ClassDB::bind_method(D_METHOD("set_command", "enable"), &InputEventWithModifiers::set_command); ClassDB::bind_method(D_METHOD("get_command"), &InputEventWithModifiers::get_command); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "store_command"), "set_store_command", "is_storing_command"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "alt"), "set_alt", "get_alt"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shift"), "set_shift", "get_shift"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "control"), "set_control", "get_control"); @@ -236,122 +248,147 @@ void InputEventWithModifiers::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "command"), "set_command", "get_command"); } -InputEventWithModifiers::InputEventWithModifiers() { - - alt = false; - shift = false; - control = false; - meta = false; +void InputEventWithModifiers::_validate_property(PropertyInfo &property) const { + if (store_command) { + // If we only want to Store "Command". +#ifdef APPLE_STYLE_KEYS + // Don't store "Meta" on Mac. + if (property.name == "meta") { + property.usage ^= PROPERTY_USAGE_STORAGE; + } +#else + // Don't store "Control". + if (property.name == "control") { + property.usage ^= PROPERTY_USAGE_STORAGE; + } +#endif + } else { + // We don't want to store command, only control or meta (on mac). + if (property.name == "command") { + property.usage ^= PROPERTY_USAGE_STORAGE; + } + } } -////////////////////////////////// +/////////////////////////////////// void InputEventKey::set_pressed(bool p_pressed) { - pressed = p_pressed; } bool InputEventKey::is_pressed() const { - return pressed; } void InputEventKey::set_keycode(uint32_t p_keycode) { - keycode = p_keycode; } uint32_t InputEventKey::get_keycode() const { - return keycode; } void InputEventKey::set_physical_keycode(uint32_t p_keycode) { - physical_keycode = p_keycode; } uint32_t InputEventKey::get_physical_keycode() const { - return physical_keycode; } void InputEventKey::set_unicode(uint32_t p_unicode) { - unicode = p_unicode; } uint32_t InputEventKey::get_unicode() const { - return unicode; } void InputEventKey::set_echo(bool p_enable) { - echo = p_enable; } bool InputEventKey::is_echo() const { - return echo; } uint32_t InputEventKey::get_keycode_with_modifiers() const { - uint32_t sc = keycode; - if (get_control()) + if (get_control()) { sc |= KEY_MASK_CTRL; - if (get_alt()) + } + if (get_alt()) { sc |= KEY_MASK_ALT; - if (get_shift()) + } + if (get_shift()) { sc |= KEY_MASK_SHIFT; - if (get_metakey()) + } + if (get_metakey()) { sc |= KEY_MASK_META; + } return sc; } uint32_t InputEventKey::get_physical_keycode_with_modifiers() const { - uint32_t sc = physical_keycode; - if (get_control()) + if (get_control()) { sc |= KEY_MASK_CTRL; - if (get_alt()) + } + if (get_alt()) { sc |= KEY_MASK_ALT; - if (get_shift()) + } + if (get_shift()) { sc |= KEY_MASK_SHIFT; - if (get_metakey()) + } + if (get_metakey()) { sc |= KEY_MASK_META; + } return sc; } String InputEventKey::as_text() const { + String kc; - String kc = keycode_get_string(keycode); - if (kc == String()) - return kc; - - if (get_metakey()) { - kc = find_keycode_name(KEY_META) + ("+" + kc); - } - if (get_alt()) { - kc = find_keycode_name(KEY_ALT) + ("+" + kc); - } - if (get_shift()) { - kc = find_keycode_name(KEY_SHIFT) + ("+" + kc); + if (keycode == 0) { + kc = keycode_get_string(physical_keycode) + " (" + RTR("Physical") + ")"; + } else { + kc = keycode_get_string(keycode); } - if (get_control()) { - kc = find_keycode_name(KEY_CONTROL) + ("+" + kc); + + if (kc == String()) { + return kc; } - return kc; + + String mods_text = InputEventWithModifiers::as_text(); + return mods_text == "" ? kc : mods_text + "+" + kc; } -bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { +String InputEventKey::to_string() { + String p = is_pressed() ? "true" : "false"; + String e = is_echo() ? "true" : "false"; + + String kc = ""; + String physical = "false"; + if (keycode == 0) { + kc = itos(physical_keycode) + " " + keycode_get_string(physical_keycode); + physical = "true"; + } else { + kc = itos(keycode) + " " + keycode_get_string(keycode); + } + + String mods = InputEventWithModifiers::as_text(); + mods = mods == "" ? TTR("None") : mods; + + return vformat("InputEventKey: keycode=%s mods=%s physical=%s pressed=%s echo=%s", kc, mods, physical, p, e); +} +bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref<InputEventKey> key = p_event; - if (key.is_null()) + if (key.is_null()) { return false; + } bool match = false; if (get_keycode() == 0) { @@ -366,19 +403,25 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed match = get_keycode() == key->get_keycode() && (!key->is_pressed() || (code & event_code) == code); } if (match) { - if (p_pressed != nullptr) + if (p_pressed != nullptr) { *p_pressed = key->is_pressed(); - if (p_strength != nullptr) - *p_strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + } + float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + if (p_strength != nullptr) { + *p_strength = strength; + } + if (p_raw_strength != nullptr) { + *p_raw_strength = strength; + } } return match; } bool InputEventKey::shortcut_match(const Ref<InputEvent> &p_event) const { - Ref<InputEventKey> key = p_event; - if (key.is_null()) + if (key.is_null()) { return false; + } uint32_t code = get_keycode_with_modifiers(); uint32_t event_code = key->get_keycode_with_modifiers(); @@ -387,7 +430,6 @@ bool InputEventKey::shortcut_match(const Ref<InputEvent> &p_event) const { } void InputEventKey::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventKey::set_pressed); ClassDB::bind_method(D_METHOD("set_keycode", "keycode"), &InputEventKey::set_keycode); @@ -411,46 +453,33 @@ void InputEventKey::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "echo"), "set_echo", "is_echo"); } -InputEventKey::InputEventKey() { - - pressed = false; - keycode = 0; - physical_keycode = 0; - unicode = 0; ///unicode - echo = false; -} - -//////////////////////////////////////// +/////////////////////////////////// void InputEventMouse::set_button_mask(int p_mask) { - button_mask = p_mask; } -int InputEventMouse::get_button_mask() const { +int InputEventMouse::get_button_mask() const { return button_mask; } void InputEventMouse::set_position(const Vector2 &p_pos) { - pos = p_pos; } -Vector2 InputEventMouse::get_position() const { +Vector2 InputEventMouse::get_position() const { return pos; } void InputEventMouse::set_global_position(const Vector2 &p_global_pos) { - global_pos = p_global_pos; } -Vector2 InputEventMouse::get_global_position() const { +Vector2 InputEventMouse::get_global_position() const { return global_pos; } void InputEventMouse::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_button_mask", "button_mask"), &InputEventMouse::set_button_mask); ClassDB::bind_method(D_METHOD("get_button_mask"), &InputEventMouse::get_button_mask); @@ -465,52 +494,41 @@ void InputEventMouse::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position"), "set_global_position", "get_global_position"); } -InputEventMouse::InputEventMouse() { - - button_mask = 0; -} - -/////////////////////////////////////// +/////////////////////////////////// void InputEventMouseButton::set_factor(float p_factor) { - factor = p_factor; } float InputEventMouseButton::get_factor() const { - return factor; } void InputEventMouseButton::set_button_index(int p_index) { - button_index = p_index; } -int InputEventMouseButton::get_button_index() const { +int InputEventMouseButton::get_button_index() const { return button_index; } void InputEventMouseButton::set_pressed(bool p_pressed) { - pressed = p_pressed; } -bool InputEventMouseButton::is_pressed() const { +bool InputEventMouseButton::is_pressed() const { return pressed; } void InputEventMouseButton::set_doubleclick(bool p_doubleclick) { - doubleclick = p_doubleclick; } -bool InputEventMouseButton::is_doubleclick() const { +bool InputEventMouseButton::is_doubleclick() const { return doubleclick; } Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Vector2 g = get_global_position(); Vector2 l = p_xform.xform(get_position() + p_local_ofs); @@ -533,63 +551,105 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co return mb; } -bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { - +bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref<InputEventMouseButton> mb = p_event; - if (mb.is_null()) + if (mb.is_null()) { return false; + } bool match = mb->button_index == button_index; if (match) { - if (p_pressed != nullptr) + if (p_pressed != nullptr) { *p_pressed = mb->is_pressed(); - if (p_strength != nullptr) - *p_strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + } + float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + if (p_strength != nullptr) { + *p_strength = strength; + } + if (p_raw_strength != nullptr) { + *p_raw_strength = strength; + } } return match; } +static const char *_mouse_button_descriptions[9] = { + TTRC("Left Mouse Button"), + TTRC("Right Mouse Button"), + TTRC("Middle Mouse Button"), + TTRC("Mouse Wheel Up"), + TTRC("Mouse Wheel Down"), + TTRC("Mouse Wheel Left"), + TTRC("Mouse Wheel Right"), + TTRC("Mouse Thumb Button 1"), + TTRC("Mouse Thumb Button 2") +}; + String InputEventMouseButton::as_text() const { + // Modifiers + String mods_text = InputEventWithModifiers::as_text(); + String full_string = mods_text == "" ? "" : mods_text + "+"; - String button_index_string = ""; - switch (get_button_index()) { + // Button + int idx = get_button_index(); + switch (idx) { case BUTTON_LEFT: - button_index_string = "BUTTON_LEFT"; - break; case BUTTON_RIGHT: - button_index_string = "BUTTON_RIGHT"; - break; case BUTTON_MIDDLE: - button_index_string = "BUTTON_MIDDLE"; - break; case BUTTON_WHEEL_UP: - button_index_string = "BUTTON_WHEEL_UP"; - break; case BUTTON_WHEEL_DOWN: - button_index_string = "BUTTON_WHEEL_DOWN"; - break; case BUTTON_WHEEL_LEFT: - button_index_string = "BUTTON_WHEEL_LEFT"; - break; case BUTTON_WHEEL_RIGHT: - button_index_string = "BUTTON_WHEEL_RIGHT"; - break; case BUTTON_XBUTTON1: - button_index_string = "BUTTON_XBUTTON1"; + case BUTTON_XBUTTON2: + full_string += RTR(_mouse_button_descriptions[idx - 1]); // button index starts from 1, array index starts from 0, so subtract 1 + break; + default: + full_string += RTR("Button") + " #" + itos(idx); break; + } + + // Double Click + if (doubleclick) { + full_string += " (" + RTR("Double Click") + ")"; + } + + return full_string; +} + +String InputEventMouseButton::to_string() { + String p = is_pressed() ? "true" : "false"; + String d = doubleclick ? "true" : "false"; + + int idx = get_button_index(); + String button_string = itos(idx); + + switch (idx) { + case BUTTON_LEFT: + case BUTTON_RIGHT: + case BUTTON_MIDDLE: + case BUTTON_WHEEL_UP: + case BUTTON_WHEEL_DOWN: + case BUTTON_WHEEL_LEFT: + case BUTTON_WHEEL_RIGHT: + case BUTTON_XBUTTON1: case BUTTON_XBUTTON2: - button_index_string = "BUTTON_XBUTTON2"; + button_string += " (" + RTR(_mouse_button_descriptions[idx - 1]) + ")"; // button index starts from 1, array index starts from 0, so subtract 1 break; default: - button_index_string = itos(get_button_index()); break; } - return "InputEventMouseButton : button_index=" + button_index_string + ", pressed=" + (pressed ? "true" : "false") + ", position=(" + String(get_position()) + "), button_mask=" + itos(get_button_mask()) + ", doubleclick=" + (doubleclick ? "true" : "false"); + + String mods = InputEventWithModifiers::as_text(); + mods = mods == "" ? TTR("None") : mods; + + // Work around the fact vformat can only take 5 substitutions but 6 need to be passed. + String index_and_mods = vformat("button_index=%s mods=%s", button_index, mods); + return vformat("InputEventMouseButton: %s pressed=%s position=(%s) button_mask=%s doubleclick=%s", index_and_mods, p, String(get_position()), itos(get_button_mask()), d); } void InputEventMouseButton::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_factor", "factor"), &InputEventMouseButton::set_factor); ClassDB::bind_method(D_METHOD("get_factor"), &InputEventMouseButton::get_factor); @@ -608,58 +668,41 @@ void InputEventMouseButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "doubleclick"), "set_doubleclick", "is_doubleclick"); } -InputEventMouseButton::InputEventMouseButton() { - - factor = 1; - button_index = 0; - pressed = false; - doubleclick = false; -} - -//////////////////////////////////////////// +/////////////////////////////////// void InputEventMouseMotion::set_tilt(const Vector2 &p_tilt) { - tilt = p_tilt; } Vector2 InputEventMouseMotion::get_tilt() const { - return tilt; } void InputEventMouseMotion::set_pressure(float p_pressure) { - pressure = p_pressure; } float InputEventMouseMotion::get_pressure() const { - return pressure; } void InputEventMouseMotion::set_relative(const Vector2 &p_relative) { - relative = p_relative; } Vector2 InputEventMouseMotion::get_relative() const { - return relative; } void InputEventMouseMotion::set_speed(const Vector2 &p_speed) { - speed = p_speed; } Vector2 InputEventMouseMotion::get_speed() const { - return speed; } Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Vector2 g = get_global_position(); Vector2 l = p_xform.xform(get_position() + p_local_ofs); Vector2 r = p_xform.basis_xform(get_relative()); @@ -686,36 +729,40 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co } String InputEventMouseMotion::as_text() const { + return vformat(RTR("Mouse motion at position (%s) with speed (%s)"), String(get_position()), String(get_speed())); +} - String button_mask_string = ""; +String InputEventMouseMotion::to_string() { + int button_mask = get_button_mask(); + String button_mask_string = itos(button_mask); switch (get_button_mask()) { case BUTTON_MASK_LEFT: - button_mask_string = "BUTTON_MASK_LEFT"; + button_mask_string += " (" + RTR(_mouse_button_descriptions[BUTTON_LEFT - 1]) + ")"; break; case BUTTON_MASK_MIDDLE: - button_mask_string = "BUTTON_MASK_MIDDLE"; + button_mask_string += " (" + RTR(_mouse_button_descriptions[BUTTON_MIDDLE - 1]) + ")"; break; case BUTTON_MASK_RIGHT: - button_mask_string = "BUTTON_MASK_RIGHT"; + button_mask_string += " (" + RTR(_mouse_button_descriptions[BUTTON_RIGHT - 1]) + ")"; break; case BUTTON_MASK_XBUTTON1: - button_mask_string = "BUTTON_MASK_XBUTTON1"; + button_mask_string += " (" + RTR(_mouse_button_descriptions[BUTTON_XBUTTON1 - 1]) + ")"; break; case BUTTON_MASK_XBUTTON2: - button_mask_string = "BUTTON_MASK_XBUTTON2"; + button_mask_string += " (" + RTR(_mouse_button_descriptions[BUTTON_XBUTTON2 - 1]) + ")"; break; default: - button_mask_string = itos(get_button_mask()); break; } + return "InputEventMouseMotion : button_mask=" + button_mask_string + ", position=(" + String(get_position()) + "), relative=(" + String(get_relative()) + "), speed=(" + String(get_speed()) + "), pressure=(" + rtos(get_pressure()) + "), tilt=(" + String(get_tilt()) + ")"; } bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) { - Ref<InputEventMouseMotion> motion = p_event; - if (motion.is_null()) + if (motion.is_null()) { return false; + } if (get_window_id() != motion->get_window_id()) { return false; @@ -754,7 +801,6 @@ bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) { } void InputEventMouseMotion::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_tilt", "tilt"), &InputEventMouseMotion::set_tilt); ClassDB::bind_method(D_METHOD("get_tilt"), &InputEventMouseMotion::get_tilt); @@ -773,72 +819,88 @@ void InputEventMouseMotion::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "speed"), "set_speed", "get_speed"); } -InputEventMouseMotion::InputEventMouseMotion() { - - pressure = 0; -} - -//////////////////////////////////////// +/////////////////////////////////// void InputEventJoypadMotion::set_axis(int p_axis) { - axis = p_axis; } int InputEventJoypadMotion::get_axis() const { - return axis; } void InputEventJoypadMotion::set_axis_value(float p_value) { - axis_value = p_value; } float InputEventJoypadMotion::get_axis_value() const { - return axis_value; } bool InputEventJoypadMotion::is_pressed() const { - return Math::abs(axis_value) >= 0.5f; } -bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { - +bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref<InputEventJoypadMotion> jm = p_event; - if (jm.is_null()) + if (jm.is_null()) { return false; + } bool match = (axis == jm->axis); // Matches even if not in the same direction, but returns a "not pressed" event. if (match) { + float jm_abs_axis_value = Math::abs(jm->get_axis_value()); bool same_direction = (((axis_value < 0) == (jm->axis_value < 0)) || jm->axis_value == 0); - bool pressed = same_direction ? Math::abs(jm->get_axis_value()) >= p_deadzone : false; - if (p_pressed != nullptr) + bool pressed = same_direction && jm_abs_axis_value >= p_deadzone; + if (p_pressed != nullptr) { *p_pressed = pressed; + } if (p_strength != nullptr) { if (pressed) { if (p_deadzone == 1.0f) { *p_strength = 1.0f; } else { - *p_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f); + *p_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, jm_abs_axis_value), 0.0f, 1.0f); } } else { *p_strength = 0.0f; } } + if (p_raw_strength != nullptr) { + if (same_direction) { // NOT pressed, because we want to ignore the deadzone. + *p_raw_strength = jm_abs_axis_value; + } else { + *p_raw_strength = 0.0f; + } + } } return match; } +static const char *_joy_axis_descriptions[JOY_AXIS_MAX] = { + TTRC("Left Stick X-Axis, Joystick 0 X-Axis"), + TTRC("Left Stick Y-Axis, Joystick 0 Y-Axis"), + TTRC("Right Stick X-Axis, Joystick 1 X-Axis"), + TTRC("Right Stick Y-Axis, Joystick 1 Y-Axis"), + TTRC("Joystick 2 X-Axis, Left Trigger, Sony L2, Xbox LT"), + TTRC("Joystick 2 Y-Axis, Right Trigger, Sony R2, Xbox RT"), + TTRC("Joystick 3 X-Axis"), + TTRC("Joystick 3 Y-Axis"), + TTRC("Joystick 4 X-Axis"), + TTRC("Joystick 4 Y-Axis"), +}; + String InputEventJoypadMotion::as_text() const { + String desc = axis < JOY_AXIS_MAX ? RTR(_joy_axis_descriptions[axis]) : TTR("Unknown Joypad Axis"); + + return vformat(TTR("Joypad Motion on Axis %s (%s) with Value %s"), itos(axis), desc, String(Variant(axis_value))); +} +String InputEventJoypadMotion::to_string() { return "InputEventJoypadMotion : axis=" + itos(axis) + ", axis_value=" + String(Variant(axis_value)); } void InputEventJoypadMotion::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_axis", "axis"), &InputEventJoypadMotion::set_axis); ClassDB::bind_method(D_METHOD("get_axis"), &InputEventJoypadMotion::get_axis); @@ -849,74 +911,101 @@ void InputEventJoypadMotion::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "axis_value"), "set_axis_value", "get_axis_value"); } -InputEventJoypadMotion::InputEventJoypadMotion() { - - axis = 0; - axis_value = 0; -} -///////////////////////////////// +/////////////////////////////////// void InputEventJoypadButton::set_button_index(int p_index) { - button_index = p_index; } int InputEventJoypadButton::get_button_index() const { - return button_index; } void InputEventJoypadButton::set_pressed(bool p_pressed) { - pressed = p_pressed; } -bool InputEventJoypadButton::is_pressed() const { +bool InputEventJoypadButton::is_pressed() const { return pressed; } void InputEventJoypadButton::set_pressure(float p_pressure) { - pressure = p_pressure; } -float InputEventJoypadButton::get_pressure() const { +float InputEventJoypadButton::get_pressure() const { return pressure; } -bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { - +bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref<InputEventJoypadButton> jb = p_event; - if (jb.is_null()) + if (jb.is_null()) { return false; + } bool match = button_index == jb->button_index; if (match) { - if (p_pressed != nullptr) + if (p_pressed != nullptr) { *p_pressed = jb->is_pressed(); - if (p_strength != nullptr) - *p_strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + } + float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + if (p_strength != nullptr) { + *p_strength = strength; + } + if (p_raw_strength != nullptr) { + *p_raw_strength = strength; + } } return match; } bool InputEventJoypadButton::shortcut_match(const Ref<InputEvent> &p_event) const { - Ref<InputEventJoypadButton> button = p_event; - if (button.is_null()) + if (button.is_null()) { return false; + } return button_index == button->button_index; } +static const char *_joy_button_descriptions[JOY_BUTTON_SDL_MAX] = { + TTRC("Bottom Action, Sony Cross, Xbox A, Nintendo B"), + TTRC("Right Action, Sony Circle, Xbox B, Nintendo A"), + TTRC("Left Action, Sony Square, Xbox X, Nintendo Y"), + TTRC("Top Action, Sony Triangle, Xbox Y, Nintendo X"), + TTRC("Back, Sony Select, Xbox Back, Nintendo -"), + TTRC("Guide, Sony PS, Xbox Home"), + TTRC("Start, Nintendo +"), + TTRC("Left Stick, Sony L3, Xbox L/LS"), + TTRC("Right Stick, Sony R3, Xbox R/RS"), + TTRC("Left Shoulder, Sony L1, Xbox LB"), + TTRC("Right Shoulder, Sony R1, Xbox RB"), + TTRC("D-pad Up"), + TTRC("D-pad Down"), + TTRC("D-pad Left"), + TTRC("D-pad Right"), +}; + String InputEventJoypadButton::as_text() const { + String text = "Joypad Button " + itos(button_index); + + if (button_index < JOY_BUTTON_SDL_MAX) { + text += vformat(" (%s)", _joy_button_descriptions[button_index]); + } + + if (pressure != 0) { + text += ", Pressure:" + String(Variant(pressure)); + } + return text; +} + +String InputEventJoypadButton::to_string() { return "InputEventJoypadButton : button_index=" + itos(button_index) + ", pressed=" + (pressed ? "true" : "false") + ", pressure=" + String(Variant(pressure)); } void InputEventJoypadButton::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_button_index", "button_index"), &InputEventJoypadButton::set_button_index); ClassDB::bind_method(D_METHOD("get_button_index"), &InputEventJoypadButton::get_button_index); @@ -931,44 +1020,33 @@ void InputEventJoypadButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed"); } -InputEventJoypadButton::InputEventJoypadButton() { - - button_index = 0; - pressure = 0; - pressed = false; -} - -////////////////////////////////////////////// +/////////////////////////////////// void InputEventScreenTouch::set_index(int p_index) { - index = p_index; } -int InputEventScreenTouch::get_index() const { +int InputEventScreenTouch::get_index() const { return index; } void InputEventScreenTouch::set_position(const Vector2 &p_pos) { - pos = p_pos; } -Vector2 InputEventScreenTouch::get_position() const { +Vector2 InputEventScreenTouch::get_position() const { return pos; } void InputEventScreenTouch::set_pressed(bool p_pressed) { - pressed = p_pressed; } -bool InputEventScreenTouch::is_pressed() const { +bool InputEventScreenTouch::is_pressed() const { return pressed; } Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Ref<InputEventScreenTouch> st; st.instance(); st->set_device(get_device()); @@ -981,12 +1059,16 @@ Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, co } String InputEventScreenTouch::as_text() const { + String status = pressed ? RTR("touched") : RTR("released"); + + return vformat(RTR("Screen %s at (%s) with %s touch points"), status, String(get_position()), itos(index)); +} +String InputEventScreenTouch::to_string() { return "InputEventScreenTouch : index=" + itos(index) + ", pressed=" + (pressed ? "true" : "false") + ", position=(" + String(get_position()) + ")"; } void InputEventScreenTouch::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_index", "index"), &InputEventScreenTouch::set_index); ClassDB::bind_method(D_METHOD("get_index"), &InputEventScreenTouch::get_index); @@ -1001,53 +1083,41 @@ void InputEventScreenTouch::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed"); } -InputEventScreenTouch::InputEventScreenTouch() { - - index = 0; - pressed = false; -} - -///////////////////////////// +/////////////////////////////////// void InputEventScreenDrag::set_index(int p_index) { - index = p_index; } int InputEventScreenDrag::get_index() const { - return index; } void InputEventScreenDrag::set_position(const Vector2 &p_pos) { - pos = p_pos; } -Vector2 InputEventScreenDrag::get_position() const { +Vector2 InputEventScreenDrag::get_position() const { return pos; } void InputEventScreenDrag::set_relative(const Vector2 &p_relative) { - relative = p_relative; } -Vector2 InputEventScreenDrag::get_relative() const { +Vector2 InputEventScreenDrag::get_relative() const { return relative; } void InputEventScreenDrag::set_speed(const Vector2 &p_speed) { - speed = p_speed; } -Vector2 InputEventScreenDrag::get_speed() const { +Vector2 InputEventScreenDrag::get_speed() const { return speed; } Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Ref<InputEventScreenDrag> sd; sd.instance(); @@ -1064,12 +1134,14 @@ Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, con } String InputEventScreenDrag::as_text() const { + return vformat(RTR("Screen dragged with %s touch points at position (%s) with speed of (%s)"), itos(index), String(get_position()), String(get_speed())); +} +String InputEventScreenDrag::to_string() { return "InputEventScreenDrag : index=" + itos(index) + ", position=(" + String(get_position()) + "), relative=(" + String(get_relative()) + "), speed=(" + String(get_speed()) + ")"; } void InputEventScreenDrag::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_index", "index"), &InputEventScreenDrag::set_index); ClassDB::bind_method(D_METHOD("get_index"), &InputEventScreenDrag::get_index); @@ -1088,27 +1160,21 @@ void InputEventScreenDrag::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "speed"), "set_speed", "get_speed"); } -InputEventScreenDrag::InputEventScreenDrag() { - - index = 0; -} -///////////////////////////// +/////////////////////////////////// void InputEventAction::set_action(const StringName &p_action) { - action = p_action; } -StringName InputEventAction::get_action() const { +StringName InputEventAction::get_action() const { return action; } void InputEventAction::set_pressed(bool p_pressed) { - pressed = p_pressed; } -bool InputEventAction::is_pressed() const { +bool InputEventAction::is_pressed() const { return pressed; } @@ -1121,40 +1187,48 @@ float InputEventAction::get_strength() const { } bool InputEventAction::shortcut_match(const Ref<InputEvent> &p_event) const { - if (p_event.is_null()) + if (p_event.is_null()) { return false; + } return p_event->is_action(action); } bool InputEventAction::is_action(const StringName &p_action) const { - return action == p_action; } -bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { - +bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref<InputEventAction> act = p_event; - if (act.is_null()) + if (act.is_null()) { return false; + } bool match = action == act->action; if (match) { - if (p_pressed != nullptr) + if (p_pressed != nullptr) { *p_pressed = act->pressed; - if (p_strength != nullptr) - *p_strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + } + float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + if (p_strength != nullptr) { + *p_strength = strength; + } + if (p_raw_strength != nullptr) { + *p_raw_strength = strength; + } } return match; } String InputEventAction::as_text() const { + return vformat(RTR("Input Action %s was %s"), action, pressed ? "pressed" : "released"); +} +String InputEventAction::to_string() { return "InputEventAction : action=" + action + ", pressed=(" + (pressed ? "true" : "false"); } void InputEventAction::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_action", "action"), &InputEventAction::set_action); ClassDB::bind_method(D_METHOD("get_action"), &InputEventAction::get_action); @@ -1171,19 +1245,13 @@ void InputEventAction::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_strength", "get_strength"); } -InputEventAction::InputEventAction() { - pressed = false; - strength = 1.0f; -} -///////////////////////////// +/////////////////////////////////// void InputEventGesture::set_position(const Vector2 &p_pos) { - pos = p_pos; } void InputEventGesture::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_position", "position"), &InputEventGesture::set_position); ClassDB::bind_method(D_METHOD("get_position"), &InputEventGesture::get_position); @@ -1191,23 +1259,20 @@ void InputEventGesture::_bind_methods() { } Vector2 InputEventGesture::get_position() const { - return pos; } -///////////////////////////// -void InputEventMagnifyGesture::set_factor(real_t p_factor) { +/////////////////////////////////// +void InputEventMagnifyGesture::set_factor(real_t p_factor) { factor = p_factor; } real_t InputEventMagnifyGesture::get_factor() const { - return factor; } Ref<InputEvent> InputEventMagnifyGesture::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Ref<InputEventMagnifyGesture> ev; ev.instance(); @@ -1223,26 +1288,23 @@ Ref<InputEvent> InputEventMagnifyGesture::xformed_by(const Transform2D &p_xform, } String InputEventMagnifyGesture::as_text() const { + return vformat(RTR("Magnify Gesture at (%s) with factor %s"), String(get_position()), rtos(get_factor())); +} +String InputEventMagnifyGesture::to_string() { return "InputEventMagnifyGesture : factor=" + rtos(get_factor()) + ", position=(" + String(get_position()) + ")"; } void InputEventMagnifyGesture::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_factor", "factor"), &InputEventMagnifyGesture::set_factor); ClassDB::bind_method(D_METHOD("get_factor"), &InputEventMagnifyGesture::get_factor); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "factor"), "set_factor", "get_factor"); } -InputEventMagnifyGesture::InputEventMagnifyGesture() { - - factor = 1.0; -} -///////////////////////////// +/////////////////////////////////// void InputEventPanGesture::set_delta(const Vector2 &p_delta) { - delta = p_delta; } @@ -1251,7 +1313,6 @@ Vector2 InputEventPanGesture::get_delta() const { } Ref<InputEvent> InputEventPanGesture::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Ref<InputEventPanGesture> ev; ev.instance(); @@ -1267,26 +1328,23 @@ Ref<InputEvent> InputEventPanGesture::xformed_by(const Transform2D &p_xform, con } String InputEventPanGesture::as_text() const { + return vformat(RTR("Pan Gesture at (%s) with delta (%s)"), String(get_position()), String(get_delta())); +} +String InputEventPanGesture::to_string() { return "InputEventPanGesture : delta=(" + String(get_delta()) + "), position=(" + String(get_position()) + ")"; } void InputEventPanGesture::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_delta", "delta"), &InputEventPanGesture::set_delta); ClassDB::bind_method(D_METHOD("get_delta"), &InputEventPanGesture::get_delta); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "delta"), "set_delta", "get_delta"); } -InputEventPanGesture::InputEventPanGesture() { - - delta = Vector2(0, 0); -} -///////////////////////////// +/////////////////////////////////// void InputEventMIDI::set_channel(const int p_channel) { - channel = p_channel; } @@ -1295,7 +1353,6 @@ int InputEventMIDI::get_channel() const { } void InputEventMIDI::set_message(const int p_message) { - message = p_message; } @@ -1304,7 +1361,6 @@ int InputEventMIDI::get_message() const { } void InputEventMIDI::set_pitch(const int p_pitch) { - pitch = p_pitch; } @@ -1313,7 +1369,6 @@ int InputEventMIDI::get_pitch() const { } void InputEventMIDI::set_velocity(const int p_velocity) { - velocity = p_velocity; } @@ -1322,7 +1377,6 @@ int InputEventMIDI::get_velocity() const { } void InputEventMIDI::set_instrument(const int p_instrument) { - instrument = p_instrument; } @@ -1331,7 +1385,6 @@ int InputEventMIDI::get_instrument() const { } void InputEventMIDI::set_pressure(const int p_pressure) { - pressure = p_pressure; } @@ -1340,7 +1393,6 @@ int InputEventMIDI::get_pressure() const { } void InputEventMIDI::set_controller_number(const int p_controller_number) { - controller_number = p_controller_number; } @@ -1349,7 +1401,6 @@ int InputEventMIDI::get_controller_number() const { } void InputEventMIDI::set_controller_value(const int p_controller_value) { - controller_value = p_controller_value; } @@ -1358,12 +1409,14 @@ int InputEventMIDI::get_controller_value() const { } String InputEventMIDI::as_text() const { + return vformat(RTR("MIDI Input on Channel=%s Message=%s"), itos(channel), itos(message)); +} - return "InputEventMIDI : channel=(" + itos(get_channel()) + "), message=(" + itos(get_message()) + ")"; +String InputEventMIDI::to_string() { + return vformat("InputEvenMIDI: channel=%s message=%s pitch=%s velocity=%s pressure=%s", itos(channel), itos(message), itos(pitch), itos(velocity), itos(pressure)); } void InputEventMIDI::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_channel", "channel"), &InputEventMIDI::set_channel); ClassDB::bind_method(D_METHOD("get_channel"), &InputEventMIDI::get_channel); ClassDB::bind_method(D_METHOD("set_message", "message"), &InputEventMIDI::set_message); @@ -1390,15 +1443,3 @@ void InputEventMIDI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number"); ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value"); } - -InputEventMIDI::InputEventMIDI() { - - channel = 0; - message = 0; - pitch = 0; - velocity = 0; - instrument = 0; - pressure = 0; - controller_number = 0; - controller_value = 0; -} diff --git a/core/input/input_event.h b/core/input/input_event.h index 8774b3b1db..1500faa24c 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,11 +31,11 @@ #ifndef INPUT_EVENT_H #define INPUT_EVENT_H +#include "core/io/resource.h" #include "core/math/transform_2d.h" #include "core/os/copymem.h" -#include "core/resource.h" +#include "core/string/ustring.h" #include "core/typedefs.h" -#include "core/ustring.h" /** * Input Event classes. These are used in the main loop. @@ -59,98 +59,37 @@ enum ButtonList { BUTTON_MASK_XBUTTON2 = (1 << (BUTTON_XBUTTON2 - 1)) }; -enum JoystickList { - - JOY_BUTTON_0 = 0, - JOY_BUTTON_1 = 1, - JOY_BUTTON_2 = 2, - JOY_BUTTON_3 = 3, - JOY_BUTTON_4 = 4, - JOY_BUTTON_5 = 5, - JOY_BUTTON_6 = 6, - JOY_BUTTON_7 = 7, - JOY_BUTTON_8 = 8, - JOY_BUTTON_9 = 9, - JOY_BUTTON_10 = 10, - JOY_BUTTON_11 = 11, - JOY_BUTTON_12 = 12, - JOY_BUTTON_13 = 13, - JOY_BUTTON_14 = 14, - JOY_BUTTON_15 = 15, - JOY_BUTTON_MAX = 16, - - JOY_L = JOY_BUTTON_4, - JOY_R = JOY_BUTTON_5, - JOY_L2 = JOY_BUTTON_6, - JOY_R2 = JOY_BUTTON_7, - JOY_L3 = JOY_BUTTON_8, - JOY_R3 = JOY_BUTTON_9, - JOY_SELECT = JOY_BUTTON_10, - JOY_START = JOY_BUTTON_11, - JOY_DPAD_UP = JOY_BUTTON_12, - JOY_DPAD_DOWN = JOY_BUTTON_13, - JOY_DPAD_LEFT = JOY_BUTTON_14, - JOY_DPAD_RIGHT = JOY_BUTTON_15, - - JOY_SONY_CIRCLE = JOY_BUTTON_1, - JOY_SONY_X = JOY_BUTTON_0, - JOY_SONY_SQUARE = JOY_BUTTON_2, - JOY_SONY_TRIANGLE = JOY_BUTTON_3, - - JOY_XBOX_A = JOY_BUTTON_0, - JOY_XBOX_B = JOY_BUTTON_1, - JOY_XBOX_X = JOY_BUTTON_2, - JOY_XBOX_Y = JOY_BUTTON_3, - - JOY_DS_A = JOY_BUTTON_1, - JOY_DS_B = JOY_BUTTON_0, - JOY_DS_X = JOY_BUTTON_3, - JOY_DS_Y = JOY_BUTTON_2, - - JOY_WII_C = JOY_BUTTON_5, - JOY_WII_Z = JOY_BUTTON_6, - - JOY_WII_MINUS = JOY_BUTTON_10, - JOY_WII_PLUS = JOY_BUTTON_11, - - JOY_VR_GRIP = JOY_BUTTON_2, - JOY_VR_PAD = JOY_BUTTON_14, - JOY_VR_TRIGGER = JOY_BUTTON_15, - - JOY_OCULUS_AX = JOY_BUTTON_7, - JOY_OCULUS_BY = JOY_BUTTON_1, - JOY_OCULUS_MENU = JOY_BUTTON_3, - - JOY_OPENVR_MENU = JOY_BUTTON_1, - - // end of history - - JOY_AXIS_0 = 0, - JOY_AXIS_1 = 1, - JOY_AXIS_2 = 2, - JOY_AXIS_3 = 3, - JOY_AXIS_4 = 4, - JOY_AXIS_5 = 5, - JOY_AXIS_6 = 6, - JOY_AXIS_7 = 7, - JOY_AXIS_8 = 8, - JOY_AXIS_9 = 9, - JOY_AXIS_MAX = 10, - - JOY_ANALOG_LX = JOY_AXIS_0, - JOY_ANALOG_LY = JOY_AXIS_1, - - JOY_ANALOG_RX = JOY_AXIS_2, - JOY_ANALOG_RY = JOY_AXIS_3, - - JOY_ANALOG_L2 = JOY_AXIS_6, - JOY_ANALOG_R2 = JOY_AXIS_7, - - JOY_VR_ANALOG_TRIGGER = JOY_AXIS_2, - JOY_VR_ANALOG_GRIP = JOY_AXIS_4, - - JOY_OPENVR_TOUCHPADX = JOY_AXIS_0, - JOY_OPENVR_TOUCHPADY = JOY_AXIS_1, +enum JoyButtonList { + JOY_BUTTON_INVALID = -1, + JOY_BUTTON_A = 0, + JOY_BUTTON_B = 1, + JOY_BUTTON_X = 2, + JOY_BUTTON_Y = 3, + JOY_BUTTON_BACK = 4, + JOY_BUTTON_GUIDE = 5, + JOY_BUTTON_START = 6, + JOY_BUTTON_LEFT_STICK = 7, + JOY_BUTTON_RIGHT_STICK = 8, + JOY_BUTTON_LEFT_SHOULDER = 9, + JOY_BUTTON_RIGHT_SHOULDER = 10, + JOY_BUTTON_DPAD_UP = 11, + JOY_BUTTON_DPAD_DOWN = 12, + JOY_BUTTON_DPAD_LEFT = 13, + JOY_BUTTON_DPAD_RIGHT = 14, + JOY_BUTTON_SDL_MAX = 15, + JOY_BUTTON_MAX = 36, // Android supports up to 36 buttons. +}; + +enum JoyAxisList { + JOY_AXIS_INVALID = -1, + JOY_AXIS_LEFT_X = 0, + JOY_AXIS_LEFT_Y = 1, + JOY_AXIS_RIGHT_X = 2, + JOY_AXIS_RIGHT_Y = 3, + JOY_AXIS_TRIGGER_LEFT = 4, + JOY_AXIS_TRIGGER_RIGHT = 5, + JOY_AXIS_SDL_MAX = 6, + JOY_AXIS_MAX = 10, // OpenVR supports up to 5 Joysticks making a total of 10 axes. }; enum MidiMessageList { @@ -171,7 +110,7 @@ enum MidiMessageList { class InputEvent : public Resource { GDCLASS(InputEvent, Resource); - int device; + int device = 0; protected: static void _bind_methods(); @@ -187,29 +126,29 @@ public: bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false) const; bool is_action_released(const StringName &p_action) const; float get_action_strength(const StringName &p_action) const; + float get_action_raw_strength(const StringName &p_action) const; // To be removed someday, since they do not make sense for all events virtual bool is_pressed() const; virtual bool is_echo() const; - // ...-. - virtual String as_text() const; + virtual String as_text() const = 0; virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; virtual bool is_action_type() const; virtual bool accumulate(const Ref<InputEvent> &p_event) { return false; } - InputEvent(); + + InputEvent() {} }; class InputEventFromWindow : public InputEvent { - GDCLASS(InputEventFromWindow, InputEvent); - int64_t window_id; + int64_t window_id = 0; protected: static void _bind_methods(); @@ -218,34 +157,39 @@ public: void set_window_id(int64_t p_id); int64_t get_window_id() const; - InputEventFromWindow(); + InputEventFromWindow() {} }; class InputEventWithModifiers : public InputEventFromWindow { GDCLASS(InputEventWithModifiers, InputEventFromWindow); - bool shift; - bool alt; + bool store_command = true; + + bool shift = false; + bool alt = false; #ifdef APPLE_STYLE_KEYS union { bool command; - bool meta; //< windows/mac key + bool meta = false; //< windows/mac key }; - bool control; + bool control = false; #else union { bool command; //< windows/mac key - bool control; + bool control = false; }; - bool meta; //< windows/mac key - + bool meta = false; //< windows/mac key #endif protected: static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; public: + void set_store_command(bool p_enabled); + bool is_storing_command() const; + void set_shift(bool p_enabled); bool get_shift() const; @@ -263,27 +207,29 @@ public: void set_modifiers_from_event(const InputEventWithModifiers *event); - InputEventWithModifiers(); + virtual String as_text() const override; + virtual String to_string() override; + + InputEventWithModifiers() {} }; class InputEventKey : public InputEventWithModifiers { - GDCLASS(InputEventKey, InputEventWithModifiers); - bool pressed; /// otherwise release + bool pressed = false; /// otherwise release - uint32_t keycode; ///< check keyboard.h , KeyCode enum, without modifier masks - uint32_t physical_keycode; - uint32_t unicode; ///unicode + uint32_t keycode = 0; ///< check keyboard.h , KeyCode enum, without modifier masks + uint32_t physical_keycode = 0; + uint32_t unicode = 0; ///unicode - bool echo; /// true if this is an echo key + bool echo = false; /// true if this is an echo key protected: static void _bind_methods(); public: void set_pressed(bool p_pressed); - virtual bool is_pressed() const; + virtual bool is_pressed() const override; void set_keycode(uint32_t p_keycode); uint32_t get_keycode() const; @@ -295,26 +241,26 @@ public: uint32_t get_unicode() const; void set_echo(bool p_enable); - virtual bool is_echo() const; + virtual bool is_echo() const override; uint32_t get_keycode_with_modifiers() const; uint32_t get_physical_keycode_with_modifiers() const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; - virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override; - virtual bool is_action_type() const { return true; } + virtual bool is_action_type() const override { return true; } - virtual String as_text() const; + virtual String as_text() const override; + virtual String to_string() override; - InputEventKey(); + InputEventKey() {} }; class InputEventMouse : public InputEventWithModifiers { - GDCLASS(InputEventMouse, InputEventWithModifiers); - int button_mask; + int button_mask = 0; Vector2 pos; Vector2 global_pos; @@ -332,17 +278,16 @@ public: void set_global_position(const Vector2 &p_global_pos); Vector2 get_global_position() const; - InputEventMouse(); + InputEventMouse() {} }; class InputEventMouseButton : public InputEventMouse { - GDCLASS(InputEventMouseButton, InputEventMouse); - float factor; - int button_index; - bool pressed; //otherwise released - bool doubleclick; //last even less than doubleclick time + float factor = 1; + int button_index = 0; + bool pressed = false; //otherwise released + bool doubleclick = false; //last even less than doubleclick time protected: static void _bind_methods(); @@ -355,26 +300,26 @@ public: int get_button_index() const; void set_pressed(bool p_pressed); - virtual bool is_pressed() const; + virtual bool is_pressed() const override; void set_doubleclick(bool p_doubleclick); bool is_doubleclick() const; - virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; - virtual bool is_action_type() const { return true; } - virtual String as_text() const; + virtual bool is_action_type() const override { return true; } + virtual String as_text() const override; + virtual String to_string() override; - InputEventMouseButton(); + InputEventMouseButton() {} }; class InputEventMouseMotion : public InputEventMouse { - GDCLASS(InputEventMouseMotion, InputEventMouse); Vector2 tilt; - float pressure; + float pressure = 0; Vector2 relative; Vector2 speed; @@ -394,19 +339,19 @@ public: void set_speed(const Vector2 &p_speed); Vector2 get_speed() const; - virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual String as_text() const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; + virtual String as_text() const override; + virtual String to_string() override; - virtual bool accumulate(const Ref<InputEvent> &p_event); + virtual bool accumulate(const Ref<InputEvent> &p_event) override; - InputEventMouseMotion(); + InputEventMouseMotion() {} }; class InputEventJoypadMotion : public InputEvent { - GDCLASS(InputEventJoypadMotion, InputEvent); - int axis; ///< Joypad axis - float axis_value; ///< -1 to 1 + int axis = 0; ///< Joypad axis + float axis_value = 0; ///< -1 to 1 protected: static void _bind_methods(); @@ -418,22 +363,23 @@ public: void set_axis_value(float p_value); float get_axis_value() const; - virtual bool is_pressed() const; + virtual bool is_pressed() const override; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; - virtual bool is_action_type() const { return true; } - virtual String as_text() const; + virtual bool is_action_type() const override { return true; } + virtual String as_text() const override; + virtual String to_string() override; - InputEventJoypadMotion(); + InputEventJoypadMotion() {} }; class InputEventJoypadButton : public InputEvent { GDCLASS(InputEventJoypadButton, InputEvent); - int button_index; - bool pressed; - float pressure; //0 to 1 + int button_index = 0; + bool pressed = false; + float pressure = 0; //0 to 1 protected: static void _bind_methods(); @@ -442,25 +388,26 @@ public: int get_button_index() const; void set_pressed(bool p_pressed); - virtual bool is_pressed() const; + virtual bool is_pressed() const override; void set_pressure(float p_pressure); float get_pressure() const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; - virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override; - virtual bool is_action_type() const { return true; } - virtual String as_text() const; + virtual bool is_action_type() const override { return true; } + virtual String as_text() const override; + virtual String to_string() override; - InputEventJoypadButton(); + InputEventJoypadButton() {} }; class InputEventScreenTouch : public InputEventFromWindow { GDCLASS(InputEventScreenTouch, InputEventFromWindow); - int index; + int index = 0; Vector2 pos; - bool pressed; + bool pressed = false; protected: static void _bind_methods(); @@ -473,18 +420,18 @@ public: Vector2 get_position() const; void set_pressed(bool p_pressed); - virtual bool is_pressed() const; + virtual bool is_pressed() const override; - virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual String as_text() const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; + virtual String as_text() const override; + virtual String to_string() override; - InputEventScreenTouch(); + InputEventScreenTouch() {} }; class InputEventScreenDrag : public InputEventFromWindow { - GDCLASS(InputEventScreenDrag, InputEventFromWindow); - int index; + int index = 0; Vector2 pos; Vector2 relative; Vector2 speed; @@ -505,19 +452,19 @@ public: void set_speed(const Vector2 &p_speed); Vector2 get_speed() const; - virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual String as_text() const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; + virtual String as_text() const override; + virtual String to_string() override; - InputEventScreenDrag(); + InputEventScreenDrag() {} }; class InputEventAction : public InputEvent { - GDCLASS(InputEventAction, InputEvent); StringName action; - bool pressed; - float strength; + bool pressed = false; + float strength = 1.0f; protected: static void _bind_methods(); @@ -527,24 +474,24 @@ public: StringName get_action() const; void set_pressed(bool p_pressed); - virtual bool is_pressed() const; + virtual bool is_pressed() const override; void set_strength(float p_strength); float get_strength() const; virtual bool is_action(const StringName &p_action) const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; - virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; - virtual bool is_action_type() const { return true; } - virtual String as_text() const; + virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override; + virtual bool is_action_type() const override { return true; } + virtual String as_text() const override; + virtual String to_string() override; - InputEventAction(); + InputEventAction() {} }; class InputEventGesture : public InputEventWithModifiers { - GDCLASS(InputEventGesture, InputEventWithModifiers); Vector2 pos; @@ -558,9 +505,8 @@ public: }; class InputEventMagnifyGesture : public InputEventGesture { - GDCLASS(InputEventMagnifyGesture, InputEventGesture); - real_t factor; + real_t factor = 1.0; protected: static void _bind_methods(); @@ -569,14 +515,14 @@ public: void set_factor(real_t p_factor); real_t get_factor() const; - virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual String as_text() const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; + virtual String as_text() const override; + virtual String to_string() override; - InputEventMagnifyGesture(); + InputEventMagnifyGesture() {} }; class InputEventPanGesture : public InputEventGesture { - GDCLASS(InputEventPanGesture, InputEventGesture); Vector2 delta; @@ -587,23 +533,24 @@ public: void set_delta(const Vector2 &p_delta); Vector2 get_delta() const; - virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual String as_text() const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; + virtual String as_text() const override; + virtual String to_string() override; - InputEventPanGesture(); + InputEventPanGesture() {} }; class InputEventMIDI : public InputEvent { GDCLASS(InputEventMIDI, InputEvent); - int channel; - int message; - int pitch; - int velocity; - int instrument; - int pressure; - int controller_number; - int controller_value; + int channel = 0; + int message = 0; + int pitch = 0; + int velocity = 0; + int instrument = 0; + int pressure = 0; + int controller_number = 0; + int controller_value = 0; protected: static void _bind_methods(); @@ -633,9 +580,10 @@ public: void set_controller_value(const int p_controller_value); int get_controller_value() const; - virtual String as_text() const; + virtual String as_text() const override; + virtual String to_string() override; - InputEventMIDI(); + InputEventMIDI() {} }; #endif // INPUT_EVENT_H diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 6b6acf062d..53ed925c1f 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,15 +30,15 @@ #include "input_map.h" +#include "core/config/project_settings.h" +#include "core/input/input.h" #include "core/os/keyboard.h" -#include "core/project_settings.h" InputMap *InputMap::singleton = nullptr; int InputMap::ALL_DEVICES = -1; void InputMap::_bind_methods() { - ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action); ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions); ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f)); @@ -49,13 +49,12 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event); ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event); ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events); - ClassDB::bind_method(D_METHOD("get_action_list", "action"), &InputMap::_get_action_list); + ClassDB::bind_method(D_METHOD("action_get_events", "action"), &InputMap::_action_get_events); ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action); - ClassDB::bind_method(D_METHOD("load_from_globals"), &InputMap::load_from_globals); + ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings); } void InputMap::add_action(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action '" + String(p_action) + "'."); input_map[p_action] = Action(); static int last_id = 1; @@ -65,20 +64,18 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) { } void InputMap::erase_action(const StringName &p_action) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map.erase(p_action); } Array InputMap::_get_actions() { - Array ret; List<StringName> actions = get_actions(); - if (actions.empty()) + if (actions.is_empty()) { return ret; + } for (const List<StringName>::Element *E = actions.front(); E; E = E->next()) { - ret.push_back(E->get()); } @@ -86,9 +83,8 @@ Array InputMap::_get_actions() { } List<StringName> InputMap::get_actions() const { - List<StringName> actions = List<StringName>(); - if (input_map.empty()) { + if (input_map.is_empty()) { return actions; } @@ -99,10 +95,10 @@ List<StringName> InputMap::get_actions() const { return actions; } -List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength) const { +List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength) const { + ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { - const Ref<InputEvent> e = E->get(); //if (e.type != Ref<InputEvent>::KEY && e.device != p_event.device) -- unsure about the KEY comparison, why is this here? @@ -110,7 +106,7 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re int device = e->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { - if (e->action_match(p_event, p_pressed, p_strength, p_action.deadzone)) { + if (e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { return E; } } @@ -120,56 +116,59 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re } bool InputMap::has_action(const StringName &p_action) const { - return input_map.has(p_action); } -void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) { +float InputMap::action_get_deadzone(const StringName &p_action) { + ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, "Request for nonexistent InputMap action '" + String(p_action) + "'."); + + return input_map[p_action].deadzone; +} +void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) { ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map[p_action].deadzone = p_deadzone; } void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); - if (_find_event(input_map[p_action], p_event)) - return; //already gots + if (_find_event(input_map[p_action], p_event)) { + return; // Already addded. + } input_map[p_action].inputs.push_back(p_event); } bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); return (_find_event(input_map[p_action], p_event) != nullptr); } void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event); - if (E) + if (E) { input_map[p_action].inputs.erase(E); + if (Input::get_singleton()->is_action_pressed(p_action)) { + Input::get_singleton()->action_release(p_action); + } + } } void InputMap::action_erase_events(const StringName &p_action) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map[p_action].inputs.clear(); } -Array InputMap::_get_action_list(const StringName &p_action) { - +Array InputMap::_action_get_events(const StringName &p_action) { Array ret; - const List<Ref<InputEvent>> *al = get_action_list(p_action); + const List<Ref<InputEvent>> *al = action_get_events(p_action); if (al) { for (const List<Ref<InputEvent>>::Element *E = al->front(); E; E = E->next()) { - ret.push_back(E->get()); } } @@ -177,11 +176,11 @@ Array InputMap::_get_action_list(const StringName &p_action) { return ret; } -const List<Ref<InputEvent>> *InputMap::get_action_list(const StringName &p_action) { - +const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_action) { const Map<StringName, Action>::Element *E = input_map.find(p_action); - if (!E) + if (!E) { return nullptr; + } return &E->get().inputs; } @@ -190,27 +189,35 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName return event_get_action_status(p_event, p_action); } -bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const { +bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength, float *p_raw_strength) const { Map<StringName, Action>::Element *E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { - if (p_pressed != nullptr) + if (p_pressed != nullptr) { *p_pressed = input_event_action->is_pressed(); - if (p_strength != nullptr) + } + if (p_strength != nullptr) { *p_strength = (p_pressed != nullptr && *p_pressed) ? input_event_action->get_strength() : 0.0f; + } return input_event_action->get_action() == p_action; } bool pressed; float strength; - List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, &pressed, &strength); + float raw_strength; + List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, &pressed, &strength, &raw_strength); if (event != nullptr) { - if (p_pressed != nullptr) + if (p_pressed != nullptr) { *p_pressed = pressed; - if (p_strength != nullptr) + } + if (p_strength != nullptr) { *p_strength = strength; + } + if (p_raw_strength != nullptr) { + *p_raw_strength = raw_strength; + } return true; } else { return false; @@ -221,8 +228,7 @@ const Map<StringName, InputMap::Action> &InputMap::get_action_map() const { return input_map; } -void InputMap::load_from_globals() { - +void InputMap::load_from_project_settings() { input_map.clear(); List<PropertyInfo> pinfo; @@ -231,8 +237,9 @@ void InputMap::load_from_globals() { for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { const PropertyInfo &pi = E->get(); - if (!pi.name.begins_with("input/")) + if (!pi.name.begins_with("input/")) { continue; + } String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); @@ -243,15 +250,15 @@ void InputMap::load_from_globals() { add_action(name, deadzone); for (int i = 0; i < events.size(); i++) { Ref<InputEvent> event = events[i]; - if (event.is_null()) + if (event.is_null()) { continue; + } action_add_event(name, event); } } } void InputMap::load_default() { - Ref<InputEventKey> key; add_action("ui_accept"); @@ -332,7 +339,6 @@ void InputMap::load_default() { } InputMap::InputMap() { - ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exist."); singleton = this; } diff --git a/core/input/input_map.h b/core/input/input_map.h index e03bc5fd4f..1646e7fadb 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,10 +32,9 @@ #define INPUT_MAP_H #include "core/input/input_event.h" -#include "core/object.h" +#include "core/object/class_db.h" class InputMap : public Object { - GDCLASS(InputMap, Object); public: @@ -55,9 +54,9 @@ private: mutable Map<StringName, Action> input_map; - List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr) const; + List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; - Array _get_action_list(const StringName &p_action); + Array _action_get_events(const StringName &p_action); Array _get_actions(); protected: @@ -71,18 +70,19 @@ public: void add_action(const StringName &p_action, float p_deadzone = 0.5); void erase_action(const StringName &p_action); + float action_get_deadzone(const StringName &p_action); void action_set_deadzone(const StringName &p_action, float p_deadzone); void action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event); bool action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event); void action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event); void action_erase_events(const StringName &p_action); - const List<Ref<InputEvent>> *get_action_list(const StringName &p_action); + const List<Ref<InputEvent>> *action_get_events(const StringName &p_action); bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const; - bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr) const; + bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; const Map<StringName, Action> &get_action_map() const; - void load_from_globals(); + void load_from_project_settings(); void load_default(); InputMap(); diff --git a/core/int_types.h b/core/int_types.h deleted file mode 100644 index 71caa2202d..0000000000 --- a/core/int_types.h +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************/ -/* int_types.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 INT_TYPES_H -#define INT_TYPES_H - -#ifdef _MSC_VER - -typedef signed __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef signed __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef signed __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - -#else - -#ifdef NO_STDINT_H -typedef unsigned char uint8_t; -typedef signed char int8_t; -typedef unsigned short uint16_t; -typedef signed short int16_t; -typedef unsigned int uint32_t; -typedef signed int int32_t; -typedef long long int64_t; -typedef unsigned long long uint64_t; -#else -#include <stdint.h> -#endif - -#endif // _MSC_VER - -#endif // INT_TYPES_H diff --git a/core/io/compression.cpp b/core/io/compression.cpp index 20c9fdca6f..456023e2a6 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,9 @@ #include "compression.h" +#include "core/config/project_settings.h" #include "core/io/zip_io.h" #include "core/os/copymem.h" -#include "core/project_settings.h" #include "thirdparty/misc/fastlz.h" @@ -40,10 +40,8 @@ #include <zstd.h> int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode) { - switch (p_mode) { case MODE_FASTLZ: { - if (p_src_size < 16) { uint8_t src[16]; zeromem(&src[p_src_size], 16 - p_src_size); @@ -56,7 +54,6 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, } break; case MODE_DEFLATE: case MODE_GZIP: { - int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; z_stream strm; @@ -65,8 +62,9 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, strm.opaque = Z_NULL; int level = p_mode == MODE_DEFLATE ? zlib_level : gzip_level; int err = deflateInit2(&strm, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY); - if (err != Z_OK) + if (err != Z_OK) { return -1; + } strm.avail_in = p_src_size; int aout = deflateBound(&strm, p_src_size); @@ -97,19 +95,17 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, } int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { - switch (p_mode) { case MODE_FASTLZ: { - int ss = p_src_size + p_src_size * 6 / 100; - if (ss < 66) + if (ss < 66) { ss = 66; + } return ss; } break; case MODE_DEFLATE: case MODE_GZIP: { - int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; z_stream strm; @@ -117,14 +113,14 @@ int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { strm.zfree = zipio_free; strm.opaque = Z_NULL; int err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY); - if (err != Z_OK) + if (err != Z_OK) { return -1; + } int aout = deflateBound(&strm, p_src_size); deflateEnd(&strm); return aout; } break; case MODE_ZSTD: { - return ZSTD_compressBound(p_src_size); } break; } @@ -133,10 +129,8 @@ int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { } int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { - switch (p_mode) { case MODE_FASTLZ: { - int ret_size = 0; if (p_dst_max_size < 16) { @@ -150,7 +144,6 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p } break; case MODE_DEFLATE: case MODE_GZIP: { - int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; z_stream strm; @@ -187,8 +180,95 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p ERR_FAIL_V(-1); } +/** + This will handle both Gzip and Deflat streams. It will automatically allocate the output buffer into the provided p_dst_vect Vector. + This is required for compressed data who's final uncompressed size is unknown, as is the case for HTTP response bodies. + This is much slower however than using Compression::decompress because it may result in multiple full copies of the output buffer. +*/ +int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { + int ret; + uint8_t *dst = nullptr; + int out_mark = 0; + z_stream strm; + + ERR_FAIL_COND_V(p_src_size <= 0, Z_DATA_ERROR); + + // This function only supports GZip and Deflate + int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; + ERR_FAIL_COND_V(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO); + + // Initialize the stream + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + + int err = inflateInit2(&strm, window_bits); + ERR_FAIL_COND_V(err != Z_OK, -1); + + // Setup the stream inputs + strm.next_in = (Bytef *)p_src; + strm.avail_in = p_src_size; + + // Ensure the destination buffer is empty + p_dst_vect->resize(0); + + // decompress until deflate stream ends or end of file + do { + // Add another chunk size to the output buffer + // This forces a copy of the whole buffer + p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); + // Get pointer to the actual output buffer + dst = p_dst_vect->ptrw(); + + // Set the stream to the new output stream + // Since it was copied, we need to reset the stream to the new buffer + strm.next_out = &(dst[out_mark]); + strm.avail_out = gzip_chunk; + + // run inflate() on input until output buffer is full and needs to be resized + // or input runs out + do { + ret = inflate(&strm, Z_SYNC_FLUSH); + + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + [[fallthrough]]; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + case Z_STREAM_ERROR: + WARN_PRINT(strm.msg); + (void)inflateEnd(&strm); + p_dst_vect->resize(0); + return ret; + } + } while (strm.avail_out > 0 && strm.avail_in > 0); + + out_mark += gzip_chunk; + + // Encorce max output size + if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { + (void)inflateEnd(&strm); + p_dst_vect->resize(0); + return Z_BUF_ERROR; + } + } while (ret != Z_STREAM_END); + + // If all done successfully, resize the output if it's larger than the actual output + if ((unsigned long)p_dst_vect->size() > strm.total_out) { + p_dst_vect->resize(strm.total_out); + } + + // clean up and return + (void)inflateEnd(&strm); + return Z_OK; +} + int Compression::zlib_level = Z_DEFAULT_COMPRESSION; int Compression::gzip_level = Z_DEFAULT_COMPRESSION; int Compression::zstd_level = 3; bool Compression::zstd_long_distance_matching = false; int Compression::zstd_window_log_size = 27; // ZSTD_WINDOWLOG_LIMIT_DEFAULT +int Compression::gzip_chunk = 16384; diff --git a/core/io/compression.h b/core/io/compression.h index 8354b581fa..cbfed74124 100644 --- a/core/io/compression.h +++ b/core/io/compression.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,16 +31,17 @@ #ifndef COMPRESSION_H #define COMPRESSION_H +#include "core/templates/vector.h" #include "core/typedefs.h" class Compression { - public: static int zlib_level; static int gzip_level; static int zstd_level; static bool zstd_long_distance_matching; static int zstd_window_log_size; + static int gzip_chunk; enum Mode { MODE_FASTLZ, @@ -52,8 +53,9 @@ public: static int compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD); static int get_max_compressed_buffer_size(int p_src_size, Mode p_mode = MODE_ZSTD); static int decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD); + static int decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode); - Compression(); + Compression() {} }; #endif // COMPRESSION_H diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 73230e3a3c..015c1f0d90 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,17 +32,15 @@ #include "core/io/file_access_encrypted.h" #include "core/os/keyboard.h" -#include "core/variant_parser.h" +#include "core/variant/variant_parser.h" PackedStringArray ConfigFile::_get_sections() const { - List<String> s; get_sections(&s); PackedStringArray arr; arr.resize(s.size()); int idx = 0; for (const List<String>::Element *E = s.front(); E; E = E->next()) { - arr.set(idx++, E->get()); } @@ -50,14 +48,12 @@ PackedStringArray ConfigFile::_get_sections() const { } PackedStringArray ConfigFile::_get_section_keys(const String &p_section) const { - List<String> s; get_section_keys(p_section, &s); PackedStringArray arr; arr.resize(s.size()); int idx = 0; for (const List<String>::Element *E = s.front(); E; E = E->next()) { - arr.set(idx++, E->get()); } @@ -65,13 +61,13 @@ PackedStringArray ConfigFile::_get_section_keys(const String &p_section) const { } void ConfigFile::set_value(const String &p_section, const String &p_key, const Variant &p_value) { - if (p_value.get_type() == Variant::NIL) { //erase - if (!values.has(p_section)) + if (!values.has(p_section)) { return; // ? + } values[p_section].erase(p_key); - if (values[p_section].empty()) { + if (values[p_section].is_empty()) { values.erase(p_section); } @@ -83,8 +79,8 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V values[p_section][p_key] = p_value; } } -Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const { +Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const { if (!values.has(p_section) || !values[p_section].has(p_key)) { ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(), vformat("Couldn't find the given section \"%s\" and key \"%s\", and no default was given.", p_section, p_key)); @@ -95,24 +91,23 @@ Variant ConfigFile::get_value(const String &p_section, const String &p_key, Vari } bool ConfigFile::has_section(const String &p_section) const { - return values.has(p_section); } -bool ConfigFile::has_section_key(const String &p_section, const String &p_key) const { - if (!values.has(p_section)) +bool ConfigFile::has_section_key(const String &p_section, const String &p_key) const { + if (!values.has(p_section)) { return false; + } return values[p_section].has(p_key); } void ConfigFile::get_sections(List<String> *r_sections) const { - for (OrderedHashMap<String, OrderedHashMap<String, Variant>>::ConstElement E = values.front(); E; E = E.next()) { r_sections->push_back(E.key()); } } -void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const { +void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const { ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot get keys from nonexistent section \"%s\".", p_section)); for (OrderedHashMap<String, Variant>::ConstElement E = values[p_section].front(); E; E = E.next()) { @@ -121,13 +116,11 @@ void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) } void ConfigFile::erase_section(const String &p_section) { - ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase nonexistent section \"%s\".", p_section)); values.erase(p_section); } void ConfigFile::erase_section_key(const String &p_section, const String &p_key) { - ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase key \"%s\" from nonexistent section \"%s\".", p_key, p_section)); ERR_FAIL_COND_MSG(!values[p_section].has(p_key), vformat("Cannot erase nonexistent key \"%s\" from section \"%s\".", p_key, p_section)); @@ -135,13 +128,13 @@ void ConfigFile::erase_section_key(const String &p_section, const String &p_key) } Error ConfigFile::save(const String &p_path) { - Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); if (err) { - if (file) + if (file) { memdelete(file); + } return err; } @@ -149,12 +142,12 @@ Error ConfigFile::save(const String &p_path) { } Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_WRITE_AES256); @@ -167,12 +160,12 @@ Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_ } Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_WRITE_AES256); @@ -186,15 +179,13 @@ Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass } Error ConfigFile::_internal_save(FileAccess *file) { - for (OrderedHashMap<String, OrderedHashMap<String, Variant>>::Element E = values.front(); E; E = E.next()) { - - if (E != values.front()) + if (E != values.front()) { file->store_string("\n"); + } file->store_string("[" + E.key() + "]\n\n"); for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) { - String vstr; VariantWriter::write_to_string(F.get(), vstr); file->store_string(F.key() + "=" + vstr + "\n"); @@ -207,23 +198,23 @@ Error ConfigFile::_internal_save(FileAccess *file) { } Error ConfigFile::load(const String &p_path) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - if (!f) + if (!f) { return err; + } return _internal_load(p_path, f); } Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_READ); @@ -236,12 +227,12 @@ Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_ } Error ConfigFile::load_encrypted_pass(const String &p_path, const String &p_pass) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_READ); @@ -255,7 +246,6 @@ Error ConfigFile::load_encrypted_pass(const String &p_path, const String &p_pass } Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { - VariantParser::StreamFile stream; stream.f = f; @@ -267,14 +257,12 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { } Error ConfigFile::parse(const String &p_data) { - VariantParser::StreamString stream; stream.s = p_data; return _parse("<string>", &stream); } Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) { - String assign; Variant value; VariantParser::Tag next_tag; @@ -285,7 +273,6 @@ Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) String section; while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); @@ -309,7 +296,6 @@ Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) } void ConfigFile::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_value", "section", "key", "value"), &ConfigFile::set_value); ClassDB::bind_method(D_METHOD("get_value", "section", "key", "default"), &ConfigFile::get_value, DEFVAL(Variant())); diff --git a/core/io/config_file.h b/core/io/config_file.h index 39fc2ab412..386d304f07 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,13 +31,12 @@ #ifndef CONFIG_FILE_H #define CONFIG_FILE_H -#include "core/ordered_hash_map.h" +#include "core/object/reference.h" #include "core/os/file_access.h" -#include "core/reference.h" -#include "core/variant_parser.h" +#include "core/templates/ordered_hash_map.h" +#include "core/variant/variant_parser.h" class ConfigFile : public Reference { - GDCLASS(ConfigFile, Reference); OrderedHashMap<String, OrderedHashMap<String, Variant>> values; diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp index 5bda06e5b9..288b2efe0e 100644 --- a/core/io/dtls_server.cpp +++ b/core/io/dtls_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,15 +29,18 @@ /*************************************************************************/ #include "dtls_server.h" + +#include "core/config/project_settings.h" #include "core/os/file_access.h" -#include "core/project_settings.h" DTLSServer *(*DTLSServer::_create)() = nullptr; bool DTLSServer::available = false; DTLSServer *DTLSServer::create() { - - return _create(); + if (_create) { + return _create(); + } + return nullptr; } bool DTLSServer::is_available() { @@ -45,10 +48,6 @@ bool DTLSServer::is_available() { } void DTLSServer::_bind_methods() { - ClassDB::bind_method(D_METHOD("setup", "key", "certificate", "chain"), &DTLSServer::setup, DEFVAL(Ref<X509Certificate>())); ClassDB::bind_method(D_METHOD("take_connection", "udp_peer"), &DTLSServer::take_connection); } - -DTLSServer::DTLSServer() { -} diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h index 7b08138f7f..92b6caf508 100644 --- a/core/io/dtls_server.h +++ b/core/io/dtls_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -51,7 +51,7 @@ public: virtual void stop() = 0; virtual Ref<PacketPeerDTLS> take_connection(Ref<PacketPeerUDP> p_peer) = 0; - DTLSServer(); + DTLSServer() {} }; #endif // DTLS_SERVER_H diff --git a/core/io/file_access_buffered.cpp b/core/io/file_access_buffered.cpp deleted file mode 100644 index ab0fb3943c..0000000000 --- a/core/io/file_access_buffered.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/*************************************************************************/ -/* file_access_buffered.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 "file_access_buffered.h" - -#include "core/error_macros.h" - -Error FileAccessBuffered::set_error(Error p_error) const { - - return (last_error = p_error); -} - -void FileAccessBuffered::set_cache_size(int p_size) { - - cache_size = p_size; -} - -int FileAccessBuffered::get_cache_size() { - - return cache_size; -} - -int FileAccessBuffered::cache_data_left() const { - - if (file.offset >= file.size) { - return 0; - } - - if (cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size()) { - - return read_data_block(file.offset, cache_size); - } - - return cache.buffer.size() - (file.offset - cache.offset); -} - -void FileAccessBuffered::seek(size_t p_position) { - - file.offset = p_position; -} - -void FileAccessBuffered::seek_end(int64_t p_position) { - - file.offset = file.size + p_position; -} - -size_t FileAccessBuffered::get_position() const { - - return file.offset; -} - -size_t FileAccessBuffered::get_len() const { - - return file.size; -} - -bool FileAccessBuffered::eof_reached() const { - - return file.offset > file.size; -} - -uint8_t FileAccessBuffered::get_8() const { - - ERR_FAIL_COND_V_MSG(!file.open, 0, "Can't get data, when file is not opened."); - - uint8_t byte = 0; - if (cache_data_left() >= 1) { - - byte = cache.buffer[file.offset - cache.offset]; - } - - ++file.offset; - - return byte; -} - -int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { - - ERR_FAIL_COND_V_MSG(!file.open, -1, "Can't get buffer, when file is not opened."); - - if (p_length > cache_size) { - - int total_read = 0; - - if (!(cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size())) { - - int size = (cache.buffer.size() - (file.offset - cache.offset)); - size = size - (size % 4); - //const uint8_t* read = cache.buffer.ptr(); - //memcpy(p_dest, read.ptr() + (file.offset - cache.offset), size); - memcpy(p_dest, cache.buffer.ptr() + (file.offset - cache.offset), size); - p_dest += size; - p_length -= size; - file.offset += size; - total_read += size; - } - - int err = read_data_block(file.offset, p_length, p_dest); - if (err >= 0) { - total_read += err; - file.offset += err; - } - - return total_read; - } - - int to_read = p_length; - int total_read = 0; - while (to_read > 0) { - - int left = cache_data_left(); - if (left == 0) { - file.offset += to_read; - return total_read; - } - if (left < 0) { - return left; - } - - int r = MIN(left, to_read); - //const uint8_t* read = cache.buffer.ptr(); - //memcpy(p_dest+total_read, &read.ptr()[file.offset - cache.offset], r); - memcpy(p_dest + total_read, cache.buffer.ptr() + (file.offset - cache.offset), r); - - file.offset += r; - total_read += r; - to_read -= r; - } - - return p_length; -} - -bool FileAccessBuffered::is_open() const { - - return file.open; -} - -Error FileAccessBuffered::get_error() const { - - return last_error; -} - -FileAccessBuffered::FileAccessBuffered() { - - cache_size = DEFAULT_CACHE_SIZE; -} - -FileAccessBuffered::~FileAccessBuffered() { -} diff --git a/core/io/file_access_buffered_fa.h b/core/io/file_access_buffered_fa.h deleted file mode 100644 index 6ec77d503b..0000000000 --- a/core/io/file_access_buffered_fa.h +++ /dev/null @@ -1,159 +0,0 @@ -/*************************************************************************/ -/* file_access_buffered_fa.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 FILE_ACCESS_BUFFERED_FA_H -#define FILE_ACCESS_BUFFERED_FA_H - -#include "core/io/file_access_buffered.h" - -template <class T> -class FileAccessBufferedFA : public FileAccessBuffered { - - T f; - - int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const { - - ERR_FAIL_COND_V_MSG(!f.is_open(), -1, "Can't read data block when file is not opened."); - - ((T *)&f)->seek(p_offset); - - if (p_dest) { - - f.get_buffer(p_dest, p_size); - return p_size; - - } else { - - cache.offset = p_offset; - cache.buffer.resize(p_size); - - // on Vector - //uint8_t* write = cache.buffer.ptrw(); - //f.get_buffer(write.ptrw(), p_size); - - // on vector - f.get_buffer(cache.buffer.ptrw(), p_size); - - return p_size; - }; - }; - - static FileAccess *create() { - - return memnew(FileAccessBufferedFA<T>()); - }; - -protected: - virtual void _set_access_type(AccessType p_access) { - f._set_access_type(p_access); - FileAccessBuffered::_set_access_type(p_access); - }; - -public: - void flush() { - - f.flush(); - }; - - void store_8(uint8_t p_dest) { - - f.store_8(p_dest); - }; - - void store_buffer(const uint8_t *p_src, int p_length) { - - f.store_buffer(p_src, p_length); - }; - - bool file_exists(const String &p_name) { - - return f.file_exists(p_name); - }; - - Error _open(const String &p_path, int p_mode_flags) { - - close(); - - Error ret = f._open(p_path, p_mode_flags); - if (ret != OK) - return ret; - //ERR_FAIL_COND_V( ret != OK, ret ); - - file.size = f.get_len(); - file.offset = 0; - file.open = true; - file.name = p_path; - file.access_flags = p_mode_flags; - - cache.buffer.resize(0); - cache.offset = 0; - - return set_error(OK); - }; - - void close() { - - f.close(); - - file.offset = 0; - file.size = 0; - file.open = false; - file.name = ""; - - cache.buffer.resize(0); - cache.offset = 0; - set_error(OK); - }; - - /* - static void make_default() { - FileAccess::create_func = FileAccessBufferedFA<T>::create; - }; - */ - - virtual uint64_t _get_modified_time(const String &p_file) { - - return f._get_modified_time(p_file); - } - - virtual uint32_t _get_unix_permissions(const String &p_file) { - return f._get_unix_permissions(p_file); - } - - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { - return f._set_unix_permissions(p_file, p_permissions); - } - - FileAccessBufferedFA(){ - - }; -}; - -#endif // FILE_ACCESS_BUFFERED_FA_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index c76142d22d..9ec2b27e88 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,16 +30,16 @@ #include "file_access_compressed.h" -#include "core/print_string.h" +#include "core/string/print_string.h" void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, int p_block_size) { - magic = p_magic.ascii().get_data(); - if (magic.length() > 4) + if (magic.length() > 4) { magic = magic.substr(0, 4); - else { - while (magic.length() < 4) + } else { + while (magic.length() < 4) { magic += " "; + } } cmode = p_mode; @@ -59,7 +59,6 @@ void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_ } Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { - f = p_base; cmode = (Compression::Mode)f->get_32(); block_size = f->get_32(); @@ -72,7 +71,6 @@ Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { int acc_ofs = f->get_position() + bc * 4; int max_bs = 0; for (int i = 0; i < bc; i++) { - ReadBlock rb; rb.offset = acc_ofs; rb.csize = f->get_32(); @@ -98,11 +96,11 @@ Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { } Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { - ERR_FAIL_COND_V(p_mode_flags == READ_WRITE, ERR_UNAVAILABLE); - if (f) + if (f) { close(); + } Error err; f = FileAccess::open(p_path, p_mode_flags, &err); @@ -114,7 +112,6 @@ Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { } if (p_mode_flags & WRITE) { - buffer.clear(); writing = true; write_pos = 0; @@ -125,7 +122,6 @@ Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { //don't store anything else unless it's done saving! } else { - char rmagic[5]; f->get_buffer((uint8_t *)rmagic, 4); rmagic[4] = 0; @@ -138,10 +134,11 @@ Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { return OK; } -void FileAccessCompressed::close() { - if (!f) +void FileAccessCompressed::close() { + if (!f) { return; + } if (writing) { //save block table and all compressed blocks @@ -159,7 +156,6 @@ void FileAccessCompressed::close() { Vector<int> block_sizes; for (int i = 0; i < bc; i++) { - int bl = i == (bc - 1) ? write_max % block_size : block_size; uint8_t *bp = &write_ptr[i * block_size]; @@ -172,15 +168,15 @@ void FileAccessCompressed::close() { } f->seek(16); //ok write block sizes - for (int i = 0; i < bc; i++) + for (int i = 0; i < bc; i++) { f->store_32(block_sizes[i]); + } f->seek_end(); f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //magic at the end too buffer.clear(); } else { - comp_buffer.clear(); buffer.clear(); read_blocks.clear(); @@ -191,21 +187,17 @@ void FileAccessCompressed::close() { } bool FileAccessCompressed::is_open() const { - return f != nullptr; } void FileAccessCompressed::seek(size_t p_position) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); if (writing) { - ERR_FAIL_COND(p_position > write_max); write_pos = p_position; } else { - ERR_FAIL_COND(p_position > read_total); if (p_position == read_total) { at_end = true; @@ -214,7 +206,6 @@ void FileAccessCompressed::seek(size_t p_position) { read_eof = false; int block_idx = p_position / block_size; if (block_idx != read_block) { - read_block = block_idx; f->seek(read_blocks[read_block].offset); f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize); @@ -228,32 +219,26 @@ void FileAccessCompressed::seek(size_t p_position) { } void FileAccessCompressed::seek_end(int64_t p_position) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); if (writing) { - seek(write_max + p_position); } else { - seek(read_total + p_position); } } -size_t FileAccessCompressed::get_position() const { +size_t FileAccessCompressed::get_position() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); if (writing) { - return write_pos; } else { - return read_block * block_size + read_pos; } } -size_t FileAccessCompressed::get_len() const { +size_t FileAccessCompressed::get_len() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); if (writing) { - return write_max; } else { return read_total; @@ -261,7 +246,6 @@ size_t FileAccessCompressed::get_len() const { } bool FileAccessCompressed::eof_reached() const { - ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); if (writing) { return false; @@ -271,7 +255,6 @@ bool FileAccessCompressed::eof_reached() const { } uint8_t FileAccessCompressed::get_8() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); @@ -301,8 +284,8 @@ uint8_t FileAccessCompressed::get_8() const { return ret; } -int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { +int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); @@ -312,7 +295,6 @@ int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { } for (int i = 0; i < p_length; i++) { - p_dst[i] = read_ptr[read_pos]; read_pos++; if (read_pos >= read_block_size) { @@ -328,8 +310,9 @@ int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { } else { read_block--; at_end = true; - if (i < p_length - 1) + if (i < p_length - 1) { read_eof = true; + } return i; } } @@ -339,46 +322,45 @@ int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessCompressed::get_error() const { - return read_eof ? ERR_FILE_EOF : OK; } void FileAccessCompressed::flush() { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); - ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); // compressed files keep data in memory till close() } void FileAccessCompressed::store_8(uint8_t p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); - ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); WRITE_FIT(1); write_ptr[write_pos++] = p_dest; } bool FileAccessCompressed::file_exists(const String &p_name) { - FileAccess *fa = FileAccess::open(p_name, FileAccess::READ); - if (!fa) + if (!fa) { return false; + } memdelete(fa); return true; } uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { - - if (f) + if (f) { return f->get_modified_time(p_file); - else + } else { return 0; + } } uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) { - if (f) + if (f) { return f->_get_unix_permissions(p_file); + } return 0; } @@ -389,27 +371,8 @@ Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t return FAILED; } -FileAccessCompressed::FileAccessCompressed() : - cmode(Compression::MODE_ZSTD), - writing(false), - write_ptr(nullptr), - write_buffer_size(0), - write_max(0), - block_size(0), - read_eof(false), - at_end(false), - read_ptr(nullptr), - read_block(0), - read_block_count(0), - read_block_size(0), - read_pos(0), - read_total(0), - magic("GCMP"), - f(nullptr) { -} - FileAccessCompressed::~FileAccessCompressed() { - - if (f) + if (f) { close(); + } } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 0bb311faa8..118d05ea57 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -35,16 +35,15 @@ #include "core/os/file_access.h" class FileAccessCompressed : public FileAccess { - - Compression::Mode cmode; - bool writing; - uint32_t write_pos; - uint8_t *write_ptr; - uint32_t write_buffer_size; - uint32_t write_max; - uint32_t block_size; - mutable bool read_eof; - mutable bool at_end; + Compression::Mode cmode = Compression::MODE_ZSTD; + bool writing = false; + uint32_t write_pos = 0; + uint8_t *write_ptr = nullptr; + uint32_t write_buffer_size = 0; + uint32_t write_max = 0; + uint32_t block_size = 0; + mutable bool read_eof = false; + mutable bool at_end = false; struct ReadBlock { int csize; @@ -52,17 +51,17 @@ class FileAccessCompressed : public FileAccess { }; mutable Vector<uint8_t> comp_buffer; - uint8_t *read_ptr; - mutable int read_block; - int read_block_count; - mutable int read_block_size; - mutable int read_pos; + uint8_t *read_ptr = nullptr; + mutable int read_block = 0; + int read_block_count = 0; + mutable int read_block_size = 0; + mutable int read_pos = 0; Vector<ReadBlock> read_blocks; - uint32_t read_total; + uint32_t read_total = 0; - String magic; + String magic = "GCMP"; mutable Vector<uint8_t> buffer; - FileAccess *f; + FileAccess *f = nullptr; public: void configure(const String &p_magic, Compression::Mode p_mode = Compression::MODE_ZSTD, int p_block_size = 4096); @@ -94,7 +93,7 @@ public: virtual uint32_t _get_unix_permissions(const String &p_file); virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); - FileAccessCompressed(); + FileAccessCompressed() {} virtual ~FileAccessCompressed(); }; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index a5b3807789..8b4c57ce64 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,61 +32,59 @@ #include "core/crypto/crypto_core.h" #include "core/os/copymem.h" -#include "core/print_string.h" -#include "core/variant.h" +#include "core/string/print_string.h" +#include "core/variant/variant.h" #include <stdio.h> -#define COMP_MAGIC 0x43454447 - -Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode) { - +Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) { ERR_FAIL_COND_V_MSG(file != nullptr, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open."); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); pos = 0; eofed = false; + use_magic = p_with_magic; if (p_mode == MODE_WRITE_AES256) { - data.clear(); writing = true; file = p_base; - mode = p_mode; key = p_key; } else if (p_mode == MODE_READ) { - writing = false; key = p_key; - uint32_t magic = p_base->get_32(); - ERR_FAIL_COND_V(magic != COMP_MAGIC, ERR_FILE_UNRECOGNIZED); - mode = Mode(p_base->get_32()); - ERR_FAIL_INDEX_V(mode, MODE_MAX, ERR_FILE_CORRUPT); - ERR_FAIL_COND_V(mode == 0, ERR_FILE_CORRUPT); + if (use_magic) { + uint32_t magic = p_base->get_32(); + ERR_FAIL_COND_V(magic != ENCRYPTED_HEADER_MAGIC, ERR_FILE_UNRECOGNIZED); + } unsigned char md5d[16]; p_base->get_buffer(md5d, 16); length = p_base->get_64(); + + unsigned char iv[16]; + for (int i = 0; i < 16; i++) { + iv[i] = p_base->get_8(); + } + base = p_base->get_position(); ERR_FAIL_COND_V(p_base->get_len() < base + length, ERR_FILE_CORRUPT); uint32_t ds = length; if (ds % 16) { ds += 16 - (ds % 16); } - data.resize(ds); uint32_t blen = p_base->get_buffer(data.ptrw(), ds); ERR_FAIL_COND_V(blen != ds, ERR_FILE_CORRUPT); - CryptoCore::AESContext ctx; - ctx.set_decode_key(key.ptrw(), 256); + { + CryptoCore::AESContext ctx; - for (size_t i = 0; i < ds; i += 16) { - - ctx.decrypt_ecb(&data.write[i], &data.write[i]); + ctx.set_encode_key(key.ptrw(), 256); // Due to the nature of CFB, same key schedule is used for both encryption and decryption! + ctx.decrypt_cfb(ds, iv, data.ptrw(), data.ptrw()); } data.resize(length); @@ -103,13 +101,11 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8 } Error FileAccessEncrypted::open_and_parse_password(FileAccess *p_base, const String &p_key, Mode p_mode) { - String cs = p_key.md5_text(); ERR_FAIL_COND_V(cs.length() != 32, ERR_INVALID_PARAMETER); Vector<uint8_t> key; key.resize(32); for (int i = 0; i < 32; i++) { - key.write[i] = cs[i]; } @@ -117,16 +113,34 @@ Error FileAccessEncrypted::open_and_parse_password(FileAccess *p_base, const Str } Error FileAccessEncrypted::_open(const String &p_path, int p_mode_flags) { - return OK; } + void FileAccessEncrypted::close() { + if (!file) { + return; + } + + _release(); + + file->close(); + memdelete(file); - if (!file) + file = nullptr; +} + +void FileAccessEncrypted::release() { + if (!file) { return; + } - if (writing) { + _release(); + file = nullptr; +} + +void FileAccessEncrypted::_release() { + if (writing) { Vector<uint8_t> compressed; size_t len = data.size(); if (len % 16) { @@ -145,82 +159,72 @@ void FileAccessEncrypted::close() { CryptoCore::AESContext ctx; ctx.set_encode_key(key.ptrw(), 256); - for (size_t i = 0; i < len; i += 16) { - - ctx.encrypt_ecb(&compressed.write[i], &compressed.write[i]); + if (use_magic) { + file->store_32(ENCRYPTED_HEADER_MAGIC); } - file->store_32(COMP_MAGIC); - file->store_32(mode); - file->store_buffer(hash, 16); file->store_64(data.size()); - file->store_buffer(compressed.ptr(), compressed.size()); - file->close(); - memdelete(file); - file = nullptr; - data.clear(); + unsigned char iv[16]; + for (int i = 0; i < 16; i++) { + iv[i] = Math::rand() % 256; + file->store_8(iv[i]); + } - } else { + ctx.encrypt_cfb(len, iv, compressed.ptrw(), compressed.ptrw()); - file->close(); - memdelete(file); + file->store_buffer(compressed.ptr(), compressed.size()); data.clear(); - file = nullptr; } } bool FileAccessEncrypted::is_open() const { - return file != nullptr; } String FileAccessEncrypted::get_path() const { - - if (file) + if (file) { return file->get_path(); - else + } else { return ""; + } } String FileAccessEncrypted::get_path_absolute() const { - - if (file) + if (file) { return file->get_path_absolute(); - else + } else { return ""; + } } void FileAccessEncrypted::seek(size_t p_position) { - - if (p_position > (size_t)data.size()) + if (p_position > (size_t)data.size()) { p_position = data.size(); + } pos = p_position; eofed = false; } void FileAccessEncrypted::seek_end(int64_t p_position) { - seek(data.size() + p_position); } -size_t FileAccessEncrypted::get_position() const { +size_t FileAccessEncrypted::get_position() const { return pos; } -size_t FileAccessEncrypted::get_len() const { +size_t FileAccessEncrypted::get_len() const { return data.size(); } bool FileAccessEncrypted::eof_reached() const { - return eofed; } uint8_t FileAccessEncrypted::get_8() const { - ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (pos >= data.size()) { eofed = true; @@ -231,13 +235,12 @@ uint8_t FileAccessEncrypted::get_8() const { pos++; return b; } -int FileAccessEncrypted::get_buffer(uint8_t *p_dst, int p_length) const { +int FileAccessEncrypted::get_buffer(uint8_t *p_dst, int p_length) const { ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); int to_copy = MIN(p_length, data.size() - pos); for (int i = 0; i < to_copy; i++) { - p_dst[i] = data[pos++]; } @@ -249,25 +252,19 @@ int FileAccessEncrypted::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessEncrypted::get_error() const { - return eofed ? ERR_FILE_EOF : OK; } void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { - - ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); if (pos < data.size()) { - for (int i = 0; i < p_length; i++) { - store_8(p_src[i]); } } else if (pos == data.size()) { - data.resize(pos + p_length); for (int i = 0; i < p_length; i++) { - data.write[pos + i] = p_src[i]; } pos += p_length; @@ -275,14 +272,13 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { } void FileAccessEncrypted::flush() { - ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); // encrypted files keep data in memory till close() } void FileAccessEncrypted::store_8(uint8_t p_dest) { - - ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); if (pos < data.size()) { data.write[pos] = p_dest; @@ -294,21 +290,19 @@ void FileAccessEncrypted::store_8(uint8_t p_dest) { } bool FileAccessEncrypted::file_exists(const String &p_name) { - FileAccess *fa = FileAccess::open(p_name, FileAccess::READ); - if (!fa) + if (!fa) { return false; + } memdelete(fa); return true; } uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) { - return 0; } uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) { - return 0; } @@ -317,17 +311,8 @@ Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t return ERR_UNAVAILABLE; } -FileAccessEncrypted::FileAccessEncrypted() { - - file = nullptr; - pos = 0; - eofed = false; - mode = MODE_MAX; - writing = false; -} - FileAccessEncrypted::~FileAccessEncrypted() { - - if (file) + if (file) { close(); + } } diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 7a9f4ecdd8..969052d04f 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,6 +33,8 @@ #include "core/os/file_access.h" +#define ENCRYPTED_HEADER_MAGIC 0x43454447 + class FileAccessEncrypted : public FileAccess { public: enum Mode { @@ -42,22 +44,25 @@ public: }; private: - Mode mode; Vector<uint8_t> key; - bool writing; - FileAccess *file; - size_t base; - size_t length; + bool writing = false; + FileAccess *file = nullptr; + size_t base = 0; + size_t length = 0; Vector<uint8_t> data; - mutable int pos; - mutable bool eofed; + mutable int pos = 0; + mutable bool eofed = false; + bool use_magic = true; + + void _release(); public: - Error open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode); + Error open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true); Error open_and_parse_password(FileAccess *p_base, const String &p_key, Mode p_mode); virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file virtual void close(); ///< close a file + virtual void release(); ///< finish and keep base file open virtual bool is_open() const; ///< true when file is open virtual String get_path() const; /// returns the path for the current open file @@ -85,7 +90,7 @@ public: virtual uint32_t _get_unix_permissions(const String &p_file); virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); - FileAccessEncrypted(); + FileAccessEncrypted() {} ~FileAccessEncrypted(); }; diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index a2379ce88f..04270de77f 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,44 +30,42 @@ #include "file_access_memory.h" -#include "core/map.h" +#include "core/config/project_settings.h" #include "core/os/copymem.h" #include "core/os/dir_access.h" -#include "core/project_settings.h" +#include "core/templates/map.h" static Map<String, Vector<uint8_t>> *files = nullptr; void FileAccessMemory::register_file(String p_name, Vector<uint8_t> p_data) { - if (!files) { files = memnew((Map<String, Vector<uint8_t>>)); } String name; - if (ProjectSettings::get_singleton()) + if (ProjectSettings::get_singleton()) { name = ProjectSettings::get_singleton()->globalize_path(p_name); - else + } else { name = p_name; + } //name = DirAccess::normalize_path(name); (*files)[name] = p_data; } void FileAccessMemory::cleanup() { - - if (!files) + if (!files) { return; + } memdelete(files); } FileAccess *FileAccessMemory::create() { - return memnew(FileAccessMemory); } bool FileAccessMemory::file_exists(const String &p_name) { - String name = fix_path(p_name); //name = DirAccess::normalize_path(name); @@ -75,7 +73,6 @@ bool FileAccessMemory::file_exists(const String &p_name) { } Error FileAccessMemory::open_custom(const uint8_t *p_data, int p_len) { - data = (uint8_t *)p_data; length = p_len; pos = 0; @@ -83,7 +80,6 @@ Error FileAccessMemory::open_custom(const uint8_t *p_data, int p_len) { } Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { - ERR_FAIL_COND_V(!files, ERR_FILE_NOT_FOUND); String name = fix_path(p_path); @@ -100,46 +96,38 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { } void FileAccessMemory::close() { - data = nullptr; } bool FileAccessMemory::is_open() const { - return data != nullptr; } void FileAccessMemory::seek(size_t p_position) { - ERR_FAIL_COND(!data); pos = p_position; } void FileAccessMemory::seek_end(int64_t p_position) { - ERR_FAIL_COND(!data); pos = length + p_position; } size_t FileAccessMemory::get_position() const { - ERR_FAIL_COND_V(!data, 0); return pos; } size_t FileAccessMemory::get_len() const { - ERR_FAIL_COND_V(!data, 0); return length; } bool FileAccessMemory::eof_reached() const { - return pos > length; } uint8_t FileAccessMemory::get_8() const { - uint8_t ret = 0; if (pos < length) { ret = data[pos]; @@ -150,7 +138,6 @@ uint8_t FileAccessMemory::get_8() const { } int FileAccessMemory::get_buffer(uint8_t *p_dst, int p_length) const { - ERR_FAIL_COND_V(!data, -1); int left = length - pos; @@ -158,7 +145,7 @@ int FileAccessMemory::get_buffer(uint8_t *p_dst, int p_length) const { if (read < p_length) { WARN_PRINT("Reading less data than requested"); - }; + } copymem(p_dst, &data[pos], read); pos += p_length; @@ -167,7 +154,6 @@ int FileAccessMemory::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessMemory::get_error() const { - return pos >= length ? ERR_FILE_EOF : OK; } @@ -176,14 +162,12 @@ void FileAccessMemory::flush() { } void FileAccessMemory::store_8(uint8_t p_byte) { - ERR_FAIL_COND(!data); ERR_FAIL_COND(pos >= length); data[pos++] = p_byte; } void FileAccessMemory::store_buffer(const uint8_t *p_src, int p_length) { - int left = length - pos; int write = MIN(p_length, left); if (write < p_length) { @@ -193,8 +177,3 @@ void FileAccessMemory::store_buffer(const uint8_t *p_src, int p_length) { copymem(&data[pos], p_src, write); pos += p_length; } - -FileAccessMemory::FileAccessMemory() { - - data = nullptr; -} diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 2db14db265..0e3b0ad7b1 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -34,10 +34,9 @@ #include "core/os/file_access.h" class FileAccessMemory : public FileAccess { - - uint8_t *data; - int length; - mutable int pos; + uint8_t *data = nullptr; + int length = 0; + mutable int pos = 0; static FileAccess *create(); @@ -73,7 +72,7 @@ public: virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; } virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; } - FileAccessMemory(); + FileAccessMemory() {} }; #endif // FILE_ACCESS_MEMORY_H diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index a3f307393f..1d9aa846eb 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,10 @@ #include "file_access_network.h" +#include "core/config/project_settings.h" #include "core/io/ip.h" #include "core/io/marshalls.h" #include "core/os/os.h" -#include "core/project_settings.h" //#define DEBUG_PRINT(m_p) print_line(m_p) //#define DEBUG_TIME(m_what) printf("MS: %s - %lli\n",m_what,OS::get_singleton()->get_ticks_usec()); @@ -41,19 +41,16 @@ #define DEBUG_TIME(m_what) void FileAccessNetworkClient::lock_mutex() { - mutex.lock(); lockcount++; } void FileAccessNetworkClient::unlock_mutex() { - lockcount--; mutex.unlock(); } void FileAccessNetworkClient::put_32(int p_32) { - uint8_t buf[4]; encode_uint32(p_32, buf); client->put_data(buf, 4); @@ -61,7 +58,6 @@ void FileAccessNetworkClient::put_32(int p_32) { } void FileAccessNetworkClient::put_64(int64_t p_64) { - uint8_t buf[8]; encode_uint64(p_64, buf); client->put_data(buf, 8); @@ -69,24 +65,20 @@ void FileAccessNetworkClient::put_64(int64_t p_64) { } int FileAccessNetworkClient::get_32() { - uint8_t buf[4]; client->get_data(buf, 4); return decode_uint32(buf); } int64_t FileAccessNetworkClient::get_64() { - uint8_t buf[8]; client->get_data(buf, 8); return decode_uint64(buf); } void FileAccessNetworkClient::_thread_func() { - client->set_no_delay(true); while (!quit) { - DEBUG_PRINT("SEM WAIT - " + itos(sem->get())); sem.wait(); DEBUG_TIME("sem_unlock"); @@ -123,13 +115,12 @@ void FileAccessNetworkClient::_thread_func() { } } - if (accesses.has(id)) + if (accesses.has(id)) { fa = accesses[id]; + } switch (response) { - case FileAccessNetwork::RESPONSE_OPEN: { - DEBUG_TIME("sem_open"); int status = get_32(); if (status != OK) { @@ -143,7 +134,6 @@ void FileAccessNetworkClient::_thread_func() { } break; case FileAccessNetwork::RESPONSE_DATA: { - int64_t offset = get_64(); uint32_t len = get_32(); @@ -151,19 +141,18 @@ void FileAccessNetworkClient::_thread_func() { block.resize(len); client->get_data(block.ptrw(), len); - if (fa) //may have been queued + if (fa) { //may have been queued fa->_set_block(offset, block); + } } break; case FileAccessNetwork::RESPONSE_FILE_EXISTS: { - int status = get_32(); fa->exists_modtime = status != 0; fa->sem.post(); } break; case FileAccessNetwork::RESPONSE_GET_MODTIME: { - uint64_t status = get_64(); fa->exists_modtime = status; fa->sem.post(); @@ -176,14 +165,12 @@ void FileAccessNetworkClient::_thread_func() { } void FileAccessNetworkClient::_thread_func(void *s) { - FileAccessNetworkClient *self = (FileAccessNetworkClient *)s; self->_thread_func(); } Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const String &p_password) { - IP_Address ip; if (p_host.is_valid_ip_address()) { @@ -222,17 +209,11 @@ Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const S FileAccessNetworkClient *FileAccessNetworkClient::singleton = nullptr; FileAccessNetworkClient::FileAccessNetworkClient() { - - thread = nullptr; - quit = false; singleton = this; - last_id = 0; client.instance(); - lockcount = 0; } FileAccessNetworkClient::~FileAccessNetworkClient() { - if (thread) { quit = true; sem.post(); @@ -242,7 +223,6 @@ FileAccessNetworkClient::~FileAccessNetworkClient() { } void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) { - int page = p_offset / page_size; ERR_FAIL_INDEX(page, pages.size()); if (page < pages.size() - 1) { @@ -264,11 +244,11 @@ void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) } void FileAccessNetwork::_respond(size_t p_len, Error p_status) { - DEBUG_PRINT("GOT RESPONSE - len: " + itos(p_len) + " status: " + itos(p_status)); response = p_status; - if (response != OK) + if (response != OK) { return; + } opened = true; total_size = p_len; int pc = ((total_size - 1) / page_size) + 1; @@ -276,10 +256,10 @@ void FileAccessNetwork::_respond(size_t p_len, Error p_status) { } Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) { - ERR_FAIL_COND_V(p_mode_flags != READ, ERR_UNAVAILABLE); - if (opened) + if (opened) { close(); + } FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; DEBUG_PRINT("open: " + p_path); @@ -311,9 +291,9 @@ Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) { } void FileAccessNetwork::close() { - - if (!opened) + if (!opened) { return; + } FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; @@ -325,13 +305,12 @@ void FileAccessNetwork::close() { opened = false; nc->unlock_mutex(); } -bool FileAccessNetwork::is_open() const { +bool FileAccessNetwork::is_open() const { return opened; } void FileAccessNetwork::seek(size_t p_position) { - ERR_FAIL_COND_MSG(!opened, "File must be opened before use."); eof_flag = p_position > total_size; @@ -343,39 +322,35 @@ void FileAccessNetwork::seek(size_t p_position) { } void FileAccessNetwork::seek_end(int64_t p_position) { - seek(total_size + p_position); } -size_t FileAccessNetwork::get_position() const { +size_t FileAccessNetwork::get_position() const { ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use."); return pos; } -size_t FileAccessNetwork::get_len() const { +size_t FileAccessNetwork::get_len() const { ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use."); return total_size; } bool FileAccessNetwork::eof_reached() const { - ERR_FAIL_COND_V_MSG(!opened, false, "File must be opened before use."); return eof_flag; } uint8_t FileAccessNetwork::get_8() const { - uint8_t v; get_buffer(&v, 1); return v; } void FileAccessNetwork::_queue_page(int p_page) const { - - if (p_page >= pages.size()) + if (p_page >= pages.size()) { return; - if (pages[p_page].buffer.empty() && !pages[p_page].queued) { - + } + if (pages[p_page].buffer.is_empty() && !pages[p_page].queued) { FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; { MutexLock lock(nc->blockrequest_mutex); @@ -394,7 +369,6 @@ void FileAccessNetwork::_queue_page(int p_page) const { } int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { - //bool eof=false; if (pos + p_length > total_size) { eof_flag = true; @@ -408,15 +382,13 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { uint8_t *buff = last_page_buff; for (int i = 0; i < p_length; i++) { - int page = pos / page_size; if (page != last_page) { buffer_mutex.lock(); - if (pages[page].buffer.empty()) { + if (pages[page].buffer.is_empty()) { waiting_on_page = page; for (int j = 0; j < read_ahead; j++) { - _queue_page(page + j); } buffer_mutex.unlock(); @@ -424,9 +396,7 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { page_sem.wait(); DEBUG_PRINT("done"); } else { - for (int j = 0; j < read_ahead; j++) { - _queue_page(page + j); } //queue pages @@ -446,7 +416,6 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessNetwork::get_error() const { - return pos == total_size ? ERR_FILE_EOF : OK; } @@ -455,12 +424,10 @@ void FileAccessNetwork::flush() { } void FileAccessNetwork::store_8(uint8_t p_dest) { - ERR_FAIL(); } bool FileAccessNetwork::file_exists(const String &p_path) { - FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); nc->put_32(id); @@ -477,7 +444,6 @@ bool FileAccessNetwork::file_exists(const String &p_path) { } uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) { - FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); nc->put_32(id); @@ -504,7 +470,6 @@ Error FileAccessNetwork::_set_unix_permissions(const String &p_file, uint32_t p_ } void FileAccessNetwork::configure() { - GLOBAL_DEF("network/remote_fs/page_size", 65536); ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_size", PropertyInfo(Variant::INT, "network/remote_fs/page_size", PROPERTY_HINT_RANGE, "1,65536,1,or_greater")); //is used as denominator and can't be zero GLOBAL_DEF("network/remote_fs/page_read_ahead", 4); @@ -512,10 +477,6 @@ void FileAccessNetwork::configure() { } FileAccessNetwork::FileAccessNetwork() { - - eof_flag = false; - opened = false; - pos = 0; FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); id = nc->last_id++; @@ -523,13 +484,9 @@ FileAccessNetwork::FileAccessNetwork() { nc->unlock_mutex(); page_size = GLOBAL_GET("network/remote_fs/page_size"); read_ahead = GLOBAL_GET("network/remote_fs/page_read_ahead"); - last_activity_val = 0; - waiting_on_page = -1; - last_page = -1; } FileAccessNetwork::~FileAccessNetwork() { - close(); FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index 7f664b46f7..6aec2869fc 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -39,9 +39,7 @@ class FileAccessNetwork; class FileAccessNetworkClient { - struct BlockRequest { - int id; uint64_t offset; int size; @@ -50,13 +48,14 @@ class FileAccessNetworkClient { List<BlockRequest> block_requests; Semaphore sem; - Thread *thread; - bool quit; + Thread *thread = nullptr; + bool quit = false; Mutex mutex; Mutex blockrequest_mutex; Map<int, FileAccessNetwork *> accesses; Ref<StreamPeerTCP> client; - int last_id; + int last_id = 0; + int lockcount = 0; Vector<uint8_t> block; @@ -67,7 +66,6 @@ class FileAccessNetworkClient { void put_64(int64_t p_64); int get_32(); int64_t get_64(); - int lockcount; void lock_mutex(); void unlock_mutex(); @@ -84,31 +82,26 @@ public: }; class FileAccessNetwork : public FileAccess { - Semaphore sem; Semaphore page_sem; Mutex buffer_mutex; - bool opened; + bool opened = false; size_t total_size; - mutable size_t pos; + mutable size_t pos = 0; int id; - mutable bool eof_flag; - mutable int last_page; - mutable uint8_t *last_page_buff; + mutable bool eof_flag = false; + mutable int last_page = -1; + mutable uint8_t *last_page_buff = nullptr; int page_size; int read_ahead; - mutable int waiting_on_page; - mutable int last_activity_val; + mutable int waiting_on_page = -1; + struct Page { - int activity; - bool queued; + int activity = 0; + bool queued = false; Vector<uint8_t> buffer; - Page() { - activity = 0; - queued = false; - } }; mutable Vector<Page> pages; diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 0a7dee9444..faf4fca14f 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,40 +30,41 @@ #include "file_access_pack.h" +#include "core/io/file_access_encrypted.h" +#include "core/object/script_language.h" #include "core/version.h" #include <stdio.h> -Error PackedData::add_pack(const String &p_path, bool p_replace_files) { - +Error PackedData::add_pack(const String &p_path, bool p_replace_files, size_t p_offset) { for (int i = 0; i < sources.size(); i++) { - - if (sources[i]->try_open_pack(p_path, p_replace_files)) { - + if (sources[i]->try_open_pack(p_path, p_replace_files, p_offset)) { return OK; - }; - }; + } + } return ERR_FILE_UNRECOGNIZED; -}; - -void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files) { +} +void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) { PathMD5 pmd5(path.md5_buffer()); - //printf("adding path %ls, %lli, %lli\n", path.c_str(), pmd5.a, pmd5.b); + //printf("adding path %s, %lli, %lli\n", path.utf8().get_data(), pmd5.a, pmd5.b); bool exists = files.has(pmd5); PackedFile pf; + pf.encrypted = p_encrypted; pf.pack = pkg_path; pf.offset = ofs; pf.size = size; - for (int i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { pf.md5[i] = p_md5[i]; + } pf.src = p_src; - if (!exists || p_replace_files) + if (!exists || p_replace_files) { files[pmd5] = pf; + } if (!exists) { //search for dir @@ -75,9 +76,7 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o Vector<String> ds = p.get_base_dir().split("/"); for (int j = 0; j < ds.size(); j++) { - if (!cd->subdirs.has(ds[j])) { - PackedDir *pd = memnew(PackedDir); pd->name = ds[j]; pd->parent = cd; @@ -90,40 +89,35 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o } String filename = path.get_file(); // Don't add as a file if the path points to a directory - if (!filename.empty()) { + if (!filename.is_empty()) { cd->files.insert(filename); } } } void PackedData::add_pack_source(PackSource *p_source) { - if (p_source != nullptr) { sources.push_back(p_source); } -}; +} PackedData *PackedData::singleton = nullptr; PackedData::PackedData() { - singleton = this; root = memnew(PackedDir); - root->parent = nullptr; - disabled = false; add_pack_source(memnew(PackedSourcePCK)); } void PackedData::_free_packed_dirs(PackedDir *p_dir) { - - for (Map<String, PackedDir *>::Element *E = p_dir->subdirs.front(); E; E = E->next()) + for (Map<String, PackedDir *>::Element *E = p_dir->subdirs.front(); E; E = E->next()) { _free_packed_dirs(E->get()); + } memdelete(p_dir); } PackedData::~PackedData() { - for (int i = 0; i < sources.size(); i++) { memdelete(sources[i]); } @@ -132,21 +126,29 @@ PackedData::~PackedData() { ////////////////////////////////////////////////////////////////// -bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) { - +bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) + if (!f) { return false; + } + + f->seek(p_offset); uint32_t magic = f->get_32(); if (magic != PACK_HEADER_MAGIC) { + // loading with offset feature not supported for self contained exe files + if (p_offset != 0) { + f->close(); + memdelete(f); + ERR_FAIL_V_MSG(false, "Loading self-contained executable with offset not supported."); + } + //maybe at the end.... self contained exe f->seek_end(); f->seek(f->get_position() - 4); magic = f->get_32(); if (magic != PACK_HEADER_MAGIC) { - f->close(); memdelete(f); return false; @@ -158,7 +160,6 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) magic = f->get_32(); if (magic != PACK_HEADER_MAGIC) { - f->close(); memdelete(f); return false; @@ -181,6 +182,11 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) ERR_FAIL_V_MSG(false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "."); } + uint32_t pack_flags = f->get_32(); + uint64_t file_base = f->get_64(); + + bool enc_directory = (pack_flags & PACK_DIR_ENCRYPTED); + for (int i = 0; i < 16; i++) { //reserved f->get_32(); @@ -188,8 +194,31 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) int file_count = f->get_32(); - for (int i = 0; i < file_count; i++) { + if (enc_directory) { + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + if (!fae) { + f->close(); + memdelete(f); + ERR_FAIL_V_MSG(false, "Can't open encrypted pack directory."); + } + + Vector<uint8_t> key; + key.resize(32); + for (int i = 0; i < key.size(); i++) { + key.write[i] = script_encryption_key[i]; + } + Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); + if (err) { + f->close(); + memdelete(f); + memdelete(fae); + ERR_FAIL_V_MSG(false, "Can't open encrypted pack directory."); + } + f = fae; + } + + for (int i = 0; i < file_count; i++) { uint32_t sl = f->get_32(); CharString cs; cs.resize(sl + 1); @@ -199,72 +228,67 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) String path; path.parse_utf8(cs.ptr()); - uint64_t ofs = f->get_64(); + uint64_t ofs = file_base + f->get_64(); uint64_t size = f->get_64(); uint8_t md5[16]; f->get_buffer(md5, 16); - PackedData::get_singleton()->add_path(p_path, path, ofs, size, md5, this, p_replace_files); - }; + uint32_t flags = f->get_32(); + + PackedData::get_singleton()->add_path(p_path, path, ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED)); + } f->close(); memdelete(f); return true; -}; +} FileAccess *PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) { - return memnew(FileAccessPack(p_path, *p_file)); -}; +} ////////////////////////////////////////////////////////////////// Error FileAccessPack::_open(const String &p_path, int p_mode_flags) { - ERR_FAIL_V(ERR_UNAVAILABLE); return ERR_UNAVAILABLE; } void FileAccessPack::close() { - f->close(); } bool FileAccessPack::is_open() const { - return f->is_open(); } void FileAccessPack::seek(size_t p_position) { - if (p_position > pf.size) { eof = true; } else { eof = false; } - f->seek(pf.offset + p_position); + f->seek(off + p_position); pos = p_position; } -void FileAccessPack::seek_end(int64_t p_position) { +void FileAccessPack::seek_end(int64_t p_position) { seek(pf.size + p_position); } -size_t FileAccessPack::get_position() const { +size_t FileAccessPack::get_position() const { return pos; } -size_t FileAccessPack::get_len() const { +size_t FileAccessPack::get_len() const { return pf.size; } bool FileAccessPack::eof_reached() const { - return eof; } uint8_t FileAccessPack::get_8() const { - if (pos >= pf.size) { eof = true; return 0; @@ -275,9 +299,9 @@ uint8_t FileAccessPack::get_8() const { } int FileAccessPack::get_buffer(uint8_t *p_dst, int p_length) const { - - if (eof) + if (eof) { return 0; + } uint64_t to_read = p_length; if (to_read + pos > pf.size) { @@ -287,8 +311,9 @@ int FileAccessPack::get_buffer(uint8_t *p_dst, int p_length) const { pos += p_length; - if (to_read <= 0) + if (to_read <= 0) { return 0; + } f->get_buffer(p_dst, to_read); return to_read; @@ -300,46 +325,65 @@ void FileAccessPack::set_endian_swap(bool p_swap) { } Error FileAccessPack::get_error() const { - - if (eof) + if (eof) { return ERR_FILE_EOF; + } return OK; } void FileAccessPack::flush() { - ERR_FAIL(); } void FileAccessPack::store_8(uint8_t p_dest) { - ERR_FAIL(); } void FileAccessPack::store_buffer(const uint8_t *p_src, int p_length) { - ERR_FAIL(); } bool FileAccessPack::file_exists(const String &p_name) { - return false; } FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) : pf(p_file), f(FileAccess::open(pf.pack, FileAccess::READ)) { - ERR_FAIL_COND_MSG(!f, "Can't open pack-referenced file '" + String(pf.pack) + "'."); f->seek(pf.offset); + off = pf.offset; + + if (pf.encrypted) { + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + if (!fae) { + ERR_FAIL_MSG("Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); + } + + Vector<uint8_t> key; + key.resize(32); + for (int i = 0; i < key.size(); i++) { + key.write[i] = script_encryption_key[i]; + } + + Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); + if (err) { + memdelete(fae); + ERR_FAIL_MSG("Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); + } + f = fae; + off = 0; + } pos = 0; eof = false; } FileAccessPack::~FileAccessPack() { - if (f) + if (f) { + f->close(); memdelete(f); + } } ////////////////////////////////////////////////////////////////////////////////// @@ -347,17 +391,14 @@ FileAccessPack::~FileAccessPack() { ////////////////////////////////////////////////////////////////////////////////// Error DirAccessPack::list_dir_begin() { - list_dirs.clear(); list_files.clear(); for (Map<String, PackedData::PackedDir *>::Element *E = current->subdirs.front(); E; E = E->next()) { - list_dirs.push_back(E->key()); } for (Set<String>::Element *E = current->files.front(); E; E = E->next()) { - list_files.push_back(E->get()); } @@ -365,7 +406,6 @@ Error DirAccessPack::list_dir_begin() { } String DirAccessPack::get_next() { - if (list_dirs.size()) { cdir = true; String d = list_dirs.front()->get(); @@ -380,32 +420,36 @@ String DirAccessPack::get_next() { return String(); } } -bool DirAccessPack::current_is_dir() const { +bool DirAccessPack::current_is_dir() const { return cdir; } -bool DirAccessPack::current_is_hidden() const { +bool DirAccessPack::current_is_hidden() const { return false; } -void DirAccessPack::list_dir_end() { +void DirAccessPack::list_dir_end() { list_dirs.clear(); list_files.clear(); } int DirAccessPack::get_drive_count() { - return 0; } -String DirAccessPack::get_drive(int p_drive) { +String DirAccessPack::get_drive(int p_drive) { return ""; } -Error DirAccessPack::change_dir(String p_dir) { - +PackedData::PackedDir *DirAccessPack::_find_dir(String p_dir) { String nd = p_dir.replace("\\", "/"); + + // Special handling since simplify_path() will forbid it + if (p_dir == "..") { + return current->parent; + } + bool absolute = false; if (nd.begins_with("res://")) { nd = nd.replace_first("res://", ""); @@ -414,7 +458,9 @@ Error DirAccessPack::change_dir(String p_dir) { nd = nd.simplify_path(); - if (nd == "") nd = "."; + if (nd == "") { + nd = "."; + } if (nd.begins_with("/")) { nd = nd.replace_first("/", ""); @@ -425,13 +471,13 @@ Error DirAccessPack::change_dir(String p_dir) { PackedData::PackedDir *pd; - if (absolute) + if (absolute) { pd = PackedData::get_singleton()->root; - else + } else { pd = current; + } for (int i = 0; i < paths.size(); i++) { - String p = paths[i]; if (p == ".") { continue; @@ -440,22 +486,27 @@ Error DirAccessPack::change_dir(String p_dir) { pd = pd->parent; } } else if (pd->subdirs.has(p)) { - pd = pd->subdirs[p]; } else { - - return ERR_INVALID_PARAMETER; + return nullptr; } } - current = pd; + return pd; +} - return OK; +Error DirAccessPack::change_dir(String p_dir) { + PackedData::PackedDir *pd = _find_dir(p_dir); + if (pd) { + current = pd; + return OK; + } else { + return ERR_INVALID_PARAMETER; + } } String DirAccessPack::get_current_dir(bool p_include_drive) { - PackedData::PackedDir *pd = current; String p = current->name; @@ -468,35 +519,34 @@ String DirAccessPack::get_current_dir(bool p_include_drive) { } bool DirAccessPack::file_exists(String p_file) { - p_file = fix_path(p_file); - return current->files.has(p_file); + PackedData::PackedDir *pd = _find_dir(p_file.get_base_dir()); + if (!pd) { + return false; + } + return pd->files.has(p_file.get_file()); } bool DirAccessPack::dir_exists(String p_dir) { - p_dir = fix_path(p_dir); - return current->subdirs.has(p_dir); + return _find_dir(p_dir) != nullptr; } Error DirAccessPack::make_dir(String p_dir) { - return ERR_UNAVAILABLE; } Error DirAccessPack::rename(String p_from, String p_to) { - return ERR_UNAVAILABLE; } -Error DirAccessPack::remove(String p_name) { +Error DirAccessPack::remove(String p_name) { return ERR_UNAVAILABLE; } size_t DirAccessPack::get_space_left() { - return 0; } @@ -505,10 +555,5 @@ String DirAccessPack::get_filesystem_type() const { } DirAccessPack::DirAccessPack() { - current = PackedData::get_singleton()->root; - cdir = false; -} - -DirAccessPack::~DirAccessPack() { } diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 8df6826ac9..3c84e6b656 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,16 +31,24 @@ #ifndef FILE_ACCESS_PACK_H #define FILE_ACCESS_PACK_H -#include "core/list.h" -#include "core/map.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" -#include "core/print_string.h" +#include "core/string/print_string.h" +#include "core/templates/list.h" +#include "core/templates/map.h" // Godot's packed file magic header ("GDPC" in ASCII). #define PACK_HEADER_MAGIC 0x43504447 // The current packed file format version number. -#define PACK_FORMAT_VERSION 1 +#define PACK_FORMAT_VERSION 2 + +enum PackFlags { + PACK_DIR_ENCRYPTED = 1 << 0 +}; + +enum PackFileFlags { + PACK_FILE_ENCRYPTED = 1 << 0 +}; class PackSource; @@ -51,27 +59,26 @@ class PackedData { public: struct PackedFile { - String pack; uint64_t offset; //if offset is ZERO, the file was ERASED uint64_t size; uint8_t md5[16]; PackSource *src; + bool encrypted; }; private: struct PackedDir { - PackedDir *parent; + PackedDir *parent = nullptr; String name; Map<String, PackedDir *> subdirs; Set<String> files; }; struct PathMD5 { - uint64_t a; - uint64_t b; + uint64_t a = 0; + uint64_t b = 0; bool operator<(const PathMD5 &p_md5) const { - if (p_md5.a == a) { return b < p_md5.b; } else { @@ -81,16 +88,14 @@ private: bool operator==(const PathMD5 &p_md5) const { return a == p_md5.a && b == p_md5.b; - }; + } - PathMD5() { - a = b = 0; - }; + PathMD5() {} PathMD5(const Vector<uint8_t> p_buf) { a = *((uint64_t *)&p_buf[0]); b = *((uint64_t *)&p_buf[8]); - }; + } }; Map<PathMD5, PackedFile> files; @@ -98,51 +103,51 @@ private: Vector<PackSource *> sources; PackedDir *root; - //Map<String,PackedDir*> dirs; static PackedData *singleton; - bool disabled; + bool disabled = false; void _free_packed_dirs(PackedDir *p_dir); public: void add_pack_source(PackSource *p_source); - void add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files); // for PackSource + void add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource void set_disabled(bool p_disabled) { disabled = p_disabled; } _FORCE_INLINE_ bool is_disabled() const { return disabled; } static PackedData *get_singleton() { return singleton; } - Error add_pack(const String &p_path, bool p_replace_files); + Error add_pack(const String &p_path, bool p_replace_files, size_t p_offset); _FORCE_INLINE_ FileAccess *try_open_path(const String &p_path); _FORCE_INLINE_ bool has_path(const String &p_path); + _FORCE_INLINE_ DirAccess *try_open_directory(const String &p_path); + _FORCE_INLINE_ bool has_directory(const String &p_path); + PackedData(); ~PackedData(); }; class PackSource { - public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files) = 0; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset) = 0; virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file) = 0; virtual ~PackSource() {} }; class PackedSourcePCK : public PackSource { - public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files); + virtual bool try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset); virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); }; class FileAccessPack : public FileAccess { - PackedData::PackedFile pf; mutable size_t pos; mutable bool eof; + uint64_t off; FileAccess *f; virtual Error _open(const String &p_path, int p_mode_flags); @@ -181,29 +186,40 @@ public: }; FileAccess *PackedData::try_open_path(const String &p_path) { - PathMD5 pmd5(p_path.md5_buffer()); Map<PathMD5, PackedFile>::Element *E = files.find(pmd5); - if (!E) + if (!E) { return nullptr; //not found - if (E->get().offset == 0) + } + if (E->get().offset == 0) { return nullptr; //was erased + } return E->get().src->get_file(p_path, &E->get()); } bool PackedData::has_path(const String &p_path) { - return files.has(PathMD5(p_path.md5_buffer())); } -class DirAccessPack : public DirAccess { +bool PackedData::has_directory(const String &p_path) { + DirAccess *da = try_open_directory(p_path); + if (da) { + memdelete(da); + return true; + } else { + return false; + } +} +class DirAccessPack : public DirAccess { PackedData::PackedDir *current; List<String> list_dirs; List<String> list_files; - bool cdir; + bool cdir = false; + + PackedData::PackedDir *_find_dir(String p_dir); public: virtual Error list_dir_begin(); @@ -231,7 +247,16 @@ public: virtual String get_filesystem_type() const; DirAccessPack(); - ~DirAccessPack(); + ~DirAccessPack() {} }; +DirAccess *PackedData::try_open_directory(const String &p_path) { + DirAccess *da = memnew(DirAccessPack()); + if (da->change_dir(p_path) != OK) { + memdelete(da); + da = nullptr; + } + return da; +} + #endif // FILE_ACCESS_PACK_H diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index 57de66afaf..01f9337a80 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -40,7 +40,6 @@ ZipArchive *ZipArchive::instance = nullptr; extern "C" { static void *godot_open(void *data, const char *p_fname, int mode) { - if (mode & ZLIB_FILEFUNC_MODE_WRITE) { return nullptr; } @@ -52,30 +51,25 @@ static void *godot_open(void *data, const char *p_fname, int mode) { } static uLong godot_read(void *data, void *fdata, void *buf, uLong size) { - FileAccess *f = (FileAccess *)data; f->get_buffer((uint8_t *)buf, size); return size; } static uLong godot_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { - return 0; } static long godot_tell(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)opaque; return f->get_position(); } static long godot_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - FileAccess *f = (FileAccess *)opaque; int pos = offset; switch (origin) { - case ZLIB_FILEFUNC_SEEK_CUR: pos = f->get_position() + offset; break; @@ -91,32 +85,26 @@ static long godot_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { } static int godot_close(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)opaque; f->close(); return 0; } static int godot_testerror(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)opaque; return f->get_error() != OK ? 1 : 0; } static voidpf godot_alloc(voidpf opaque, uInt items, uInt size) { - return memalloc(items * size); } static void godot_free(voidpf opaque, voidpf address) { - memfree(address); } - } // extern "C" void ZipArchive::close_handle(unzFile p_file) const { - ERR_FAIL_COND_MSG(!p_file, "Cannot close a file if none is open."); FileAccess *f = (FileAccess *)unzGetOpaque(p_file); unzCloseCurrentFile(p_file); @@ -125,7 +113,6 @@ void ZipArchive::close_handle(unzFile p_file) const { } unzFile ZipArchive::get_file_handle(String p_file) const { - ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, "File '" + p_file + " doesn't exist."); File file = files[p_file]; @@ -152,7 +139,6 @@ unzFile ZipArchive::get_file_handle(String p_file) const { ERR_FAIL_COND_V(!pkg, nullptr); int unz_err = unzGoToFilePos(pkg, &file.file_pos); if (unz_err != UNZ_OK || unzOpenCurrentFile(pkg) != UNZ_OK) { - unzClose(pkg); ERR_FAIL_V(nullptr); } @@ -160,17 +146,21 @@ unzFile ZipArchive::get_file_handle(String p_file) const { return pkg; } -bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { +bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset = 0) { + //printf("opening zip pack %s, %i, %i\n", p_name.utf8().get_data(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); + // load with offset feature only supported for PCK files + ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with ZIP archives."); - //printf("opening zip pack %ls, %i, %i\n", p_name.c_str(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); - if (p_path.get_extension().nocasecmp_to("zip") != 0 && p_path.get_extension().nocasecmp_to("pcz") != 0) + if (p_path.get_extension().nocasecmp_to("zip") != 0 && p_path.get_extension().nocasecmp_to("pcz") != 0) { return false; + } zlib_filefunc_def io; FileAccess *fa = FileAccess::open(p_path, FileAccess::READ); - if (!fa) + if (!fa) { return false; + } io.opaque = fa; io.zopen_file = godot_open; io.zread_file = godot_read; @@ -195,7 +185,6 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { int pkg_num = packages.size() - 1; for (uint64_t i = 0; i < gi.number_entry; i++) { - char filename_inzip[256]; unz_file_info64 file_info; @@ -210,8 +199,8 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { files[fname] = f; uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this, p_replace_files); - //printf("packed data add path %ls, %ls\n", p_name.c_str(), fname.c_str()); + PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this, p_replace_files, false); + //printf("packed data add path %s, %s\n", p_name.utf8().get_data(), fname.utf8().get_data()); if ((i + 1) < gi.number_entry) { unzGoToNextFile(zfile); @@ -222,17 +211,14 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { } bool ZipArchive::file_exists(String p_name) const { - return files.has(p_name); } FileAccess *ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file) { - return memnew(FileAccessZip(p_path, *p_file)); } ZipArchive *ZipArchive::get_singleton() { - if (instance == nullptr) { instance = memnew(ZipArchive); } @@ -241,15 +227,11 @@ ZipArchive *ZipArchive::get_singleton() { } ZipArchive::ZipArchive() { - instance = this; - //fa_create_func = FileAccess::get_create_func(); } ZipArchive::~ZipArchive() { - for (int i = 0; i < packages.size(); i++) { - FileAccess *f = (FileAccess *)unzGetOpaque(packages[i].zfile); unzClose(packages[i].zfile); memdelete(f); @@ -259,7 +241,6 @@ ZipArchive::~ZipArchive() { } Error FileAccessZip::_open(const String &p_path, int p_mode_flags) { - close(); ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, FAILED); @@ -275,9 +256,9 @@ Error FileAccessZip::_open(const String &p_path, int p_mode_flags) { } void FileAccessZip::close() { - - if (!zfile) + if (!zfile) { return; + } ZipArchive *arch = ZipArchive::get_singleton(); ERR_FAIL_COND(!arch); @@ -286,65 +267,57 @@ void FileAccessZip::close() { } bool FileAccessZip::is_open() const { - return zfile != nullptr; } void FileAccessZip::seek(size_t p_position) { - ERR_FAIL_COND(!zfile); unzSeekCurrentFile(zfile, p_position); } void FileAccessZip::seek_end(int64_t p_position) { - ERR_FAIL_COND(!zfile); unzSeekCurrentFile(zfile, get_len() + p_position); } size_t FileAccessZip::get_position() const { - ERR_FAIL_COND_V(!zfile, 0); return unztell(zfile); } size_t FileAccessZip::get_len() const { - ERR_FAIL_COND_V(!zfile, 0); return file_info.uncompressed_size; } bool FileAccessZip::eof_reached() const { - ERR_FAIL_COND_V(!zfile, true); return at_eof; } uint8_t FileAccessZip::get_8() const { - uint8_t ret = 0; get_buffer(&ret, 1); return ret; } int FileAccessZip::get_buffer(uint8_t *p_dst, int p_length) const { - ERR_FAIL_COND_V(!zfile, -1); at_eof = unzeof(zfile); - if (at_eof) + if (at_eof) { return 0; + } int read = unzReadCurrentFile(zfile, p_dst, p_length); ERR_FAIL_COND_V(read < 0, read); - if (read < p_length) + if (read < p_length) { at_eof = true; + } return read; } Error FileAccessZip::get_error() const { - if (!zfile) { - return ERR_UNCONFIGURED; } if (eof_reached()) { @@ -355,28 +328,23 @@ Error FileAccessZip::get_error() const { } void FileAccessZip::flush() { - ERR_FAIL(); } void FileAccessZip::store_8(uint8_t p_dest) { - ERR_FAIL(); } bool FileAccessZip::file_exists(const String &p_name) { - return false; } -FileAccessZip::FileAccessZip(const String &p_path, const PackedData::PackedFile &p_file) : - zfile(nullptr) { +FileAccessZip::FileAccessZip(const String &p_path, const PackedData::PackedFile &p_file) { _open(p_path, FileAccess::READ); } FileAccessZip::~FileAccessZip() { - close(); } -#endif +#endif // MINIZIP_ENABLED diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index d5ce7d7a8d..8559f871ce 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,35 +28,30 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MINIZIP_ENABLED - #ifndef FILE_ACCESS_ZIP_H #define FILE_ACCESS_ZIP_H +#ifdef MINIZIP_ENABLED + #include "core/io/file_access_pack.h" -#include "core/map.h" +#include "core/templates/map.h" #include "thirdparty/minizip/unzip.h" #include <stdlib.h> class ZipArchive : public PackSource { - public: struct File { - - int package; + int package = -1; unz_file_pos file_pos; - File() { - - package = -1; - }; + File() {} }; private: struct Package { String filename; - unzFile zfile; + unzFile zfile = nullptr; }; Vector<Package> packages; @@ -64,8 +59,6 @@ private: static ZipArchive *instance; - FileAccess::CreateFunc fa_create_func; - public: void close_handle(unzFile p_file) const; unzFile get_file_handle(String p_file) const; @@ -74,7 +67,7 @@ public: bool file_exists(String p_name) const; - virtual bool try_open_pack(const String &p_path, bool p_replace_files); + virtual bool try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset); FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); static ZipArchive *get_singleton(); @@ -84,8 +77,7 @@ public: }; class FileAccessZip : public FileAccess { - - unzFile zfile; + unzFile zfile = nullptr; unz_file_info64 file_info; mutable bool at_eof; @@ -119,6 +111,6 @@ public: ~FileAccessZip(); }; -#endif // FILE_ACCESS_ZIP_H - #endif // MINIZIP_ENABLED + +#endif // FILE_ACCESS_ZIP_H diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 56f8f1ff91..a2fcf074ae 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -47,7 +47,6 @@ const char *HTTPClient::_methods[METHOD_MAX] = { #ifndef JAVASCRIPT_ENABLED Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) { - close(); conn_port = p_port; @@ -58,10 +57,8 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, String host_lower = conn_host.to_lower(); if (host_lower.begins_with("http://")) { - conn_host = conn_host.substr(7, conn_host.length() - 7); } else if (host_lower.begins_with("https://")) { - ssl = true; conn_host = conn_host.substr(8, conn_host.length() - 8); } @@ -97,38 +94,56 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, } void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) { - ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object."); + if (connection == p_connection) { + return; + } + close(); connection = p_connection; status = STATUS_CONNECTED; } Ref<StreamPeer> HTTPClient::get_connection() const { - return connection; } -Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) { +static bool _check_request_url(HTTPClient::Method p_method, const String &p_url) { + switch (p_method) { + case HTTPClient::METHOD_CONNECT: { + // Authority in host:port format, as in RFC7231 + int pos = p_url.find_char(':'); + return 0 < pos && pos < p_url.length() - 1; + } + case HTTPClient::METHOD_OPTIONS: { + if (p_url == "*") { + return true; + } + [[fallthrough]]; + } + default: + // Absolute path or absolute URL + return p_url.begins_with("/") || p_url.begins_with("http://") || p_url.begins_with("https://"); + } +} +Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) { ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!_check_request_url(p_method, p_url), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA); String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n"; - if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) { - // Don't append the standard ports - request += "Host: " + conn_host + "\r\n"; - } else { - request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; - } + bool add_host = true; bool add_clen = p_body.size() > 0; bool add_uagent = true; bool add_accept = true; for (int i = 0; i < p_headers.size(); i++) { request += p_headers[i] + "\r\n"; + if (add_host && p_headers[i].findn("Host:") == 0) { + add_host = false; + } if (add_clen && p_headers[i].findn("Content-Length:") == 0) { add_clen = false; } @@ -139,6 +154,14 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector add_accept = false; } } + if (add_host) { + if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) { + // Don't append the standard ports + request += "Host: " + conn_host + "\r\n"; + } else { + request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; + } + } if (add_clen) { request += "Content-Length: " + itos(p_body.size()) + "\r\n"; // Should it add utf8 encoding? @@ -179,24 +202,21 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector } Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) { - ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!_check_request_url(p_method, p_url), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA); String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n"; - if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) { - // Don't append the standard ports - request += "Host: " + conn_host + "\r\n"; - } else { - request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; - } + bool add_host = true; bool add_uagent = true; bool add_accept = true; bool add_clen = p_body.length() > 0; for (int i = 0; i < p_headers.size(); i++) { request += p_headers[i] + "\r\n"; + if (add_host && p_headers[i].findn("Host:") == 0) { + add_host = false; + } if (add_clen && p_headers[i].findn("Content-Length:") == 0) { add_clen = false; } @@ -207,6 +227,14 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str add_accept = false; } } + if (add_host) { + if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) { + // Don't append the standard ports + request += "Host: " + conn_host + "\r\n"; + } else { + request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; + } + } if (add_clen) { request += "Content-Length: " + itos(p_body.utf8().length()) + "\r\n"; // Should it add utf8 encoding? @@ -235,27 +263,23 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str } bool HTTPClient::has_response() const { - return response_headers.size() != 0; } bool HTTPClient::is_response_chunked() const { - return chunked; } int HTTPClient::get_response_code() const { - return response_num; } Error HTTPClient::get_response_headers(List<String> *r_response) { - - if (!response_headers.size()) + if (!response_headers.size()) { return ERR_INVALID_PARAMETER; + } for (int i = 0; i < response_headers.size(); i++) { - r_response->push_back(response_headers[i]); } @@ -265,15 +289,14 @@ Error HTTPClient::get_response_headers(List<String> *r_response) { } void HTTPClient::close() { - - if (tcp_connection->get_status() != StreamPeerTCP::STATUS_NONE) + if (tcp_connection->get_status() != StreamPeerTCP::STATUS_NONE) { tcp_connection->disconnect_from_host(); + } connection.unref(); status = STATUS_DISCONNECTED; head_request = false; if (resolving != IP::RESOLVER_INVALID_ID) { - IP::get_singleton()->erase_resolve_item(resolving); resolving = IP::RESOLVER_INVALID_ID; } @@ -283,16 +306,14 @@ void HTTPClient::close() { body_size = -1; body_left = 0; chunk_left = 0; - chunk_trailer_part = 0; + chunk_trailer_part = false; read_until_eof = false; response_num = 0; handshaking = false; } Error HTTPClient::poll() { - switch (status) { - case STATUS_RESOLVING: { ERR_FAIL_COND_V(resolving == IP::RESOLVER_INVALID_ID, ERR_BUG); @@ -302,7 +323,6 @@ Error HTTPClient::poll() { return OK; // Still resolving case IP::RESOLVER_STATUS_DONE: { - IP_Address host = IP::get_singleton()->get_resolve_item_address(resolving); Error err = tcp_connection->connect_to_host(host, conn_port); IP::get_singleton()->erase_resolve_item(resolving); @@ -316,7 +336,6 @@ Error HTTPClient::poll() { } break; case IP::RESOLVER_STATUS_NONE: case IP::RESOLVER_STATUS_ERROR: { - IP::get_singleton()->erase_resolve_item(resolving); resolving = IP::RESOLVER_INVALID_ID; close(); @@ -326,10 +345,8 @@ Error HTTPClient::poll() { } } break; case STATUS_CONNECTING: { - StreamPeerTCP::Status s = tcp_connection->get_status(); switch (s) { - case StreamPeerTCP::STATUS_CONNECTING: { return OK; } break; @@ -379,7 +396,6 @@ Error HTTPClient::poll() { } break; case StreamPeerTCP::STATUS_ERROR: case StreamPeerTCP::STATUS_NONE: { - close(); status = STATUS_CANT_CONNECT; return ERR_CANT_CONNECT; @@ -404,7 +420,6 @@ Error HTTPClient::poll() { return OK; } break; case STATUS_REQUESTING: { - while (true) { uint8_t byte; int rec = 0; @@ -415,15 +430,15 @@ Error HTTPClient::poll() { return ERR_CONNECTION_ERROR; } - if (rec == 0) + if (rec == 0) { return OK; // Still requesting, keep trying! + } response_str.push_back(byte); int rs = response_str.size(); if ( (rs >= 2 && response_str[rs - 2] == '\n' && response_str[rs - 1] == '\n') || (rs >= 4 && response_str[rs - 4] == '\r' && response_str[rs - 3] == '\n' && response_str[rs - 2] == '\r' && response_str[rs - 1] == '\n')) { - // End of response, parse. response_str.push_back(0); String response; @@ -445,11 +460,11 @@ Error HTTPClient::poll() { bool keep_alive = true; for (int i = 0; i < responses.size(); i++) { - String header = responses[i].strip_edges(); String s = header.to_lower(); - if (s.length() == 0) + if (s.length() == 0) { continue; + } if (s.begins_with("content-length:")) { body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int(); body_left = body_size; @@ -464,30 +479,25 @@ Error HTTPClient::poll() { } if (i == 0 && responses[i].begins_with("HTTP")) { - String num = responses[i].get_slicec(' ', 1); response_num = num.to_int(); } else { - response_headers.push_back(header); } } - // This is a HEAD request, we wont receive anything. + // This is a HEAD request, we won't receive anything. if (head_request) { body_size = 0; body_left = 0; } if (body_size != -1 || chunked) { - status = STATUS_BODY; } else if (!keep_alive) { - read_until_eof = true; status = STATUS_BODY; } else { - status = STATUS_CONNECTED; } return OK; @@ -513,29 +523,26 @@ Error HTTPClient::poll() { } int HTTPClient::get_response_body_length() const { - return body_size; } PackedByteArray HTTPClient::read_response_body_chunk() { - ERR_FAIL_COND_V(status != STATUS_BODY, PackedByteArray()); PackedByteArray ret; Error err = OK; if (chunked) { - while (true) { - if (chunk_trailer_part) { // We need to consume the trailer part too or keep-alive will break uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); - if (rec == 0) + if (rec == 0) { break; + } chunk.push_back(b); int cs = chunk.size(); @@ -557,8 +564,9 @@ PackedByteArray HTTPClient::read_response_body_chunk() { int rec = 0; err = _get_http_data(&b, 1, rec); - if (rec == 0) + if (rec == 0) { break; + } chunk.push_back(b); @@ -569,18 +577,17 @@ PackedByteArray HTTPClient::read_response_body_chunk() { } if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') { - int len = 0; for (int i = 0; i < chunk.size() - 2; i++) { char c = chunk[i]; int v = 0; - if (c >= '0' && c <= '9') + if (c >= '0' && c <= '9') { v = c - '0'; - else if (c >= 'a' && c <= 'f') + } else if (c >= 'a' && c <= 'f') { v = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') + } else if (c >= 'A' && c <= 'F') { v = c - 'A' + 10; - else { + } else { ERR_PRINT("HTTP Chunk len not in hex!!"); status = STATUS_CONNECTION_ERROR; break; @@ -605,7 +612,6 @@ PackedByteArray HTTPClient::read_response_body_chunk() { chunk.resize(chunk_left); } } else { - int rec = 0; err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec); if (rec == 0) { @@ -614,7 +620,6 @@ PackedByteArray HTTPClient::read_response_body_chunk() { chunk_left -= rec; if (chunk_left == 0) { - if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') { ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)"); status = STATUS_CONNECTION_ERROR; @@ -632,7 +637,6 @@ PackedByteArray HTTPClient::read_response_body_chunk() { } } else { - int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size; ret.resize(to_read); int _offset = 0; @@ -652,24 +656,21 @@ PackedByteArray HTTPClient::read_response_body_chunk() { body_left -= rec; } } - if (err != OK) + if (err != OK) { break; + } } } if (err != OK) { - close(); if (err == ERR_FILE_EOF) { - status = STATUS_DISCONNECTED; // Server disconnected } else { - status = STATUS_CONNECTION_ERROR; } } else if (body_left == 0 && !chunked && !read_until_eof) { - status = STATUS_CONNECTED; } @@ -677,24 +678,19 @@ PackedByteArray HTTPClient::read_response_body_chunk() { } HTTPClient::Status HTTPClient::get_status() const { - return status; } void HTTPClient::set_blocking_mode(bool p_enable) { - blocking = p_enable; } bool HTTPClient::is_blocking_mode_enabled() const { - return blocking; } Error HTTPClient::_get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - if (blocking) { - // We can't use StreamPeer.get_data, since when reaching EOF we will get an // error without knowing how many bytes we received. Error err = ERR_FILE_EOF; @@ -729,27 +725,10 @@ int HTTPClient::get_read_chunk_size() const { } HTTPClient::HTTPClient() { - tcp_connection.instance(); - resolving = IP::RESOLVER_INVALID_ID; - status = STATUS_DISCONNECTED; - head_request = false; - conn_port = -1; - body_size = -1; - chunked = false; - body_left = 0; - read_until_eof = false; - chunk_left = 0; - chunk_trailer_part = false; - response_num = 0; - ssl = false; - blocking = false; - handshaking = false; - read_chunk_size = 4096; } -HTTPClient::~HTTPClient() { -} +HTTPClient::~HTTPClient() {} #endif // #ifndef JAVASCRIPT_ENABLED @@ -784,15 +763,15 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { } Dictionary HTTPClient::_get_response_headers_as_dictionary() { - List<String> rh; get_response_headers(&rh); Dictionary ret; for (const List<String>::Element *E = rh.front(); E; E = E->next()) { const String &s = E->get(); int sp = s.find(":"); - if (sp == -1) + if (sp == -1) { continue; + } String key = s.substr(0, sp).strip_edges(); String value = s.substr(sp + 1, s.length()).strip_edges(); ret[key] = value; @@ -802,7 +781,6 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() { } PackedStringArray HTTPClient::_get_response_headers() { - List<String> rh; get_response_headers(&rh); PackedStringArray ret; @@ -816,7 +794,6 @@ PackedStringArray HTTPClient::_get_response_headers() { } void HTTPClient::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_ssl", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_connection", "connection"), &HTTPClient::set_connection); ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection); diff --git a/core/io/http_client.h b/core/io/http_client.h index 03ba20f8dd..ec4b86b26f 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -34,15 +34,13 @@ #include "core/io/ip.h" #include "core/io/stream_peer.h" #include "core/io/stream_peer_tcp.h" -#include "core/reference.h" +#include "core/object/reference.h" class HTTPClient : public Reference { - GDCLASS(HTTPClient, Reference); public: enum ResponseCode { - // 1xx informational RESPONSE_CONTINUE = 100, RESPONSE_SWITCHING_PROTOCOLS = 101, @@ -117,7 +115,6 @@ public: }; enum Method { - METHOD_GET, METHOD_HEAD, METHOD_POST, @@ -132,7 +129,6 @@ public: }; enum Status { - STATUS_DISCONNECTED, STATUS_RESOLVING, // Resolving hostname (if passed a hostname) STATUS_CANT_RESOLVE, @@ -151,39 +147,39 @@ private: static const int HOST_MIN_LEN = 4; enum Port { - PORT_HTTP = 80, PORT_HTTPS = 443, }; #ifndef JAVASCRIPT_ENABLED - Status status; - IP::ResolverID resolving; - int conn_port; + Status status = STATUS_DISCONNECTED; + IP::ResolverID resolving = IP::RESOLVER_INVALID_ID; + int conn_port = -1; String conn_host; - bool ssl; - bool ssl_verify_host; - bool blocking; - bool handshaking; - bool head_request; + bool ssl = false; + bool ssl_verify_host = false; + bool blocking = false; + bool handshaking = false; + bool head_request = false; Vector<uint8_t> response_str; - bool chunked; + bool chunked = false; Vector<uint8_t> chunk; - int chunk_left; - bool chunk_trailer_part; - int body_size; - int body_left; - bool read_until_eof; + int chunk_left = 0; + bool chunk_trailer_part = false; + int body_size = -1; + int body_left = 0; + bool read_until_eof = false; Ref<StreamPeerTCP> tcp_connection; Ref<StreamPeer> connection; - int response_num; + int response_num = 0; Vector<String> response_headers; - int read_chunk_size; + // 64 KiB by default (favors fast download speeds at the cost of memory usage). + int read_chunk_size = 65536; Error _get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received); diff --git a/core/image.cpp b/core/io/image.cpp index 58351ae2e5..986c29b539 100644 --- a/core/image.cpp +++ b/core/io/image.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,14 +30,13 @@ #include "image.h" -#include "core/hash_map.h" +#include "core/error/error_macros.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/math/math_funcs.h" #include "core/os/copymem.h" -#include "core/print_string.h" - -#include "thirdparty/misc/hq2x.h" +#include "core/string/print_string.h" +#include "core/templates/hash_map.h" #include <stdio.h> @@ -67,10 +66,10 @@ const char *Image::format_names[Image::FORMAT_MAX] = { "BPTC_RGBA", "BPTC_RGBF", "BPTC_RGBFU", - "PVRTC2", //pvrtc - "PVRTC2A", - "PVRTC4", - "PVRTC4A", + "PVRTC1_2", //pvrtc + "PVRTC1_2A", + "PVRTC1_4", + "PVRTC1_4A", "ETC", //etc1 "ETC2_R11", //etc2 "ETC2_R11S", //signed", NOT srgb. @@ -81,7 +80,6 @@ const char *Image::format_names[Image::FORMAT_MAX] = { "ETC2_RGB8A1", "ETC2_RA_AS_RG", "FORMAT_DXT5_RA_AS_RG", - }; SavePNGFunc Image::save_png_func = nullptr; @@ -90,7 +88,6 @@ SaveEXRFunc Image::save_exr_func = nullptr; SavePNGBufferFunc Image::save_png_buffer_func = nullptr; void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) { - uint32_t ofs = (p_y * width + p_x) * p_pixelsize; for (uint32_t i = 0; i < p_pixelsize; i++) { @@ -99,7 +96,6 @@ void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, } void Image::_get_pixelb(int p_x, int p_y, uint32_t p_pixelsize, const uint8_t *p_data, uint8_t *p_pixel) { - uint32_t ofs = (p_y * width + p_x) * p_pixelsize; for (uint32_t i = 0; i < p_pixelsize; i++) { @@ -108,29 +104,41 @@ void Image::_get_pixelb(int p_x, int p_y, uint32_t p_pixelsize, const uint8_t *p } int Image::get_format_pixel_size(Format p_format) { - switch (p_format) { case FORMAT_L8: return 1; //luminance case FORMAT_LA8: return 2; //luminance-alpha - case FORMAT_R8: return 1; - case FORMAT_RG8: return 2; - case FORMAT_RGB8: return 3; - case FORMAT_RGBA8: return 4; - case FORMAT_RGBA4444: return 2; - case FORMAT_RGB565: return 2; + case FORMAT_R8: + return 1; + case FORMAT_RG8: + return 2; + case FORMAT_RGB8: + return 3; + case FORMAT_RGBA8: + return 4; + case FORMAT_RGBA4444: + return 2; + case FORMAT_RGB565: + return 2; case FORMAT_RF: return 4; //float - case FORMAT_RGF: return 8; - case FORMAT_RGBF: return 12; - case FORMAT_RGBAF: return 16; + case FORMAT_RGF: + return 8; + case FORMAT_RGBF: + return 12; + case FORMAT_RGBAF: + return 16; case FORMAT_RH: return 2; //half float - case FORMAT_RGH: return 4; - case FORMAT_RGBH: return 6; - case FORMAT_RGBAH: return 8; - case FORMAT_RGBE9995: return 4; + case FORMAT_RGH: + return 4; + case FORMAT_RGBH: + return 6; + case FORMAT_RGBAH: + return 8; + case FORMAT_RGBE9995: + return 4; case FORMAT_DXT1: return 1; //s3tc bc1 case FORMAT_DXT3: @@ -147,24 +155,34 @@ int Image::get_format_pixel_size(Format p_format) { return 1; //float / case FORMAT_BPTC_RGBFU: return 1; //unsigned float - case FORMAT_PVRTC2: + case FORMAT_PVRTC1_2: return 1; //pvrtc - case FORMAT_PVRTC2A: return 1; - case FORMAT_PVRTC4: return 1; - case FORMAT_PVRTC4A: return 1; + case FORMAT_PVRTC1_2A: + return 1; + case FORMAT_PVRTC1_4: + return 1; + case FORMAT_PVRTC1_4A: + return 1; case FORMAT_ETC: return 1; //etc1 case FORMAT_ETC2_R11: return 1; //etc2 case FORMAT_ETC2_R11S: return 1; //signed: return 1; NOT srgb. - case FORMAT_ETC2_RG11: return 1; - case FORMAT_ETC2_RG11S: return 1; - case FORMAT_ETC2_RGB8: return 1; - case FORMAT_ETC2_RGBA8: return 1; - case FORMAT_ETC2_RGB8A1: return 1; - case FORMAT_ETC2_RA_AS_RG: return 1; - case FORMAT_DXT5_RA_AS_RG: return 1; + case FORMAT_ETC2_RG11: + return 1; + case FORMAT_ETC2_RG11S: + return 1; + case FORMAT_ETC2_RGB8: + return 1; + case FORMAT_ETC2_RGBA8: + return 1; + case FORMAT_ETC2_RGB8A1: + return 1; + case FORMAT_ETC2_RA_AS_RG: + return 1; + case FORMAT_DXT5_RA_AS_RG: + return 1; case FORMAT_MAX: { } } @@ -172,7 +190,6 @@ int Image::get_format_pixel_size(Format p_format) { } void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { - switch (p_format) { case FORMAT_DXT1: //s3tc bc1 case FORMAT_DXT3: //bc2 @@ -183,27 +200,23 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { r_w = 4; r_h = 4; } break; - case FORMAT_PVRTC2: - case FORMAT_PVRTC2A: { - + case FORMAT_PVRTC1_2: + case FORMAT_PVRTC1_2A: { r_w = 16; r_h = 8; } break; - case FORMAT_PVRTC4A: - case FORMAT_PVRTC4: { - + case FORMAT_PVRTC1_4A: + case FORMAT_PVRTC1_4: { r_w = 8; r_h = 8; } break; case FORMAT_ETC: { - r_w = 4; r_h = 4; } break; case FORMAT_BPTC_RGBA: case FORMAT_BPTC_RGBF: case FORMAT_BPTC_RGBFU: { - r_w = 4; r_h = 4; } break; @@ -216,7 +229,6 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { case FORMAT_ETC2_RGB8A1: case FORMAT_ETC2_RA_AS_RG: case FORMAT_DXT5_RA_AS_RG: { - r_w = 4; r_h = 4; @@ -230,17 +242,16 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { } int Image::get_format_pixel_rshift(Format p_format) { - - if (p_format == FORMAT_DXT1 || p_format == FORMAT_RGTC_R || p_format == FORMAT_PVRTC4 || p_format == FORMAT_PVRTC4A || p_format == FORMAT_ETC || p_format == FORMAT_ETC2_R11 || p_format == FORMAT_ETC2_R11S || p_format == FORMAT_ETC2_RGB8 || p_format == FORMAT_ETC2_RGB8A1) + if (p_format == FORMAT_DXT1 || p_format == FORMAT_RGTC_R || p_format == FORMAT_PVRTC1_4 || p_format == FORMAT_PVRTC1_4A || p_format == FORMAT_ETC || p_format == FORMAT_ETC2_R11 || p_format == FORMAT_ETC2_R11S || p_format == FORMAT_ETC2_RGB8 || p_format == FORMAT_ETC2_RGB8A1) { return 1; - else if (p_format == FORMAT_PVRTC2 || p_format == FORMAT_PVRTC2A) + } else if (p_format == FORMAT_PVRTC1_2 || p_format == FORMAT_PVRTC1_2A) { return 2; - else + } else { return 0; + } } int Image::get_format_block_size(Format p_format) { - switch (p_format) { case FORMAT_DXT1: //s3tc bc1 case FORMAT_DXT3: //bc2 @@ -250,24 +261,20 @@ int Image::get_format_block_size(Format p_format) { return 4; } - case FORMAT_PVRTC2: - case FORMAT_PVRTC2A: { - + case FORMAT_PVRTC1_2: + case FORMAT_PVRTC1_2A: { return 4; } - case FORMAT_PVRTC4A: - case FORMAT_PVRTC4: { - + case FORMAT_PVRTC1_4A: + case FORMAT_PVRTC1_4: { return 4; } case FORMAT_ETC: { - return 4; } case FORMAT_BPTC_RGBA: case FORMAT_BPTC_RGBF: case FORMAT_BPTC_RGBFU: { - return 4; } case FORMAT_ETC2_R11: //etc2 @@ -281,7 +288,6 @@ int Image::get_format_block_size(Format p_format) { case FORMAT_DXT5_RA_AS_RG: //used to make basis universal happy { - return 4; } default: { @@ -292,7 +298,6 @@ int Image::get_format_block_size(Format p_format) { } void Image::_get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_width, int &r_height) const { - int w = width; int h = height; int ofs = 0; @@ -322,7 +327,6 @@ void Image::_get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_widt } int Image::get_mipmap_offset(int p_mipmap) const { - ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1); int ofs, w, h; @@ -331,7 +335,6 @@ int Image::get_mipmap_offset(int p_mipmap) const { } int Image::get_mipmap_byte_size(int p_mipmap) const { - ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1); int ofs, w, h; @@ -342,7 +345,6 @@ int Image::get_mipmap_byte_size(int p_mipmap) const { } void Image::get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const { - int ofs, w, h; _get_mipmap_offset_and_size(p_mipmap, ofs, w, h); int ofs2; @@ -352,7 +354,6 @@ void Image::get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) co } void Image::get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const { - int ofs; _get_mipmap_offset_and_size(p_mipmap, ofs, w, h); int ofs2, w2, h2; @@ -361,43 +362,113 @@ void Image::get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int r_size = ofs2 - ofs; } -int Image::get_width() const { +Image::Image3DValidateError Image::validate_3d_image(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_images) { + int w = p_width; + int h = p_height; + int d = p_depth; + + int arr_ofs = 0; + + while (true) { + for (int i = 0; i < d; i++) { + int idx = i + arr_ofs; + if (idx >= p_images.size()) { + return VALIDATE_3D_ERR_MISSING_IMAGES; + } + if (p_images[idx].is_null() || p_images[idx]->is_empty()) { + return VALIDATE_3D_ERR_IMAGE_EMPTY; + } + if (p_images[idx]->get_format() != p_format) { + return VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH; + } + if (p_images[idx]->get_width() != w || p_images[idx]->get_height() != h) { + return VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH; + } + if (p_images[idx]->has_mipmaps()) { + return VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS; + } + } + + arr_ofs += d; + + if (!p_mipmaps) { + break; + } + + if (w == 1 && h == 1 && d == 1) { + break; + } + + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + d = MAX(1, d >> 1); + } + + if (arr_ofs != p_images.size()) { + return VALIDATE_3D_ERR_EXTRA_IMAGES; + } + + return VALIDATE_3D_OK; +} + +String Image::get_3d_image_validation_error_text(Image3DValidateError p_error) { + switch (p_error) { + case VALIDATE_3D_OK: { + return TTR("Ok"); + } break; + case VALIDATE_3D_ERR_IMAGE_EMPTY: { + return TTR("Empty Image found"); + } break; + case VALIDATE_3D_ERR_MISSING_IMAGES: { + return TTR("Missing Images"); + } break; + case VALIDATE_3D_ERR_EXTRA_IMAGES: { + return TTR("Too many Images"); + } break; + case VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH: { + return TTR("Image size mismatch"); + } break; + case VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH: { + return TTR("Image format mismatch"); + } break; + case VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS: { + return TTR("Image has included mipmaps"); + } break; + } + return String(); +} +int Image::get_width() const { return width; } int Image::get_height() const { - return height; } Vector2 Image::get_size() const { - return Vector2(width, height); } bool Image::has_mipmaps() const { - return mipmaps; } int Image::get_mipmap_count() const { - - if (mipmaps) + if (mipmaps) { return get_image_required_mipmaps(width, height, format); - else + } else { return 0; + } } //using template generates perfectly optimized code due to constant expression reduction and unused variable removal present in all compilers template <uint32_t read_bytes, bool read_alpha, uint32_t write_bytes, bool write_alpha, bool read_gray, bool write_gray> static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p_dst) { - uint32_t max_bytes = MAX(read_bytes, write_bytes); for (int y = 0; y < p_height; y++) { for (int x = 0; x < p_width; x++) { - const uint8_t *rofs = &p_src[((y * p_width) + x) * (read_bytes + (read_alpha ? 1 : 0))]; uint8_t *wofs = &p_dst[((y * p_width) + x) * (write_bytes + (write_alpha ? 1 : 0))]; @@ -408,9 +479,7 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p rgba[1] = rofs[0]; rgba[2] = rofs[0]; } else { - for (uint32_t i = 0; i < max_bytes; i++) { - rgba[i] = (i < read_bytes) ? rofs[i] : 0; } } @@ -424,7 +493,6 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p wofs[0] = uint8_t((uint16_t(rofs[0]) + uint16_t(rofs[1]) + uint16_t(rofs[2])) / 3); } else { for (uint32_t i = 0; i < write_bytes; i++) { - wofs[i] = rgba[i]; } } @@ -437,25 +505,23 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p } void Image::convert(Format p_new_format) { - - if (data.size() == 0) + if (data.size() == 0) { return; + } - if (p_new_format == format) + if (p_new_format == format) { return; + } if (format > FORMAT_RGBE9995 || p_new_format > FORMAT_RGBE9995) { - ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead."); } else if (format > FORMAT_RGBA8 || p_new_format > FORMAT_RGBA8) { - //use put/set pixel which is slower but works with non byte formats - Image new_img(width, height, 0, p_new_format); + Image new_img(width, height, false, p_new_format); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { - new_img.set_pixel(i, j, get_pixel(i, j)); } } @@ -469,7 +535,7 @@ void Image::convert(Format p_new_format) { return; } - Image new_img(width, height, 0, p_new_format); + Image new_img(width, height, false, p_new_format); const uint8_t *rptr = data.ptr(); uint8_t *wptr = new_img.data.ptrw(); @@ -477,69 +543,127 @@ void Image::convert(Format p_new_format) { int conversion_type = format | p_new_format << 8; switch (conversion_type) { - - case FORMAT_L8 | (FORMAT_LA8 << 8): _convert<1, false, 1, true, true, true>(width, height, rptr, wptr); break; - case FORMAT_L8 | (FORMAT_R8 << 8): _convert<1, false, 1, false, true, false>(width, height, rptr, wptr); break; - case FORMAT_L8 | (FORMAT_RG8 << 8): _convert<1, false, 2, false, true, false>(width, height, rptr, wptr); break; - case FORMAT_L8 | (FORMAT_RGB8 << 8): _convert<1, false, 3, false, true, false>(width, height, rptr, wptr); break; - case FORMAT_L8 | (FORMAT_RGBA8 << 8): _convert<1, false, 3, true, true, false>(width, height, rptr, wptr); break; - case FORMAT_LA8 | (FORMAT_L8 << 8): _convert<1, true, 1, false, true, true>(width, height, rptr, wptr); break; - case FORMAT_LA8 | (FORMAT_R8 << 8): _convert<1, true, 1, false, true, false>(width, height, rptr, wptr); break; - case FORMAT_LA8 | (FORMAT_RG8 << 8): _convert<1, true, 2, false, true, false>(width, height, rptr, wptr); break; - case FORMAT_LA8 | (FORMAT_RGB8 << 8): _convert<1, true, 3, false, true, false>(width, height, rptr, wptr); break; - case FORMAT_LA8 | (FORMAT_RGBA8 << 8): _convert<1, true, 3, true, true, false>(width, height, rptr, wptr); break; - case FORMAT_R8 | (FORMAT_L8 << 8): _convert<1, false, 1, false, false, true>(width, height, rptr, wptr); break; - case FORMAT_R8 | (FORMAT_LA8 << 8): _convert<1, false, 1, true, false, true>(width, height, rptr, wptr); break; - case FORMAT_R8 | (FORMAT_RG8 << 8): _convert<1, false, 2, false, false, false>(width, height, rptr, wptr); break; - case FORMAT_R8 | (FORMAT_RGB8 << 8): _convert<1, false, 3, false, false, false>(width, height, rptr, wptr); break; - case FORMAT_R8 | (FORMAT_RGBA8 << 8): _convert<1, false, 3, true, false, false>(width, height, rptr, wptr); break; - case FORMAT_RG8 | (FORMAT_L8 << 8): _convert<2, false, 1, false, false, true>(width, height, rptr, wptr); break; - case FORMAT_RG8 | (FORMAT_LA8 << 8): _convert<2, false, 1, true, false, true>(width, height, rptr, wptr); break; - case FORMAT_RG8 | (FORMAT_R8 << 8): _convert<2, false, 1, false, false, false>(width, height, rptr, wptr); break; - case FORMAT_RG8 | (FORMAT_RGB8 << 8): _convert<2, false, 3, false, false, false>(width, height, rptr, wptr); break; - case FORMAT_RG8 | (FORMAT_RGBA8 << 8): _convert<2, false, 3, true, false, false>(width, height, rptr, wptr); break; - case FORMAT_RGB8 | (FORMAT_L8 << 8): _convert<3, false, 1, false, false, true>(width, height, rptr, wptr); break; - case FORMAT_RGB8 | (FORMAT_LA8 << 8): _convert<3, false, 1, true, false, true>(width, height, rptr, wptr); break; - case FORMAT_RGB8 | (FORMAT_R8 << 8): _convert<3, false, 1, false, false, false>(width, height, rptr, wptr); break; - case FORMAT_RGB8 | (FORMAT_RG8 << 8): _convert<3, false, 2, false, false, false>(width, height, rptr, wptr); break; - case FORMAT_RGB8 | (FORMAT_RGBA8 << 8): _convert<3, false, 3, true, false, false>(width, height, rptr, wptr); break; - case FORMAT_RGBA8 | (FORMAT_L8 << 8): _convert<3, true, 1, false, false, true>(width, height, rptr, wptr); break; - case FORMAT_RGBA8 | (FORMAT_LA8 << 8): _convert<3, true, 1, true, false, true>(width, height, rptr, wptr); break; - case FORMAT_RGBA8 | (FORMAT_R8 << 8): _convert<3, true, 1, false, false, false>(width, height, rptr, wptr); break; - case FORMAT_RGBA8 | (FORMAT_RG8 << 8): _convert<3, true, 2, false, false, false>(width, height, rptr, wptr); break; - case FORMAT_RGBA8 | (FORMAT_RGB8 << 8): _convert<3, true, 3, false, false, false>(width, height, rptr, wptr); break; + case FORMAT_L8 | (FORMAT_LA8 << 8): + _convert<1, false, 1, true, true, true>(width, height, rptr, wptr); + break; + case FORMAT_L8 | (FORMAT_R8 << 8): + _convert<1, false, 1, false, true, false>(width, height, rptr, wptr); + break; + case FORMAT_L8 | (FORMAT_RG8 << 8): + _convert<1, false, 2, false, true, false>(width, height, rptr, wptr); + break; + case FORMAT_L8 | (FORMAT_RGB8 << 8): + _convert<1, false, 3, false, true, false>(width, height, rptr, wptr); + break; + case FORMAT_L8 | (FORMAT_RGBA8 << 8): + _convert<1, false, 3, true, true, false>(width, height, rptr, wptr); + break; + case FORMAT_LA8 | (FORMAT_L8 << 8): + _convert<1, true, 1, false, true, true>(width, height, rptr, wptr); + break; + case FORMAT_LA8 | (FORMAT_R8 << 8): + _convert<1, true, 1, false, true, false>(width, height, rptr, wptr); + break; + case FORMAT_LA8 | (FORMAT_RG8 << 8): + _convert<1, true, 2, false, true, false>(width, height, rptr, wptr); + break; + case FORMAT_LA8 | (FORMAT_RGB8 << 8): + _convert<1, true, 3, false, true, false>(width, height, rptr, wptr); + break; + case FORMAT_LA8 | (FORMAT_RGBA8 << 8): + _convert<1, true, 3, true, true, false>(width, height, rptr, wptr); + break; + case FORMAT_R8 | (FORMAT_L8 << 8): + _convert<1, false, 1, false, false, true>(width, height, rptr, wptr); + break; + case FORMAT_R8 | (FORMAT_LA8 << 8): + _convert<1, false, 1, true, false, true>(width, height, rptr, wptr); + break; + case FORMAT_R8 | (FORMAT_RG8 << 8): + _convert<1, false, 2, false, false, false>(width, height, rptr, wptr); + break; + case FORMAT_R8 | (FORMAT_RGB8 << 8): + _convert<1, false, 3, false, false, false>(width, height, rptr, wptr); + break; + case FORMAT_R8 | (FORMAT_RGBA8 << 8): + _convert<1, false, 3, true, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RG8 | (FORMAT_L8 << 8): + _convert<2, false, 1, false, false, true>(width, height, rptr, wptr); + break; + case FORMAT_RG8 | (FORMAT_LA8 << 8): + _convert<2, false, 1, true, false, true>(width, height, rptr, wptr); + break; + case FORMAT_RG8 | (FORMAT_R8 << 8): + _convert<2, false, 1, false, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RG8 | (FORMAT_RGB8 << 8): + _convert<2, false, 3, false, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RG8 | (FORMAT_RGBA8 << 8): + _convert<2, false, 3, true, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RGB8 | (FORMAT_L8 << 8): + _convert<3, false, 1, false, false, true>(width, height, rptr, wptr); + break; + case FORMAT_RGB8 | (FORMAT_LA8 << 8): + _convert<3, false, 1, true, false, true>(width, height, rptr, wptr); + break; + case FORMAT_RGB8 | (FORMAT_R8 << 8): + _convert<3, false, 1, false, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RGB8 | (FORMAT_RG8 << 8): + _convert<3, false, 2, false, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RGB8 | (FORMAT_RGBA8 << 8): + _convert<3, false, 3, true, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RGBA8 | (FORMAT_L8 << 8): + _convert<3, true, 1, false, false, true>(width, height, rptr, wptr); + break; + case FORMAT_RGBA8 | (FORMAT_LA8 << 8): + _convert<3, true, 1, true, false, true>(width, height, rptr, wptr); + break; + case FORMAT_RGBA8 | (FORMAT_R8 << 8): + _convert<3, true, 1, false, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RGBA8 | (FORMAT_RG8 << 8): + _convert<3, true, 2, false, false, false>(width, height, rptr, wptr); + break; + case FORMAT_RGBA8 | (FORMAT_RGB8 << 8): + _convert<3, true, 3, false, false, false>(width, height, rptr, wptr); + break; } bool gen_mipmaps = mipmaps; _copy_internals_from(new_img); - if (gen_mipmaps) + if (gen_mipmaps) { generate_mipmaps(); + } } Image::Format Image::get_format() const { - return format; } static double _bicubic_interp_kernel(double x) { - x = ABS(x); double bc = 0; - if (x <= 1) + if (x <= 1) { bc = (1.5 * x - 2.5) * x * x + 1; - else if (x < 2) + } else if (x < 2) { bc = ((-0.5 * x + 2.5) * x - 4) * x + 2; + } return bc; } template <int CC, class T> static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { - // get source image size int width = p_src_width; int height = p_src_height; @@ -580,20 +704,24 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_ k1 = _bicubic_interp_kernel(dy - (double)n); oy2 = oy1 + n; - if (oy2 < 0) + if (oy2 < 0) { oy2 = 0; - if (oy2 > ymax) + } + if (oy2 > ymax) { oy2 = ymax; + } for (int m = -1; m < 3; m++) { // get X coefficient k2 = k1 * _bicubic_interp_kernel((double)m - dx); ox2 = ox1 + m; - if (ox2 < 0) + if (ox2 < 0) { ox2 = 0; - if (ox2 > xmax) + } + if (ox2 > xmax) { ox2 = xmax; + } // get pixel of original image const T *__restrict p = ((T *)p_src) + (oy2 * p_src_width + ox2) * CC; @@ -623,44 +751,43 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_ template <int CC, class T> static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { - enum { FRAC_BITS = 8, FRAC_LEN = (1 << FRAC_BITS), + FRAC_HALF = (FRAC_LEN >> 1), FRAC_MASK = FRAC_LEN - 1 - }; for (uint32_t i = 0; i < p_dst_height; i++) { - - uint32_t src_yofs_up_fp = (i * p_src_height * FRAC_LEN / p_dst_height); - uint32_t src_yofs_frac = src_yofs_up_fp & FRAC_MASK; - uint32_t src_yofs_up = src_yofs_up_fp >> FRAC_BITS; - - uint32_t src_yofs_down = (i + 1) * p_src_height / p_dst_height; - if (src_yofs_down >= p_src_height) + // Add 0.5 in order to interpolate based on pixel center + uint32_t src_yofs_up_fp = (i + 0.5) * p_src_height * FRAC_LEN / p_dst_height; + // Calculate nearest src pixel center above current, and truncate to get y index + uint32_t src_yofs_up = src_yofs_up_fp >= FRAC_HALF ? (src_yofs_up_fp - FRAC_HALF) >> FRAC_BITS : 0; + uint32_t src_yofs_down = (src_yofs_up_fp + FRAC_HALF) >> FRAC_BITS; + if (src_yofs_down >= p_src_height) { src_yofs_down = p_src_height - 1; - - //src_yofs_up*=CC; - //src_yofs_down*=CC; + } + // Calculate distance to pixel center of src_yofs_up + uint32_t src_yofs_frac = src_yofs_up_fp & FRAC_MASK; + src_yofs_frac = src_yofs_frac >= FRAC_HALF ? src_yofs_frac - FRAC_HALF : src_yofs_frac + FRAC_HALF; uint32_t y_ofs_up = src_yofs_up * p_src_width * CC; uint32_t y_ofs_down = src_yofs_down * p_src_width * CC; for (uint32_t j = 0; j < p_dst_width; j++) { - - uint32_t src_xofs_left_fp = (j * p_src_width * FRAC_LEN / p_dst_width); - uint32_t src_xofs_frac = src_xofs_left_fp & FRAC_MASK; - uint32_t src_xofs_left = src_xofs_left_fp >> FRAC_BITS; - uint32_t src_xofs_right = (j + 1) * p_src_width / p_dst_width; - if (src_xofs_right >= p_src_width) + uint32_t src_xofs_left_fp = (j + 0.5) * p_src_width * FRAC_LEN / p_dst_width; + uint32_t src_xofs_left = src_xofs_left_fp >= FRAC_HALF ? (src_xofs_left_fp - FRAC_HALF) >> FRAC_BITS : 0; + uint32_t src_xofs_right = (src_xofs_left_fp + FRAC_HALF) >> FRAC_BITS; + if (src_xofs_right >= p_src_width) { src_xofs_right = p_src_width - 1; + } + uint32_t src_xofs_frac = src_xofs_left_fp & FRAC_MASK; + src_xofs_frac = src_xofs_frac >= FRAC_HALF ? src_xofs_frac - FRAC_HALF : src_xofs_frac + FRAC_HALF; src_xofs_left *= CC; src_xofs_right *= CC; for (uint32_t l = 0; l < CC; l++) { - if (sizeof(T) == 1) { //uint8 uint32_t p00 = p_src[y_ofs_up + src_xofs_left + l] << FRAC_BITS; uint32_t p10 = p_src[y_ofs_up + src_xofs_right + l] << FRAC_BITS; @@ -714,19 +841,15 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict template <int CC, class T> static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { - for (uint32_t i = 0; i < p_dst_height; i++) { - uint32_t src_yofs = i * p_src_height / p_dst_height; uint32_t y_ofs = src_yofs * p_src_width * CC; for (uint32_t j = 0; j < p_dst_width; j++) { - uint32_t src_xofs = j * p_src_width / p_dst_width; src_xofs *= CC; for (uint32_t l = 0; l < CC; l++) { - const T *src = ((const T *)p_src); T *dst = ((T *)p_dst); @@ -745,7 +868,6 @@ static float _lanczos(float p_x) { template <int CC, class T> static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { - int32_t src_width = p_src_width; int32_t src_height = p_src_height; int32_t dst_height = p_dst_height; @@ -764,40 +886,40 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict float *kernel = memnew_arr(float, half_kernel * 2); for (int32_t buffer_x = 0; buffer_x < dst_width; buffer_x++) { - // The corresponding point on the source image float src_x = (buffer_x + 0.5f) * x_scale; // Offset by 0.5 so it uses the pixel's center int32_t start_x = MAX(0, int32_t(src_x) - half_kernel + 1); int32_t end_x = MIN(src_width - 1, int32_t(src_x) + half_kernel); // Create the kernel used by all the pixels of the column - for (int32_t target_x = start_x; target_x <= end_x; target_x++) + for (int32_t target_x = start_x; target_x <= end_x; target_x++) { kernel[target_x - start_x] = _lanczos((target_x + 0.5f - src_x) / scale_factor); + } for (int32_t buffer_y = 0; buffer_y < src_height; buffer_y++) { - float pixel[CC] = { 0 }; float weight = 0; for (int32_t target_x = start_x; target_x <= end_x; target_x++) { - float lanczos_val = kernel[target_x - start_x]; weight += lanczos_val; const T *__restrict src_data = ((const T *)p_src) + (buffer_y * src_width + target_x) * CC; for (uint32_t i = 0; i < CC; i++) { - if (sizeof(T) == 2) //half float + if (sizeof(T) == 2) { //half float pixel[i] += Math::half_to_float(src_data[i]) * lanczos_val; - else + } else { pixel[i] += src_data[i] * lanczos_val; + } } } float *dst_data = ((float *)buffer) + (buffer_y * dst_width + buffer_x) * CC; - for (uint32_t i = 0; i < CC; i++) + for (uint32_t i = 0; i < CC; i++) { dst_data[i] = pixel[i] / weight; // Normalize the sum of all the samples + } } } @@ -814,28 +936,27 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict float *kernel = memnew_arr(float, half_kernel * 2); for (int32_t dst_y = 0; dst_y < dst_height; dst_y++) { - float buffer_y = (dst_y + 0.5f) * y_scale; int32_t start_y = MAX(0, int32_t(buffer_y) - half_kernel + 1); int32_t end_y = MIN(src_height - 1, int32_t(buffer_y) + half_kernel); - for (int32_t target_y = start_y; target_y <= end_y; target_y++) + for (int32_t target_y = start_y; target_y <= end_y; target_y++) { kernel[target_y - start_y] = _lanczos((target_y + 0.5f - buffer_y) / scale_factor); + } for (int32_t dst_x = 0; dst_x < dst_width; dst_x++) { - float pixel[CC] = { 0 }; float weight = 0; for (int32_t target_y = start_y; target_y <= end_y; target_y++) { - float lanczos_val = kernel[target_y - start_y]; weight += lanczos_val; float *buffer_data = ((float *)buffer) + (target_y * dst_width + dst_x) * CC; - for (uint32_t i = 0; i < CC; i++) + for (uint32_t i = 0; i < CC; i++) { pixel[i] += buffer_data[i] * lanczos_val; + } } T *dst_data = ((T *)p_dst) + (dst_y * dst_width + dst_x) * CC; @@ -843,12 +964,13 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict for (uint32_t i = 0; i < CC; i++) { pixel[i] /= weight; - if (sizeof(T) == 1) //byte + if (sizeof(T) == 1) { //byte dst_data[i] = CLAMP(Math::fast_ftoi(pixel[i]), 0, 255); - else if (sizeof(T) == 2) //half float + } else if (sizeof(T) == 2) { //half float dst_data[i] = Math::make_half_float(pixel[i]); - else // float + } else { // float dst_data[i] = pixel[i]; + } } } } @@ -860,11 +982,9 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict } static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) { - uint16_t alpha = MIN((uint16_t)(p_alpha * 256.0f), 256); for (uint32_t i = 0; i < p_width * p_height * p_pixel_size; i++) { - p_dst[i] = (p_dst[i] * (256 - alpha) + p_src[i] * alpha) >> 8; } } @@ -873,8 +993,7 @@ bool Image::is_size_po2() const { return uint32_t(width) == next_power_of_2(width) && uint32_t(height) == next_power_of_2(height); } -void Image::resize_to_po2(bool p_square) { - +void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) { ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); int w = next_power_of_2(width); @@ -884,16 +1003,15 @@ void Image::resize_to_po2(bool p_square) { } if (w == width && h == height) { - - if (!p_square || w == h) + if (!p_square || w == h) { return; //nothing to do + } } - resize(w, h); + resize(w, h, p_interpolation); } void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { - ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use create() or create_from_data() first."); ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); @@ -905,10 +1023,11 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); - if (p_width == width && p_height == height) + if (p_width == width && p_height == height) { return; + } - Image dst(p_width, p_height, 0, format); + Image dst(p_width, p_height, false, format); // Setup mipmap-aware scaling Image dst2; @@ -928,7 +1047,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } bool interpolate_mipmaps = mipmap_aware && mip1 != mip2; if (interpolate_mipmaps) { - dst2.create(p_width, p_height, 0, format); + dst2.create(p_width, p_height, false, format); } bool had_mipmaps = mipmaps; @@ -944,37 +1063,58 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { unsigned char *w_ptr = w; switch (p_interpolation) { - case INTERPOLATE_NEAREST: { - if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { switch (get_format_pixel_size(format)) { - case 1: _scale_nearest<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_nearest<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_nearest<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_nearest<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 1: + _scale_nearest<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 2: + _scale_nearest<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 3: + _scale_nearest<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 4: + _scale_nearest<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { switch (get_format_pixel_size(format)) { - case 4: _scale_nearest<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 8: _scale_nearest<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 12: _scale_nearest<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 16: _scale_nearest<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: + _scale_nearest<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 8: + _scale_nearest<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 12: + _scale_nearest<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 16: + _scale_nearest<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { switch (get_format_pixel_size(format)) { - case 2: _scale_nearest<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_nearest<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 6: _scale_nearest<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 8: _scale_nearest<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: + _scale_nearest<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 4: + _scale_nearest<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 6: + _scale_nearest<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 8: + _scale_nearest<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } } break; case INTERPOLATE_BILINEAR: case INTERPOLATE_TRILINEAR: { - for (int i = 0; i < 2; ++i) { int src_width; int src_height; @@ -1013,24 +1153,48 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { switch (get_format_pixel_size(format)) { - case 1: _scale_bilinear<1, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 2: _scale_bilinear<2, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 3: _scale_bilinear<3, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 4: _scale_bilinear<4, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 1: + _scale_bilinear<1, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 2: + _scale_bilinear<2, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 3: + _scale_bilinear<3, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 4: + _scale_bilinear<4, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; } } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { switch (get_format_pixel_size(format)) { - case 4: _scale_bilinear<1, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 8: _scale_bilinear<2, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 12: _scale_bilinear<3, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 16: _scale_bilinear<4, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 4: + _scale_bilinear<1, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 8: + _scale_bilinear<2, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 12: + _scale_bilinear<3, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 16: + _scale_bilinear<4, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; } } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { switch (get_format_pixel_size(format)) { - case 2: _scale_bilinear<1, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 4: _scale_bilinear<2, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 6: _scale_bilinear<3, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 8: _scale_bilinear<4, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 2: + _scale_bilinear<1, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 4: + _scale_bilinear<2, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 6: + _scale_bilinear<3, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; + case 8: + _scale_bilinear<4, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); + break; } } } @@ -1043,52 +1207,98 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } break; case INTERPOLATE_CUBIC: { - if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { switch (get_format_pixel_size(format)) { - case 1: _scale_cubic<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_cubic<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_cubic<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_cubic<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 1: + _scale_cubic<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 2: + _scale_cubic<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 3: + _scale_cubic<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 4: + _scale_cubic<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { switch (get_format_pixel_size(format)) { - case 4: _scale_cubic<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 8: _scale_cubic<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 12: _scale_cubic<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 16: _scale_cubic<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: + _scale_cubic<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 8: + _scale_cubic<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 12: + _scale_cubic<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 16: + _scale_cubic<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { switch (get_format_pixel_size(format)) { - case 2: _scale_cubic<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_cubic<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 6: _scale_cubic<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 8: _scale_cubic<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: + _scale_cubic<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 4: + _scale_cubic<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 6: + _scale_cubic<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 8: + _scale_cubic<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } } break; case INTERPOLATE_LANCZOS: { - if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { switch (get_format_pixel_size(format)) { - case 1: _scale_lanczos<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_lanczos<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_lanczos<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_lanczos<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 1: + _scale_lanczos<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 2: + _scale_lanczos<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 3: + _scale_lanczos<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 4: + _scale_lanczos<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { switch (get_format_pixel_size(format)) { - case 4: _scale_lanczos<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 8: _scale_lanczos<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 12: _scale_lanczos<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 16: _scale_lanczos<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: + _scale_lanczos<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 8: + _scale_lanczos<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 12: + _scale_lanczos<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 16: + _scale_lanczos<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { switch (get_format_pixel_size(format)) { - case 2: _scale_lanczos<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_lanczos<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 6: _scale_lanczos<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 8: _scale_lanczos<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: + _scale_lanczos<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 4: + _scale_lanczos<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 6: + _scale_lanczos<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; + case 8: + _scale_lanczos<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); + break; } } } break; @@ -1098,14 +1308,14 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { dst._copy_internals_from(dst2); } - if (had_mipmaps) + if (had_mipmaps) { dst.generate_mipmaps(); + } _copy_internals_from(dst); } void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { - ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot crop in compressed or custom image formats."); ERR_FAIL_COND_MSG(p_x < 0, "Start x position cannot be smaller than 0."); @@ -1119,13 +1329,14 @@ void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { will most likely either not be used much, or in critical areas, for now it won't, because it's a waste of time. */ - if (p_width == width && p_height == height && p_x == 0 && p_y == 0) + if (p_width == width && p_height == height && p_x == 0 && p_y == 0) { return; + } uint8_t pdata[16]; //largest is 16 uint32_t pixel_size = get_format_pixel_size(format); - Image dst(p_width, p_height, 0, format); + Image dst(p_width, p_height, false, format); { const uint8_t *r = data.ptr(); @@ -1134,12 +1345,11 @@ void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { int m_h = p_y + p_height; int m_w = p_x + p_width; for (int y = p_y; y < m_h; y++) { - for (int x = p_x; x < m_w; x++) { - if ((x >= width || y >= height)) { - for (uint32_t i = 0; i < pixel_size; i++) + for (uint32_t i = 0; i < pixel_size; i++) { pdata[i] = 0; + } } else { _get_pixelb(x, y, pixel_size, r, pdata); } @@ -1149,18 +1359,17 @@ void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { } } - if (has_mipmaps()) + if (has_mipmaps()) { dst.generate_mipmaps(); + } _copy_internals_from(dst); } void Image::crop(int p_width, int p_height) { - crop_from_point(0, 0, p_width, p_height); } void Image::flip_y() { - ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_y in compressed or custom image formats."); bool used_mipmaps = has_mipmaps(); @@ -1175,9 +1384,7 @@ void Image::flip_y() { uint32_t pixel_size = get_format_pixel_size(format); for (int y = 0; y < height / 2; y++) { - for (int x = 0; x < width; x++) { - _get_pixelb(x, y, pixel_size, w, up); _get_pixelb(x, height - y - 1, pixel_size, w, down); @@ -1193,7 +1400,6 @@ void Image::flip_y() { } void Image::flip_x() { - ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_x in compressed or custom image formats."); bool used_mipmaps = has_mipmaps(); @@ -1208,9 +1414,7 @@ void Image::flip_x() { uint32_t pixel_size = get_format_pixel_size(format); for (int y = 0; y < height; y++) { - for (int x = 0; x < width / 2; x++) { - _get_pixelb(x, y, pixel_size, w, up); _get_pixelb(width - x - 1, y, pixel_size, w, down); @@ -1226,7 +1430,6 @@ void Image::flip_x() { } int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) { - int size = 0; int w = p_width; int h = p_height; @@ -1241,7 +1444,6 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & int minw = 1, minh = 1; while (true) { - int bw = w % block != 0 ? w + (block - w % block) : w; int bh = h % block != 0 ? h + (block - h % block) : h; @@ -1259,28 +1461,28 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & *r_mm_height = bh; } - if (p_mipmaps >= 0 && mm == p_mipmaps) + if (p_mipmaps >= 0 && mm == p_mipmaps) { break; + } if (p_mipmaps >= 0) { - w = MAX(minw, w >> 1); h = MAX(minh, h >> 1); } else { - if (w == minw && h == minh) + if (w == minw && h == minh) { break; + } w = MAX(minw, w >> 1); h = MAX(minh, h >> 1); } mm++; - }; + } r_mipmaps = mm; return size; } bool Image::_can_modify(Format p_format) const { - return p_format <= FORMAT_RGBE9995; } @@ -1288,7 +1490,6 @@ template <class Component, int CC, bool renormalize, void (*average_func)(Component &, const Component &, const Component &, const Component &, const Component &), void (*renormalize_func)(Component *)> static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint32_t p_width, uint32_t p_height) { - //fast power of 2 mipmap generation uint32_t dst_w = MAX(p_width >> 1, 1); uint32_t dst_h = MAX(p_height >> 1, 1); @@ -1297,7 +1498,6 @@ static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint3 int down_step = (p_height == 1) ? 0 : (p_width * CC); for (uint32_t i = 0; i < dst_h; i++) { - const Component *rup_ptr = &p_src[i * 2 * down_step]; const Component *rdown_ptr = rup_ptr + down_step; Component *dst_ptr = &p_dst[i * dst_w * CC]; @@ -1320,52 +1520,10 @@ static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint3 } } -void Image::expand_x2_hq2x() { - - ERR_FAIL_COND(!_can_modify(format)); - - bool used_mipmaps = has_mipmaps(); - if (used_mipmaps) { - clear_mipmaps(); - } - - Format current = format; - - if (current != FORMAT_RGBA8) - convert(FORMAT_RGBA8); - - Vector<uint8_t> dest; - dest.resize(width * 2 * height * 2 * 4); - - { - const uint8_t *r = data.ptr(); - uint8_t *w = dest.ptrw(); - - ERR_FAIL_COND(!r); - - hq2x_resize((const uint32_t *)r, width, height, (uint32_t *)w); - } - - width *= 2; - height *= 2; - data = dest; - - if (current != FORMAT_RGBA8) - convert(current); - - // FIXME: This is likely meant to use "used_mipmaps" as defined above, but if we do, - // we end up with a regression: GH-22747 - if (mipmaps) { - generate_mipmaps(); - } -} - void Image::shrink_x2() { - ERR_FAIL_COND(data.size() == 0); if (mipmaps) { - //just use the lower mipmap as base and copy all Vector<uint8_t> new_img; @@ -1387,7 +1545,6 @@ void Image::shrink_x2() { data = new_img; } else { - Vector<uint8_t> new_img; ERR_FAIL_COND(!_can_modify(format)); @@ -1401,25 +1558,52 @@ void Image::shrink_x2() { const uint8_t *r = data.ptr(); switch (format) { - case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<uint8_t, 1, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); break; - case FORMAT_LA8: _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); break; - case FORMAT_RG8: _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); break; - case FORMAT_RGB8: _generate_po2_mipmap<uint8_t, 3, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); break; - case FORMAT_RGBA8: _generate_po2_mipmap<uint8_t, 4, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); break; - - case FORMAT_RF: _generate_po2_mipmap<float, 1, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r), reinterpret_cast<float *>(w), width, height); break; - case FORMAT_RGF: _generate_po2_mipmap<float, 2, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r), reinterpret_cast<float *>(w), width, height); break; - case FORMAT_RGBF: _generate_po2_mipmap<float, 3, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r), reinterpret_cast<float *>(w), width, height); break; - case FORMAT_RGBAF: _generate_po2_mipmap<float, 4, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r), reinterpret_cast<float *>(w), width, height); break; - - case FORMAT_RH: _generate_po2_mipmap<uint16_t, 1, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r), reinterpret_cast<uint16_t *>(w), width, height); break; - case FORMAT_RGH: _generate_po2_mipmap<uint16_t, 2, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r), reinterpret_cast<uint16_t *>(w), width, height); break; - case FORMAT_RGBH: _generate_po2_mipmap<uint16_t, 3, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r), reinterpret_cast<uint16_t *>(w), width, height); break; - case FORMAT_RGBAH: _generate_po2_mipmap<uint16_t, 4, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r), reinterpret_cast<uint16_t *>(w), width, height); break; - - case FORMAT_RGBE9995: _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(r), reinterpret_cast<uint32_t *>(w), width, height); break; + case FORMAT_R8: + _generate_po2_mipmap<uint8_t, 1, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); + break; + case FORMAT_LA8: + _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); + break; + case FORMAT_RG8: + _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); + break; + case FORMAT_RGB8: + _generate_po2_mipmap<uint8_t, 3, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); + break; + case FORMAT_RGBA8: + _generate_po2_mipmap<uint8_t, 4, false, Image::average_4_uint8, Image::renormalize_uint8>(r, w, width, height); + break; + + case FORMAT_RF: + _generate_po2_mipmap<float, 1, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r), reinterpret_cast<float *>(w), width, height); + break; + case FORMAT_RGF: + _generate_po2_mipmap<float, 2, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r), reinterpret_cast<float *>(w), width, height); + break; + case FORMAT_RGBF: + _generate_po2_mipmap<float, 3, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r), reinterpret_cast<float *>(w), width, height); + break; + case FORMAT_RGBAF: + _generate_po2_mipmap<float, 4, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r), reinterpret_cast<float *>(w), width, height); + break; + + case FORMAT_RH: + _generate_po2_mipmap<uint16_t, 1, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r), reinterpret_cast<uint16_t *>(w), width, height); + break; + case FORMAT_RGH: + _generate_po2_mipmap<uint16_t, 2, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r), reinterpret_cast<uint16_t *>(w), width, height); + break; + case FORMAT_RGBH: + _generate_po2_mipmap<uint16_t, 3, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r), reinterpret_cast<uint16_t *>(w), width, height); + break; + case FORMAT_RGBAH: + _generate_po2_mipmap<uint16_t, 4, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r), reinterpret_cast<uint16_t *>(w), width, height); + break; + + case FORMAT_RGBE9995: + _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(r), reinterpret_cast<uint32_t *>(w), width, height); + break; default: { } } @@ -1432,16 +1616,13 @@ void Image::shrink_x2() { } void Image::normalize() { - bool used_mipmaps = has_mipmaps(); if (used_mipmaps) { clear_mipmaps(); } for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - Color c = get_pixel(x, y); Vector3 v(c.r * 2.0 - 1.0, c.g * 2.0 - 1.0, c.b * 2.0 - 1.0); v.normalize(); @@ -1458,7 +1639,6 @@ void Image::normalize() { } Error Image::generate_mipmaps(bool p_renormalize) { - ERR_FAIL_COND_V_MSG(!_can_modify(format), ERR_UNAVAILABLE, "Cannot generate mipmaps in compressed or custom image formats."); ERR_FAIL_COND_V_MSG(format == FORMAT_RGBA4444, ERR_UNAVAILABLE, "Cannot generate mipmaps from RGBA4444 format."); @@ -1478,28 +1658,32 @@ Error Image::generate_mipmaps(bool p_renormalize) { int prev_w = width; for (int i = 1; i <= mmcount; i++) { - int ofs, w, h; _get_mipmap_offset_and_size(i, ofs, w, h); switch (format) { - case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<uint8_t, 1, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_R8: + _generate_po2_mipmap<uint8_t, 1, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + break; case FORMAT_LA8: - case FORMAT_RG8: _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_RG8: + _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + break; case FORMAT_RGB8: - if (p_renormalize) + if (p_renormalize) { _generate_po2_mipmap<uint8_t, 3, true, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - else + } else { _generate_po2_mipmap<uint8_t, 3, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + } break; case FORMAT_RGBA8: - if (p_renormalize) + if (p_renormalize) { _generate_po2_mipmap<uint8_t, 4, true, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - else + } else { _generate_po2_mipmap<uint8_t, 4, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + } break; case FORMAT_RF: _generate_po2_mipmap<float, 1, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); @@ -1508,17 +1692,19 @@ Error Image::generate_mipmaps(bool p_renormalize) { _generate_po2_mipmap<float, 2, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); break; case FORMAT_RGBF: - if (p_renormalize) + if (p_renormalize) { _generate_po2_mipmap<float, 3, true, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); - else + } else { _generate_po2_mipmap<float, 3, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); + } break; case FORMAT_RGBAF: - if (p_renormalize) + if (p_renormalize) { _generate_po2_mipmap<float, 4, true, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); - else + } else { _generate_po2_mipmap<float, 4, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); + } break; case FORMAT_RH: @@ -1528,24 +1714,27 @@ Error Image::generate_mipmaps(bool p_renormalize) { _generate_po2_mipmap<uint16_t, 2, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); break; case FORMAT_RGBH: - if (p_renormalize) + if (p_renormalize) { _generate_po2_mipmap<uint16_t, 3, true, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); - else + } else { _generate_po2_mipmap<uint16_t, 3, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); + } break; case FORMAT_RGBAH: - if (p_renormalize) + if (p_renormalize) { _generate_po2_mipmap<uint16_t, 4, true, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); - else + } else { _generate_po2_mipmap<uint16_t, 4, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); + } break; case FORMAT_RGBE9995: - if (p_renormalize) + if (p_renormalize) { _generate_po2_mipmap<uint32_t, 1, true, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(&wp[prev_ofs]), reinterpret_cast<uint32_t *>(&wp[ofs]), prev_w, prev_h); - else + } else { _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(&wp[prev_ofs]), reinterpret_cast<uint32_t *>(&wp[ofs]), prev_w, prev_h); + } break; default: { @@ -1563,12 +1752,11 @@ Error Image::generate_mipmaps(bool p_renormalize) { } Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map) { - Vector<double> normal_sat_vec; //summed area table - double *normal_sat = nullptr; //summed area table for normalmap + double *normal_sat = nullptr; //summed area table for normal map int normal_w = 0, normal_h = 0; - ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->empty(), ERR_INVALID_PARAMETER, "Must provide a valid normalmap for roughness mipmaps"); + ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->is_empty(), ERR_INVALID_PARAMETER, "Must provide a valid normal map for roughness mipmaps"); Ref<Image> nm = p_normal_map->duplicate(); if (nm->is_compressed()) { @@ -1628,7 +1816,6 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con uint8_t *base_ptr = data.ptrw(); for (int i = 1; i <= mmcount; i++) { - int ofs, w, h; _get_mipmap_offset_and_size(i, ofs, w, h); uint8_t *ptr = &base_ptr[ofs]; @@ -1759,12 +1946,13 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con } void Image::clear_mipmaps() { - - if (!mipmaps) + if (!mipmaps) { return; + } - if (empty()) + if (is_empty()) { return; + } int ofs, w, h; _get_mipmap_offset_and_size(1, ofs, w, h); @@ -1773,20 +1961,19 @@ void Image::clear_mipmaps() { mipmaps = false; } -bool Image::empty() const { - +bool Image::is_empty() const { return (data.size() == 0); } Vector<uint8_t> Image::get_data() const { - return data; } void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { - - ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH); - ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0."); + ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0."); + ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); + ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); int mm = 0; @@ -1805,9 +1992,10 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma } void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { - - ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH); - ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0."); + ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0."); + ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); + ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); int mm; @@ -1824,7 +2012,6 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma } void Image::create(const char **p_xpm) { - int size_width = 0; int size_height = 0; int pixelchars = 0; @@ -1844,16 +2031,13 @@ void Image::create(const char **p_xpm) { HashMap<String, Color> colormap; int colormap_size = 0; uint32_t pixel_size = 0; - uint8_t *w; + uint8_t *data_write = nullptr; while (status != DONE) { - const char *line_ptr = p_xpm[line]; switch (status) { - case READING_HEADER: { - String line_str = line_ptr; line_str.replace("\t", " "); @@ -1868,25 +2052,24 @@ void Image::create(const char **p_xpm) { status = READING_COLORS; } break; case READING_COLORS: { - String colorstring; for (int i = 0; i < pixelchars; i++) { - colorstring += *line_ptr; line_ptr++; } //skip spaces while (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == 0) { - if (*line_ptr == 0) + if (*line_ptr == 0) { break; + } line_ptr++; } if (*line_ptr == 'c') { - line_ptr++; while (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == 0) { - if (*line_ptr == 0) + if (*line_ptr == 0) { break; + } line_ptr++; } @@ -1898,55 +2081,63 @@ void Image::create(const char **p_xpm) { //uint8_t col_a=255; for (int i = 0; i < 6; i++) { - char v = line_ptr[i]; - if (v >= '0' && v <= '9') + if (v >= '0' && v <= '9') { v -= '0'; - else if (v >= 'A' && v <= 'F') + } else if (v >= 'A' && v <= 'F') { v = (v - 'A') + 10; - else if (v >= 'a' && v <= 'f') + } else if (v >= 'a' && v <= 'f') { v = (v - 'a') + 10; - else + } else { break; + } switch (i) { - case 0: col_r = v << 4; break; - case 1: col_r |= v; break; - case 2: col_g = v << 4; break; - case 3: col_g |= v; break; - case 4: col_b = v << 4; break; - case 5: col_b |= v; break; - }; + case 0: + col_r = v << 4; + break; + case 1: + col_r |= v; + break; + case 2: + col_g = v << 4; + break; + case 3: + col_g |= v; + break; + case 4: + col_b = v << 4; + break; + case 5: + col_b |= v; + break; + } } // magenta mask if (col_r == 255 && col_g == 0 && col_b == 255) { - colormap[colorstring] = Color(0, 0, 0, 0); has_alpha = true; } else { - colormap[colorstring] = Color(col_r / 255.0, col_g / 255.0, col_b / 255.0, 1.0); } } } if (line == colormap_size) { - status = READING_PIXELS; - create(size_width, size_height, 0, has_alpha ? FORMAT_RGBA8 : FORMAT_RGB8); - w = data.ptrw(); + create(size_width, size_height, false, has_alpha ? FORMAT_RGBA8 : FORMAT_RGB8); + data_write = data.ptrw(); pixel_size = has_alpha ? 4 : 3; } } break; case READING_PIXELS: { - int y = line - colormap_size - 1; for (int x = 0; x < size_width; x++) { - char pixelstr[6] = { 0, 0, 0, 0, 0, 0 }; - for (int i = 0; i < pixelchars; i++) + for (int i = 0; i < pixelchars; i++) { pixelstr[i] = line_ptr[x * pixelchars + i]; + } Color *colorptr = colormap.getptr(pixelstr); ERR_FAIL_COND(!colorptr); @@ -1954,11 +2145,12 @@ void Image::create(const char **p_xpm) { for (uint32_t i = 0; i < pixel_size; i++) { pixel[i] = CLAMP((*colorptr)[i] * 255, 0, 255); } - _put_pixelb(x, y, pixel_size, w, pixel); + _put_pixelb(x, y, pixel_size, data_write, pixel); } - if (y == (size_height - 1)) + if (y == (size_height - 1)) { status = DONE; + } } break; default: { } @@ -1976,7 +2168,6 @@ void Image::create(const char **p_xpm) { if (value < DETECT_ALPHA_MIN_THRESHOLD) \ bit = true; \ else if (value < DETECT_ALPHA_MAX_THRESHOLD) { \ - \ detected = true; \ break; \ } \ @@ -1986,22 +2177,22 @@ void Image::create(const char **p_xpm) { { \ uint8_t value = m_value; \ if (value > 0) { \ - \ detected = true; \ break; \ } \ } bool Image::is_invisible() const { - if (format == FORMAT_L8 || - format == FORMAT_RGB8 || format == FORMAT_RG8) + format == FORMAT_RGB8 || format == FORMAT_RG8) { return false; + } int len = data.size(); - if (len == 0) + if (len == 0) { return true; + } int w, h; _get_mipmap_offset_and_size(1, len, w, h); @@ -2012,24 +2203,21 @@ bool Image::is_invisible() const { bool detected = false; switch (format) { - case FORMAT_LA8: { - for (int i = 0; i < (len >> 1); i++) { DETECT_NON_ALPHA(data_ptr[(i << 1) + 1]); } } break; case FORMAT_RGBA8: { - for (int i = 0; i < (len >> 2); i++) { DETECT_NON_ALPHA(data_ptr[(i << 2) + 3]) } } break; - case FORMAT_PVRTC2A: - case FORMAT_PVRTC4A: + case FORMAT_PVRTC1_2A: + case FORMAT_PVRTC1_4A: case FORMAT_DXT3: case FORMAT_DXT5: { detected = true; @@ -2042,11 +2230,11 @@ bool Image::is_invisible() const { } Image::AlphaMode Image::detect_alpha() const { - int len = data.size(); - if (len == 0) + if (len == 0) { return ALPHA_NONE; + } int w, h; _get_mipmap_offset_and_size(1, len, w, h); @@ -2058,23 +2246,20 @@ Image::AlphaMode Image::detect_alpha() const { bool detected = false; switch (format) { - case FORMAT_LA8: { - for (int i = 0; i < (len >> 1); i++) { DETECT_ALPHA(data_ptr[(i << 1) + 1]); } } break; case FORMAT_RGBA8: { - for (int i = 0; i < (len >> 2); i++) { DETECT_ALPHA(data_ptr[(i << 2) + 3]) } } break; - case FORMAT_PVRTC2A: - case FORMAT_PVRTC4A: + case FORMAT_PVRTC1_2A: + case FORMAT_PVRTC1_4A: case FORMAT_DXT3: case FORMAT_DXT5: { detected = true; @@ -2083,12 +2268,13 @@ Image::AlphaMode Image::detect_alpha() const { } } - if (detected) + if (detected) { return ALPHA_BLEND; - else if (bit) + } else if (bit) { return ALPHA_BIT; - else + } else { return ALPHA_NONE; + } } Error Image::load(const String &p_path) { @@ -2101,9 +2287,9 @@ Error Image::load(const String &p_path) { } Error Image::save_png(const String &p_path) const { - - if (save_png_func == nullptr) + if (save_png_func == nullptr) { return ERR_UNAVAILABLE; + } return save_png_func(p_path, Ref<Image>((Image *)this)); } @@ -2117,21 +2303,19 @@ Vector<uint8_t> Image::save_png_to_buffer() const { } Error Image::save_exr(const String &p_path, bool p_grayscale) const { - - if (save_exr_func == nullptr) + if (save_exr_func == nullptr) { return ERR_UNAVAILABLE; + } return save_exr_func(p_path, Ref<Image>((Image *)this), p_grayscale); } int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { - int mm; return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0); } int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format) { - int mm; _get_dst_image_size(p_width, p_height, p_format, mm, -1); return mm; @@ -2145,7 +2329,6 @@ Size2i Image::get_image_mipmap_size(int p_width, int p_height, Format p_format, } int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) { - if (p_mipmap <= 0) { return 0; } @@ -2154,7 +2337,6 @@ int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, i } int Image::get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h) { - if (p_mipmap <= 0) { r_w = p_width; r_h = p_height; @@ -2169,57 +2351,45 @@ bool Image::is_compressed() const { } Error Image::decompress() { - - if (((format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG) || (format == FORMAT_DXT5_RA_AS_RG)) && _image_decompress_bc) + if (((format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG) || (format == FORMAT_DXT5_RA_AS_RG)) && _image_decompress_bc) { _image_decompress_bc(this); - else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc) + } else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc) { _image_decompress_bptc(this); - else if (format >= FORMAT_PVRTC2 && format <= FORMAT_PVRTC4A && _image_decompress_pvrtc) + } else if (format >= FORMAT_PVRTC1_2 && format <= FORMAT_PVRTC1_4A && _image_decompress_pvrtc) { _image_decompress_pvrtc(this); - else if (format == FORMAT_ETC && _image_decompress_etc1) + } else if (format == FORMAT_ETC && _image_decompress_etc1) { _image_decompress_etc1(this); - else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RA_AS_RG && _image_decompress_etc2) + } else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RA_AS_RG && _image_decompress_etc2) { _image_decompress_etc2(this); - else + } else { return ERR_UNAVAILABLE; + } return OK; } Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) { - return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality); } -Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality) { +Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality) { switch (p_mode) { - case COMPRESS_S3TC: { - ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); _image_compress_bc_func(this, p_lossy_quality, p_channels); } break; - case COMPRESS_PVRTC2: { - - ERR_FAIL_COND_V(!_image_compress_pvrtc2_func, ERR_UNAVAILABLE); - _image_compress_pvrtc2_func(this); - } break; - case COMPRESS_PVRTC4: { - - ERR_FAIL_COND_V(!_image_compress_pvrtc4_func, ERR_UNAVAILABLE); - _image_compress_pvrtc4_func(this); + case COMPRESS_PVRTC1_4: { + ERR_FAIL_COND_V(!_image_compress_pvrtc1_4bpp_func, ERR_UNAVAILABLE); + _image_compress_pvrtc1_4bpp_func(this); } break; case COMPRESS_ETC: { - ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE); _image_compress_etc1_func(this, p_lossy_quality); } break; case COMPRESS_ETC2: { - ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE); _image_compress_etc2_func(this, p_lossy_quality, p_channels); } break; case COMPRESS_BPTC: { - ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); _image_compress_bptc_func(this, p_lossy_quality, p_channels); } break; @@ -2229,7 +2399,6 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels } Image::Image(const char **p_xpm) { - width = 0; height = 0; mipmaps = false; @@ -2239,7 +2408,6 @@ Image::Image(const char **p_xpm) { } Image::Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { - width = 0; height = 0; mipmaps = p_use_mipmaps; @@ -2249,7 +2417,6 @@ Image::Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { } Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { - width = 0; height = 0; mipmaps = p_mipmaps; @@ -2259,48 +2426,52 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const V } Rect2 Image::get_used_rect() const { - - if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGB565) + if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGB565) { return Rect2(Point2(), Size2(width, height)); + } int len = data.size(); - if (len == 0) + if (len == 0) { return Rect2(); + } int minx = 0xFFFFFF, miny = 0xFFFFFFF; int maxx = -1, maxy = -1; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { - - if (!(get_pixel(i, j).a > 0)) + if (!(get_pixel(i, j).a > 0)) { continue; - if (i > maxx) + } + if (i > maxx) { maxx = i; - if (j > maxy) + } + if (j > maxy) { maxy = j; - if (i < minx) + } + if (i < minx) { minx = i; - if (j < miny) + } + if (j < miny) { miny = j; + } } } - if (maxx == -1) + if (maxx == -1) { return Rect2(); - else + } else { return Rect2(minx, miny, maxx - minx + 1, maxy - miny + 1); + } } Ref<Image> Image::get_rect(const Rect2 &p_area) const { - Ref<Image> img = memnew(Image(p_area.size.x, p_area.size.y, mipmaps, format)); img->blit_rect(Ref<Image>((Image *)this), p_area, Point2(0, 0)); return img; } void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); @@ -2309,18 +2480,21 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po ERR_FAIL_COND(format != p_src->format); ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot blit_rect in compressed or custom image formats."); - Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).intersection(p_src_rect); - if (p_dest.x < 0) + if (p_dest.x < 0) { clipped_src_rect.position.x = ABS(p_dest.x); - if (p_dest.y < 0) + } + if (p_dest.y < 0) { clipped_src_rect.position.y = ABS(p_dest.y); + } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { return; + } Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y)); - Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); + Rect2i dest_rect = Rect2i(0, 0, width, height).intersection(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); uint8_t *wp = data.ptrw(); uint8_t *dst_data_ptr = wp; @@ -2331,9 +2505,7 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po int pixel_size = get_format_pixel_size(format); for (int i = 0; i < dest_rect.size.y; i++) { - for (int j = 0; j < dest_rect.size.x; j++) { - int src_x = clipped_src_rect.position.x + j; int src_y = clipped_src_rect.position.y + i; @@ -2351,7 +2523,6 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po } void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); @@ -2364,18 +2535,21 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co ERR_FAIL_COND_MSG(p_src->height != p_mask->height, "Source image height is different from mask height."); ERR_FAIL_COND(format != p_src->format); - Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).intersection(p_src_rect); - if (p_dest.x < 0) + if (p_dest.x < 0) { clipped_src_rect.position.x = ABS(p_dest.x); - if (p_dest.y < 0) + } + if (p_dest.y < 0) { clipped_src_rect.position.y = ABS(p_dest.y); + } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { return; + } Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y)); - Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); + Rect2i dest_rect = Rect2i(0, 0, width, height).intersection(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); uint8_t *wp = data.ptrw(); uint8_t *dst_data_ptr = wp; @@ -2388,14 +2562,11 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co Ref<Image> msk = p_mask; for (int i = 0; i < dest_rect.size.y; i++) { - for (int j = 0; j < dest_rect.size.x; j++) { - int src_x = clipped_src_rect.position.x + j; int src_y = clipped_src_rect.position.y + i; if (msk->get_pixel(src_x, src_y).a != 0) { - int dst_x = dest_rect.position.x + j; int dst_y = dest_rect.position.y + i; @@ -2411,7 +2582,6 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co } void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); @@ -2419,25 +2589,26 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P ERR_FAIL_COND(srcdsize == 0); ERR_FAIL_COND(format != p_src->format); - Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).intersection(p_src_rect); - if (p_dest.x < 0) + if (p_dest.x < 0) { clipped_src_rect.position.x = ABS(p_dest.x); - if (p_dest.y < 0) + } + if (p_dest.y < 0) { clipped_src_rect.position.y = ABS(p_dest.y); + } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { return; + } Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y)); - Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); + Rect2i dest_rect = Rect2i(0, 0, width, height).intersection(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); Ref<Image> img = p_src; for (int i = 0; i < dest_rect.size.y; i++) { - for (int j = 0; j < dest_rect.size.x; j++) { - int src_x = clipped_src_rect.position.x + j; int src_y = clipped_src_rect.position.y + i; @@ -2445,18 +2616,16 @@ 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); + } } } } void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); @@ -2469,26 +2638,27 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c ERR_FAIL_COND_MSG(p_src->height != p_mask->height, "Source image height is different from mask height."); ERR_FAIL_COND(format != p_src->format); - Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).intersection(p_src_rect); - if (p_dest.x < 0) + if (p_dest.x < 0) { clipped_src_rect.position.x = ABS(p_dest.x); - if (p_dest.y < 0) + } + if (p_dest.y < 0) { clipped_src_rect.position.y = ABS(p_dest.y); + } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { return; + } Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y)); - Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); + Rect2i dest_rect = Rect2i(0, 0, width, height).intersection(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); Ref<Image> img = p_src; Ref<Image> msk = p_mask; for (int i = 0; i < dest_rect.size.y; i++) { - for (int j = 0; j < dest_rect.size.x; j++) { - int src_x = clipped_src_rect.position.x + j; int src_y = clipped_src_rect.position.y + i; @@ -2496,17 +2666,15 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c //Color c = msk->get_pixel(src_x, src_y); //if (c.a == 0) continue; if (msk->get_pixel(src_x, src_y).a != 0) { - int dst_x = dest_rect.position.x + j; 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); + } } } } @@ -2524,9 +2692,7 @@ void Image::fill(const Color &c) { set_pixel(0, 0, c); for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uint8_t *dst = &dst_data_ptr[(y * width + x) * pixel_size]; for (int k = 0; k < pixel_size; k++) { @@ -2539,11 +2705,12 @@ void Image::fill(const Color &c) { ImageMemLoadFunc Image::_png_mem_loader_func = nullptr; ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr; ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr; -void (*Image::_image_compress_pvrtc2_func)(Image *) = nullptr; -void (*Image::_image_compress_pvrtc4_func)(Image *) = nullptr; +void (*Image::_image_compress_pvrtc1_4bpp_func)(Image *) = nullptr; void (*Image::_image_compress_etc1_func)(Image *, float) = nullptr; void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = nullptr; void (*Image::_image_decompress_pvrtc)(Image *) = nullptr; @@ -2560,7 +2727,6 @@ Vector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::Used Ref<Image> (*Image::basis_universal_unpacker)(const Vector<uint8_t> &) = nullptr; void Image::_set_data(const Dictionary &p_data) { - ERR_FAIL_COND(!p_data.has("width")); ERR_FAIL_COND(!p_data.has("height")); ERR_FAIL_COND(!p_data.has("format")); @@ -2586,7 +2752,6 @@ void Image::_set_data(const Dictionary &p_data) { } Dictionary Image::_get_data() const { - Dictionary d; d["width"] = width; d["height"] = height; @@ -2596,12 +2761,11 @@ Dictionary Image::_get_data() const { return d; } -Color Image::get_pixelv(const Point2 &p_src) const { - return get_pixel(p_src.x, p_src.y); +Color Image::get_pixelv(const Point2i &p_point) const { + return get_pixel(p_point.x, p_point.y); } Color Image::_get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const { - switch (format) { case FORMAT_L8: { float l = ptr[ofs] / 255.0; @@ -2613,12 +2777,10 @@ Color Image::_get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const { return Color(l, l, l, a); } case FORMAT_R8: { - float r = ptr[ofs] / 255.0; return Color(r, 0, 0, 1); } case FORMAT_RG8: { - float r = ptr[ofs * 2 + 0] / 255.0; float g = ptr[ofs * 2 + 1] / 255.0; return Color(r, g, 0, 1); @@ -2645,7 +2807,6 @@ Color Image::_get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const { return Color(r, g, b, a); } case FORMAT_RGB565: { - uint16_t u = ((uint16_t *)ptr)[ofs]; float r = (u & 0x1F) / 31.0; float g = ((u >> 5) & 0x3F) / 63.0; @@ -2653,25 +2814,21 @@ Color Image::_get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const { return Color(r, g, b, 1.0); } case FORMAT_RF: { - float r = ((float *)ptr)[ofs]; return Color(r, 0, 0, 1); } case FORMAT_RGF: { - float r = ((float *)ptr)[ofs * 2 + 0]; float g = ((float *)ptr)[ofs * 2 + 1]; return Color(r, g, 0, 1); } case FORMAT_RGBF: { - float r = ((float *)ptr)[ofs * 3 + 0]; float g = ((float *)ptr)[ofs * 3 + 1]; float b = ((float *)ptr)[ofs * 3 + 2]; return Color(r, g, b, 1); } case FORMAT_RGBAF: { - float r = ((float *)ptr)[ofs * 4 + 0]; float g = ((float *)ptr)[ofs * 4 + 1]; float b = ((float *)ptr)[ofs * 4 + 2]; @@ -2679,25 +2836,21 @@ Color Image::_get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const { return Color(r, g, b, a); } case FORMAT_RH: { - uint16_t r = ((uint16_t *)ptr)[ofs]; return Color(Math::half_to_float(r), 0, 0, 1); } case FORMAT_RGH: { - uint16_t r = ((uint16_t *)ptr)[ofs * 2 + 0]; uint16_t g = ((uint16_t *)ptr)[ofs * 2 + 1]; return Color(Math::half_to_float(r), Math::half_to_float(g), 0, 1); } case FORMAT_RGBH: { - uint16_t r = ((uint16_t *)ptr)[ofs * 3 + 0]; uint16_t g = ((uint16_t *)ptr)[ofs * 3 + 1]; uint16_t b = ((uint16_t *)ptr)[ofs * 3 + 2]; return Color(Math::half_to_float(r), Math::half_to_float(g), Math::half_to_float(b), 1); } case FORMAT_RGBAH: { - uint16_t r = ((uint16_t *)ptr)[ofs * 4 + 0]; uint16_t g = ((uint16_t *)ptr)[ofs * 4 + 1]; uint16_t b = ((uint16_t *)ptr)[ofs * 4 + 2]; @@ -2723,11 +2876,9 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) ptr[ofs * 2 + 1] = uint8_t(CLAMP(p_color.a * 255.0, 0, 255)); } break; case FORMAT_R8: { - ptr[ofs] = uint8_t(CLAMP(p_color.r * 255.0, 0, 255)); } break; case FORMAT_RG8: { - ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.r * 255.0, 0, 255)); ptr[ofs * 2 + 1] = uint8_t(CLAMP(p_color.g * 255.0, 0, 255)); } break; @@ -2744,7 +2895,6 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) } break; case FORMAT_RGBA4444: { - uint16_t rgba = 0; rgba = uint16_t(CLAMP(p_color.r * 15.0, 0, 15)) << 12; @@ -2756,7 +2906,6 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) } break; case FORMAT_RGB565: { - uint16_t rgba = 0; rgba = uint16_t(CLAMP(p_color.r * 31.0, 0, 31)); @@ -2767,51 +2916,42 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) } break; case FORMAT_RF: { - ((float *)ptr)[ofs] = p_color.r; } break; case FORMAT_RGF: { - ((float *)ptr)[ofs * 2 + 0] = p_color.r; ((float *)ptr)[ofs * 2 + 1] = p_color.g; } break; case FORMAT_RGBF: { - ((float *)ptr)[ofs * 3 + 0] = p_color.r; ((float *)ptr)[ofs * 3 + 1] = p_color.g; ((float *)ptr)[ofs * 3 + 2] = p_color.b; } break; case FORMAT_RGBAF: { - ((float *)ptr)[ofs * 4 + 0] = p_color.r; ((float *)ptr)[ofs * 4 + 1] = p_color.g; ((float *)ptr)[ofs * 4 + 2] = p_color.b; ((float *)ptr)[ofs * 4 + 3] = p_color.a; } break; case FORMAT_RH: { - ((uint16_t *)ptr)[ofs] = Math::make_half_float(p_color.r); } break; case FORMAT_RGH: { - ((uint16_t *)ptr)[ofs * 2 + 0] = Math::make_half_float(p_color.r); ((uint16_t *)ptr)[ofs * 2 + 1] = Math::make_half_float(p_color.g); } break; case FORMAT_RGBH: { - ((uint16_t *)ptr)[ofs * 3 + 0] = Math::make_half_float(p_color.r); ((uint16_t *)ptr)[ofs * 3 + 1] = Math::make_half_float(p_color.g); ((uint16_t *)ptr)[ofs * 3 + 2] = Math::make_half_float(p_color.b); } break; case FORMAT_RGBAH: { - ((uint16_t *)ptr)[ofs * 4 + 0] = Math::make_half_float(p_color.r); ((uint16_t *)ptr)[ofs * 4 + 1] = Math::make_half_float(p_color.g); ((uint16_t *)ptr)[ofs * 4 + 2] = Math::make_half_float(p_color.b); ((uint16_t *)ptr)[ofs * 4 + 3] = Math::make_half_float(p_color.a); } break; case FORMAT_RGBE9995: { - ((uint32_t *)ptr)[ofs] = p_color.to_rgbe9995(); } break; @@ -2831,8 +2971,8 @@ Color Image::get_pixel(int p_x, int p_y) const { return _get_color_at_ofs(data.ptr(), ofs); } -void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) { - set_pixel(p_dst.x, p_dst.y, p_color); +void Image::set_pixelv(const Point2i &p_point, const Color &p_color) { + set_pixel(p_point.x, p_point.y, p_color); } void Image::set_pixel(int p_x, int p_y, const Color &p_color) { @@ -2846,24 +2986,26 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { } Image::UsedChannels Image::detect_used_channels(CompressSource p_source) { - ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA); ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA); bool r = false, g = false, b = false, a = false, c = false; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { - Color col = get_pixel(i, j); - if (col.r > 0.001) + if (col.r > 0.001) { r = true; - if (col.g > 0.001) + } + if (col.g > 0.001) { g = true; - if (col.b > 0.001) + } + if (col.b > 0.001) { b = true; - if (col.a < 0.999) + } + if (col.a < 0.999) { a = true; + } if (col.r != col.b || col.r != col.g || col.b != col.g) { c = true; @@ -2873,18 +3015,19 @@ Image::UsedChannels Image::detect_used_channels(CompressSource p_source) { UsedChannels used_channels; - if (!c && !a) + if (!c && !a) { used_channels = USED_CHANNELS_L; - else if (!c && a) + } else if (!c && a) { used_channels = USED_CHANNELS_LA; - else if (r && !g && !b && !a) + } else if (r && !g && !b && !a) { used_channels = USED_CHANNELS_R; - else if (r && g && !b && !a) + } else if (r && g && !b && !a) { used_channels = USED_CHANNELS_RG; - else if (r && g && b && !a) + } else if (r && g && b && !a) { used_channels = USED_CHANNELS_RGB; - else + } else { used_channels = USED_CHANNELS_RGBA; + } if (p_source == COMPRESS_SOURCE_SRGB && (used_channels == USED_CHANNELS_R || used_channels == USED_CHANNELS_RG)) { //R and RG do not support SRGB @@ -2901,17 +3044,28 @@ Image::UsedChannels Image::detect_used_channels(CompressSource p_source) { void Image::optimize_channels() { switch (detect_used_channels()) { - case USED_CHANNELS_L: convert(FORMAT_L8); break; - case USED_CHANNELS_LA: convert(FORMAT_LA8); break; - case USED_CHANNELS_R: convert(FORMAT_R8); break; - case USED_CHANNELS_RG: convert(FORMAT_RG8); break; - case USED_CHANNELS_RGB: convert(FORMAT_RGB8); break; - case USED_CHANNELS_RGBA: convert(FORMAT_RGBA8); break; + case USED_CHANNELS_L: + convert(FORMAT_L8); + break; + case USED_CHANNELS_LA: + convert(FORMAT_LA8); + break; + case USED_CHANNELS_R: + convert(FORMAT_R8); + break; + case USED_CHANNELS_RG: + convert(FORMAT_RG8); + break; + case USED_CHANNELS_RGB: + convert(FORMAT_RGB8); + break; + case USED_CHANNELS_RGBA: + convert(FORMAT_RGBA8); + break; } } void Image::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_width"), &Image::get_width); ClassDB::bind_method(D_METHOD("get_height"), &Image::get_height); ClassDB::bind_method(D_METHOD("get_size"), &Image::get_size); @@ -2923,10 +3077,9 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mipmap_offset", "mipmap"), &Image::get_mipmap_offset); - ClassDB::bind_method(D_METHOD("resize_to_po2", "square"), &Image::resize_to_po2, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("resize_to_po2", "square", "interpolation"), &Image::resize_to_po2, DEFVAL(false), DEFVAL(INTERPOLATE_BILINEAR)); ClassDB::bind_method(D_METHOD("resize", "width", "height", "interpolation"), &Image::resize, DEFVAL(INTERPOLATE_BILINEAR)); ClassDB::bind_method(D_METHOD("shrink_x2"), &Image::shrink_x2); - ClassDB::bind_method(D_METHOD("expand_x2_hq2x"), &Image::expand_x2_hq2x); ClassDB::bind_method(D_METHOD("crop", "width", "height"), &Image::crop); ClassDB::bind_method(D_METHOD("flip_x"), &Image::flip_x); @@ -2937,10 +3090,11 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::_create_empty); ClassDB::bind_method(D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::_create_from_data); - ClassDB::bind_method(D_METHOD("is_empty"), &Image::empty); + ClassDB::bind_method(D_METHOD("is_empty"), &Image::is_empty); ClassDB::bind_method(D_METHOD("load", "path"), &Image::load); ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png); + ClassDB::bind_method(D_METHOD("save_png_to_buffer"), &Image::save_png_to_buffer); ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false)); ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha); @@ -2955,9 +3109,9 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("fix_alpha_edges"), &Image::fix_alpha_edges); ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha); ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear); - ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy); + ClassDB::bind_method(D_METHOD("normal_map_to_xy"), &Image::normal_map_to_xy); ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb); - ClassDB::bind_method(D_METHOD("bumpmap_to_normalmap", "bump_scale"), &Image::bumpmap_to_normalmap, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("bump_map_to_normal_map", "bump_scale"), &Image::bump_map_to_normal_map, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect); ClassDB::bind_method(D_METHOD("blit_rect_mask", "src", "mask", "src_rect", "dst"), &Image::blit_rect_mask); @@ -2973,14 +3127,16 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data", "data"), &Image::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &Image::_get_data); - ClassDB::bind_method(D_METHOD("get_pixelv", "src"), &Image::get_pixelv); + ClassDB::bind_method(D_METHOD("get_pixelv", "point"), &Image::get_pixelv); ClassDB::bind_method(D_METHOD("get_pixel", "x", "y"), &Image::get_pixel); - ClassDB::bind_method(D_METHOD("set_pixelv", "dst", "color"), &Image::set_pixelv); + ClassDB::bind_method(D_METHOD("set_pixelv", "point", "color"), &Image::set_pixelv); ClassDB::bind_method(D_METHOD("set_pixel", "x", "y", "color"), &Image::set_pixel); ClassDB::bind_method(D_METHOD("load_png_from_buffer", "buffer"), &Image::load_png_from_buffer); ClassDB::bind_method(D_METHOD("load_jpg_from_buffer", "buffer"), &Image::load_jpg_from_buffer); ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); + ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); + ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data"); @@ -3012,10 +3168,10 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBA); //btpc bc6h BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBF); //float / BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBFU); //unsigned float - BIND_ENUM_CONSTANT(FORMAT_PVRTC2); //pvrtc - BIND_ENUM_CONSTANT(FORMAT_PVRTC2A); - BIND_ENUM_CONSTANT(FORMAT_PVRTC4); - BIND_ENUM_CONSTANT(FORMAT_PVRTC4A); + BIND_ENUM_CONSTANT(FORMAT_PVRTC1_2); //pvrtc + BIND_ENUM_CONSTANT(FORMAT_PVRTC1_2A); + BIND_ENUM_CONSTANT(FORMAT_PVRTC1_4); + BIND_ENUM_CONSTANT(FORMAT_PVRTC1_4A); BIND_ENUM_CONSTANT(FORMAT_ETC); //etc1 BIND_ENUM_CONSTANT(FORMAT_ETC2_R11); //etc2 BIND_ENUM_CONSTANT(FORMAT_ETC2_R11S); //signed ); NOT srgb. @@ -3039,10 +3195,10 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(ALPHA_BLEND); BIND_ENUM_CONSTANT(COMPRESS_S3TC); - BIND_ENUM_CONSTANT(COMPRESS_PVRTC2); - BIND_ENUM_CONSTANT(COMPRESS_PVRTC4); + BIND_ENUM_CONSTANT(COMPRESS_PVRTC1_4); BIND_ENUM_CONSTANT(COMPRESS_ETC); BIND_ENUM_CONSTANT(COMPRESS_ETC2); + BIND_ENUM_CONSTANT(COMPRESS_BPTC); BIND_ENUM_CONSTANT(USED_CHANNELS_L); BIND_ENUM_CONSTANT(USED_CHANNELS_LA); @@ -3057,17 +3213,14 @@ void Image::_bind_methods() { } void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { - _image_compress_bc_func = p_compress_func; } void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { - _image_compress_bptc_func = p_compress_func; } -void Image::normalmap_to_xy() { - +void Image::normal_map_to_xy() { convert(Image::FORMAT_RGBA8); { @@ -3075,7 +3228,6 @@ void Image::normalmap_to_xy() { uint8_t *data_ptr = data.ptrw(); for (int i = 0; i < len; i++) { - data_ptr[(i << 2) + 3] = data_ptr[(i << 2) + 0]; //x to w data_ptr[(i << 2) + 0] = data_ptr[(i << 2) + 1]; //y to xz data_ptr[(i << 2) + 2] = data_ptr[(i << 2) + 1]; @@ -3086,15 +3238,15 @@ void Image::normalmap_to_xy() { } Ref<Image> Image::rgbe_to_srgb() { - - if (data.size() == 0) + if (data.size() == 0) { return Ref<Image>(); + } ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>()); Ref<Image> new_image; new_image.instance(); - new_image->create(width, height, 0, Image::FORMAT_RGB8); + new_image->create(width, height, false, Image::FORMAT_RGB8); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { @@ -3110,7 +3262,6 @@ Ref<Image> Image::rgbe_to_srgb() { } Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const { - int ofs, size, w, h; get_mipmap_offset_size_and_dimensions(p_mipamp, ofs, size, w, h); @@ -3134,7 +3285,7 @@ Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const { return image; } -void Image::bumpmap_to_normalmap(float bump_scale) { +void Image::bump_map_to_normal_map(float bump_scale) { ERR_FAIL_COND(!_can_modify(format)); convert(Image::FORMAT_RF); @@ -3152,11 +3303,15 @@ void Image::bumpmap_to_normalmap(float bump_scale) { for (int ty = 0; ty < height; ty++) { int py = ty + 1; - if (py >= height) py -= height; + if (py >= height) { + py -= height; + } for (int tx = 0; tx < width; tx++) { int px = tx + 1; - if (px >= width) px -= width; + if (px >= width) { + px -= width; + } float here = read_ptr[ty * width + tx]; float to_right = read_ptr[ty * width + px]; float above = read_ptr[py * width + tx]; @@ -3178,33 +3333,29 @@ void Image::bumpmap_to_normalmap(float bump_scale) { } void Image::srgb_to_linear() { - - if (data.size() == 0) + if (data.size() == 0) { return; + } static const uint8_t srgb2lin[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 24, 25, 26, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 47, 48, 49, 50, 51, 52, 53, 54, 55, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 87, 88, 89, 90, 92, 93, 94, 95, 97, 98, 99, 101, 102, 103, 105, 106, 107, 109, 110, 112, 113, 114, 116, 117, 119, 120, 122, 123, 125, 126, 128, 129, 131, 132, 134, 135, 137, 139, 140, 142, 144, 145, 147, 148, 150, 152, 153, 155, 157, 159, 160, 162, 164, 166, 167, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 188, 190, 192, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 235, 237, 239, 241, 243, 245, 248, 250, 252, 255 }; ERR_FAIL_COND(format != FORMAT_RGB8 && format != FORMAT_RGBA8); if (format == FORMAT_RGBA8) { - int len = data.size() / 4; uint8_t *data_ptr = data.ptrw(); for (int i = 0; i < len; i++) { - data_ptr[(i << 2) + 0] = srgb2lin[data_ptr[(i << 2) + 0]]; data_ptr[(i << 2) + 1] = srgb2lin[data_ptr[(i << 2) + 1]]; data_ptr[(i << 2) + 2] = srgb2lin[data_ptr[(i << 2) + 2]]; } } else if (format == FORMAT_RGB8) { - int len = data.size() / 3; uint8_t *data_ptr = data.ptrw(); for (int i = 0; i < len; i++) { - data_ptr[(i * 3) + 0] = srgb2lin[data_ptr[(i * 3) + 0]]; data_ptr[(i * 3) + 1] = srgb2lin[data_ptr[(i * 3) + 1]]; data_ptr[(i * 3) + 2] = srgb2lin[data_ptr[(i * 3) + 2]]; @@ -3213,18 +3364,18 @@ void Image::srgb_to_linear() { } void Image::premultiply_alpha() { - - if (data.size() == 0) + if (data.size() == 0) { return; + } - if (format != FORMAT_RGBA8) + if (format != FORMAT_RGBA8) { return; //not needed + } uint8_t *data_ptr = data.ptrw(); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { - uint8_t *ptr = &data_ptr[(i * width + j) * 4]; ptr[0] = (uint16_t(ptr[0]) * uint16_t(ptr[3])) >> 8; @@ -3235,12 +3386,13 @@ void Image::premultiply_alpha() { } void Image::fix_alpha_edges() { - - if (data.size() == 0) + if (data.size() == 0) { return; + } - if (format != FORMAT_RGBA8) + if (format != FORMAT_RGBA8) { return; //not needed + } Vector<uint8_t> dcopy = data; const uint8_t *srcptr = dcopy.ptr(); @@ -3253,12 +3405,12 @@ void Image::fix_alpha_edges() { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { - const uint8_t *rptr = &srcptr[(i * width + j) * 4]; uint8_t *wptr = &data_ptr[(i * width + j) * 4]; - if (rptr[3] >= alpha_threshold) + if (rptr[3] >= alpha_threshold) { continue; + } int closest_dist = max_dist; uint8_t closest_color[3]; @@ -3270,17 +3422,18 @@ void Image::fix_alpha_edges() { for (int k = from_y; k <= to_y; k++) { for (int l = from_x; l <= to_x; l++) { - int dy = i - k; int dx = j - l; int dist = dy * dy + dx * dx; - if (dist >= closest_dist) + if (dist >= closest_dist) { continue; + } const uint8_t *rp2 = &srcptr[(k * width + l) << 2]; - if (rp2[3] < alpha_threshold) + if (rp2[3] < alpha_threshold) { continue; + } closest_dist = dist; closest_color[0] = rp2[0]; @@ -3290,7 +3443,6 @@ void Image::fix_alpha_edges() { } if (closest_dist != max_dist) { - wptr[0] = closest_color[0]; wptr[1] = closest_color[1]; wptr[2] = closest_color[2]; @@ -3300,7 +3452,6 @@ void Image::fix_alpha_edges() { } String Image::get_format_name(Format p_format) { - ERR_FAIL_INDEX_V(p_format, FORMAT_MAX, String()); return format_names[p_format]; } @@ -3317,6 +3468,22 @@ Error Image::load_webp_from_buffer(const Vector<uint8_t> &p_array) { return _load_from_buffer(p_array, _webp_mem_loader_func); } +Error Image::load_tga_from_buffer(const Vector<uint8_t> &p_array) { + ERR_FAIL_NULL_V_MSG( + _tga_mem_loader_func, + ERR_UNAVAILABLE, + "The TGA module isn't enabled. Recompile the Godot editor or export template binary with the `module_tga_enabled=yes` SCons option."); + return _load_from_buffer(p_array, _tga_mem_loader_func); +} + +Error Image::load_bmp_from_buffer(const Vector<uint8_t> &p_array) { + ERR_FAIL_NULL_V_MSG( + _bmp_mem_loader_func, + ERR_UNAVAILABLE, + "The BMP module isn't enabled. Recompile the Godot editor or export template binary with the `module_bmp_enabled=yes` SCons option."); + return _load_from_buffer(p_array, _bmp_mem_loader_func); +} + void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); @@ -3329,6 +3496,7 @@ void Image::convert_rg_to_ra_rgba8() { w[i + 2] = 0; } } + void Image::convert_ra_rgba8_to_rg() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); @@ -3408,7 +3576,6 @@ void Image::renormalize_rgbe9995(uint32_t *p_rgb) { } Image::Image(const uint8_t *p_mem_png_jpg, int p_len) { - width = 0; height = 0; mipmaps = false; @@ -3418,26 +3585,18 @@ Image::Image(const uint8_t *p_mem_png_jpg, int p_len) { copy_internals_from(_png_mem_loader_func(p_mem_png_jpg, p_len)); } - if (empty() && _jpg_mem_loader_func) { + if (is_empty() && _jpg_mem_loader_func) { copy_internals_from(_jpg_mem_loader_func(p_mem_png_jpg, p_len)); } } Ref<Resource> Image::duplicate(bool p_subresources) const { - Ref<Image> copy; copy.instance(); copy->_copy_internals_from(*this); return copy; } -Image::Image() { - - width = 0; - height = 0; - mipmaps = false; - format = FORMAT_L8; -} - -Image::~Image() { +void Image::set_as_black() { + zeromem(data.ptrw(), data.size()); } diff --git a/core/image.h b/core/io/image.h index 5bd73fa677..b894be7df4 100644 --- a/core/image.h +++ b/core/io/image.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,11 +31,10 @@ #ifndef IMAGE_H #define IMAGE_H -#include "core/color.h" +#include "core/io/resource.h" +#include "core/math/color.h" #include "core/math/rect2.h" -#include "core/resource.h" - /** * @author Juan Linietsky <reduzio@gmail.com> * @@ -67,7 +66,6 @@ public: }; enum Format { - FORMAT_L8, //luminance FORMAT_LA8, //luminance-alpha FORMAT_R8, @@ -93,10 +91,10 @@ public: FORMAT_BPTC_RGBA, //btpc bc7 FORMAT_BPTC_RGBF, //float bc6h FORMAT_BPTC_RGBFU, //unsigned float bc6hu - FORMAT_PVRTC2, //pvrtc - FORMAT_PVRTC2A, - FORMAT_PVRTC4, - FORMAT_PVRTC4A, + FORMAT_PVRTC1_2, //pvrtc1 + FORMAT_PVRTC1_2A, + FORMAT_PVRTC1_4, + FORMAT_PVRTC1_4A, FORMAT_ETC, //etc1 FORMAT_ETC2_R11, //etc2 FORMAT_ETC2_R11S, //signed, NOT srgb. @@ -112,7 +110,6 @@ public: static const char *format_names[FORMAT_MAX]; enum Interpolation { - INTERPOLATE_NEAREST, INTERPOLATE_BILINEAR, INTERPOLATE_CUBIC, @@ -136,11 +133,12 @@ public: static ImageMemLoadFunc _png_mem_loader_func; static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func; + static ImageMemLoadFunc _tga_mem_loader_func; + static ImageMemLoadFunc _bmp_mem_loader_func; static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels); - static void (*_image_compress_pvrtc2_func)(Image *); - static void (*_image_compress_pvrtc4_func)(Image *); + static void (*_image_compress_pvrtc1_4bpp_func)(Image *); static void (*_image_compress_etc1_func)(Image *, float); static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels); @@ -172,10 +170,11 @@ private: create(p_width, p_height, p_use_mipmaps, p_format, p_data); } - Format format; + Format format = FORMAT_L8; Vector<uint8_t> data; - int width, height; - bool mipmaps; + int width = 0; + int height = 0; + bool mipmaps = false; void _copy_internals_from(const Image &p_image) { format = p_image.format; @@ -229,13 +228,25 @@ public: void get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const; //get where the mipmap begins in data void get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const; //get where the mipmap begins in data + enum Image3DValidateError { + VALIDATE_3D_OK, + VALIDATE_3D_ERR_IMAGE_EMPTY, + VALIDATE_3D_ERR_MISSING_IMAGES, + VALIDATE_3D_ERR_EXTRA_IMAGES, + VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH, + VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH, + VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS, + }; + + static Image3DValidateError validate_3d_image(Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_images); + static String get_3d_image_validation_error_text(Image3DValidateError p_error); + /** * Resize the image, using the preferred interpolation method. */ - void resize_to_po2(bool p_square = false); + void resize_to_po2(bool p_square = false, Interpolation p_interpolation = INTERPOLATE_BILINEAR); void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR); void shrink_x2(); - void expand_x2_hq2x(); bool is_size_po2() const; /** * Crop the image to a specific size, if larger, then the image is filled by black @@ -274,7 +285,7 @@ public: /** * returns true when the image is empty (0,0) in size */ - bool empty() const; + bool is_empty() const; Vector<uint8_t> get_data() const; @@ -286,7 +297,7 @@ public: /** * create an empty image */ - Image(); + Image() {} /** * create an empty image of a specific size and format */ @@ -296,6 +307,8 @@ public: */ Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data); + ~Image() {} + enum AlphaMode { ALPHA_NONE, ALPHA_BIT, @@ -318,11 +331,10 @@ public: enum CompressMode { COMPRESS_S3TC, - COMPRESS_PVRTC2, - COMPRESS_PVRTC4, + COMPRESS_PVRTC1_4, COMPRESS_ETC, COMPRESS_ETC2, - COMPRESS_BPTC + COMPRESS_BPTC, }; enum CompressSource { COMPRESS_SOURCE_GENERIC, @@ -338,10 +350,10 @@ public: void fix_alpha_edges(); void premultiply_alpha(); void srgb_to_linear(); - void normalmap_to_xy(); + void normal_map_to_xy(); Ref<Image> rgbe_to_srgb(); Ref<Image> get_image_from_mipmap(int p_mipamp) const; - void bumpmap_to_normalmap(float bump_scale = 1.0); + void bump_map_to_normal_map(float bump_scale = 1.0); void blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest); void blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest); @@ -359,6 +371,8 @@ public: Error load_png_from_buffer(const Vector<uint8_t> &p_array); Error load_jpg_from_buffer(const Vector<uint8_t> &p_array); Error load_webp_from_buffer(const Vector<uint8_t> &p_array); + Error load_tga_from_buffer(const Vector<uint8_t> &p_array); + Error load_bmp_from_buffer(const Vector<uint8_t> &p_array); void convert_rg_to_ra_rgba8(); void convert_ra_rgba8_to_rg(); @@ -366,16 +380,18 @@ public: Image(const uint8_t *p_mem_png_jpg, int p_len = -1); Image(const char **p_xpm); - virtual Ref<Resource> duplicate(bool p_subresources = false) const; + virtual Ref<Resource> duplicate(bool p_subresources = false) const override; UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC); void optimize_channels(); - Color get_pixelv(const Point2 &p_src) const; + Color get_pixelv(const Point2i &p_point) const; Color get_pixel(int p_x, int p_y) const; - void set_pixelv(const Point2 &p_dst, const Color &p_color); + void set_pixelv(const Point2i &p_point, const Color &p_color); void set_pixel(int p_x, int p_y, const Color &p_color); + void set_as_black(); + void copy_internals_from(const Ref<Image> &p_image) { ERR_FAIL_COND_MSG(p_image.is_null(), "It's not a reference to a valid Image object."); format = p_image->format; @@ -384,8 +400,6 @@ public: mipmaps = p_image->mipmaps; data = p_image->data; } - - ~Image(); }; VARIANT_ENUM_CAST(Image::Format) diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 01e6bb5618..8ca1cb3beb 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,16 +30,15 @@ #include "image_loader.h" -#include "core/print_string.h" +#include "core/string/print_string.h" bool ImageFormatLoader::recognize(const String &p_extension) const { - List<String> extensions; get_recognized_extensions(&extensions); for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - - if (E->get().nocasecmp_to(p_extension) == 0) + if (E->get().nocasecmp_to(p_extension) == 0) { return true; + } } return false; @@ -61,43 +60,41 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c String extension = p_file.get_extension(); for (int i = 0; i < loader.size(); i++) { - - if (!loader[i]->recognize(extension)) + if (!loader[i]->recognize(extension)) { continue; + } Error err = loader[i]->load_image(p_image, f, p_force_linear, p_scale); if (err != OK) { ERR_PRINT("Error loading image: " + p_file); } if (err != ERR_FILE_UNRECOGNIZED) { - - if (!p_custom) + if (!p_custom) { memdelete(f); + } return err; } } - if (!p_custom) + if (!p_custom) { memdelete(f); + } return ERR_FILE_UNRECOGNIZED; } void ImageLoader::get_recognized_extensions(List<String> *p_extensions) { - for (int i = 0; i < loader.size(); i++) { - loader[i]->get_recognized_extensions(p_extensions); } } ImageFormatLoader *ImageLoader::recognize(const String &p_extension) { - for (int i = 0; i < loader.size(); i++) { - - if (loader[i]->recognize(p_extension)) + if (loader[i]->recognize(p_extension)) { return loader[i]; + } } return nullptr; @@ -106,22 +103,18 @@ ImageFormatLoader *ImageLoader::recognize(const String &p_extension) { Vector<ImageFormatLoader *> ImageLoader::loader; void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { - loader.push_back(p_loader); } void ImageLoader::remove_image_format_loader(ImageFormatLoader *p_loader) { - loader.erase(p_loader); } const Vector<ImageFormatLoader *> &ImageLoader::get_image_format_loaders() { - return loader; } void ImageLoader::cleanup() { - while (loader.size()) { remove_image_format_loader(loader[0]); } @@ -130,7 +123,6 @@ void ImageLoader::cleanup() { ///////////////// RES ResourceFormatLoaderImage::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) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); if (!f) { if (r_error) { @@ -192,16 +184,13 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin } void ResourceFormatLoaderImage::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("image"); } bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { - return p_type == "Image"; } String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { - return p_path.get_extension().to_lower() == "image" ? "Image" : String(); } diff --git a/core/io/image_loader.h b/core/io/image_loader.h index 15ce6031d7..bf67e1486f 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,11 +31,11 @@ #ifndef IMAGE_LOADER_H #define IMAGE_LOADER_H -#include "core/image.h" +#include "core/io/image.h" #include "core/io/resource_loader.h" -#include "core/list.h" #include "core/os/file_access.h" -#include "core/ustring.h" +#include "core/string/ustring.h" +#include "core/templates/list.h" class ImageLoader; @@ -53,7 +53,6 @@ public: }; class ImageLoader { - static Vector<ImageFormatLoader *> loader; friend class ResourceFormatLoaderImage; diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 5de7fb7186..6fb812e78d 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,18 +30,16 @@ #include "ip.h" -#include "core/hash_map.h" #include "core/os/semaphore.h" #include "core/os/thread.h" +#include "core/templates/hash_map.h" VARIANT_ENUM_CAST(IP::ResolverStatus); /************* RESOLVER ******************/ struct _IP_ResolverPrivate { - struct QueueItem { - volatile IP::ResolverStatus status; IP_Address response; String hostname; @@ -56,16 +54,16 @@ struct _IP_ResolverPrivate { QueueItem() { clear(); - }; + } }; QueueItem queue[IP::RESOLVER_MAX_QUERIES]; IP::ResolverID find_empty_id() const { - for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) { - if (queue[i].status == IP::RESOLVER_STATUS_NONE) + if (queue[i].status == IP::RESOLVER_STATUS_NONE) { return i; + } } return IP::RESOLVER_INVALID_ID; } @@ -78,26 +76,24 @@ struct _IP_ResolverPrivate { bool thread_abort; void resolve_queues() { - for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) { - - if (queue[i].status != IP::RESOLVER_STATUS_WAITING) + if (queue[i].status != IP::RESOLVER_STATUS_WAITING) { continue; + } queue[i].response = IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type); - if (!queue[i].response.is_valid()) + if (!queue[i].response.is_valid()) { queue[i].status = IP::RESOLVER_STATUS_ERROR; - else + } else { queue[i].status = IP::RESOLVER_STATUS_DONE; + } } } static void _thread_function(void *self) { - _IP_ResolverPrivate *ipr = (_IP_ResolverPrivate *)self; while (!ipr->thread_abort) { - ipr->sem.wait(); MutexLock lock(ipr->mutex); @@ -113,7 +109,6 @@ struct _IP_ResolverPrivate { }; IP_Address IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { - MutexLock lock(resolver->mutex); String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); @@ -128,7 +123,6 @@ IP_Address IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { } IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Type p_type) { - MutexLock lock(resolver->mutex); ResolverID id = resolver->find_empty_id(); @@ -147,17 +141,17 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ } else { resolver->queue[id].response = IP_Address(); resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING; - if (resolver->thread) + if (resolver->thread) { resolver->sem.post(); - else + } else { resolver->resolve_queues(); + } } return id; } IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { - ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP::RESOLVER_STATUS_NONE); MutexLock lock(resolver->mutex); @@ -171,7 +165,6 @@ IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { } IP_Address IP::get_resolve_item_address(ResolverID p_id) const { - ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP_Address()); MutexLock lock(resolver->mutex); @@ -186,7 +179,6 @@ IP_Address IP::get_resolve_item_address(ResolverID p_id) const { } void IP::erase_resolve_item(ResolverID p_id) { - ERR_FAIL_INDEX(p_id, IP::RESOLVER_MAX_QUERIES); MutexLock lock(resolver->mutex); @@ -195,10 +187,9 @@ void IP::erase_resolve_item(ResolverID p_id) { } void IP::clear_cache(const String &p_hostname) { - MutexLock lock(resolver->mutex); - if (p_hostname.empty()) { + if (p_hostname.is_empty()) { resolver->cache.clear(); } else { resolver->cache.erase(_IP_ResolverPrivate::get_cache_key(p_hostname, IP::TYPE_NONE)); @@ -209,7 +200,6 @@ void IP::clear_cache(const String &p_hostname) { } Array IP::_get_local_addresses() const { - Array addresses; List<IP_Address> ip_addresses; get_local_addresses(&ip_addresses); @@ -221,7 +211,6 @@ Array IP::_get_local_addresses() const { } Array IP::_get_local_interfaces() const { - Array results; Map<String, Interface_Info> interfaces; get_local_interfaces(&interfaces); @@ -245,7 +234,6 @@ Array IP::_get_local_interfaces() const { } void IP::get_local_addresses(List<IP_Address> *r_addresses) const { - Map<String, Interface_Info> interfaces; get_local_interfaces(&interfaces); for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { @@ -256,7 +244,6 @@ void IP::get_local_addresses(List<IP_Address> *r_addresses) const { } void IP::_bind_methods() { - ClassDB::bind_method(D_METHOD("resolve_hostname", "host", "ip_type"), &IP::resolve_hostname, DEFVAL(IP::TYPE_ANY)); ClassDB::bind_method(D_METHOD("resolve_hostname_queue_item", "host", "ip_type"), &IP::resolve_hostname_queue_item, DEFVAL(IP::TYPE_ANY)); ClassDB::bind_method(D_METHOD("get_resolve_item_status", "id"), &IP::get_resolve_item_status); @@ -283,21 +270,18 @@ void IP::_bind_methods() { IP *IP::singleton = nullptr; IP *IP::get_singleton() { - return singleton; } IP *(*IP::_create)() = nullptr; IP *IP::create() { - ERR_FAIL_COND_V_MSG(singleton, nullptr, "IP singleton already exist."); ERR_FAIL_COND_V(!_create, nullptr); return _create(); } IP::IP() { - singleton = this; resolver = memnew(_IP_ResolverPrivate); @@ -312,7 +296,6 @@ IP::IP() { } IP::~IP() { - #ifndef NO_THREADS if (resolver->thread) { resolver->thread_abort = true; diff --git a/core/io/ip.h b/core/io/ip.h index d434d02f9b..ae080b8e26 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -42,7 +42,6 @@ class IP : public Object { public: enum ResolverStatus { - RESOLVER_STATUS_NONE, RESOLVER_STATUS_WAITING, RESOLVER_STATUS_DONE, @@ -50,7 +49,6 @@ public: }; enum Type { - TYPE_NONE = 0, TYPE_IPV4 = 1, TYPE_IPV6 = 2, diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index f5fd8ae205..5f98eb69e8 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,6 @@ #include "ip_address.h" /* IP_Address::operator Variant() const { - return operator String(); }*/ @@ -39,40 +38,40 @@ IP_Address::operator Variant() const { #include <string.h> IP_Address::operator String() const { - - if (wildcard) + if (wildcard) { return "*"; + } - if (!valid) + if (!valid) { return ""; + } - if (is_ipv4()) + if (is_ipv4()) { // IPv4 address mapped to IPv6 return itos(field8[12]) + "." + itos(field8[13]) + "." + itos(field8[14]) + "." + itos(field8[15]); + } String ret; for (int i = 0; i < 8; i++) { - if (i > 0) + if (i > 0) { ret = ret + ":"; + } uint16_t num = (field8[i * 2] << 8) + field8[i * 2 + 1]; ret = ret + String::num_int64(num, 16); - }; + } return ret; } static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { - uint16_t ret = 0; for (int i = p_start; i < p_start + 4; i++) { - if (i >= p_string.length()) { break; - }; + } int n = 0; - CharType c = p_string[i]; + char32_t c = p_string[i]; if (c >= '0' && c <= '9') { - n = c - '0'; } else if (c >= 'a' && c <= 'f') { n = 10 + (c - 'a'); @@ -82,17 +81,16 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { break; } else { ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + "."); - }; + } ret = ret << 4; ret += n; - }; + } p_dst[0] = ret >> 8; p_dst[1] = ret & 0xff; -}; +} void IP_Address::_parse_ipv6(const String &p_string) { - static const int parts_total = 8; int parts[parts_total] = { 0 }; int parts_count = 0; @@ -102,20 +100,17 @@ void IP_Address::_parse_ipv6(const String &p_string) { int parts_idx = 0; for (int i = 0; i < p_string.length(); i++) { - - CharType c = p_string[i]; + char32_t c = p_string[i]; if (c == ':') { - if (i == 0) { continue; // next must be a ":" - }; + } if (!part_found) { part_skip = true; parts[parts_idx++] = -1; - }; + } part_found = false; } else if (c == '.') { - part_ipv4 = true; } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { @@ -123,58 +118,54 @@ void IP_Address::_parse_ipv6(const String &p_string) { parts[parts_idx++] = i; part_found = true; ++parts_count; - }; + } } else { ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + "."); - }; - }; + } + } int parts_extra = 0; if (part_skip) { parts_extra = parts_total - parts_count; - }; + } int idx = 0; for (int i = 0; i < parts_idx; i++) { - if (parts[i] == -1) { - for (int j = 0; j < parts_extra; j++) { field16[idx++] = 0; - }; + } continue; - }; + } if (part_ipv4 && i == parts_idx - 1) { _parse_ipv4(p_string, parts[i], (uint8_t *)&field16[idx]); // should be the last one } else { _parse_hex(p_string, parts[i], (uint8_t *)&(field16[idx++])); - }; - }; -}; + } + } +} void IP_Address::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret) { - String ip; if (p_start != 0) { ip = p_string.substr(p_start, p_string.length() - p_start); } else { ip = p_string; - }; + } int slices = ip.get_slice_count("."); ERR_FAIL_COND_MSG(slices != 4, "Invalid IP address string: " + ip + "."); for (int i = 0; i < 4; i++) { p_ret[i] = ip.get_slicec('.', i).to_int(); } -}; +} void IP_Address::clear() { - memset(&field8[0], 0, sizeof(field8)); valid = false; wildcard = false; -}; +} bool IP_Address::is_ipv4() const { return (field32[0] == 0 && field32[1] == 0 && field16[4] == 0 && field16[5] == 0xffff); @@ -199,12 +190,12 @@ const uint8_t *IP_Address::get_ipv6() const { void IP_Address::set_ipv6(const uint8_t *p_buf) { clear(); valid = true; - for (int i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { field8[i] = p_buf[i]; + } } IP_Address::IP_Address(const String &p_string) { - clear(); if (p_string == "*") { @@ -228,15 +219,13 @@ IP_Address::IP_Address(const String &p_string) { } _FORCE_INLINE_ static void _32_to_buf(uint8_t *p_dst, uint32_t p_n) { - p_dst[0] = (p_n >> 24) & 0xff; p_dst[1] = (p_n >> 16) & 0xff; p_dst[2] = (p_n >> 8) & 0xff; p_dst[3] = (p_n >> 0) & 0xff; -}; +} IP_Address::IP_Address(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, bool is_v6) { - clear(); valid = true; if (!is_v6) { @@ -247,7 +236,6 @@ IP_Address::IP_Address(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, b field8[14] = p_c; field8[15] = p_d; } else { - _32_to_buf(&field8[0], p_a); _32_to_buf(&field8[4], p_b); _32_to_buf(&field8[8], p_c); diff --git a/core/io/ip_address.h b/core/io/ip_address.h index 89cf37ff8f..49bf83d72f 100644 --- a/core/io/ip_address.h +++ b/core/io/ip_address.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,10 +31,9 @@ #ifndef IP_ADDRESS_H #define IP_ADDRESS_H -#include "core/ustring.h" +#include "core/string/ustring.h" struct IP_Address { - private: union { uint8_t field8[16]; @@ -52,19 +51,32 @@ protected: public: //operator Variant() const; bool operator==(const IP_Address &p_ip) const { - if (p_ip.valid != valid) return false; - if (!valid) return false; - for (int i = 0; i < 4; i++) - if (field32[i] != p_ip.field32[i]) + if (p_ip.valid != valid) { + return false; + } + if (!valid) { + return false; + } + for (int i = 0; i < 4; i++) { + if (field32[i] != p_ip.field32[i]) { return false; + } + } return true; } + bool operator!=(const IP_Address &p_ip) const { - if (p_ip.valid != valid) return true; - if (!valid) return true; - for (int i = 0; i < 4; i++) - if (field32[i] != p_ip.field32[i]) + if (p_ip.valid != valid) { + return true; + } + if (!valid) { + return true; + } + for (int i = 0; i < 4; i++) { + if (field32[i] != p_ip.field32[i]) { return true; + } + } return false; } diff --git a/core/io/json.cpp b/core/io/json.cpp index 3a0edceb81..bc4527869b 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "json.h" -#include "core/print_string.h" +#include "core/string/print_string.h" const char *JSON::tk_name[TK_MAX] = { "'{'", @@ -46,38 +46,39 @@ const char *JSON::tk_name[TK_MAX] = { }; static String _make_indent(const String &p_indent, int p_size) { - String indent_text = ""; - if (!p_indent.empty()) { - for (int i = 0; i < p_size; i++) + if (!p_indent.is_empty()) { + for (int i = 0; i < p_size; i++) { indent_text += p_indent; + } } return indent_text; } String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys) { - String colon = ":"; String end_statement = ""; - if (!p_indent.empty()) { + if (!p_indent.is_empty()) { colon += " "; end_statement += "\n"; } switch (p_var.get_type()) { - - case Variant::NIL: return "null"; - case Variant::BOOL: return p_var.operator bool() ? "true" : "false"; - case Variant::INT: return itos(p_var); - case Variant::FLOAT: return rtos(p_var); + case Variant::NIL: + return "null"; + case Variant::BOOL: + return p_var.operator bool() ? "true" : "false"; + case Variant::INT: + return itos(p_var); + case Variant::FLOAT: + return rtos(p_var); case Variant::PACKED_INT32_ARRAY: case Variant::PACKED_INT64_ARRAY: case Variant::PACKED_FLOAT32_ARRAY: case Variant::PACKED_FLOAT64_ARRAY: case Variant::PACKED_STRING_ARRAY: case Variant::ARRAY: { - String s = "["; s += end_statement; Array a = p_var; @@ -90,20 +91,19 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ } s += end_statement + _make_indent(p_indent, p_cur_indent) + "]"; return s; - }; + } case Variant::DICTIONARY: { - String s = "{"; s += end_statement; Dictionary d = p_var; List<Variant> keys; d.get_key_list(&keys); - if (p_sort_keys) + if (p_sort_keys) { keys.sort(); + } for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - if (E != keys.front()) { s += ","; s += end_statement; @@ -115,69 +115,59 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ s += end_statement + _make_indent(p_indent, p_cur_indent) + "}"; return s; - }; - default: return "\"" + String(p_var).json_escape() + "\""; + } + default: + return "\"" + String(p_var).json_escape() + "\""; } } String JSON::print(const Variant &p_var, const String &p_indent, bool p_sort_keys) { - return _print_var(p_var, p_indent, 0, p_sort_keys); } -Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) { - +Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) { while (p_len > 0) { switch (p_str[index]) { - case '\n': { - line++; index++; break; - }; + } case 0: { r_token.type = TK_EOF; return OK; } break; case '{': { - r_token.type = TK_CURLY_BRACKET_OPEN; index++; return OK; - }; + } case '}': { - r_token.type = TK_CURLY_BRACKET_CLOSE; index++; return OK; - }; + } case '[': { - r_token.type = TK_BRACKET_OPEN; index++; return OK; - }; + } case ']': { - r_token.type = TK_BRACKET_CLOSE; index++; return OK; - }; + } case ':': { - r_token.type = TK_COLON; index++; return OK; - }; + } case ',': { - r_token.type = TK_COMMA; index++; return OK; - }; + } case '"': { - index++; String str; while (true) { @@ -190,34 +180,42 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to } else if (p_str[index] == '\\') { //escaped characters... index++; - CharType next = p_str[index]; + char32_t next = p_str[index]; if (next == 0) { r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { - - case 'b': res = 8; break; - case 't': res = 9; break; - case 'n': res = 10; break; - case 'f': res = 12; break; - case 'r': res = 13; break; + case 'b': + res = 8; + break; + case 't': + res = 9; + break; + case 'n': + res = 10; + break; + case 'f': + res = 12; + break; + case 'r': + res = 13; + break; case 'u': { // hex number for (int j = 0; j < 4; j++) { - CharType c = p_str[index + j + 1]; + char32_t c = p_str[index + j + 1]; if (c == 0) { r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { - r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -245,8 +243,9 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to str += res; } else { - if (p_str[index] == '\n') + if (p_str[index] == '\n') { line++; + } str += p_str[index]; } index++; @@ -258,7 +257,6 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to } break; default: { - if (p_str[index] <= 32) { index++; break; @@ -266,19 +264,17 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) { //a number - const CharType *rptr; - double number = String::to_double(&p_str[index], &rptr); + const char32_t *rptr; + double number = String::to_float(&p_str[index], &rptr); index += (rptr - &p_str[index]); r_token.type = TK_NUMBER; r_token.value = number; return OK; } else if ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { - String id; while ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { - id += p_str[index]; index++; } @@ -297,46 +293,42 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to return ERR_PARSE_ERROR; } -Error JSON::_parse_value(Variant &value, Token &token, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str) { - +Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { if (token.type == TK_CURLY_BRACKET_OPEN) { - Dictionary d; Error err = _parse_object(d, p_str, index, p_len, line, r_err_str); - if (err) + if (err) { return err; + } value = d; return OK; } else if (token.type == TK_BRACKET_OPEN) { - Array a; Error err = _parse_array(a, p_str, index, p_len, line, r_err_str); - if (err) + if (err) { return err; + } value = a; return OK; } else if (token.type == TK_IDENTIFIER) { - String id = token.value; - if (id == "true") + if (id == "true") { value = true; - else if (id == "false") + } else if (id == "false") { value = false; - else if (id == "null") + } else if (id == "null") { value = Variant(); - else { + } else { r_err_str = "Expected 'true','false' or 'null', got '" + id + "'."; return ERR_PARSE_ERROR; } return OK; } else if (token.type == TK_NUMBER) { - value = token.value; return OK; } else if (token.type == TK_STRING) { - value = token.value; return OK; } else { @@ -345,26 +337,22 @@ Error JSON::_parse_value(Variant &value, Token &token, const CharType *p_str, in } } -Error JSON::_parse_array(Array &array, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str) { - +Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { Token token; bool need_comma = false; while (index < p_len) { - Error err = _get_token(p_str, index, p_len, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type == TK_BRACKET_CLOSE) { - return OK; } if (need_comma) { - if (token.type != TK_COMMA) { - r_err_str = "Expected ','"; return ERR_PARSE_ERROR; } else { @@ -375,40 +363,37 @@ Error JSON::_parse_array(Array &array, const CharType *p_str, int &index, int p_ Variant v; err = _parse_value(v, token, p_str, index, p_len, line, r_err_str); - if (err) + if (err) { return err; + } array.push_back(v); need_comma = true; } + r_err_str = "Expected ']'"; return ERR_PARSE_ERROR; } -Error JSON::_parse_object(Dictionary &object, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str) { - +Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { bool at_key = true; String key; Token token; bool need_comma = false; while (index < p_len) { - if (at_key) { - Error err = _get_token(p_str, index, p_len, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type == TK_CURLY_BRACKET_CLOSE) { - return OK; } if (need_comma) { - if (token.type != TK_COMMA) { - r_err_str = "Expected '}' or ','"; return ERR_PARSE_ERROR; } else { @@ -418,43 +403,43 @@ Error JSON::_parse_object(Dictionary &object, const CharType *p_str, int &index, } if (token.type != TK_STRING) { - r_err_str = "Expected key"; return ERR_PARSE_ERROR; } key = token.value; err = _get_token(p_str, index, p_len, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type != TK_COLON) { - r_err_str = "Expected ':'"; return ERR_PARSE_ERROR; } at_key = false; } else { - Error err = _get_token(p_str, index, p_len, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } Variant v; err = _parse_value(v, token, p_str, index, p_len, line, r_err_str); - if (err) + if (err) { return err; + } object[key] = v; need_comma = true; at_key = true; } } + r_err_str = "Expected '}'"; return ERR_PARSE_ERROR; } Error JSON::parse(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) { - - const CharType *str = p_json.ptr(); + const char32_t *str = p_json.ptr(); int idx = 0; int len = p_json.length(); Token token; @@ -462,10 +447,43 @@ Error JSON::parse(const String &p_json, Variant &r_ret, String &r_err_str, int & String aux_key; Error err = _get_token(str, idx, len, token, r_err_line, r_err_str); - if (err) + if (err) { return err; + } err = _parse_value(r_ret, token, str, idx, len, r_err_line, r_err_str); return err; } + +Error JSONParser::parse_string(const String &p_json_string) { + return JSON::parse(p_json_string, data, err_text, err_line); +} +String JSONParser::get_error_text() const { + return err_text; +} +int JSONParser::get_error_line() const { + return err_line; +} +Variant JSONParser::get_data() const { + return data; +} + +Error JSONParser::decode_data(const Variant &p_data, const String &p_indent, bool p_sort_keys) { + string = JSON::print(p_data, p_indent, p_sort_keys); + data = p_data; + return OK; +} + +String JSONParser::get_string() const { + return string; +} + +void JSONParser::_bind_methods() { + ClassDB::bind_method(D_METHOD("parse_string", "json_string"), &JSONParser::parse_string); + ClassDB::bind_method(D_METHOD("get_error_text"), &JSONParser::get_error_text); + ClassDB::bind_method(D_METHOD("get_error_line"), &JSONParser::get_error_line); + ClassDB::bind_method(D_METHOD("get_data"), &JSONParser::get_data); + ClassDB::bind_method(D_METHOD("decode_data", "data", "indent", "sort_keys"), &JSONParser::decode_data, DEFVAL(""), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_string"), &JSONParser::get_string); +} diff --git a/core/io/json.h b/core/io/json.h index 2e851afcf4..537477666e 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,10 +31,9 @@ #ifndef JSON_H #define JSON_H -#include "core/variant.h" - +#include "core/object/reference.h" +#include "core/variant/variant.h" class JSON { - enum TokenType { TK_CURLY_BRACKET_OPEN, TK_CURLY_BRACKET_CLOSE, @@ -50,7 +49,6 @@ class JSON { }; enum Expecting { - EXPECT_OBJECT, EXPECT_OBJECT_KEY, EXPECT_COLON, @@ -58,7 +56,6 @@ class JSON { }; struct Token { - TokenType type; Variant value; }; @@ -67,14 +64,35 @@ class JSON { static String _print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys); - static Error _get_token(const CharType *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str); - static Error _parse_value(Variant &value, Token &token, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str); - static Error _parse_array(Array &array, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str); - static Error _parse_object(Dictionary &object, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str); + static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str); + static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); + static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); + static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); public: static String print(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true); static Error parse(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line); }; +class JSONParser : public Reference { + GDCLASS(JSONParser, Reference); + + Variant data; + String string; + String err_text; + int err_line = 0; + +protected: + static void _bind_methods(); + +public: + Error parse_string(const String &p_json_string); + String get_error_text() const; + int get_error_line() const; + Variant get_data() const; + + Error decode_data(const Variant &p_data, const String &p_indent = "", bool p_sort_keys = true); + String get_string() const; +}; + #endif // JSON_H diff --git a/core/io/logger.cpp b/core/io/logger.cpp index ad0cc81023..da200f5717 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,10 @@ #include "logger.h" +#include "core/config/project_settings.h" #include "core/os/dir_access.h" #include "core/os/os.h" -#include "core/print_string.h" +#include "core/string/print_string.h" #if defined(MINGW_ENABLED) || defined(_MSC_VER) #define sprintf sprintf_s @@ -49,18 +50,29 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c const char *err_type = "ERROR"; switch (p_type) { - case ERR_ERROR: err_type = "ERROR"; break; - case ERR_WARNING: err_type = "WARNING"; break; - case ERR_SCRIPT: err_type = "SCRIPT ERROR"; break; - case ERR_SHADER: err_type = "SHADER ERROR"; break; - default: ERR_PRINT("Unknown error type"); break; + case ERR_ERROR: + err_type = "ERROR"; + break; + case ERR_WARNING: + err_type = "WARNING"; + break; + case ERR_SCRIPT: + err_type = "SCRIPT ERROR"; + break; + case ERR_SHADER: + err_type = "SHADER ERROR"; + break; + default: + ERR_PRINT("Unknown error type"); + break; } const char *err_details; - if (p_rationale && *p_rationale) + if (p_rationale && *p_rationale) { err_details = p_rationale; - else + } else { err_details = p_code; + } logf_error("%s: %s\n", err_type, err_details); logf_error(" at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code); @@ -92,8 +104,6 @@ void Logger::logf_error(const char *p_format, ...) { va_end(argp); } -Logger::~Logger() {} - void RotatedFileLogger::close_file() { if (file) { memdelete(file); @@ -143,7 +153,7 @@ void RotatedFileLogger::rotate_file() { char timestamp[21]; OS::Date date = OS::get_singleton()->get_date(); OS::Time time = OS::get_singleton()->get_time(); - sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); + sprintf(timestamp, "_%04d-%02d-%02d_%02d.%02d.%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); String backup_name = base_path.get_basename() + timestamp; if (base_path.get_extension() != String()) { @@ -170,8 +180,7 @@ void RotatedFileLogger::rotate_file() { RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) : base_path(p_base_path.simplify_path()), - max_files(p_max_files > 0 ? p_max_files : 1), - file(nullptr) { + max_files(p_max_files > 0 ? p_max_files : 1) { rotate_file(); } @@ -193,15 +202,14 @@ void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { } va_end(list_copy); file->store_buffer((uint8_t *)buf, len); + if (len >= static_buf_size) { Memory::free_static(buf); } -#ifdef DEBUG_ENABLED - const bool need_flush = true; -#else - bool need_flush = p_err; -#endif - if (need_flush) { + + if (p_err || GLOBAL_GET("application/run/flush_stdout_on_print")) { + // Don't always flush when printing stdout to avoid performance + // issues when `print()` is spammed in release builds. file->flush(); } } @@ -220,14 +228,14 @@ void StdLogger::logv(const char *p_format, va_list p_list, bool p_err) { vfprintf(stderr, p_format, p_list); } else { vprintf(p_format, p_list); -#ifdef DEBUG_ENABLED - fflush(stdout); -#endif + if (GLOBAL_GET("application/run/flush_stdout_on_print")) { + // Don't always flush when printing stdout to avoid performance + // issues when `print()` is spammed in release builds. + fflush(stdout); + } } } -StdLogger::~StdLogger() {} - CompositeLogger::CompositeLogger(Vector<Logger *> p_loggers) : loggers(p_loggers) { } diff --git a/core/io/logger.h b/core/io/logger.h index 7028551185..b8e615b436 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,8 +32,8 @@ #define LOGGER_H #include "core/os/file_access.h" -#include "core/ustring.h" -#include "core/vector.h" +#include "core/string/ustring.h" +#include "core/templates/vector.h" #include <stdarg.h> @@ -55,17 +55,16 @@ public: void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; - virtual ~Logger(); + virtual ~Logger() {} }; /** * Writes messages to stdout/stderr. */ class StdLogger : public Logger { - public: virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0; - virtual ~StdLogger(); + virtual ~StdLogger() {} }; /** @@ -78,7 +77,7 @@ class RotatedFileLogger : public Logger { String base_path; int max_files; - FileAccess *file; + FileAccess *file = nullptr; void rotate_file_without_closing(); void close_file(); diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 81bc45b2f7..218a612da2 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,9 @@ #include "marshalls.h" +#include "core/object/reference.h" #include "core/os/keyboard.h" -#include "core/print_string.h" -#include "core/reference.h" +#include "core/string/print_string.h" #include <limits.h> #include <stdio.h> @@ -49,13 +49,9 @@ void EncodedObjectAsID::set_object_id(ObjectID p_id) { } ObjectID EncodedObjectAsID::get_object_id() const { - return id; } -EncodedObjectAsID::EncodedObjectAsID() { -} - #define _S(a) ((int32_t)a) #define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) #define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) @@ -100,7 +96,6 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r } Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_objects) { - const uint8_t *buf = p_buffer; int len = p_len; @@ -112,95 +107,95 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int buf += 4; len -= 4; - if (r_len) + if (r_len) { *r_len = 4; + } switch (type & ENCODE_MASK) { - case Variant::NIL: { - r_variant = Variant(); } break; case Variant::BOOL: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); bool val = decode_uint32(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4; + } } break; case Variant::INT: { - if (type & ENCODE_FLAG_64) { ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); int64_t val = decode_uint64(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 8; + } } else { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t val = decode_uint32(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4; + } } } break; case Variant::FLOAT: { - if (type & ENCODE_FLAG_64) { ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); double val = decode_double(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 8; + } } else { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); float val = decode_float(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4; + } } } break; case Variant::STRING: { - String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } r_variant = str; } break; // math types case Variant::VECTOR2: { - ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA); Vector2 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 2; + } } break; case Variant::VECTOR2I: { - ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA); Vector2i val; val.x = decode_uint32(&buf[0]); val.y = decode_uint32(&buf[4]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 2; + } } break; case Variant::RECT2: { - ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Rect2 val; val.position.x = decode_float(&buf[0]); @@ -209,12 +204,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.size.y = decode_float(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } } break; case Variant::RECT2I: { - ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Rect2i val; val.position.x = decode_uint32(&buf[0]); @@ -223,12 +218,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.size.y = decode_uint32(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } } break; case Variant::VECTOR3: { - ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA); Vector3 val; val.x = decode_float(&buf[0]); @@ -236,12 +231,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.z = decode_float(&buf[8]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 3; + } } break; case Variant::VECTOR3I: { - ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA); Vector3i val; val.x = decode_uint32(&buf[0]); @@ -249,29 +244,28 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.z = decode_uint32(&buf[8]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 3; + } } break; case Variant::TRANSFORM2D: { - ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); Transform2D val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - val.elements[i][j] = decode_float(&buf[(i * 2 + j) * 4]); } } r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 6; + } } break; case Variant::PLANE: { - ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Plane val; val.normal.x = decode_float(&buf[0]); @@ -280,12 +274,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.d = decode_float(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } } break; case Variant::QUAT: { - ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Quat val; val.x = decode_float(&buf[0]); @@ -294,12 +288,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.w = decode_float(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } } break; case Variant::AABB: { - ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); AABB val; val.position.x = decode_float(&buf[0]); @@ -310,34 +304,32 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.size.z = decode_float(&buf[20]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 6; + } } break; case Variant::BASIS: { - ERR_FAIL_COND_V(len < 4 * 9, ERR_INVALID_DATA); Basis val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - val.elements[i][j] = decode_float(&buf[(i * 3 + j) * 4]); } } r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 9; + } } break; case Variant::TRANSFORM: { - ERR_FAIL_COND_V(len < 4 * 12, ERR_INVALID_DATA); Transform val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - val.basis.elements[i][j] = decode_float(&buf[(i * 3 + j) * 4]); } } @@ -347,14 +339,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 12; + } } break; // misc types case Variant::COLOR: { - ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Color val; val.r = decode_float(&buf[0]); @@ -363,22 +355,22 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.a = decode_float(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } } break; case Variant::STRING_NAME: { - String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } r_variant = StringName(str); } break; case Variant::NODE_PATH: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t strlen = decode_uint32(buf); @@ -395,25 +387,28 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int len -= 12; buf += 12; - if (flags & 2) // Obsolete format with property separate from subpath + if (flags & 2) { // Obsolete format with property separate from subpath subnamecount++; + } uint32_t total = namecount + subnamecount; - if (r_len) + if (r_len) { (*r_len) += 12; + } for (uint32_t i = 0; i < total; i++) { - String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } - if (i < namecount) + if (i < namecount) { names.push_back(str); - else + } else { subnames.push_back(str); + } } r_variant = NodePath(names, subnames, flags & 1); @@ -425,18 +420,17 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } } break; - case Variant::_RID: { - + case Variant::RID: { r_variant = RID(); } break; case Variant::OBJECT: { - if (type & ENCODE_FLAG_OBJECT_AS_ID) { //this _is_ allowed ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); ObjectID val = ObjectID(decode_uint64(buf)); - if (r_len) + if (r_len) { (*r_len) += 8; + } if (val.is_null()) { r_variant = (Object *)nullptr; @@ -453,13 +447,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } if (str == String()) { r_variant = (Object *)nullptr; } else { - Object *obj = ClassDB::instance(str); ERR_FAIL_COND_V(!obj, ERR_UNAVAILABLE); @@ -473,17 +467,18 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } for (int i = 0; i < count; i++) { - str = String(); err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } Variant value; int used; err = decode_variant(value, buf, len, &used, p_allow_objects); - if (err) + if (err) { return err; + } buf += used; len -= used; @@ -505,16 +500,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::CALLABLE: { - r_variant = Callable(); } break; case Variant::SIGNAL: { - r_variant = Signal(); } break; case Variant::DICTIONARY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; @@ -530,7 +522,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Dictionary d; for (int i = 0; i < count; i++) { - Variant key, value; int used; @@ -559,7 +550,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; @@ -575,7 +565,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Array varr; for (int i = 0; i < count; i++) { - int used = 0; Variant v; Error err = decode_variant(v, buf, len, &used, p_allow_objects); @@ -594,7 +583,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // arrays case Variant::PACKED_BYTE_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -607,7 +595,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int data.resize(count); uint8_t *w = data.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i] = buf[i]; } } @@ -615,14 +602,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = data; if (r_len) { - if (count % 4) + if (count % 4) { (*r_len) += 4 - count % 4; + } (*r_len) += 4 + count; } } break; case Variant::PACKED_INT32_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -637,7 +624,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int data.resize(count); int32_t *w = data.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i] = decode_uint32(&buf[i * 4]); } } @@ -648,7 +634,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_INT64_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int64_t count = decode_uint64(buf); buf += 4; @@ -663,7 +648,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int data.resize(count); int64_t *w = data.ptrw(); for (int64_t i = 0; i < count; i++) { - w[i] = decode_uint64(&buf[i * 8]); } } @@ -674,7 +658,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_FLOAT32_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -689,7 +672,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int data.resize(count); float *w = data.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i] = decode_float(&buf[i * 4]); } } @@ -701,7 +683,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_FLOAT64_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int64_t count = decode_uint64(buf); buf += 4; @@ -716,7 +697,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int data.resize(count); double *w = data.ptrw(); for (int64_t i = 0; i < count; i++) { - w[i] = decode_double(&buf[i * 8]); } } @@ -728,7 +708,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_STRING_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); @@ -736,16 +715,17 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int buf += 4; len -= 4; - if (r_len) + if (r_len) { (*r_len) += 4; + } //printf("string count: %i\n",count); for (int32_t i = 0; i < count; i++) { - String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } strings.push_back(str); } @@ -754,7 +734,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_VECTOR2_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -773,22 +752,21 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Vector2 *w = varray.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i].x = decode_float(buf + i * 4 * 2 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 2 + 4 * 1); } int adv = 4 * 2 * count; - if (r_len) + if (r_len) { (*r_len) += adv; + } } r_variant = varray; } break; case Variant::PACKED_VECTOR3_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -808,7 +786,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Vector3 *w = varray.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i].x = decode_float(buf + i * 4 * 3 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 3 + 4 * 1); w[i].z = decode_float(buf + i * 4 * 3 + 4 * 2); @@ -816,15 +793,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int int adv = 4 * 3 * count; - if (r_len) + if (r_len) { (*r_len) += adv; + } } r_variant = varray; } break; case Variant::PACKED_COLOR_ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -844,7 +821,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Color *w = carray.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i].r = decode_float(buf + i * 4 * 4 + 4 * 0); w[i].g = decode_float(buf + i * 4 * 4 + 4 * 1); w[i].b = decode_float(buf + i * 4 * 4 + 4 * 2); @@ -853,8 +829,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int int adv = 4 * 4 * count; - if (r_len) + if (r_len) { (*r_len) += adv; + } } r_variant = carray; @@ -869,7 +846,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { - CharString utf8 = p_string.utf8(); if (buf) { @@ -889,7 +865,6 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { } Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects) { - uint8_t *buf = r_buffer; r_len = 0; @@ -897,7 +872,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo uint32_t flags = 0; switch (p_variant.get_type()) { - case Variant::INT: { int64_t val = p_variant; if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) { @@ -905,7 +879,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } } break; case Variant::FLOAT: { - double d = p_variant; float f = d; if (double(f) != d) { @@ -913,7 +886,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } } break; case Variant::OBJECT: { - // Test for potential wrong values sent by the debugger when it breaks. Object *obj = p_variant.get_validated_object(); if (!obj) { @@ -940,13 +912,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; switch (p_variant.get_type()) { - case Variant::NIL: { - //nothing to do } break; case Variant::BOOL: { - if (buf) { encode_uint32(p_variant.operator bool(), buf); } @@ -955,7 +924,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::INT: { - if (flags & ENCODE_FLAG_64) { //64 bits if (buf) { @@ -972,7 +940,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } } break; case Variant::FLOAT: { - if (flags & ENCODE_FLAG_64) { if (buf) { encode_double(p_variant.operator double(), buf); @@ -981,7 +948,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 8; } else { - if (buf) { encode_float(p_variant.operator float(), buf); } @@ -991,14 +957,14 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::NODE_PATH: { - NodePath np = p_variant; if (buf) { encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); //for compatibility with the old format encode_uint32(np.get_subname_count(), buf + 4); uint32_t np_flags = 0; - if (np.is_absolute()) + if (np.is_absolute()) { np_flags |= 1; + } encode_uint32(np_flags, buf + 8); @@ -1010,20 +976,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo int total = np.get_name_count() + np.get_subname_count(); for (int i = 0; i < total; i++) { - String str; - if (i < np.get_name_count()) + if (i < np.get_name_count()) { str = np.get_name(i); - else + } else { str = np.get_subname(i - np.get_name_count()); + } CharString utf8 = str.utf8(); int pad = 0; - if (utf8.length() % 4) + if (utf8.length() % 4) { pad = 4 - utf8.length() % 4; + } if (buf) { encode_uint32(utf8.length(), buf); @@ -1036,20 +1003,14 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } } break; - case Variant::STRING: { - - _encode_string(p_variant, buf, r_len); - - } break; + case Variant::STRING: case Variant::STRING_NAME: { - _encode_string(p_variant, buf, r_len); } break; // math types case Variant::VECTOR2: { - if (buf) { Vector2 v2 = p_variant; encode_float(v2.x, &buf[0]); @@ -1060,7 +1021,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::VECTOR2I: { - if (buf) { Vector2i v2 = p_variant; encode_uint32(v2.x, &buf[0]); @@ -1071,7 +1031,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::RECT2: { - if (buf) { Rect2 r2 = p_variant; encode_float(r2.position.x, &buf[0]); @@ -1083,7 +1042,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::RECT2I: { - if (buf) { Rect2i r2 = p_variant; encode_uint32(r2.position.x, &buf[0]); @@ -1095,7 +1053,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::VECTOR3: { - if (buf) { Vector3 v3 = p_variant; encode_float(v3.x, &buf[0]); @@ -1107,7 +1064,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::VECTOR3I: { - if (buf) { Vector3i v3 = p_variant; encode_uint32(v3.x, &buf[0]); @@ -1119,12 +1075,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::TRANSFORM2D: { - if (buf) { Transform2D val = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - copymem(&buf[(i * 2 + j) * 4], &val.elements[i][j], sizeof(float)); } } @@ -1134,7 +1088,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::PLANE: { - if (buf) { Plane p = p_variant; encode_float(p.normal.x, &buf[0]); @@ -1147,7 +1100,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::QUAT: { - if (buf) { Quat q = p_variant; encode_float(q.x, &buf[0]); @@ -1160,7 +1112,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::AABB: { - if (buf) { AABB aabb = p_variant; encode_float(aabb.position.x, &buf[0]); @@ -1175,12 +1126,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::BASIS: { - if (buf) { Basis val = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - copymem(&buf[(i * 3 + j) * 4], &val.elements[i][j], sizeof(float)); } } @@ -1190,12 +1139,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::TRANSFORM: { - if (buf) { Transform val = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - copymem(&buf[(i * 3 + j) * 4], &val.basis.elements[i][j], sizeof(float)); } } @@ -1211,7 +1158,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo // misc types case Variant::COLOR: { - if (buf) { Color c = p_variant; encode_float(c.r, &buf[0]); @@ -1223,19 +1169,14 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 * 4; } break; - case Variant::_RID: { - + case Variant::RID: { } break; case Variant::CALLABLE: { - } break; case Variant::SIGNAL: { - } break; case Variant::OBJECT: { - if (p_full_objects) { - Object *obj = p_variant; if (!obj) { if (buf) { @@ -1251,9 +1192,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo int pc = 0; for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; + } pc++; } @@ -1265,25 +1206,26 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; + } _encode_string(E->get().name, buf, r_len); int len; Error err = encode_variant(obj->get(E->get().name), buf, len, p_full_objects); - if (err) + if (err) { return err; + } ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } } } } else { if (buf) { - Object *obj = p_variant.get_validated_object(); ObjectID id; if (obj) { @@ -1298,7 +1240,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::DICTIONARY: { - Dictionary d = p_variant; if (buf) { @@ -1311,7 +1252,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - /* CharString utf8 = E->->utf8(); @@ -1329,20 +1269,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_variant(E->get(), buf, len, p_full_objects); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } Variant *v = d.getptr(E->get()); ERR_FAIL_COND_V(!v, ERR_BUG); encode_variant(*v, buf, len, p_full_objects); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } } } break; case Variant::ARRAY: { - Array v = p_variant; if (buf) { @@ -1353,19 +1294,18 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; for (int i = 0; i < v.size(); i++) { - int len; encode_variant(v.get(i), buf, len, p_full_objects); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } } } break; // arrays case Variant::PACKED_BYTE_ARRAY: { - Vector<uint8_t> data = p_variant; int datalen = data.size(); int datasize = sizeof(uint8_t); @@ -1381,13 +1321,13 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 + datalen * datasize; while (r_len % 4) { r_len++; - if (buf) + if (buf) { *(buf++) = 0; + } } } break; case Variant::PACKED_INT32_ARRAY: { - Vector<int32_t> data = p_variant; int datalen = data.size(); int datasize = sizeof(int32_t); @@ -1396,15 +1336,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_uint32(datalen, buf); buf += 4; const int32_t *r = data.ptr(); - for (int32_t i = 0; i < datalen; i++) + for (int32_t i = 0; i < datalen; i++) { encode_uint32(r[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; } break; case Variant::PACKED_INT64_ARRAY: { - Vector<int64_t> data = p_variant; int datalen = data.size(); int datasize = sizeof(int64_t); @@ -1413,15 +1353,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_uint64(datalen, buf); buf += 4; const int64_t *r = data.ptr(); - for (int64_t i = 0; i < datalen; i++) + for (int64_t i = 0; i < datalen; i++) { encode_uint64(r[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; } break; case Variant::PACKED_FLOAT32_ARRAY: { - Vector<float> data = p_variant; int datalen = data.size(); int datasize = sizeof(float); @@ -1430,15 +1370,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_uint32(datalen, buf); buf += 4; const float *r = data.ptr(); - for (int i = 0; i < datalen; i++) + for (int i = 0; i < datalen; i++) { encode_float(r[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; } break; case Variant::PACKED_FLOAT64_ARRAY: { - Vector<double> data = p_variant; int datalen = data.size(); int datasize = sizeof(double); @@ -1447,15 +1387,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_uint32(datalen, buf); buf += 4; const double *r = data.ptr(); - for (int i = 0; i < datalen; i++) + for (int i = 0; i < datalen; i++) { encode_double(r[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; } break; case Variant::PACKED_STRING_ARRAY: { - Vector<String> data = p_variant; int len = data.size(); @@ -1467,7 +1407,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; for (int i = 0; i < len; i++) { - CharString utf8 = data.get(i).utf8(); if (buf) { @@ -1480,14 +1419,14 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 + utf8.length() + 1; while (r_len % 4) { r_len++; //pad - if (buf) + if (buf) { *(buf++) = 0; + } } } } break; case Variant::PACKED_VECTOR2_ARRAY: { - Vector<Vector2> data = p_variant; int len = data.size(); @@ -1499,9 +1438,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; if (buf) { - for (int i = 0; i < len; i++) { - Vector2 v = data.get(i); encode_float(v.x, &buf[0]); @@ -1514,7 +1451,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::PACKED_VECTOR3_ARRAY: { - Vector<Vector3> data = p_variant; int len = data.size(); @@ -1526,9 +1462,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; if (buf) { - for (int i = 0; i < len; i++) { - Vector3 v = data.get(i); encode_float(v.x, &buf[0]); @@ -1542,7 +1476,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::PACKED_COLOR_ARRAY: { - Vector<Color> data = p_variant; int len = data.size(); @@ -1554,9 +1487,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; if (buf) { - for (int i = 0; i < len; i++) { - Color c = data.get(i); encode_float(c.r, &buf[0]); diff --git a/core/io/marshalls.h b/core/io/marshalls.h index d029ed238c..cc0e9ba301 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,9 +31,9 @@ #ifndef MARSHALLS_H #define MARSHALLS_H -#include "core/reference.h" +#include "core/object/reference.h" #include "core/typedefs.h" -#include "core/variant.h" +#include "core/variant/variant.h" /** * Miscellaneous helpers for marshalling data types, and encoding @@ -41,21 +41,17 @@ */ union MarshallFloat { - uint32_t i; ///< int float f; ///< float }; union MarshallDouble { - uint64_t l; ///< long long double d; ///< double }; static inline unsigned int encode_uint16(uint16_t p_uint, uint8_t *p_arr) { - for (int i = 0; i < 2; i++) { - *p_arr = p_uint & 0xFF; p_arr++; p_uint >>= 8; @@ -65,9 +61,7 @@ static inline unsigned int encode_uint16(uint16_t p_uint, uint8_t *p_arr) { } static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) { - for (int i = 0; i < 4; i++) { - *p_arr = p_uint & 0xFF; p_arr++; p_uint >>= 8; @@ -77,7 +71,6 @@ static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) { } static inline unsigned int encode_float(float p_float, uint8_t *p_arr) { - MarshallFloat mf; mf.f = p_float; encode_uint32(mf.i, p_arr); @@ -86,9 +79,7 @@ static inline unsigned int encode_float(float p_float, uint8_t *p_arr) { } static inline unsigned int encode_uint64(uint64_t p_uint, uint8_t *p_arr) { - for (int i = 0; i < 8; i++) { - *p_arr = p_uint & 0xFF; p_arr++; p_uint >>= 8; @@ -98,7 +89,6 @@ static inline unsigned int encode_uint64(uint64_t p_uint, uint8_t *p_arr) { } static inline unsigned int encode_double(double p_double, uint8_t *p_arr) { - MarshallDouble md; md.d = p_double; encode_uint64(md.l, p_arr); @@ -107,30 +97,27 @@ static inline unsigned int encode_double(double p_double, uint8_t *p_arr) { } static inline int encode_cstring(const char *p_string, uint8_t *p_data) { - int len = 0; while (*p_string) { - if (p_data) { - *p_data = (uint8_t)*p_string; p_data++; } p_string++; len++; - }; + } - if (p_data) *p_data = 0; + if (p_data) { + *p_data = 0; + } return len + 1; } static inline uint16_t decode_uint16(const uint8_t *p_arr) { - uint16_t u = 0; for (int i = 0; i < 2; i++) { - uint16_t b = *p_arr; b <<= (i * 8); u |= b; @@ -141,11 +128,9 @@ static inline uint16_t decode_uint16(const uint8_t *p_arr) { } static inline uint32_t decode_uint32(const uint8_t *p_arr) { - uint32_t u = 0; for (int i = 0; i < 4; i++) { - uint32_t b = *p_arr; b <<= (i * 8); u |= b; @@ -156,18 +141,15 @@ static inline uint32_t decode_uint32(const uint8_t *p_arr) { } static inline float decode_float(const uint8_t *p_arr) { - MarshallFloat mf; mf.i = decode_uint32(p_arr); return mf.f; } static inline uint64_t decode_uint64(const uint8_t *p_arr) { - uint64_t u = 0; for (int i = 0; i < 8; i++) { - uint64_t b = (*p_arr) & 0xFF; b <<= (i * 8); u |= b; @@ -178,7 +160,6 @@ static inline uint64_t decode_uint64(const uint8_t *p_arr) { } static inline double decode_double(const uint8_t *p_arr) { - MarshallDouble md; md.l = decode_uint64(p_arr); return md.d; @@ -196,7 +177,7 @@ public: void set_object_id(ObjectID p_id); ObjectID get_object_id() const; - EncodedObjectAsID(); + EncodedObjectAsID() {} }; Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false); diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 3bec52416e..6b550e69c8 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,6 +33,7 @@ #include "core/debugger/engine_debugger.h" #include "core/io/marshalls.h" #include "scene/main/node.h" + #include <stdint.h> #define NODE_ID_COMPRESSION_SHIFT 3 @@ -44,9 +45,7 @@ #endif _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { - switch (mode) { - case MultiplayerAPI::RPC_MODE_DISABLED: { // Do nothing. } break; @@ -54,8 +53,9 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas // Do nothing also. Remote cannot produce a local call. } break; case MultiplayerAPI::RPC_MODE_MASTERSYNC: { - if (is_master) + if (is_master) { r_skip_rpc = true; // I am the master, so skip remote call. + } [[fallthrough]]; } case MultiplayerAPI::RPC_MODE_REMOTESYNC: @@ -64,8 +64,9 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas return true; } break; case MultiplayerAPI::RPC_MODE_MASTER: { - if (is_master) + if (is_master) { r_skip_rpc = true; // I am the master, so skip remote call. + } return is_master; } break; case MultiplayerAPI::RPC_MODE_PUPPET: { @@ -77,7 +78,6 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { switch (mode) { - case MultiplayerAPI::RPC_MODE_DISABLED: { return false; } break; @@ -99,17 +99,17 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i } void MultiplayerAPI::poll() { - - if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) + if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) { return; + } network_peer->poll(); - if (!network_peer.is_valid()) // It's possible that polling might have resulted in a disconnection, so check here. + if (!network_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. return; + } while (network_peer->get_available_packet_count()) { - int sender = network_peer->get_packet_peer(); const uint8_t *packet; int len; @@ -142,9 +142,14 @@ void MultiplayerAPI::set_root_node(Node *p_node) { root_node = p_node; } -void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) { +Node *MultiplayerAPI::get_root_node() { + return root_node; +} - if (p_peer == network_peer) return; // Nothing to do +void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) { + if (p_peer == network_peer) { + return; // Nothing to do + } ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Supplied NetworkedMultiplayerPeer must be connecting or connected."); @@ -183,6 +188,7 @@ void _profile_node_data(const String &p_what, ObjectID p_id) { EngineDebugger::profiler_add_frame_data("multiplayer", values); } } + void _profile_bandwidth_data(const String &p_inout, int p_size) { if (EngineDebugger::is_profiling("multiplayer")) { Array values; @@ -206,7 +212,6 @@ int get_packet_len(uint32_t p_node_target, int p_packet_len) { } void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); @@ -218,20 +223,16 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ uint8_t packet_type = p_packet[0] & 7; switch (packet_type) { - case NETWORK_COMMAND_SIMPLIFY_PATH: { - _process_simplify_path(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_CONFIRM_PATH: { - _process_confirm_path(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_REMOTE_CALL: case NETWORK_COMMAND_REMOTE_SET: { - // Extract packet meta int packet_min_size = 1; int name_id_offset = 1; @@ -302,25 +303,21 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ const int packet_len = get_packet_len(node_target, p_packet_len); if (packet_type == NETWORK_COMMAND_REMOTE_CALL) { - _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); } else { - _process_rset(node, name_id, p_from, p_packet, packet_len, packet_min_size); } } break; case NETWORK_COMMAND_RAW: { - _process_raw(p_from, p_packet, p_packet_len); } break; } } Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { - Node *node = nullptr; if (p_node_target & 0x80000000) { @@ -337,8 +334,9 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin node = root_node->get_node(np); - if (!node) + if (!node) { ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); + } } else { // Use cached path. int id = p_node_target; @@ -353,14 +351,14 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin // Do proper caching later. node = root_node->get_node(ni->path); - if (!node) + if (!node) { ERR_PRINT("Failed to get cached path from RPC: " + String(ni->path) + "."); + } } return node; } void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RPC on this node. @@ -413,7 +411,6 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, p_offset += len; } else { for (int i = 0; i < argc; i++) { - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); int vlen; @@ -436,7 +433,6 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, } void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RSET on this node. @@ -470,7 +466,6 @@ void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_i } void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); int ofs = 1; @@ -519,7 +514,6 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, } void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); const bool valid_rpc_checksum = p_packet[1]; @@ -546,12 +540,13 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC List<int> peers_to_add; // If one is missing, take note to add it. for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - - if (p_target < 0 && E->get() == -p_target) + if (p_target < 0 && E->get() == -p_target) { continue; // Continue, excluded. + } - if (p_target > 0 && E->get() != p_target) + if (p_target > 0 && E->get() != p_target) { continue; // Continue, not for this peer. + } Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); @@ -567,7 +562,6 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC } if (peers_to_add.size() > 0) { - // Those that need to be added, send a message for this. // Encode function name. @@ -592,7 +586,6 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC ofs += encode_cstring(path.get_data(), &packet.write[ofs]); for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { - network_peer->set_target_peer(E->get()); // To all of you. network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->put_packet(packet.ptr(), packet.size()); @@ -617,7 +610,6 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC #define ENCODE_32 2 << 5 #define ENCODE_64 3 << 5 Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { - // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); @@ -679,8 +671,9 @@ Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uin default: // Any other case is not yet compressed. Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); - if (err != OK) + if (err != OK) { return err; + } if (r_buffer) { // The first byte is not used by the marshaling, so store the type // so we know how to decompress and decode this variant. @@ -690,8 +683,8 @@ Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uin return OK; } -Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { +Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { const uint8_t *buf = p_buffer; int len = p_len; @@ -705,55 +698,61 @@ Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const u case Variant::BOOL: { bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0; r_variant = val; - if (r_len) + if (r_len) { *r_len = 1; + } } break; case Variant::INT: { buf += 1; len -= 1; - if (r_len) + if (r_len) { *r_len = 1; + } if (encode_mode == ENCODE_8) { // 8 bits. ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); int8_t val = buf[0]; r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 1; + } } else if (encode_mode == ENCODE_16) { // 16 bits. ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA); int16_t val = decode_uint16(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 2; + } } else if (encode_mode == ENCODE_32) { // 32 bits. ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t val = decode_uint32(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4; + } } else { // 64 bits. ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); int64_t val = decode_uint64(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 8; + } } } break; default: Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); - if (err != OK) + if (err != OK) { return err; + } } return OK; } void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); @@ -787,8 +786,9 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p int ofs = 0; -#define MAKE_ROOM(m_amount) \ - if (packet_cache.size() < m_amount) packet_cache.resize(m_amount); +#define MAKE_ROOM(m_amount) \ + if (packet_cache.size() < m_amount) \ + packet_cache.resize(m_amount); // Encode meta. // The meta is composed by a single byte that contains (starting from the least segnificant bit): @@ -838,7 +838,6 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p } if (p_set) { - // Take the rpc property ID uint16_t property_id = p_from->get_node_rset_property_id(p_name); if (property_id == UINT16_MAX && p_from->get_script_instance()) { @@ -931,7 +930,6 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); if (has_all_peers) { - // They all have verified paths, so send fast. network_peer->set_target_peer(p_to); // To all of you. network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. @@ -948,12 +946,13 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - - if (p_to < 0 && E->get() == -p_to) + if (p_to < 0 && E->get() == -p_to) { continue; // Continue, excluded. + } - if (p_to > 0 && E->get() != p_to) + if (p_to > 0 && E->get() != p_to) { continue; // Continue, not for this peer. + } Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); ERR_CONTINUE(!F); // Should never happen. @@ -995,22 +994,18 @@ void MultiplayerAPI::_del_peer(int p_id) { } void MultiplayerAPI::_connected_to_server() { - emit_signal("connected_to_server"); } void MultiplayerAPI::_connection_failed() { - emit_signal("connection_failed"); } void MultiplayerAPI::_server_disconnected() { - emit_signal("server_disconnected"); } void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); @@ -1037,7 +1032,6 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } if (!skip_rpc) { - #ifdef DEBUG_ENABLED _profile_node_data("out_rpc", p_node->get_instance_id()); #endif @@ -1078,7 +1072,6 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to RSET while no network peer is active."); ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to RSET on a node which is not inside SceneTree."); ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to send an RSET via a network peer which is not connected."); @@ -1143,7 +1136,6 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const } Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { - ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); @@ -1160,7 +1152,6 @@ Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMult } void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); Vector<uint8_t> out; @@ -1174,32 +1165,27 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac } int MultiplayerAPI::get_network_unique_id() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID."); return network_peer->get_unique_id(); } bool MultiplayerAPI::is_network_server() const { - // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server."); return network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'."); network_peer->set_refuse_new_connections(p_refuse); } bool MultiplayerAPI::is_refusing_new_network_connections() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'."); return network_peer->is_refusing_new_connections(); } Vector<int> MultiplayerAPI::get_network_connected_peers() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected."); Vector<int> ret; @@ -1211,17 +1197,16 @@ Vector<int> MultiplayerAPI::get_network_connected_peers() const { } void MultiplayerAPI::set_allow_object_decoding(bool p_enable) { - allow_object_decoding = p_enable; } bool MultiplayerAPI::is_object_decoding_allowed() const { - return allow_object_decoding; } void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); + ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); @@ -1241,6 +1226,7 @@ void MultiplayerAPI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_root_node", "get_root_node"); ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false); ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); @@ -1259,10 +1245,7 @@ void MultiplayerAPI::_bind_methods() { BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); } -MultiplayerAPI::MultiplayerAPI() : - allow_object_decoding(false) { - rpc_sender_id = 0; - root_node = nullptr; +MultiplayerAPI::MultiplayerAPI() { clear(); } diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index 4eb4a53e99..7f88b53a27 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,10 +32,9 @@ #define MULTIPLAYER_API_H #include "core/io/networked_multiplayer_peer.h" -#include "core/reference.h" +#include "core/object/reference.h" class MultiplayerAPI : public Reference { - GDCLASS(MultiplayerAPI, Reference); private: @@ -56,14 +55,14 @@ private: }; Ref<NetworkedMultiplayerPeer> network_peer; - int rpc_sender_id; + int rpc_sender_id = 0; Set<int> connected_peers; HashMap<NodePath, PathSentCache> path_send_cache; Map<int, PathGetCache> path_get_cache; int last_send_cache_id; Vector<uint8_t> packet_cache; - Node *root_node; - bool allow_object_decoding; + Node *root_node = nullptr; + bool allow_object_decoding = false; protected: static void _bind_methods(); @@ -103,7 +102,6 @@ public: }; enum RPCMode { - RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote @@ -116,6 +114,7 @@ public: void poll(); void clear(); void set_root_node(Node *p_node); + Node *get_root_node(); void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer); Ref<NetworkedMultiplayerPeer> get_network_peer() const; Error send_bytes(Vector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer::TransferMode p_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); diff --git a/core/io/net_socket.cpp b/core/io/net_socket.cpp index 838c674cec..b51d26ba83 100644 --- a/core/io/net_socket.cpp +++ b/core/io/net_socket.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,9 +33,9 @@ NetSocket *(*NetSocket::_create)() = nullptr; NetSocket *NetSocket::create() { - - if (_create) + if (_create) { return _create(); + } ERR_PRINT("Unable to create network socket, platform not supported"); return nullptr; diff --git a/core/io/net_socket.h b/core/io/net_socket.h index 376fd87a27..bc09477693 100644 --- a/core/io/net_socket.h +++ b/core/io/net_socket.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,10 +32,9 @@ #define NET_SOCKET_H #include "core/io/ip.h" -#include "core/reference.h" +#include "core/object/reference.h" class NetSocket : public Reference { - protected: static NetSocket *(*_create)(); diff --git a/core/io/networked_multiplayer_peer.cpp b/core/io/networked_multiplayer_peer.cpp index b2f810d212..b6af046e77 100644 --- a/core/io/networked_multiplayer_peer.cpp +++ b/core/io/networked_multiplayer_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,6 @@ #include "networked_multiplayer_peer.h" void NetworkedMultiplayerPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &NetworkedMultiplayerPeer::set_transfer_mode); ClassDB::bind_method(D_METHOD("get_transfer_mode"), &NetworkedMultiplayerPeer::get_transfer_mode); ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &NetworkedMultiplayerPeer::set_target_peer); @@ -66,6 +65,3 @@ void NetworkedMultiplayerPeer::_bind_methods() { ADD_SIGNAL(MethodInfo("connection_succeeded")); ADD_SIGNAL(MethodInfo("connection_failed")); } - -NetworkedMultiplayerPeer::NetworkedMultiplayerPeer() { -} diff --git a/core/io/networked_multiplayer_peer.h b/core/io/networked_multiplayer_peer.h index c1f1924051..7c90f97d88 100644 --- a/core/io/networked_multiplayer_peer.h +++ b/core/io/networked_multiplayer_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -34,7 +34,6 @@ #include "core/io/packet_peer.h" class NetworkedMultiplayerPeer : public PacketPeer { - GDCLASS(NetworkedMultiplayerPeer, PacketPeer); protected: @@ -74,7 +73,7 @@ public: virtual ConnectionStatus get_connection_status() const = 0; - NetworkedMultiplayerPeer(); + NetworkedMultiplayerPeer() {} }; VARIANT_ENUM_CAST(NetworkedMultiplayerPeer::TransferMode) diff --git a/core/packed_data_container.cpp b/core/io/packed_data_container.cpp index 04deba2c14..a0b97772e6 100644 --- a/core/packed_data_container.cpp +++ b/core/io/packed_data_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -34,51 +34,53 @@ #include "core/io/marshalls.h" Variant PackedDataContainer::getvar(const Variant &p_key, bool *r_valid) const { - bool err = false; Variant ret = _key_at_ofs(0, p_key, err); - if (r_valid) + if (r_valid) { *r_valid = !err; + } + if (err) { + return Object::getvar(p_key, r_valid); + } return ret; } int PackedDataContainer::size() const { - return _size(0); -}; +} Variant PackedDataContainer::_iter_init_ofs(const Array &p_iter, uint32_t p_offset) { - Array ref = p_iter; uint32_t size = _size(p_offset); - if (size == 0 || ref.size() != 1) + if (size == 0 || ref.size() != 1) { return false; - else { + } else { ref[0] = 0; return true; } } Variant PackedDataContainer::_iter_next_ofs(const Array &p_iter, uint32_t p_offset) { - Array ref = p_iter; int size = _size(p_offset); - if (ref.size() != 1) + if (ref.size() != 1) { return false; + } int pos = ref[0]; - if (pos < 0 || pos >= size) + if (pos < 0 || pos >= size) { return false; + } pos += 1; ref[0] = pos; return pos != size; } Variant PackedDataContainer::_iter_get_ofs(const Variant &p_iter, uint32_t p_offset) { - int size = _size(p_offset); int pos = p_iter; - if (pos < 0 || pos >= size) + if (pos < 0 || pos >= size) { return Variant(); + } const uint8_t *rd = data.ptr(); const uint8_t *r = &rd[p_offset]; @@ -86,12 +88,10 @@ Variant PackedDataContainer::_iter_get_ofs(const Variant &p_iter, uint32_t p_off bool err = false; if (type == TYPE_ARRAY) { - uint32_t vpos = decode_uint32(rd + p_offset + 8 + pos * 4); return _get_at_ofs(vpos, rd, err); } else if (type == TYPE_DICT) { - uint32_t vpos = decode_uint32(rd + p_offset + 8 + pos * 12 + 4); return _get_at_ofs(vpos, rd, err); } else { @@ -100,11 +100,9 @@ Variant PackedDataContainer::_iter_get_ofs(const Variant &p_iter, uint32_t p_off } Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, bool &err) const { - uint32_t type = decode_uint32(p_buf + p_ofs); if (type == TYPE_ARRAY || type == TYPE_DICT) { - Ref<PackedDataContainerRef> pdcr = memnew(PackedDataContainerRef); Ref<PackedDataContainer> pdc = Ref<PackedDataContainer>((PackedDataContainer *)this); @@ -112,12 +110,10 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b pdcr->offset = p_ofs; return pdcr; } else { - Variant v; Error rerr = decode_variant(v, p_buf + p_ofs, datalen - p_ofs, nullptr, false); if (rerr != OK) { - err = true; ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); } @@ -126,45 +122,38 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b } uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const { - const uint8_t *rd = data.ptr(); const uint8_t *r = &rd[p_ofs]; uint32_t type = decode_uint32(r); return type; -}; +} int PackedDataContainer::_size(uint32_t p_ofs) const { - const uint8_t *rd = data.ptr(); ERR_FAIL_COND_V(!rd, 0); const uint8_t *r = &rd[p_ofs]; uint32_t type = decode_uint32(r); if (type == TYPE_ARRAY) { - uint32_t len = decode_uint32(r + 4); return len; } else if (type == TYPE_DICT) { - uint32_t len = decode_uint32(r + 4); return len; - }; + } return -1; -}; +} Variant PackedDataContainer::_key_at_ofs(uint32_t p_ofs, const Variant &p_key, bool &err) const { - const uint8_t *rd = data.ptr(); const uint8_t *r = &rd[p_ofs]; uint32_t type = decode_uint32(r); if (type == TYPE_ARRAY) { - if (p_key.is_num()) { - int idx = p_key; int len = decode_uint32(r + 4); if (idx < 0 || idx >= len) { @@ -180,7 +169,6 @@ Variant PackedDataContainer::_key_at_ofs(uint32_t p_ofs, const Variant &p_key, b } } else if (type == TYPE_DICT) { - uint32_t hash = p_key.hash(); uint32_t len = decode_uint32(r + 4); @@ -189,16 +177,18 @@ Variant PackedDataContainer::_key_at_ofs(uint32_t p_ofs, const Variant &p_key, b uint32_t khash = decode_uint32(r + 8 + i * 12 + 0); if (khash == hash) { Variant key = _get_at_ofs(decode_uint32(r + 8 + i * 12 + 4), rd, err); - if (err) + if (err) { return Variant(); + } if (key == p_key) { //key matches, return value return _get_at_ofs(decode_uint32(r + 8 + i * 12 + 8), rd, err); } found = true; } else { - if (found) + if (found) { break; + } } } @@ -206,18 +196,14 @@ Variant PackedDataContainer::_key_at_ofs(uint32_t p_ofs, const Variant &p_key, b return Variant(); } else { - err = true; return Variant(); } } uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpdata, Map<String, uint32_t> &string_cache) { - switch (p_data.get_type()) { - case Variant::STRING: { - String s = p_data; if (string_cache.has(s)) { return string_cache[s]; @@ -251,7 +237,6 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd case Variant::PACKED_COLOR_ARRAY: case Variant::STRING_NAME: case Variant::NODE_PATH: { - uint32_t pos = tmpdata.size(); int len; encode_variant(p_data, nullptr, len, false); @@ -261,13 +246,11 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd } break; // misc types - case Variant::_RID: + case Variant::RID: case Variant::OBJECT: { - return _pack(Variant(), tmpdata, string_cache); } break; case Variant::DICTIONARY: { - Dictionary d = p_data; //size is known, use sort uint32_t pos = tmpdata.size(); @@ -281,7 +264,6 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd List<DictKey> sortk; for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - DictKey dk; dk.hash = E->get().hash(); dk.key = E->get(); @@ -292,7 +274,6 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd int idx = 0; for (List<DictKey>::Element *E = sortk.front(); E; E = E->next()) { - encode_uint32(E->get().hash, &tmpdata.write[pos + 8 + idx * 12 + 0]); uint32_t ofs = _pack(E->get().key, tmpdata, string_cache); encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 4]); @@ -305,7 +286,6 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd } break; case Variant::ARRAY: { - Array a = p_data; //size is known, use sort uint32_t pos = tmpdata.size(); @@ -315,7 +295,6 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd encode_uint32(len, &tmpdata.write[pos + 4]); for (int i = 0; i < len; i++) { - uint32_t ofs = _pack(a[i], tmpdata, string_cache); encode_uint32(ofs, &tmpdata.write[pos + 8 + i * 4]); } @@ -332,7 +311,6 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd } Error PackedDataContainer::pack(const Variant &p_data) { - Vector<uint8_t> tmpdata; Map<String, uint32_t> string_cache; _pack(p_data, tmpdata, string_cache); @@ -345,7 +323,6 @@ Error PackedDataContainer::pack(const Variant &p_data) { } void PackedDataContainer::_set_data(const Vector<uint8_t> &p_data) { - data = p_data; datalen = data.size(); } @@ -355,21 +332,18 @@ Vector<uint8_t> PackedDataContainer::_get_data() const { } Variant PackedDataContainer::_iter_init(const Array &p_iter) { - return _iter_init_ofs(p_iter, 0); } Variant PackedDataContainer::_iter_next(const Array &p_iter) { - return _iter_next_ofs(p_iter, 0); } -Variant PackedDataContainer::_iter_get(const Variant &p_iter) { +Variant PackedDataContainer::_iter_get(const Variant &p_iter) { return _iter_get_ofs(p_iter, 0); } void PackedDataContainer::_bind_methods() { - ClassDB::bind_method(D_METHOD("_set_data"), &PackedDataContainer::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &PackedDataContainer::_get_data); ClassDB::bind_method(D_METHOD("_iter_init"), &PackedDataContainer::_iter_init); @@ -381,34 +355,25 @@ void PackedDataContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "__data__"), "_set_data", "_get_data"); } -PackedDataContainer::PackedDataContainer() { - - datalen = 0; -} - ////////////////// Variant PackedDataContainerRef::_iter_init(const Array &p_iter) { - return from->_iter_init_ofs(p_iter, offset); } Variant PackedDataContainerRef::_iter_next(const Array &p_iter) { - return from->_iter_next_ofs(p_iter, offset); } -Variant PackedDataContainerRef::_iter_get(const Variant &p_iter) { +Variant PackedDataContainerRef::_iter_get(const Variant &p_iter) { return from->_iter_get_ofs(p_iter, offset); } bool PackedDataContainerRef::_is_dictionary() const { - return from->_type_at_ofs(offset) == PackedDataContainer::TYPE_DICT; -}; +} void PackedDataContainerRef::_bind_methods() { - ClassDB::bind_method(D_METHOD("size"), &PackedDataContainerRef::size); ClassDB::bind_method(D_METHOD("_iter_init"), &PackedDataContainerRef::_iter_init); ClassDB::bind_method(D_METHOD("_iter_get"), &PackedDataContainerRef::_iter_get); @@ -417,18 +382,14 @@ void PackedDataContainerRef::_bind_methods() { } Variant PackedDataContainerRef::getvar(const Variant &p_key, bool *r_valid) const { - bool err = false; Variant ret = from->_key_at_ofs(offset, p_key, err); - if (r_valid) + if (r_valid) { *r_valid = !err; + } return ret; } int PackedDataContainerRef::size() const { - return from->_size(offset); -}; - -PackedDataContainerRef::PackedDataContainerRef() { } diff --git a/core/packed_data_container.h b/core/io/packed_data_container.h index 0f08a1cb7b..7791e21bb3 100644 --- a/core/packed_data_container.h +++ b/core/io/packed_data_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,10 +31,9 @@ #ifndef PACKED_DATA_CONTAINER_H #define PACKED_DATA_CONTAINER_H -#include "core/resource.h" +#include "core/io/resource.h" class PackedDataContainer : public Resource { - GDCLASS(PackedDataContainer, Resource); enum { @@ -49,7 +48,7 @@ class PackedDataContainer : public Resource { }; Vector<uint8_t> data; - int datalen; + int datalen = 0; uint32_t _pack(const Variant &p_data, Vector<uint8_t> &tmpdata, Map<String, uint32_t> &string_cache); @@ -73,19 +72,19 @@ protected: static void _bind_methods(); public: - virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const; + virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const override; Error pack(const Variant &p_data); int size() const; - PackedDataContainer(); + PackedDataContainer() {} }; class PackedDataContainerRef : public Reference { GDCLASS(PackedDataContainerRef, Reference); friend class PackedDataContainer; - uint32_t offset; + uint32_t offset = 0; Ref<PackedDataContainer> from; protected: @@ -98,9 +97,9 @@ public: bool _is_dictionary() const; int size() const; - virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const; + virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const override; - PackedDataContainerRef(); + PackedDataContainerRef() {} }; #endif // PACKED_DATA_CONTAINER_H diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 38abb5c0d6..318fd10243 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,18 +30,12 @@ #include "packet_peer.h" +#include "core/config/project_settings.h" #include "core/io/marshalls.h" -#include "core/project_settings.h" /* helpers / binders */ -PacketPeer::PacketPeer() : - last_get_error(OK), - encode_buffer_max_size(8 * 1024 * 1024) { -} - void PacketPeer::set_encode_buffer_max_size(int p_max_size) { - ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes"); ERR_FAIL_COND_MSG(p_max_size > 256 * 1024 * 1024, "Max encode buffer cannot exceed 256 MiB"); encode_buffer_max_size = next_power_of_2(p_max_size); @@ -49,59 +43,61 @@ void PacketPeer::set_encode_buffer_max_size(int p_max_size) { } int PacketPeer::get_encode_buffer_max_size() const { - return encode_buffer_max_size; } Error PacketPeer::get_packet_buffer(Vector<uint8_t> &r_buffer) { - const uint8_t *buffer; int buffer_size; Error err = get_packet(&buffer, buffer_size); - if (err) + if (err) { return err; + } r_buffer.resize(buffer_size); - if (buffer_size == 0) + if (buffer_size == 0) { return OK; + } uint8_t *w = r_buffer.ptrw(); - for (int i = 0; i < buffer_size; i++) + for (int i = 0; i < buffer_size; i++) { w[i] = buffer[i]; + } return OK; } Error PacketPeer::put_packet_buffer(const Vector<uint8_t> &p_buffer) { - int len = p_buffer.size(); - if (len == 0) + if (len == 0) { return OK; + } const uint8_t *r = p_buffer.ptr(); return put_packet(&r[0], len); } Error PacketPeer::get_var(Variant &r_variant, bool p_allow_objects) { - const uint8_t *buffer; int buffer_size; Error err = get_packet(&buffer, buffer_size); - if (err) + if (err) { return err; + } return decode_variant(r_variant, buffer, buffer_size, nullptr, p_allow_objects); } Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { - int len; Error err = encode_variant(p_packet, nullptr, len, p_full_objects); // compute len first - if (err) + if (err) { return err; + } - if (len == 0) + if (len == 0) { return OK; + } ERR_FAIL_COND_V_MSG(len > encode_buffer_max_size, ERR_OUT_OF_MEMORY, "Failed to encode variant, encode size is bigger then encode_buffer_max_size. Consider raising it via 'set_encode_buffer_max_size'."); @@ -128,20 +124,18 @@ Variant PacketPeer::_bnd_get_var(bool p_allow_objects) { Error PacketPeer::_put_packet(const Vector<uint8_t> &p_buffer) { return put_packet_buffer(p_buffer); } -Vector<uint8_t> PacketPeer::_get_packet() { +Vector<uint8_t> PacketPeer::_get_packet() { Vector<uint8_t> raw; last_get_error = get_packet_buffer(raw); return raw; } Error PacketPeer::_get_packet_error() const { - return last_get_error; } void PacketPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &PacketPeer::_bnd_get_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("put_var", "var", "full_objects"), &PacketPeer::put_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_packet"), &PacketPeer::_get_packet); @@ -153,18 +147,11 @@ void PacketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_encode_buffer_max_size", "max_size"), &PacketPeer::set_encode_buffer_max_size); ADD_PROPERTY(PropertyInfo(Variant::INT, "encode_buffer_max_size"), "set_encode_buffer_max_size", "get_encode_buffer_max_size"); -}; +} /***************/ -void PacketPeerStream::_set_stream_peer(REF p_peer) { - - ERR_FAIL_COND_MSG(p_peer.is_null(), "It's not a reference to a valid Resource object."); - set_stream_peer(p_peer); -} - void PacketPeerStream::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_stream_peer", "peer"), &PacketPeerStream::set_stream_peer); ClassDB::bind_method(D_METHOD("get_stream_peer"), &PacketPeerStream::get_stream_peer); ClassDB::bind_method(D_METHOD("set_input_buffer_max_size", "max_size_bytes"), &PacketPeerStream::set_input_buffer_max_size); @@ -178,16 +165,17 @@ void PacketPeerStream::_bind_methods() { } Error PacketPeerStream::_poll_buffer() const { - ERR_FAIL_COND_V(peer.is_null(), ERR_UNCONFIGURED); int read = 0; ERR_FAIL_COND_V(input_buffer.size() < ring_buffer.space_left(), ERR_UNAVAILABLE); Error err = peer->get_partial_data(input_buffer.ptrw(), ring_buffer.space_left(), read); - if (err) + if (err) { return err; - if (read == 0) + } + if (read == 0) { return OK; + } int w = ring_buffer.write(&input_buffer[0], read); ERR_FAIL_COND_V(w != read, ERR_BUG); @@ -196,7 +184,6 @@ Error PacketPeerStream::_poll_buffer() const { } int PacketPeerStream::get_available_packet_count() const { - _poll_buffer(); uint32_t remaining = ring_buffer.data_left(); @@ -205,14 +192,14 @@ int PacketPeerStream::get_available_packet_count() const { int count = 0; while (remaining >= 4) { - uint8_t lbuf[4]; ring_buffer.copy(lbuf, ofs, 4); uint32_t len = decode_uint32(lbuf); remaining -= 4; ofs += 4; - if (len > remaining) + if (len > remaining) { break; + } remaining -= len; ofs += len; count++; @@ -222,7 +209,6 @@ int PacketPeerStream::get_available_packet_count() const { } Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - ERR_FAIL_COND_V(peer.is_null(), ERR_UNCONFIGURED); _poll_buffer(); @@ -244,50 +230,46 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) } Error PacketPeerStream::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(peer.is_null(), ERR_UNCONFIGURED); Error err = _poll_buffer(); //won't hurt to poll here too - if (err) + if (err) { return err; + } - if (p_buffer_size == 0) + if (p_buffer_size == 0) { return OK; + } ERR_FAIL_COND_V(p_buffer_size < 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_buffer_size + 4 > output_buffer.size(), ERR_INVALID_PARAMETER); encode_uint32(p_buffer_size, output_buffer.ptrw()); uint8_t *dst = &output_buffer.write[4]; - for (int i = 0; i < p_buffer_size; i++) + for (int i = 0; i < p_buffer_size; i++) { dst[i] = p_buffer[i]; + } return peer->put_data(&output_buffer[0], p_buffer_size + 4); } int PacketPeerStream::get_max_packet_size() const { - return output_buffer.size(); } void PacketPeerStream::set_stream_peer(const Ref<StreamPeer> &p_peer) { - - //ERR_FAIL_COND(p_peer.is_null()); - if (p_peer.ptr() != peer.ptr()) { - ring_buffer.advance_read(ring_buffer.data_left()); // reset the ring buffer - }; + ring_buffer.advance_read(ring_buffer.data_left()); // Reset the ring buffer. + } peer = p_peer; } Ref<StreamPeer> PacketPeerStream::get_stream_peer() const { - return peer; } void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { - ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0."); //warning may lose packets ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data."); @@ -296,22 +278,18 @@ void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { } int PacketPeerStream::get_input_buffer_max_size() const { - return input_buffer.size() - 4; } void PacketPeerStream::set_output_buffer_max_size(int p_max_size) { - output_buffer.resize(next_power_of_2(p_max_size + 4)); } int PacketPeerStream::get_output_buffer_max_size() const { - return output_buffer.size() - 4; } PacketPeerStream::PacketPeerStream() { - int rbsize = GLOBAL_GET("network/limits/packet_peer_stream/max_buffer_po2"); ring_buffer.resize(rbsize); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 62144259cc..9e03c44750 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,11 +32,10 @@ #define PACKET_PEER_H #include "core/io/stream_peer.h" -#include "core/object.h" -#include "core/ring_buffer.h" +#include "core/object/class_db.h" +#include "core/templates/ring_buffer.h" class PacketPeer : public Reference { - GDCLASS(PacketPeer, Reference); Variant _bnd_get_var(bool p_allow_objects = false); @@ -47,9 +46,9 @@ class PacketPeer : public Reference { Vector<uint8_t> _get_packet(); Error _get_packet_error() const; - mutable Error last_get_error; + mutable Error last_get_error = OK; - int encode_buffer_max_size; + int encode_buffer_max_size = 8 * 1024 * 1024; Vector<uint8_t> encode_buffer; public: @@ -70,12 +69,11 @@ public: void set_encode_buffer_max_size(int p_max_size); int get_encode_buffer_max_size() const; - PacketPeer(); + PacketPeer() {} ~PacketPeer() {} }; class PacketPeerStream : public PacketPeer { - GDCLASS(PacketPeerStream, PacketPeer); //the way the buffers work sucks, will change later @@ -88,15 +86,14 @@ class PacketPeerStream : public PacketPeer { Error _poll_buffer() const; protected: - void _set_stream_peer(REF p_peer); static void _bind_methods(); public: - virtual int get_available_packet_count() const; - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const; + virtual int get_max_packet_size() const override; void set_stream_peer(const Ref<StreamPeer> &p_peer); Ref<StreamPeer> get_stream_peer() const; diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp index 6da115eed2..bac98e20e7 100644 --- a/core/io/packet_peer_dtls.cpp +++ b/core/io/packet_peer_dtls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,15 +29,17 @@ /*************************************************************************/ #include "packet_peer_dtls.h" +#include "core/config/project_settings.h" #include "core/os/file_access.h" -#include "core/project_settings.h" PacketPeerDTLS *(*PacketPeerDTLS::_create)() = nullptr; bool PacketPeerDTLS::available = false; PacketPeerDTLS *PacketPeerDTLS::create() { - - return _create(); + if (_create) { + return _create(); + } + return nullptr; } bool PacketPeerDTLS::is_available() { @@ -45,7 +47,6 @@ bool PacketPeerDTLS::is_available() { } void PacketPeerDTLS::_bind_methods() { - ClassDB::bind_method(D_METHOD("poll"), &PacketPeerDTLS::poll); ClassDB::bind_method(D_METHOD("connect_to_peer", "packet_peer", "validate_certs", "for_hostname", "valid_certificate"), &PacketPeerDTLS::connect_to_peer, DEFVAL(true), DEFVAL(String()), DEFVAL(Ref<X509Certificate>())); ClassDB::bind_method(D_METHOD("get_status"), &PacketPeerDTLS::get_status); @@ -57,6 +58,3 @@ void PacketPeerDTLS::_bind_methods() { BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); } - -PacketPeerDTLS::PacketPeerDTLS() { -} diff --git a/core/io/packet_peer_dtls.h b/core/io/packet_peer_dtls.h index 4f9f4535bc..31c52f539f 100644 --- a/core/io/packet_peer_dtls.h +++ b/core/io/packet_peer_dtls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -60,7 +60,7 @@ public: static PacketPeerDTLS *create(); static bool is_available(); - PacketPeerDTLS(); + PacketPeerDTLS() {} }; VARIANT_ENUM_CAST(PacketPeerDTLS::Status); diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index f800ffc3db..d8d63d976f 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,20 +31,22 @@ #include "packet_peer_udp.h" #include "core/io/ip.h" +#include "core/io/udp_server.h" void PacketPeerUDP::set_blocking_mode(bool p_enable) { - blocking = p_enable; } void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) { + ERR_FAIL_COND(udp_server); broadcast = p_enabled; - if (_sock.is_valid() && _sock->is_open()) + if (_sock.is_valid() && _sock->is_open()) { _sock->set_broadcasting_enabled(p_enabled); + } } Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_if_name) { - + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER); @@ -59,26 +61,25 @@ Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_i } Error PacketPeerUDP::leave_multicast_group(IP_Address p_multi_address, String p_if_name) { - + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED); return _sock->leave_multicast_group(p_multi_address, p_if_name); } String PacketPeerUDP::_get_packet_ip() const { - return get_packet_address(); } Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { - IP_Address ip; if (p_address.is_valid_ip_address()) { ip = p_address; } else { ip = IP::get_singleton()->resolve_hostname(p_address); - if (!ip.is_valid()) + if (!ip.is_valid()) { return ERR_CANT_RESOLVE; + } } set_dest_address(ip, p_port); @@ -86,22 +87,23 @@ Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { } int PacketPeerUDP::get_available_packet_count() const { - // TODO we should deprecate this, and expose poll instead! Error err = const_cast<PacketPeerUDP *>(this)->_poll(); - if (err != OK) + if (err != OK) { return -1; + } return queue_count; } Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - Error err = _poll(); - if (err != OK) + if (err != OK) { return err; - if (queue_count == 0) + } + if (queue_count == 0) { return ERR_UNAVAILABLE; + } uint32_t size = 0; uint8_t ipv6[16]; @@ -117,7 +119,6 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { } Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); @@ -133,16 +134,17 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { } do { - if (connected) { + if (connected && !udp_server) { err = _sock->send(p_buffer, p_buffer_size, sent); } else { err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); } if (err != OK) { - if (err != ERR_BUSY) + if (err != ERR_BUSY) { return FAILED; - else if (!blocking) + } else if (!blocking) { return ERR_BUSY; + } // Keep trying to send full packet continue; } @@ -154,12 +156,10 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { } int PacketPeerUDP::get_max_packet_size() const { - return 512; // uhm maybe not } Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); @@ -167,16 +167,17 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_ Error err; IP::Type ip_type = IP::TYPE_ANY; - if (p_bind_address.is_valid()) + if (p_bind_address.is_valid()) { ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + } err = _sock->open(NetSocket::TYPE_UDP, ip_type); - if (err != OK) + if (err != OK) { return ERR_CANT_CREATE; + } _sock->set_blocking_enabled(false); - _sock->set_reuse_address_enabled(true); _sock->set_broadcasting_enabled(broadcast); err = _sock->bind(p_bind_address, p_port); @@ -188,26 +189,25 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_ return OK; } -Error PacketPeerUDP::connect_socket(Ref<NetSocket> p_sock) { - Error err; - int read = 0; - uint16_t r_port; - IP_Address r_ip; - - err = p_sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, r_ip, r_port, true); - ERR_FAIL_COND_V(err != OK, err); - err = p_sock->connect_to_host(r_ip, r_port); - ERR_FAIL_COND_V(err != OK, err); +Error PacketPeerUDP::connect_shared_socket(Ref<NetSocket> p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *p_server) { + udp_server = p_server; + connected = true; _sock = p_sock; - peer_addr = r_ip; - peer_port = r_port; + peer_addr = p_ip; + peer_port = p_port; packet_ip = peer_addr; packet_port = peer_port; - connected = true; return OK; } +void PacketPeerUDP::disconnect_shared_socket() { + udp_server = nullptr; + _sock = Ref<NetSocket>(NetSocket::create()); + close(); +} + Error PacketPeerUDP::connect_to_host(const IP_Address &p_host, int p_port) { + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); @@ -245,27 +245,32 @@ bool PacketPeerUDP::is_connected_to_host() const { } void PacketPeerUDP::close() { - - if (_sock.is_valid()) + if (udp_server) { + udp_server->remove_peer(peer_addr, peer_port); + udp_server = nullptr; + _sock = Ref<NetSocket>(NetSocket::create()); + } else if (_sock.is_valid()) { _sock->close(); + } rb.resize(16); queue_count = 0; connected = false; } Error PacketPeerUDP::wait() { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); return _sock->poll(NetSocket::POLL_TYPE_IN, -1); } Error PacketPeerUDP::_poll() { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); if (!_sock->is_open()) { return FAILED; } + if (udp_server) { + return OK; // Handled by UDPServer. + } Error err; int read; @@ -282,52 +287,54 @@ Error PacketPeerUDP::_poll() { } if (err != OK) { - if (err == ERR_BUSY) + if (err == ERR_BUSY) { break; + } return FAILED; } - if (rb.space_left() < read + 24) { + err = store_packet(ip, port, recv_buffer, read); #ifdef TOOLS_ENABLED + if (err != OK) { WARN_PRINT("Buffer full, dropping packets!"); -#endif - continue; } - - uint32_t port32 = port; - rb.write(ip.get_ipv6(), 16); - rb.write((uint8_t *)&port32, 4); - rb.write((uint8_t *)&read, 4); - rb.write(recv_buffer, read); - ++queue_count; +#endif } return OK; } -bool PacketPeerUDP::is_listening() const { +Error PacketPeerUDP::store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size) { + if (rb.space_left() < p_buf_size + 24) { + return ERR_OUT_OF_MEMORY; + } + rb.write(p_ip.get_ipv6(), 16); + rb.write((uint8_t *)&p_port, 4); + rb.write((uint8_t *)&p_buf_size, 4); + rb.write(p_buf, p_buf_size); + ++queue_count; + return OK; +} + +bool PacketPeerUDP::is_listening() const { return _sock.is_valid() && _sock->is_open(); } IP_Address PacketPeerUDP::get_packet_address() const { - return packet_ip; } int PacketPeerUDP::get_packet_port() const { - return packet_port; } void PacketPeerUDP::set_dest_address(const IP_Address &p_address, int p_port) { - ERR_FAIL_COND_MSG(connected, "Destination address cannot be set for connected sockets"); peer_addr = p_address; peer_port = p_port; } void PacketPeerUDP::_bind_methods() { - ClassDB::bind_method(D_METHOD("listen", "port", "bind_address", "recv_buf_size"), &PacketPeerUDP::listen, DEFVAL("*"), DEFVAL(65536)); ClassDB::bind_method(D_METHOD("close"), &PacketPeerUDP::close); ClassDB::bind_method(D_METHOD("wait"), &PacketPeerUDP::wait); @@ -343,17 +350,10 @@ void PacketPeerUDP::_bind_methods() { } PacketPeerUDP::PacketPeerUDP() : - packet_port(0), - queue_count(0), - peer_port(0), - connected(false), - blocking(true), - broadcast(false), _sock(Ref<NetSocket>(NetSocket::create())) { rb.resize(16); } PacketPeerUDP::~PacketPeerUDP() { - close(); } diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index b5a9fc9ec3..4bac6994fc 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -35,6 +35,8 @@ #include "core/io/net_socket.h" #include "core/io/packet_peer.h" +class UDPServer; + class PacketPeerUDP : public PacketPeer { GDCLASS(PacketPeerUDP, PacketPeer); @@ -47,14 +49,15 @@ protected: uint8_t recv_buffer[PACKET_BUFFER_SIZE]; uint8_t packet_buffer[PACKET_BUFFER_SIZE]; IP_Address packet_ip; - int packet_port; - int queue_count; + int packet_port = 0; + int queue_count = 0; IP_Address peer_addr; - int peer_port; - bool connected; - bool blocking; - bool broadcast; + int peer_port = 0; + bool connected = false; + bool blocking = true; + bool broadcast = false; + UDPServer *udp_server = nullptr; Ref<NetSocket> _sock; static void _bind_methods(); @@ -72,7 +75,9 @@ public: Error wait(); bool is_listening() const; - Error connect_socket(Ref<NetSocket> p_sock); // Used by UDPServer + Error connect_shared_socket(Ref<NetSocket> p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *ref); // Used by UDPServer + void disconnect_shared_socket(); // Used by UDPServer + Error store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size); // Used internally and by UDPServer Error connect_to_host(const IP_Address &p_host, int p_port); bool is_connected_to_host() const; @@ -80,10 +85,10 @@ public: int get_packet_port() const; void set_dest_address(const IP_Address &p_address, int p_port); - Error put_packet(const uint8_t *p_buffer, int p_buffer_size); - Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); - int get_available_packet_count() const; - int get_max_packet_size() const; + Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; + int get_available_packet_count() const override; + int get_max_packet_size() const override; void set_broadcast_enabled(bool p_enabled); Error join_multicast_group(IP_Address p_multi_address, String p_if_name); Error leave_multicast_group(IP_Address p_multi_address, String p_if_name); diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 5c4b3379ee..a0697ca18b 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,38 +30,57 @@ #include "pck_packer.h" +#include "core/crypto/crypto_core.h" +#include "core/io/file_access_encrypted.h" #include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION #include "core/os/file_access.h" #include "core/version.h" -static uint64_t _align(uint64_t p_n, int p_alignment) { - - if (p_alignment == 0) - return p_n; - - uint64_t rest = p_n % p_alignment; - if (rest == 0) - return p_n; - else - return p_n + (p_alignment - rest); -}; - -static void _pad(FileAccess *p_file, int p_bytes) { - - for (int i = 0; i < p_bytes; i++) { +static int _get_pad(int p_alignment, int p_n) { + int rest = p_n % p_alignment; + int pad = 0; + if (rest > 0) { + pad = p_alignment - rest; + } - p_file->store_8(0); - }; -}; + return pad; +} void PCKPacker::_bind_methods() { - - ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment"), &PCKPacker::pck_start, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path"), &PCKPacker::add_file); + ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(0), DEFVAL(String()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false)); ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false)); -}; - -Error PCKPacker::pck_start(const String &p_file, int p_alignment) { +} + +Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &p_key, bool p_encrypt_directory) { + ERR_FAIL_COND_V_MSG((p_key.is_empty() || !p_key.is_valid_hex_number(false) || p_key.length() != 64), ERR_CANT_CREATE, "Invalid Encryption Key (must be 64 characters long)."); + + String _key = p_key.to_lower(); + key.resize(32); + for (int i = 0; i < 32; i++) { + int v = 0; + if (i * 2 < _key.length()) { + char32_t ct = _key[i * 2]; + if (ct >= '0' && ct <= '9') { + ct = ct - '0'; + } else if (ct >= 'a' && ct <= 'f') { + ct = 10 + ct - 'a'; + } + v |= ct << 4; + } + + if (i * 2 + 1 < _key.length()) { + char32_t ct = _key[i * 2 + 1]; + if (ct >= '0' && ct <= '9') { + ct = ct - '0'; + } else if (ct >= 'a' && ct <= 'f') { + ct = 10 + ct - 'a'; + } + v |= ct; + } + key.write[i] = v; + } + enc_dir = p_encrypt_directory; if (file != nullptr) { memdelete(file); @@ -79,28 +98,53 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment) { file->store_32(VERSION_MINOR); file->store_32(VERSION_PATCH); - for (int i = 0; i < 16; i++) { - - file->store_32(0); // reserved - }; + uint32_t pack_flags = 0; + if (enc_dir) { + pack_flags |= PACK_DIR_ENCRYPTED; + } + file->store_32(pack_flags); // flags files.clear(); + ofs = 0; return OK; -}; - -Error PCKPacker::add_file(const String &p_file, const String &p_src) { +} +Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) { FileAccess *f = FileAccess::open(p_src, FileAccess::READ); if (!f) { return ERR_FILE_CANT_OPEN; - }; + } File pf; pf.path = p_file; pf.src_path = p_src; + pf.ofs = ofs; pf.size = f->get_len(); - pf.offset_offset = 0; + + Vector<uint8_t> data = FileAccess::get_file_as_array(p_src); + { + unsigned char hash[16]; + CryptoCore::md5(data.ptr(), data.size(), hash); + pf.md5.resize(16); + for (int i = 0; i < 16; i++) { + pf.md5.write[i] = hash[i]; + } + } + pf.encrypted = p_encrypt; + + uint64_t _size = pf.size; + if (p_encrypt) { // Add encryption overhead. + if (_size % 16) { // Pad to encryption block size. + _size += 16 - (_size % 16); + } + _size += 16; // hash + _size += 8; // data size + _size += 16; // iv + } + + int pad = _get_pad(alignment, ofs + _size); + ofs = ofs + _size + pad; files.push_back(pf); @@ -108,86 +152,130 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src) { memdelete(f); return OK; -}; +} Error PCKPacker::flush(bool p_verbose) { - ERR_FAIL_COND_V_MSG(!file, ERR_INVALID_PARAMETER, "File must be opened before use."); - // write the index + int64_t file_base_ofs = file->get_position(); + file->store_64(0); // files base + for (int i = 0; i < 16; i++) { + file->store_32(0); // reserved + } + + // write the index file->store_32(files.size()); - for (int i = 0; i < files.size(); i++) { + FileAccessEncrypted *fae = nullptr; + FileAccess *fhead = file; - file->store_pascal_string(files[i].path); - files.write[i].offset_offset = file->get_position(); - file->store_64(0); // offset - file->store_64(files[i].size); // size + if (enc_dir) { + fae = memnew(FileAccessEncrypted); + ERR_FAIL_COND_V(!fae, ERR_CANT_CREATE); - // # empty md5 - file->store_32(0); - file->store_32(0); - file->store_32(0); - file->store_32(0); - }; + Error err = fae->open_and_parse(file, key, FileAccessEncrypted::MODE_WRITE_AES256, false); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - uint64_t ofs = file->get_position(); - ofs = _align(ofs, alignment); + fhead = fae; + } + + for (int i = 0; i < files.size(); i++) { + int string_len = files[i].path.utf8().length(); + int pad = _get_pad(4, string_len); + + fhead->store_32(string_len + pad); + fhead->store_buffer((const uint8_t *)files[i].path.utf8().get_data(), string_len); + for (int j = 0; j < pad; j++) { + fhead->store_8(0); + } + + fhead->store_64(files[i].ofs); + fhead->store_64(files[i].size); // pay attention here, this is where file is + fhead->store_buffer(files[i].md5.ptr(), 16); //also save md5 for file + + uint32_t flags = 0; + if (files[i].encrypted) { + flags |= PACK_FILE_ENCRYPTED; + } + fhead->store_32(flags); + } + + if (fae) { + fae->release(); + memdelete(fae); + } + + int header_padding = _get_pad(alignment, file->get_position()); + for (int i = 0; i < header_padding; i++) { + file->store_8(Math::rand() % 256); + } - _pad(file, ofs - file->get_position()); + int64_t file_base = file->get_position(); + file->seek(file_base_ofs); + file->store_64(file_base); // update files base + file->seek(file_base); const uint32_t buf_max = 65536; uint8_t *buf = memnew_arr(uint8_t, buf_max); int count = 0; for (int i = 0; i < files.size(); i++) { - FileAccess *src = FileAccess::open(files[i].src_path, FileAccess::READ); uint64_t to_write = files[i].size; - while (to_write > 0) { + fae = nullptr; + FileAccess *ftmp = file; + if (files[i].encrypted) { + fae = memnew(FileAccessEncrypted); + ERR_FAIL_COND_V(!fae, ERR_CANT_CREATE); + + Error err = fae->open_and_parse(file, key, FileAccessEncrypted::MODE_WRITE_AES256, false); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + ftmp = fae; + } + + while (to_write > 0) { int read = src->get_buffer(buf, MIN(to_write, buf_max)); - file->store_buffer(buf, read); + ftmp->store_buffer(buf, read); to_write -= read; - }; + } - uint64_t pos = file->get_position(); - file->seek(files[i].offset_offset); // go back to store the file's offset - file->store_64(ofs); - file->seek(pos); + if (fae) { + fae->release(); + memdelete(fae); + } - ofs = _align(ofs + files[i].size, alignment); - _pad(file, ofs - pos); + int pad = _get_pad(alignment, file->get_position()); + for (int j = 0; j < pad; j++) { + file->store_8(Math::rand() % 256); + } src->close(); memdelete(src); count += 1; - if (p_verbose && files.size() > 0) { + const int file_num = files.size(); + if (p_verbose && (file_num > 0)) { if (count % 100 == 0) { - printf("%i/%i (%.2f)\r", count, files.size(), float(count) / files.size() * 100); + printf("%i/%i (%.2f)\r", count, file_num, float(count) / file_num * 100); fflush(stdout); - }; - }; - }; + } + } + } - if (p_verbose) + if (p_verbose) { printf("\n"); + } file->close(); memdelete_arr(buf); return OK; -}; - -PCKPacker::PCKPacker() { - - file = nullptr; -}; +} PCKPacker::~PCKPacker() { if (file != nullptr) { memdelete(file); - }; + } file = nullptr; -}; +} diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h index 6058de8345..dec8f8748d 100644 --- a/core/io/pck_packer.h +++ b/core/io/pck_packer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,34 +31,38 @@ #ifndef PCK_PACKER_H #define PCK_PACKER_H -#include "core/reference.h" +#include "core/object/reference.h" class FileAccess; class PCKPacker : public Reference { - GDCLASS(PCKPacker, Reference); - FileAccess *file; - int alignment; + FileAccess *file = nullptr; + int alignment = 0; + uint64_t ofs = 0; + + Vector<uint8_t> key; + bool enc_dir = false; static void _bind_methods(); struct File { - String path; String src_path; - int size; - uint64_t offset_offset; + uint64_t ofs = 0; + uint64_t size = 0; + bool encrypted = false; + Vector<uint8_t> md5; }; Vector<File> files; public: - Error pck_start(const String &p_file, int p_alignment = 0); - Error add_file(const String &p_file, const String &p_src); + Error pck_start(const String &p_file, int p_alignment = 0, const String &p_key = String(), bool p_encrypt_directory = false); + Error add_file(const String &p_file, const String &p_src, bool p_encrypt = false); Error flush(bool p_verbose = false); - PCKPacker(); + PCKPacker() {} ~PCKPacker(); }; diff --git a/core/resource.cpp b/core/io/resource.cpp index 8d5c441b21..2c97e617f2 100644 --- a/core/resource.cpp +++ b/core/io/resource.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,14 +32,14 @@ #include "core/core_string_names.h" #include "core/io/resource_loader.h" +#include "core/object/script_language.h" #include "core/os/file_access.h" -#include "core/script_language.h" +#include "core/os/os.h" #include "scene/main/node.h" //only so casting works #include <stdio.h> void Resource::emit_changed() { - emit_signal(CoreStringNames::get_singleton()->changed); } @@ -47,12 +47,11 @@ void Resource::_resource_path_changed() { } void Resource::set_path(const String &p_path, bool p_take_over) { - - if (path_cache == p_path) + if (path_cache == p_path) { return; + } if (path_cache != "") { - ResourceCache::lock->write_lock(); ResourceCache::resources.erase(path_cache); ResourceCache::lock->write_unlock(); @@ -66,7 +65,6 @@ void Resource::set_path(const String &p_path, bool p_take_over) { if (has_path) { if (p_take_over) { - ResourceCache::lock->write_lock(); Resource **res = ResourceCache::resources.getptr(p_path); if (res) { @@ -84,7 +82,6 @@ void Resource::set_path(const String &p_path, bool p_take_over) { path_cache = p_path; if (path_cache != "") { - ResourceCache::lock->write_lock(); ResourceCache::resources[path_cache] = this; ResourceCache::lock->write_unlock(); @@ -95,85 +92,78 @@ void Resource::set_path(const String &p_path, bool p_take_over) { } String Resource::get_path() const { - return path_cache; } void Resource::set_subindex(int p_sub_index) { - subindex = p_sub_index; } int Resource::get_subindex() const { - return subindex; } void Resource::set_name(const String &p_name) { - name = p_name; _change_notify("resource_name"); } -String Resource::get_name() const { +String Resource::get_name() const { return name; } bool Resource::editor_can_reload_from_file() { - return true; //by default yes } void Resource::reload_from_file() { - String path = get_path(); - if (!path.is_resource_file()) + if (!path.is_resource_file()) { return; + } Ref<Resource> s = ResourceLoader::load(ResourceLoader::path_remap(path), get_class(), true); - if (!s.is_valid()) + if (!s.is_valid()) { return; + } List<PropertyInfo> pi; s->get_property_list(&pi); for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; - if (E->get().name == "resource_path") + } + if (E->get().name == "resource_path") { continue; //do not change path + } set(E->get().name, s->get(E->get().name)); } } Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource>> &remap_cache) { - List<PropertyInfo> plist; get_property_list(&plist); - Resource *r = Object::cast_to<Resource>(ClassDB::instance(get_class())); - ERR_FAIL_COND_V(!r, Ref<Resource>()); + Ref<Resource> r = Object::cast_to<Resource>(ClassDB::instance(get_class())); + ERR_FAIL_COND_V(r.is_null(), Ref<Resource>()); r->local_scene = p_for_scene; for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; + } Variant p = get(E->get().name); if (p.get_type() == Variant::OBJECT) { - RES sr = p; if (sr.is_valid()) { - if (sr->is_local_to_scene()) { if (remap_cache.has(sr)) { p = remap_cache[sr]; } else { - RES dupe = sr->duplicate_for_local_scene(p_for_scene, remap_cache); p = dupe; remap_cache[sr] = dupe; @@ -185,28 +175,23 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res r->set(E->get().name, p); } - RES res = Ref<Resource>(r); - - return res; + return r; } void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource>> &remap_cache) { - List<PropertyInfo> plist; get_property_list(&plist); local_scene = p_for_scene; for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; + } Variant p = get(E->get().name); if (p.get_type() == Variant::OBJECT) { - RES sr = p; if (sr.is_valid()) { - if (sr->is_local_to_scene()) { if (!remap_cache.has(sr)) { sr->configure_for_local_scene(p_for_scene, remap_cache); @@ -219,65 +204,55 @@ void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, R } Ref<Resource> Resource::duplicate(bool p_subresources) const { - List<PropertyInfo> plist; get_property_list(&plist); - Resource *r = (Resource *)ClassDB::instance(get_class()); - ERR_FAIL_COND_V(!r, Ref<Resource>()); + Ref<Resource> r = (Resource *)ClassDB::instance(get_class()); + ERR_FAIL_COND_V(r.is_null(), Ref<Resource>()); for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; + } Variant p = get(E->get().name); if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) { r->set(E->get().name, p.duplicate(p_subresources)); } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) { - RES sr = p; if (sr.is_valid()) { r->set(E->get().name, sr->duplicate(p_subresources)); } } else { - r->set(E->get().name, p); } } - return Ref<Resource>(r); + return r; } void Resource::_set_path(const String &p_path) { - set_path(p_path, false); } void Resource::_take_over_path(const String &p_path) { - set_path(p_path, true); } RID Resource::get_rid() const { - return RID(); } void Resource::register_owner(Object *p_owner) { - owners.insert(p_owner->get_instance_id()); } void Resource::unregister_owner(Object *p_owner) { - owners.erase(p_owner->get_instance_id()); } void Resource::notify_change_to_owners() { - for (Set<ObjectID>::Element *E = owners.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->get()); ERR_CONTINUE_MSG(!obj, "Object was deleted, while still owning a resource."); //wtf //TODO store string @@ -288,14 +263,12 @@ void Resource::notify_change_to_owners() { #ifdef TOOLS_ENABLED uint32_t Resource::hash_edited_version() const { - uint32_t hash = hash_djb2_one_32(get_edited_version()); List<PropertyInfo> plist; get_property_list(&plist); for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE && E->get().type == Variant::OBJECT && E->get().hint == PROPERTY_HINT_RESOURCE_TYPE) { RES res = get(E->get().name); if (res.is_valid()) { @@ -310,19 +283,17 @@ uint32_t Resource::hash_edited_version() const { #endif void Resource::set_local_to_scene(bool p_enable) { - local_to_scene = p_enable; } bool Resource::is_local_to_scene() const { - return local_to_scene; } Node *Resource::get_local_scene() const { - - if (local_scene) + if (local_scene) { return local_scene; + } if (_get_local_scene_func) { return _get_local_scene_func(); @@ -332,17 +303,17 @@ Node *Resource::get_local_scene() const { } void Resource::setup_local_to_scene() { - - if (get_script_instance()) + if (get_script_instance()) { get_script_instance()->call("_setup_local_to_scene"); + } } Node *(*Resource::_get_local_scene_func)() = nullptr; void Resource::set_as_translation_remapped(bool p_remapped) { - - if (remapped_list.in_list() == p_remapped) + if (remapped_list.in_list() == p_remapped) { return; + } if (ResourceCache::lock) { ResourceCache::lock->write_lock(); @@ -360,7 +331,6 @@ void Resource::set_as_translation_remapped(bool p_remapped) { } bool Resource::is_translation_remapped() const { - return remapped_list.in_list(); } @@ -406,7 +376,6 @@ int Resource::get_id_for_path(const String &p_path) const { #endif void Resource::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_path", "path"), &Resource::_set_path); ClassDB::bind_method(D_METHOD("take_over_path", "path"), &Resource::_take_over_path); ClassDB::bind_method(D_METHOD("get_path"), &Resource::get_path); @@ -417,6 +386,7 @@ void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("is_local_to_scene"), &Resource::is_local_to_scene); ClassDB::bind_method(D_METHOD("get_local_scene"), &Resource::get_local_scene); ClassDB::bind_method(D_METHOD("setup_local_to_scene"), &Resource::setup_local_to_scene); + ClassDB::bind_method(D_METHOD("emit_changed"), &Resource::emit_changed); ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false)); ADD_SIGNAL(MethodInfo("changed")); @@ -429,20 +399,9 @@ void Resource::_bind_methods() { } Resource::Resource() : - remapped_list(this) { - -#ifdef TOOLS_ENABLED - last_modified_time = 0; - import_last_modified_time = 0; -#endif - - subindex = 0; - local_to_scene = false; - local_scene = nullptr; -} + remapped_list(this) {} Resource::~Resource() { - if (path_cache != "") { ResourceCache::lock->write_lock(); ResourceCache::resources.erase(path_cache); @@ -464,7 +423,6 @@ RWLock *ResourceCache::path_cache_lock = nullptr; #endif void ResourceCache::setup() { - lock = RWLock::create(); #ifdef TOOLS_ENABLED path_cache_lock = RWLock::create(); @@ -472,8 +430,16 @@ void ResourceCache::setup() { } void ResourceCache::clear() { - if (resources.size()) - ERR_PRINT("Resources Still in use at Exit!"); + if (resources.size()) { + ERR_PRINT("Resources still in use at exit (run with --verbose for details)."); + if (OS::get_singleton()->is_stdout_verbose()) { + const String *K = nullptr; + while ((K = resources.next(K))) { + Resource *r = resources[*K]; + print_line(vformat("Resource still in use: %s (%s)", *K, r->get_class())); + } + } + } resources.clear(); memdelete(lock); @@ -483,25 +449,17 @@ void ResourceCache::clear() { } void ResourceCache::reload_externals() { - - /* - const String *K=nullptr; - while ((K=resources.next(K))) { - resources[*K]->reload_external_data(); - } - */ } bool ResourceCache::has(const String &p_path) { - lock->read_lock(); bool b = resources.has(p_path); lock->read_unlock(); return b; } -Resource *ResourceCache::get(const String &p_path) { +Resource *ResourceCache::get(const String &p_path) { lock->read_lock(); Resource **res = resources.getptr(p_path); @@ -516,11 +474,9 @@ Resource *ResourceCache::get(const String &p_path) { } void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) { - lock->read_lock(); const String *K = nullptr; while ((K = resources.next(K))) { - Resource *r = resources[*K]; p_resources->push_back(Ref<Resource>(r)); } @@ -528,7 +484,6 @@ void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) { } int ResourceCache::get_cached_resource_count() { - lock->read_lock(); int rc = resources.size(); lock->read_unlock(); @@ -550,7 +505,6 @@ void ResourceCache::dump(const char *p_file, bool p_short) { const String *K = nullptr; while ((K = resources.next(K))) { - Resource *r = resources[*K]; if (!type_count.has(r->get_class())) { @@ -560,15 +514,16 @@ void ResourceCache::dump(const char *p_file, bool p_short) { type_count[r->get_class()]++; if (!p_short) { - if (f) + if (f) { f->store_line(r->get_class() + ": " + r->get_path()); + } } } for (Map<String, int>::Element *E = type_count.front(); E; E = E->next()) { - - if (f) + if (f) { f->store_line(E->key() + " count: " + itos(E->get())); + } } if (f) { f->close(); @@ -576,6 +531,5 @@ void ResourceCache::dump(const char *p_file, bool p_short) { } lock->read_unlock(); - #endif } diff --git a/core/resource.h b/core/io/resource.h index 3b7812c870..eda18a8538 100644 --- a/core/resource.h +++ b/core/io/resource.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,25 +31,27 @@ #ifndef RESOURCE_H #define RESOURCE_H -#include "core/class_db.h" -#include "core/object.h" -#include "core/reference.h" -#include "core/safe_refcount.h" -#include "core/self_list.h" +#include "core/object/class_db.h" +#include "core/object/reference.h" +#include "core/templates/safe_refcount.h" +#include "core/templates/self_list.h" #define RES_BASE_EXTENSION(m_ext) \ public: \ static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension(m_ext, get_class_static()); } \ - virtual String get_base_extension() const { return m_ext; } \ + virtual String get_base_extension() const override { return m_ext; } \ \ private: class Resource : public Reference { - GDCLASS(Resource, Reference); OBJ_CATEGORY("Resources"); - RES_BASE_EXTENSION("res"); +public: + static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension("res", get_class_static()); } + virtual String get_base_extension() const { return "res"; } + +private: Set<ObjectID> owners; friend class ResBase; @@ -57,19 +59,19 @@ class Resource : public Reference { String name; String path_cache; - int subindex; + int subindex = 0; virtual bool _use_builtin_script() const { return true; } #ifdef TOOLS_ENABLED - uint64_t last_modified_time; - uint64_t import_last_modified_time; + uint64_t last_modified_time = 0; + uint64_t import_last_modified_time = 0; String import_path; #endif - bool local_to_scene; + bool local_to_scene = false; friend class SceneState; - Node *local_scene; + Node *local_scene = nullptr; SelfList<Resource> remapped_list; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 8c7559479b..ae4643a19f 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,18 +30,17 @@ #include "resource_format_binary.h" -#include "core/image.h" +#include "core/config/project_settings.h" #include "core/io/file_access_compressed.h" +#include "core/io/image.h" #include "core/io/marshalls.h" #include "core/os/dir_access.h" -#include "core/project_settings.h" #include "core/version.h" //#define print_bl(m_what) print_line(m_what) #define print_bl(m_what) (void)(m_what) enum { - //numbering must be different from variant, in case new variant types are added (variant must be always contiguous for jumptable optimization) VARIANT_NIL = 1, VARIANT_BOOL = 2, @@ -90,28 +89,27 @@ enum { FORMAT_VERSION = 3, FORMAT_VERSION_CAN_RENAME_DEPS = 1, FORMAT_VERSION_NO_NODEPATH_PROPERTY = 3, - }; void ResourceLoaderBinary::_advance_padding(uint32_t p_len) { - uint32_t extra = 4 - (p_len % 4); if (extra < 4) { - for (uint32_t i = 0; i < extra; i++) + for (uint32_t i = 0; i < extra; i++) { f->get_8(); //pad to 32 + } } } StringName ResourceLoaderBinary::_get_string() { - uint32_t id = f->get_32(); if (id & 0x80000000) { uint32_t len = id & 0x7FFFFFFF; if ((int)len > str_buf.size()) { str_buf.resize(len); } - if (len == 0) + if (len == 0) { return StringName(); + } f->get_buffer((uint8_t *)&str_buf[0], len); String s; s.parse_utf8(&str_buf[0]); @@ -122,42 +120,32 @@ StringName ResourceLoaderBinary::_get_string() { } Error ResourceLoaderBinary::parse_variant(Variant &r_v) { - uint32_t type = f->get_32(); print_bl("find property of type: " + itos(type)); switch (type) { - case VARIANT_NIL: { - r_v = Variant(); } break; case VARIANT_BOOL: { - r_v = bool(f->get_32()); } break; case VARIANT_INT: { - r_v = int(f->get_32()); } break; case VARIANT_INT64: { - r_v = int64_t(f->get_64()); } break; case VARIANT_FLOAT: { - r_v = f->get_real(); } break; case VARIANT_DOUBLE: { - r_v = f->get_double(); } break; case VARIANT_STRING: { - r_v = get_unicode_string(); } break; case VARIANT_VECTOR2: { - Vector2 v; v.x = f->get_real(); v.y = f->get_real(); @@ -165,7 +153,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_VECTOR2I: { - Vector2i v; v.x = f->get_32(); v.y = f->get_32(); @@ -173,7 +160,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_RECT2: { - Rect2 v; v.position.x = f->get_real(); v.position.y = f->get_real(); @@ -183,7 +169,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_RECT2I: { - Rect2i v; v.position.x = f->get_32(); v.position.y = f->get_32(); @@ -193,7 +178,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_VECTOR3: { - Vector3 v; v.x = f->get_real(); v.y = f->get_real(); @@ -201,7 +185,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; case VARIANT_VECTOR3I: { - Vector3i v; v.x = f->get_32(); v.y = f->get_32(); @@ -209,7 +192,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; case VARIANT_PLANE: { - Plane v; v.normal.x = f->get_real(); v.normal.y = f->get_real(); @@ -227,7 +209,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_AABB: { - AABB v; v.position.x = f->get_real(); v.position.y = f->get_real(); @@ -239,7 +220,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_MATRIX32: { - Transform2D v; v.elements[0].x = f->get_real(); v.elements[0].y = f->get_real(); @@ -251,7 +231,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_MATRIX3: { - Basis v; v.elements[0].x = f->get_real(); v.elements[0].y = f->get_real(); @@ -266,7 +245,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_TRANSFORM: { - Transform v; v.basis.elements[0].x = f->get_real(); v.basis.elements[0].y = f->get_real(); @@ -283,7 +261,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; case VARIANT_COLOR: { - Color v; v.r = f->get_real(); v.g = f->get_real(); @@ -293,12 +270,10 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_STRING_NAME: { - r_v = StringName(get_unicode_string()); } break; case VARIANT_NODE_PATH: { - Vector<StringName> names; Vector<StringName> subnames; bool absolute; @@ -311,10 +286,12 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { subname_count += 1; // has a property field, so we should count it as well } - for (int i = 0; i < name_count; i++) + for (int i = 0; i < name_count; i++) { names.push_back(_get_string()); - for (uint32_t i = 0; i < subname_count; i++) + } + for (uint32_t i = 0; i < subname_count; i++) { subnames.push_back(_get_string()); + } NodePath np = NodePath(names, subnames, absolute); @@ -322,25 +299,26 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_RID: { - r_v = f->get_32(); } break; case VARIANT_OBJECT: { - uint32_t objtype = f->get_32(); switch (objtype) { - case OBJECT_EMPTY: { //do none } break; case OBJECT_INTERNAL_RESOURCE: { uint32_t index = f->get_32(); + String path = res_path + "::" + itos(index); + if (use_nocache) { - r_v = internal_resources[index].cache; + if (!internal_index_cache.has(path)) { + WARN_PRINT(String("Couldn't load resource (no cache): " + path).utf8().get_data()); + } + r_v = internal_index_cache[path]; } else { - String path = res_path + "::" + itos(index); RES res = ResourceLoader::load(path); if (res.is_null()) { WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data()); @@ -380,7 +358,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { WARN_PRINT("Broken external resource! (index out of size)"); r_v = Variant(); } else { - if (external_resources[erindex].cache.is_null()) { //cache not here yet, wait for it? if (use_sub_threads) { @@ -389,10 +366,8 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { if (err != OK || external_resources[erindex].cache.is_null()) { if (!ResourceLoader::get_abort_on_missing_resources()) { - ResourceLoader::notify_dependency_error(local_path, external_resources[erindex].path, external_resources[erindex].type); } else { - error = ERR_FILE_MISSING_DEPENDENCIES; ERR_FAIL_V_MSG(error, "Can't load dependency: " + external_resources[erindex].path + "."); } @@ -405,23 +380,19 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; default: { - ERR_FAIL_V(ERR_FILE_CORRUPT); } break; } } break; case VARIANT_CALLABLE: { - r_v = Callable(); } break; case VARIANT_SIGNAL: { - r_v = Signal(); } break; case VARIANT_DICTIONARY: { - uint32_t len = f->get_32(); Dictionary d; //last bit means shared len &= 0x7FFFFFFF; @@ -437,7 +408,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = d; } break; case VARIANT_ARRAY: { - uint32_t len = f->get_32(); Array a; //last bit means shared len &= 0x7FFFFFFF; @@ -452,7 +422,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_RAW_ARRAY: { - uint32_t len = f->get_32(); Vector<uint8_t> array; @@ -465,7 +434,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_INT32_ARRAY: { - uint32_t len = f->get_32(); Vector<int32_t> array; @@ -476,7 +444,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len; i++) { - ptr[i] = BSWAP32(ptr[i]); } } @@ -486,7 +453,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; case VARIANT_INT64_ARRAY: { - uint32_t len = f->get_32(); Vector<int64_t> array; @@ -497,7 +463,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { { uint64_t *ptr = (uint64_t *)w.ptr(); for (int i = 0; i < len; i++) { - ptr[i] = BSWAP64(ptr[i]); } } @@ -507,7 +472,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; case VARIANT_FLOAT32_ARRAY: { - uint32_t len = f->get_32(); Vector<float> array; @@ -518,7 +482,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len; i++) { - ptr[i] = BSWAP32(ptr[i]); } } @@ -528,7 +491,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; case VARIANT_FLOAT64_ARRAY: { - uint32_t len = f->get_32(); Vector<double> array; @@ -539,7 +501,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { { uint64_t *ptr = (uint64_t *)w.ptr(); for (int i = 0; i < len; i++) { - ptr[i] = BSWAP64(ptr[i]); } } @@ -549,19 +510,18 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; case VARIANT_STRING_ARRAY: { - uint32_t len = f->get_32(); Vector<String> array; array.resize(len); String *w = array.ptrw(); - for (uint32_t i = 0; i < len; i++) + for (uint32_t i = 0; i < len; i++) { w[i] = get_unicode_string(); + } r_v = array; } break; case VARIANT_VECTOR2_ARRAY: { - uint32_t len = f->get_32(); Vector<Vector2> array; @@ -573,7 +533,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len * 2; i++) { - ptr[i] = BSWAP32(ptr[i]); } } @@ -588,7 +547,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_VECTOR3_ARRAY: { - uint32_t len = f->get_32(); Vector<Vector3> array; @@ -600,7 +558,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len * 3; i++) { - ptr[i] = BSWAP32(ptr[i]); } } @@ -615,7 +572,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_COLOR_ARRAY: { - uint32_t len = f->get_32(); Vector<Color> array; @@ -627,7 +583,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len * 4; i++) { - ptr[i] = BSWAP32(ptr[i]); } } @@ -649,23 +604,21 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } void ResourceLoaderBinary::set_local_path(const String &p_local_path) { - res_path = p_local_path; } Ref<Resource> ResourceLoaderBinary::get_resource() { - return resource; } -Error ResourceLoaderBinary::load() { - if (error != OK) +Error ResourceLoaderBinary::load() { + if (error != OK) { return error; + } int stage = 0; for (int i = 0; i < external_resources.size(); i++) { - String path = external_resources[i].path; if (remaps.has(path)) { @@ -684,10 +637,8 @@ Error ResourceLoaderBinary::load() { if (external_resources[i].cache.is_null()) { if (!ResourceLoader::get_abort_on_missing_resources()) { - ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type); } else { - error = ERR_FILE_MISSING_DEPENDENCIES; ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + "."); } @@ -697,10 +648,8 @@ Error ResourceLoaderBinary::load() { Error err = ResourceLoader::load_threaded_request(path, external_resources[i].type, use_sub_threads, local_path); if (err != OK) { if (!ResourceLoader::get_abort_on_missing_resources()) { - ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type); } else { - error = ERR_FILE_MISSING_DEPENDENCIES; ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + "."); } @@ -711,7 +660,6 @@ Error ResourceLoaderBinary::load() { } for (int i = 0; i < internal_resources.size(); i++) { - bool main = i == (internal_resources.size() - 1); //maybe it is loaded already @@ -719,15 +667,15 @@ Error ResourceLoaderBinary::load() { int subindex = 0; if (!main) { + path = internal_resources[i].path; - if (!use_nocache) { - path = internal_resources[i].path; - if (path.begins_with("local://")) { - path = path.replace_first("local://", ""); - subindex = path.to_int(); - path = res_path + "::" + path; - } + if (path.begins_with("local://")) { + path = path.replace_first("local://", ""); + subindex = path.to_int(); + path = res_path + "::" + path; + } + if (!use_nocache) { if (ResourceCache::has(path)) { //already loaded, don't do anything stage++; @@ -736,9 +684,9 @@ Error ResourceLoaderBinary::load() { } } } else { - - if (!use_nocache && !ResourceCache::has(res_path)) + if (!use_nocache && !ResourceCache::has(res_path)) { path = res_path; + } } uint64_t offset = internal_resources[i].offset; @@ -769,7 +717,7 @@ Error ResourceLoaderBinary::load() { r->set_subindex(subindex); if (!main) { - internal_resources.write[i].cache = res; + internal_index_cache[path] = res; } int pc = f->get_32(); @@ -777,7 +725,6 @@ Error ResourceLoaderBinary::load() { //set properties for (int j = 0; j < pc; j++) { - StringName name = _get_string(); if (name == StringName()) { @@ -788,8 +735,9 @@ Error ResourceLoaderBinary::load() { Variant value; error = parse_variant(value); - if (error) + if (error) { return error; + } res->set(name, value); } @@ -805,7 +753,6 @@ Error ResourceLoaderBinary::load() { resource_cache.push_back(res); if (main) { - f->close(); resource = res; resource->set_as_translation_remapped(translation_remapped); @@ -818,19 +765,16 @@ Error ResourceLoaderBinary::load() { } void ResourceLoaderBinary::set_translation_remapped(bool p_remapped) { - translation_remapped = p_remapped; } static void save_ustring(FileAccess *f, const String &p_string) { - CharString utf8 = p_string.utf8(); f->store_32(utf8.length() + 1); f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); } static String get_ustring(FileAccess *f) { - int len = f->get_32(); Vector<char> str_buf; str_buf.resize(len); @@ -841,13 +785,13 @@ static String get_ustring(FileAccess *f) { } String ResourceLoaderBinary::get_unicode_string() { - int len = f->get_32(); if (len > str_buf.size()) { str_buf.resize(len); } - if (len == 0) + if (len == 0) { return String(); + } f->get_buffer((uint8_t *)&str_buf[0], len); String s; s.parse_utf8(&str_buf[0]); @@ -855,13 +799,12 @@ String ResourceLoaderBinary::get_unicode_string() { } void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) { - open(p_f); - if (error) + if (error) { return; + } for (int i = 0; i < external_resources.size(); i++) { - String dep = external_resources[i].path; if (p_add_types && external_resources[i].type != String()) { @@ -873,7 +816,6 @@ void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dep } void ResourceLoaderBinary::open(FileAccess *p_f) { - error = OK; f = p_f; @@ -918,9 +860,9 @@ void ResourceLoaderBinary::open(FileAccess *p_f) { print_bl("format: " + itos(ver_format)); if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - f->close(); - ERR_FAIL_MSG("File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + "."); + ERR_FAIL_MSG(vformat("File '%s' can't be loaded, as it uses a format version (%d) or engine version (%d.%d) which are not supported by your engine version (%s).", + local_path, ver_format, ver_major, ver_minor, VERSION_BRANCH)); } type = get_unicode_string(); @@ -928,13 +870,13 @@ void ResourceLoaderBinary::open(FileAccess *p_f) { print_bl("type: " + type); importmd_ofs = f->get_64(); - for (int i = 0; i < 14; i++) + for (int i = 0; i < 14; i++) { f->get_32(); //skip a few reserved fields + } uint32_t string_table_size = f->get_32(); string_map.resize(string_table_size); for (uint32_t i = 0; i < string_table_size; i++) { - StringName s = get_unicode_string(); string_map.write[i] = s; } @@ -943,7 +885,6 @@ void ResourceLoaderBinary::open(FileAccess *p_f) { uint32_t ext_resources_size = f->get_32(); for (uint32_t i = 0; i < ext_resources_size; i++) { - ExtResource er; er.type = get_unicode_string(); @@ -956,7 +897,6 @@ void ResourceLoaderBinary::open(FileAccess *p_f) { uint32_t int_resources_size = f->get_32(); for (uint32_t i = 0; i < int_resources_size; i++) { - IntResource ir; ir.path = get_unicode_string(); ir.offset = f->get_64(); @@ -966,7 +906,6 @@ void ResourceLoaderBinary::open(FileAccess *p_f) { print_bl("int resources: " + itos(int_resources_size)); if (f->eof_reached()) { - error = ERR_FILE_CORRUPT; f->close(); ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + "."); @@ -974,7 +913,6 @@ void ResourceLoaderBinary::open(FileAccess *p_f) { } String ResourceLoaderBinary::recognize(FileAccess *p_f) { - error = OK; f = p_f; @@ -1008,7 +946,6 @@ String ResourceLoaderBinary::recognize(FileAccess *p_f) { uint32_t ver_format = f->get_32(); if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - f->close(); return ""; } @@ -1018,28 +955,16 @@ String ResourceLoaderBinary::recognize(FileAccess *p_f) { return type; } -ResourceLoaderBinary::ResourceLoaderBinary() : - translation_remapped(false), - ver_format(0), - f(nullptr), - importmd_ofs(0), - error(OK) { - - use_nocache = false; - progress = nullptr; - use_sub_threads = false; -} - ResourceLoaderBinary::~ResourceLoaderBinary() { - - if (f) + if (f) { memdelete(f); + } } RES ResourceFormatLoaderBinary::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) { - - if (r_error) + if (r_error) { *r_error = ERR_FILE_CANT_OPEN; + } Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -1069,7 +994,6 @@ RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_origi } void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - if (p_type == "") { get_recognized_extensions(p_extensions); return; @@ -1085,8 +1009,8 @@ void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String p_extensions->push_back(ext); } } -void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_extensions) const { List<String> extensions; ClassDB::get_resource_base_extensions(&extensions); extensions.sort(); @@ -1098,12 +1022,10 @@ void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_exten } bool ResourceFormatLoaderBinary::handles_type(const String &p_type) const { - return true; //handles all } void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_MSG(!f, "Cannot open file '" + p_path + "'."); @@ -1115,7 +1037,6 @@ void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<Str } Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - //Error error=OK; FileAccess *f = FileAccess::open(p_path, FileAccess::READ); @@ -1181,7 +1102,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons uint32_t ver_format = f->get_32(); if (ver_format < FORMAT_VERSION_CAN_RENAME_DEPS) { - memdelete(f); memdelete(fw); DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -1213,10 +1133,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons } if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - memdelete(f); memdelete(fw); - ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + "."); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, + vformat("File '%s' can't be loaded, as it uses a format version (%d) or engine version (%d.%d) which are not supported by your engine version (%s).", + local_path, ver_format, ver_major, ver_minor, VERSION_BRANCH)); } // Since we're not actually converting the file contents, leave the version @@ -1242,7 +1163,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons fw->store_32(string_table_size); for (uint32_t i = 0; i < string_table_size; i++) { - String s = get_ustring(f); save_ustring(fw, s); } @@ -1251,7 +1171,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons uint32_t ext_resources_size = f->get_32(); fw->store_32(ext_resources_size); for (uint32_t i = 0; i < ext_resources_size; i++) { - String type = get_ustring(f); String path = get_ustring(f); @@ -1282,7 +1201,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons fw->store_32(int_resources_size); for (uint32_t i = 0; i < int_resources_size; i++) { - String path = get_ustring(f); uint64_t offset = f->get_64(); save_ustring(fw, path); @@ -1316,7 +1234,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons } String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); if (!f) { return ""; //could not rwead @@ -1335,36 +1252,30 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const /////////////////////////////////////////////////////////// void ResourceFormatSaverBinaryInstance::_pad_buffer(FileAccess *f, int p_bytes) { - int extra = 4 - (p_bytes % 4); if (extra < 4) { - for (int i = 0; i < extra; i++) + for (int i = 0; i < extra; i++) { f->store_8(0); //pad to 32 + } } } void ResourceFormatSaverBinaryInstance::_write_variant(const Variant &p_property, const PropertyInfo &p_hint) { - write_variant(f, p_property, resource_set, external_resources, string_map, p_hint); } void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Set<RES> &resource_set, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) { - switch (p_property.get_type()) { - case Variant::NIL: { - f->store_32(VARIANT_NIL); // don't store anything } break; case Variant::BOOL: { - f->store_32(VARIANT_BOOL); bool val = p_property; f->store_32(val); } break; case Variant::INT: { - int64_t val = p_property; if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) { f->store_32(VARIANT_INT64); @@ -1377,28 +1288,24 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::FLOAT: { - double d = p_property; float fl = d; if (double(fl) != d) { f->store_32(VARIANT_DOUBLE); f->store_double(d); } else { - f->store_32(VARIANT_FLOAT); f->store_real(fl); } } break; case Variant::STRING: { - f->store_32(VARIANT_STRING); String val = p_property; save_unicode_string(f, val); } break; case Variant::VECTOR2: { - f->store_32(VARIANT_VECTOR2); Vector2 val = p_property; f->store_real(val.x); @@ -1406,7 +1313,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::VECTOR2I: { - f->store_32(VARIANT_VECTOR2I); Vector2i val = p_property; f->store_32(val.x); @@ -1414,7 +1320,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::RECT2: { - f->store_32(VARIANT_RECT2); Rect2 val = p_property; f->store_real(val.position.x); @@ -1424,7 +1329,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::RECT2I: { - f->store_32(VARIANT_RECT2I); Rect2i val = p_property; f->store_32(val.position.x); @@ -1434,7 +1338,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::VECTOR3: { - f->store_32(VARIANT_VECTOR3); Vector3 val = p_property; f->store_real(val.x); @@ -1443,7 +1346,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::VECTOR3I: { - f->store_32(VARIANT_VECTOR3I); Vector3i val = p_property; f->store_32(val.x); @@ -1452,7 +1354,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PLANE: { - f->store_32(VARIANT_PLANE); Plane val = p_property; f->store_real(val.normal.x); @@ -1462,7 +1363,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::QUAT: { - f->store_32(VARIANT_QUAT); Quat val = p_property; f->store_real(val.x); @@ -1472,7 +1372,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::AABB: { - f->store_32(VARIANT_AABB); AABB val = p_property; f->store_real(val.position.x); @@ -1484,7 +1383,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::TRANSFORM2D: { - f->store_32(VARIANT_MATRIX32); Transform2D val = p_property; f->store_real(val.elements[0].x); @@ -1496,7 +1394,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::BASIS: { - f->store_32(VARIANT_MATRIX3); Basis val = p_property; f->store_real(val.elements[0].x); @@ -1511,7 +1408,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::TRANSFORM: { - f->store_32(VARIANT_TRANSFORM); Transform val = p_property; f->store_real(val.basis.elements[0].x); @@ -1529,7 +1425,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::COLOR: { - f->store_32(VARIANT_COLOR); Color val = p_property; f->store_real(val.r); @@ -1539,7 +1434,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::STRING_NAME: { - f->store_32(VARIANT_STRING_NAME); String val = p_property; save_unicode_string(f, val); @@ -1551,8 +1445,9 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia NodePath np = p_property; f->store_16(np.get_name_count()); uint16_t snc = np.get_subname_count(); - if (np.is_absolute()) + if (np.is_absolute()) { snc |= 0x8000; + } f->store_16(snc); for (int i = 0; i < np.get_name_count(); i++) { if (string_map.has(np.get_name(i))) { @@ -1570,15 +1465,13 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } } break; - case Variant::_RID: { - + case Variant::RID: { f->store_32(VARIANT_RID); WARN_PRINT("Can't save RIDs."); RID val = p_property; f->store_32(val.get_id()); } break; case Variant::OBJECT: { - f->store_32(VARIANT_OBJECT); RES res = p_property; if (res.is_null()) { @@ -1590,7 +1483,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX); f->store_32(external_resources[res]); } else { - if (!resource_set.has(res)) { f->store_32(OBJECT_EMPTY); ERR_FAIL_MSG("Resource was not pre cached for the resource section, most likely due to circular reference."); @@ -1603,18 +1495,15 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::CALLABLE: { - f->store_32(VARIANT_CALLABLE); WARN_PRINT("Can't save Callables."); } break; case Variant::SIGNAL: { - f->store_32(VARIANT_SIGNAL); WARN_PRINT("Can't save Signals."); } break; case Variant::DICTIONARY: { - f->store_32(VARIANT_DICTIONARY); Dictionary d = p_property; f->store_32(uint32_t(d.size())); @@ -1623,7 +1512,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - /* if (!_check_type(dict[E->get()])) continue; @@ -1635,18 +1523,15 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::ARRAY: { - f->store_32(VARIANT_ARRAY); Array a = p_property; f->store_32(uint32_t(a.size())); for (int i = 0; i < a.size(); i++) { - write_variant(f, a[i], resource_set, external_resources, string_map); } } break; case Variant::PACKED_BYTE_ARRAY: { - f->store_32(VARIANT_RAW_ARRAY); Vector<uint8_t> arr = p_property; int len = arr.size(); @@ -1657,29 +1542,28 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_INT32_ARRAY: { - f->store_32(VARIANT_INT32_ARRAY); Vector<int32_t> arr = p_property; int len = arr.size(); f->store_32(len); const int32_t *r = arr.ptr(); - for (int i = 0; i < len; i++) + for (int i = 0; i < len; i++) { f->store_32(r[i]); + } } break; case Variant::PACKED_INT64_ARRAY: { - f->store_32(VARIANT_INT64_ARRAY); Vector<int64_t> arr = p_property; int len = arr.size(); f->store_32(len); const int64_t *r = arr.ptr(); - for (int i = 0; i < len; i++) + for (int i = 0; i < len; i++) { f->store_64(r[i]); + } } break; case Variant::PACKED_FLOAT32_ARRAY: { - f->store_32(VARIANT_FLOAT32_ARRAY); Vector<float> arr = p_property; int len = arr.size(); @@ -1691,7 +1575,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_FLOAT64_ARRAY: { - f->store_32(VARIANT_FLOAT64_ARRAY); Vector<double> arr = p_property; int len = arr.size(); @@ -1703,7 +1586,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_STRING_ARRAY: { - f->store_32(VARIANT_STRING_ARRAY); Vector<String> arr = p_property; int len = arr.size(); @@ -1715,7 +1597,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_VECTOR3_ARRAY: { - f->store_32(VARIANT_VECTOR3_ARRAY); Vector<Vector3> arr = p_property; int len = arr.size(); @@ -1729,7 +1610,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_VECTOR2_ARRAY: { - f->store_32(VARIANT_VECTOR2_ARRAY); Vector<Vector2> arr = p_property; int len = arr.size(); @@ -1742,7 +1622,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_COLOR_ARRAY: { - f->store_32(VARIANT_COLOR_ARRAY); Vector<Color> arr = p_property; int len = arr.size(); @@ -1757,21 +1636,19 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; default: { - ERR_FAIL_MSG("Invalid variant."); } } } void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant, bool p_main) { - switch (p_variant.get_type()) { case Variant::OBJECT: { - RES res = p_variant; - if (res.is_null() || external_resources.has(res)) + if (res.is_null() || external_resources.has(res)) { return; + } if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) { if (res->get_path() == path) { @@ -1783,17 +1660,16 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant return; } - if (resource_set.has(res)) + if (resource_set.has(res)) { return; + } List<PropertyInfo> property_list; res->get_property_list(&property_list); for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE) { - Variant value = res->get(E->get().name); if (E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { RES sres = value; @@ -1817,11 +1693,9 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant } break; case Variant::ARRAY: { - Array varray = p_variant; int len = varray.size(); for (int i = 0; i < len; i++) { - const Variant &v = varray.get(i); _find_resources(v); } @@ -1829,12 +1703,10 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant } break; case Variant::DICTIONARY: { - Dictionary d = p_variant; List<Variant> keys; d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _find_resources(E->get()); Variant v = d[E->get()]; _find_resources(v); @@ -1843,10 +1715,12 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant case Variant::NODE_PATH: { //take the chance and save node path strings NodePath np = p_variant; - for (int i = 0; i < np.get_name_count(); i++) + for (int i = 0; i < np.get_name_count(); i++) { get_string_index(np.get_name(i)); - for (int i = 0; i < np.get_subname_count(); i++) + } + for (int i = 0; i < np.get_subname_count(); i++) { get_string_index(np.get_subname(i)); + } } break; default: { @@ -1855,7 +1729,6 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant } void ResourceFormatSaverBinaryInstance::save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len) { - CharString utf8 = p_string.utf8(); if (p_bit_on_len) { f->store_32((utf8.length() + 1) | 0x80000000); @@ -1866,10 +1739,10 @@ void ResourceFormatSaverBinaryInstance::save_unicode_string(FileAccess *f, const } int ResourceFormatSaverBinaryInstance::get_string_index(const String &p_string) { - StringName s = p_string; - if (string_map.has(s)) + if (string_map.has(s)) { return string_map[s]; + } string_map[s] = strings.size(); strings.push_back(s); @@ -1877,15 +1750,15 @@ int ResourceFormatSaverBinaryInstance::get_string_index(const String &p_string) } Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - Error err; if (p_flags & ResourceSaver::FLAG_COMPRESS) { FileAccessCompressed *fac = memnew(FileAccessCompressed); fac->configure("RSCC"); f = fac; err = fac->_open(p_path, FileAccess::WRITE); - if (err) + if (err) { memdelete(f); + } } else { f = FileAccess::open(p_path, FileAccess::WRITE, &err); @@ -1899,8 +1772,9 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p big_endian = p_flags & ResourceSaver::FLAG_SAVE_BIG_ENDIAN; takeover_paths = p_flags & ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS; - if (!p_path.begins_with("res://")) + if (!p_path.begins_with("res://")) { takeover_paths = false; + } local_path = p_path.get_base_dir(); path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1916,8 +1790,9 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p if (big_endian) { f->store_32(1); f->set_endian_swap(true); - } else + } else { f->store_32(0); + } f->store_32(0); //64 bits file, false for now f->store_32(VERSION_MAJOR); @@ -1932,15 +1807,14 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p save_unicode_string(f, p_resource->get_class()); f->store_64(0); //offset to import metadata - for (int i = 0; i < 14; i++) + for (int i = 0; i < 14; i++) { f->store_32(0); // reserved + } List<ResourceData> resources; { - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - ResourceData &rd = resources.push_back(ResourceData())->get(); rd.type = E->get()->get_class(); @@ -1948,9 +1822,9 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p E->get()->get_property_list(&property_list); for (List<PropertyInfo>::Element *F = property_list.front(); F; F = F->next()) { - - if (skip_editor && F->get().name.begins_with("__editor")) + if (skip_editor && F->get().name.begins_with("__editor")) { continue; + } if ((F->get().usage & PROPERTY_USAGE_STORAGE)) { Property p; p.name_idx = get_string_index(F->get().name); @@ -1995,7 +1869,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p } for (int i = 0; i < save_order.size(); i++) { - save_unicode_string(f, save_order[i]->get_save_class()); String path = save_order[i]->get_path(); path = relative_paths ? local_path.path_to_file(path) : path; @@ -2007,10 +1880,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p Set<int> used_indices; for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES r = E->get(); if (r->get_path() == "" || r->get_path().find("::") != -1) { - if (r->get_subindex() != 0) { if (used_indices.has(r->get_subindex())) { r->set_subindex(0); //repeated @@ -2022,7 +1893,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p } for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES r = E->get(); if (r->get_path() == "" || r->get_path().find("::") != -1) { if (r->get_subindex() == 0) { @@ -2053,7 +1923,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p //now actually save the resources for (List<ResourceData>::Element *E = resources.front(); E; E = E->next()) { - ResourceData &rd = E->get(); ofs_table.push_back(f->get_position()); @@ -2061,7 +1930,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p f->store_32(rd.properties.size()); for (List<Property>::Element *F = rd.properties.front(); F; F = F->next()) { - Property &p = F->get(); f->store_32(p.name_idx); _write_variant(p.value, F->get().pi); @@ -2090,28 +1958,25 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p } Error ResourceFormatSaverBinary::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - String local_path = ProjectSettings::get_singleton()->localize_path(p_path); ResourceFormatSaverBinaryInstance saver; return saver.save(local_path, p_resource, p_flags); } bool ResourceFormatSaverBinary::recognize(const RES &p_resource) const { - return true; //all recognized } void ResourceFormatSaverBinary::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { - String base = p_resource->get_base_extension().to_lower(); p_extensions->push_back(base); - if (base != "res") + if (base != "res") { p_extensions->push_back("res"); + } } ResourceFormatSaverBinary *ResourceFormatSaverBinary::singleton = nullptr; ResourceFormatSaverBinary::ResourceFormatSaverBinary() { - singleton = this; } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 0f8fc9445b..428725f1d2 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -36,17 +36,16 @@ #include "core/os/file_access.h" class ResourceLoaderBinary { - - bool translation_remapped; + bool translation_remapped = false; String local_path; String res_path; String type; Ref<Resource> resource; - uint32_t ver_format; + uint32_t ver_format = 0; - FileAccess *f; + FileAccess *f = nullptr; - uint64_t importmd_ofs; + uint64_t importmd_ofs = 0; Vector<char> str_buf; List<RES> resource_cache; @@ -61,25 +60,25 @@ class ResourceLoaderBinary { RES cache; }; - bool use_sub_threads; - float *progress; + bool use_sub_threads = false; + float *progress = nullptr; Vector<ExtResource> external_resources; struct IntResource { String path; uint64_t offset; - RES cache; }; Vector<IntResource> internal_resources; + Map<String, RES> internal_index_cache; String get_unicode_string(); void _advance_padding(uint32_t p_len); Map<String, String> remaps; - Error error; + Error error = OK; - bool use_nocache; + bool use_nocache = false; friend class ResourceFormatLoaderBinary; @@ -98,7 +97,7 @@ public: String recognize(FileAccess *p_f); void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types); - ResourceLoaderBinary(); + ResourceLoaderBinary() {} ~ResourceLoaderBinary(); }; @@ -114,7 +113,6 @@ public: }; class ResourceFormatSaverBinaryInstance { - String local_path; String path; @@ -147,7 +145,6 @@ class ResourceFormatSaverBinaryInstance { }; struct ResourceData { - String type; List<Property> properties; }; diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 643df53f8c..d86877ee14 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,15 +30,15 @@ #include "resource_importer.h" +#include "core/config/project_settings.h" #include "core/os/os.h" -#include "core/variant_parser.h" +#include "core/variant/variant_parser.h" bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const { return p_a->get_importer_name() < p_b->get_importer_name(); } Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid) const { - Error err; FileAccess *f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); @@ -64,7 +64,6 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy String error_text; bool path_found = false; //first match must have priority while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); @@ -91,7 +90,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy r_path_and_type.path = value; path_found = true; //first match must have priority } else if (assign == "type") { - r_path_and_type.type = value; + r_path_and_type.type = ClassDB::get_compatibility_remapped_class(value); } else if (assign == "importer") { r_path_and_type.importer = value; } else if (assign == "group_file") { @@ -118,14 +117,13 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy } RES ResourceFormatImporter::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) { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); if (err != OK) { - - if (r_error) + if (r_error) { *r_error = err; + } return RES(); } @@ -143,7 +141,6 @@ RES ResourceFormatImporter::load(const String &p_path, const String &p_original_ } void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extensions) const { - Set<String> found; for (int i = 0; i < importers.size(); i++) { @@ -159,7 +156,6 @@ void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extension } void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - if (p_type == "") { get_recognized_extensions(p_extensions); return; @@ -169,11 +165,13 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_ for (int i = 0; i < importers.size(); i++) { String res_type = importers[i]->get_resource_type(); - if (res_type == String()) + if (res_type == String()) { continue; + } - if (!ClassDB::is_parent_class(res_type, p_type)) + if (!ClassDB::is_parent_class(res_type, p_type)) { continue; + } List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); @@ -187,26 +185,17 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_ } bool ResourceFormatImporter::exists(const String &p_path) const { - return FileAccess::exists(p_path + ".import"); } bool ResourceFormatImporter::recognize_path(const String &p_path, const String &p_for_type) const { - return FileAccess::exists(p_path + ".import"); } -bool ResourceFormatImporter::can_be_imported(const String &p_path) const { - - return ResourceFormatLoader::recognize_path(p_path); -} - int ResourceFormatImporter::get_import_order(const String &p_path) const { - Ref<ResourceImporter> importer; if (FileAccess::exists(p_path + ".import")) { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); @@ -214,37 +203,35 @@ int ResourceFormatImporter::get_import_order(const String &p_path) const { importer = get_importer_by_name(pat.importer); } } else { - importer = get_importer_by_extension(p_path.get_extension().to_lower()); } - if (importer.is_valid()) + if (importer.is_valid()) { return importer->get_import_order(); + } return 0; } bool ResourceFormatImporter::handles_type(const String &p_type) const { - for (int i = 0; i < importers.size(); i++) { - String res_type = importers[i]->get_resource_type(); - if (res_type == String()) + if (res_type == String()) { continue; - if (ClassDB::is_parent_class(res_type, p_type)) + } + if (ClassDB::is_parent_class(res_type, p_type)) { return true; + } } return true; } String ResourceFormatImporter::get_internal_resource_path(const String &p_path) const { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); if (err != OK) { - return String(); } @@ -252,12 +239,12 @@ String ResourceFormatImporter::get_internal_resource_path(const String &p_path) } void ResourceFormatImporter::get_internal_resource_path_list(const String &p_path, List<String> *r_paths) { - Error err; FileAccess *f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); - if (!f) + if (!f) { return; + } VariantParser::StreamFile stream; stream.f = f; @@ -269,7 +256,6 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat int lines = 0; String error_text; while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); @@ -298,7 +284,6 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat } String ResourceFormatImporter::get_import_group_file(const String &p_path) const { - bool valid = true; PathAndType pat; _get_path_and_type(p_path, pat, &valid); @@ -306,7 +291,6 @@ String ResourceFormatImporter::get_import_group_file(const String &p_path) const } bool ResourceFormatImporter::is_import_valid(const String &p_path) const { - bool valid = true; PathAndType pat; _get_path_and_type(p_path, pat, &valid); @@ -314,12 +298,10 @@ bool ResourceFormatImporter::is_import_valid(const String &p_path) const { } String ResourceFormatImporter::get_resource_type(const String &p_path) const { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); if (err != OK) { - return ""; } @@ -331,7 +313,6 @@ Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) cons Error err = _get_path_and_type(p_path, pat); if (err != OK) { - return Variant(); } @@ -339,12 +320,10 @@ Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) cons } void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); if (err != OK) { - return; } @@ -352,7 +331,6 @@ void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> } Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String &p_name) const { - for (int i = 0; i < importers.size(); i++) { if (importers[i]->get_importer_name() == p_name) { return importers[i]; @@ -363,7 +341,6 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String } void ResourceFormatImporter::get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter>> *r_importers) { - for (int i = 0; i < importers.size(); i++) { List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); @@ -376,12 +353,10 @@ void ResourceFormatImporter::get_importers_for_extension(const String &p_extensi } Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const String &p_extension) const { - Ref<ResourceImporter> importer; float priority = 0; for (int i = 0; i < importers.size(); i++) { - List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); for (List<String>::Element *F = local_exts.front(); F; F = F->next()) { @@ -396,12 +371,10 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St } String ResourceFormatImporter::get_import_base_path(const String &p_for_file) const { - - return "res://.import/" + p_for_file.get_file() + "-" + p_for_file.md5_text(); + return ProjectSettings::IMPORTED_FILES_PATH.plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text()); } bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const { - bool valid = true; PathAndType pat; _get_path_and_type(p_path, pat, &valid); @@ -422,7 +395,6 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con } String ResourceFormatImporter::get_import_settings_hash() const { - Vector<Ref<ResourceImporter>> sorted_importers = importers; sorted_importers.sort_custom<SortImporterByName>(); diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 6b76912494..d31273e3cb 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -36,7 +36,6 @@ class ResourceImporter; class ResourceFormatImporter : public ResourceFormatLoader { - struct PathAndType { String path; String type; @@ -71,7 +70,6 @@ public: virtual String get_import_group_file(const String &p_path) const; virtual bool exists(const String &p_path) const; - virtual bool can_be_imported(const String &p_path) const; virtual int get_import_order(const String &p_path) const; String get_internal_resource_path(const String &p_path) const; @@ -93,7 +91,6 @@ public: }; class ResourceImporter : public Reference { - GDCLASS(ResourceImporter, Reference); public: @@ -104,6 +101,7 @@ public: virtual String get_resource_type() const = 0; virtual float get_priority() const { return 1.0; } virtual int get_import_order() const { return 0; } + virtual int get_format_version() const { return 0; } struct ImportOption { PropertyInfo option; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index d90802d7e2..821e468aee 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,14 +30,13 @@ #include "resource_loader.h" +#include "core/config/project_settings.h" #include "core/io/resource_importer.h" #include "core/os/file_access.h" #include "core/os/os.h" -#include "core/path_remap.h" -#include "core/print_string.h" -#include "core/project_settings.h" -#include "core/translation.h" -#include "core/variant_parser.h" +#include "core/string/print_string.h" +#include "core/string/translation.h" +#include "core/variant/variant_parser.h" #ifdef DEBUG_LOAD_THREADED #define print_lt(m_text) print_line(m_text) @@ -50,7 +49,6 @@ Ref<ResourceFormatLoader> ResourceLoader::loader[ResourceLoader::MAX_LOADERS]; int ResourceLoader::loader_count = 0; bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_for_type) const { - String extension = p_path.get_extension(); List<String> extensions; @@ -61,16 +59,15 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_ } for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - - if (E->get().nocasecmp_to(extension) == 0) + if (E->get().nocasecmp_to(extension) == 0) { return true; + } } return false; } bool ResourceFormatLoader::handles_type(const String &p_type) const { - if (get_script_instance() && get_script_instance()->has_method("handles_type")) { // I guess custom loaders for custom resources should use "Resource" return get_script_instance()->call("handles_type", p_type); @@ -80,7 +77,6 @@ bool ResourceFormatLoader::handles_type(const String &p_type) const { } String ResourceFormatLoader::get_resource_type(const String &p_path) const { - if (get_script_instance() && get_script_instance()->has_method("get_resource_type")) { return get_script_instance()->call("get_resource_type", p_path); } @@ -89,13 +85,12 @@ String ResourceFormatLoader::get_resource_type(const String &p_path) const { } void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - - if (p_type == "" || handles_type(p_type)) + if (p_type == "" || handles_type(p_type)) { get_recognized_extensions(p_extensions); + } } void ResourceLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) { - for (int i = 0; i < loader_count; i++) { loader[i]->get_recognized_extensions_for_type(p_type, p_extensions); } @@ -106,7 +101,6 @@ bool ResourceFormatLoader::exists(const String &p_path) const { } void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions) const { - if (get_script_instance() && get_script_instance()->has_method("get_recognized_extensions")) { PackedStringArray exts = get_script_instance()->call("get_recognized_extensions"); @@ -120,19 +114,18 @@ void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions) } RES ResourceFormatLoader::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) { - if (get_script_instance() && get_script_instance()->has_method("load")) { Variant res = get_script_instance()->call("load", p_path, p_original_path, p_use_sub_threads); if (res.get_type() == Variant::INT) { - - if (r_error) + if (r_error) { *r_error = (Error)res.operator int64_t(); + } } else { - - if (r_error) + if (r_error) { *r_error = OK; + } return res; } @@ -143,7 +136,6 @@ RES ResourceFormatLoader::load(const String &p_path, const String &p_original_pa } void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - if (get_script_instance() && get_script_instance()->has_method("get_dependencies")) { PackedStringArray deps = get_script_instance()->call("get_dependencies", p_path, p_add_types); @@ -157,9 +149,7 @@ void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> * } Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - if (get_script_instance() && get_script_instance()->has_method("rename_dependencies")) { - Dictionary deps_dict; for (Map<String, String>::Element *E = p_map.front(); E; E = E->next()) { deps_dict[E->key()] = E->value(); @@ -173,7 +163,6 @@ Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map< } void ResourceFormatLoader::_bind_methods() { - { MethodInfo info = MethodInfo(Variant::NIL, "load", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "original_path")); info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; @@ -190,12 +179,10 @@ void ResourceFormatLoader::_bind_methods() { /////////////////////////////////// RES ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error, bool p_use_sub_threads, float *r_progress) { - bool found = false; // Try all loaders and pick the first match for the type hint for (int i = 0; i < loader_count; i++) { - if (!loader[i]->recognize_path(p_path, p_type_hint)) { continue; } @@ -208,7 +195,8 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c return res; } - ERR_FAIL_COND_V_MSG(found, RES(), "Failed loading resource: " + p_path + "."); + ERR_FAIL_COND_V_MSG(found, RES(), + vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path)); #ifdef TOOLS_ENABLED FileAccessRef file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); @@ -219,7 +207,6 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c } void ResourceLoader::_thread_load_function(void *p_userdata) { - ThreadLoadTask &load_task = *(ThreadLoadTask *)p_userdata; load_task.loader_id = Thread::get_caller_id(); @@ -238,7 +225,6 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { load_task.status = THREAD_LOAD_LOADED; } if (load_task.semaphore) { - if (load_task.start_next && thread_waiting_count > 0) { thread_waiting_count--; //thread loading count remains constant, this ends but another one begins @@ -259,8 +245,9 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { if (load_task.resource.is_valid()) { load_task.resource->set_path(load_task.local_path); - if (load_task.xl_remapped) + if (load_task.xl_remapped) { load_task.resource->set_as_translation_remapped(true); + } #ifdef TOOLS_ENABLED @@ -279,13 +266,14 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { thread_load_mutex->unlock(); } -Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, const String &p_source_resource) { +Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, const String &p_source_resource) { String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } thread_load_mutex->lock(); @@ -388,7 +376,6 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String & } float ResourceLoader::_dependency_get_progress(const String &p_path) { - if (thread_load_tasks.has(p_path)) { ThreadLoadTask &load_task = thread_load_tasks[p_path]; int dep_count = load_task.sub_tasks.size(); @@ -411,12 +398,12 @@ float ResourceLoader::_dependency_get_progress(const String &p_path) { } ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, float *r_progress) { - String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } thread_load_mutex->lock(); if (!thread_load_tasks.has(local_path)) { @@ -434,13 +421,14 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const return status; } -RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) { +RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) { String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } thread_load_mutex->lock(); if (!thread_load_tasks.has(local_path)) { @@ -517,18 +505,18 @@ RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) { } RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { - - if (r_error) + if (r_error) { *r_error = ERR_CANT_OPEN; + } String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } if (!p_no_cache) { - thread_load_mutex->lock(); //Is it already being loaded? poll until done @@ -593,7 +581,6 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p return load_threaded_get(p_path, r_error); } else { - bool xl_remapped = false; String path = _path_remap(local_path, &xl_remapped); @@ -610,8 +597,9 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p return RES(); } - if (xl_remapped) + if (xl_remapped) { res->set_as_translation_remapped(true); + } #ifdef TOOLS_ENABLED @@ -628,15 +616,14 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p } bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { - String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } if (ResourceCache::has(local_path)) { - return true; // If cached, it probably exists } @@ -645,20 +632,19 @@ bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { // Try all loaders and pick the first match for the type hint for (int i = 0; i < loader_count; i++) { - if (!loader[i]->recognize_path(path, p_type_hint)) { continue; } - if (loader[i]->exists(path)) + if (loader[i]->exists(path)) { return true; + } } return false; } void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front) { - ERR_FAIL_COND(p_format_loader.is_null()); ERR_FAIL_COND(loader_count >= MAX_LOADERS); @@ -674,14 +660,14 @@ void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> p_form } void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader) { - ERR_FAIL_COND(p_format_loader.is_null()); // Find loader int i = 0; for (; i < loader_count; ++i) { - if (loader[i] == p_format_loader) + if (loader[i] == p_format_loader) { break; + } } ERR_FAIL_COND(i >= loader_count); // Not found @@ -695,19 +681,19 @@ void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_f } int ResourceLoader::get_import_order(const String &p_path) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -723,15 +709,16 @@ String ResourceLoader::get_import_group_file(const String &p_path) { String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -744,19 +731,19 @@ String ResourceLoader::get_import_group_file(const String &p_path) { } bool ResourceLoader::is_import_valid(const String &p_path) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -769,19 +756,19 @@ bool ResourceLoader::is_import_valid(const String &p_path) { } bool ResourceLoader::is_imported(const String &p_path) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -794,19 +781,19 @@ bool ResourceLoader::is_imported(const String &p_path) { } void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -817,19 +804,19 @@ void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_depe } Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -842,15 +829,14 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String } String ResourceLoader::get_resource_type(const String &p_path) { - String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } for (int i = 0; i < loader_count; i++) { - String result = loader[i]->get_resource_type(local_path); if (result != "") { return result; @@ -861,7 +847,6 @@ String ResourceLoader::get_resource_type(const String &p_path) { } String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_remapped) { - String new_path = p_path; if (translation_remaps.has(p_path)) { @@ -881,7 +866,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem bool near_match = false; for (int i = 0; i < res_remaps.size(); i++) { - int split = res_remaps[i].find_last(":"); + int split = res_remaps[i].rfind(":"); if (split == -1) { continue; } @@ -920,7 +905,6 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem FileAccess *f = FileAccess::open(p_path + ".remap", FileAccess::READ, &err); if (f) { - VariantParser::StreamFile stream; stream.f = f; @@ -931,7 +915,6 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem int lines = 0; String error_text; while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); @@ -960,9 +943,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem } String ResourceLoader::import_remap(const String &p_path) { - if (ResourceFormatImporter::get_singleton()->recognize_path(p_path)) { - return ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path); } @@ -974,7 +955,6 @@ String ResourceLoader::path_remap(const String &p_path) { } void ResourceLoader::reload_translation_remaps() { - if (ResourceCache::lock) { ResourceCache::lock->read_lock(); } @@ -999,15 +979,14 @@ void ResourceLoader::reload_translation_remaps() { } void ResourceLoader::load_translation_remaps() { - - if (!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) + if (!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) { return; + } Dictionary remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); List<Variant> keys; remaps.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - Array langs = remaps[E->get()]; Vector<String> lang_remaps; lang_remaps.resize(langs.size()); @@ -1021,12 +1000,15 @@ void ResourceLoader::load_translation_remaps() { void ResourceLoader::clear_translation_remaps() { translation_remaps.clear(); + while (remapped_list.first() != nullptr) { + remapped_list.remove(remapped_list.first()); + } } void ResourceLoader::load_path_remaps() { - - if (!ProjectSettings::get_singleton()->has_setting("path_remap/remapped_paths")) + if (!ProjectSettings::get_singleton()->has_setting("path_remap/remapped_paths")) { return; + } Vector<String> remaps = ProjectSettings::get_singleton()->get("path_remap/remapped_paths"); int rc = remaps.size(); @@ -1034,13 +1016,11 @@ void ResourceLoader::load_path_remaps() { const String *r = remaps.ptr(); for (int i = 0; i < rc; i += 2) { - path_remaps[r[i]] = r[i + 1]; } } void ResourceLoader::clear_path_remaps() { - path_remaps.clear(); } @@ -1060,9 +1040,9 @@ Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(St } bool ResourceLoader::add_custom_resource_format_loader(String script_path) { - - if (_find_custom_resource_format_loader(script_path).is_valid()) + if (_find_custom_resource_format_loader(script_path).is_valid()) { return false; + } Ref<Resource> res = ResourceLoader::load(script_path); ERR_FAIL_COND_V(res.is_null(), false); @@ -1077,7 +1057,7 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); - ResourceFormatLoader *crl = Object::cast_to<ResourceFormatLoader>(obj); + Ref<ResourceFormatLoader> crl = Object::cast_to<ResourceFormatLoader>(obj); crl->set_script(s); ResourceLoader::add_resource_format_loader(crl); @@ -1085,10 +1065,10 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { } void ResourceLoader::remove_custom_resource_format_loader(String script_path) { - Ref<ResourceFormatLoader> custom_loader = _find_custom_resource_format_loader(script_path); - if (custom_loader.is_valid()) + if (custom_loader.is_valid()) { remove_resource_format_loader(custom_loader); + } } void ResourceLoader::add_custom_loaders() { @@ -1100,7 +1080,6 @@ void ResourceLoader::add_custom_loaders() { ScriptServer::get_global_class_list(&global_classes); for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { - StringName class_name = E->get(); StringName base_class = ScriptServer::get_global_class_native_base(class_name); @@ -1112,7 +1091,6 @@ void ResourceLoader::add_custom_loaders() { } void ResourceLoader::remove_custom_loaders() { - Vector<Ref<ResourceFormatLoader>> custom_loaders; for (int i = 0; i < loader_count; ++i) { if (loader[i]->get_script_instance()) { @@ -1135,7 +1113,6 @@ void ResourceLoader::initialize() { } void ResourceLoader::finalize() { - memdelete(thread_load_mutex); memdelete(thread_load_semaphore); } diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 2d95d5545f..dbf6be46c5 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,12 +31,11 @@ #ifndef RESOURCE_LOADER_H #define RESOURCE_LOADER_H +#include "core/io/resource.h" #include "core/os/semaphore.h" #include "core/os/thread.h" -#include "core/resource.h" class ResourceFormatLoader : public Reference { - GDCLASS(ResourceFormatLoader, Reference); protected: @@ -67,7 +66,6 @@ typedef Error (*ResourceLoaderImport)(const String &p_path); typedef void (*ResourceLoadedCallback)(RES p_resource, const String &p_path); class ResourceLoader { - enum { MAX_LOADERS = 64 }; @@ -160,7 +158,9 @@ public: static bool get_timestamp_on_load() { return timestamp_on_load; } static void notify_load_error(const String &p_err) { - if (err_notify) err_notify(err_notify_ud, p_err); + if (err_notify) { + err_notify(err_notify_ud, p_err); + } } static void set_error_notify_func(void *p_ud, ResourceLoadErrorNotify p_err_notify) { err_notify = p_err_notify; @@ -168,7 +168,9 @@ public: } static void notify_dependency_error(const String &p_path, const String &p_dependency, const String &p_type) { - if (dep_err_notify) dep_err_notify(dep_err_notify_ud, p_path, p_dependency, p_type); + if (dep_err_notify) { + dep_err_notify(dep_err_notify_ud, p_path, p_dependency, p_type); + } } static void set_dependency_error_notify_func(void *p_ud, DependencyErrorNotify p_err_notify) { dep_err_notify = p_err_notify; diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 09128adb50..7ebc7f34b3 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,10 +29,10 @@ /*************************************************************************/ #include "resource_saver.h" +#include "core/config/project_settings.h" #include "core/io/resource_loader.h" +#include "core/object/script_language.h" #include "core/os/file_access.h" -#include "core/project_settings.h" -#include "core/script_language.h" Ref<ResourceFormatSaver> ResourceSaver::saver[MAX_SAVERS]; @@ -41,7 +41,6 @@ bool ResourceSaver::timestamp_on_save = false; ResourceSavedCallback ResourceSaver::save_callback = nullptr; Error ResourceFormatSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - if (get_script_instance() && get_script_instance()->has_method("save")) { return (Error)get_script_instance()->call("save", p_path, p_resource, p_flags).operator int64_t(); } @@ -50,7 +49,6 @@ Error ResourceFormatSaver::save(const String &p_path, const RES &p_resource, uin } bool ResourceFormatSaver::recognize(const RES &p_resource) const { - if (get_script_instance() && get_script_instance()->has_method("recognize")) { return get_script_instance()->call("recognize", p_resource); } @@ -59,7 +57,6 @@ bool ResourceFormatSaver::recognize(const RES &p_resource) const { } void ResourceFormatSaver::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { - if (get_script_instance() && get_script_instance()->has_method("get_recognized_extensions")) { PackedStringArray exts = get_script_instance()->call("get_recognized_extensions", p_resource); @@ -73,7 +70,6 @@ void ResourceFormatSaver::get_recognized_extensions(const RES &p_resource, List< } void ResourceFormatSaver::_bind_methods() { - { PropertyInfo arg0 = PropertyInfo(Variant::STRING, "path"); PropertyInfo arg1 = PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"); @@ -86,40 +82,40 @@ void ResourceFormatSaver::_bind_methods() { } Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - String extension = p_path.get_extension(); Error err = ERR_FILE_UNRECOGNIZED; for (int i = 0; i < saver_count; i++) { - - if (!saver[i]->recognize(p_resource)) + if (!saver[i]->recognize(p_resource)) { continue; + } List<String> extensions; bool recognized = false; saver[i]->get_recognized_extensions(p_resource, &extensions); for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - - if (E->get().nocasecmp_to(extension) == 0) + if (E->get().nocasecmp_to(extension) == 0) { recognized = true; + } } - if (!recognized) + if (!recognized) { continue; + } String old_path = p_resource->get_path(); String local_path = ProjectSettings::get_singleton()->localize_path(p_path); RES rwcopy = p_resource; - if (p_flags & FLAG_CHANGE_PATH) + if (p_flags & FLAG_CHANGE_PATH) { rwcopy->set_path(local_path); + } err = saver[i]->save(p_path, p_resource, p_flags); if (err == OK) { - #ifdef TOOLS_ENABLED ((Resource *)p_resource.ptr())->set_edited(false); @@ -130,11 +126,13 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t } #endif - if (p_flags & FLAG_CHANGE_PATH) + if (p_flags & FLAG_CHANGE_PATH) { rwcopy->set_path(old_path); + } - if (save_callback && p_path.begins_with("res://")) + if (save_callback && p_path.begins_with("res://")) { save_callback(p_resource, p_path); + } return OK; } @@ -144,20 +142,16 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t } void ResourceSaver::set_save_callback(ResourceSavedCallback p_callback) { - save_callback = p_callback; } void ResourceSaver::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) { - for (int i = 0; i < saver_count; i++) { - saver[i]->get_recognized_extensions(p_resource, p_extensions); } } void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front) { - ERR_FAIL_COND_MSG(p_format_saver.is_null(), "It's not a reference to a valid ResourceFormatSaver object."); ERR_FAIL_COND(saver_count >= MAX_SAVERS); @@ -173,14 +167,14 @@ void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_ } void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver) { - ERR_FAIL_COND_MSG(p_format_saver.is_null(), "It's not a reference to a valid ResourceFormatSaver object."); // Find saver int i = 0; for (; i < saver_count; ++i) { - if (saver[i] == p_format_saver) + if (saver[i] == p_format_saver) { break; + } } ERR_FAIL_COND(i >= saver_count); // Not found @@ -203,9 +197,9 @@ Ref<ResourceFormatSaver> ResourceSaver::_find_custom_resource_format_saver(Strin } bool ResourceSaver::add_custom_resource_format_saver(String script_path) { - - if (_find_custom_resource_format_saver(script_path).is_valid()) + if (_find_custom_resource_format_saver(script_path).is_valid()) { return false; + } Ref<Resource> res = ResourceLoader::load(script_path); ERR_FAIL_COND_V(res.is_null(), false); @@ -220,7 +214,7 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); - ResourceFormatSaver *crl = Object::cast_to<ResourceFormatSaver>(obj); + Ref<ResourceFormatSaver> crl = Object::cast_to<ResourceFormatSaver>(obj); crl->set_script(s); ResourceSaver::add_resource_format_saver(crl); @@ -228,10 +222,10 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { } void ResourceSaver::remove_custom_resource_format_saver(String script_path) { - Ref<ResourceFormatSaver> custom_saver = _find_custom_resource_format_saver(script_path); - if (custom_saver.is_valid()) + if (custom_saver.is_valid()) { remove_resource_format_saver(custom_saver); + } } void ResourceSaver::add_custom_savers() { @@ -243,7 +237,6 @@ void ResourceSaver::add_custom_savers() { ScriptServer::get_global_class_list(&global_classes); for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { - StringName class_name = E->get(); StringName base_class = ScriptServer::get_global_class_native_base(class_name); @@ -255,7 +248,6 @@ void ResourceSaver::add_custom_savers() { } void ResourceSaver::remove_custom_savers() { - Vector<Ref<ResourceFormatSaver>> custom_savers; for (int i = 0; i < saver_count; ++i) { if (saver[i]->get_script_instance()) { diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 2ddebf0581..2c9e8f1aa3 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef RESOURCE_SAVER_H #define RESOURCE_SAVER_H -#include "core/resource.h" +#include "core/io/resource.h" class ResourceFormatSaver : public Reference { GDCLASS(ResourceFormatSaver, Reference); @@ -50,7 +50,6 @@ public: typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path); class ResourceSaver { - enum { MAX_SAVERS = 64 }; @@ -64,7 +63,6 @@ class ResourceSaver { public: enum SaverFlags { - FLAG_RELATIVE_PATHS = 1, FLAG_BUNDLE_RESOURCES = 2, FLAG_CHANGE_PATH = 4, diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index b28b17aa95..8407d55196 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,16 +33,15 @@ #include "core/io/marshalls.h" Error StreamPeer::_put_data(const Vector<uint8_t> &p_data) { - int len = p_data.size(); - if (len == 0) + if (len == 0) { return OK; + } const uint8_t *r = p_data.ptr(); return put_data(&r[0], len); } Array StreamPeer::_put_partial_data(const Vector<uint8_t> &p_data) { - Array ret; int len = p_data.size(); @@ -65,13 +64,11 @@ Array StreamPeer::_put_partial_data(const Vector<uint8_t> &p_data) { } Array StreamPeer::_get_data(int p_bytes) { - Array ret; Vector<uint8_t> data; data.resize(p_bytes); if (data.size() != p_bytes) { - ret.push_back(ERR_OUT_OF_MEMORY); ret.push_back(Vector<uint8_t>()); return ret; @@ -86,13 +83,11 @@ Array StreamPeer::_get_data(int p_bytes) { } Array StreamPeer::_get_partial_data(int p_bytes) { - Array ret; Vector<uint8_t> data; data.resize(p_bytes); if (data.size() != p_bytes) { - ret.push_back(ERR_OUT_OF_MEMORY); ret.push_back(Vector<uint8_t>()); return ret; @@ -105,7 +100,6 @@ Array StreamPeer::_get_partial_data(int p_bytes) { if (err != OK) { data.resize(0); } else if (received != data.size()) { - data.resize(received); } @@ -115,12 +109,10 @@ Array StreamPeer::_get_partial_data(int p_bytes) { } void StreamPeer::set_big_endian(bool p_enable) { - big_endian = p_enable; } bool StreamPeer::is_big_endian_enabled() const { - return big_endian; } @@ -129,11 +121,10 @@ void StreamPeer::put_u8(uint8_t p_val) { } void StreamPeer::put_8(int8_t p_val) { - put_data((const uint8_t *)&p_val, 1); } -void StreamPeer::put_u16(uint16_t p_val) { +void StreamPeer::put_u16(uint16_t p_val) { if (big_endian) { p_val = BSWAP16(p_val); } @@ -141,8 +132,8 @@ void StreamPeer::put_u16(uint16_t p_val) { encode_uint16(p_val, buf); put_data(buf, 2); } -void StreamPeer::put_16(int16_t p_val) { +void StreamPeer::put_16(int16_t p_val) { if (big_endian) { p_val = BSWAP16(p_val); } @@ -150,8 +141,8 @@ void StreamPeer::put_16(int16_t p_val) { encode_uint16(p_val, buf); put_data(buf, 2); } -void StreamPeer::put_u32(uint32_t p_val) { +void StreamPeer::put_u32(uint32_t p_val) { if (big_endian) { p_val = BSWAP32(p_val); } @@ -159,8 +150,8 @@ void StreamPeer::put_u32(uint32_t p_val) { encode_uint32(p_val, buf); put_data(buf, 4); } -void StreamPeer::put_32(int32_t p_val) { +void StreamPeer::put_32(int32_t p_val) { if (big_endian) { p_val = BSWAP32(p_val); } @@ -168,8 +159,8 @@ void StreamPeer::put_32(int32_t p_val) { encode_uint32(p_val, buf); put_data(buf, 4); } -void StreamPeer::put_u64(uint64_t p_val) { +void StreamPeer::put_u64(uint64_t p_val) { if (big_endian) { p_val = BSWAP64(p_val); } @@ -177,8 +168,8 @@ void StreamPeer::put_u64(uint64_t p_val) { encode_uint64(p_val, buf); put_data(buf, 8); } -void StreamPeer::put_64(int64_t p_val) { +void StreamPeer::put_64(int64_t p_val) { if (big_endian) { p_val = BSWAP64(p_val); } @@ -186,8 +177,8 @@ void StreamPeer::put_64(int64_t p_val) { encode_uint64(p_val, buf); put_data(buf, 8); } -void StreamPeer::put_float(float p_val) { +void StreamPeer::put_float(float p_val) { uint8_t buf[4]; encode_float(p_val, buf); @@ -198,8 +189,8 @@ void StreamPeer::put_float(float p_val) { put_data(buf, 4); } -void StreamPeer::put_double(double p_val) { +void StreamPeer::put_double(double p_val) { uint8_t buf[8]; encode_double(p_val, buf); if (big_endian) { @@ -208,20 +199,20 @@ void StreamPeer::put_double(double p_val) { } put_data(buf, 8); } -void StreamPeer::put_string(const String &p_string) { +void StreamPeer::put_string(const String &p_string) { CharString cs = p_string.ascii(); put_u32(cs.length()); put_data((const uint8_t *)cs.get_data(), cs.length()); } -void StreamPeer::put_utf8_string(const String &p_string) { +void StreamPeer::put_utf8_string(const String &p_string) { CharString cs = p_string.utf8(); put_u32(cs.length()); put_data((const uint8_t *)cs.get_data(), cs.length()); } -void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) { +void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) { int len = 0; Vector<uint8_t> buf; encode_variant(p_variant, nullptr, len, p_full_objects); @@ -232,19 +223,18 @@ void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) { } uint8_t StreamPeer::get_u8() { - uint8_t buf[1]; get_data(buf, 1); return buf[0]; } -int8_t StreamPeer::get_8() { +int8_t StreamPeer::get_8() { uint8_t buf[1]; get_data(buf, 1); return buf[0]; } -uint16_t StreamPeer::get_u16() { +uint16_t StreamPeer::get_u16() { uint8_t buf[2]; get_data(buf, 2); uint16_t r = decode_uint16(buf); @@ -253,8 +243,8 @@ uint16_t StreamPeer::get_u16() { } return r; } -int16_t StreamPeer::get_16() { +int16_t StreamPeer::get_16() { uint8_t buf[2]; get_data(buf, 2); uint16_t r = decode_uint16(buf); @@ -263,8 +253,8 @@ int16_t StreamPeer::get_16() { } return r; } -uint32_t StreamPeer::get_u32() { +uint32_t StreamPeer::get_u32() { uint8_t buf[4]; get_data(buf, 4); uint32_t r = decode_uint32(buf); @@ -273,8 +263,8 @@ uint32_t StreamPeer::get_u32() { } return r; } -int32_t StreamPeer::get_32() { +int32_t StreamPeer::get_32() { uint8_t buf[4]; get_data(buf, 4); uint32_t r = decode_uint32(buf); @@ -283,8 +273,8 @@ int32_t StreamPeer::get_32() { } return r; } -uint64_t StreamPeer::get_u64() { +uint64_t StreamPeer::get_u64() { uint8_t buf[8]; get_data(buf, 8); uint64_t r = decode_uint64(buf); @@ -293,8 +283,8 @@ uint64_t StreamPeer::get_u64() { } return r; } -int64_t StreamPeer::get_64() { +int64_t StreamPeer::get_64() { uint8_t buf[8]; get_data(buf, 8); uint64_t r = decode_uint64(buf); @@ -303,8 +293,8 @@ int64_t StreamPeer::get_64() { } return r; } -float StreamPeer::get_float() { +float StreamPeer::get_float() { uint8_t buf[4]; get_data(buf, 4); @@ -317,7 +307,6 @@ float StreamPeer::get_float() { } double StreamPeer::get_double() { - uint8_t buf[8]; get_data(buf, 8); @@ -328,10 +317,11 @@ double StreamPeer::get_double() { return decode_double(buf); } -String StreamPeer::get_string(int p_bytes) { - if (p_bytes < 0) +String StreamPeer::get_string(int p_bytes) { + if (p_bytes < 0) { p_bytes = get_u32(); + } ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<char> buf; @@ -342,10 +332,11 @@ String StreamPeer::get_string(int p_bytes) { buf.write[p_bytes] = 0; return buf.ptr(); } -String StreamPeer::get_utf8_string(int p_bytes) { - if (p_bytes < 0) +String StreamPeer::get_utf8_string(int p_bytes) { + if (p_bytes < 0) { p_bytes = get_u32(); + } ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<uint8_t> buf; @@ -358,8 +349,8 @@ String StreamPeer::get_utf8_string(int p_bytes) { ret.parse_utf8((const char *)buf.ptr(), buf.size()); return ret; } -Variant StreamPeer::get_var(bool p_allow_objects) { +Variant StreamPeer::get_var(bool p_allow_objects) { int len = get_32(); Vector<uint8_t> var; Error err = var.resize(len); @@ -375,7 +366,6 @@ Variant StreamPeer::get_var(bool p_allow_objects) { } void StreamPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("put_data", "data"), &StreamPeer::_put_data); ClassDB::bind_method(D_METHOD("put_partial_data", "data"), &StreamPeer::_put_partial_data); @@ -417,10 +407,10 @@ void StreamPeer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian_enabled"); } + //////////////////////////////// void StreamPeerBuffer::_bind_methods() { - ClassDB::bind_method(D_METHOD("seek", "position"), &StreamPeerBuffer::seek); ClassDB::bind_method(D_METHOD("get_size"), &StreamPeerBuffer::get_size); ClassDB::bind_method(D_METHOD("get_position"), &StreamPeerBuffer::get_position); @@ -434,9 +424,9 @@ void StreamPeerBuffer::_bind_methods() { } Error StreamPeerBuffer::put_data(const uint8_t *p_data, int p_bytes) { - - if (p_bytes <= 0) + if (p_bytes <= 0) { return OK; + } if (pointer + p_bytes > data.size()) { data.resize(pointer + p_bytes); @@ -450,23 +440,21 @@ Error StreamPeerBuffer::put_data(const uint8_t *p_data, int p_bytes) { } Error StreamPeerBuffer::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - r_sent = p_bytes; return put_data(p_data, p_bytes); } Error StreamPeerBuffer::get_data(uint8_t *p_buffer, int p_bytes) { - int recv; get_partial_data(p_buffer, p_bytes, recv); - if (recv != p_bytes) + if (recv != p_bytes) { return ERR_INVALID_PARAMETER; + } return OK; } Error StreamPeerBuffer::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - if (pointer + p_bytes > data.size()) { r_received = data.size() - pointer; if (r_received <= 0) { @@ -487,57 +475,44 @@ Error StreamPeerBuffer::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_ } int StreamPeerBuffer::get_available_bytes() const { - return data.size() - pointer; } void StreamPeerBuffer::seek(int p_pos) { - ERR_FAIL_COND(p_pos < 0); ERR_FAIL_COND(p_pos > data.size()); pointer = p_pos; } -int StreamPeerBuffer::get_size() const { +int StreamPeerBuffer::get_size() const { return data.size(); } int StreamPeerBuffer::get_position() const { - return pointer; } void StreamPeerBuffer::resize(int p_size) { - data.resize(p_size); } void StreamPeerBuffer::set_data_array(const Vector<uint8_t> &p_data) { - data = p_data; pointer = 0; } Vector<uint8_t> StreamPeerBuffer::get_data_array() const { - return data; } void StreamPeerBuffer::clear() { - data.resize(0); pointer = 0; } Ref<StreamPeerBuffer> StreamPeerBuffer::duplicate() const { - Ref<StreamPeerBuffer> spb; spb.instance(); spb->data = data; return spb; } - -StreamPeerBuffer::StreamPeerBuffer() { - - pointer = 0; -} diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 9358a2c07c..dadedbd2dc 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef STREAM_PEER_H #define STREAM_PEER_H -#include "core/reference.h" +#include "core/object/reference.h" class StreamPeer : public Reference { GDCLASS(StreamPeer, Reference); @@ -47,7 +47,7 @@ protected: Array _get_data(int p_bytes); Array _get_partial_data(int p_bytes); - bool big_endian; + bool big_endian = false; public: virtual Error put_data(const uint8_t *p_data, int p_bytes) = 0; ///< put a whole chunk of data, blocking until it sent @@ -89,27 +89,26 @@ public: String get_utf8_string(int p_bytes = -1); Variant get_var(bool p_allow_objects = false); - StreamPeer() { big_endian = false; } + StreamPeer() {} }; class StreamPeerBuffer : public StreamPeer { - GDCLASS(StreamPeerBuffer, StreamPeer); Vector<uint8_t> data; - int pointer; + int pointer = 0; protected: static void _bind_methods(); public: - Error put_data(const uint8_t *p_data, int p_bytes); - Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); + Error put_data(const uint8_t *p_data, int p_bytes) override; + Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; - Error get_data(uint8_t *p_buffer, int p_bytes); - Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); + Error get_data(uint8_t *p_buffer, int p_bytes) override; + Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; - virtual int get_available_bytes() const; + virtual int get_available_bytes() const override; void seek(int p_pos); int get_size() const; @@ -123,7 +122,7 @@ public: Ref<StreamPeerBuffer> duplicate() const; - StreamPeerBuffer(); + StreamPeerBuffer() {} }; #endif // STREAM_PEER_H diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index d98935f77c..6f5a06474c 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,14 +30,14 @@ #include "stream_peer_ssl.h" -#include "core/engine.h" +#include "core/config/engine.h" StreamPeerSSL *(*StreamPeerSSL::_create)() = nullptr; StreamPeerSSL *StreamPeerSSL::create() { - - if (_create) + if (_create) { return _create(); + } return nullptr; } @@ -56,7 +56,6 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const { } void StreamPeerSSL::_bind_methods() { - ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerSSL::accept_stream, DEFVAL(Ref<X509Certificate>())); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>())); @@ -73,7 +72,3 @@ void StreamPeerSSL::_bind_methods() { BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); } - -StreamPeerSSL::StreamPeerSSL() { - blocking_handshake = true; -} diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index de3cb09c60..1df9ced08c 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -43,7 +43,7 @@ protected: static bool available; - bool blocking_handshake; + bool blocking_handshake = true; public: enum Status { @@ -68,7 +68,7 @@ public: static bool is_available(); - StreamPeerSSL(); + StreamPeerSSL() {} }; VARIANT_ENUM_CAST(StreamPeerSSL::Status); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index f0c5816d73..760710a9eb 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,9 @@ #include "stream_peer_tcp.h" -#include "core/project_settings.h" +#include "core/config/project_settings.h" Error StreamPeerTCP::_poll_connection() { - ERR_FAIL_COND_V(status != STATUS_CONNECTING || !_sock.is_valid() || !_sock->is_open(), FAILED); Error err = _sock->connect_to_host(peer_host, peer_port); @@ -58,7 +57,6 @@ Error StreamPeerTCP::_poll_connection() { } void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port) { - _sock = p_sock; _sock->set_blocking_enabled(false); @@ -70,7 +68,6 @@ void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint } Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); @@ -103,18 +100,14 @@ Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) } Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); if (status == STATUS_NONE || status == STATUS_ERROR) { - return FAILED; } if (status != STATUS_CONNECTED) { - if (_poll_connection() != OK) { - return FAILED; } @@ -124,8 +117,9 @@ Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool } } - if (!_sock->is_open()) + if (!_sock->is_open()) { return FAILED; + } Error err; int data_to_send = p_bytes; @@ -133,12 +127,10 @@ Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool int total_sent = 0; while (data_to_send) { - int sent_amount = 0; err = _sock->send(offset, data_to_send, sent_amount); if (err != OK) { - if (err != ERR_BUSY) { disconnect_from_host(); return FAILED; @@ -156,7 +148,6 @@ Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool return FAILED; } } else { - data_to_send -= sent_amount; offset += sent_amount; total_sent += sent_amount; @@ -169,16 +160,12 @@ Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool } Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) { - if (!is_connected_to_host()) { - return FAILED; } if (status == STATUS_CONNECTING) { - if (_poll_connection() != OK) { - return FAILED; } @@ -194,12 +181,10 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool r_received = 0; while (to_read) { - int read = 0; err = _sock->recv(p_buffer + total_read, to_read, read); if (err != OK) { - if (err != ERR_BUSY) { disconnect_from_host(); return FAILED; @@ -218,13 +203,11 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool } } else if (read == 0) { - disconnect_from_host(); r_received = total_read; return ERR_FILE_EOF; } else { - to_read -= read; total_read += read; @@ -241,18 +224,15 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool } void StreamPeerTCP::set_no_delay(bool p_enabled) { - ERR_FAIL_COND(!is_connected_to_host()); _sock->set_tcp_no_delay_enabled(p_enabled); } bool StreamPeerTCP::is_connected_to_host() const { - return _sock.is_valid() && _sock->is_open() && (status == STATUS_CONNECTED || status == STATUS_CONNECTING); } StreamPeerTCP::Status StreamPeerTCP::get_status() { - if (status == STATUS_CONNECTING) { _poll_connection(); } else if (status == STATUS_CONNECTED) { @@ -278,9 +258,9 @@ StreamPeerTCP::Status StreamPeerTCP::get_status() { } void StreamPeerTCP::disconnect_from_host() { - - if (_sock.is_valid() && _sock->is_open()) + if (_sock.is_valid() && _sock->is_open()) { _sock->close(); + } timeout = 0; status = STATUS_NONE; @@ -294,59 +274,51 @@ Error StreamPeerTCP::poll(NetSocket::PollType p_type, int timeout) { } Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) { - int total; return write(p_data, p_bytes, total, true); } Error StreamPeerTCP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - return write(p_data, p_bytes, r_sent, false); } Error StreamPeerTCP::get_data(uint8_t *p_buffer, int p_bytes) { - int total; return read(p_buffer, p_bytes, total, true); } Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - return read(p_buffer, p_bytes, r_received, false); } int StreamPeerTCP::get_available_bytes() const { - ERR_FAIL_COND_V(!_sock.is_valid(), -1); return _sock->get_available_bytes(); } IP_Address StreamPeerTCP::get_connected_host() const { - return peer_host; } uint16_t StreamPeerTCP::get_connected_port() const { - return peer_port; } Error StreamPeerTCP::_connect(const String &p_address, int p_port) { - IP_Address ip; if (p_address.is_valid_ip_address()) { ip = p_address; } else { ip = IP::get_singleton()->resolve_hostname(p_address); - if (!ip.is_valid()) + if (!ip.is_valid()) { return ERR_CANT_RESOLVE; + } } return connect_to_host(ip, p_port); } void StreamPeerTCP::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port"), &StreamPeerTCP::_connect); ClassDB::bind_method(D_METHOD("is_connected_to_host"), &StreamPeerTCP::is_connected_to_host); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTCP::get_status); @@ -362,13 +334,9 @@ void StreamPeerTCP::_bind_methods() { } StreamPeerTCP::StreamPeerTCP() : - _sock(Ref<NetSocket>(NetSocket::create())), - timeout(0), - status(STATUS_NONE), - peer_port(0) { + _sock(Ref<NetSocket>(NetSocket::create())) { } StreamPeerTCP::~StreamPeerTCP() { - disconnect_from_host(); } diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index 86df9ab8cf..10b90908d4 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -37,13 +37,11 @@ #include "core/io/stream_peer.h" class StreamPeerTCP : public StreamPeer { - GDCLASS(StreamPeerTCP, StreamPeer); OBJ_CATEGORY("Networking"); public: enum Status { - STATUS_NONE, STATUS_CONNECTING, STATUS_CONNECTED, @@ -52,10 +50,10 @@ public: protected: Ref<NetSocket> _sock; - uint64_t timeout; - Status status; + uint64_t timeout = 0; + Status status = STATUS_NONE; IP_Address peer_host; - uint16_t peer_port; + uint16_t peer_port = 0; Error _connect(const String &p_address, int p_port); Error _poll_connection(); @@ -73,7 +71,7 @@ public: uint16_t get_connected_port() const; void disconnect_from_host(); - int get_available_bytes() const; + int get_available_bytes() const override; Status get_status(); void set_no_delay(bool p_enabled); @@ -82,10 +80,10 @@ public: Error poll(NetSocket::PollType p_type, int timeout = 0); // Read/Write from StreamPeer - Error put_data(const uint8_t *p_data, int p_bytes); - Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); - Error get_data(uint8_t *p_buffer, int p_bytes); - Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); + Error put_data(const uint8_t *p_data, int p_bytes) override; + Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; + Error get_data(uint8_t *p_buffer, int p_bytes) override; + Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; StreamPeerTCP(); ~StreamPeerTCP(); diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp index 69c2ba7943..323d2bbd7f 100644 --- a/core/io/tcp_server.cpp +++ b/core/io/tcp_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,6 @@ #include "tcp_server.h" void TCP_Server::_bind_methods() { - ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); ClassDB::bind_method(D_METHOD("is_listening"), &TCP_Server::is_listening); @@ -40,7 +39,6 @@ void TCP_Server::_bind_methods() { } Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); @@ -49,8 +47,9 @@ Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { IP::Type ip_type = IP::TYPE_ANY; // If the bind address is valid use its type as the socket type - if (p_bind_address.is_valid()) + if (p_bind_address.is_valid()) { ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + } err = _sock->open(NetSocket::TYPE_TCP, ip_type); @@ -62,7 +61,6 @@ Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { err = _sock->bind(p_bind_address, p_port); if (err != OK) { - _sock->close(); return ERR_ALREADY_IN_USE; } @@ -83,18 +81,17 @@ bool TCP_Server::is_listening() const { } bool TCP_Server::is_connection_available() const { - ERR_FAIL_COND_V(!_sock.is_valid(), false); - if (!_sock->is_open()) + if (!_sock->is_open()) { return false; + } Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); return (err == OK); } Ref<StreamPeerTCP> TCP_Server::take_connection() { - Ref<StreamPeerTCP> conn; if (!is_connection_available()) { return conn; @@ -104,8 +101,9 @@ Ref<StreamPeerTCP> TCP_Server::take_connection() { IP_Address ip; uint16_t port = 0; ns = _sock->accept(ip, port); - if (!ns.is_valid()) + if (!ns.is_valid()) { return conn; + } conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); conn->accept_socket(ns, ip, port); @@ -113,7 +111,6 @@ Ref<StreamPeerTCP> TCP_Server::take_connection() { } void TCP_Server::stop() { - if (_sock.is_valid()) { _sock->close(); } @@ -124,6 +121,5 @@ TCP_Server::TCP_Server() : } TCP_Server::~TCP_Server() { - stop(); } diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index ca52b13ba1..f06ddd2d99 100644 --- a/core/io/tcp_server.h +++ b/core/io/tcp_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -37,7 +37,6 @@ #include "core/io/stream_peer_tcp.h" class TCP_Server : public Reference { - GDCLASS(TCP_Server, Reference); protected: diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index bce5361c76..ce2c3eb1cd 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,76 +31,147 @@ #include "translation_loader_po.h" #include "core/os/file_access.h" -#include "core/translation.h" +#include "core/string/translation.h" +#include "core/string/translation_po.h" RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { - enum Status { - STATUS_NONE, STATUS_READING_ID, STATUS_READING_STRING, + STATUS_READING_CONTEXT, + STATUS_READING_PLURAL, }; Status status = STATUS_NONE; String msg_id; String msg_str; + String msg_context; + Vector<String> msgs_plural; String config; - if (r_error) + if (r_error) { *r_error = ERR_FILE_CORRUPT; + } - Ref<Translation> translation = Ref<Translation>(memnew(Translation)); + Ref<TranslationPO> translation = Ref<TranslationPO>(memnew(TranslationPO)); int line = 1; + int plural_forms = 0; + int plural_index = -1; + bool entered_context = false; bool skip_this = false; bool skip_next = false; bool is_eof = false; + const String path = f->get_path(); while (!is_eof) { - String l = f->get_line().strip_edges(); is_eof = f->eof_reached(); // If we reached last line and it's not a content line, break, otherwise let processing that last loop - if (is_eof && l.empty()) { - - if (status == STATUS_READING_ID) { + if (is_eof && l.is_empty()) { + if (status == STATUS_READING_ID || status == STATUS_READING_CONTEXT || (status == STATUS_READING_PLURAL && plural_index != plural_forms - 1)) { memdelete(f); - ERR_FAIL_V_MSG(RES(), f->get_path() + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading PO file at: " + path + ":" + itos(line)); } else { break; } } - if (l.begins_with("msgid")) { + if (l.begins_with("msgctxt")) { + if (status != STATUS_READING_STRING && status != STATUS_READING_PLURAL) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line)); + } - if (status == STATUS_READING_ID) { + // In PO file, "msgctxt" appears before "msgid". If we encounter a "msgctxt", we add what we have read + // and set "entered_context" to true to prevent adding twice. + if (!skip_this && msg_id != "") { + if (status == STATUS_READING_STRING) { + translation->add_message(msg_id, msg_str, msg_context); + } else if (status == STATUS_READING_PLURAL) { + if (plural_index != plural_forms - 1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + } + translation->add_plural_message(msg_id, msgs_plural, msg_context); + } + } + msg_context = ""; + l = l.substr(7, l.length()).strip_edges(); + status = STATUS_READING_CONTEXT; + entered_context = true; + } + if (l.begins_with("msgid_plural")) { + if (plural_forms == 0) { memdelete(f); - ERR_FAIL_V_MSG(RES(), f->get_path() + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: "); + ERR_FAIL_V_MSG(RES(), "PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: " + path + ":" + itos(line)); + } else if (status != STATUS_READING_ID) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: " + path + ":" + itos(line)); + } + // We don't record the message in "msgid_plural" itself as tr_n(), TTRN(), RTRN() interfaces provide the plural string already. + // We just have to reset variables related to plurals for "msgstr[]" later on. + l = l.substr(12, l.length()).strip_edges(); + plural_index = -1; + msgs_plural.clear(); + msgs_plural.resize(plural_forms); + status = STATUS_READING_PLURAL; + } else if (l.begins_with("msgid")) { + if (status == STATUS_READING_ID) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line)); } if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") + if (!skip_this && !entered_context) { + if (status == STATUS_READING_STRING) { + translation->add_message(msg_id, msg_str, msg_context); + } else if (status == STATUS_READING_PLURAL) { + if (plural_index != plural_forms - 1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + } + translation->add_plural_message(msg_id, msgs_plural, msg_context); + } + } + } else if (config == "") { config = msg_str; + // Record plural rule. + int p_start = config.find("Plural-Forms"); + if (p_start != -1) { + int p_end = config.find("\n", p_start); + translation->set_plural_rule(config.substr(p_start, p_end - p_start)); + plural_forms = translation->get_plural_forms(); + } + } l = l.substr(5, l.length()).strip_edges(); status = STATUS_READING_ID; + // If we did not encounter msgctxt, we reset context to empty to reset it. + if (!entered_context) { + msg_context = ""; + } msg_id = ""; msg_str = ""; skip_this = skip_next; skip_next = false; + entered_context = false; } - if (l.begins_with("msgstr")) { - + if (l.begins_with("msgstr[")) { + if (status != STATUS_READING_PLURAL) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: " + path + ":" + itos(line)); + } + plural_index++; // Increment to add to the next slot in vector msgs_plural. + l = l.substr(9, l.length()).strip_edges(); + } else if (l.begins_with("msgstr")) { if (status != STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), f->get_path() + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: "); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line)); } l = l.substr(6, l.length()).strip_edges(); @@ -112,10 +183,13 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { skip_next = true; } line++; - continue; //nothing to read or comment + continue; // Nothing to read or comment. } - ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), f->get_path() + ":" + itos(line) + " Invalid line '" + l + "' while parsing: "); + if (!l.begins_with("\"") || status == STATUS_NONE) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line)); + } l = l.substr(1, l.length()); // Find final quote, ignoring escaped ones (\"). @@ -137,40 +211,57 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { escape_next = false; } - ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), f->get_path() + ":" + itos(line) + ": Expected '\"' at end of message while parsing file."); + if (end_pos == -1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line)); + } l = l.substr(0, end_pos); l = l.c_unescape(); - if (status == STATUS_READING_ID) + if (status == STATUS_READING_ID) { msg_id += l; - else + } else if (status == STATUS_READING_STRING) { msg_str += l; + } else if (status == STATUS_READING_CONTEXT) { + msg_context += l; + } else if (status == STATUS_READING_PLURAL && plural_index >= 0) { + msgs_plural.write[plural_index] = msgs_plural[plural_index] + l; + } line++; } - f->close(); memdelete(f); + // Add the last set of data from last iteration. if (status == STATUS_READING_STRING) { - if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") + if (!skip_this) { + translation->add_message(msg_id, msg_str, msg_context); + } + } else if (config == "") { config = msg_str; + } + } else if (status == STATUS_READING_PLURAL) { + if (!skip_this && msg_id != "") { + if (plural_index != plural_forms - 1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + } + translation->add_plural_message(msg_id, msgs_plural, msg_context); + } } - ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + f->get_path() + "."); + ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + path + "."); Vector<String> configs = config.split("\n"); for (int i = 0; i < configs.size(); i++) { - String c = configs[i].strip_edges(); int p = c.find(":"); - if (p == -1) + if (p == -1) { continue; + } String prop = c.substr(0, p).strip_edges(); String value = c.substr(p + 1, c.length()).strip_edges(); @@ -179,16 +270,17 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { } } - if (r_error) + if (r_error) { *r_error = OK; + } return translation; } RES TranslationLoaderPO::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) { - - if (r_error) + if (r_error) { *r_error = ERR_CANT_OPEN; + } FileAccess *f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot open file '" + p_path + "'."); @@ -197,21 +289,16 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat } void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("po"); - //p_extensions->push_back("mo"); //mo in the future... } -bool TranslationLoaderPO::handles_type(const String &p_type) const { +bool TranslationLoaderPO::handles_type(const String &p_type) const { return (p_type == "Translation"); } String TranslationLoaderPO::get_resource_type(const String &p_path) const { - - if (p_path.get_extension().to_lower() == "po") + if (p_path.get_extension().to_lower() == "po") { return "Translation"; + } return ""; } - -TranslationLoaderPO::TranslationLoaderPO() { -} diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 137dfd1768..a524972588 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,7 +33,7 @@ #include "core/io/resource_loader.h" #include "core/os/file_access.h" -#include "core/translation.h" +#include "core/string/translation.h" class TranslationLoaderPO : public ResourceFormatLoader { public: @@ -43,7 +43,7 @@ public: virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; - TranslationLoaderPO(); + TranslationLoaderPO() {} }; #endif // TRANSLATION_LOADER_PO_H diff --git a/core/io/udp_server.cpp b/core/io/udp_server.cpp index 16b7863cdd..f56fb431ef 100644 --- a/core/io/udp_server.cpp +++ b/core/io/udp_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,16 +31,62 @@ #include "udp_server.h" void UDPServer::_bind_methods() { - ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &UDPServer::listen, DEFVAL("*")); + ClassDB::bind_method(D_METHOD("poll"), &UDPServer::poll); ClassDB::bind_method(D_METHOD("is_connection_available"), &UDPServer::is_connection_available); ClassDB::bind_method(D_METHOD("is_listening"), &UDPServer::is_listening); ClassDB::bind_method(D_METHOD("take_connection"), &UDPServer::take_connection); ClassDB::bind_method(D_METHOD("stop"), &UDPServer::stop); + ClassDB::bind_method(D_METHOD("set_max_pending_connections", "max_pending_connections"), &UDPServer::set_max_pending_connections); + ClassDB::bind_method(D_METHOD("get_max_pending_connections"), &UDPServer::get_max_pending_connections); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_pending_connections", PROPERTY_HINT_RANGE, "0,256,1"), "set_max_pending_connections", "get_max_pending_connections"); } -Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) { +Error UDPServer::poll() { + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + if (!_sock->is_open()) { + return ERR_UNCONFIGURED; + } + Error err; + int read; + IP_Address ip; + uint16_t port; + while (true) { + err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + if (err != OK) { + if (err == ERR_BUSY) { + break; + } + return FAILED; + } + Peer p; + p.ip = ip; + p.port = port; + List<Peer>::Element *E = peers.find(p); + if (!E) { + E = pending.find(p); + } + if (E) { + E->get().peer->store_packet(ip, port, recv_buffer, read); + } else { + if (pending.size() >= max_pending_connections) { + // Drop connection. + continue; + } + // It's a new peer, add it to the pending list. + Peer peer; + peer.ip = ip; + peer.port = port; + peer.peer = memnew(PacketPeerUDP); + peer.peer->connect_shared_socket(_sock, ip, port, this); + peer.peer->store_packet(ip, port, recv_buffer, read); + pending.push_back(peer); + } + } + return OK; +} +Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) { ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); @@ -48,13 +94,15 @@ Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) { Error err; IP::Type ip_type = IP::TYPE_ANY; - if (p_bind_address.is_valid()) + if (p_bind_address.is_valid()) { ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + } err = _sock->open(NetSocket::TYPE_UDP, ip_type); - if (err != OK) + if (err != OK) { return ERR_CANT_CREATE; + } _sock->set_blocking_enabled(false); _sock->set_reuse_address_enabled(true); @@ -76,37 +124,73 @@ bool UDPServer::is_listening() const { } bool UDPServer::is_connection_available() const { - ERR_FAIL_COND_V(!_sock.is_valid(), false); - if (!_sock->is_open()) + if (!_sock->is_open()) { return false; + } - Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); - return (err == OK); + return pending.size() > 0; } -Ref<PacketPeerUDP> UDPServer::take_connection() { +void UDPServer::set_max_pending_connections(int p_max) { + ERR_FAIL_COND_MSG(p_max < 0, "Max pending connections value must be a positive number (0 means refuse new connections)."); + max_pending_connections = p_max; + while (p_max > pending.size()) { + List<Peer>::Element *E = pending.back(); + if (!E) { + break; + } + memdelete(E->get().peer); + pending.erase(E); + } +} +int UDPServer::get_max_pending_connections() const { + return max_pending_connections; +} + +Ref<PacketPeerUDP> UDPServer::take_connection() { Ref<PacketPeerUDP> conn; if (!is_connection_available()) { return conn; } - conn = Ref<PacketPeerUDP>(memnew(PacketPeerUDP)); - conn->connect_socket(_sock); - _sock = Ref<NetSocket>(NetSocket::create()); - listen(bind_port, bind_address); - return conn; + Peer peer = pending[0]; + pending.pop_front(); + peers.push_back(peer); + return peer.peer; } -void UDPServer::stop() { +void UDPServer::remove_peer(IP_Address p_ip, int p_port) { + Peer peer; + peer.ip = p_ip; + peer.port = p_port; + List<Peer>::Element *E = peers.find(peer); + if (E) { + peers.erase(E); + } +} +void UDPServer::stop() { if (_sock.is_valid()) { _sock->close(); } bind_port = 0; bind_address = IP_Address(); + List<Peer>::Element *E = peers.front(); + while (E) { + E->get().peer->disconnect_shared_socket(); + E = E->next(); + } + E = pending.front(); + while (E) { + E->get().peer->disconnect_shared_socket(); + memdelete(E->get().peer); + E = E->next(); + } + peers.clear(); + pending.clear(); } UDPServer::UDPServer() : @@ -114,6 +198,5 @@ UDPServer::UDPServer() : } UDPServer::~UDPServer() { - stop(); } diff --git a/core/io/udp_server.h b/core/io/udp_server.h index 90bb82b62b..bbd2f951c9 100644 --- a/core/io/udp_server.h +++ b/core/io/udp_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -38,15 +38,40 @@ class UDPServer : public Reference { GDCLASS(UDPServer, Reference); protected: - static void _bind_methods(); - int bind_port; + enum { + PACKET_BUFFER_SIZE = 65536 + }; + + struct Peer { + PacketPeerUDP *peer; + IP_Address ip; + uint16_t port = 0; + + bool operator==(const Peer &p_other) const { + return (ip == p_other.ip && port == p_other.port); + } + }; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + + int bind_port = 0; IP_Address bind_address; + + List<Peer> peers; + List<Peer> pending; + int max_pending_connections = 16; + Ref<NetSocket> _sock; + static void _bind_methods(); + public: + void remove_peer(IP_Address p_ip, int p_port); Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + Error poll(); bool is_listening() const; bool is_connection_available() const; + void set_max_pending_connections(int p_max); + int get_max_pending_connections() const; Ref<PacketPeerUDP> take_connection(); void stop(); diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 9613ad3f10..905be6089d 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,17 +30,19 @@ #include "xml_parser.h" -#include "core/print_string.h" +#include "core/string/print_string.h" //#define DEBUG_XML VARIANT_ENUM_CAST(XMLParser::NodeType); -static bool _equalsn(const CharType *str1, const CharType *str2, int len) { +static bool _equalsn(const char32_t *str1, const char32_t *str2, int len) { int i; - for (i = 0; i < len && str1[i] && str2[i]; ++i) - if (str1[i] != str2[i]) + for (i = 0; i < len && str1[i] && str2[i]; ++i) { + if (str1[i] != str2[i]) { return false; + } + } // if one (or both) of the strings was smaller then they // are only equal if they have the same length @@ -48,12 +50,12 @@ static bool _equalsn(const CharType *str1, const CharType *str2, int len) { } String XMLParser::_replace_special_characters(const String &origstr) { - int pos = origstr.find("&"); int oldPos = 0; - if (pos == -1) + if (pos == -1) { return origstr; + } String newstr; @@ -62,7 +64,7 @@ String XMLParser::_replace_special_characters(const String &origstr) { int specialChar = -1; for (int i = 0; i < (int)special_characters.size(); ++i) { - const CharType *p = &origstr[pos] + 1; + const char32_t *p = &origstr[pos] + 1; if (_equalsn(&special_characters[i][1], p, special_characters[i].length() - 1)) { specialChar = i; @@ -84,8 +86,9 @@ String XMLParser::_replace_special_characters(const String &origstr) { pos = origstr.find("&", pos); } - if (oldPos < origstr.length() - 1) + if (oldPos < origstr.length() - 1) { newstr += (origstr.substr(oldPos, origstr.length() - oldPos)); + } return newstr; } @@ -100,12 +103,15 @@ bool XMLParser::_set_text(char *start, char *end) { // only white space, so that this text won't be reported if (end - start < 3) { char *p = start; - for (; p != end; ++p) - if (!_is_white_space(*p)) + for (; p != end; ++p) { + if (!_is_white_space(*p)) { break; + } + } - if (p == end) + if (p == end) { return false; + } } // set current text to the parsed text, and replace xml special characters @@ -126,8 +132,9 @@ void XMLParser::_parse_closing_xml_element() { ++P; const char *pBeginClose = P; - while (*P != '>') + while (*P != '>') { ++P; + } node_name = String::utf8(pBeginClose, (int)(P - pBeginClose)); #ifdef DEBUG_XML @@ -141,16 +148,17 @@ void XMLParser::_ignore_definition() { char *F = P; // move until end marked with '>' reached - while (*P != '>') + while (*P != '>') { ++P; + } node_name.parse_utf8(F, P - F); ++P; } bool XMLParser::_parse_cdata() { - - if (*(P + 1) != '[') + if (*(P + 1) != '[') { return false; + } node_type = NODE_CDATA; @@ -161,8 +169,9 @@ bool XMLParser::_parse_cdata() { ++count; } - if (!*P) + if (!*P) { return true; + } char *cDataBegin = P; char *cDataEnd = nullptr; @@ -178,10 +187,11 @@ bool XMLParser::_parse_cdata() { ++P; } - if (cDataEnd) + if (cDataEnd) { node_name = String::utf8(cDataBegin, (int)(cDataEnd - cDataBegin)); - else + } else { node_name = ""; + } #ifdef DEBUG_XML print_line("XML CDATA: " + node_name); #endif @@ -190,7 +200,6 @@ bool XMLParser::_parse_cdata() { } void XMLParser::_parse_comment() { - node_type = NODE_COMMENT; P += 1; @@ -200,10 +209,11 @@ void XMLParser::_parse_comment() { // move until end of comment reached while (count) { - if (*P == '>') + if (*P == '>') { --count; - else if (*P == '<') + } else if (*P == '<') { ++count; + } ++P; } @@ -217,7 +227,6 @@ void XMLParser::_parse_comment() { } void XMLParser::_parse_opening_xml_element() { - node_type = NODE_ELEMENT; node_empty = false; attributes.clear(); @@ -226,46 +235,52 @@ void XMLParser::_parse_opening_xml_element() { const char *startName = P; // find end of element - while (*P != '>' && !_is_white_space(*P)) + while (*P != '>' && !_is_white_space(*P)) { ++P; + } const char *endName = P; // find attributes while (*P != '>') { - if (_is_white_space(*P)) + if (_is_white_space(*P)) { ++P; - else { + } else { if (*P != '/') { // we've got an attribute // read the attribute names const char *attributeNameBegin = P; - while (!_is_white_space(*P) && *P != '=') + while (!_is_white_space(*P) && *P != '=') { ++P; + } const char *attributeNameEnd = P; ++P; // read the attribute value // check for quotes and single quotes, thx to murphy - while ((*P != '\"') && (*P != '\'') && *P) + while ((*P != '\"') && (*P != '\'') && *P) { ++P; + } - if (!*P) // malformatted xml file + if (!*P) { // malformatted xml file return; + } const char attributeQuoteChar = *P; ++P; const char *attributeValueBegin = P; - while (*P != attributeQuoteChar && *P) + while (*P != attributeQuoteChar && *P) { ++P; + } - if (!*P) // malformatted xml file + if (!*P) { // malformatted xml file return; + } const char *attributeValueEnd = P; ++P; @@ -304,21 +319,23 @@ void XMLParser::_parse_opening_xml_element() { } void XMLParser::_parse_current_node() { - char *start = P; node_offset = P - data; // more forward until '<' found - while (*P != '<' && *P) + while (*P != '<' && *P) { ++P; + } - if (!*P) + if (!*P) { return; + } if (P - start > 0) { // we found some text, store it - if (_set_text(start, P)) + if (_set_text(start, P)) { return; + } } ++P; @@ -332,8 +349,9 @@ void XMLParser::_parse_current_node() { _ignore_definition(); break; case '!': - if (!_parse_cdata()) + if (!_parse_cdata()) { _parse_comment(); + } break; default: _parse_opening_xml_element(); @@ -342,22 +360,19 @@ void XMLParser::_parse_current_node() { } uint64_t XMLParser::get_node_offset() const { - return node_offset; -}; +} Error XMLParser::seek(uint64_t p_pos) { - ERR_FAIL_COND_V(!data, ERR_FILE_EOF); ERR_FAIL_COND_V(p_pos >= length, ERR_FILE_EOF); P = data + p_pos; return read(); -}; +} void XMLParser::_bind_methods() { - ClassDB::bind_method(D_METHOD("read"), &XMLParser::read); ClassDB::bind_method(D_METHOD("get_node_type"), &XMLParser::get_node_type); ClassDB::bind_method(D_METHOD("get_node_name"), &XMLParser::get_node_name); @@ -383,10 +398,9 @@ void XMLParser::_bind_methods() { BIND_ENUM_CONSTANT(NODE_COMMENT); BIND_ENUM_CONSTANT(NODE_CDATA); BIND_ENUM_CONSTANT(NODE_UNKNOWN); -}; +} Error XMLParser::read() { - // if not end reached, parse the node if (P && (P - data) < (int64_t)length - 1 && *P != 0) { _parse_current_node(); @@ -397,11 +411,10 @@ Error XMLParser::read() { } XMLParser::NodeType XMLParser::get_node_type() { - return node_type; } -String XMLParser::get_node_data() const { +String XMLParser::get_node_data() const { ERR_FAIL_COND_V(node_type != NODE_TEXT, ""); return node_name; } @@ -410,31 +423,32 @@ String XMLParser::get_node_name() const { ERR_FAIL_COND_V(node_type == NODE_TEXT, ""); return node_name; } -int XMLParser::get_attribute_count() const { +int XMLParser::get_attribute_count() const { return attributes.size(); } -String XMLParser::get_attribute_name(int p_idx) const { +String XMLParser::get_attribute_name(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, attributes.size(), ""); return attributes[p_idx].name; } -String XMLParser::get_attribute_value(int p_idx) const { +String XMLParser::get_attribute_value(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, attributes.size(), ""); return attributes[p_idx].value; } -bool XMLParser::has_attribute(const String &p_name) const { +bool XMLParser::has_attribute(const String &p_name) const { for (int i = 0; i < attributes.size(); i++) { - if (attributes[i].name == p_name) + if (attributes[i].name == p_name) { return true; + } } return false; } -String XMLParser::get_attribute_value(const String &p_name) const { +String XMLParser::get_attribute_value(const String &p_name) const { int idx = -1; for (int i = 0; i < attributes.size(); i++) { if (attributes[i].name == p_name) { @@ -449,7 +463,6 @@ String XMLParser::get_attribute_value(const String &p_name) const { } String XMLParser::get_attribute_value_safe(const String &p_name) const { - int idx = -1; for (int i = 0; i < attributes.size(); i++) { if (attributes[i].name == p_name) { @@ -458,17 +471,17 @@ String XMLParser::get_attribute_value_safe(const String &p_name) const { } } - if (idx < 0) + if (idx < 0) { return ""; + } return attributes[idx].value; } -bool XMLParser::is_empty() const { +bool XMLParser::is_empty() const { return node_empty; } Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) { - ERR_FAIL_COND_V(p_buffer.size() == 0, ERR_INVALID_DATA); if (data) { @@ -484,7 +497,6 @@ Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) { } Error XMLParser::open(const String &p_path) { - Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &err); @@ -508,10 +520,10 @@ Error XMLParser::open(const String &p_path) { } void XMLParser::skip_section() { - // skip if this element is empty anyway. - if (is_empty()) + if (is_empty()) { return; + } // read until we've reached the last element in this section int tagcount = 1; @@ -520,15 +532,16 @@ void XMLParser::skip_section() { if (get_node_type() == XMLParser::NODE_ELEMENT && !is_empty()) { ++tagcount; - } else if (get_node_type() == XMLParser::NODE_ELEMENT_END) + } else if (get_node_type() == XMLParser::NODE_ELEMENT_END) { --tagcount; + } } } void XMLParser::close() { - - if (data) + if (data) { memdelete_arr(data); + } data = nullptr; length = 0; P = nullptr; @@ -538,22 +551,19 @@ void XMLParser::close() { } int XMLParser::get_current_line() const { - return 0; } XMLParser::XMLParser() { - - data = nullptr; - close(); special_characters.push_back("&"); special_characters.push_back("<lt;"); special_characters.push_back(">gt;"); special_characters.push_back("\"quot;"); special_characters.push_back("'apos;"); } -XMLParser::~XMLParser() { - if (data) +XMLParser::~XMLParser() { + if (data) { memdelete_arr(data); + } } diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h index 26c3e6802f..01af6a90ad 100644 --- a/core/io/xml_parser.h +++ b/core/io/xml_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,17 +31,16 @@ #ifndef XML_PARSER_H #define XML_PARSER_H +#include "core/object/reference.h" #include "core/os/file_access.h" -#include "core/reference.h" -#include "core/ustring.h" -#include "core/vector.h" +#include "core/string/ustring.h" +#include "core/templates/vector.h" /* Based on irrXML (see their zlib license). Added mainly for compatibility with their Collada loader. */ class XMLParser : public Reference { - GDCLASS(XMLParser, Reference); public: @@ -66,15 +65,15 @@ public: }; private: - char *data; - char *P; - uint64_t length; + char *data = nullptr; + char *P = nullptr; + uint64_t length = 0; void unescape(String &p_str); Vector<String> special_characters; String node_name; - bool node_empty; - NodeType node_type; - uint64_t node_offset; + bool node_empty = false; + NodeType node_type = NODE_NONE; + uint64_t node_offset = 0; struct Attribute { String name; diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index 3a2a207d22..4b4a46e198 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,7 +33,6 @@ #include "core/os/copymem.h" void *zipio_open(void *data, const char *p_fname, int mode) { - FileAccess *&f = *(FileAccess **)data; String fname; @@ -42,42 +41,37 @@ void *zipio_open(void *data, const char *p_fname, int mode) { if (mode & ZLIB_FILEFUNC_MODE_WRITE) { f = FileAccess::open(fname, FileAccess::WRITE); } else { - f = FileAccess::open(fname, FileAccess::READ); } - if (!f) + if (!f) { return nullptr; + } return data; } uLong zipio_read(void *data, void *fdata, void *buf, uLong size) { - FileAccess *f = *(FileAccess **)data; return f->get_buffer((uint8_t *)buf, size); } uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { - FileAccess *f = *(FileAccess **)opaque; f->store_buffer((uint8_t *)buf, size); return size; } long zipio_tell(voidpf opaque, voidpf stream) { - FileAccess *f = *(FileAccess **)opaque; return f->get_position(); } long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - FileAccess *f = *(FileAccess **)opaque; int pos = offset; switch (origin) { - case ZLIB_FILEFUNC_SEEK_CUR: pos = f->get_position() + offset; break; @@ -86,14 +80,13 @@ long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { break; default: break; - }; + } f->seek(pos); return 0; } int zipio_close(voidpf opaque, voidpf stream) { - FileAccess *&f = *(FileAccess **)opaque; if (f) { f->close(); @@ -104,25 +97,21 @@ int zipio_close(voidpf opaque, voidpf stream) { } int zipio_testerror(voidpf opaque, voidpf stream) { - FileAccess *f = *(FileAccess **)opaque; return (f && f->get_error() != OK) ? 1 : 0; } voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { - voidpf ptr = memalloc(items * size); zeromem(ptr, items * size); return ptr; } void zipio_free(voidpf opaque, voidpf address) { - memfree(address); } zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { - zlib_filefunc_def io; io.opaque = p_file; io.zopen_file = zipio_open; diff --git a/core/io/zip_io.h b/core/io/zip_io.h index ba2065b5d1..52691c65e9 100644 --- a/core/io/zip_io.h +++ b/core/io/zip_io.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/make_binders.py b/core/make_binders.py deleted file mode 100644 index 94bee95bfb..0000000000 --- a/core/make_binders.py +++ /dev/null @@ -1,390 +0,0 @@ -# -*- coding: ibm850 -*- - -template_typed = """ -#ifdef TYPED_METHOD_BIND -template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> -class MethodBind$argc$$ifret R$$ifconst C$ : public MethodBind { -public: - - $ifret R$ $ifnoret void$ (T::*method)($arg, P@$) $ifconst const$; -#ifdef DEBUG_METHODS_ENABLED - virtual Variant::Type _gen_argument_type(int p_arg) const { return _get_argument_type(p_arg); } - virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { - $ifret if (p_arg==-1) return GetTypeInfo<R>::METADATA;$ - $arg if (p_arg==(@-1)) return GetTypeInfo<P@>::METADATA; - $ - return GodotTypeInfo::METADATA_NONE; - } - Variant::Type _get_argument_type(int p_argument) const { - $ifret if (p_argument==-1) return (Variant::Type)GetTypeInfo<R>::VARIANT_TYPE;$ - $arg if (p_argument==(@-1)) return (Variant::Type)GetTypeInfo<P@>::VARIANT_TYPE; - $ - return Variant::NIL; - } - virtual PropertyInfo _gen_argument_type_info(int p_argument) const { - $ifret if (p_argument==-1) return GetTypeInfo<R>::get_class_info();$ - $arg if (p_argument==(@-1)) return GetTypeInfo<P@>::get_class_info(); - $ - return PropertyInfo(); - } -#endif - virtual String get_instance_class() const { - return T::get_class_static(); - } - - virtual Variant call(Object* p_object,const Variant** p_args,int p_arg_count, Callable::CallError& r_error) { - - T *instance=Object::cast_to<T>(p_object); - r_error.error=Callable::CallError::CALL_OK; -#ifdef DEBUG_METHODS_ENABLED - - ERR_FAIL_COND_V(!instance,Variant()); - if (p_arg_count>get_argument_count()) { - r_error.error=Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument=get_argument_count(); - return Variant(); - - } - if (p_arg_count<(get_argument_count()-get_default_argument_count())) { - - r_error.error=Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument=get_argument_count()-get_default_argument_count(); - return Variant(); - } - $arg CHECK_ARG(@); - $ -#endif - $ifret Variant ret = $(instance->*method)($arg, _VC(@)$); - $ifret return Variant(ret);$ - $ifnoret return Variant();$ - } - -#ifdef PTRCALL_ENABLED - virtual void ptrcall(Object*p_object,const void** p_args,void *r_ret) { - - T *instance=Object::cast_to<T>(p_object); - $ifret PtrToArg<R>::encode( $ (instance->*method)($arg, PtrToArg<P@>::convert(p_args[@-1])$) $ifret ,r_ret)$ ; - } -#endif - MethodBind$argc$$ifret R$$ifconst C$ () { -#ifdef DEBUG_METHODS_ENABLED - _set_const($ifconst true$$ifnoconst false$); - _generate_argument_types($argc$); -#else - set_argument_count($argc$); -#endif - - $ifret _set_returns(true); $ - }; -}; - -template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> -MethodBind* create_method_bind($ifret R$ $ifnoret void$ (T::*p_method)($arg, P@$) $ifconst const$ ) { - - MethodBind$argc$$ifret R$$ifconst C$<T $ifret ,R$ $ifargs ,$ $arg, P@$> * a = memnew( (MethodBind$argc$$ifret R$$ifconst C$<T $ifret ,R$ $ifargs ,$ $arg, P@$>) ); - a->method=p_method; - return a; -} -#endif -""" - -template = """ -#ifndef TYPED_METHOD_BIND -$iftempl template<$ $ifret class R$ $ifretargs ,$ $arg, class P@$ $iftempl >$ -class MethodBind$argc$$ifret R$$ifconst C$ : public MethodBind { - -public: - - StringName type_name; - $ifret R$ $ifnoret void$ (__UnexistingClass::*method)($arg, P@$) $ifconst const$; - -#ifdef DEBUG_METHODS_ENABLED - virtual Variant::Type _gen_argument_type(int p_arg) const { return _get_argument_type(p_arg); } - virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { - $ifret if (p_arg==-1) return GetTypeInfo<R>::METADATA;$ - $arg if (p_arg==(@-1)) return GetTypeInfo<P@>::METADATA; - $ - return GodotTypeInfo::METADATA_NONE; - } - - Variant::Type _get_argument_type(int p_argument) const { - $ifret if (p_argument==-1) return (Variant::Type)GetTypeInfo<R>::VARIANT_TYPE;$ - $arg if (p_argument==(@-1)) return (Variant::Type)GetTypeInfo<P@>::VARIANT_TYPE; - $ - return Variant::NIL; - } - - virtual PropertyInfo _gen_argument_type_info(int p_argument) const { - $ifret if (p_argument==-1) return GetTypeInfo<R>::get_class_info();$ - $arg if (p_argument==(@-1)) return GetTypeInfo<P@>::get_class_info(); - $ - return PropertyInfo(); - } - -#endif - virtual String get_instance_class() const { - return type_name; - } - - virtual Variant call(Object* p_object,const Variant** p_args,int p_arg_count, Callable::CallError& r_error) { - - __UnexistingClass *instance = (__UnexistingClass*)p_object; - - r_error.error=Callable::CallError::CALL_OK; -#ifdef DEBUG_METHODS_ENABLED - - ERR_FAIL_COND_V(!instance,Variant()); - if (p_arg_count>get_argument_count()) { - r_error.error=Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument=get_argument_count(); - return Variant(); - } - - if (p_arg_count<(get_argument_count()-get_default_argument_count())) { - - r_error.error=Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument=get_argument_count()-get_default_argument_count(); - return Variant(); - } - - $arg CHECK_ARG(@); - $ -#endif - $ifret Variant ret = $(instance->*method)($arg, _VC(@)$); - $ifret return Variant(ret);$ - $ifnoret return Variant();$ - } -#ifdef PTRCALL_ENABLED - virtual void ptrcall(Object*p_object,const void** p_args,void *r_ret) { - __UnexistingClass *instance = (__UnexistingClass*)p_object; - $ifret PtrToArg<R>::encode( $ (instance->*method)($arg, PtrToArg<P@>::convert(p_args[@-1])$) $ifret ,r_ret) $ ; - } -#endif - MethodBind$argc$$ifret R$$ifconst C$ () { -#ifdef DEBUG_METHODS_ENABLED - _set_const($ifconst true$$ifnoconst false$); - _generate_argument_types($argc$); -#else - set_argument_count($argc$); -#endif - $ifret _set_returns(true); $ - - - }; -}; - -template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> -MethodBind* create_method_bind($ifret R$ $ifnoret void$ (T::*p_method)($arg, P@$) $ifconst const$ ) { - - MethodBind$argc$$ifret R$$ifconst C$ $iftempl <$ $ifret R$ $ifretargs ,$ $arg, P@$ $iftempl >$ * a = memnew( (MethodBind$argc$$ifret R$$ifconst C$ $iftempl <$ $ifret R$ $ifretargs ,$ $arg, P@$ $iftempl >$) ); - union { - - $ifret R$ $ifnoret void$ (T::*sm)($arg, P@$) $ifconst const$; - $ifret R$ $ifnoret void$ (__UnexistingClass::*dm)($arg, P@$) $ifconst const$; - } u; - u.sm=p_method; - a->method=u.dm; - a->type_name=T::get_class_static(); - return a; -} -#endif -""" - - -template_typed_free_func = """ -#ifdef TYPED_METHOD_BIND -template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> -class FunctionBind$argc$$ifret R$$ifconst C$ : public MethodBind { -public: - - $ifret R$ $ifnoret void$ (*method) ($ifconst const$ T *$ifargs , $$arg, P@$); -#ifdef DEBUG_METHODS_ENABLED - virtual Variant::Type _gen_argument_type(int p_arg) const { return _get_argument_type(p_arg); } - virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { - $ifret if (p_arg==-1) return GetTypeInfo<R>::METADATA;$ - $arg if (p_arg==(@-1)) return GetTypeInfo<P@>::METADATA; - $ - return GodotTypeInfo::METADATA_NONE; - } - Variant::Type _get_argument_type(int p_argument) const { - $ifret if (p_argument==-1) return (Variant::Type)GetTypeInfo<R>::VARIANT_TYPE;$ - $arg if (p_argument==(@-1)) return (Variant::Type)GetTypeInfo<P@>::VARIANT_TYPE; - $ - return Variant::NIL; - } - virtual PropertyInfo _gen_argument_type_info(int p_argument) const { - $ifret if (p_argument==-1) return GetTypeInfo<R>::get_class_info();$ - $arg if (p_argument==(@-1)) return GetTypeInfo<P@>::get_class_info(); - $ - return PropertyInfo(); - } -#endif - virtual String get_instance_class() const { - return T::get_class_static(); - } - - virtual Variant call(Object* p_object,const Variant** p_args,int p_arg_count, Callable::CallError& r_error) { - - T *instance=Object::cast_to<T>(p_object); - r_error.error=Callable::CallError::CALL_OK; -#ifdef DEBUG_METHODS_ENABLED - - ERR_FAIL_COND_V(!instance,Variant()); - if (p_arg_count>get_argument_count()) { - r_error.error=Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument=get_argument_count(); - return Variant(); - - } - if (p_arg_count<(get_argument_count()-get_default_argument_count())) { - - r_error.error=Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument=get_argument_count()-get_default_argument_count(); - return Variant(); - } - $arg CHECK_ARG(@); - $ -#endif - $ifret Variant ret = $(method)(instance$ifargs , $$arg, _VC(@)$); - $ifret return Variant(ret);$ - $ifnoret return Variant();$ - } - -#ifdef PTRCALL_ENABLED - virtual void ptrcall(Object*p_object,const void** p_args,void *r_ret) { - - T *instance=Object::cast_to<T>(p_object); - $ifret PtrToArg<R>::encode( $ (method)(instance$ifargs , $$arg, PtrToArg<P@>::convert(p_args[@-1])$) $ifret ,r_ret)$ ; - } -#endif - FunctionBind$argc$$ifret R$$ifconst C$ () { -#ifdef DEBUG_METHODS_ENABLED - _set_const($ifconst true$$ifnoconst false$); - _generate_argument_types($argc$); -#else - set_argument_count($argc$); -#endif - - $ifret _set_returns(true); $ - }; -}; - -template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> -MethodBind* create_method_bind($ifret R$ $ifnoret void$ (*p_method)($ifconst const$ T *$ifargs , $$arg, P@$) ) { - - FunctionBind$argc$$ifret R$$ifconst C$<T $ifret ,R$ $ifargs ,$ $arg, P@$> * a = memnew( (FunctionBind$argc$$ifret R$$ifconst C$<T $ifret ,R$ $ifargs ,$ $arg, P@$>) ); - a->method=p_method; - return a; -} -#endif -""" - - -def make_version(template, nargs, argmax, const, ret): - - intext = template - from_pos = 0 - outtext = "" - - while True: - to_pos = intext.find("$", from_pos) - if to_pos == -1: - outtext += intext[from_pos:] - break - else: - outtext += intext[from_pos:to_pos] - end = intext.find("$", to_pos + 1) - if end == -1: - break # ignore - macro = intext[to_pos + 1 : end] - cmd = "" - data = "" - - if macro.find(" ") != -1: - cmd = macro[0 : macro.find(" ")] - data = macro[macro.find(" ") + 1 :] - else: - cmd = macro - - if cmd == "argc": - outtext += str(nargs) - if cmd == "ifret" and ret: - outtext += data - if cmd == "ifargs" and nargs: - outtext += data - if cmd == "ifretargs" and nargs and ret: - outtext += data - if cmd == "ifconst" and const: - outtext += data - elif cmd == "ifnoconst" and not const: - outtext += data - elif cmd == "ifnoret" and not ret: - outtext += data - elif cmd == "iftempl" and (nargs > 0 or ret): - outtext += data - elif cmd == "arg,": - for i in range(1, nargs + 1): - if i > 1: - outtext += ", " - outtext += data.replace("@", str(i)) - elif cmd == "arg": - for i in range(1, nargs + 1): - outtext += data.replace("@", str(i)) - elif cmd == "noarg": - for i in range(nargs + 1, argmax + 1): - outtext += data.replace("@", str(i)) - - from_pos = end + 1 - - return outtext - - -def run(target, source, env): - - versions = 15 - versions_ext = 6 - text = "" - text_ext = "" - text_free_func = "#ifndef METHOD_BIND_FREE_FUNC_H\n#define METHOD_BIND_FREE_FUNC_H\n" - text_free_func += "\n//including this header file allows method binding to use free functions\n" - text_free_func += ( - "//note that the free function must have a pointer to an instance of the class as its first parameter\n" - ) - - for i in range(0, versions + 1): - - t = "" - t += make_version(template, i, versions, False, False) - t += make_version(template_typed, i, versions, False, False) - t += make_version(template, i, versions, False, True) - t += make_version(template_typed, i, versions, False, True) - t += make_version(template, i, versions, True, False) - t += make_version(template_typed, i, versions, True, False) - t += make_version(template, i, versions, True, True) - t += make_version(template_typed, i, versions, True, True) - if i >= versions_ext: - text_ext += t - else: - text += t - - text_free_func += make_version(template_typed_free_func, i, versions, False, False) - text_free_func += make_version(template_typed_free_func, i, versions, False, True) - text_free_func += make_version(template_typed_free_func, i, versions, True, False) - text_free_func += make_version(template_typed_free_func, i, versions, True, True) - - text_free_func += "#endif" - - with open(target[0], "w") as f: - f.write(text) - - with open(target[1], "w") as f: - f.write(text_ext) - - with open(target[2], "w") as f: - f.write(text_free_func) - - -if __name__ == "__main__": - from platform_methods import subprocess_main - - subprocess_main(globals()) diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 3e3e6c50a7..88e11a630c 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,13 +30,12 @@ #include "a_star.h" -#include "core/math/geometry.h" -#include "core/script_language.h" +#include "core/math/geometry_3d.h" +#include "core/object/script_language.h" #include "scene/scene_string_names.h" int AStar::get_available_point_id() const { - - if (points.empty()) { + if (points.is_empty()) { return 1; } @@ -54,7 +53,6 @@ int AStar::get_available_point_id() const { } void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { - ERR_FAIL_COND(p_id < 0); ERR_FAIL_COND(p_weight_scale < 1); @@ -78,7 +76,6 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { } Vector3 AStar::get_point_position(int p_id) const { - Point *p; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_V(!p_exists, Vector3()); @@ -87,7 +84,6 @@ Vector3 AStar::get_point_position(int p_id) const { } void AStar::set_point_position(int p_id, const Vector3 &p_pos) { - Point *p; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND(!p_exists); @@ -96,7 +92,6 @@ void AStar::set_point_position(int p_id, const Vector3 &p_pos) { } real_t AStar::get_point_weight_scale(int p_id) const { - Point *p; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_V(!p_exists, 0); @@ -105,7 +100,6 @@ real_t AStar::get_point_weight_scale(int p_id) const { } void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) { - Point *p; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND(!p_exists); @@ -115,13 +109,11 @@ void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) { } void AStar::remove_point(int p_id) { - Point *p; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND(!p_exists); for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { - Segment s(p_id, (*it.key)); segments.erase(s); @@ -130,7 +122,6 @@ void AStar::remove_point(int p_id) { } for (OAHashMap<int, Point *>::Iterator it = p->unlinked_neighbours.iter(); it.valid; it = p->unlinked_neighbours.next_iter(it)) { - Segment s(p_id, (*it.key)); segments.erase(s); @@ -144,7 +135,6 @@ void AStar::remove_point(int p_id) { } void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { - ERR_FAIL_COND(p_id == p_with_id); Point *a; @@ -164,7 +154,9 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { } Segment s(p_id, p_with_id); - if (bidirectional) s.direction = Segment::BIDIRECTIONAL; + if (bidirectional) { + s.direction = Segment::BIDIRECTIONAL; + } Set<Segment>::Element *element = segments.find(s); if (element != nullptr) { @@ -181,7 +173,6 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { } void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) { - Point *a; bool a_exists = points.lookup(p_id, a); ERR_FAIL_COND(!a_exists); @@ -207,25 +198,25 @@ void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) { b->unlinked_neighbours.remove(a->id); } } else { - if (s.direction == Segment::NONE) + if (s.direction == Segment::NONE) { b->unlinked_neighbours.remove(a->id); - else + } else { a->unlinked_neighbours.set(b->id, b); + } } segments.erase(element); - if (s.direction != Segment::NONE) + if (s.direction != Segment::NONE) { segments.insert(s); + } } } bool AStar::has_point(int p_id) const { - return points.has(p_id); } Array AStar::get_points() { - Array point_list; for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { @@ -236,7 +227,6 @@ Array AStar::get_points() { } Vector<int> AStar::get_point_connections(int p_id) { - Point *p; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_V(!p_exists, Vector<int>()); @@ -251,7 +241,6 @@ Vector<int> AStar::get_point_connections(int p_id) { } bool AStar::are_points_connected(int p_id, int p_with_id, bool bidirectional) const { - Segment s(p_id, p_with_id); const Set<Segment>::Element *element = segments.find(s); @@ -260,7 +249,6 @@ bool AStar::are_points_connected(int p_id, int p_with_id, bool bidirectional) co } void AStar::clear() { - last_free_id = 0; for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { memdelete(*(it.value)); @@ -284,18 +272,24 @@ void AStar::reserve_space(int p_num_nodes) { } int AStar::get_closest_point(const Vector3 &p_point, bool p_include_disabled) const { - int closest_id = -1; real_t closest_dist = 1e20; for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { + if (!p_include_disabled && !(*it.value)->enabled) { + continue; // Disabled points should not be considered. + } - if (!p_include_disabled && !(*it.value)->enabled) continue; // Disabled points should not be considered. - + // Keep the closest point's ID, and in case of multiple closest IDs, + // the smallest one (makes it deterministic). real_t d = p_point.distance_squared_to((*it.value)->pos); - if (closest_id < 0 || d < closest_dist) { + int id = *(it.key); + if (d <= closest_dist) { + if (d == closest_dist && id > closest_id) { // Keep lowest ID. + continue; + } closest_dist = d; - closest_id = *(it.key); + closest_id = id; } } @@ -303,13 +297,10 @@ int AStar::get_closest_point(const Vector3 &p_point, bool p_include_disabled) co } Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const { - - bool found = false; real_t closest_dist = 1e20; Vector3 closest_point; for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) { - Point *from_point = nullptr, *to_point = nullptr; points.lookup(E->get().u, from_point); points.lookup(E->get().v, to_point); @@ -323,13 +314,11 @@ 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) { - + if (d < closest_dist) { closest_point = p; closest_dist = d; - found = true; } } @@ -337,10 +326,11 @@ Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const { } bool AStar::_solve(Point *begin_point, Point *end_point) { - pass++; - if (!end_point->enabled) return false; + if (!end_point->enabled) { + return false; + } bool found_route = false; @@ -351,8 +341,7 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); open_list.push_back(begin_point); - while (!open_list.empty()) { - + while (!open_list.is_empty()) { Point *p = open_list[0]; // The currently processed point if (p == end_point) { @@ -365,7 +354,6 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { p->closed_pass = pass; // Mark the point as closed for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { - Point *e = *(it.value); // The neighbour point if (!e->enabled || e->closed_pass == pass) { @@ -400,9 +388,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { } real_t AStar::_estimate_cost(int p_from_id, int p_to_id) { - - if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) + if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) { return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id); + } Point *from_point; bool from_exists = points.lookup(p_from_id, from_point); @@ -416,9 +404,9 @@ real_t AStar::_estimate_cost(int p_from_id, int p_to_id) { } real_t AStar::_compute_cost(int p_from_id, int p_to_id) { - - if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) + if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) { return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id); + } Point *from_point; bool from_exists = points.lookup(p_from_id, from_point); @@ -432,7 +420,6 @@ real_t AStar::_compute_cost(int p_from_id, int p_to_id) { } Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { - Point *a; bool from_exists = points.lookup(p_from_id, a); ERR_FAIL_COND_V(!from_exists, Vector<Vector3>()); @@ -451,7 +438,9 @@ Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { Point *end_point = b; bool found_route = _solve(begin_point, end_point); - if (!found_route) return Vector<Vector3>(); + if (!found_route) { + return Vector<Vector3>(); + } Point *p = end_point; int pc = 1; // Begin point @@ -480,7 +469,6 @@ Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { } Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) { - Point *a; bool from_exists = points.lookup(p_from_id, a); ERR_FAIL_COND_V(!from_exists, Vector<int>()); @@ -499,7 +487,9 @@ Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) { Point *end_point = b; bool found_route = _solve(begin_point, end_point); - if (!found_route) return Vector<int>(); + if (!found_route) { + return Vector<int>(); + } Point *p = end_point; int pc = 1; // Begin point @@ -528,7 +518,6 @@ Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) { } void AStar::set_point_disabled(int p_id, bool p_disabled) { - Point *p; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND(!p_exists); @@ -537,7 +526,6 @@ void AStar::set_point_disabled(int p_id, bool p_disabled) { } bool AStar::is_point_disabled(int p_id) const { - Point *p; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_V(!p_exists, false); @@ -546,7 +534,6 @@ bool AStar::is_point_disabled(int p_id) const { } void AStar::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_available_point_id"), &AStar::get_available_point_id); ClassDB::bind_method(D_METHOD("add_point", "id", "position", "weight_scale"), &AStar::add_point, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStar::get_point_position); @@ -580,11 +567,6 @@ void AStar::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::FLOAT, "_compute_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id"))); } -AStar::AStar() { - last_free_id = 0; - pass = 1; -} - AStar::~AStar() { clear(); } @@ -678,9 +660,9 @@ Vector2 AStar2D::get_closest_position_in_segment(const Vector2 &p_point) const { } real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) { - - if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) + if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) { return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id); + } AStar::Point *from_point; bool from_exists = astar.points.lookup(p_from_id, from_point); @@ -694,9 +676,9 @@ real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) { } real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) { - - if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) + if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) { return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id); + } AStar::Point *from_point; bool from_exists = astar.points.lookup(p_from_id, from_point); @@ -710,7 +692,6 @@ real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) { } Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) { - AStar::Point *a; bool from_exists = astar.points.lookup(p_from_id, a); ERR_FAIL_COND_V(!from_exists, Vector<Vector2>()); @@ -729,7 +710,9 @@ Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) { AStar::Point *end_point = b; bool found_route = _solve(begin_point, end_point); - if (!found_route) return Vector<Vector2>(); + if (!found_route) { + return Vector<Vector2>(); + } AStar::Point *p = end_point; int pc = 1; // Begin point @@ -758,7 +741,6 @@ Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) { } Vector<int> AStar2D::get_id_path(int p_from_id, int p_to_id) { - AStar::Point *a; bool from_exists = astar.points.lookup(p_from_id, a); ERR_FAIL_COND_V(!from_exists, Vector<int>()); @@ -777,7 +759,9 @@ Vector<int> AStar2D::get_id_path(int p_from_id, int p_to_id) { AStar::Point *end_point = b; bool found_route = _solve(begin_point, end_point); - if (!found_route) return Vector<int>(); + if (!found_route) { + return Vector<int>(); + } AStar::Point *p = end_point; int pc = 1; // Begin point @@ -806,10 +790,11 @@ Vector<int> AStar2D::get_id_path(int p_from_id, int p_to_id) { } bool AStar2D::_solve(AStar::Point *begin_point, AStar::Point *end_point) { - astar.pass++; - if (!end_point->enabled) return false; + if (!end_point->enabled) { + return false; + } bool found_route = false; @@ -820,8 +805,7 @@ bool AStar2D::_solve(AStar::Point *begin_point, AStar::Point *end_point) { begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); open_list.push_back(begin_point); - while (!open_list.empty()) { - + while (!open_list.is_empty()) { AStar::Point *p = open_list[0]; // The currently processed point if (p == end_point) { @@ -834,7 +818,6 @@ bool AStar2D::_solve(AStar::Point *begin_point, AStar::Point *end_point) { p->closed_pass = astar.pass; // Mark the point as closed for (OAHashMap<int, AStar::Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { - AStar::Point *e = *(it.value); // The neighbour point if (!e->enabled || e->closed_pass == astar.pass) { @@ -869,7 +852,6 @@ bool AStar2D::_solve(AStar::Point *begin_point, AStar::Point *end_point) { } void AStar2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_available_point_id"), &AStar2D::get_available_point_id); ClassDB::bind_method(D_METHOD("add_point", "id", "position", "weight_scale"), &AStar2D::add_point, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStar2D::get_point_position); @@ -902,9 +884,3 @@ void AStar2D::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::FLOAT, "_estimate_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id"))); BIND_VMETHOD(MethodInfo(Variant::FLOAT, "_compute_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id"))); } - -AStar2D::AStar2D() { -} - -AStar2D::~AStar2D() { -} diff --git a/core/math/a_star.h b/core/math/a_star.h index 8c10ace33c..4c61abd91c 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,8 +31,8 @@ #ifndef A_STAR_H #define A_STAR_H -#include "core/oa_hash_map.h" -#include "core/reference.h" +#include "core/object/reference.h" +#include "core/templates/oa_hash_map.h" /** A* pathfinding algorithm @@ -41,30 +41,26 @@ */ class AStar : public Reference { - GDCLASS(AStar, Reference); friend class AStar2D; struct Point { + Point() {} - Point() : - neighbours(4u), - unlinked_neighbours(4u) {} - - int id; + int id = 0; Vector3 pos; - real_t weight_scale; - bool enabled; + real_t weight_scale = 0; + bool enabled = false; - OAHashMap<int, Point *> neighbours; - OAHashMap<int, Point *> unlinked_neighbours; + OAHashMap<int, Point *> neighbours = 4u; + OAHashMap<int, Point *> unlinked_neighbours = 4u; // Used for pathfinding. - Point *prev_point; - real_t g_score; - real_t f_score; - uint64_t open_pass; - uint64_t closed_pass; + Point *prev_point = nullptr; + real_t g_score = 0; + real_t f_score = 0; + uint64_t open_pass = 0; + uint64_t closed_pass = 0; }; struct SortPoints { @@ -85,7 +81,7 @@ class AStar : public Reference { int32_t u; int32_t v; }; - uint64_t key; + uint64_t key = 0; }; enum { @@ -94,13 +90,11 @@ class AStar : public Reference { BACKWARD = 2, BIDIRECTIONAL = FORWARD | BACKWARD }; - unsigned char direction; + unsigned char direction = NONE; bool operator<(const Segment &p_s) const { return key < p_s.key; } - Segment() { - key = 0; - direction = NONE; - } + + Segment() {} Segment(int p_from, int p_to) { if (p_from < p_to) { u = p_from; @@ -114,8 +108,8 @@ class AStar : public Reference { } }; - int last_free_id; - uint64_t pass; + int last_free_id = 0; + uint64_t pass = 1; OAHashMap<int, Point *> points; Set<Segment> segments; @@ -159,7 +153,7 @@ public: Vector<Vector3> get_point_path(int p_from_id, int p_to_id); Vector<int> get_id_path(int p_from_id, int p_to_id); - AStar(); + AStar() {} ~AStar(); }; @@ -206,8 +200,8 @@ public: Vector<Vector2> get_point_path(int p_from_id, int p_to_id); Vector<int> get_id_path(int p_from_id, int p_to_id); - AStar2D(); - ~AStar2D(); + AStar2D() {} + ~AStar2D() {} }; #endif // A_STAR_H diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index 19d60fea72..2c721997d8 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,24 +30,22 @@ #include "aabb.h" -#include "core/print_string.h" +#include "core/string/print_string.h" +#include "core/variant/variant.h" real_t AABB::get_area() const { - return size.x * size.y * size.z; } bool AABB::operator==(const AABB &p_rval) const { - return ((position == p_rval.position) && (size == p_rval.size)); } -bool AABB::operator!=(const AABB &p_rval) const { +bool AABB::operator!=(const AABB &p_rval) const { return ((position != p_rval.position) || (size != p_rval.size)); } void AABB::merge_with(const AABB &p_aabb) { - Vector3 beg_1, beg_2; Vector3 end_1, end_2; Vector3 min, max; @@ -70,12 +68,10 @@ void AABB::merge_with(const AABB &p_aabb) { } bool AABB::is_equal_approx(const AABB &p_aabb) const { - return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size); } AABB AABB::intersection(const AABB &p_aabb) const { - Vector3 src_min = position; Vector3 src_max = position + size; Vector3 dst_min = p_aabb.position; @@ -83,26 +79,23 @@ AABB AABB::intersection(const AABB &p_aabb) const { Vector3 min, max; - if (src_min.x > dst_max.x || src_max.x < dst_min.x) + if (src_min.x > dst_max.x || src_max.x < dst_min.x) { return AABB(); - else { - + } else { min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x; max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x; } - if (src_min.y > dst_max.y || src_max.y < dst_min.y) + if (src_min.y > dst_max.y || src_max.y < dst_min.y) { return AABB(); - else { - + } else { min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y; max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y; } - if (src_min.z > dst_max.z || src_max.z < dst_min.z) + if (src_min.z > dst_max.z || src_max.z < dst_min.z) { return AABB(); - else { - + } else { min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z; max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z; } @@ -111,7 +104,6 @@ AABB AABB::intersection(const AABB &p_aabb) const { } bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip, Vector3 *r_normal) const { - Vector3 c1, c2; Vector3 end = position + size; real_t near = -1e20; @@ -143,8 +135,9 @@ bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 * } } - if (r_clip) + if (r_clip) { *r_clip = c1; + } if (r_normal) { *r_normal = Vector3(); (*r_normal)[axis] = p_dir[axis] ? -1 : 1; @@ -154,7 +147,6 @@ bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 * } bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip, Vector3 *r_normal) const { - real_t min = 0, max = 1; int axis = 0; real_t sign = 0; @@ -168,18 +160,18 @@ bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector real_t csign; if (seg_from < seg_to) { - - if (seg_from > box_end || seg_to < box_begin) + if (seg_from > box_end || seg_to < box_begin) { return false; + } real_t length = seg_to - seg_from; cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; csign = -1.0; } else { - - if (seg_to > box_end || seg_from < box_begin) + if (seg_to > box_end || seg_from < box_begin) { return false; + } real_t length = seg_to - seg_from; cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; @@ -191,10 +183,12 @@ bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector axis = i; sign = csign; } - if (cmax < max) + if (cmax < max) { max = cmax; - if (max < min) + } + if (max < min) { return false; + } } Vector3 rel = p_to - p_from; @@ -205,14 +199,14 @@ bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector *r_normal = normal; } - if (r_clip) + if (r_clip) { *r_clip = p_from + rel * min; + } return true; } bool AABB::intersects_plane(const Plane &p_plane) const { - Vector3 points[8] = { Vector3(position.x, position.y, position.z), Vector3(position.x, position.y, position.z + size.z), @@ -228,18 +222,17 @@ bool AABB::intersects_plane(const Plane &p_plane) const { bool under = false; for (int i = 0; i < 8; i++) { - - if (p_plane.distance_to(points[i]) > 0) + if (p_plane.distance_to(points[i]) > 0) { over = true; - else + } else { under = true; + } } return under && over; } Vector3 AABB::get_longest_axis() const { - Vector3 axis(1, 0, 0); real_t max_size = size.x; @@ -254,8 +247,8 @@ Vector3 AABB::get_longest_axis() const { return axis; } -int AABB::get_longest_axis_index() const { +int AABB::get_longest_axis_index() const { int axis = 0; real_t max_size = size.x; @@ -272,7 +265,6 @@ int AABB::get_longest_axis_index() const { } Vector3 AABB::get_shortest_axis() const { - Vector3 axis(1, 0, 0); real_t max_size = size.x; @@ -287,8 +279,8 @@ Vector3 AABB::get_shortest_axis() const { return axis; } -int AABB::get_shortest_axis_index() const { +int AABB::get_shortest_axis_index() const { int axis = 0; real_t max_size = size.x; @@ -305,35 +297,31 @@ int AABB::get_shortest_axis_index() const { } AABB AABB::merge(const AABB &p_with) const { - AABB aabb = *this; aabb.merge_with(p_with); return aabb; } + AABB AABB::expand(const Vector3 &p_vector) const { AABB aabb = *this; aabb.expand_to(p_vector); return aabb; } -AABB AABB::grow(real_t p_by) const { +AABB AABB::grow(real_t p_by) const { AABB aabb = *this; aabb.grow_by(p_by); return aabb; } void AABB::get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const { - ERR_FAIL_INDEX(p_edge, 12); switch (p_edge) { - case 0: { - r_from = Vector3(position.x + size.x, position.y, position.z); r_to = Vector3(position.x, position.y, position.z); } break; case 1: { - r_from = Vector3(position.x + size.x, position.y, position.z + size.z); r_to = Vector3(position.x + size.x, position.y, position.z); } break; @@ -343,18 +331,15 @@ void AABB::get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const { } break; case 3: { - r_from = Vector3(position.x, position.y, position.z); r_to = Vector3(position.x, position.y, position.z + size.z); } break; case 4: { - r_from = Vector3(position.x, position.y + size.y, position.z); r_to = Vector3(position.x + size.x, position.y + size.y, position.z); } break; case 5: { - r_from = Vector3(position.x + size.x, position.y + size.y, position.z); r_to = Vector3(position.x + size.x, position.y + size.y, position.z + size.z); } break; @@ -364,31 +349,26 @@ void AABB::get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const { } break; case 7: { - r_from = Vector3(position.x, position.y + size.y, position.z + size.z); r_to = Vector3(position.x, position.y + size.y, position.z); } break; case 8: { - r_from = Vector3(position.x, position.y, position.z + size.z); r_to = Vector3(position.x, position.y + size.y, position.z + size.z); } break; case 9: { - r_from = Vector3(position.x, position.y, position.z); r_to = Vector3(position.x, position.y + size.y, position.z); } break; case 10: { - r_from = Vector3(position.x + size.x, position.y, position.z); r_to = Vector3(position.x + size.x, position.y + size.y, position.z); } break; case 11: { - r_from = Vector3(position.x + size.x, position.y, position.z + size.z); r_to = Vector3(position.x + size.x, position.y + size.y, position.z + size.z); @@ -396,7 +376,21 @@ void AABB::get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const { } } -AABB::operator String() const { +Variant AABB::intersects_segment_bind(const Vector3 &p_from, const Vector3 &p_to) const { + Vector3 inters; + if (intersects_segment(p_from, p_to, &inters)) { + return inters; + } + return Variant(); +} +Variant AABB::intersects_ray_bind(const Vector3 &p_from, const Vector3 &p_dir) const { + Vector3 inters; + if (intersects_ray(p_from, p_dir, &inters)) { + return inters; + } + return Variant(); +} +AABB::operator String() const { return String() + position + " - " + size; } diff --git a/core/math/aabb.h b/core/math/aabb.h index 7fdad07c89..2861358e32 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -39,6 +39,7 @@ * AABB / AABB (Axis Aligned Bounding Box) * This is implemented by a point (position) and the box size */ +class Variant; class AABB { public: @@ -47,12 +48,10 @@ public: real_t get_area() const; /// get area _FORCE_INLINE_ bool has_no_area() const { - return (size.x <= 0 || size.y <= 0 || size.z <= 0); } _FORCE_INLINE_ bool has_no_surface() const { - return (size.x <= 0 && size.y <= 0 && size.z <= 0); } @@ -101,6 +100,24 @@ public: _FORCE_INLINE_ void project_range_in_plane(const Plane &p_plane, real_t &r_min, real_t &r_max) const; _FORCE_INLINE_ void expand_to(const Vector3 &p_vector); /** expand to contain a point if necessary */ + _FORCE_INLINE_ AABB abs() const { + return AABB(Vector3(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0), position.z + MIN(size.z, 0)), size.abs()); + } + + Variant intersects_segment_bind(const Vector3 &p_from, const Vector3 &p_to) const; + Variant intersects_ray_bind(const Vector3 &p_from, const Vector3 &p_dir) const; + + _FORCE_INLINE_ void quantize(float p_unit); + _FORCE_INLINE_ AABB quantized(float p_unit) const; + + _FORCE_INLINE_ void set_end(const Vector3 &p_end) { + size = p_end - position; + } + + _FORCE_INLINE_ Vector3 get_end() const { + return position + size; + } + operator String() const; _FORCE_INLINE_ AABB() {} @@ -111,43 +128,52 @@ public: }; inline bool AABB::intersects(const AABB &p_aabb) const { - - if (position.x >= (p_aabb.position.x + p_aabb.size.x)) + if (position.x >= (p_aabb.position.x + p_aabb.size.x)) { return false; - if ((position.x + size.x) <= p_aabb.position.x) + } + if ((position.x + size.x) <= p_aabb.position.x) { return false; - if (position.y >= (p_aabb.position.y + p_aabb.size.y)) + } + if (position.y >= (p_aabb.position.y + p_aabb.size.y)) { return false; - if ((position.y + size.y) <= p_aabb.position.y) + } + if ((position.y + size.y) <= p_aabb.position.y) { return false; - if (position.z >= (p_aabb.position.z + p_aabb.size.z)) + } + if (position.z >= (p_aabb.position.z + p_aabb.size.z)) { return false; - if ((position.z + size.z) <= p_aabb.position.z) + } + if ((position.z + size.z) <= p_aabb.position.z) { return false; + } return true; } inline bool AABB::intersects_inclusive(const AABB &p_aabb) const { - - if (position.x > (p_aabb.position.x + p_aabb.size.x)) + if (position.x > (p_aabb.position.x + p_aabb.size.x)) { return false; - if ((position.x + size.x) < p_aabb.position.x) + } + if ((position.x + size.x) < p_aabb.position.x) { return false; - if (position.y > (p_aabb.position.y + p_aabb.size.y)) + } + if (position.y > (p_aabb.position.y + p_aabb.size.y)) { return false; - if ((position.y + size.y) < p_aabb.position.y) + } + if ((position.y + size.y) < p_aabb.position.y) { return false; - if (position.z > (p_aabb.position.z + p_aabb.size.z)) + } + if (position.z > (p_aabb.position.z + p_aabb.size.z)) { return false; - if ((position.z + size.z) < p_aabb.position.z) + } + if ((position.z + size.z) < p_aabb.position.z) { return false; + } return true; } inline bool AABB::encloses(const AABB &p_aabb) const { - Vector3 src_min = position; Vector3 src_max = position + size; Vector3 dst_min = p_aabb.position; @@ -163,35 +189,40 @@ inline bool AABB::encloses(const AABB &p_aabb) const { } Vector3 AABB::get_support(const Vector3 &p_normal) const { - Vector3 half_extents = size * 0.5; Vector3 ofs = position + half_extents; return Vector3( - (p_normal.x > 0) ? -half_extents.x : half_extents.x, - (p_normal.y > 0) ? -half_extents.y : half_extents.y, - (p_normal.z > 0) ? -half_extents.z : half_extents.z) + + (p_normal.x > 0) ? half_extents.x : -half_extents.x, + (p_normal.y > 0) ? half_extents.y : -half_extents.y, + (p_normal.z > 0) ? half_extents.z : -half_extents.z) + ofs; } Vector3 AABB::get_endpoint(int p_point) const { - switch (p_point) { - case 0: return Vector3(position.x, position.y, position.z); - case 1: return Vector3(position.x, position.y, position.z + size.z); - case 2: return Vector3(position.x, position.y + size.y, position.z); - case 3: return Vector3(position.x, position.y + size.y, position.z + size.z); - case 4: return Vector3(position.x + size.x, position.y, position.z); - case 5: return Vector3(position.x + size.x, position.y, position.z + size.z); - case 6: return Vector3(position.x + size.x, position.y + size.y, position.z); - case 7: return Vector3(position.x + size.x, position.y + size.y, position.z + size.z); - }; + case 0: + return Vector3(position.x, position.y, position.z); + case 1: + return Vector3(position.x, position.y, position.z + size.z); + case 2: + return Vector3(position.x, position.y + size.y, position.z); + case 3: + return Vector3(position.x, position.y + size.y, position.z + size.z); + case 4: + return Vector3(position.x + size.x, position.y, position.z); + case 5: + return Vector3(position.x + size.x, position.y, position.z + size.z); + case 6: + return Vector3(position.x + size.x, position.y + size.y, position.z); + case 7: + return Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + } ERR_FAIL_V(Vector3()); } bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const { - Vector3 half_extents = size * 0.5; Vector3 ofs = position + half_extents; @@ -202,8 +233,9 @@ bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count, con (p.normal.y > 0) ? -half_extents.y : half_extents.y, (p.normal.z > 0) ? -half_extents.z : half_extents.z); point += ofs; - if (p.is_point_over(point)) + if (p.is_point_over(point)) { return false; + } } // Make sure all points in the shape aren't fully separated from the AABB on @@ -212,7 +244,6 @@ bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count, con int bad_point_counts_negative[3] = { 0 }; for (int k = 0; k < 3; k++) { - for (int i = 0; i < p_point_count; i++) { if (p_points[i].coord[k] > ofs.coord[k] + half_extents.coord[k]) { bad_point_counts_positive[k]++; @@ -234,7 +265,6 @@ bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count, con } bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const { - Vector3 half_extents = size * 0.5; Vector3 ofs = position + half_extents; @@ -245,56 +275,66 @@ bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const { (p.normal.y < 0) ? -half_extents.y : half_extents.y, (p.normal.z < 0) ? -half_extents.z : half_extents.z); point += ofs; - if (p.is_point_over(point)) + if (p.is_point_over(point)) { return false; + } } return true; } bool AABB::has_point(const Vector3 &p_point) const { - - if (p_point.x < position.x) + if (p_point.x < position.x) { return false; - if (p_point.y < position.y) + } + if (p_point.y < position.y) { return false; - if (p_point.z < position.z) + } + if (p_point.z < position.z) { return false; - if (p_point.x > position.x + size.x) + } + if (p_point.x > position.x + size.x) { return false; - if (p_point.y > position.y + size.y) + } + if (p_point.y > position.y + size.y) { return false; - if (p_point.z > position.z + size.z) + } + if (p_point.z > position.z + size.z) { return false; + } return true; } inline void AABB::expand_to(const Vector3 &p_vector) { - Vector3 begin = position; Vector3 end = position + size; - if (p_vector.x < begin.x) + if (p_vector.x < begin.x) { begin.x = p_vector.x; - if (p_vector.y < begin.y) + } + if (p_vector.y < begin.y) { begin.y = p_vector.y; - if (p_vector.z < begin.z) + } + if (p_vector.z < begin.z) { begin.z = p_vector.z; + } - if (p_vector.x > end.x) + if (p_vector.x > end.x) { end.x = p_vector.x; - if (p_vector.y > end.y) + } + if (p_vector.y > end.y) { end.y = p_vector.y; - if (p_vector.z > end.z) + } + if (p_vector.z > end.z) { end.z = p_vector.z; + } position = begin; size = end - begin; } void AABB::project_range_in_plane(const Plane &p_plane, real_t &r_min, real_t &r_max) const { - Vector3 half_extents(size.x * 0.5, size.y * 0.5, size.z * 0.5); Vector3 center(position.x + half_extents.x, position.y + half_extents.y, position.z + half_extents.z); @@ -305,7 +345,6 @@ void AABB::project_range_in_plane(const Plane &p_plane, real_t &r_min, real_t &r } inline real_t AABB::get_longest_axis_size() const { - real_t max_size = size.x; if (size.y > max_size) { @@ -320,7 +359,6 @@ inline real_t AABB::get_longest_axis_size() const { } inline real_t AABB::get_shortest_axis_size() const { - real_t max_size = size.x; if (size.y < max_size) { @@ -335,7 +373,6 @@ inline real_t AABB::get_shortest_axis_size() const { } bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const { - real_t divx = 1.0 / p_dir.x; real_t divy = 1.0 / p_dir.y; real_t divz = 1.0 / p_dir.z; @@ -356,12 +393,15 @@ bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real tymin = (upbound.y - p_from.y) * divy; tymax = (position.y - p_from.y) * divy; } - if ((tmin > tymax) || (tymin > tmax)) + if ((tmin > tymax) || (tymin > tmax)) { return false; - if (tymin > tmin) + } + if (tymin > tmin) { tmin = tymin; - if (tymax < tmax) + } + if (tymax < tmax) { tmax = tymax; + } if (p_dir.z >= 0) { tzmin = (position.z - p_from.z) * divz; tzmax = (upbound.z - p_from.z) * divz; @@ -369,17 +409,19 @@ bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real tzmin = (upbound.z - p_from.z) * divz; tzmax = (position.z - p_from.z) * divz; } - if ((tmin > tzmax) || (tzmin > tmax)) + if ((tmin > tzmax) || (tzmin > tmax)) { return false; - if (tzmin > tmin) + } + if (tzmin > tmin) { tmin = tzmin; - if (tzmax < tmax) + } + if (tzmax < tmax) { tmax = tzmax; + } return ((tmin < t1) && (tmax > t0)); } void AABB::grow_by(real_t p_amount) { - position.x -= p_amount; position.y -= p_amount; position.z -= p_amount; @@ -388,4 +430,28 @@ void AABB::grow_by(real_t p_amount) { size.z += 2.0 * p_amount; } +void AABB::quantize(float p_unit) { + size += position; + + position.x -= Math::fposmodp(position.x, p_unit); + position.y -= Math::fposmodp(position.y, p_unit); + position.z -= Math::fposmodp(position.z, p_unit); + + size.x -= Math::fposmodp(size.x, p_unit); + size.y -= Math::fposmodp(size.y, p_unit); + size.z -= Math::fposmodp(size.z, p_unit); + + size.x += p_unit; + size.y += p_unit; + size.z += p_unit; + + size -= position; +} + +AABB AABB::quantized(float p_unit) const { + AABB ret = *this; + ret.quantize(p_unit); + return ret; +} + #endif // AABB_H diff --git a/core/math/audio_frame.cpp b/core/math/audio_frame.cpp deleted file mode 100644 index c565ea9b13..0000000000 --- a/core/math/audio_frame.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/*************************************************************************/ -/* audio_frame.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 "audio_frame.h" diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index 4665311059..5773da9211 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -48,7 +48,6 @@ static inline float undenormalise(volatile float f) { } struct AudioFrame { - //left and right samples float l, r; @@ -105,7 +104,6 @@ struct AudioFrame { } _FORCE_INLINE_ AudioFrame lerp(const AudioFrame &p_b, float p_t) const { - AudioFrame res = *this; res.l += (p_t * (p_b.l - l)); @@ -123,7 +121,7 @@ struct AudioFrame { r = p_frame.r; } - _ALWAYS_INLINE_ AudioFrame operator=(const AudioFrame &p_frame) { + _ALWAYS_INLINE_ AudioFrame &operator=(const AudioFrame &p_frame) { l = p_frame.l; r = p_frame.r; return *this; diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 0f519a20d8..cbdd8a8c9f 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,22 +32,19 @@ #include "core/math/math_funcs.h" #include "core/os/copymem.h" -#include "core/print_string.h" +#include "core/string/print_string.h" #define cofac(row1, col1, row2, col2) \ (elements[row1][col1] * elements[row2][col2] - elements[row1][col2] * elements[row2][col1]) void Basis::from_z(const Vector3 &p_z) { - if (Math::abs(p_z.z) > Math_SQRT12) { - // choose p in y-z plane real_t a = p_z[1] * p_z[1] + p_z[2] * p_z[2]; real_t k = 1.0 / Math::sqrt(a); elements[0] = Vector3(0, -p_z[2] * k, p_z[1] * k); elements[1] = Vector3(a * k, -p_z[0] * elements[0][2], p_z[0] * elements[0][1]); } else { - // choose p in x-y plane real_t a = p_z.x * p_z.x + p_z.y * p_z.y; real_t k = 1.0 / Math::sqrt(a); @@ -58,7 +55,6 @@ void Basis::from_z(const Vector3 &p_z) { } void Basis::invert() { - real_t co[3] = { cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1) }; @@ -76,7 +72,6 @@ void Basis::invert() { } void Basis::orthonormalize() { - // Gram-Schmidt Process Vector3 x = get_axis(0); @@ -95,7 +90,6 @@ void Basis::orthonormalize() { } Basis Basis::orthonormalized() const { - Basis c = *this; c.orthonormalize(); return c; @@ -119,20 +113,24 @@ bool Basis::is_rotation() const { return Math::is_equal_approx(determinant(), 1, UNIT_EPSILON) && is_orthogonal(); } +#ifdef MATH_CHECKS +// This method is only used once, in diagonalize. If it's desired elsewhere, feel free to remove the #ifdef. bool Basis::is_symmetric() const { - - if (!Math::is_equal_approx_ratio(elements[0][1], elements[1][0], UNIT_EPSILON)) + if (!Math::is_equal_approx(elements[0][1], elements[1][0])) { return false; - if (!Math::is_equal_approx_ratio(elements[0][2], elements[2][0], UNIT_EPSILON)) + } + if (!Math::is_equal_approx(elements[0][2], elements[2][0])) { return false; - if (!Math::is_equal_approx_ratio(elements[1][2], elements[2][1], UNIT_EPSILON)) + } + if (!Math::is_equal_approx(elements[1][2], elements[2][1])) { return false; + } return true; } +#endif Basis Basis::diagonalize() { - //NOTE: only implemented for symmetric matrices //with the Jacobi iterative method method #ifdef MATH_CHECKS @@ -193,21 +191,18 @@ Basis Basis::diagonalize() { } Basis Basis::inverse() const { - Basis inv = *this; inv.invert(); return inv; } void Basis::transpose() { - SWAP(elements[0][1], elements[1][0]); SWAP(elements[0][2], elements[2][0]); SWAP(elements[1][2], elements[2][1]); } Basis Basis::transposed() const { - Basis tr = *this; tr.transpose(); return tr; @@ -216,7 +211,6 @@ Basis Basis::transposed() const { // Multiplies the matrix from left by the scaling matrix: M -> S.M // See the comment for Basis::rotated for further explanation. void Basis::scale(const Vector3 &p_scale) { - elements[0][0] *= p_scale.x; elements[0][1] *= p_scale.x; elements[0][2] *= p_scale.x; @@ -260,7 +254,6 @@ Basis Basis::scaled_local(const Vector3 &p_scale) const { } Vector3 Basis::get_scale_abs() const { - return Vector3( Vector3(elements[0][0], elements[1][0], elements[2][0]).length(), Vector3(elements[0][1], elements[1][1], elements[2][1]).length(), @@ -340,8 +333,8 @@ void Basis::rotate_local(const Vector3 &p_axis, real_t p_phi) { // M -> (M.R.Minv).M = M.R. *this = rotated_local(p_axis, p_phi); } -Basis Basis::rotated_local(const Vector3 &p_axis, real_t p_phi) const { +Basis Basis::rotated_local(const Vector3 &p_axis, real_t p_phi) const { return (*this) * Basis(p_axis, p_phi); } @@ -430,7 +423,6 @@ void Basis::get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) cons // the angles in the decomposition R = X(a1).Y(a2).Z(a3) where Z(a) rotates // around the z-axis by a and so on. Vector3 Basis::get_euler_xyz() const { - // Euler angles in XYZ convention. // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix // @@ -439,12 +431,9 @@ Vector3 Basis::get_euler_xyz() const { // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy Vector3 euler; -#ifdef MATH_CHECKS - ERR_FAIL_COND_V(!is_rotation(), euler); -#endif real_t sy = elements[0][2]; - if (sy < 1.0) { - if (sy > -1.0) { + if (sy < (1.0 - CMP_EPSILON)) { + if (sy > -(1.0 - CMP_EPSILON)) { // is this a pure Y rotation? if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) { // return the simplest form (human friendlier in editor and scripts) @@ -457,12 +446,12 @@ Vector3 Basis::get_euler_xyz() const { euler.z = Math::atan2(-elements[0][1], elements[0][0]); } } else { - euler.x = -Math::atan2(elements[0][1], elements[1][1]); + euler.x = Math::atan2(elements[2][1], elements[1][1]); euler.y = -Math_PI / 2.0; euler.z = 0.0; } } else { - euler.x = Math::atan2(elements[0][1], elements[1][1]); + euler.x = Math::atan2(elements[2][1], elements[1][1]); euler.y = Math_PI / 2.0; euler.z = 0.0; } @@ -474,7 +463,6 @@ Vector3 Basis::get_euler_xyz() const { // and similar for other axes. // The current implementation uses XYZ convention (Z is the first rotation). void Basis::set_euler_xyz(const Vector3 &p_euler) { - real_t c, s; c = Math::cos(p_euler.x); @@ -493,16 +481,106 @@ void Basis::set_euler_xyz(const Vector3 &p_euler) { *this = xmat * (ymat * zmat); } +Vector3 Basis::get_euler_xzy() const { + // Euler angles in XZY convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy -sz cz*sy + // sx*sy+cx*cy*sz cx*cz cx*sz*sy-cy*sx + // cy*sx*sz cz*sx cx*cy+sx*sz*sy + + Vector3 euler; + real_t sz = elements[0][1]; + if (sz < (1.0 - CMP_EPSILON)) { + if (sz > -(1.0 - CMP_EPSILON)) { + euler.x = Math::atan2(elements[2][1], elements[1][1]); + euler.y = Math::atan2(elements[0][2], elements[0][0]); + euler.z = Math::asin(-sz); + } else { + // It's -1 + euler.x = -Math::atan2(elements[1][2], elements[2][2]); + euler.y = 0.0; + euler.z = Math_PI / 2.0; + } + } else { + // It's 1 + euler.x = -Math::atan2(elements[1][2], elements[2][2]); + euler.y = 0.0; + euler.z = -Math_PI / 2.0; + } + return euler; +} + +void Basis::set_euler_xzy(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); + + *this = xmat * zmat * ymat; +} + +Vector3 Basis::get_euler_yzx() const { + // Euler angles in YZX convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx + // sz cz*cx -cz*sx + // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx + + Vector3 euler; + real_t sz = elements[1][0]; + if (sz < (1.0 - CMP_EPSILON)) { + if (sz > -(1.0 - CMP_EPSILON)) { + euler.x = Math::atan2(-elements[1][2], elements[1][1]); + euler.y = Math::atan2(-elements[2][0], elements[0][0]); + euler.z = Math::asin(sz); + } else { + // It's -1 + euler.x = Math::atan2(elements[2][1], elements[2][2]); + euler.y = 0.0; + euler.z = -Math_PI / 2.0; + } + } else { + // It's 1 + euler.x = Math::atan2(elements[2][1], elements[2][2]); + euler.y = 0.0; + euler.z = Math_PI / 2.0; + } + return euler; +} + +void Basis::set_euler_yzx(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); + + *this = ymat * zmat * xmat; +} + // get_euler_yxz returns a vector containing the Euler angles in the YXZ convention, // as in first-Z, then-X, last-Y. The angles for X, Y, and Z rotations are returned // as the x, y, and z components of a Vector3 respectively. Vector3 Basis::get_euler_yxz() const { - - /* checking this is a bad idea, because obtaining from scaled transform is a valid use case -#ifdef MATH_CHECKS - ERR_FAIL_COND(!is_rotation()); -#endif -*/ // Euler angles in YXZ convention. // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix // @@ -514,8 +592,8 @@ Vector3 Basis::get_euler_yxz() const { real_t m12 = elements[1][2]; - if (m12 < 1) { - if (m12 > -1) { + if (m12 < (1 - CMP_EPSILON)) { + if (m12 > -(1 - CMP_EPSILON)) { // is this a pure X rotation? if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) { // return the simplest form (human friendlier in editor and scripts) @@ -529,12 +607,12 @@ Vector3 Basis::get_euler_yxz() const { } } else { // m12 == -1 euler.x = Math_PI * 0.5; - euler.y = -atan2(-elements[0][1], elements[0][0]); + euler.y = atan2(elements[0][1], elements[0][0]); euler.z = 0; } } else { // m12 == 1 euler.x = -Math_PI * 0.5; - euler.y = -atan2(-elements[0][1], elements[0][0]); + euler.y = -atan2(elements[0][1], elements[0][0]); euler.z = 0; } @@ -546,7 +624,6 @@ Vector3 Basis::get_euler_yxz() const { // and similar for other axes. // The current implementation uses YXZ convention (Z is the first rotation). void Basis::set_euler_yxz(const Vector3 &p_euler) { - real_t c, s; c = Math::cos(p_euler.x); @@ -565,29 +642,110 @@ void Basis::set_euler_yxz(const Vector3 &p_euler) { *this = ymat * xmat * zmat; } -bool Basis::is_equal_approx(const Basis &p_basis) const { - - return elements[0].is_equal_approx(p_basis.elements[0]) && elements[1].is_equal_approx(p_basis.elements[1]) && elements[2].is_equal_approx(p_basis.elements[2]); +Vector3 Basis::get_euler_zxy() const { + // Euler angles in ZXY convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx + // cy*sz+cz*sx*sy cz*cx sz*sy-cz*cy*sx + // -cx*sy sx cx*cy + Vector3 euler; + real_t sx = elements[2][1]; + if (sx < (1.0 - CMP_EPSILON)) { + if (sx > -(1.0 - CMP_EPSILON)) { + euler.x = Math::asin(sx); + euler.y = Math::atan2(-elements[2][0], elements[2][2]); + euler.z = Math::atan2(-elements[0][1], elements[1][1]); + } else { + // It's -1 + euler.x = -Math_PI / 2.0; + euler.y = Math::atan2(elements[0][2], elements[0][0]); + euler.z = 0; + } + } else { + // It's 1 + euler.x = Math_PI / 2.0; + euler.y = Math::atan2(elements[0][2], elements[0][0]); + euler.z = 0; + } + return euler; } -bool Basis::is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon) const { +void Basis::set_euler_zxy(const Vector3 &p_euler) { + real_t c, s; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - if (!Math::is_equal_approx_ratio(a.elements[i][j], b.elements[i][j], p_epsilon)) - return false; + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); + + *this = zmat * xmat * ymat; +} + +Vector3 Basis::get_euler_zyx() const { + // Euler angles in ZYX convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*cy + // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx + // -sy cy*sx cy*cx + Vector3 euler; + real_t sy = elements[2][0]; + if (sy < (1.0 - CMP_EPSILON)) { + if (sy > -(1.0 - CMP_EPSILON)) { + euler.x = Math::atan2(elements[2][1], elements[2][2]); + euler.y = Math::asin(-sy); + euler.z = Math::atan2(elements[1][0], elements[0][0]); + } else { + // It's -1 + euler.x = 0; + euler.y = Math_PI / 2.0; + euler.z = -Math::atan2(elements[0][1], elements[1][1]); } + } else { + // It's 1 + euler.x = 0; + euler.y = -Math_PI / 2.0; + euler.z = -Math::atan2(elements[0][1], elements[1][1]); } + return euler; +} - return true; +void Basis::set_euler_zyx(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); + + *this = zmat * ymat * xmat; } -bool Basis::operator==(const Basis &p_matrix) const { +bool Basis::is_equal_approx(const Basis &p_basis) const { + return elements[0].is_equal_approx(p_basis.elements[0]) && elements[1].is_equal_approx(p_basis.elements[1]) && elements[2].is_equal_approx(p_basis.elements[2]); +} +bool Basis::operator==(const Basis &p_matrix) const { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - if (elements[i][j] != p_matrix.elements[i][j]) + if (elements[i][j] != p_matrix.elements[i][j]) { return false; + } } } @@ -595,21 +753,18 @@ bool Basis::operator==(const Basis &p_matrix) const { } bool Basis::operator!=(const Basis &p_matrix) const { - return (!(*this == p_matrix)); } Basis::operator String() const { - String mtx; for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - - if (i != 0 || j != 0) + if (i != 0 || j != 0) { mtx += ", "; + } - mtx += rtos(elements[i][j]); + mtx += rtos(elements[j][i]); //matrix is stored transposed for performance, so print it transposed } } @@ -617,7 +772,6 @@ Basis::operator String() const { } Quat Basis::get_quat() const { - #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(!is_rotation(), Quat(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quat() or call orthonormalized() instead."); #endif @@ -636,8 +790,8 @@ Quat Basis::get_quat() const { temp[2] = ((m.elements[1][0] - m.elements[0][1]) * s); } else { int i = m.elements[0][0] < m.elements[1][1] ? - (m.elements[1][1] < m.elements[2][2] ? 2 : 1) : - (m.elements[0][0] < m.elements[2][2] ? 2 : 0); + (m.elements[1][1] < m.elements[2][2] ? 2 : 1) : + (m.elements[0][0] < m.elements[2][2] ? 2 : 0); int j = (i + 1) % 3; int k = (i + 2) % 3; @@ -681,35 +835,33 @@ static const Basis _ortho_bases[24] = { }; int Basis::get_orthogonal_index() const { - //could be sped up if i come up with a way Basis orth = *this; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - real_t v = orth[i][j]; - if (v > 0.5) + if (v > 0.5) { v = 1.0; - else if (v < -0.5) + } else if (v < -0.5) { v = -1.0; - else + } else { v = 0; + } orth[i][j] = v; } } for (int i = 0; i < 24; i++) { - - if (_ortho_bases[i] == orth) + if (_ortho_bases[i] == orth) { return i; + } } return 0; } void Basis::set_orthogonal_index(int p_index) { - //there only exist 24 orthogonal bases in r3 ERR_FAIL_INDEX(p_index, 24); @@ -783,7 +935,9 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { real_t s = Math::sqrt((elements[1][2] - elements[2][1]) * (elements[1][2] - elements[2][1]) + (elements[2][0] - elements[0][2]) * (elements[2][0] - elements[0][2]) + (elements[0][1] - elements[1][0]) * (elements[0][1] - elements[1][0])); // s=|axis||sin(angle)|, used to normalise angle = Math::acos((elements[0][0] + elements[1][1] + elements[2][2] - 1) / 2); - if (angle < 0) s = -s; + if (angle < 0) { + s = -s; + } x = (elements[2][1] - elements[1][2]) / s; y = (elements[0][2] - elements[2][0]) / s; z = (elements[1][0] - elements[0][1]) / s; @@ -793,7 +947,6 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { } void Basis::set_quat(const Quat &p_quat) { - real_t d = p_quat.length_squared(); real_t s = 2.0 / d; real_t xs = p_quat.x * s, ys = p_quat.y * s, zs = p_quat.z * s; @@ -864,16 +1017,125 @@ void Basis::set_diagonal(const Vector3 &p_diag) { elements[2][2] = p_diag.z; } -Basis Basis::slerp(const Basis &target, const real_t &t) const { - +Basis Basis::slerp(const Basis &p_to, const real_t &p_weight) const { //consider scale Quat from(*this); - Quat to(target); + Quat to(p_to); - Basis b(from.slerp(to, t)); - b.elements[0] *= Math::lerp(elements[0].length(), target.elements[0].length(), t); - b.elements[1] *= Math::lerp(elements[1].length(), target.elements[1].length(), t); - b.elements[2] *= Math::lerp(elements[2].length(), target.elements[2].length(), t); + Basis b(from.slerp(to, p_weight)); + b.elements[0] *= Math::lerp(elements[0].length(), p_to.elements[0].length(), p_weight); + b.elements[1] *= Math::lerp(elements[1].length(), p_to.elements[1].length(), p_weight); + b.elements[2] *= Math::lerp(elements[2].length(), p_to.elements[2].length(), p_weight); return b; } + +void Basis::rotate_sh(real_t *p_values) { + // code by John Hable + // http://filmicworlds.com/blog/simple-and-fast-spherical-harmonic-rotation/ + // this code is Public Domain + + const static real_t s_c3 = 0.94617469575; // (3*sqrt(5))/(4*sqrt(pi)) + const static real_t s_c4 = -0.31539156525; // (-sqrt(5))/(4*sqrt(pi)) + const static real_t s_c5 = 0.54627421529; // (sqrt(15))/(4*sqrt(pi)) + + const static real_t s_c_scale = 1.0 / 0.91529123286551084; + const static real_t s_c_scale_inv = 0.91529123286551084; + + const static real_t s_rc2 = 1.5853309190550713 * s_c_scale; + const static real_t s_c4_div_c3 = s_c4 / s_c3; + const static real_t s_c4_div_c3_x2 = (s_c4 / s_c3) * 2.0; + + const static real_t s_scale_dst2 = s_c3 * s_c_scale_inv; + const static real_t s_scale_dst4 = s_c5 * s_c_scale_inv; + + real_t src[9] = { p_values[0], p_values[1], p_values[2], p_values[3], p_values[4], p_values[5], p_values[6], p_values[7], p_values[8] }; + + real_t m00 = elements[0][0]; + real_t m01 = elements[0][1]; + real_t m02 = elements[0][2]; + real_t m10 = elements[1][0]; + real_t m11 = elements[1][1]; + real_t m12 = elements[1][2]; + real_t m20 = elements[2][0]; + real_t m21 = elements[2][1]; + real_t m22 = elements[2][2]; + + p_values[0] = src[0]; + p_values[1] = m11 * src[1] - m12 * src[2] + m10 * src[3]; + p_values[2] = -m21 * src[1] + m22 * src[2] - m20 * src[3]; + p_values[3] = m01 * src[1] - m02 * src[2] + m00 * src[3]; + + real_t sh0 = src[7] + src[8] + src[8] - src[5]; + real_t sh1 = src[4] + s_rc2 * src[6] + src[7] + src[8]; + real_t sh2 = src[4]; + real_t sh3 = -src[7]; + real_t sh4 = -src[5]; + + // Rotations. R0 and R1 just use the raw matrix columns + real_t r2x = m00 + m01; + real_t r2y = m10 + m11; + real_t r2z = m20 + m21; + + real_t r3x = m00 + m02; + real_t r3y = m10 + m12; + real_t r3z = m20 + m22; + + real_t r4x = m01 + m02; + real_t r4y = m11 + m12; + real_t r4z = m21 + m22; + + // dense matrix multiplication one column at a time + + // column 0 + real_t sh0_x = sh0 * m00; + real_t sh0_y = sh0 * m10; + real_t d0 = sh0_x * m10; + real_t d1 = sh0_y * m20; + real_t d2 = sh0 * (m20 * m20 + s_c4_div_c3); + real_t d3 = sh0_x * m20; + real_t d4 = sh0_x * m00 - sh0_y * m10; + + // column 1 + real_t sh1_x = sh1 * m02; + real_t sh1_y = sh1 * m12; + d0 += sh1_x * m12; + d1 += sh1_y * m22; + d2 += sh1 * (m22 * m22 + s_c4_div_c3); + d3 += sh1_x * m22; + d4 += sh1_x * m02 - sh1_y * m12; + + // column 2 + real_t sh2_x = sh2 * r2x; + real_t sh2_y = sh2 * r2y; + d0 += sh2_x * r2y; + d1 += sh2_y * r2z; + d2 += sh2 * (r2z * r2z + s_c4_div_c3_x2); + d3 += sh2_x * r2z; + d4 += sh2_x * r2x - sh2_y * r2y; + + // column 3 + real_t sh3_x = sh3 * r3x; + real_t sh3_y = sh3 * r3y; + d0 += sh3_x * r3y; + d1 += sh3_y * r3z; + d2 += sh3 * (r3z * r3z + s_c4_div_c3_x2); + d3 += sh3_x * r3z; + d4 += sh3_x * r3x - sh3_y * r3y; + + // column 4 + real_t sh4_x = sh4 * r4x; + real_t sh4_y = sh4 * r4y; + d0 += sh4_x * r4y; + d1 += sh4_y * r4z; + d2 += sh4 * (r4z * r4z + s_c4_div_c3_x2); + d3 += sh4_x * r4z; + d4 += sh4_x * r4x - sh4_y * r4y; + + // extra multipliers + p_values[4] = d0; + p_values[5] = -d1; + p_values[6] = d2 * s_scale_dst2; + p_values[7] = -d3; + p_values[8] = d4 * s_scale_dst4; +} diff --git a/core/math/basis.h b/core/math/basis.h index 0261cf67c6..56f6227313 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -36,14 +36,16 @@ class Basis { public: - Vector3 elements[3]; + Vector3 elements[3] = { + Vector3(1, 0, 0), + Vector3(0, 1, 0), + Vector3(0, 0, 1) + }; _FORCE_INLINE_ const Vector3 &operator[](int axis) const { - return elements[axis]; } _FORCE_INLINE_ Vector3 &operator[](int axis) { - return elements[axis]; } @@ -90,9 +92,22 @@ public: Vector3 get_euler_xyz() const; void set_euler_xyz(const Vector3 &p_euler); + + Vector3 get_euler_xzy() const; + void set_euler_xzy(const Vector3 &p_euler); + + Vector3 get_euler_yzx() const; + void set_euler_yzx(const Vector3 &p_euler); + Vector3 get_euler_yxz() const; void set_euler_yxz(const Vector3 &p_euler); + Vector3 get_euler_zxy() const; + void set_euler_zxy(const Vector3 &p_euler); + + Vector3 get_euler_zyx() const; + void set_euler_zyx(const Vector3 &p_euler); + Quat get_quat() const; void set_quat(const Quat &p_quat); @@ -131,9 +146,6 @@ public: } bool is_equal_approx(const Basis &p_basis) const; - // TODO: Break compatibility in 4.0 by getting rid of this so that it's only an instance method. See also TODO in variant_call.cpp - bool is_equal_approx(const Basis &a, const Basis &b) const { return a.is_equal_approx(b); } - bool is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon = UNIT_EPSILON) const; bool operator==(const Basis &p_matrix) const; bool operator!=(const Basis &p_matrix) const; @@ -158,14 +170,14 @@ public: bool is_diagonal() const; bool is_rotation() const; - Basis slerp(const Basis &target, const real_t &t) const; + Basis slerp(const Basis &p_to, const real_t &p_weight) const; + void rotate_sh(real_t *p_values); operator String() const; /* create / set */ _FORCE_INLINE_ void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { - elements[0][0] = xx; elements[0][1] = xy; elements[0][2] = xz; @@ -177,18 +189,15 @@ public: elements[2][2] = zz; } _FORCE_INLINE_ void set(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z) { - set_axis(0, p_x); set_axis(1, p_y); set_axis(2, p_z); } _FORCE_INLINE_ Vector3 get_column(int i) const { - return Vector3(elements[0][i], elements[1][i], elements[2][i]); } _FORCE_INLINE_ Vector3 get_row(int i) const { - return Vector3(elements[i][0], elements[i][1], elements[i][2]); } _FORCE_INLINE_ Vector3 get_main_diagonal() const { @@ -220,14 +229,15 @@ public: elements[0].z * m[0].z + elements[1].z * m[1].z + elements[2].z * m[2].z); } Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { - set(xx, xy, xz, yx, yy, yz, zx, zy, zz); } void orthonormalize(); Basis orthonormalized() const; +#ifdef MATH_CHECKS bool is_symmetric() const; +#endif Basis diagonalize(); operator Quat() const { return get_quat(); } @@ -247,22 +257,10 @@ public: elements[2] = row2; } - _FORCE_INLINE_ Basis() { - - elements[0][0] = 1; - elements[0][1] = 0; - elements[0][2] = 0; - elements[1][0] = 0; - elements[1][1] = 1; - elements[1][2] = 0; - elements[2][0] = 0; - elements[2][1] = 0; - elements[2][2] = 1; - } + _FORCE_INLINE_ Basis() {} }; _FORCE_INLINE_ void Basis::operator*=(const Basis &p_matrix) { - set( p_matrix.tdotx(elements[0]), p_matrix.tdoty(elements[0]), p_matrix.tdotz(elements[0]), p_matrix.tdotx(elements[1]), p_matrix.tdoty(elements[1]), p_matrix.tdotz(elements[1]), @@ -270,7 +268,6 @@ _FORCE_INLINE_ void Basis::operator*=(const Basis &p_matrix) { } _FORCE_INLINE_ Basis Basis::operator*(const Basis &p_matrix) const { - return Basis( p_matrix.tdotx(elements[0]), p_matrix.tdoty(elements[0]), p_matrix.tdotz(elements[0]), p_matrix.tdotx(elements[1]), p_matrix.tdoty(elements[1]), p_matrix.tdotz(elements[1]), @@ -278,49 +275,42 @@ _FORCE_INLINE_ Basis Basis::operator*(const Basis &p_matrix) const { } _FORCE_INLINE_ void Basis::operator+=(const Basis &p_matrix) { - elements[0] += p_matrix.elements[0]; elements[1] += p_matrix.elements[1]; elements[2] += p_matrix.elements[2]; } _FORCE_INLINE_ Basis Basis::operator+(const Basis &p_matrix) const { - Basis ret(*this); ret += p_matrix; return ret; } _FORCE_INLINE_ void Basis::operator-=(const Basis &p_matrix) { - elements[0] -= p_matrix.elements[0]; elements[1] -= p_matrix.elements[1]; elements[2] -= p_matrix.elements[2]; } _FORCE_INLINE_ Basis Basis::operator-(const Basis &p_matrix) const { - Basis ret(*this); ret -= p_matrix; return ret; } _FORCE_INLINE_ void Basis::operator*=(real_t p_val) { - elements[0] *= p_val; elements[1] *= p_val; elements[2] *= p_val; } _FORCE_INLINE_ Basis Basis::operator*(real_t p_val) const { - Basis ret(*this); ret *= p_val; return ret; } Vector3 Basis::xform(const Vector3 &p_vector) const { - return Vector3( elements[0].dot(p_vector), elements[1].dot(p_vector), @@ -328,7 +318,6 @@ Vector3 Basis::xform(const Vector3 &p_vector) const { } Vector3 Basis::xform_inv(const Vector3 &p_vector) const { - return Vector3( (elements[0][0] * p_vector.x) + (elements[1][0] * p_vector.y) + (elements[2][0] * p_vector.z), (elements[0][1] * p_vector.x) + (elements[1][1] * p_vector.y) + (elements[2][1] * p_vector.z), @@ -336,7 +325,6 @@ Vector3 Basis::xform_inv(const Vector3 &p_vector) const { } real_t Basis::determinant() const { - return elements[0][0] * (elements[1][1] * elements[2][2] - elements[2][1] * elements[1][2]) - elements[1][0] * (elements[0][1] * elements[2][2] - elements[2][1] * elements[0][2]) + elements[2][0] * (elements[0][1] * elements[1][2] - elements[1][1] * elements[0][2]); diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index c36070e47f..9ec71af57f 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,32 +31,40 @@ #include "camera_matrix.h" #include "core/math/math_funcs.h" -#include "core/print_string.h" +#include "core/string/print_string.h" + +float CameraMatrix::determinant() const { + return matrix[0][3] * matrix[1][2] * matrix[2][1] * matrix[3][0] - matrix[0][2] * matrix[1][3] * matrix[2][1] * matrix[3][0] - + matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0] + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0] + + matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0] - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0] - + matrix[0][3] * matrix[1][2] * matrix[2][0] * matrix[3][1] + matrix[0][2] * matrix[1][3] * matrix[2][0] * matrix[3][1] + + matrix[0][3] * matrix[1][0] * matrix[2][2] * matrix[3][1] - matrix[0][0] * matrix[1][3] * matrix[2][2] * matrix[3][1] - + matrix[0][2] * matrix[1][0] * matrix[2][3] * matrix[3][1] + matrix[0][0] * matrix[1][2] * matrix[2][3] * matrix[3][1] + + matrix[0][3] * matrix[1][1] * matrix[2][0] * matrix[3][2] - matrix[0][1] * matrix[1][3] * matrix[2][0] * matrix[3][2] - + matrix[0][3] * matrix[1][0] * matrix[2][1] * matrix[3][2] + matrix[0][0] * matrix[1][3] * matrix[2][1] * matrix[3][2] + + matrix[0][1] * matrix[1][0] * matrix[2][3] * matrix[3][2] - matrix[0][0] * matrix[1][1] * matrix[2][3] * matrix[3][2] - + matrix[0][2] * matrix[1][1] * matrix[2][0] * matrix[3][3] + matrix[0][1] * matrix[1][2] * matrix[2][0] * matrix[3][3] + + matrix[0][2] * matrix[1][0] * matrix[2][1] * matrix[3][3] - matrix[0][0] * matrix[1][2] * matrix[2][1] * matrix[3][3] - + matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3] + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3]; +} void CameraMatrix::set_identity() { - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - matrix[i][j] = (i == j) ? 1 : 0; } } } void CameraMatrix::set_zero() { - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - matrix[i][j] = 0; } } } Plane CameraMatrix::xform4(const Plane &p_vec4) const { - Plane ret; ret.normal.x = matrix[0][0] * p_vec4.normal.x + matrix[1][0] * p_vec4.normal.y + matrix[2][0] * p_vec4.normal.z + matrix[3][0] * p_vec4.d; @@ -67,7 +75,6 @@ Plane CameraMatrix::xform4(const Plane &p_vec4) const { } void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) { - if (p_flip_fov) { p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); } @@ -109,18 +116,18 @@ void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_ left = -xmax + frustumshift; right = xmax + frustumshift; modeltranslation = p_intraocular_dist / 2.0; - }; break; + } break; case 2: { // right eye left = -xmax - frustumshift; right = xmax - frustumshift; modeltranslation = -p_intraocular_dist / 2.0; - }; break; + } break; default: { // mono, should give the same result as set_perspective(p_fovy_degrees,p_aspect,p_z_near,p_z_far,p_flip_fov) left = -xmax; right = xmax; modeltranslation = 0.0; - }; break; - }; + } break; + } set_frustum(left, right, -ymax, ymax, p_z_near, p_z_far); @@ -150,17 +157,16 @@ void CameraMatrix::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_ switch (p_eye) { case 1: { // left eye set_frustum(-f2 * p_z_near, f1 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); - }; break; + } break; case 2: { // right eye set_frustum(-f1 * p_z_near, f2 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); - }; break; + } break; default: { // mono, does not apply here! - }; break; - }; -}; + } break; + } +} void CameraMatrix::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { - set_identity(); matrix[0][0] = 2.0 / (p_right - p_left); @@ -173,7 +179,6 @@ void CameraMatrix::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom } void CameraMatrix::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) { - if (!p_flip_fov) { p_size *= p_aspect; } @@ -182,7 +187,6 @@ void CameraMatrix::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear } void CameraMatrix::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { - ERR_FAIL_COND(p_right <= p_left); ERR_FAIL_COND(p_top <= p_bottom); ERR_FAIL_COND(p_far <= p_near); @@ -223,7 +227,6 @@ void CameraMatrix::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, } real_t CameraMatrix::get_z_far() const { - const real_t *matrix = (const real_t *)this->matrix; Plane new_plane = Plane(matrix[3] - matrix[2], matrix[7] - matrix[6], @@ -235,8 +238,8 @@ real_t CameraMatrix::get_z_far() const { return new_plane.d; } -real_t CameraMatrix::get_z_near() const { +real_t CameraMatrix::get_z_near() const { const real_t *matrix = (const real_t *)this->matrix; Plane new_plane = Plane(matrix[3] + matrix[2], matrix[7] + matrix[6], @@ -248,7 +251,6 @@ real_t CameraMatrix::get_z_near() const { } Vector2 CameraMatrix::get_viewport_half_extents() const { - const real_t *matrix = (const real_t *)this->matrix; ///////--- Near Plane ---/////// Plane near_plane = Plane(matrix[3] + matrix[2], @@ -276,8 +278,7 @@ Vector2 CameraMatrix::get_viewport_half_extents() const { return Vector2(res.x, res.y); } -void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const { - +Vector2 CameraMatrix::get_far_plane_half_extents() const { const real_t *matrix = (const real_t *)this->matrix; ///////--- Far Plane ---/////// Plane far_plane = Plane(matrix[3] - matrix[2], @@ -302,12 +303,10 @@ void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const { Vector3 res; far_plane.intersect_3(right_plane, top_plane, &res); - r_width = res.x; - r_height = res.y; + return Vector2(res.x, res.y); } bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const { - Vector<Plane> planes = get_projection_planes(Transform()); const Planes intersections[8][3] = { { PLANE_FAR, PLANE_LEFT, PLANE_TOP }, @@ -321,7 +320,6 @@ bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8point }; for (int i = 0; i < 8; i++) { - Vector3 point; bool res = planes[intersections[i][0]].intersect_3(planes[intersections[i][1]], planes[intersections[i][2]], &point); ERR_FAIL_COND_V(!res, false); @@ -332,7 +330,6 @@ bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8point } Vector<Plane> CameraMatrix::get_projection_planes(const Transform &p_transform) const { - /** Fast Plane Extraction from combined modelview/projection matrices. * References: * https://web.archive.org/web/20011221205252/http://www.markmorley.com/opengl/frustumculling.html @@ -415,14 +412,12 @@ Vector<Plane> CameraMatrix::get_projection_planes(const Transform &p_transform) } CameraMatrix CameraMatrix::inverse() const { - CameraMatrix cm = *this; cm.invert(); return cm; } void CameraMatrix::invert() { - int i, j, k; int pvt_i[4], pvt_j[4]; /* Locations of pivot matrix */ real_t pvt_val; /* Value of current pivot element */ @@ -473,20 +468,26 @@ void CameraMatrix::invert() { /** Divide column by minus pivot value **/ for (i = 0; i < 4; i++) { - if (i != k) matrix[i][k] /= (-pvt_val); + if (i != k) { + matrix[i][k] /= (-pvt_val); + } } /** Reduce the matrix **/ for (i = 0; i < 4; i++) { hold = matrix[i][k]; for (j = 0; j < 4; j++) { - if (i != k && j != k) matrix[i][j] += hold * matrix[k][j]; + if (i != k && j != k) { + matrix[i][j] += hold * matrix[k][j]; + } } } /** Divide row by pivot **/ for (j = 0; j < 4; j++) { - if (j != k) matrix[k][j] /= pvt_val; + if (j != k) { + matrix[k][j] /= pvt_val; + } } /** Replace pivot by reciprocal (at last we can touch it). **/ @@ -506,12 +507,13 @@ void CameraMatrix::invert() { } j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */ - if (j != k) /* If columns are different */ + if (j != k) { /* If columns are different */ for (i = 0; i < 4; i++) { hold = matrix[i][k]; matrix[i][k] = -matrix[i][j]; matrix[i][j] = hold; } + } } } @@ -522,19 +524,18 @@ void CameraMatrix::flip_y() { } CameraMatrix::CameraMatrix() { - set_identity(); } CameraMatrix CameraMatrix::operator*(const CameraMatrix &p_matrix) const { - CameraMatrix new_matrix; for (int j = 0; j < 4; j++) { for (int i = 0; i < 4; i++) { real_t ab = 0; - for (int k = 0; k < 4; k++) + for (int k = 0; k < 4; k++) { ab += matrix[k][i] * p_matrix.matrix[j][k]; + } new_matrix.matrix[j][i] = ab; } } @@ -543,7 +544,6 @@ CameraMatrix CameraMatrix::operator*(const CameraMatrix &p_matrix) const { } void CameraMatrix::set_depth_correction(bool p_flip_y) { - real_t *m = &matrix[0][0]; m[0] = 1; @@ -565,7 +565,6 @@ void CameraMatrix::set_depth_correction(bool p_flip_y) { } void CameraMatrix::set_light_bias() { - real_t *m = &matrix[0][0]; m[0] = 0.5; @@ -587,7 +586,6 @@ void CameraMatrix::set_light_bias() { } void CameraMatrix::set_light_atlas_rect(const Rect2 &p_rect) { - real_t *m = &matrix[0][0]; m[0] = p_rect.size.width; @@ -609,30 +607,28 @@ void CameraMatrix::set_light_atlas_rect(const Rect2 &p_rect) { } CameraMatrix::operator String() const { - String str; - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { str += String((j > 0) ? ", " : "\n") + rtos(matrix[i][j]); + } + } return str; } real_t CameraMatrix::get_aspect() const { - Vector2 vp_he = get_viewport_half_extents(); return vp_he.x / vp_he.y; } int CameraMatrix::get_pixels_per_meter(int p_for_pixel_width) const { - Vector3 result = xform(Vector3(1, 0, -1)); return int((result.x * 0.5 + 0.5) * p_for_pixel_width); } bool CameraMatrix::is_orthogonal() const { - return matrix[3][3] == 1.0; } @@ -659,8 +655,18 @@ real_t CameraMatrix::get_fov() const { } } -void CameraMatrix::make_scale(const Vector3 &p_scale) { +float CameraMatrix::get_lod_multiplier() const { + if (is_orthogonal()) { + return get_viewport_half_extents().x; + } else { + float zn = get_z_near(); + float width = get_viewport_half_extents().x * 2.0; + return 1.0 / (zn / width); + } + //usage is lod_size / (lod_distance * multiplier) < threshold +} +void CameraMatrix::make_scale(const Vector3 &p_scale) { set_identity(); matrix[0][0] = p_scale.x; matrix[1][1] = p_scale.y; @@ -668,7 +674,6 @@ void CameraMatrix::make_scale(const Vector3 &p_scale) { } void CameraMatrix::scale_translate_to_fit(const AABB &p_aabb) { - Vector3 min = p_aabb.position; Vector3 max = p_aabb.position + p_aabb.size; @@ -694,7 +699,6 @@ void CameraMatrix::scale_translate_to_fit(const AABB &p_aabb) { } CameraMatrix::operator Transform() const { - Transform tr; const real_t *m = &matrix[0][0]; @@ -718,7 +722,6 @@ CameraMatrix::operator Transform() const { } CameraMatrix::CameraMatrix(const Transform &p_transform) { - const Transform &tr = p_transform; real_t *m = &matrix[0][0]; diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index c10193bc84..03068bc7ea 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -35,7 +35,6 @@ #include "core/math/transform.h" struct CameraMatrix { - enum Planes { PLANE_NEAR, PLANE_FAR, @@ -47,6 +46,7 @@ struct CameraMatrix { real_t matrix[4][4]; + float determinant() const; void set_identity(); void set_zero(); void set_light_bias(); @@ -61,7 +61,6 @@ struct CameraMatrix { void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); static real_t get_fovy(real_t p_fovx, real_t p_aspect) { - return Math::rad2deg(Math::atan(p_aspect * Math::tan(Math::deg2rad(p_fovx) * 0.5)) * 2.0); } @@ -75,7 +74,7 @@ struct CameraMatrix { bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const; Vector2 get_viewport_half_extents() const; - void get_far_plane_size(real_t &r_width, real_t &r_height) const; + Vector2 get_far_plane_half_extents() const; void invert(); CameraMatrix inverse() const; @@ -109,13 +108,14 @@ struct CameraMatrix { return !(*this == p_cam); } + float get_lod_multiplier() const; + CameraMatrix(); CameraMatrix(const Transform &p_transform); ~CameraMatrix(); }; Vector3 CameraMatrix::xform(const Vector3 &p_vec3) const { - Vector3 ret; ret.x = matrix[0][0] * p_vec3.x + matrix[1][0] * p_vec3.y + matrix[2][0] * p_vec3.z + matrix[3][0]; ret.y = matrix[0][1] * p_vec3.x + matrix[1][1] * p_vec3.y + matrix[2][1] * p_vec3.z + matrix[3][1]; diff --git a/core/color.cpp b/core/math/color.cpp index 03aeb2085b..588aedf821 100644 --- a/core/color.cpp +++ b/core/math/color.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,13 +30,12 @@ #include "color.h" -#include "core/color_names.inc" -#include "core/map.h" +#include "color_names.inc" #include "core/math/math_funcs.h" -#include "core/print_string.h" +#include "core/string/print_string.h" +#include "core/templates/map.h" uint32_t Color::to_argb32() const { - uint32_t c = (uint8_t)Math::round(a * 255); c <<= 8; c |= (uint8_t)Math::round(r * 255); @@ -49,7 +48,6 @@ uint32_t Color::to_argb32() const { } uint32_t Color::to_abgr32() const { - uint32_t c = (uint8_t)Math::round(a * 255); c <<= 8; c |= (uint8_t)Math::round(b * 255); @@ -62,7 +60,6 @@ uint32_t Color::to_abgr32() const { } uint32_t Color::to_rgba32() const { - uint32_t c = (uint8_t)Math::round(r * 255); c <<= 8; c |= (uint8_t)Math::round(g * 255); @@ -75,7 +72,6 @@ uint32_t Color::to_rgba32() const { } uint64_t Color::to_abgr64() const { - uint64_t c = (uint16_t)Math::round(a * 65535); c <<= 16; c |= (uint16_t)Math::round(b * 65535); @@ -88,7 +84,6 @@ uint64_t Color::to_abgr64() const { } uint64_t Color::to_argb64() const { - uint64_t c = (uint16_t)Math::round(a * 65535); c <<= 16; c |= (uint16_t)Math::round(r * 65535); @@ -101,7 +96,6 @@ uint64_t Color::to_argb64() const { } uint64_t Color::to_rgba64() const { - uint64_t c = (uint16_t)Math::round(r * 65535); c <<= 16; c |= (uint16_t)Math::round(g * 65535); @@ -114,7 +108,6 @@ uint64_t Color::to_rgba64() const { } float Color::get_h() const { - float min = MIN(r, g); min = MIN(min, b); float max = MAX(r, g); @@ -122,26 +115,28 @@ float Color::get_h() const { float delta = max - min; - if (delta == 0) + if (delta == 0) { return 0; + } float h; - if (r == max) + if (r == max) { h = (g - b) / delta; // between yellow & magenta - else if (g == max) + } else if (g == max) { h = 2 + (b - r) / delta; // between cyan & yellow - else + } else { h = 4 + (r - g) / delta; // between magenta & cyan + } h /= 6.0; - if (h < 0) + if (h < 0) { h += 1.0; + } return h; } float Color::get_s() const { - float min = MIN(r, g); min = MIN(min, b); float max = MAX(r, g); @@ -153,20 +148,18 @@ float Color::get_s() const { } float Color::get_v() const { - float max = MAX(r, g); max = MAX(max, b); return max; } void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) { - int i; float f, p, q, t; a = p_alpha; if (p_s == 0) { - // acp_hromatic (grey) + // Achromatic (grey) r = g = b = p_v; return; } @@ -215,25 +208,16 @@ void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) { } bool Color::is_equal_approx(const Color &p_color) const { - return Math::is_equal_approx(r, p_color.r) && Math::is_equal_approx(g, p_color.g) && Math::is_equal_approx(b, p_color.b) && Math::is_equal_approx(a, p_color.a); } void Color::invert() { - r = 1.0 - r; g = 1.0 - g; b = 1.0 - b; } -void Color::contrast() { - - r = Math::fmod(r + 0.5, 1.0); - g = Math::fmod(g + 0.5, 1.0); - b = Math::fmod(b + 0.5, 1.0); -} Color Color::hex(uint32_t p_hex) { - float a = (p_hex & 0xFF) / 255.0; p_hex >>= 8; float b = (p_hex & 0xFF) / 255.0; @@ -246,7 +230,6 @@ Color Color::hex(uint32_t p_hex) { } Color Color::hex64(uint64_t p_hex) { - float a = (p_hex & 0xFFFF) / 65535.0; p_hex >>= 16; float b = (p_hex & 0xFFFF) / 65535.0; @@ -259,7 +242,6 @@ Color Color::hex64(uint64_t p_hex) { } Color Color::from_rgbe9995(uint32_t p_rgbe) { - float r = p_rgbe & 0x1ff; float g = (p_rgbe >> 9) & 0x1ff; float b = (p_rgbe >> 18) & 0x1ff; @@ -273,140 +255,123 @@ Color Color::from_rgbe9995(uint32_t p_rgbe) { return Color(rd, gd, bd, 1.0f); } -static float _parse_col(const String &p_str, int p_ofs) { - - int ig = 0; - - for (int i = 0; i < 2; i++) { - - int c = p_str[i + p_ofs]; - int v = 0; - - if (c >= '0' && c <= '9') { - v = c - '0'; - } else if (c >= 'a' && c <= 'f') { - v = c - 'a'; - v += 10; - } else if (c >= 'A' && c <= 'F') { - v = c - 'A'; - v += 10; - } else { - return -1; - } +static int _parse_col4(const String &p_str, int p_ofs) { + char character = p_str[p_ofs]; - if (i == 0) - ig += v * 16; - else - ig += v; + if (character >= '0' && character <= '9') { + return character - '0'; + } else if (character >= 'a' && character <= 'f') { + return character + (10 - 'a'); + } else if (character >= 'A' && character <= 'F') { + return character + (10 - 'A'); } + return -1; +} - return ig; +static int _parse_col8(const String &p_str, int p_ofs) { + return _parse_col4(p_str, p_ofs) * 16 + _parse_col4(p_str, p_ofs + 1); } Color Color::inverted() const { - Color c = *this; c.invert(); return c; } -Color Color::contrasted() const { - - Color c = *this; - c.contrast(); - return c; -} - -Color Color::html(const String &p_color) { - - String color = p_color; - if (color.length() == 0) +Color Color::html(const String &p_rgba) { + String color = p_rgba; + if (color.length() == 0) { return Color(); - if (color[0] == '#') - color = color.substr(1, color.length() - 1); - if (color.length() == 3 || color.length() == 4) { - String exp_color; - for (int i = 0; i < color.length(); i++) { - exp_color += color[i]; - exp_color += color[i]; - } - color = exp_color; + } + if (color[0] == '#') { + color = color.substr(1); } + // If enabled, use 1 hex digit per channel instead of 2. + // Other sizes aren't in the HTML/CSS spec but we could add them if desired. + bool is_shorthand = color.length() < 5; bool alpha = false; if (color.length() == 8) { alpha = true; } else if (color.length() == 6) { alpha = false; + } else if (color.length() == 4) { + alpha = true; + } else if (color.length() == 3) { + alpha = false; } else { - ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_color + "."); + ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_rgba + "."); } - int a = 255; - if (alpha) { - a = _parse_col(color, 0); - ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_color + "."); + float r, g, b, a = 1.0; + if (is_shorthand) { + r = _parse_col4(color, 0) / 15.0; + g = _parse_col4(color, 1) / 15.0; + b = _parse_col4(color, 2) / 15.0; + if (alpha) { + a = _parse_col4(color, 3) / 15.0; + } + } else { + r = _parse_col8(color, 0) / 255.0; + g = _parse_col8(color, 2) / 255.0; + b = _parse_col8(color, 4) / 255.0; + if (alpha) { + a = _parse_col8(color, 6) / 255.0; + } } + ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_rgba + "."); + ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_rgba + "."); + ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_rgba + "."); + ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_rgba + "."); - int from = alpha ? 2 : 0; - - int r = _parse_col(color, from + 0); - ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_color + "."); - int g = _parse_col(color, from + 2); - ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_color + "."); - int b = _parse_col(color, from + 4); - ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_color + "."); - - return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0); + return Color(r, g, b, a); } bool Color::html_is_valid(const String &p_color) { - String color = p_color; - if (color.length() == 0) + if (color.length() == 0) { return false; - if (color[0] == '#') - color = color.substr(1, color.length() - 1); - - bool alpha = false; + } + if (color[0] == '#') { + color = color.substr(1); + } - if (color.length() == 8) { - alpha = true; - } else if (color.length() == 6) { - alpha = false; - } else { + // Check if the amount of hex digits is valid. + int len = color.length(); + if (!(len == 3 || len == 4 || len == 6 || len == 8)) { return false; } - if (alpha) { - int a = _parse_col(color, 0); - if (a < 0) { + // Check if each hex digit is valid. + for (int i = 0; i < len; i++) { + if (_parse_col4(color, i) == -1) { return false; } } - int from = alpha ? 2 : 0; + return true; +} - int r = _parse_col(color, from + 0); - if (r < 0) { - return false; - } - int g = _parse_col(color, from + 2); - if (g < 0) { - return false; - } - int b = _parse_col(color, from + 4); - if (b < 0) { - return false; +Color Color::named(const String &p_name) { + int idx = find_named_color(p_name); + if (idx == -1) { + ERR_FAIL_V_MSG(Color(), "Invalid color name: " + p_name + "."); + return Color(); } + return get_named_color(idx); +} - return true; +Color Color::named(const String &p_name, const Color &p_default) { + int idx = find_named_color(p_name); + if (idx == -1) { + return p_default; + } + return get_named_color(idx); } -Color Color::named(const String &p_name) { - if (_named_colors.empty()) _populate_named_colors(); // from color_names.inc +int Color::find_named_color(const String &p_name) { String name = p_name; // Normalize name name = name.replace(" ", ""); @@ -416,28 +381,57 @@ Color Color::named(const String &p_name) { name = name.replace(".", ""); name = name.to_lower(); - const Map<String, Color>::Element *color = _named_colors.find(name); - ERR_FAIL_NULL_V_MSG(color, Color(), "Invalid color name: " + p_name + "."); - return color->value(); + int idx = 0; + while (named_colors[idx].name != nullptr) { + if (name == named_colors[idx].name) { + return idx; + } + idx++; + } + + return -1; } -String _to_hex(float p_val) { +int Color::get_named_color_count() { + int idx = 0; + while (named_colors[idx].name != nullptr) { + idx++; + } + return idx; +} +String Color::get_named_color_name(int p_idx) { + return named_colors[p_idx].name; +} + +Color Color::get_named_color(int p_idx) { + return named_colors[p_idx].color; +} + +Color Color::from_string(const String &p_string, const Color &p_default) { + if (html_is_valid(p_string)) { + return html(p_string); + } else { + return named(p_string, p_default); + } +} + +String _to_hex(float p_val) { int v = Math::round(p_val * 255); v = CLAMP(v, 0, 255); String ret; for (int i = 0; i < 2; i++) { - - CharType c[2] = { 0, 0 }; + char32_t c[2] = { 0, 0 }; int lv = v & 0xF; - if (lv < 10) + if (lv < 10) { c[0] = '0' + lv; - else + } else { c[0] = 'a' + lv - 10; + } v >>= 4; - String cs = (const CharType *)c; + String cs = (const char32_t *)c; ret = cs + ret; } @@ -445,21 +439,21 @@ String _to_hex(float p_val) { } String Color::to_html(bool p_alpha) const { - String txt; txt += _to_hex(r); txt += _to_hex(g); txt += _to_hex(b); - if (p_alpha) - txt = _to_hex(a) + txt; + if (p_alpha) { + txt += _to_hex(a); + } return txt; } Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { - p_h = Math::fmod(p_h * 360.0f, 360.0f); - if (p_h < 0.0) + if (p_h < 0.0) { p_h += 360.0f; + } const float h_ = p_h / 60.0f; const float c = p_v * p_s; @@ -509,12 +503,10 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { } Color::operator String() const { - return rtos(r) + ", " + rtos(g) + ", " + rtos(b) + ", " + rtos(a); } Color Color::operator+(const Color &p_color) const { - return Color( r + p_color.r, g + p_color.g, @@ -522,8 +514,14 @@ Color Color::operator+(const Color &p_color) const { a + p_color.a); } -Color Color::operator-(const Color &p_color) const { +void Color::operator+=(const Color &p_color) { + r = r + p_color.r; + g = g + p_color.g; + b = b + p_color.b; + a = a + p_color.a; +} +Color Color::operator-(const Color &p_color) const { return Color( r - p_color.r, g - p_color.g, @@ -532,7 +530,6 @@ Color Color::operator-(const Color &p_color) const { } void Color::operator-=(const Color &p_color) { - r = r - p_color.r; g = g - p_color.g; b = b - p_color.b; @@ -540,7 +537,6 @@ void Color::operator-=(const Color &p_color) { } Color Color::operator*(const Color &p_color) const { - return Color( r * p_color.r, g * p_color.g, @@ -548,33 +544,29 @@ Color Color::operator*(const Color &p_color) const { a * p_color.a); } -Color Color::operator*(const real_t &rvalue) const { - +Color Color::operator*(real_t p_rvalue) const { return Color( - r * rvalue, - g * rvalue, - b * rvalue, - a * rvalue); + r * p_rvalue, + g * p_rvalue, + b * p_rvalue, + a * p_rvalue); } void Color::operator*=(const Color &p_color) { - r = r * p_color.r; g = g * p_color.g; b = b * p_color.b; a = a * p_color.a; } -void Color::operator*=(const real_t &rvalue) { - - r = r * rvalue; - g = g * rvalue; - b = b * rvalue; - a = a * rvalue; +void Color::operator*=(real_t p_rvalue) { + r = r * p_rvalue; + g = g * p_rvalue; + b = b * p_rvalue; + a = a * p_rvalue; } Color Color::operator/(const Color &p_color) const { - return Color( r / p_color.r, g / p_color.g, @@ -582,40 +574,36 @@ Color Color::operator/(const Color &p_color) const { a / p_color.a); } -Color Color::operator/(const real_t &rvalue) const { - +Color Color::operator/(real_t p_rvalue) const { return Color( - r / rvalue, - g / rvalue, - b / rvalue, - a / rvalue); + r / p_rvalue, + g / p_rvalue, + b / p_rvalue, + a / p_rvalue); } void Color::operator/=(const Color &p_color) { - r = r / p_color.r; g = g / p_color.g; b = b / p_color.b; a = a / p_color.a; } -void Color::operator/=(const real_t &rvalue) { - - if (rvalue == 0) { +void Color::operator/=(real_t p_rvalue) { + if (p_rvalue == 0) { r = 1.0; g = 1.0; b = 1.0; a = 1.0; } else { - r = r / rvalue; - g = g / rvalue; - b = b / rvalue; - a = a / rvalue; + r = r / p_rvalue; + g = g / p_rvalue; + b = b / p_rvalue; + a = a / p_rvalue; } -}; +} Color Color::operator-() const { - return Color( 1.0 - r, 1.0 - g, diff --git a/core/color.h b/core/math/color.h index 8b689fdde1..779f770761 100644 --- a/core/color.h +++ b/core/math/color.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,24 +32,19 @@ #define COLOR_H #include "core/math/math_funcs.h" -#include "core/ustring.h" +#include "core/string/ustring.h" struct Color { - union { - struct { float r; float g; float b; float a; }; - float components[4]; + float components[4] = { 0, 0, 0, 1.0 }; }; - bool operator==(const Color &p_color) const { return (r == p_color.r && g == p_color.g && b == p_color.b && a == p_color.a); } - bool operator!=(const Color &p_color) const { return (r != p_color.r || g != p_color.g || b != p_color.b || a != p_color.a); } - uint32_t to_rgba32() const; uint32_t to_argb32() const; uint32_t to_abgr32() const; @@ -61,56 +56,54 @@ struct Color { float get_v() const; void set_hsv(float p_h, float p_s, float p_v, float p_alpha = 1.0); - _FORCE_INLINE_ float &operator[](int idx) { - return components[idx]; + _FORCE_INLINE_ float &operator[](int p_idx) { + return components[p_idx]; } - _FORCE_INLINE_ const float &operator[](int idx) const { - return components[idx]; + _FORCE_INLINE_ const float &operator[](int p_idx) const { + return components[p_idx]; } - Color operator+(const Color &p_color) const; - _FORCE_INLINE_ void operator+=(const Color &p_color) { - r = r + p_color.r; - g = g + p_color.g; - b = b + p_color.b; - a = a + p_color.a; + bool operator==(const Color &p_color) const { + return (r == p_color.r && g == p_color.g && b == p_color.b && a == p_color.a); } + bool operator!=(const Color &p_color) const { + return (r != p_color.r || g != p_color.g || b != p_color.b || a != p_color.a); + } + + Color operator+(const Color &p_color) const; + void operator+=(const Color &p_color); Color operator-() const; Color operator-(const Color &p_color) const; void operator-=(const Color &p_color); Color operator*(const Color &p_color) const; - Color operator*(const real_t &rvalue) const; + Color operator*(real_t p_rvalue) const; void operator*=(const Color &p_color); - void operator*=(const real_t &rvalue); + void operator*=(real_t p_rvalue); Color operator/(const Color &p_color) const; - Color operator/(const real_t &rvalue) const; + Color operator/(real_t p_rvalue) const; void operator/=(const Color &p_color); - void operator/=(const real_t &rvalue); + void operator/=(real_t p_rvalue); bool is_equal_approx(const Color &p_color) const; void invert(); - void contrast(); Color inverted() const; - Color contrasted() const; - - _FORCE_INLINE_ Color lerp(const Color &p_b, float p_t) const { + _FORCE_INLINE_ Color lerp(const Color &p_to, float p_weight) const { Color res = *this; - res.r += (p_t * (p_b.r - r)); - res.g += (p_t * (p_b.g - g)); - res.b += (p_t * (p_b.b - b)); - res.a += (p_t * (p_b.a - a)); + res.r += (p_weight * (p_to.r - r)); + res.g += (p_weight * (p_to.g - g)); + res.b += (p_weight * (p_to.b - b)); + res.a += (p_weight * (p_to.a - a)); return res; } _FORCE_INLINE_ Color darkened(float p_amount) const { - Color res = *this; res.r = res.r * (1.0f - p_amount); res.g = res.g * (1.0f - p_amount); @@ -119,7 +112,6 @@ struct Color { } _FORCE_INLINE_ Color lightened(float p_amount) const { - Color res = *this; res.r = res.r + (1.0f - res.r) * p_amount; res.g = res.g + (1.0f - res.g) * p_amount; @@ -128,13 +120,11 @@ struct Color { } _FORCE_INLINE_ uint32_t to_rgbe9995() const { - const float pow2to9 = 512.0f; const float B = 15.0f; - //const float Emax = 31.0f; const float N = 9.0f; - float sharedexp = 65408.000f; //(( pow2to9 - 1.0f)/ pow2to9)*powf( 2.0f, 31.0f - 15.0f); + float sharedexp = 65408.000f; // Result of: ((pow2to9 - 1.0f) / pow2to9) * powf(2.0f, 31.0f - 15.0f) float cRed = MAX(0.0f, MIN(sharedexp, r)); float cGreen = MAX(0.0f, MIN(sharedexp, g)); @@ -142,8 +132,6 @@ struct Color { float cMax = MAX(cRed, MAX(cGreen, cBlue)); - // expp = MAX(-B - 1, log2(maxc)) + 1 + B - float expp = MAX(-B - 1.0f, floor(Math::log(cMax) / Math_LN2)) + 1.0f + B; float sMax = (float)floor((cMax / Math::pow(2.0f, expp - B - N)) + 0.5f); @@ -162,7 +150,6 @@ struct Color { } _FORCE_INLINE_ Color blend(const Color &p_over) const { - Color res; float sa = 1.0 - p_over.a; res.a = a * sa + p_over.a; @@ -177,7 +164,6 @@ struct Color { } _FORCE_INLINE_ Color to_linear() const { - return Color( r < 0.04045 ? r * (1.0 / 12.92) : Math::pow((r + 0.055) * (1.0 / (1 + 0.055)), 2.4), g < 0.04045 ? g * (1.0 / 12.92) : Math::pow((g + 0.055) * (1.0 / (1 + 0.055)), 2.4), @@ -185,7 +171,6 @@ struct Color { a); } _FORCE_INLINE_ Color to_srgb() const { - return Color( r < 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math::pow(r, 1.0f / 2.4f) - 0.055, g < 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math::pow(g, 1.0f / 2.4f) - 0.055, @@ -194,9 +179,15 @@ struct Color { static Color hex(uint32_t p_hex); static Color hex64(uint64_t p_hex); - static Color html(const String &p_color); + static Color html(const String &p_rgba); static bool html_is_valid(const String &p_color); static Color named(const String &p_name); + static Color named(const String &p_name, const Color &p_default); + static int find_named_color(const String &p_name); + static int get_named_color_count(); + static String get_named_color_name(int p_idx); + static Color get_named_color(int p_idx); + static Color from_string(const String &p_string, const Color &p_default); String to_html(bool p_alpha = true) const; Color from_hsv(float p_h, float p_s, float p_v, float p_a) const; static Color from_rgbe9995(uint32_t p_rgbe); @@ -204,24 +195,41 @@ struct Color { _FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys operator String() const; + // For the binder. + _FORCE_INLINE_ void set_r8(int32_t r8) { r = (CLAMP(r8, 0, 255) / 255.0); } + _FORCE_INLINE_ int32_t get_r8() const { return int32_t(CLAMP(r * 255.0, 0.0, 255.0)); } + _FORCE_INLINE_ void set_g8(int32_t g8) { g = (CLAMP(g8, 0, 255) / 255.0); } + _FORCE_INLINE_ int32_t get_g8() const { return int32_t(CLAMP(g * 255.0, 0.0, 255.0)); } + _FORCE_INLINE_ void set_b8(int32_t b8) { b = (CLAMP(b8, 0, 255) / 255.0); } + _FORCE_INLINE_ int32_t get_b8() const { return int32_t(CLAMP(b * 255.0, 0.0, 255.0)); } + _FORCE_INLINE_ void set_a8(int32_t a8) { a = (CLAMP(a8, 0, 255) / 255.0); } + _FORCE_INLINE_ int32_t get_a8() const { return int32_t(CLAMP(a * 255.0, 0.0, 255.0)); } + + _FORCE_INLINE_ void set_h(float p_h) { set_hsv(p_h, get_s(), get_v()); } + _FORCE_INLINE_ void set_s(float p_s) { set_hsv(get_h(), p_s, get_v()); } + _FORCE_INLINE_ void set_v(float p_v) { set_hsv(get_h(), get_s(), p_v); } + + _FORCE_INLINE_ Color() {} + /** - * No construct parameters, r=0, g=0, b=0. a=1 + * RGBA construct parameters. + * Alpha is not optional as otherwise we can't bind the RGB version for scripting. */ - _FORCE_INLINE_ Color() { - r = 0; - g = 0; - b = 0; - a = 1.0; + _FORCE_INLINE_ Color(float p_r, float p_g, float p_b, float p_a) { + r = p_r; + g = p_g; + b = p_b; + a = p_a; } /** - * RGB / RGBA construct parameters. Alpha is optional, but defaults to 1.0 + * RGB construct parameters. */ - _FORCE_INLINE_ Color(float p_r, float p_g, float p_b, float p_a = 1.0) { + _FORCE_INLINE_ Color(float p_r, float p_g, float p_b) { r = p_r; g = p_g; b = p_b; - a = p_a; + a = 1.0; } /** @@ -236,17 +244,23 @@ struct Color { }; bool Color::operator<(const Color &p_color) const { - if (r == p_color.r) { if (g == p_color.g) { if (b == p_color.b) { return (a < p_color.a); - } else + } else { return (b < p_color.b); - } else + } + } else { return g < p_color.g; - } else + } + } else { return r < p_color.r; + } +} + +_FORCE_INLINE_ Color operator*(real_t p_real, const Color &p_color) { + return p_color * p_real; } #endif // COLOR_H diff --git a/core/math/color_names.inc b/core/math/color_names.inc new file mode 100644 index 0000000000..e5b935ea9c --- /dev/null +++ b/core/math/color_names.inc @@ -0,0 +1,160 @@ +// Names from https://en.wikipedia.org/wiki/X11_color_names + +// So this in a way that does not require memory allocation +// the old way leaked memory +// this is not used as often as for more performance to make sense + +struct NamedColor { + const char *name; + Color color; +}; + +static NamedColor named_colors[] = { + { "aliceblue", Color(0.94, 0.97, 1.00) }, + { "antiquewhite", Color(0.98, 0.92, 0.84) }, + { "aqua", Color(0.00, 1.00, 1.00) }, + { "aquamarine", Color(0.50, 1.00, 0.83) }, + { "azure", Color(0.94, 1.00, 1.00) }, + { "beige", Color(0.96, 0.96, 0.86) }, + { "bisque", Color(1.00, 0.89, 0.77) }, + { "black", Color(0.00, 0.00, 0.00) }, + { "blanchedalmond", Color(1.00, 0.92, 0.80) }, + { "blue", Color(0.00, 0.00, 1.00) }, + { "blueviolet", Color(0.54, 0.17, 0.89) }, + { "brown", Color(0.65, 0.16, 0.16) }, + { "burlywood", Color(0.87, 0.72, 0.53) }, + { "cadetblue", Color(0.37, 0.62, 0.63) }, + { "chartreuse", Color(0.50, 1.00, 0.00) }, + { "chocolate", Color(0.82, 0.41, 0.12) }, + { "coral", Color(1.00, 0.50, 0.31) }, + { "cornflower", Color(0.39, 0.58, 0.93) }, + { "cornsilk", Color(1.00, 0.97, 0.86) }, + { "crimson", Color(0.86, 0.08, 0.24) }, + { "cyan", Color(0.00, 1.00, 1.00) }, + { "darkblue", Color(0.00, 0.00, 0.55) }, + { "darkcyan", Color(0.00, 0.55, 0.55) }, + { "darkgoldenrod", Color(0.72, 0.53, 0.04) }, + { "darkgray", Color(0.66, 0.66, 0.66) }, + { "darkgreen", Color(0.00, 0.39, 0.00) }, + { "darkkhaki", Color(0.74, 0.72, 0.42) }, + { "darkmagenta", Color(0.55, 0.00, 0.55) }, + { "darkolivegreen", Color(0.33, 0.42, 0.18) }, + { "darkorange", Color(1.00, 0.55, 0.00) }, + { "darkorchid", Color(0.60, 0.20, 0.80) }, + { "darkred", Color(0.55, 0.00, 0.00) }, + { "darksalmon", Color(0.91, 0.59, 0.48) }, + { "darkseagreen", Color(0.56, 0.74, 0.56) }, + { "darkslateblue", Color(0.28, 0.24, 0.55) }, + { "darkslategray", Color(0.18, 0.31, 0.31) }, + { "darkturquoise", Color(0.00, 0.81, 0.82) }, + { "darkviolet", Color(0.58, 0.00, 0.83) }, + { "deeppink", Color(1.00, 0.08, 0.58) }, + { "deepskyblue", Color(0.00, 0.75, 1.00) }, + { "dimgray", Color(0.41, 0.41, 0.41) }, + { "dodgerblue", Color(0.12, 0.56, 1.00) }, + { "firebrick", Color(0.70, 0.13, 0.13) }, + { "floralwhite", Color(1.00, 0.98, 0.94) }, + { "forestgreen", Color(0.13, 0.55, 0.13) }, + { "fuchsia", Color(1.00, 0.00, 1.00) }, + { "gainsboro", Color(0.86, 0.86, 0.86) }, + { "ghostwhite", Color(0.97, 0.97, 1.00) }, + { "gold", Color(1.00, 0.84, 0.00) }, + { "goldenrod", Color(0.85, 0.65, 0.13) }, + { "gray", Color(0.75, 0.75, 0.75) }, + { "green", Color(0.00, 1.00, 0.00) }, + { "greenyellow", Color(0.68, 1.00, 0.18) }, + { "honeydew", Color(0.94, 1.00, 0.94) }, + { "hotpink", Color(1.00, 0.41, 0.71) }, + { "indianred", Color(0.80, 0.36, 0.36) }, + { "indigo", Color(0.29, 0.00, 0.51) }, + { "ivory", Color(1.00, 1.00, 0.94) }, + { "khaki", Color(0.94, 0.90, 0.55) }, + { "lavender", Color(0.90, 0.90, 0.98) }, + { "lavenderblush", Color(1.00, 0.94, 0.96) }, + { "lawngreen", Color(0.49, 0.99, 0.00) }, + { "lemonchiffon", Color(1.00, 0.98, 0.80) }, + { "lightblue", Color(0.68, 0.85, 0.90) }, + { "lightcoral", Color(0.94, 0.50, 0.50) }, + { "lightcyan", Color(0.88, 1.00, 1.00) }, + { "lightgoldenrod", Color(0.98, 0.98, 0.82) }, + { "lightgray", Color(0.83, 0.83, 0.83) }, + { "lightgreen", Color(0.56, 0.93, 0.56) }, + { "lightpink", Color(1.00, 0.71, 0.76) }, + { "lightsalmon", Color(1.00, 0.63, 0.48) }, + { "lightseagreen", Color(0.13, 0.70, 0.67) }, + { "lightskyblue", Color(0.53, 0.81, 0.98) }, + { "lightslategray", Color(0.47, 0.53, 0.60) }, + { "lightsteelblue", Color(0.69, 0.77, 0.87) }, + { "lightyellow", Color(1.00, 1.00, 0.88) }, + { "lime", Color(0.00, 1.00, 0.00) }, + { "limegreen", Color(0.20, 0.80, 0.20) }, + { "linen", Color(0.98, 0.94, 0.90) }, + { "magenta", Color(1.00, 0.00, 1.00) }, + { "maroon", Color(0.69, 0.19, 0.38) }, + { "mediumaquamarine", Color(0.40, 0.80, 0.67) }, + { "mediumblue", Color(0.00, 0.00, 0.80) }, + { "mediumorchid", Color(0.73, 0.33, 0.83) }, + { "mediumpurple", Color(0.58, 0.44, 0.86) }, + { "mediumseagreen", Color(0.24, 0.70, 0.44) }, + { "mediumslateblue", Color(0.48, 0.41, 0.93) }, + { "mediumspringgreen", Color(0.00, 0.98, 0.60) }, + { "mediumturquoise", Color(0.28, 0.82, 0.80) }, + { "mediumvioletred", Color(0.78, 0.08, 0.52) }, + { "midnightblue", Color(0.10, 0.10, 0.44) }, + { "mintcream", Color(0.96, 1.00, 0.98) }, + { "mistyrose", Color(1.00, 0.89, 0.88) }, + { "moccasin", Color(1.00, 0.89, 0.71) }, + { "navajowhite", Color(1.00, 0.87, 0.68) }, + { "navyblue", Color(0.00, 0.00, 0.50) }, + { "oldlace", Color(0.99, 0.96, 0.90) }, + { "olive", Color(0.50, 0.50, 0.00) }, + { "olivedrab", Color(0.42, 0.56, 0.14) }, + { "orange", Color(1.00, 0.65, 0.00) }, + { "orangered", Color(1.00, 0.27, 0.00) }, + { "orchid", Color(0.85, 0.44, 0.84) }, + { "palegoldenrod", Color(0.93, 0.91, 0.67) }, + { "palegreen", Color(0.60, 0.98, 0.60) }, + { "paleturquoise", Color(0.69, 0.93, 0.93) }, + { "palevioletred", Color(0.86, 0.44, 0.58) }, + { "papayawhip", Color(1.00, 0.94, 0.84) }, + { "peachpuff", Color(1.00, 0.85, 0.73) }, + { "peru", Color(0.80, 0.52, 0.25) }, + { "pink", Color(1.00, 0.75, 0.80) }, + { "plum", Color(0.87, 0.63, 0.87) }, + { "powderblue", Color(0.69, 0.88, 0.90) }, + { "purple", Color(0.63, 0.13, 0.94) }, + { "rebeccapurple", Color(0.40, 0.20, 0.60) }, + { "red", Color(1.00, 0.00, 0.00) }, + { "rosybrown", Color(0.74, 0.56, 0.56) }, + { "royalblue", Color(0.25, 0.41, 0.88) }, + { "saddlebrown", Color(0.55, 0.27, 0.07) }, + { "salmon", Color(0.98, 0.50, 0.45) }, + { "sandybrown", Color(0.96, 0.64, 0.38) }, + { "seagreen", Color(0.18, 0.55, 0.34) }, + { "seashell", Color(1.00, 0.96, 0.93) }, + { "sienna", Color(0.63, 0.32, 0.18) }, + { "silver", Color(0.75, 0.75, 0.75) }, + { "skyblue", Color(0.53, 0.81, 0.92) }, + { "slateblue", Color(0.42, 0.35, 0.80) }, + { "slategray", Color(0.44, 0.50, 0.56) }, + { "snow", Color(1.00, 0.98, 0.98) }, + { "springgreen", Color(0.00, 1.00, 0.50) }, + { "steelblue", Color(0.27, 0.51, 0.71) }, + { "tan", Color(0.82, 0.71, 0.55) }, + { "teal", Color(0.00, 0.50, 0.50) }, + { "thistle", Color(0.85, 0.75, 0.85) }, + { "tomato", Color(1.00, 0.39, 0.28) }, + { "transparent", Color(1.00, 1.00, 1.00, 0.00) }, + { "turquoise", Color(0.25, 0.88, 0.82) }, + { "violet", Color(0.93, 0.51, 0.93) }, + { "webgray", Color(0.50, 0.50, 0.50) }, + { "webgreen", Color(0.00, 0.50, 0.00) }, + { "webmaroon", Color(0.50, 0.00, 0.00) }, + { "webpurple", Color(0.50, 0.00, 0.50) }, + { "wheat", Color(0.96, 0.87, 0.70) }, + { "white", Color(1.00, 1.00, 1.00) }, + { "whitesmoke", Color(0.96, 0.96, 0.96) }, + { "yellow", Color(1.00, 1.00, 0.00) }, + { "yellowgreen", Color(0.60, 0.80, 0.20) }, + { nullptr, Color() }, +}; diff --git a/core/math/delaunay.h b/core/math/delaunay_2d.h index 29f84210d2..95064e5700 100644 --- a/core/math/delaunay.h +++ b/core/math/delaunay_2d.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* delaunay.h */ +/* delaunay_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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,39 +28,35 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef DELAUNAY_H -#define DELAUNAY_H +#ifndef DELAUNAY_2D_H +#define DELAUNAY_2D_H #include "core/math/rect2.h" class Delaunay2D { public: struct Triangle { - int points[3]; - bool bad; - Triangle() { bad = false; } + bool bad = false; + Triangle() {} Triangle(int p_a, int p_b, int p_c) { points[0] = p_a; points[1] = p_b; points[2] = p_c; - bad = false; } }; struct Edge { int edge[2]; - bool bad; - Edge() { bad = false; } + bool bad = false; + Edge() {} Edge(int p_a, int p_b) { - bad = false; edge[0] = p_a; edge[1] = p_b; } }; static bool circum_circle_contains(const Vector<Vector2> &p_vertices, const Triangle &p_triangle, int p_vertex) { - Vector2 p1 = p_vertices[p_triangle.points[0]]; Vector2 p2 = p_vertices[p_triangle.points[1]]; Vector2 p3 = p_vertices[p_triangle.points[2]]; @@ -92,7 +88,6 @@ public: } static Vector<Triangle> triangulate(const Vector<Vector2> &p_points) { - Vector<Vector2> points = p_points; Vector<Triangle> triangles; @@ -115,9 +110,6 @@ public: triangles.push_back(Triangle(p_points.size() + 0, p_points.size() + 1, p_points.size() + 2)); for (int i = 0; i < p_points.size(); i++) { - //std::cout << "Traitement du point " << *p << std::endl; - //std::cout << "_triangles contains " << _triangles.size() << " elements" << std::endl; - Vector<Edge> polygon; for (int j = 0; j < triangles.size(); j++) { @@ -146,7 +138,6 @@ public: } for (int j = 0; j < polygon.size(); j++) { - if (polygon[j].bad) { continue; } @@ -172,4 +163,4 @@ public: } }; -#endif // DELAUNAY_H +#endif // DELAUNAY_2D_H diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h new file mode 100644 index 0000000000..25cc1125db --- /dev/null +++ b/core/math/delaunay_3d.h @@ -0,0 +1,408 @@ +/*************************************************************************/ +/* delaunay_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef DELAUNAY_3D_H +#define DELAUNAY_3D_H + +#include "core/math/aabb.h" +#include "core/math/camera_matrix.h" +#include "core/math/vector3.h" +#include "core/os/file_access.h" +#include "core/string/print_string.h" +#include "core/templates/local_vector.h" +#include "core/templates/oa_hash_map.h" +#include "core/templates/vector.h" +#include "core/variant/variant.h" + +#include "thirdparty/misc/r128.h" + +class Delaunay3D { + struct Simplex; + + enum { + ACCEL_GRID_SIZE = 16 + }; + struct GridPos { + Vector3i pos; + List<Simplex *>::Element *E = nullptr; + }; + + struct Simplex { + uint32_t points[4]; + R128 circum_center_x; + R128 circum_center_y; + R128 circum_center_z; + R128 circum_r2; + LocalVector<GridPos> grid_positions; + List<Simplex *>::Element *SE = nullptr; + + _FORCE_INLINE_ Simplex() {} + _FORCE_INLINE_ Simplex(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d) { + points[0] = p_a; + points[1] = p_b; + points[2] = p_c; + points[3] = p_d; + } + }; + + struct Triangle { + uint32_t triangle[3]; + bool bad = false; + _FORCE_INLINE_ bool operator==(const Triangle &p_triangle) const { + return triangle[0] == p_triangle.triangle[0] && triangle[1] == p_triangle.triangle[1] && triangle[2] == p_triangle.triangle[2]; + } + + _FORCE_INLINE_ Triangle() {} + _FORCE_INLINE_ Triangle(uint32_t p_a, uint32_t p_b, uint32_t p_c) { + if (p_a > p_b) { + SWAP(p_a, p_b); + } + if (p_b > p_c) { + SWAP(p_b, p_c); + } + if (p_a > p_b) { + SWAP(p_a, p_b); + } + + triangle[0] = p_a; + triangle[1] = p_b; + triangle[2] = p_c; + } + }; + + struct TriangleHasher { + _FORCE_INLINE_ static uint32_t hash(const Triangle &p_triangle) { + uint32_t h = hash_djb2_one_32(p_triangle.triangle[0]); + h = hash_djb2_one_32(p_triangle.triangle[1], h); + return hash_djb2_one_32(p_triangle.triangle[2], h); + } + }; + + _FORCE_INLINE_ static void circum_sphere_compute(const Vector3 *p_points, Simplex *p_simplex) { + // the only part in the algorithm where there may be precision errors is this one, so ensure that + // we do it as maximum precision as possible + + R128 v0_x = p_points[p_simplex->points[0]].x; + R128 v0_y = p_points[p_simplex->points[0]].y; + R128 v0_z = p_points[p_simplex->points[0]].z; + R128 v1_x = p_points[p_simplex->points[1]].x; + R128 v1_y = p_points[p_simplex->points[1]].y; + R128 v1_z = p_points[p_simplex->points[1]].z; + R128 v2_x = p_points[p_simplex->points[2]].x; + R128 v2_y = p_points[p_simplex->points[2]].y; + R128 v2_z = p_points[p_simplex->points[2]].z; + R128 v3_x = p_points[p_simplex->points[3]].x; + R128 v3_y = p_points[p_simplex->points[3]].y; + R128 v3_z = p_points[p_simplex->points[3]].z; + + //Create the rows of our "unrolled" 3x3 matrix + R128 row1_x = v1_x - v0_x; + R128 row1_y = v1_y - v0_y; + R128 row1_z = v1_z - v0_z; + + R128 row2_x = v2_x - v0_x; + R128 row2_y = v2_y - v0_y; + R128 row2_z = v2_z - v0_z; + + R128 row3_x = v3_x - v0_x; + R128 row3_y = v3_y - v0_y; + R128 row3_z = v3_z - v0_z; + + R128 sq_lenght1 = row1_x * row1_x + row1_y * row1_y + row1_z * row1_z; + R128 sq_lenght2 = row2_x * row2_x + row2_y * row2_y + row2_z * row2_z; + R128 sq_lenght3 = row3_x * row3_x + row3_y * row3_y + row3_z * row3_z; + + //Compute the determinant of said matrix + R128 determinant = row1_x * (row2_y * row3_z - row3_y * row2_z) - row2_x * (row1_y * row3_z - row3_y * row1_z) + row3_x * (row1_y * row2_z - row2_y * row1_z); + + // Compute the volume of the tetrahedron, and precompute a scalar quantity for re-use in the formula + R128 volume = determinant / R128(6.f); + R128 i12volume = R128(1.f) / (volume * R128(12.f)); + + R128 center_x = v0_x + i12volume * ((row2_y * row3_z - row3_y * row2_z) * sq_lenght1 - (row1_y * row3_z - row3_y * row1_z) * sq_lenght2 + (row1_y * row2_z - row2_y * row1_z) * sq_lenght3); + R128 center_y = v0_y + i12volume * (-(row2_x * row3_z - row3_x * row2_z) * sq_lenght1 + (row1_x * row3_z - row3_x * row1_z) * sq_lenght2 - (row1_x * row2_z - row2_x * row1_z) * sq_lenght3); + R128 center_z = v0_z + i12volume * ((row2_x * row3_y - row3_x * row2_y) * sq_lenght1 - (row1_x * row3_y - row3_x * row1_y) * sq_lenght2 + (row1_x * row2_y - row2_x * row1_y) * sq_lenght3); + + //Once we know the center, the radius is clearly the distance to any vertex + + R128 rel1_x = center_x - v0_x; + R128 rel1_y = center_y - v0_y; + R128 rel1_z = center_z - v0_z; + + R128 radius1 = rel1_x * rel1_x + rel1_y * rel1_y + rel1_z * rel1_z; + + p_simplex->circum_center_x = center_x; + p_simplex->circum_center_y = center_y; + p_simplex->circum_center_z = center_z; + p_simplex->circum_r2 = radius1; + } + + _FORCE_INLINE_ static bool simplex_contains(const Vector3 *p_points, const Simplex &p_simplex, uint32_t p_vertex) { + R128 v_x = p_points[p_vertex].x; + R128 v_y = p_points[p_vertex].y; + R128 v_z = p_points[p_vertex].z; + + R128 rel2_x = p_simplex.circum_center_x - v_x; + R128 rel2_y = p_simplex.circum_center_y - v_y; + R128 rel2_z = p_simplex.circum_center_z - v_z; + + R128 radius2 = rel2_x * rel2_x + rel2_y * rel2_y + rel2_z * rel2_z; + + return radius2 < (p_simplex.circum_r2 - R128(0.00001)); + } + + static bool simplex_is_coplanar(const Vector3 *p_points, const Simplex &p_simplex) { + Plane p(p_points[p_simplex.points[0]], p_points[p_simplex.points[1]], p_points[p_simplex.points[2]]); + if (ABS(p.distance_to(p_points[p_simplex.points[3]])) < CMP_EPSILON) { + return true; + } + + CameraMatrix cm; + + cm.matrix[0][0] = p_points[p_simplex.points[0]].x; + cm.matrix[0][1] = p_points[p_simplex.points[1]].x; + cm.matrix[0][2] = p_points[p_simplex.points[2]].x; + cm.matrix[0][3] = p_points[p_simplex.points[3]].x; + + cm.matrix[1][0] = p_points[p_simplex.points[0]].y; + cm.matrix[1][1] = p_points[p_simplex.points[1]].y; + cm.matrix[1][2] = p_points[p_simplex.points[2]].y; + cm.matrix[1][3] = p_points[p_simplex.points[3]].y; + + cm.matrix[2][0] = p_points[p_simplex.points[0]].z; + cm.matrix[2][1] = p_points[p_simplex.points[1]].z; + cm.matrix[2][2] = p_points[p_simplex.points[2]].z; + cm.matrix[2][3] = p_points[p_simplex.points[3]].z; + + cm.matrix[3][0] = 1.0; + cm.matrix[3][1] = 1.0; + cm.matrix[3][2] = 1.0; + cm.matrix[3][3] = 1.0; + + return ABS(cm.determinant()) <= CMP_EPSILON; + } + +public: + struct OutputSimplex { + uint32_t points[4]; + }; + + static Vector<OutputSimplex> tetrahedralize(const Vector<Vector3> &p_points) { + uint32_t point_count = p_points.size(); + Vector3 *points = (Vector3 *)memalloc(sizeof(Vector3) * (point_count + 4)); + + { + const Vector3 *src_points = p_points.ptr(); + AABB rect; + for (uint32_t i = 0; i < point_count; i++) { + Vector3 point = src_points[i]; + if (i == 0) { + rect.position = point; + } else { + rect.expand_to(point); + } + points[i] = point; + } + + for (uint32_t i = 0; i < point_count; i++) { + points[i] = (points[i] - rect.position) / rect.size; + } + + float delta_max = Math::sqrt(2.0) * 20.0; + Vector3 center = Vector3(0.5, 0.5, 0.5); + + // any simplex that contains everything is good + points[point_count + 0] = center + Vector3(0, 1, 0) * delta_max; + points[point_count + 1] = center + Vector3(0, -1, 1) * delta_max; + points[point_count + 2] = center + Vector3(1, -1, -1) * delta_max; + points[point_count + 3] = center + Vector3(-1, -1, -1) * delta_max; + } + + List<Simplex *> acceleration_grid[ACCEL_GRID_SIZE][ACCEL_GRID_SIZE][ACCEL_GRID_SIZE]; + + List<Simplex *> simplex_list; + { + //create root simplex + Simplex *root = memnew(Simplex(point_count + 0, point_count + 1, point_count + 2, point_count + 3)); + root->SE = simplex_list.push_back(root); + + for (uint32_t i = 0; i < ACCEL_GRID_SIZE; i++) { + for (uint32_t j = 0; j < ACCEL_GRID_SIZE; j++) { + for (uint32_t k = 0; k < ACCEL_GRID_SIZE; k++) { + GridPos gp; + gp.E = acceleration_grid[i][j][k].push_back(root); + gp.pos = Vector3i(i, j, k); + root->grid_positions.push_back(gp); + } + } + } + + circum_sphere_compute(points, root); + } + + OAHashMap<Triangle, uint32_t, TriangleHasher> triangles_inserted; + LocalVector<Triangle> triangles; + + for (uint32_t i = 0; i < point_count; i++) { + bool unique = true; + for (uint32_t j = i + 1; j < point_count; j++) { + if (points[i].is_equal_approx(points[j])) { + unique = false; + break; + } + } + if (!unique) { + continue; + } + + Vector3i grid_pos = Vector3i(points[i] * ACCEL_GRID_SIZE); + grid_pos.x = CLAMP(grid_pos.x, 0, ACCEL_GRID_SIZE - 1); + grid_pos.y = CLAMP(grid_pos.y, 0, ACCEL_GRID_SIZE - 1); + grid_pos.z = CLAMP(grid_pos.z, 0, ACCEL_GRID_SIZE - 1); + + for (List<Simplex *>::Element *E = acceleration_grid[grid_pos.x][grid_pos.y][grid_pos.z].front(); E;) { + List<Simplex *>::Element *N = E->next(); //may be deleted + + Simplex *simplex = E->get(); + + if (simplex_contains(points, *simplex, i)) { + static const uint32_t triangle_order[4][3] = { + { 0, 1, 2 }, + { 0, 1, 3 }, + { 0, 2, 3 }, + { 1, 2, 3 }, + }; + + for (uint32_t k = 0; k < 4; k++) { + Triangle t = Triangle(simplex->points[triangle_order[k][0]], simplex->points[triangle_order[k][1]], simplex->points[triangle_order[k][2]]); + uint32_t *p = triangles_inserted.lookup_ptr(t); + if (p) { + triangles[*p].bad = true; + } else { + triangles_inserted.insert(t, triangles.size()); + triangles.push_back(t); + } + } + + //remove simplex and continue + simplex_list.erase(simplex->SE); + + for (uint32_t k = 0; k < simplex->grid_positions.size(); k++) { + Vector3i p = simplex->grid_positions[k].pos; + acceleration_grid[p.x][p.y][p.z].erase(simplex->grid_positions[k].E); + } + memdelete(simplex); + } + E = N; + } + + uint32_t good_triangles = 0; + for (uint32_t j = 0; j < triangles.size(); j++) { + if (triangles[j].bad) { + continue; + } + Simplex *new_simplex = memnew(Simplex(triangles[j].triangle[0], triangles[j].triangle[1], triangles[j].triangle[2], i)); + circum_sphere_compute(points, new_simplex); + new_simplex->SE = simplex_list.push_back(new_simplex); + { + Vector3 center; + center.x = double(new_simplex->circum_center_x); + center.y = double(new_simplex->circum_center_y); + center.z = double(new_simplex->circum_center_z); + + float radius2 = Math::sqrt(double(new_simplex->circum_r2)); + radius2 += 0.0001; // + Vector3 extents = Vector3(radius2, radius2, radius2); + Vector3i from = Vector3i((center - extents) * ACCEL_GRID_SIZE); + Vector3i to = Vector3i((center + extents) * ACCEL_GRID_SIZE); + from.x = CLAMP(from.x, 0, ACCEL_GRID_SIZE - 1); + from.y = CLAMP(from.y, 0, ACCEL_GRID_SIZE - 1); + from.z = CLAMP(from.z, 0, ACCEL_GRID_SIZE - 1); + to.x = CLAMP(to.x, 0, ACCEL_GRID_SIZE - 1); + to.y = CLAMP(to.y, 0, ACCEL_GRID_SIZE - 1); + to.z = CLAMP(to.z, 0, ACCEL_GRID_SIZE - 1); + + for (int32_t x = from.x; x <= to.x; x++) { + for (int32_t y = from.y; y <= to.y; y++) { + for (int32_t z = from.z; z <= to.z; z++) { + GridPos gp; + gp.pos = Vector3(x, y, z); + gp.E = acceleration_grid[x][y][z].push_back(new_simplex); + new_simplex->grid_positions.push_back(gp); + } + } + } + } + + good_triangles++; + } + + //print_line("at point " + itos(i) + "/" + itos(point_count) + " simplices added " + itos(good_triangles) + "/" + itos(simplex_list.size()) + " - triangles: " + itos(triangles.size())); + triangles.clear(); + triangles_inserted.clear(); + } + + //print_line("end with simplices: " + itos(simplex_list.size())); + Vector<OutputSimplex> ret_simplices; + ret_simplices.resize(simplex_list.size()); + OutputSimplex *ret_simplicesw = ret_simplices.ptrw(); + uint32_t simplices_written = 0; + + for (List<Simplex *>::Element *E = simplex_list.front(); E; E = E->next()) { + Simplex *simplex = E->get(); + bool invalid = false; + for (int j = 0; j < 4; j++) { + if (simplex->points[j] >= point_count) { + invalid = true; + break; + } + } + if (invalid || simplex_is_coplanar(points, *simplex)) { + memdelete(simplex); + continue; + } + + ret_simplicesw[simplices_written].points[0] = simplex->points[0]; + ret_simplicesw[simplices_written].points[1] = simplex->points[1]; + ret_simplicesw[simplices_written].points[2] = simplex->points[2]; + ret_simplicesw[simplices_written].points[3] = simplex->points[3]; + simplices_written++; + memdelete(simplex); + } + + ret_simplices.resize(simplices_written); + + memfree(points); + + return ret_simplices; + } +}; + +#endif // DELAUNAY_3D_H diff --git a/core/math/disjoint_set.cpp b/core/math/disjoint_set.cpp deleted file mode 100644 index a508151ad3..0000000000 --- a/core/math/disjoint_set.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/*************************************************************************/ -/* disjoint_set.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 "disjoint_set.h" diff --git a/core/math/disjoint_set.h b/core/math/disjoint_set.h index 32b9875e4c..b155412f64 100644 --- a/core/math/disjoint_set.h +++ b/core/math/disjoint_set.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,8 +31,8 @@ #ifndef DISJOINT_SET_H #define DISJOINT_SET_H -#include "core/map.h" -#include "core/vector.h" +#include "core/templates/map.h" +#include "core/templates/vector.h" /** @author Marios Staikopoulos <marios@staik.net> @@ -41,7 +41,6 @@ /* This DisjointSet class uses Find with path compression and Union by rank */ template <typename T, class C = Comparator<T>, class AL = DefaultAllocator> class DisjointSet { - struct Element { T object; Element *parent = nullptr; @@ -103,7 +102,6 @@ typename DisjointSet<T, C, AL>::Element *DisjointSet<T, C, AL>::insert_or_get(T template <typename T, class C, class AL> void DisjointSet<T, C, AL>::create_union(T a, T b) { - Element *x = insert_or_get(a); Element *y = insert_or_get(b); @@ -111,8 +109,9 @@ void DisjointSet<T, C, AL>::create_union(T a, T b) { Element *y_root = get_parent(y); // Already in the same set - if (x_root == y_root) + if (x_root == y_root) { return; + } // Not in the same set, merge if (x_root->rank < y_root->rank) { diff --git a/core/math/dynamic_bvh.cpp b/core/math/dynamic_bvh.cpp new file mode 100644 index 0000000000..4639a52278 --- /dev/null +++ b/core/math/dynamic_bvh.cpp @@ -0,0 +1,431 @@ +/*************************************************************************/ +/* dynamic_bvh.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "dynamic_bvh.h" + +void DynamicBVH::_delete_node(Node *p_node) { + node_allocator.free(p_node); +} + +void DynamicBVH::_recurse_delete_node(Node *p_node) { + if (!p_node->is_leaf()) { + _recurse_delete_node(p_node->childs[0]); + _recurse_delete_node(p_node->childs[1]); + } + if (p_node == bvh_root) { + bvh_root = nullptr; + } + _delete_node(p_node); +} + +DynamicBVH::Node *DynamicBVH::_create_node(Node *p_parent, void *p_data) { + Node *node = node_allocator.alloc(); + node->parent = p_parent; + node->data = p_data; + return (node); +} + +DynamicBVH::Node *DynamicBVH::_create_node_with_volume(Node *p_parent, const Volume &p_volume, void *p_data) { + Node *node = _create_node(p_parent, p_data); + node->volume = p_volume; + return node; +} + +void DynamicBVH::_insert_leaf(Node *p_root, Node *p_leaf) { + if (!bvh_root) { + bvh_root = p_leaf; + p_leaf->parent = 0; + } else { + if (!p_root->is_leaf()) { + do { + p_root = p_root->childs[p_leaf->volume.select_by_proximity( + p_root->childs[0]->volume, + p_root->childs[1]->volume)]; + } while (!p_root->is_leaf()); + } + Node *prev = p_root->parent; + Node *node = _create_node_with_volume(prev, p_leaf->volume.merge(p_root->volume), 0); + if (prev) { + prev->childs[p_root->get_index_in_parent()] = node; + node->childs[0] = p_root; + p_root->parent = node; + node->childs[1] = p_leaf; + p_leaf->parent = node; + do { + if (!prev->volume.contains(node->volume)) { + prev->volume = prev->childs[0]->volume.merge(prev->childs[1]->volume); + } else { + break; + } + node = prev; + } while (0 != (prev = node->parent)); + } else { + node->childs[0] = p_root; + p_root->parent = node; + node->childs[1] = p_leaf; + p_leaf->parent = node; + bvh_root = node; + } + } +} + +DynamicBVH::Node *DynamicBVH::_remove_leaf(Node *leaf) { + if (leaf == bvh_root) { + bvh_root = 0; + return (0); + } else { + Node *parent = leaf->parent; + Node *prev = parent->parent; + Node *sibling = parent->childs[1 - leaf->get_index_in_parent()]; + if (prev) { + prev->childs[parent->get_index_in_parent()] = sibling; + sibling->parent = prev; + _delete_node(parent); + while (prev) { + const Volume pb = prev->volume; + prev->volume = prev->childs[0]->volume.merge(prev->childs[1]->volume); + if (pb.is_not_equal_to(prev->volume)) { + prev = prev->parent; + } else + break; + } + return (prev ? prev : bvh_root); + } else { + bvh_root = sibling; + sibling->parent = 0; + _delete_node(parent); + return (bvh_root); + } + } +} + +void DynamicBVH::_fetch_leaves(Node *p_root, LocalVector<Node *> &r_leaves, int p_depth) { + if (p_root->is_internal() && p_depth) { + _fetch_leaves(p_root->childs[0], r_leaves, p_depth - 1); + _fetch_leaves(p_root->childs[1], r_leaves, p_depth - 1); + _delete_node(p_root); + } else { + r_leaves.push_back(p_root); + } +} + +// Partitions leaves such that leaves[0, n) are on the +// left of axis, and leaves[n, count) are on the right +// of axis. returns N. +int DynamicBVH::_split(Node **leaves, int p_count, const Vector3 &p_org, const Vector3 &p_axis) { + int begin = 0; + int end = p_count; + for (;;) { + while (begin != end && leaves[begin]->is_left_of_axis(p_org, p_axis)) { + ++begin; + } + + if (begin == end) { + break; + } + + while (begin != end && !leaves[end - 1]->is_left_of_axis(p_org, p_axis)) { + --end; + } + + if (begin == end) { + break; + } + + // swap out of place nodes + --end; + Node *temp = leaves[begin]; + leaves[begin] = leaves[end]; + leaves[end] = temp; + ++begin; + } + + return begin; +} + +DynamicBVH::Volume DynamicBVH::_bounds(Node **leaves, int p_count) { + Volume volume = leaves[0]->volume; + for (int i = 1, ni = p_count; i < ni; ++i) { + volume = volume.merge(leaves[i]->volume); + } + return (volume); +} + +void DynamicBVH::_bottom_up(Node **leaves, int p_count) { + while (p_count > 1) { + real_t minsize = Math_INF; + int minidx[2] = { -1, -1 }; + for (int i = 0; i < p_count; ++i) { + for (int j = i + 1; j < p_count; ++j) { + const real_t sz = leaves[i]->volume.merge(leaves[j]->volume).get_size(); + if (sz < minsize) { + minsize = sz; + minidx[0] = i; + minidx[1] = j; + } + } + } + Node *n[] = { leaves[minidx[0]], leaves[minidx[1]] }; + Node *p = _create_node_with_volume(nullptr, n[0]->volume.merge(n[1]->volume), nullptr); + p->childs[0] = n[0]; + p->childs[1] = n[1]; + n[0]->parent = p; + n[1]->parent = p; + leaves[minidx[0]] = p; + leaves[minidx[1]] = leaves[p_count - 1]; + --p_count; + } +} + +DynamicBVH::Node *DynamicBVH::_top_down(Node **leaves, int p_count, int p_bu_threshold) { + static const Vector3 axis[] = { Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1) }; + + ERR_FAIL_COND_V(p_bu_threshold <= 1, nullptr); + if (p_count > 1) { + if (p_count > p_bu_threshold) { + const Volume vol = _bounds(leaves, p_count); + const Vector3 org = vol.get_center(); + int partition; + int bestaxis = -1; + int bestmidp = p_count; + int splitcount[3][2] = { { 0, 0 }, { 0, 0 }, { 0, 0 } }; + int i; + for (i = 0; i < p_count; ++i) { + const Vector3 x = leaves[i]->volume.get_center() - org; + for (int j = 0; j < 3; ++j) { + ++splitcount[j][x.dot(axis[j]) > 0 ? 1 : 0]; + } + } + for (i = 0; i < 3; ++i) { + if ((splitcount[i][0] > 0) && (splitcount[i][1] > 0)) { + const int midp = (int)Math::abs(real_t(splitcount[i][0] - splitcount[i][1])); + if (midp < bestmidp) { + bestaxis = i; + bestmidp = midp; + } + } + } + if (bestaxis >= 0) { + partition = _split(leaves, p_count, org, axis[bestaxis]); + ERR_FAIL_COND_V(partition == 0 || partition == p_count, nullptr); + } else { + partition = p_count / 2 + 1; + } + + Node *node = _create_node_with_volume(nullptr, vol, nullptr); + node->childs[0] = _top_down(&leaves[0], partition, p_bu_threshold); + node->childs[1] = _top_down(&leaves[partition], p_count - partition, p_bu_threshold); + node->childs[0]->parent = node; + node->childs[1]->parent = node; + return (node); + } else { + _bottom_up(leaves, p_count); + return (leaves[0]); + } + } + return (leaves[0]); +} + +DynamicBVH::Node *DynamicBVH::_node_sort(Node *n, Node *&r) { + Node *p = n->parent; + ERR_FAIL_COND_V(!n->is_internal(), nullptr); + if (p > n) { + const int i = n->get_index_in_parent(); + const int j = 1 - i; + Node *s = p->childs[j]; + Node *q = p->parent; + ERR_FAIL_COND_V(n != p->childs[i], nullptr); + if (q) + q->childs[p->get_index_in_parent()] = n; + else + r = n; + s->parent = n; + p->parent = n; + n->parent = q; + p->childs[0] = n->childs[0]; + p->childs[1] = n->childs[1]; + n->childs[0]->parent = p; + n->childs[1]->parent = p; + n->childs[i] = p; + n->childs[j] = s; + SWAP(p->volume, n->volume); + return (p); + } + return (n); +} + +void DynamicBVH::clear() { + if (bvh_root) { + _recurse_delete_node(bvh_root); + } + lkhd = -1; + opath = 0; +} + +void DynamicBVH::optimize_bottom_up() { + if (bvh_root) { + LocalVector<Node *> leaves; + _fetch_leaves(bvh_root, leaves); + _bottom_up(&leaves[0], leaves.size()); + bvh_root = leaves[0]; + } +} + +void DynamicBVH::optimize_top_down(int bu_threshold) { + if (bvh_root) { + LocalVector<Node *> leaves; + _fetch_leaves(bvh_root, leaves); + bvh_root = _top_down(&leaves[0], leaves.size(), bu_threshold); + } +} + +void DynamicBVH::optimize_incremental(int passes) { + if (passes < 0) + passes = total_leaves; + if (bvh_root && (passes > 0)) { + do { + Node *node = bvh_root; + unsigned bit = 0; + while (node->is_internal()) { + node = _node_sort(node, bvh_root)->childs[(opath >> bit) & 1]; + bit = (bit + 1) & (sizeof(unsigned) * 8 - 1); + } + _update(node); + ++opath; + } while (--passes); + } +} + +DynamicBVH::ID DynamicBVH::insert(const AABB &p_box, void *p_userdata) { + Volume volume; + volume.min = p_box.position; + volume.max = p_box.position + p_box.size; + + Node *leaf = _create_node_with_volume(nullptr, volume, p_userdata); + _insert_leaf(bvh_root, leaf); + ++total_leaves; + + ID id; + id.node = leaf; + + return id; +} + +void DynamicBVH::_update(Node *leaf, int lookahead) { + Node *root = _remove_leaf(leaf); + if (root) { + if (lookahead >= 0) { + for (int i = 0; (i < lookahead) && root->parent; ++i) { + root = root->parent; + } + } else + root = bvh_root; + } + _insert_leaf(root, leaf); +} + +bool DynamicBVH::update(const ID &p_id, const AABB &p_box) { + ERR_FAIL_COND_V(!p_id.is_valid(), false); + Node *leaf = p_id.node; + + Volume volume; + volume.min = p_box.position; + volume.max = p_box.position + p_box.size; + + if (leaf->volume.min.is_equal_approx(volume.min) && leaf->volume.max.is_equal_approx(volume.max)) { + // noop + return false; + } + + Node *base = _remove_leaf(leaf); + if (base) { + if (lkhd >= 0) { + for (int i = 0; (i < lkhd) && base->parent; ++i) { + base = base->parent; + } + } else + base = bvh_root; + } + leaf->volume = volume; + _insert_leaf(base, leaf); + return true; +} + +void DynamicBVH::remove(const ID &p_id) { + ERR_FAIL_COND(!p_id.is_valid()); + Node *leaf = p_id.node; + _remove_leaf(leaf); + _delete_node(leaf); + --total_leaves; +} + +void DynamicBVH::_extract_leaves(Node *p_node, List<ID> *r_elements) { + if (p_node->is_internal()) { + _extract_leaves(p_node->childs[0], r_elements); + _extract_leaves(p_node->childs[1], r_elements); + } else { + ID id; + id.node = p_node; + r_elements->push_back(id); + } +} + +void DynamicBVH::set_index(uint32_t p_index) { + ERR_FAIL_COND(bvh_root != nullptr); + index = p_index; +} + +uint32_t DynamicBVH::get_index() const { + return index; +} + +void DynamicBVH::get_elements(List<ID> *r_elements) { + if (bvh_root) { + _extract_leaves(bvh_root, r_elements); + } +} + +int DynamicBVH::get_leaf_count() const { + return total_leaves; +} +int DynamicBVH::get_max_depth() const { + if (bvh_root) { + int depth = 1; + int max_depth = 0; + bvh_root->get_max_depth(depth, max_depth); + return max_depth; + } else { + return 0; + } +} + +DynamicBVH::~DynamicBVH() { + clear(); +} diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h new file mode 100644 index 0000000000..c71db2d24d --- /dev/null +++ b/core/math/dynamic_bvh.h @@ -0,0 +1,468 @@ +/*************************************************************************/ +/* dynamic_bvh.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 DYNAMICBVH_H +#define DYNAMICBVH_H + +#include "core/math/aabb.h" +#include "core/templates/list.h" +#include "core/templates/local_vector.h" +#include "core/templates/paged_allocator.h" +#include "core/typedefs.h" + +// Based on bullet Dbvh + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +///DynamicBVH implementation by Nathanael Presson +// The DynamicBVH class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree). + +class DynamicBVH { + struct Node; + +public: + struct ID { + Node *node = nullptr; + + public: + _FORCE_INLINE_ bool is_valid() const { return node != nullptr; } + }; + +private: + struct Volume { + Vector3 min, max; + + _FORCE_INLINE_ Vector3 get_center() const { return ((min + max) / 2); } + _FORCE_INLINE_ Vector3 get_length() const { return (max - min); } + + _FORCE_INLINE_ bool contains(const Volume &a) const { + return ((min.x <= a.min.x) && + (min.y <= a.min.y) && + (min.z <= a.min.z) && + (max.x >= a.max.x) && + (max.y >= a.max.y) && + (max.z >= a.max.z)); + } + + _FORCE_INLINE_ Volume merge(const Volume &b) const { + Volume r; + for (int i = 0; i < 3; ++i) { + if (min[i] < b.min[i]) + r.min[i] = min[i]; + else + r.min[i] = b.min[i]; + if (max[i] > b.max[i]) + r.max[i] = max[i]; + else + r.max[i] = b.max[i]; + } + return r; + } + + _FORCE_INLINE_ real_t get_size() const { + const Vector3 edges = get_length(); + return (edges.x * edges.y * edges.z + + edges.x + edges.y + edges.z); + } + + _FORCE_INLINE_ bool is_not_equal_to(const Volume &b) const { + return ((min.x != b.min.x) || + (min.y != b.min.y) || + (min.z != b.min.z) || + (max.x != b.max.x) || + (max.y != b.max.y) || + (max.z != b.max.z)); + } + + _FORCE_INLINE_ real_t get_proximity_to(const Volume &b) const { + const Vector3 d = (min + max) - (b.min + b.max); + return (Math::abs(d.x) + Math::abs(d.y) + Math::abs(d.z)); + } + + _FORCE_INLINE_ int select_by_proximity(const Volume &a, const Volume &b) const { + return (get_proximity_to(a) < get_proximity_to(b) ? 0 : 1); + } + + // + _FORCE_INLINE_ bool intersects(const Volume &b) const { + return ((min.x <= b.max.x) && + (max.x >= b.min.x) && + (min.y <= b.max.y) && + (max.y >= b.min.y) && + (min.z <= b.max.z) && + (max.z >= b.min.z)); + } + + _FORCE_INLINE_ bool intersects_convex(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const { + Vector3 half_extents = (max - min) * 0.5; + Vector3 ofs = min + half_extents; + + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + Vector3 point( + (p.normal.x > 0) ? -half_extents.x : half_extents.x, + (p.normal.y > 0) ? -half_extents.y : half_extents.y, + (p.normal.z > 0) ? -half_extents.z : half_extents.z); + point += ofs; + if (p.is_point_over(point)) { + return false; + } + } + + // Make sure all points in the shape aren't fully separated from the AABB on + // each axis. + int bad_point_counts_positive[3] = { 0 }; + int bad_point_counts_negative[3] = { 0 }; + + for (int k = 0; k < 3; k++) { + for (int i = 0; i < p_point_count; i++) { + if (p_points[i].coord[k] > ofs.coord[k] + half_extents.coord[k]) { + bad_point_counts_positive[k]++; + } + if (p_points[i].coord[k] < ofs.coord[k] - half_extents.coord[k]) { + bad_point_counts_negative[k]++; + } + } + + if (bad_point_counts_negative[k] == p_point_count) { + return false; + } + if (bad_point_counts_positive[k] == p_point_count) { + return false; + } + } + + return true; + } + }; + + struct Node { + Volume volume; + Node *parent = nullptr; + union { + Node *childs[2]; + void *data; + }; + + _FORCE_INLINE_ bool is_leaf() const { return childs[1] == nullptr; } + _FORCE_INLINE_ bool is_internal() const { return (!is_leaf()); } + + _FORCE_INLINE_ int get_index_in_parent() const { + ERR_FAIL_COND_V(!parent, 0); + return (parent->childs[1] == this) ? 1 : 0; + } + void get_max_depth(int depth, int &maxdepth) { + if (is_internal()) { + childs[0]->get_max_depth(depth + 1, maxdepth); + childs[1]->get_max_depth(depth + 1, maxdepth); + } else { + maxdepth = MAX(maxdepth, depth); + } + } + + // + int count_leaves() const { + if (is_internal()) + return childs[0]->count_leaves() + childs[1]->count_leaves(); + else + return (1); + } + + bool is_left_of_axis(const Vector3 &org, const Vector3 &axis) const { + return axis.dot(volume.get_center() - org) <= 0; + } + + Node() { + childs[0] = nullptr; + childs[1] = nullptr; + } + }; + + PagedAllocator<Node> node_allocator; + // Fields + Node *bvh_root = nullptr; + int lkhd = -1; + int total_leaves = 0; + uint32_t opath = 0; + uint32_t index = 0; + + enum { + ALLOCA_STACK_SIZE = 128 + }; + + _FORCE_INLINE_ void _delete_node(Node *p_node); + void _recurse_delete_node(Node *p_node); + _FORCE_INLINE_ Node *_create_node(Node *p_parent, void *p_data); + _FORCE_INLINE_ DynamicBVH::Node *_create_node_with_volume(Node *p_parent, const Volume &p_volume, void *p_data); + _FORCE_INLINE_ void _insert_leaf(Node *p_root, Node *p_leaf); + _FORCE_INLINE_ Node *_remove_leaf(Node *leaf); + void _fetch_leaves(Node *p_root, LocalVector<Node *> &r_leaves, int p_depth = -1); + static int _split(Node **leaves, int p_count, const Vector3 &p_org, const Vector3 &p_axis); + static Volume _bounds(Node **leaves, int p_count); + void _bottom_up(Node **leaves, int p_count); + Node *_top_down(Node **leaves, int p_count, int p_bu_threshold); + Node *_node_sort(Node *n, Node *&r); + + _FORCE_INLINE_ void _update(Node *leaf, int lookahead = -1); + + void _extract_leaves(Node *p_node, List<ID> *r_elements); + + _FORCE_INLINE_ bool _ray_aabb(const Vector3 &rayFrom, const Vector3 &rayInvDirection, const unsigned int raySign[3], const Vector3 bounds[2], real_t &tmin, real_t lambda_min, real_t lambda_max) { + real_t tmax, tymin, tymax, tzmin, tzmax; + tmin = (bounds[raySign[0]].x - rayFrom.x) * rayInvDirection.x; + tmax = (bounds[1 - raySign[0]].x - rayFrom.x) * rayInvDirection.x; + tymin = (bounds[raySign[1]].y - rayFrom.y) * rayInvDirection.y; + tymax = (bounds[1 - raySign[1]].y - rayFrom.y) * rayInvDirection.y; + + if ((tmin > tymax) || (tymin > tmax)) + return false; + + if (tymin > tmin) + tmin = tymin; + + if (tymax < tmax) + tmax = tymax; + + tzmin = (bounds[raySign[2]].z - rayFrom.z) * rayInvDirection.z; + tzmax = (bounds[1 - raySign[2]].z - rayFrom.z) * rayInvDirection.z; + + if ((tmin > tzmax) || (tzmin > tmax)) + return false; + if (tzmin > tmin) + tmin = tzmin; + if (tzmax < tmax) + tmax = tzmax; + return ((tmin < lambda_max) && (tmax > lambda_min)); + } + +public: + // Methods + void clear(); + bool is_empty() const { return (0 == bvh_root); } + void optimize_bottom_up(); + void optimize_top_down(int bu_threshold = 128); + void optimize_incremental(int passes); + ID insert(const AABB &p_box, void *p_userdata); + bool update(const ID &p_id, const AABB &p_box); + void remove(const ID &p_id); + void get_elements(List<ID> *r_elements); + + int get_leaf_count() const; + int get_max_depth() const; + + /* Discouraged, but works as a reference on how it must be used */ + struct DefaultQueryResult { + virtual bool operator()(void *p_data) = 0; //return true whether you want to continue the query + virtual ~DefaultQueryResult() {} + }; + + template <class QueryResult> + _FORCE_INLINE_ void aabb_query(const AABB &p_aabb, QueryResult &r_result); + template <class QueryResult> + _FORCE_INLINE_ void convex_query(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, QueryResult &r_result); + template <class QueryResult> + _FORCE_INLINE_ void ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResult &r_result); + + void set_index(uint32_t p_index); + uint32_t get_index() const; + + ~DynamicBVH(); +}; + +template <class QueryResult> +void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { + if (!bvh_root) { + return; + } + + Volume volume; + volume.min = p_box.position; + volume.max = p_box.position + p_box.size; + + const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + stack[0] = bvh_root; + int32_t depth = 1; + int32_t threshold = ALLOCA_STACK_SIZE - 2; + + LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time. + + do { + depth--; + const Node *n = stack[depth]; + if (n->volume.intersects(volume)) { + if (n->is_internal()) { + if (depth > threshold) { + if (aux_stack.is_empty()) { + aux_stack.resize(ALLOCA_STACK_SIZE * 2); + copymem(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + } else { + aux_stack.resize(aux_stack.size() * 2); + } + stack = aux_stack.ptr(); + threshold = aux_stack.size() - 2; + } + stack[depth++] = n->childs[0]; + stack[depth++] = n->childs[1]; + } else { + if (r_result(n->data)) { + return; + } + } + } + } while (depth > 0); +} + +template <class QueryResult> +void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, QueryResult &r_result) { + if (!bvh_root) { + return; + } + + //generate a volume anyway to improve pre-testing + Volume volume; + for (int i = 0; i < p_point_count; i++) { + if (i == 0) { + volume.min = p_points[0]; + volume.max = p_points[0]; + } else { + volume.min.x = MIN(volume.min.x, p_points[i].x); + volume.min.y = MIN(volume.min.y, p_points[i].y); + volume.min.z = MIN(volume.min.z, p_points[i].z); + + volume.max.x = MAX(volume.max.x, p_points[i].x); + volume.max.y = MAX(volume.max.y, p_points[i].y); + volume.max.z = MAX(volume.max.z, p_points[i].z); + } + } + + const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + stack[0] = bvh_root; + int32_t depth = 1; + int32_t threshold = ALLOCA_STACK_SIZE - 2; + + LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time. + + do { + depth--; + const Node *n = stack[depth]; + if (n->volume.intersects(volume) && n->volume.intersects_convex(p_planes, p_plane_count, p_points, p_point_count)) { + if (n->is_internal()) { + if (depth > threshold) { + if (aux_stack.is_empty()) { + aux_stack.resize(ALLOCA_STACK_SIZE * 2); + copymem(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + } else { + aux_stack.resize(aux_stack.size() * 2); + } + stack = aux_stack.ptr(); + threshold = aux_stack.size() - 2; + } + stack[depth++] = n->childs[0]; + stack[depth++] = n->childs[1]; + } else { + if (r_result(n->data)) { + return; + } + } + } + } while (depth > 0); +} +template <class QueryResult> +void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResult &r_result) { + if (!bvh_root) { + return; + } + + Vector3 ray_dir = (p_to - p_from); + ray_dir.normalize(); + + ///what about division by zero? --> just set rayDirection[i] to INF/B3_LARGE_FLOAT + Vector3 inv_dir; + inv_dir[0] = ray_dir[0] == real_t(0.0) ? real_t(1e20) : real_t(1.0) / ray_dir[0]; + inv_dir[1] = ray_dir[1] == real_t(0.0) ? real_t(1e20) : real_t(1.0) / ray_dir[1]; + inv_dir[2] = ray_dir[2] == real_t(0.0) ? real_t(1e20) : real_t(1.0) / ray_dir[2]; + unsigned int signs[3] = { inv_dir[0] < 0.0, inv_dir[1] < 0.0, inv_dir[2] < 0.0 }; + + real_t lambda_max = ray_dir.dot(p_to - p_from); + + Vector3 bounds[2]; + + const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + stack[0] = bvh_root; + int32_t depth = 1; + int32_t threshold = ALLOCA_STACK_SIZE - 2; + + LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time. + + do { + depth--; + const Node *node = stack[depth]; + bounds[0] = node->volume.min; + bounds[1] = node->volume.max; + real_t tmin = 1.f, lambda_min = 0.f; + unsigned int result1 = false; + result1 = _ray_aabb(p_from, inv_dir, signs, bounds, tmin, lambda_min, lambda_max); + if (result1) { + if (node->is_internal()) { + if (depth > threshold) { + if (aux_stack.is_empty()) { + aux_stack.resize(ALLOCA_STACK_SIZE * 2); + copymem(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + } else { + aux_stack.resize(aux_stack.size() * 2); + } + stack = aux_stack.ptr(); + threshold = aux_stack.size() - 2; + } + stack[depth++] = node->childs[0]; + stack[depth++] = node->childs[1]; + } else { + if (r_result(node->data)) { + return; + } + } + } + } while (depth > 0); +} + +#endif // DYNAMICBVH_H diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 859b9be8c5..636ea601c7 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,856 +30,61 @@ #include "expression.h" -#include "core/class_db.h" -#include "core/func_ref.h" #include "core/io/marshalls.h" #include "core/math/math_funcs.h" +#include "core/object/class_db.h" +#include "core/object/reference.h" #include "core/os/os.h" -#include "core/reference.h" -#include "core/variant_parser.h" - -const char *Expression::func_name[Expression::FUNC_MAX] = { - "sin", - "cos", - "tan", - "sinh", - "cosh", - "tanh", - "asin", - "acos", - "atan", - "atan2", - "sqrt", - "fmod", - "fposmod", - "posmod", - "floor", - "ceil", - "round", - "abs", - "sign", - "pow", - "log", - "exp", - "is_nan", - "is_inf", - "ease", - "step_decimals", - "stepify", - "lerp", - "lerp_angle", - "inverse_lerp", - "range_lerp", - "smoothstep", - "move_toward", - "dectime", - "randomize", - "randi", - "randf", - "rand_range", - "seed", - "rand_seed", - "deg2rad", - "rad2deg", - "linear2db", - "db2linear", - "polar2cartesian", - "cartesian2polar", - "wrapi", - "wrapf", - "max", - "min", - "clamp", - "nearest_po2", - "weakref", - "funcref", - "convert", - "typeof", - "type_exists", - "char", - "ord", - "str", - "print", - "printerr", - "printraw", - "var2str", - "str2var", - "var2bytes", - "bytes2var", - "color_named", -}; - -Expression::BuiltinFunc Expression::find_function(const String &p_string) { - - for (int i = 0; i < FUNC_MAX; i++) { - if (p_string == func_name[i]) - return BuiltinFunc(i); - } - - return FUNC_MAX; -} - -String Expression::get_func_name(BuiltinFunc p_func) { - - ERR_FAIL_INDEX_V(p_func, FUNC_MAX, String()); - return func_name[p_func]; -} - -int Expression::get_func_argument_count(BuiltinFunc p_func) { - - switch (p_func) { - - case MATH_RANDOMIZE: - case MATH_RAND: - case MATH_RANDF: - return 0; - case MATH_SIN: - case MATH_COS: - case MATH_TAN: - case MATH_SINH: - case MATH_COSH: - case MATH_TANH: - case MATH_ASIN: - case MATH_ACOS: - case MATH_ATAN: - case MATH_SQRT: - case MATH_FLOOR: - case MATH_CEIL: - case MATH_ROUND: - case MATH_ABS: - case MATH_SIGN: - case MATH_LOG: - case MATH_EXP: - case MATH_ISNAN: - case MATH_ISINF: - case MATH_STEP_DECIMALS: - case MATH_SEED: - case MATH_RANDSEED: - case MATH_DEG2RAD: - case MATH_RAD2DEG: - case MATH_LINEAR2DB: - case MATH_DB2LINEAR: - case LOGIC_NEAREST_PO2: - case OBJ_WEAKREF: - case TYPE_OF: - case TEXT_CHAR: - case TEXT_ORD: - case TEXT_STR: - case TEXT_PRINT: - case TEXT_PRINTERR: - case TEXT_PRINTRAW: - case VAR_TO_STR: - case STR_TO_VAR: - case TYPE_EXISTS: - return 1; - case VAR_TO_BYTES: - case BYTES_TO_VAR: - case MATH_ATAN2: - case MATH_FMOD: - case MATH_FPOSMOD: - case MATH_POSMOD: - case MATH_POW: - case MATH_EASE: - case MATH_STEPIFY: - case MATH_RANDOM: - case MATH_POLAR2CARTESIAN: - case MATH_CARTESIAN2POLAR: - case LOGIC_MAX: - case LOGIC_MIN: - case FUNC_FUNCREF: - case TYPE_CONVERT: - case COLORN: - return 2; - case MATH_LERP: - case MATH_LERP_ANGLE: - case MATH_INVERSE_LERP: - case MATH_SMOOTHSTEP: - case MATH_MOVE_TOWARD: - case MATH_DECTIME: - case MATH_WRAP: - case MATH_WRAPF: - case LOGIC_CLAMP: - return 3; - case MATH_RANGE_LERP: - return 5; - case FUNC_MAX: { - } - } - return 0; -} - -#define VALIDATE_ARG_NUM(m_arg) \ - if (!p_inputs[m_arg]->is_num()) { \ - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ - r_error.argument = m_arg; \ - r_error.expected = Variant::FLOAT; \ - return; \ - } - -void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Callable::CallError &r_error, String &r_error_str) { - r_error.error = Callable::CallError::CALL_OK; - switch (p_func) { - case MATH_SIN: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::sin((double)*p_inputs[0]); - } break; - case MATH_COS: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::cos((double)*p_inputs[0]); - } break; - case MATH_TAN: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::tan((double)*p_inputs[0]); - } break; - case MATH_SINH: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::sinh((double)*p_inputs[0]); - } break; - case MATH_COSH: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::cosh((double)*p_inputs[0]); - } break; - case MATH_TANH: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::tanh((double)*p_inputs[0]); - } break; - case MATH_ASIN: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::asin((double)*p_inputs[0]); - } break; - case MATH_ACOS: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::acos((double)*p_inputs[0]); - } break; - case MATH_ATAN: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::atan((double)*p_inputs[0]); - } break; - case MATH_ATAN2: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *r_return = Math::atan2((double)*p_inputs[0], (double)*p_inputs[1]); - } break; - case MATH_SQRT: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::sqrt((double)*p_inputs[0]); - } break; - case MATH_FMOD: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *r_return = Math::fmod((double)*p_inputs[0], (double)*p_inputs[1]); - } break; - case MATH_FPOSMOD: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *r_return = Math::fposmod((double)*p_inputs[0], (double)*p_inputs[1]); - } break; - case MATH_POSMOD: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *r_return = Math::posmod((int)*p_inputs[0], (int)*p_inputs[1]); - } break; - case MATH_FLOOR: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::floor((double)*p_inputs[0]); - } break; - case MATH_CEIL: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::ceil((double)*p_inputs[0]); - } break; - case MATH_ROUND: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::round((double)*p_inputs[0]); - } break; - case MATH_ABS: { - - if (p_inputs[0]->get_type() == Variant::INT) { - - int64_t i = *p_inputs[0]; - *r_return = ABS(i); - } else if (p_inputs[0]->get_type() == Variant::FLOAT) { - - real_t r = *p_inputs[0]; - *r_return = Math::abs(r); - } else { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::FLOAT; - } - } break; - case MATH_SIGN: { - - if (p_inputs[0]->get_type() == Variant::INT) { - - int64_t i = *p_inputs[0]; - *r_return = i < 0 ? -1 : (i > 0 ? +1 : 0); - } else if (p_inputs[0]->get_type() == Variant::FLOAT) { - - real_t r = *p_inputs[0]; - *r_return = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0); - } else { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::FLOAT; - } - } break; - case MATH_POW: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *r_return = Math::pow((double)*p_inputs[0], (double)*p_inputs[1]); - } break; - case MATH_LOG: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::log((double)*p_inputs[0]); - } break; - case MATH_EXP: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::exp((double)*p_inputs[0]); - } break; - case MATH_ISNAN: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::is_nan((double)*p_inputs[0]); - } break; - case MATH_ISINF: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::is_inf((double)*p_inputs[0]); - } break; - case MATH_EASE: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *r_return = Math::ease((double)*p_inputs[0], (double)*p_inputs[1]); - } break; - case MATH_STEP_DECIMALS: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::step_decimals((double)*p_inputs[0]); - } break; - case MATH_STEPIFY: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *r_return = Math::stepify((double)*p_inputs[0], (double)*p_inputs[1]); - } break; - case MATH_LERP: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); - } break; - case MATH_LERP_ANGLE: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::lerp_angle((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); - } break; - case MATH_INVERSE_LERP: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); - } break; - case MATH_RANGE_LERP: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - VALIDATE_ARG_NUM(3); - VALIDATE_ARG_NUM(4); - *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); - } break; - case MATH_SMOOTHSTEP: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); - } break; - case MATH_MOVE_TOWARD: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::move_toward((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); - } break; - case MATH_DECTIME: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::dectime((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); - } break; - case MATH_RANDOMIZE: { - Math::randomize(); - - } break; - case MATH_RAND: { - *r_return = Math::rand(); - } break; - case MATH_RANDF: { - *r_return = Math::randf(); - } break; - case MATH_RANDOM: { - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]); - } break; - case MATH_SEED: { - - VALIDATE_ARG_NUM(0); - uint64_t seed = *p_inputs[0]; - Math::seed(seed); - - } break; - case MATH_RANDSEED: { - - VALIDATE_ARG_NUM(0); - uint64_t seed = *p_inputs[0]; - int ret = Math::rand_from_seed(&seed); - Array reta; - reta.push_back(ret); - reta.push_back(seed); - *r_return = reta; - - } break; - case MATH_DEG2RAD: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::deg2rad((double)*p_inputs[0]); - } break; - case MATH_RAD2DEG: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::rad2deg((double)*p_inputs[0]); - } break; - case MATH_LINEAR2DB: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::linear2db((double)*p_inputs[0]); - } break; - case MATH_DB2LINEAR: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::db2linear((double)*p_inputs[0]); - } break; - case MATH_POLAR2CARTESIAN: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double r = *p_inputs[0]; - double th = *p_inputs[1]; - *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); - } break; - case MATH_CARTESIAN2POLAR: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double x = *p_inputs[0]; - double y = *p_inputs[1]; - *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); - } break; - case MATH_WRAP: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::wrapi((int64_t)*p_inputs[0], (int64_t)*p_inputs[1], (int64_t)*p_inputs[2]); - } break; - case MATH_WRAPF: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::wrapf((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); - } break; - case LOGIC_MAX: { - - if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { - - int64_t a = *p_inputs[0]; - int64_t b = *p_inputs[1]; - *r_return = MAX(a, b); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - - real_t a = *p_inputs[0]; - real_t b = *p_inputs[1]; - - *r_return = MAX(a, b); - } - - } break; - case LOGIC_MIN: { - - if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { - - int64_t a = *p_inputs[0]; - int64_t b = *p_inputs[1]; - *r_return = MIN(a, b); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - - real_t a = *p_inputs[0]; - real_t b = *p_inputs[1]; - - *r_return = MIN(a, b); - } - } break; - case LOGIC_CLAMP: { - - if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT && p_inputs[2]->get_type() == Variant::INT) { - - int64_t a = *p_inputs[0]; - int64_t b = *p_inputs[1]; - int64_t c = *p_inputs[2]; - *r_return = CLAMP(a, b, c); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - - real_t a = *p_inputs[0]; - real_t b = *p_inputs[1]; - real_t c = *p_inputs[2]; - - *r_return = CLAMP(a, b, c); - } - } break; - case LOGIC_NEAREST_PO2: { - - VALIDATE_ARG_NUM(0); - int64_t num = *p_inputs[0]; - *r_return = next_power_of_2(num); - } break; - case OBJ_WEAKREF: { - - if (p_inputs[0]->get_type() != Variant::OBJECT) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - - return; - } - - if (p_inputs[0]->is_ref()) { - - REF r = *p_inputs[0]; - if (!r.is_valid()) { - - return; - } - - Ref<WeakRef> wref = memnew(WeakRef); - wref->set_ref(r); - *r_return = wref; - } else { - Object *obj = *p_inputs[0]; - if (!obj) { - - return; - } - Ref<WeakRef> wref = memnew(WeakRef); - wref->set_obj(obj); - *r_return = wref; - } - - } break; - case FUNC_FUNCREF: { - - if (p_inputs[0]->get_type() != Variant::OBJECT) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - - return; - } - if (p_inputs[1]->get_type() != Variant::STRING && p_inputs[1]->get_type() != Variant::NODE_PATH) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::STRING; - - return; - } - - Ref<FuncRef> fr = memnew(FuncRef); - - fr->set_instance(*p_inputs[0]); - fr->set_function(*p_inputs[1]); - - *r_return = fr; - - } break; - case TYPE_CONVERT: { - - VALIDATE_ARG_NUM(1); - int type = *p_inputs[1]; - if (type < 0 || type >= Variant::VARIANT_MAX) { - - r_error_str = RTR("Invalid type argument to convert(), use TYPE_* constants."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::INT; - return; - - } else { - - *r_return = Variant::construct(Variant::Type(type), p_inputs, 1, r_error); - } - } break; - case TYPE_OF: { - - *r_return = p_inputs[0]->get_type(); - - } break; - case TYPE_EXISTS: { - - *r_return = ClassDB::class_exists(*p_inputs[0]); - - } break; - case TEXT_CHAR: { - - CharType result[2] = { *p_inputs[0], 0 }; - - *r_return = String(result); - - } break; - case TEXT_ORD: { - - if (p_inputs[0]->get_type() != Variant::STRING) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - - return; - } - - String str = *p_inputs[0]; - - if (str.length() != 1) { - - r_error_str = RTR("Expected a string of length 1 (a character)."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - - return; - } - - *r_return = str.get(0); - - } break; - case TEXT_STR: { - - String str = *p_inputs[0]; - - *r_return = str; - - } break; - case TEXT_PRINT: { - - String str = *p_inputs[0]; - print_line(str); - - } break; - - case TEXT_PRINTERR: { - - String str = *p_inputs[0]; - print_error(str); - - } break; - case TEXT_PRINTRAW: { - - String str = *p_inputs[0]; - OS::get_singleton()->print("%s", str.utf8().get_data()); - - } break; - case VAR_TO_STR: { - - String vars; - VariantWriter::write_to_string(*p_inputs[0], vars); - *r_return = vars; - } break; - case STR_TO_VAR: { - - if (p_inputs[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - - return; - } - - VariantParser::StreamString ss; - ss.s = *p_inputs[0]; - - String errs; - int line; - Error err = VariantParser::parse(&ss, *r_return, errs, line); +#include "core/variant/variant_parser.h" - if (err != OK) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - *r_return = "Parse error at line " + itos(line) + ": " + errs; - return; - } - - } break; - case VAR_TO_BYTES: { - - PackedByteArray barr; - bool full_objects = *p_inputs[1]; - int len; - Error err = encode_variant(*p_inputs[0], nullptr, len, full_objects); - if (err) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::NIL; - r_error_str = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; - return; - } - - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - encode_variant(*p_inputs[0], w, len, full_objects); - } - *r_return = barr; - } break; - case BYTES_TO_VAR: { - - if (p_inputs[0]->get_type() != Variant::PACKED_BYTE_ARRAY) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::PACKED_BYTE_ARRAY; - - return; - } - - PackedByteArray varr = *p_inputs[0]; - bool allow_objects = *p_inputs[1]; - Variant ret; - { - const uint8_t *r = varr.ptr(); - Error err = decode_variant(ret, r, varr.size(), nullptr, allow_objects); - if (err != OK) { - r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::PACKED_BYTE_ARRAY; - return; - } - } - - *r_return = ret; - - } break; - case COLORN: { - - VALIDATE_ARG_NUM(1); - - Color color = Color::named(*p_inputs[0]); - color.a = *p_inputs[1]; - - *r_return = String(color); - - } break; - default: { - } - } -} - -//////// - -static bool _is_number(CharType c) { +static bool _is_number(char32_t c) { return (c >= '0' && c <= '9'); } Error Expression::_get_token(Token &r_token) { - while (true) { #define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) - CharType cchar = GET_CHAR(); + char32_t cchar = GET_CHAR(); switch (cchar) { - case 0: { r_token.type = TK_EOF; return OK; - }; + } case '{': { - r_token.type = TK_CURLY_BRACKET_OPEN; return OK; - }; + } case '}': { - r_token.type = TK_CURLY_BRACKET_CLOSE; return OK; - }; + } case '[': { - r_token.type = TK_BRACKET_OPEN; return OK; - }; + } case ']': { - r_token.type = TK_BRACKET_CLOSE; return OK; - }; + } case '(': { - r_token.type = TK_PARENTHESIS_OPEN; return OK; - }; + } case ')': { - r_token.type = TK_PARENTHESIS_CLOSE; return OK; - }; + } case ',': { - r_token.type = TK_COMMA; return OK; - }; + } case ':': { - r_token.type = TK_COLON; return OK; - }; + } case '$': { - r_token.type = TK_INPUT; int index = 0; do { @@ -896,9 +101,8 @@ Error Expression::_get_token(Token &r_token) { r_token.value = index; return OK; - }; + } case '=': { - cchar = GET_CHAR(); if (cchar == '=') { r_token.type = TK_OP_EQUAL; @@ -908,9 +112,8 @@ Error Expression::_get_token(Token &r_token) { return ERR_PARSE_ERROR; } return OK; - }; + } case '!': { - if (expression[str_ofs] == '=') { r_token.type = TK_OP_NOT_EQUAL; str_ofs++; @@ -918,9 +121,8 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_OP_NOT; } return OK; - }; + } case '>': { - if (expression[str_ofs] == '=') { r_token.type = TK_OP_GREATER_EQUAL; str_ofs++; @@ -931,9 +133,8 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_OP_GREATER; } return OK; - }; + } case '<': { - if (expression[str_ofs] == '=') { r_token.type = TK_OP_LESS_EQUAL; str_ofs++; @@ -944,29 +145,28 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_OP_LESS; } return OK; - }; + } case '+': { r_token.type = TK_OP_ADD; return OK; - }; + } case '-': { r_token.type = TK_OP_SUB; return OK; - }; + } case '/': { r_token.type = TK_OP_DIV; return OK; - }; + } case '*': { r_token.type = TK_OP_MUL; return OK; - }; + } case '%': { r_token.type = TK_OP_MOD; return OK; - }; + } case '&': { - if (expression[str_ofs] == '&') { r_token.type = TK_OP_AND; str_ofs++; @@ -974,9 +174,8 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_OP_BIT_AND; } return OK; - }; + } case '|': { - if (expression[str_ofs] == '|') { r_token.type = TK_OP_OR; str_ofs++; @@ -984,54 +183,61 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_OP_BIT_OR; } return OK; - }; + } case '^': { - r_token.type = TK_OP_BIT_XOR; return OK; - }; + } case '~': { - r_token.type = TK_OP_BIT_INVERT; return OK; - }; + } + case '\'': case '"': { - String str; while (true) { - - CharType ch = GET_CHAR(); + char32_t ch = GET_CHAR(); if (ch == 0) { _set_error("Unterminated String"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; - } else if (ch == '"') { + } else if (ch == cchar) { + // cchar contain a corresponding quote symbol break; } else if (ch == '\\') { //escaped characters... - CharType next = GET_CHAR(); + char32_t next = GET_CHAR(); if (next == 0) { _set_error("Unterminated String"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { - - case 'b': res = 8; break; - case 't': res = 9; break; - case 'n': res = 10; break; - case 'f': res = 12; break; - case 'r': res = 13; break; + case 'b': + res = 8; + break; + case 't': + res = 9; + break; + case 'n': + res = 10; + break; + case 'f': + res = 12; + break; + case 'r': + res = 13; + break; case 'u': { // hex number for (int j = 0; j < 4; j++) { - CharType c = GET_CHAR(); + char32_t c = GET_CHAR(); if (c == 0) { _set_error("Unterminated String"); @@ -1039,12 +245,11 @@ Error Expression::_get_token(Token &r_token) { return ERR_PARSE_ERROR; } if (!(_is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { - _set_error("Malformed hex constant in string"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (_is_number(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -1081,12 +286,11 @@ Error Expression::_get_token(Token &r_token) { } break; default: { - if (cchar <= 32) { break; } - CharType next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; + char32_t next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) { //a number @@ -1098,16 +302,14 @@ Error Expression::_get_token(Token &r_token) { #define READING_DONE 4 int reading = READING_INT; - CharType c = cchar; + char32_t c = cchar; bool exp_sign = false; bool exp_beg = false; bool is_float = false; while (true) { - switch (reading) { case READING_INT: { - if (_is_number(c)) { //pass } else if (c == '.') { @@ -1121,9 +323,7 @@ Error Expression::_get_token(Token &r_token) { } break; case READING_DEC: { - if (_is_number(c)) { - } else if (c == 'e') { reading = READING_EXP; @@ -1133,13 +333,13 @@ Error Expression::_get_token(Token &r_token) { } break; case READING_EXP: { - if (_is_number(c)) { exp_beg = true; } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { - if (c == '-') + if (c == '-') { is_float = true; + } exp_sign = true; } else { @@ -1148,8 +348,9 @@ Error Expression::_get_token(Token &r_token) { } break; } - if (reading == READING_DONE) + if (reading == READING_DONE) { break; + } num += String::chr(c); c = GET_CHAR(); } @@ -1158,19 +359,18 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_CONSTANT; - if (is_float) - r_token.value = num.to_double(); - else - r_token.value = num.to_int64(); + if (is_float) { + r_token.value = num.to_float(); + } else { + r_token.value = num.to_int(); + } return OK; } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { - String id; bool first = true; while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) { - id += String::chr(cchar); cchar = GET_CHAR(); first = false; @@ -1210,19 +410,9 @@ Error Expression::_get_token(Token &r_token) { } else if (id == "self") { r_token.type = TK_SELF; } else { - - for (int i = 0; i < Variant::VARIANT_MAX; i++) { - if (id == Variant::get_type_name(Variant::Type(i))) { - r_token.type = TK_BASIC_TYPE; - r_token.value = i; - return OK; - } - } - - BuiltinFunc bifunc = find_function(id); - if (bifunc != FUNC_MAX) { + if (Variant::has_utility_function(id)) { r_token.type = TK_BUILTIN_FUNC; - r_token.value = bifunc; + r_token.value = id; return OK; } @@ -1293,7 +483,6 @@ const char *Expression::token_name[TK_MAX] = { }; Expression::ENode *Expression::_parse_expression() { - Vector<ExpressionNode> expression; while (true) { @@ -1302,8 +491,9 @@ Expression::ENode *Expression::_parse_expression() { Token tk; _get_token(tk); - if (error_set) + if (error_set) { return nullptr; + } switch (tk.type) { case TK_CURLY_BRACKET_OPEN: { @@ -1311,7 +501,6 @@ Expression::ENode *Expression::_parse_expression() { DictionaryNode *dn = alloc_node<DictionaryNode>(); while (true) { - int cofs = str_ofs; _get_token(tk); if (tk.type == TK_CURLY_BRACKET_CLOSE) { @@ -1320,8 +509,9 @@ Expression::ENode *Expression::_parse_expression() { str_ofs = cofs; //revert //parse an expression ENode *subexpr = _parse_expression(); - if (!subexpr) + if (!subexpr) { return nullptr; + } dn->dict.push_back(subexpr); _get_token(tk); @@ -1331,8 +521,9 @@ Expression::ENode *Expression::_parse_expression() { } subexpr = _parse_expression(); - if (!subexpr) + if (!subexpr) { return nullptr; + } dn->dict.push_back(subexpr); @@ -1355,7 +546,6 @@ Expression::ENode *Expression::_parse_expression() { ArrayNode *an = alloc_node<ArrayNode>(); while (true) { - int cofs = str_ofs; _get_token(tk); if (tk.type == TK_BRACKET_CLOSE) { @@ -1364,8 +554,9 @@ Expression::ENode *Expression::_parse_expression() { str_ofs = cofs; //revert //parse an expression ENode *subexpr = _parse_expression(); - if (!subexpr) + if (!subexpr) { return nullptr; + } an->array.push_back(subexpr); cofs = str_ofs; @@ -1384,8 +575,9 @@ Expression::ENode *Expression::_parse_expression() { case TK_PARENTHESIS_OPEN: { //a suexpression ENode *e = _parse_expression(); - if (error_set) + if (error_set) { return nullptr; + } _get_token(tk); if (tk.type != TK_PARENTHESIS_CLOSE) { _set_error("Expected ')'"); @@ -1396,7 +588,6 @@ Expression::ENode *Expression::_parse_expression() { } break; case TK_IDENTIFIER: { - String identifier = tk.value; int cofs = str_ofs; @@ -1409,7 +600,6 @@ Expression::ENode *Expression::_parse_expression() { func_call->base = self_node; while (true) { - int cofs2 = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_CLOSE) { @@ -1418,8 +608,9 @@ Expression::ENode *Expression::_parse_expression() { str_ofs = cofs2; //revert //parse an expression ENode *subexpr = _parse_expression(); - if (!subexpr) + if (!subexpr) { return nullptr; + } func_call->arguments.push_back(subexpr); @@ -1452,7 +643,6 @@ Expression::ENode *Expression::_parse_expression() { input->index = input_index; expr = input; } else { - NamedIndexNode *index = alloc_node<NamedIndexNode>(); SelfNode *self_node = alloc_node<SelfNode>(); index->base = self_node; @@ -1462,13 +652,11 @@ Expression::ENode *Expression::_parse_expression() { } } break; case TK_INPUT: { - InputNode *input = alloc_node<InputNode>(); input->index = tk.value; expr = input; } break; case TK_SELF: { - SelfNode *self = alloc_node<SelfNode>(); expr = self; } break; @@ -1491,7 +679,6 @@ Expression::ENode *Expression::_parse_expression() { constructor->data_type = bt; while (true) { - int cofs = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_CLOSE) { @@ -1500,8 +687,9 @@ Expression::ENode *Expression::_parse_expression() { str_ofs = cofs; //revert //parse an expression ENode *subexpr = _parse_expression(); - if (!subexpr) + if (!subexpr) { return nullptr; + } constructor->arguments.push_back(subexpr); @@ -1522,6 +710,8 @@ Expression::ENode *Expression::_parse_expression() { case TK_BUILTIN_FUNC: { //builtin function + StringName func = tk.value; + _get_token(tk); if (tk.type != TK_PARENTHESIS_OPEN) { _set_error("Expected '('"); @@ -1529,10 +719,9 @@ Expression::ENode *Expression::_parse_expression() { } BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>(); - bifunc->func = BuiltinFunc(int(tk.value)); + bifunc->func = func; while (true) { - int cofs = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_CLOSE) { @@ -1541,8 +730,9 @@ Expression::ENode *Expression::_parse_expression() { str_ofs = cofs; //revert //parse an expression ENode *subexpr = _parse_expression(); - if (!subexpr) + if (!subexpr) { return nullptr; + } bifunc->arguments.push_back(subexpr); @@ -1557,16 +747,17 @@ Expression::ENode *Expression::_parse_expression() { } } - int expected_args = get_func_argument_count(bifunc->func); - if (bifunc->arguments.size() != expected_args) { - _set_error("Builtin func '" + get_func_name(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); + if (!Variant::is_utility_function_vararg(bifunc->func)) { + int expected_args = Variant::get_utility_function_argument_count(bifunc->func); + if (expected_args != bifunc->arguments.size()) { + _set_error("Builtin func '" + String(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); + } } expr = bifunc; } break; case TK_OP_SUB: { - ExpressionNode e; e.is_op = true; e.op = Variant::OP_NEGATE; @@ -1574,7 +765,6 @@ Expression::ENode *Expression::_parse_expression() { continue; } break; case TK_OP_NOT: { - ExpressionNode e; e.is_op = true; e.op = Variant::OP_NOT; @@ -1593,8 +783,9 @@ Expression::ENode *Expression::_parse_expression() { while (true) { int cofs2 = str_ofs; _get_token(tk); - if (error_set) + if (error_set) { return nullptr; + } bool done = false; @@ -1606,8 +797,9 @@ Expression::ENode *Expression::_parse_expression() { index->base = expr; ENode *what = _parse_expression(); - if (!what) + if (!what) { return nullptr; + } index->index = what; @@ -1638,7 +830,6 @@ Expression::ENode *Expression::_parse_expression() { func_call->base = expr; while (true) { - int cofs3 = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_CLOSE) { @@ -1647,8 +838,9 @@ Expression::ENode *Expression::_parse_expression() { str_ofs = cofs3; //revert //parse an expression ENode *subexpr = _parse_expression(); - if (!subexpr) + if (!subexpr) { return nullptr; + } func_call->arguments.push_back(subexpr); @@ -1681,8 +873,9 @@ Expression::ENode *Expression::_parse_expression() { } break; } - if (done) + if (done) { break; + } } //push expression @@ -1697,35 +890,78 @@ Expression::ENode *Expression::_parse_expression() { int cofs = str_ofs; _get_token(tk); - if (error_set) + if (error_set) { return nullptr; + } Variant::Operator op = Variant::OP_MAX; switch (tk.type) { - case TK_OP_IN: op = Variant::OP_IN; break; - case TK_OP_EQUAL: op = Variant::OP_EQUAL; break; - case TK_OP_NOT_EQUAL: op = Variant::OP_NOT_EQUAL; break; - case TK_OP_LESS: op = Variant::OP_LESS; break; - case TK_OP_LESS_EQUAL: op = Variant::OP_LESS_EQUAL; break; - case TK_OP_GREATER: op = Variant::OP_GREATER; break; - case TK_OP_GREATER_EQUAL: op = Variant::OP_GREATER_EQUAL; break; - case TK_OP_AND: op = Variant::OP_AND; break; - case TK_OP_OR: op = Variant::OP_OR; break; - case TK_OP_NOT: op = Variant::OP_NOT; break; - case TK_OP_ADD: op = Variant::OP_ADD; break; - case TK_OP_SUB: op = Variant::OP_SUBTRACT; break; - case TK_OP_MUL: op = Variant::OP_MULTIPLY; break; - case TK_OP_DIV: op = Variant::OP_DIVIDE; break; - case TK_OP_MOD: op = Variant::OP_MODULE; break; - case TK_OP_SHIFT_LEFT: op = Variant::OP_SHIFT_LEFT; break; - case TK_OP_SHIFT_RIGHT: op = Variant::OP_SHIFT_RIGHT; break; - case TK_OP_BIT_AND: op = Variant::OP_BIT_AND; break; - case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; - case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; - case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; + case TK_OP_IN: + op = Variant::OP_IN; + break; + case TK_OP_EQUAL: + op = Variant::OP_EQUAL; + break; + case TK_OP_NOT_EQUAL: + op = Variant::OP_NOT_EQUAL; + break; + case TK_OP_LESS: + op = Variant::OP_LESS; + break; + case TK_OP_LESS_EQUAL: + op = Variant::OP_LESS_EQUAL; + break; + case TK_OP_GREATER: + op = Variant::OP_GREATER; + break; + case TK_OP_GREATER_EQUAL: + op = Variant::OP_GREATER_EQUAL; + break; + case TK_OP_AND: + op = Variant::OP_AND; + break; + case TK_OP_OR: + op = Variant::OP_OR; + break; + case TK_OP_NOT: + op = Variant::OP_NOT; + break; + case TK_OP_ADD: + op = Variant::OP_ADD; + break; + case TK_OP_SUB: + op = Variant::OP_SUBTRACT; + break; + case TK_OP_MUL: + op = Variant::OP_MULTIPLY; + break; + case TK_OP_DIV: + op = Variant::OP_DIVIDE; + break; + case TK_OP_MOD: + op = Variant::OP_MODULE; + break; + case TK_OP_SHIFT_LEFT: + op = Variant::OP_SHIFT_LEFT; + break; + case TK_OP_SHIFT_RIGHT: + op = Variant::OP_SHIFT_RIGHT; + break; + case TK_OP_BIT_AND: + op = Variant::OP_BIT_AND; + break; + case TK_OP_BIT_OR: + op = Variant::OP_BIT_OR; + break; + case TK_OP_BIT_XOR: + op = Variant::OP_BIT_XOR; + break; + case TK_OP_BIT_INVERT: + op = Variant::OP_BIT_NEGATE; + break; default: { - }; + } } if (op == Variant::OP_MAX) { //stop appending stuff @@ -1745,15 +981,12 @@ Expression::ENode *Expression::_parse_expression() { /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ while (expression.size() > 1) { - int next_op = -1; int min_priority = 0xFFFFF; bool is_unary = false; for (int i = 0; i < expression.size(); i++) { - if (!expression[i].is_op) { - continue; } @@ -1762,7 +995,6 @@ Expression::ENode *Expression::_parse_expression() { bool unary = false; switch (expression[i].op) { - case Variant::OP_BIT_NEGATE: priority = 0; unary = true; @@ -1771,38 +1003,49 @@ Expression::ENode *Expression::_parse_expression() { priority = 1; unary = true; break; - - case Variant::OP_MULTIPLY: priority = 2; break; - case Variant::OP_DIVIDE: priority = 2; break; - case Variant::OP_MODULE: priority = 2; break; - - case Variant::OP_ADD: priority = 3; break; - case Variant::OP_SUBTRACT: priority = 3; break; - - case Variant::OP_SHIFT_LEFT: priority = 4; break; - case Variant::OP_SHIFT_RIGHT: priority = 4; break; - - case Variant::OP_BIT_AND: priority = 5; break; - case Variant::OP_BIT_XOR: priority = 6; break; - case Variant::OP_BIT_OR: priority = 7; break; - - case Variant::OP_LESS: priority = 8; break; - case Variant::OP_LESS_EQUAL: priority = 8; break; - case Variant::OP_GREATER: priority = 8; break; - case Variant::OP_GREATER_EQUAL: priority = 8; break; - - case Variant::OP_EQUAL: priority = 8; break; - case Variant::OP_NOT_EQUAL: priority = 8; break; - - case Variant::OP_IN: priority = 10; break; - + case Variant::OP_MULTIPLY: + case Variant::OP_DIVIDE: + case Variant::OP_MODULE: + priority = 2; + break; + case Variant::OP_ADD: + case Variant::OP_SUBTRACT: + priority = 3; + break; + case Variant::OP_SHIFT_LEFT: + case Variant::OP_SHIFT_RIGHT: + priority = 4; + break; + case Variant::OP_BIT_AND: + priority = 5; + break; + case Variant::OP_BIT_XOR: + priority = 6; + break; + case Variant::OP_BIT_OR: + priority = 7; + break; + case Variant::OP_LESS: + case Variant::OP_LESS_EQUAL: + case Variant::OP_GREATER: + case Variant::OP_GREATER_EQUAL: + case Variant::OP_EQUAL: + case Variant::OP_NOT_EQUAL: + priority = 8; + break; + case Variant::OP_IN: + priority = 10; + break; case Variant::OP_NOT: priority = 11; unary = true; break; - case Variant::OP_AND: priority = 12; break; - case Variant::OP_OR: priority = 13; break; - + case Variant::OP_AND: + priority = 12; + break; + case Variant::OP_OR: + priority = 13; + break; default: { _set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op)); return nullptr; @@ -1820,17 +1063,14 @@ Expression::ENode *Expression::_parse_expression() { } if (next_op == -1) { - _set_error("Yet another parser bug...."); ERR_FAIL_V(nullptr); } // OK! create operator.. if (is_unary) { - int expr_pos = next_op; while (expression[expr_pos].is_op) { - expr_pos++; if (expr_pos == expression.size()) { //can happen.. @@ -1841,7 +1081,6 @@ Expression::ENode *Expression::_parse_expression() { //consecutively do unary operators for (int i = expr_pos - 1; i >= next_op; i--) { - OperatorNode *op = alloc_node<OperatorNode>(); op->op = expression[i].op; op->nodes[0] = expression[i + 1].node; @@ -1852,7 +1091,6 @@ Expression::ENode *Expression::_parse_expression() { } } else { - if (next_op < 1 || next_op >= (expression.size() - 1)) { _set_error("Parser bug..."); ERR_FAIL_V(nullptr); @@ -1862,7 +1100,6 @@ Expression::ENode *Expression::_parse_expression() { op->op = expression[next_op].op; if (expression[next_op - 1].is_op) { - _set_error("Parser bug..."); ERR_FAIL_V(nullptr); } @@ -1891,9 +1128,9 @@ Expression::ENode *Expression::_parse_expression() { } bool Expression::_compile_expression() { - - if (!expression_dirty) + if (!expression_dirty) { return error_set; + } if (nodes) { memdelete(nodes); @@ -1921,10 +1158,8 @@ bool Expression::_compile_expression() { } bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) { - switch (p_node->type) { case Expression::ENode::TYPE_INPUT: { - const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node); if (in->index < 0 || in->index >= p_inputs.size()) { r_error_str = vformat(RTR("Invalid input %i (not passed) in expression"), in->index); @@ -1933,13 +1168,11 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: r_ret = p_inputs[in->index]; } break; case Expression::ENode::TYPE_CONSTANT: { - const Expression::ConstantNode *c = static_cast<const Expression::ConstantNode *>(p_node); r_ret = c->value; } break; case Expression::ENode::TYPE_SELF: { - if (!p_instance) { r_error_str = RTR("self can't be used because instance is null (not passed)"); return true; @@ -1947,20 +1180,21 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: r_ret = p_instance; } break; case Expression::ENode::TYPE_OPERATOR: { - const Expression::OperatorNode *op = static_cast<const Expression::OperatorNode *>(p_node); Variant a; bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str); - if (ret) + if (ret) { return true; + } Variant b; if (op->nodes[1]) { ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str); - if (ret) + if (ret) { return true; + } } bool valid = true; @@ -1972,19 +1206,20 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: } break; case Expression::ENode::TYPE_INDEX: { - const Expression::IndexNode *index = static_cast<const Expression::IndexNode *>(p_node); Variant base; bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); - if (ret) + if (ret) { return true; + } Variant idx; ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str); - if (ret) + if (ret) { return true; + } bool valid; r_ret = base.get(idx, &valid); @@ -1995,16 +1230,16 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: } break; case Expression::ENode::TYPE_NAMED_INDEX: { - const Expression::NamedIndexNode *index = static_cast<const Expression::NamedIndexNode *>(p_node); Variant base; bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); - if (ret) + if (ret) { return true; + } bool valid; - r_ret = base.get_named(index->name, &valid); + r_ret = base.get_named(index->name, valid); if (!valid) { r_error_str = vformat(RTR("Invalid named index '%s' for base type %s"), String(index->name), Variant::get_type_name(base.get_type())); return true; @@ -2017,12 +1252,12 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: Array arr; arr.resize(array->array.size()); for (int i = 0; i < array->array.size(); i++) { - Variant value; bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str); - if (ret) + if (ret) { return true; + } arr[i] = value; } @@ -2034,17 +1269,18 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: Dictionary d; for (int i = 0; i < dictionary->dict.size(); i += 2) { - Variant key; bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str); - if (ret) + if (ret) { return true; + } Variant value; ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str); - if (ret) + if (ret) { return true; + } d[key] = value; } @@ -2052,7 +1288,6 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: r_ret = d; } break; case Expression::ENode::TYPE_CONSTRUCTOR: { - const Expression::ConstructorNode *constructor = static_cast<const Expression::ConstructorNode *>(p_node); Vector<Variant> arr; @@ -2061,18 +1296,18 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: argp.resize(constructor->arguments.size()); for (int i = 0; i < constructor->arguments.size(); i++) { - Variant value; bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str); - if (ret) + if (ret) { return true; + } arr.write[i] = value; argp.write[i] = &arr[i]; } Callable::CallError ce; - r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce); + Variant::construct(constructor->data_type, r_ret, (const Variant **)argp.ptr(), argp.size(), ce); if (ce.error != Callable::CallError::CALL_OK) { r_error_str = vformat(RTR("Invalid arguments to construct '%s'"), Variant::get_type_name(constructor->data_type)); @@ -2081,7 +1316,6 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: } break; case Expression::ENode::TYPE_BUILTIN_FUNC: { - const Expression::BuiltinFuncNode *bifunc = static_cast<const Expression::BuiltinFuncNode *>(p_node); Vector<Variant> arr; @@ -2090,33 +1324,33 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: argp.resize(bifunc->arguments.size()); for (int i = 0; i < bifunc->arguments.size(); i++) { - Variant value; bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str); - if (ret) + if (ret) { return true; + } arr.write[i] = value; argp.write[i] = &arr[i]; } + r_ret = Variant(); //may not return anything Callable::CallError ce; - exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str); - + Variant::call_utility_function(bifunc->func, &r_ret, (const Variant **)argp.ptr(), argp.size(), ce); if (ce.error != Callable::CallError::CALL_OK) { - r_error_str = "Builtin Call Failed. " + r_error_str; + r_error_str = "Builtin Call Failed. " + Variant::get_call_error_text(bifunc->func, (const Variant **)argp.ptr(), argp.size(), ce); return true; } } break; case Expression::ENode::TYPE_CALL: { - const Expression::CallNode *call = static_cast<const Expression::CallNode *>(p_node); Variant base; bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str); - if (ret) + if (ret) { return true; + } Vector<Variant> arr; Vector<const Variant *> argp; @@ -2124,18 +1358,18 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: argp.resize(call->arguments.size()); for (int i = 0; i < call->arguments.size(); i++) { - Variant value; ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str); - if (ret) + if (ret) { return true; + } arr.write[i] = value; argp.write[i] = &arr[i]; } Callable::CallError ce; - r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce); + base.call(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce); if (ce.error != Callable::CallError::CALL_OK) { r_error_str = vformat(RTR("On call to '%s':"), String(call->method)); @@ -2148,7 +1382,6 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: } Error Expression::parse(const String &p_expression, const Vector<String> &p_input_names) { - if (nodes) { memdelete(nodes); nodes = nullptr; @@ -2176,7 +1409,6 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu } Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { - ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + "."); execution_error = false; @@ -2201,26 +1433,13 @@ String Expression::get_error_text() const { } void Expression::_bind_methods() { - ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector<String>())); ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed); ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text); } -Expression::Expression() : - output_type(Variant::NIL), - sequenced(false), - error_set(true), - root(nullptr), - nodes(nullptr), - execution_error(false) { - str_ofs = 0; - expression_dirty = false; -} - Expression::~Expression() { - if (nodes) { memdelete(nodes); } diff --git a/core/math/expression.h b/core/math/expression.h index 78de225ebf..a6b288ed6e 100644 --- a/core/math/expression.h +++ b/core/math/expression.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,110 +31,27 @@ #ifndef EXPRESSION_H #define EXPRESSION_H -#include "core/reference.h" +#include "core/object/reference.h" class Expression : public Reference { GDCLASS(Expression, Reference); -public: - enum BuiltinFunc { - MATH_SIN, - MATH_COS, - MATH_TAN, - MATH_SINH, - MATH_COSH, - MATH_TANH, - MATH_ASIN, - MATH_ACOS, - MATH_ATAN, - MATH_ATAN2, - MATH_SQRT, - MATH_FMOD, - MATH_FPOSMOD, - MATH_POSMOD, - MATH_FLOOR, - MATH_CEIL, - MATH_ROUND, - MATH_ABS, - MATH_SIGN, - MATH_POW, - MATH_LOG, - MATH_EXP, - MATH_ISNAN, - MATH_ISINF, - MATH_EASE, - MATH_STEP_DECIMALS, - MATH_STEPIFY, - MATH_LERP, - MATH_LERP_ANGLE, - MATH_INVERSE_LERP, - MATH_RANGE_LERP, - MATH_SMOOTHSTEP, - MATH_MOVE_TOWARD, - MATH_DECTIME, - MATH_RANDOMIZE, - MATH_RAND, - MATH_RANDF, - MATH_RANDOM, - MATH_SEED, - MATH_RANDSEED, - MATH_DEG2RAD, - MATH_RAD2DEG, - MATH_LINEAR2DB, - MATH_DB2LINEAR, - MATH_POLAR2CARTESIAN, - MATH_CARTESIAN2POLAR, - MATH_WRAP, - MATH_WRAPF, - LOGIC_MAX, - LOGIC_MIN, - LOGIC_CLAMP, - LOGIC_NEAREST_PO2, - OBJ_WEAKREF, - FUNC_FUNCREF, - TYPE_CONVERT, - TYPE_OF, - TYPE_EXISTS, - TEXT_CHAR, - TEXT_ORD, - TEXT_STR, - TEXT_PRINT, - TEXT_PRINTERR, - TEXT_PRINTRAW, - VAR_TO_STR, - STR_TO_VAR, - VAR_TO_BYTES, - BYTES_TO_VAR, - COLORN, - FUNC_MAX - }; - - static int get_func_argument_count(BuiltinFunc p_func); - static String get_func_name(BuiltinFunc p_func); - static void exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Callable::CallError &r_error, String &r_error_str); - static BuiltinFunc find_function(const String &p_string); - private: - static const char *func_name[FUNC_MAX]; - struct Input { - - Variant::Type type; + Variant::Type type = Variant::NIL; String name; - Input() : - type(Variant::NIL) { - } + Input() {} }; Vector<Input> inputs; - Variant::Type output_type; + Variant::Type output_type = Variant::NIL; String expression; - bool sequenced; - int str_ofs; - bool expression_dirty; + bool sequenced = false; + int str_ofs = 0; + bool expression_dirty = false; bool _compile_expression(); @@ -182,14 +99,14 @@ private: static const char *token_name[TK_MAX]; struct Token { - TokenType type; Variant value; }; void _set_error(const String &p_err) { - if (error_set) + if (error_set) { return; + } error_str = p_err; error_set = true; } @@ -197,10 +114,9 @@ private: Error _get_token(Token &r_token); String error_str; - bool error_set; + bool error_set = true; struct ENode { - enum Type { TYPE_INPUT, TYPE_CONSTANT, @@ -215,11 +131,11 @@ private: TYPE_CALL }; - ENode *next; + ENode *next = nullptr; - Type type; + Type type = TYPE_INPUT; - ENode() { next = nullptr; } + ENode() {} virtual ~ENode() { if (next) { memdelete(next); @@ -228,8 +144,7 @@ private: }; struct ExpressionNode { - - bool is_op; + bool is_op = false; union { Variant::Operator op; ENode *node; @@ -239,26 +154,23 @@ private: ENode *_parse_expression(); struct InputNode : public ENode { - - int index; + int index = 0; InputNode() { type = TYPE_INPUT; } }; struct ConstantNode : public ENode { - - Variant value; + Variant value = Variant::NIL; ConstantNode() { type = TYPE_CONSTANT; } }; struct OperatorNode : public ENode { + Variant::Operator op = Variant::Operator::OP_ADD; - Variant::Operator op; - - ENode *nodes[2]; + ENode *nodes[2] = { nullptr, nullptr }; OperatorNode() { type = TYPE_OPERATOR; @@ -266,15 +178,14 @@ private: }; struct SelfNode : public ENode { - SelfNode() { type = TYPE_SELF; } }; struct IndexNode : public ENode { - ENode *base; - ENode *index; + ENode *base = nullptr; + ENode *index = nullptr; IndexNode() { type = TYPE_INDEX; @@ -282,7 +193,7 @@ private: }; struct NamedIndexNode : public ENode { - ENode *base; + ENode *base = nullptr; StringName name; NamedIndexNode() { @@ -291,7 +202,7 @@ private: }; struct ConstructorNode : public ENode { - Variant::Type data_type; + Variant::Type data_type = Variant::Type::NIL; Vector<ENode *> arguments; ConstructorNode() { @@ -300,7 +211,7 @@ private: }; struct CallNode : public ENode { - ENode *base; + ENode *base = nullptr; StringName method; Vector<ENode *> arguments; @@ -324,7 +235,7 @@ private: }; struct BuiltinFuncNode : public ENode { - BuiltinFunc func; + StringName func; Vector<ENode *> arguments; BuiltinFuncNode() { type = TYPE_BUILTIN_FUNC; @@ -339,12 +250,12 @@ private: return node; } - ENode *root; - ENode *nodes; + ENode *root = nullptr; + ENode *nodes = nullptr; Vector<String> input_names; - bool execution_error; + bool execution_error = false; bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str); protected: @@ -352,11 +263,11 @@ protected: public: Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>()); - Variant execute(Array p_inputs, Object *p_base = nullptr, bool p_show_error = true); + Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true); bool has_execute_failed() const; String get_error_text() const; - Expression(); + Expression() {} ~Expression(); }; diff --git a/core/math/face3.cpp b/core/math/face3.cpp index 74331b391f..beb0a8e405 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,9 @@ #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); Vector3 above[4]; @@ -43,7 +42,6 @@ int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_ int below_count = 0; for (int i = 0; i < 3; i++) { - if (p_plane.has_point(vertex[i], CMP_EPSILON)) { // point is in plane ERR_FAIL_COND_V(above_count >= 4, 0); @@ -52,7 +50,6 @@ int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_ below[below_count++] = vertex[i]; } else { - if (p_plane.is_point_over(vertex[i])) { //Point is over ERR_FAIL_COND_V(above_count >= 4, 0); @@ -67,8 +64,9 @@ int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_ /* Check for Intersection between this and the next vertex*/ Vector3 inters; - if (!p_plane.intersects_segment(vertex[i], vertex[(i + 1) % 3], &inters)) + if (!p_plane.intersects_segment(vertex[i], vertex[(i + 1) % 3], &inters)) { continue; + } /* Intersection goes to both */ ERR_FAIL_COND_V(above_count >= 4, 0); @@ -83,13 +81,11 @@ int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_ ERR_FAIL_COND_V(above_count >= 4 && below_count >= 4, 0); //bug in the algo if (above_count >= 3) { - p_res[polygons_created] = Face3(above[0], above[1], above[2]); p_is_point_over[polygons_created] = true; polygons_created++; if (above_count == 4) { - p_res[polygons_created] = Face3(above[2], above[3], above[0]); p_is_point_over[polygons_created] = true; polygons_created++; @@ -97,13 +93,11 @@ int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_ } if (below_count >= 3) { - p_res[polygons_created] = Face3(below[0], below[1], below[2]); p_is_point_over[polygons_created] = false; polygons_created++; if (below_count == 4) { - p_res[polygons_created] = Face3(below[2], below[3], below[0]); p_is_point_over[polygons_created] = false; polygons_created++; @@ -114,52 +108,49 @@ 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 { - Vector3 normal = vec3_cross(vertex[0] - vertex[1], vertex[0] - vertex[2]); return (normal.length_squared() < CMP_EPSILON2); } Face3::Side Face3::get_side_of(const Face3 &p_face, ClockDirection p_clock_dir) const { - int over = 0, under = 0; Plane plane = get_plane(p_clock_dir); for (int i = 0; i < 3; i++) { - const Vector3 &v = p_face.vertex[i]; - if (plane.has_point(v)) //coplanar, don't bother + if (plane.has_point(v)) { //coplanar, don't bother continue; + } - if (plane.is_point_over(v)) + if (plane.is_point_over(v)) { over++; - else + } else { under++; + } } - if (over > 0 && under == 0) + if (over > 0 && under == 0) { return SIDE_OVER; - else if (under > 0 && over == 0) + } else if (under > 0 && over == 0) { return SIDE_UNDER; - else if (under == 0 && over == 0) + } else if (under == 0 && over == 0) { return SIDE_COPLANAR; - else + } else { return SIDE_SPANNING; + } } Vector3 Face3::get_random_point_inside() const { - real_t a = Math::random(0, 1); real_t b = Math::random(0, 1); if (a > b) { @@ -170,32 +161,28 @@ Vector3 Face3::get_random_point_inside() const { } Plane Face3::get_plane(ClockDirection p_dir) const { - return Plane(vertex[0], vertex[1], vertex[2], p_dir); } Vector3 Face3::get_median_point() const { - return (vertex[0] + vertex[1] + vertex[2]) / 3.0; } real_t Face3::get_area() const { - return vec3_cross(vertex[0] - vertex[1], vertex[0] - vertex[2]).length(); } ClockDirection Face3::get_clock_dir() const { - Vector3 normal = vec3_cross(vertex[0] - vertex[1], vertex[0] - vertex[2]); //printf("normal is %g,%g,%g x %g,%g,%g- wtfu is %g\n",tofloat(normal.x),tofloat(normal.y),tofloat(normal.z),tofloat(vertex[0].x),tofloat(vertex[0].y),tofloat(vertex[0].z),tofloat( normal.dot( vertex[0] ) ) ); return (normal.dot(vertex[0]) >= 0) ? CLOCKWISE : COUNTERCLOCKWISE; } bool Face3::intersects_aabb(const AABB &p_aabb) const { - /** TEST PLANE **/ - if (!p_aabb.intersects_plane(get_plane())) + if (!p_aabb.intersects_plane(get_plane())) { return false; + } #define TEST_AXIS(m_ax) \ /** TEST FACE AXIS */ \ @@ -228,7 +215,6 @@ bool Face3::intersects_aabb(const AABB &p_aabb) const { }; for (int i = 0; i < 12; i++) { - Vector3 from, to; p_aabb.get_edge(i, from, to); Vector3 e1 = from - to; @@ -237,58 +223,57 @@ bool Face3::intersects_aabb(const AABB &p_aabb) const { Vector3 axis = vec3_cross(e1, e2); - if (axis.length_squared() < 0.0001) + if (axis.length_squared() < 0.0001) { continue; // coplanar + } axis.normalize(); real_t minA, maxA, minB, maxB; p_aabb.project_range_in_plane(Plane(axis, 0), minA, maxA); project_range(axis, Transform(), minB, maxB); - if (maxA < minB || maxB < minA) + if (maxA < minB || maxB < minA) { return false; + } } } return true; } Face3::operator String() const { - return String() + vertex[0] + ", " + vertex[1] + ", " + vertex[2]; } void Face3::project_range(const Vector3 &p_normal, const Transform &p_transform, real_t &r_min, real_t &r_max) const { - for (int i = 0; i < 3; i++) { - Vector3 v = p_transform.xform(vertex[i]); real_t d = p_normal.dot(v); - if (i == 0 || d > r_max) + if (i == 0 || d > r_max) { r_max = d; + } - if (i == 0 || d < r_min) + if (i == 0 || d < r_min) { r_min = d; + } } } void Face3::get_support(const Vector3 &p_normal, const Transform &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const { - #define _FACE_IS_VALID_SUPPORT_THRESHOLD 0.98 #define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.05 - if (p_max <= 0) + if (p_max <= 0) { return; + } Vector3 n = p_transform.basis.xform_inv(p_normal); /** TEST FACE AS SUPPORT **/ if (get_plane().normal.dot(n) > _FACE_IS_VALID_SUPPORT_THRESHOLD) { - *p_count = MIN(3, p_max); for (int i = 0; i < *p_count; i++) { - p_vertices[i] = p_transform.xform(vertex[i]); } @@ -301,7 +286,6 @@ void Face3::get_support(const Vector3 &p_normal, const Transform &p_transform, V real_t support_max = 0; for (int i = 0; i < 3; i++) { - real_t d = n.dot(vertex[i]); if (i == 0 || d > support_max) { @@ -313,19 +297,19 @@ void Face3::get_support(const Vector3 &p_normal, const Transform &p_transform, V /** TEST EDGES AS SUPPORT **/ for (int i = 0; i < 3; i++) { - - if (i != vert_support_idx && i + 1 != vert_support_idx) + if (i != vert_support_idx && i + 1 != vert_support_idx) { continue; + } // check if edge is valid as a support real_t dot = (vertex[i] - vertex[(i + 1) % 3]).normalized().dot(n); dot = ABS(dot); if (dot < _EDGE_IS_VALID_SUPPORT_THRESHOLD) { - *p_count = MIN(2, p_max); - for (int j = 0; j < *p_count; j++) + for (int j = 0; j < *p_count; j++) { p_vertices[j] = p_transform.xform(vertex[(j + i) % 3]); + } return; } @@ -336,7 +320,6 @@ void Face3::get_support(const Vector3 &p_normal, const Transform &p_transform, V } Vector3 Face3::get_closest_point_to(const Vector3 &p_point) const { - Vector3 edge0 = vertex[1] - vertex[0]; Vector3 edge1 = vertex[2] - vertex[0]; Vector3 v0 = vertex[0] - p_point; diff --git a/core/math/face3.h b/core/math/face3.h index f4b8721caa..2e86b0a904 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -69,8 +69,8 @@ public: Vector3 get_median_point() const; Vector3 get_closest_point_to(const Vector3 &p_point) const; - bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection = 0) const; - bool intersects_segment(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection = 0) const; + bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection = nullptr) const; + bool intersects_segment(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection = nullptr) const; ClockDirection get_clock_dir() const; ///< todo, test if this is returning the proper clockwisity @@ -78,7 +78,6 @@ public: void project_range(const Vector3 &p_normal, const Transform &p_transform, real_t &r_min, real_t &r_max) const; AABB get_aabb() const { - AABB aabb(vertex[0], Vector3()); aabb.expand_to(vertex[1]); aabb.expand_to(vertex[2]); @@ -98,7 +97,6 @@ public: }; bool Face3::intersects_aabb2(const AABB &p_aabb) const { - Vector3 perp = (vertex[0] - vertex[2]).cross(vertex[0] - vertex[1]); Vector3 half_extents = p_aabb.size * 0.5; @@ -113,8 +111,9 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const { real_t dist_a = perp.dot(ofs + sup) - d; real_t dist_b = perp.dot(ofs - sup) - d; - if (dist_a * dist_b > 0) + if (dist_a * dist_b > 0) { return false; //does not intersect the plane + } #define TEST_AXIS(m_ax) \ { \ @@ -145,17 +144,13 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const { }; for (int i = 0; i < 12; i++) { - Vector3 from, to; switch (i) { - case 0: { - from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z); to = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z); } break; case 1: { - from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z); } break; @@ -165,18 +160,15 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const { } break; case 3: { - from = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z); to = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); } break; case 4: { - from = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); } break; case 5: { - from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); } break; @@ -186,31 +178,26 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const { } break; case 7: { - from = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); to = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); } break; case 8: { - from = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); to = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); } break; case 9: { - from = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z); to = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); } break; case 10: { - from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z); to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); } break; case 11: { - from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); @@ -223,8 +210,9 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const { Vector3 axis = vec3_cross(e1, e2); - if (axis.length_squared() < 0.0001) + if (axis.length_squared() < 0.0001) { continue; // coplanar + } //axis.normalize(); Vector3 sup2 = Vector3( @@ -240,18 +228,20 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const { real_t minT = 1e20, maxT = -1e20; for (int k = 0; k < 3; k++) { - real_t vert_d = axis.dot(vertex[k]); - if (vert_d > maxT) + if (vert_d > maxT) { maxT = vert_d; + } - if (vert_d < minT) + if (vert_d < minT) { minT = vert_d; + } } - if (maxB < minT || maxT < minB) + if (maxB < minT || maxT < minB) { return false; + } } } return true; diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp new file mode 100644 index 0000000000..783750b9e6 --- /dev/null +++ b/core/math/geometry_2d.cpp @@ -0,0 +1,384 @@ +/*************************************************************************/ +/* geometry_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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/polypartition.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<TPPLPoly> in_poly, out_poly; + + TPPLPoly inp; + inp.Init(polygon.size()); + for (int i = 0; i < polygon.size(); i++) { + inp.GetPoint(i) = polygon[i]; + } + inp.SetOrientation(TPPL_ORIENTATION_CCW); + in_poly.push_back(inp); + TPPLPartition 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<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { + TPPLPoly &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..4b5aef352f --- /dev/null +++ b/core/math/geometry_2d.h @@ -0,0 +1,409 @@ +/*************************************************************************/ +/* geometry_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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/object.h" +#include "core/templates/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. + } + +// Disable False Positives in MSVC compiler; we correctly check for 0 here to prevent a division by 0. +// See: https://github.com/godotengine/godot/pull/44274 +#ifdef _MSC_VER +#pragma warning(disable : 4723) +#endif + + 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; + } + +// Re-enable division by 0 warning +#ifdef _MSC_VER +#pragma warning(default : 4723) +#endif + + 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 fa96fc4535..553184303d 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry_3d.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* geometry.cpp */ +/* geometry_3d.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,36 +28,18 @@ /* 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 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; -} -*/ +#include "core/string/print_string.h" -void Geometry::MeshData::optimize_vertices() { +#include "thirdparty/misc/clipper.hpp" +#include "thirdparty/misc/polypartition.h" +void Geometry3D::MeshData::optimize_vertices() { Map<int, int> vtx_remap; for (int i = 0; i < faces.size(); i++) { - for (int j = 0; j < faces[i].indices.size(); j++) { - int idx = faces[i].indices[j]; if (!vtx_remap.has(idx)) { int ni = vtx_remap.size(); @@ -69,7 +51,6 @@ void Geometry::MeshData::optimize_vertices() { } for (int i = 0; i < edges.size(); i++) { - int a = edges[i].a; int b = edges[i].b; @@ -90,36 +71,28 @@ void Geometry::MeshData::optimize_vertices() { new_vertices.resize(vtx_remap.size()); for (int i = 0; i < vertices.size(); i++) { - - if (vtx_remap.has(i)) + if (vtx_remap.has(i)) { new_vertices.write[vtx_remap[i]] = vertices[i]; + } } vertices = new_vertices; } struct _FaceClassify { - struct _Link { - - int face; - int edge; + int face = -1; + int edge = -1; void clear() { face = -1; edge = -1; } - _Link() { - face = -1; - edge = -1; - } + _Link() {} }; - bool valid; - int group; + bool valid = false; + int group = -1; _Link links[3]; Face3 face; - _FaceClassify() { - group = -1; - valid = false; - }; + _FaceClassify() {} }; static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) { @@ -129,42 +102,36 @@ static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) { bool error = false; for (int i = 0; i < len; i++) { - for (int j = 0; j < 3; j++) { - p_faces[i].links[j].clear(); } } for (int i = 0; i < len; i++) { - - if (p_faces[i].group != p_group) + if (p_faces[i].group != p_group) { continue; + } for (int j = i + 1; j < len; j++) { - - if (p_faces[j].group != p_group) + if (p_faces[j].group != p_group) { continue; + } for (int k = 0; k < 3; k++) { - Vector3 vi1 = p_faces[i].face.vertex[k]; Vector3 vi2 = p_faces[i].face.vertex[(k + 1) % 3]; for (int l = 0; l < 3; l++) { - Vector3 vj2 = p_faces[j].face.vertex[l]; Vector3 vj1 = p_faces[j].face.vertex[(l + 1) % 3]; if (vi1.distance_to(vj1) < 0.00001 && vi2.distance_to(vj2) < 0.00001) { if (p_faces[i].links[k].face != -1) { - ERR_PRINT("already linked\n"); error = true; break; } if (p_faces[j].links[l].face != -1) { - ERR_PRINT("already linked\n"); error = true; break; @@ -176,37 +143,38 @@ static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) { p_faces[j].links[l].edge = k; } } - if (error) + if (error) { break; + } } - if (error) + if (error) { break; + } } - if (error) + if (error) { break; + } } for (int i = 0; i < len; i++) { - p_faces[i].valid = true; for (int j = 0; j < 3; j++) { - - if (p_faces[i].links[j].face == -1) + if (p_faces[i].links[j].face == -1) { p_faces[i].valid = false; + } } } return error; } static bool _group_face(_FaceClassify *p_faces, int len, int p_index, int p_group) { - - if (p_faces[p_index].group >= 0) + if (p_faces[p_index].group >= 0) { return false; + } p_faces[p_index].group = p_group; for (int i = 0; i < 3; i++) { - ERR_FAIL_INDEX_V(p_faces[p_index].links[i].face, len, true); _group_face(p_faces, len, p_faces[p_index].links[i].face, p_group); } @@ -214,8 +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(); @@ -229,7 +196,6 @@ Vector<Vector<Face3>> Geometry::separate_objects(Vector<Face3> p_array) { _FaceClassify *_fcptr = fc.ptrw(); for (int i = 0; i < len; i++) { - _fcptr[i].face = arrayptr[i]; } @@ -241,9 +207,9 @@ Vector<Vector<Face3>> Geometry::separate_objects(Vector<Face3> p_array) { int group = 0; for (int i = 0; i < len; i++) { - - if (!_fcptr[i].valid) + if (!_fcptr[i].valid) { continue; + } if (_group_face(_fcptr, len, i, group)) { group++; } @@ -252,20 +218,18 @@ Vector<Vector<Face3>> Geometry::separate_objects(Vector<Face3> p_array) { // Group connected faces in separate objects. for (int i = 0; i < len; i++) { - _fcptr[i].face = arrayptr[i]; } if (group >= 0) { - objects.resize(group); Vector<Face3> *group_faces = objects.ptrw(); for (int i = 0; i < len; i++) { - if (!_fcptr[i].valid) + if (!_fcptr[i].valid) { continue; + } if (_fcptr[i].group >= 0 && _fcptr[i].group < group) { - group_faces[_fcptr[i].group].push_back(_fcptr[i].face); } } @@ -277,7 +241,6 @@ Vector<Vector<Face3>> Geometry::separate_objects(Vector<Face3> p_array) { /*** GEOMETRY WRAPPER ***/ enum _CellFlags { - _CELL_SOLID = 1, _CELL_EXTERIOR = 2, _CELL_STEP_MASK = 0x1C, @@ -298,20 +261,18 @@ enum _CellFlags { _CELL_PREV_Z_POS = 5 << 5, _CELL_PREV_Z_NEG = 6 << 5, _CELL_PREV_FIRST = 7 << 5, - }; static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z, const Vector3 &voxelsize, const Face3 &p_face) { - AABB aabb(Vector3(x, y, z), Vector3(len_x, len_y, len_z)); aabb.position = aabb.position * voxelsize; aabb.size = aabb.size * voxelsize; - if (!p_face.intersects_aabb(aabb)) + if (!p_face.intersects_aabb(aabb)) { return; + } if (len_x == 1 && len_y == 1 && len_z == 1) { - p_cell_status[x][y][z] = _CELL_SOLID; return; } @@ -340,15 +301,12 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int int new_len_z; for (int i = 0; i < div_x; i++) { - _SPLIT(i, div_x, x, len_x, new_x, new_len_x); for (int j = 0; j < div_y; j++) { - _SPLIT(j, div_y, y, len_y, new_y, new_len_y); for (int k = 0; k < div_z; k++) { - _SPLIT(k, div_z, z, len_z, new_z, new_len_z); _plot_face(p_cell_status, new_x, new_y, new_z, new_len_x, new_len_y, new_len_z, voxelsize, p_face); @@ -358,14 +316,13 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int } static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z) { - - if (p_cell_status[x][y][z] & 3) + if (p_cell_status[x][y][z] & 3) { return; // Nothing to do, already used and/or visited. + } p_cell_status[x][y][z] = _CELL_PREV_FIRST; while (true) { - uint8_t &c = p_cell_status[x][y][z]; if ((c & _CELL_STEP_MASK) == _CELL_STEP_NONE) { @@ -419,9 +376,7 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, uint8_t prev = 0; switch (c & _CELL_STEP_MASK) { - case _CELL_STEP_Y_POS: { - next_y++; prev = _CELL_PREV_Y_NEG; } break; @@ -445,18 +400,23 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, next_z--; prev = _CELL_PREV_Z_POS; } break; - default: ERR_FAIL(); + default: + ERR_FAIL(); } - if (next_x < 0 || next_x >= len_x) + if (next_x < 0 || next_x >= len_x) { continue; - if (next_y < 0 || next_y >= len_y) + } + if (next_y < 0 || next_y >= len_y) { continue; - if (next_z < 0 || next_z >= len_z) + } + if (next_z < 0 || next_z >= len_z) { continue; + } - if (p_cell_status[next_x][next_y][next_z] & 3) + if (p_cell_status[next_x][next_y][next_z] & 3) { continue; + } x = next_x; y = next_y; @@ -466,13 +426,13 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, } static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z, Vector<Face3> &p_faces) { - ERR_FAIL_INDEX(x, len_x); ERR_FAIL_INDEX(y, len_y); ERR_FAIL_INDEX(z, len_z); - if (p_cell_status[x][y][z] & _CELL_EXTERIOR) + if (p_cell_status[x][y][z] & _CELL_EXTERIOR) { return; + } #define vert(m_idx) Vector3(((m_idx)&4) >> 2, ((m_idx)&2) >> 1, (m_idx)&1) @@ -487,7 +447,6 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i }; for (int i = 0; i < 6; i++) { - Vector3 face_points[4]; int disp_x = x + ((i % 3) == 0 ? ((i < 3) ? 1 : -1) : 0); int disp_y = y + (((i - 1) % 3) == 0 ? ((i < 3) ? 1 : -1) : 0); @@ -495,21 +454,27 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i bool plot = false; - if (disp_x < 0 || disp_x >= len_x) + if (disp_x < 0 || disp_x >= len_x) { plot = true; - if (disp_y < 0 || disp_y >= len_y) + } + if (disp_y < 0 || disp_y >= len_y) { plot = true; - if (disp_z < 0 || disp_z >= len_z) + } + if (disp_z < 0 || disp_z >= len_z) { plot = true; + } - if (!plot && (p_cell_status[disp_x][disp_y][disp_z] & _CELL_EXTERIOR)) + if (!plot && (p_cell_status[disp_x][disp_y][disp_z] & _CELL_EXTERIOR)) { plot = true; + } - if (!plot) + if (!plot) { continue; + } - for (int j = 0; j < 4; j++) + for (int j = 0; j < 4; j++) { face_points[j] = vert(indices[i][j]) + Vector3(x, y, z); + } p_faces.push_back( Face3( @@ -525,8 +490,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 @@ -536,12 +500,9 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { AABB global_aabb; for (int i = 0; i < face_count; i++) { - if (i == 0) { - global_aabb = faces[i].get_aabb(); } else { - global_aabb.merge_with(faces[i].get_aabb()); } } @@ -551,20 +512,23 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { // Determine amount of cells in grid axis. int div_x, div_y, div_z; - if (global_aabb.size.x / _MIN_SIZE < _MAX_LENGTH) + if (global_aabb.size.x / _MIN_SIZE < _MAX_LENGTH) { div_x = (int)(global_aabb.size.x / _MIN_SIZE) + 1; - else + } else { div_x = _MAX_LENGTH; + } - if (global_aabb.size.y / _MIN_SIZE < _MAX_LENGTH) + if (global_aabb.size.y / _MIN_SIZE < _MAX_LENGTH) { div_y = (int)(global_aabb.size.y / _MIN_SIZE) + 1; - else + } else { div_y = _MAX_LENGTH; + } - if (global_aabb.size.z / _MIN_SIZE < _MAX_LENGTH) + if (global_aabb.size.z / _MIN_SIZE < _MAX_LENGTH) { div_z = (int)(global_aabb.size.z / _MIN_SIZE) + 1; - else + } else { div_z = _MAX_LENGTH; + } Vector3 voxelsize = global_aabb.size; voxelsize.x /= div_x; @@ -575,15 +539,12 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { uint8_t ***cell_status = memnew_arr(uint8_t **, div_x); for (int i = 0; i < div_x; i++) { - cell_status[i] = memnew_arr(uint8_t *, div_y); for (int j = 0; j < div_y; j++) { - cell_status[i][j] = memnew_arr(uint8_t, div_z); for (int k = 0; k < div_z; k++) { - cell_status[i][j][k] = 0; } } @@ -592,10 +553,8 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { // Plot faces into cells. for (int i = 0; i < face_count; i++) { - Face3 f = faces[i]; for (int j = 0; j < 3; j++) { - f.vertex[j] -= global_aabb.position; } _plot_face(cell_status, 0, 0, 0, div_x, div_y, div_z, voxelsize, f); @@ -604,27 +563,21 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { // Determine which cells connect to the outside by traversing the outside and recursively flood-fill marking. for (int i = 0; i < div_x; i++) { - for (int j = 0; j < div_y; j++) { - _mark_outside(cell_status, i, j, 0, div_x, div_y, div_z); _mark_outside(cell_status, i, j, div_z - 1, div_x, div_y, div_z); } } for (int i = 0; i < div_z; i++) { - for (int j = 0; j < div_y; j++) { - _mark_outside(cell_status, 0, j, i, div_x, div_y, div_z); _mark_outside(cell_status, div_x - 1, j, i, div_x, div_y, div_z); } } for (int i = 0; i < div_x; i++) { - for (int j = 0; j < div_z; j++) { - _mark_outside(cell_status, i, 0, j, div_x, div_y, div_z); _mark_outside(cell_status, i, div_y - 1, j, div_x, div_y, div_z); } @@ -635,11 +588,8 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { Vector<Face3> wrapped_faces; for (int i = 0; i < div_x; i++) { - for (int j = 0; j < div_y; j++) { - for (int k = 0; k < div_z; k++) { - _build_faces(cell_status, i, j, k, div_x, div_y, div_z, wrapped_faces); } } @@ -651,9 +601,7 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { Face3 *wrapped_faces_ptr = wrapped_faces.ptrw(); for (int i = 0; i < wrapped_faces_count; i++) { - for (int j = 0; j < 3; j++) { - Vector3 &v = wrapped_faces_ptr[i].vertex[j]; v = v * voxelsize; v += global_aabb.position; @@ -663,9 +611,7 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { // clean up grid for (int i = 0; i < div_x; i++) { - for (int j = 0; j < div_y; j++) { - memdelete_arr(cell_status[i][j]); } @@ -673,68 +619,34 @@ Vector<Face3> Geometry::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { } memdelete_arr(cell_status); - if (p_error) + if (p_error) { *p_error = voxelsize.length(); - - 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; + return wrapped_faces; } -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 real_t subplane_size = 1024.0; // Should compute this from the actual plane. for (int i = 0; i < p_planes.size(); i++) { - Plane p = p_planes[i]; Vector3 ref = Vector3(0.0, 1.0, 0.0); - if (ABS(p.normal.dot(ref)) > 0.95) + if (ABS(p.normal.dot(ref)) > 0.95) { ref = Vector3(0.0, 0.0, 1.0); // Change axis. + } Vector3 right = p.normal.cross(ref).normalized(); Vector3 up = p.normal.cross(right).normalized(); Vector<Vector3> vertices; - Vector3 center = p.get_any_point(); + Vector3 center = p.center(); // make a quad clockwise vertices.push_back(center - up * subplane_size + right * subplane_size); vertices.push_back(center - up * subplane_size - right * subplane_size); @@ -742,21 +654,22 @@ Geometry::MeshData Geometry::build_convex_mesh(const Vector<Plane> &p_planes) { vertices.push_back(center + up * subplane_size + right * subplane_size); for (int j = 0; j < p_planes.size(); j++) { - - if (j == i) + if (j == i) { continue; + } Vector<Vector3> new_vertices; Plane clip = p_planes[j]; - if (clip.normal.dot(p.normal) > 0.95) + if (clip.normal.dot(p.normal) > 0.95) { continue; + } - if (vertices.size() < 3) + if (vertices.size() < 3) { break; + } for (int k = 0; k < vertices.size(); k++) { - int k_n = (k + 1) % vertices.size(); Vector3 edge0_A = vertices[k]; @@ -772,13 +685,13 @@ Geometry::MeshData Geometry::build_convex_mesh(const Vector<Plane> &p_planes) { // Check for different sides and non coplanar. if ((dist0 * dist1) < 0) { - // Calculate intersection. Vector3 rel = edge1_A - edge0_A; real_t den = clip.normal.dot(rel); - if (Math::is_zero_approx(den)) + if (Math::is_zero_approx(den)) { continue; // Point too short. + } real_t dist = -(clip.normal.dot(edge0_A) - clip.d) / den; Vector3 inters = edge0_A + rel * dist; @@ -789,8 +702,9 @@ Geometry::MeshData Geometry::build_convex_mesh(const Vector<Plane> &p_planes) { vertices = new_vertices; } - if (vertices.size() < 3) + if (vertices.size() < 3) { continue; + } // Result is a clockwise face. @@ -798,19 +712,15 @@ Geometry::MeshData Geometry::build_convex_mesh(const Vector<Plane> &p_planes) { // Add face indices. for (int j = 0; j < vertices.size(); j++) { - int idx = -1; for (int k = 0; k < mesh.vertices.size(); k++) { - if (mesh.vertices[k].distance_to(vertices[j]) < 0.001) { - idx = k; break; } } if (idx == -1) { - idx = mesh.vertices.size(); mesh.vertices.push_back(vertices[j]); } @@ -823,13 +733,11 @@ Geometry::MeshData Geometry::build_convex_mesh(const Vector<Plane> &p_planes) { // Add edge. for (int j = 0; j < face.indices.size(); j++) { - int a = face.indices[j]; int b = face.indices[(j + 1) % face.indices.size()]; bool found = false; for (int k = 0; k < mesh.edges.size(); k++) { - if (mesh.edges[k].a == a && mesh.edges[k].b == b) { found = true; break; @@ -840,8 +748,9 @@ Geometry::MeshData Geometry::build_convex_mesh(const Vector<Plane> &p_planes) { } } - if (found) + if (found) { continue; + } MeshData::Edge edge; edge.a = a; edge.b = b; @@ -852,8 +761,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)); @@ -866,12 +774,10 @@ 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++) { - Vector3 normal; normal[(p_axis + 1) % 3] = Math::cos(i * (2.0 * Math_PI) / p_sides); normal[(p_axis + 2) % 3] = Math::sin(i * (2.0 * Math_PI) / p_sides); @@ -888,8 +794,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; @@ -901,7 +806,6 @@ Vector<Plane> Geometry::build_sphere_planes(real_t p_radius, int p_lats, int p_l axis_neg[p_axis] = -1.0; for (int i = 0; i < p_lons; i++) { - Vector3 normal; normal[(p_axis + 1) % 3] = Math::cos(i * (2.0 * Math_PI) / p_lons); normal[(p_axis + 2) % 3] = Math::sin(i * (2.0 * Math_PI) / p_lons); @@ -909,7 +813,6 @@ Vector<Plane> Geometry::build_sphere_planes(real_t p_radius, int p_lats, int p_l planes.push_back(Plane(normal, p_radius)); for (int j = 1; j <= p_lats; j++) { - // FIXME: This is stupid. Vector3 angle = normal.lerp(axis, j / (real_t)p_lats).normalized(); Vector3 pos = angle * p_radius; @@ -921,8 +824,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; @@ -934,7 +836,6 @@ Vector<Plane> Geometry::build_capsule_planes(real_t p_radius, real_t p_height, i axis_neg[p_axis] = -1.0; for (int i = 0; i < p_sides; i++) { - Vector3 normal; normal[(p_axis + 1) % 3] = Math::cos(i * (2.0 * Math_PI) / p_sides); normal[(p_axis + 2) % 3] = Math::sin(i * (2.0 * Math_PI) / p_sides); @@ -942,7 +843,6 @@ Vector<Plane> Geometry::build_capsule_planes(real_t p_radius, real_t p_height, i planes.push_back(Plane(normal, p_radius)); for (int j = 1; j <= p_lats; j++) { - Vector3 angle = normal.lerp(axis, j / (real_t)p_lats).normalized(); Vector3 pos = axis * p_height * 0.5 + angle * p_radius; planes.push_back(Plane(pos, angle)); @@ -953,267 +853,161 @@ 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; - } +Vector<Vector3> Geometry3D::compute_convex_mesh_points(const Plane *p_planes, int p_plane_count) { + Vector<Vector3> points; - int from_y = 0; - for (int k = 0; k < wrects[j].s.width; k++) { + // Iterate through every unique combination of any three planes. + for (int i = p_plane_count - 1; i >= 0; i--) { + for (int j = i - 1; j >= 0; j--) { + for (int k = j - 1; k >= 0; k--) { + // Find the point where these planes all cross over (if they + // do at all). + Vector3 convex_shape_point; + if (p_planes[i].intersect_3(p_planes[j], p_planes[k], &convex_shape_point)) { + // See if any *other* plane excludes this point because it's + // on the wrong side. + bool excluded = false; + for (int n = 0; n < p_plane_count; n++) { + if (n != i && n != j && n != k) { + real_t dp = p_planes[n].normal.dot(convex_shape_point); + if (dp - p_planes[n].d > CMP_EPSILON) { + excluded = true; + break; + } + } + } - if (hmax[ofs + k] > from_y) - from_y = hmax[ofs + k]; + // Only add the point if it passed all tests. + if (!excluded) { + points.push_back(convex_shape_point); + } + } } + } + } - 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; - } + return points; +} - if (end_h > max_h) - max_h = end_h; +#define square(m_s) ((m_s) * (m_s)) +#define INF 1e20 - if (end_w > max_w) - max_w = end_w; +/* dt of 1d function using squared distance */ +static void edt(float *f, int stride, int n) { + float *d = (float *)alloca(sizeof(float) * n + sizeof(int) * n + sizeof(float) * (n + 1)); + int *v = (int *)&(d[n]); + float *z = (float *)&v[n]; - if (ofs == 0 || end_h > limit_h) // While h limit not reached, keep stacking. - ofs += wrects[j].s.width; + int k = 0; + v[0] = 0; + z[0] = -INF; + z[1] = +INF; + for (int q = 1; q <= n - 1; q++) { + float s = ((f[q * stride] + square(q)) - (f[v[k] * stride] + square(v[k]))) / (2 * q - 2 * v[k]); + while (s <= z[k]) { + k--; + s = ((f[q * stride] + square(q)) - (f[v[k] * stride] + square(v[k]))) / (2 * q - 2 * v[k]); } + k++; + v[k] = q; - _AtlasWorkRectResult result; - result.result = wrects; - result.max_h = max_h; - result.max_w = max_w; - results.push_back(result); + z[k] = s; + z[k + 1] = +INF; } - // 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; + k = 0; + for (int q = 0; q <= n - 1; q++) { + while (z[k + 1] < q) { + k++; } + d[q] = square(q - v[k]) + f[v[k] * stride]; } - 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; + for (int i = 0; i < n; i++) { + f[i * stride] = d[i]; } - - 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; +#undef square - // 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. +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; - Paths paths; + ERR_FAIL_COND_V((uint32_t)p_voxels.size() != float_count, Vector<uint32_t>()); - 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. + float *work_memory = memnew_arr(float, float_count); + for (uint32_t i = 0; i < float_count; i++) { + work_memory[i] = INF; } - // Have to scale points down now. - Vector<Vector<Point2>> polypaths; - for (Paths::size_type i = 0; i < paths.size(); ++i) { - Vector<Vector2> polypath; + uint32_t y_mult = p_size.x; + uint32_t z_mult = y_mult * p_size.y; - 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)); + //plot solid cells + { + const bool *voxr = p_voxels.ptr(); + for (uint32_t i = 0; i < float_count; i++) { + bool plot = voxr[i]; + if (p_negative) { + plot = !plot; + } + if (plot) { + work_memory[i] = 0; + } } - 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) { + //process in each direction - using namespace ClipperLib; + //xy->z - 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; + for (int i = 0; i < p_size.x; i++) { + for (int j = 0; j < p_size.y; j++) { + edt(&work_memory[i + j * y_mult], z_mult, p_size.z); + } } - EndType et = etClosedPolygon; + //xz->y - 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; + for (int i = 0; i < p_size.x; i++) { + for (int j = 0; j < p_size.z; j++) { + edt(&work_memory[i + j * z_mult], y_mult, p_size.y); + } } - 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); + //yz->x + for (int i = 0; i < p_size.y; i++) { + for (int j = 0; j < p_size.z; j++) { + edt(&work_memory[i * y_mult + j * z_mult], 1, p_size.x); + } } - 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)); + Vector<uint32_t> ret; + ret.resize(float_count); + { + uint32_t *w = ret.ptrw(); + for (uint32_t i = 0; i < float_count; i++) { + w[i] = uint32_t(Math::sqrt(work_memory[i])); } - polypaths.push_back(polypath); } - return polypaths; -} - -Vector<Vector3> Geometry::compute_convex_mesh_points(const Plane *p_planes, int p_plane_count) { - - Vector<Vector3> points; - // Iterate through every unique combination of any three planes. - for (int i = p_plane_count - 1; i >= 0; i--) { - for (int j = i - 1; j >= 0; j--) { - for (int k = j - 1; k >= 0; k--) { + memdelete_arr(work_memory); - // Find the point where these planes all cross over (if they - // do at all). - Vector3 convex_shape_point; - if (p_planes[i].intersect_3(p_planes[j], p_planes[k], &convex_shape_point)) { + return ret; +} - // See if any *other* plane excludes this point because it's - // on the wrong side. - bool excluded = false; - for (int n = 0; n < p_plane_count; n++) { - if (n != i && n != j && n != k) { - real_t dp = p_planes[n].normal.dot(convex_shape_point); - if (dp - p_planes[n].d > CMP_EPSILON) { - excluded = true; - break; - } - } - } +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(); + sdf8.resize(s); - // Only add the point if it passed all tests. - if (!excluded) { - points.push_back(convex_shape_point); - } - } - } - } + const uint32_t *rpos = p_positive.ptr(); + const uint32_t *rneg = p_negative.ptr(); + int8_t *wsdf = sdf8.ptrw(); + for (int i = 0; i < s; i++) { + int32_t diff = int32_t(rpos[i]) - int32_t(rneg[i]); + wsdf[i] = CLAMP(diff, -128, 127); } - - return points; + return sdf8; } diff --git a/core/math/geometry.h b/core/math/geometry_3d.h index ea063a8a59..825817af5e 100644 --- a/core/math/geometry.h +++ b/core/math/geometry_3d.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* geometry.h */ +/* geometry_3d.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,83 +28,18 @@ /* 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.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/object/object.h" +#include "core/templates/vector.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)) @@ -113,10 +48,18 @@ public: real_t mub = (d_of(p1, q1, q2, q1) + mua * d_of(q2, q1, p2, p1)) / d_of(q2, q1, q2, q1); // Clip the value between [0..1] constraining the solution to lie on the original curves. - if (mua < 0) mua = 0; - if (mub < 0) mub = 0; - if (mua > 1) mua = 1; - if (mub > 1) mub = 1; + if (mua < 0) { + mua = 0; + } + if (mub < 0) { + mub = 0; + } + if (mua > 1) { + mua = 1; + } + if (mub > 1) { + mub = 1; + } c1 = p1.lerp(p2, mua); c2 = q1.lerp(q2, mub); } @@ -157,22 +100,22 @@ public: if (tN < 0.0) { // tc < 0 => the t=0 edge is visible. tN = 0.0; // Recompute sc for this edge. - if (-d < 0.0) + if (-d < 0.0) { sN = 0.0; - else if (-d > a) + } else if (-d > a) { sN = sD; - else { + } else { sN = -d; sD = a; } } else if (tN > tD) { // tc > 1 => the t=1 edge is visible. tN = tD; // Recompute sc for this edge. - if ((-d + b) < 0.0) + if ((-d + b) < 0.0) { sN = 0; - else if ((-d + b) > a) + } else if ((-d + b) > a) { sN = sD; - else { + } else { sN = (-d + b); sD = a; } @@ -187,120 +130,134 @@ public: return dP.length(); // Return the closest distance. } - static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = 0) { + static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = nullptr) { Vector3 e1 = p_v1 - p_v0; Vector3 e2 = p_v2 - p_v0; Vector3 h = p_dir.cross(e2); real_t a = e1.dot(h); - if (Math::is_zero_approx(a)) // Parallel test. + if (Math::is_zero_approx(a)) { // Parallel test. return false; + } real_t f = 1.0 / a; Vector3 s = p_from - p_v0; real_t u = f * s.dot(h); - if (u < 0.0 || u > 1.0) + if (u < 0.0 || u > 1.0) { return false; + } Vector3 q = s.cross(e1); real_t v = f * p_dir.dot(q); - if (v < 0.0 || u + v > 1.0) + if (v < 0.0 || u + v > 1.0) { return false; + } // At this stage we can compute t to find out where // the intersection point is on the line. real_t t = f * e2.dot(q); if (t > 0.00001) { // ray intersection - if (r_res) + if (r_res) { *r_res = p_from + p_dir * t; + } return true; - } else // This means that there is a line intersection but not a ray intersection. + } else { // This means that there is a line intersection but not a ray intersection. return false; + } } - static inline bool segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = 0) { - + static inline bool segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = nullptr) { Vector3 rel = p_to - p_from; Vector3 e1 = p_v1 - p_v0; Vector3 e2 = p_v2 - p_v0; Vector3 h = rel.cross(e2); real_t a = e1.dot(h); - if (Math::is_zero_approx(a)) // Parallel test. + if (Math::is_zero_approx(a)) { // Parallel test. return false; + } real_t f = 1.0 / a; Vector3 s = p_from - p_v0; real_t u = f * s.dot(h); - if (u < 0.0 || u > 1.0) + if (u < 0.0 || u > 1.0) { return false; + } Vector3 q = s.cross(e1); real_t v = f * rel.dot(q); - if (v < 0.0 || u + v > 1.0) + if (v < 0.0 || u + v > 1.0) { return false; + } // At this stage we can compute t to find out where // the intersection point is on the line. real_t t = f * e2.dot(q); if (t > CMP_EPSILON && t <= 1.0) { // Ray intersection. - if (r_res) + if (r_res) { *r_res = p_from + rel * t; + } return true; - } else // This means that there is a line intersection but not a ray intersection. + } else { // This means that there is a line intersection but not a ray intersection. return false; + } } - static inline bool segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 *r_res = 0, Vector3 *r_norm = 0) { - + static inline bool segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 *r_res = nullptr, Vector3 *r_norm = nullptr) { Vector3 sphere_pos = p_sphere_pos - p_from; Vector3 rel = (p_to - p_from); real_t rel_l = rel.length(); - if (rel_l < CMP_EPSILON) + if (rel_l < CMP_EPSILON) { return false; // Both points are the same. + } Vector3 normal = rel / rel_l; real_t sphere_d = normal.dot(sphere_pos); real_t ray_distance = sphere_pos.distance_to(normal * sphere_d); - if (ray_distance >= p_sphere_radius) + if (ray_distance >= p_sphere_radius) { return false; + } real_t inters_d2 = p_sphere_radius * p_sphere_radius - ray_distance * ray_distance; real_t inters_d = sphere_d; - if (inters_d2 >= CMP_EPSILON) + if (inters_d2 >= CMP_EPSILON) { inters_d -= Math::sqrt(inters_d2); + } // Check in segment. - if (inters_d < 0 || inters_d > rel_l) + if (inters_d < 0 || inters_d > rel_l) { return false; + } Vector3 result = p_from + normal * inters_d; - if (r_res) + if (r_res) { *r_res = result; - if (r_norm) + } + if (r_norm) { *r_norm = (result - p_sphere_pos).normalized(); + } return true; } - static inline bool segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, real_t p_height, real_t p_radius, Vector3 *r_res = 0, Vector3 *r_norm = 0) { - + static inline bool segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, real_t p_height, real_t p_radius, Vector3 *r_res = nullptr, Vector3 *r_norm = nullptr) { Vector3 rel = (p_to - p_from); real_t rel_l = rel.length(); - if (rel_l < CMP_EPSILON) + if (rel_l < CMP_EPSILON) { return false; // Both points are the same. + } // First check if they are parallel. Vector3 normal = (rel / rel_l); @@ -317,13 +274,15 @@ public: real_t dist = z_dir.dot(p_from); - if (dist >= p_radius) + if (dist >= p_radius) { return false; // Too far away. + } // Convert to 2D. real_t w2 = p_radius * p_radius - dist * dist; - if (w2 < CMP_EPSILON) + if (w2 < CMP_EPSILON) { return false; // Avoid numerical error. + } Size2 size(Math::sqrt(w2), p_height * 0.5); Vector3 x_dir = z_dir.cross(Vector3(0, 0, 1)).normalized(); @@ -336,7 +295,6 @@ public: int axis = -1; for (int i = 0; i < 2; i++) { - real_t seg_from = from2D[i]; real_t seg_to = to2D[i]; real_t box_begin = -size[i]; @@ -344,17 +302,17 @@ public: real_t cmin, cmax; if (seg_from < seg_to) { - - if (seg_from > box_end || seg_to < box_begin) + if (seg_from > box_end || seg_to < box_begin) { return false; + } real_t length = seg_to - seg_from; cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; } else { - - if (seg_to > box_end || seg_from < box_begin) + if (seg_to > box_end || seg_from < box_begin) { return false; + } real_t length = seg_to - seg_from; cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; @@ -364,10 +322,12 @@ public: min = cmin; axis = i; } - if (cmax < max) + if (cmax < max) { max = cmax; - if (max < min) + } + if (max < min) { return false; + } } // Convert to 3D again. @@ -383,45 +343,47 @@ public: res_normal.normalize(); - if (r_res) + if (r_res) { *r_res = result; - if (r_norm) + } + if (r_norm) { *r_norm = res_normal; + } return true; } static bool segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Plane *p_planes, int p_plane_count, Vector3 *p_res, Vector3 *p_norm) { - real_t min = -1e20, max = 1e20; Vector3 rel = p_to - p_from; real_t rel_l = rel.length(); - if (rel_l < CMP_EPSILON) + if (rel_l < CMP_EPSILON) { return false; + } Vector3 dir = rel / rel_l; int min_index = -1; for (int i = 0; i < p_plane_count; i++) { - const Plane &p = p_planes[i]; real_t den = p.normal.dot(dir); - if (Math::abs(den) <= CMP_EPSILON) + if (Math::abs(den) <= CMP_EPSILON) { continue; // Ignore parallel plane. + } real_t dist = -p.distance_to(p_from) / den; if (den > 0) { // Backwards facing plane. - if (dist < max) + if (dist < max) { max = dist; + } } else { - // Front facing plane. if (dist > min) { min = dist; @@ -430,169 +392,89 @@ public: } } - if (max <= min || min < 0 || min > rel_l || min_index == -1) // Exit conditions. + if (max <= min || min < 0 || min > rel_l || min_index == -1) { // Exit conditions. return false; // No intersection. + } - if (p_res) + if (p_res) { *p_res = p_from + dir * min; - if (p_norm) + } + if (p_norm) { *p_norm = p_planes[min_index].normal; + } return true; } static Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 *p_segment) { - Vector3 p = p_point - p_segment[0]; Vector3 n = p_segment[1] - p_segment[0]; real_t l2 = n.length_squared(); - if (l2 < 1e-20) + 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) + if (d <= 0.0) { return p_segment[0]; // Before first point. - else if (d >= 1.0) + } else if (d >= 1.0) { return p_segment[1]; // After first point. - else + } else { return p_segment[0] + n * d; // Inside. + } } static Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 *p_segment) { - Vector3 p = p_point - p_segment[0]; Vector3 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 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) + 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); Vector3 n1 = (p_point - p_v3).cross(p_point - p_v2); - if (face_n.dot(n1) < 0) + if (face_n.dot(n1) < 0) { return false; + } Vector3 n2 = (p_v1 - p_v3).cross(p_v1 - p_point); - if (face_n.dot(n2) < 0) + if (face_n.dot(n2) < 0) { return false; + } Vector3 n3 = (p_v1 - p_point).cross(p_v1 - p_v2); - if (face_n.dot(n3) < 0) + if (face_n.dot(n3) < 0) { return false; + } return true; } static inline bool triangle_sphere_intersection_test(const Vector3 *p_triangle, const Vector3 &p_normal, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 &r_triangle_contact, Vector3 &r_sphere_contact) { - real_t d = p_normal.dot(p_sphere_pos) - p_normal.dot(p_triangle[0]); - if (d > p_sphere_radius || d < -p_sphere_radius) // Not touching the plane of the face, return. + if (d > p_sphere_radius || d < -p_sphere_radius) { + // Not touching the plane of the face, return. return false; + } Vector3 contact = p_sphere_pos - (p_normal * d); /** 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"); @@ -604,7 +486,6 @@ public: const Vector3 verts[4] = { p_triangle[0], p_triangle[1], p_triangle[2], p_triangle[0] }; // for() friendly for (int i = 0; i < 3; i++) { - // Check edge cylinder. Vector3 n1 = verts[i] - verts[i + 1]; @@ -629,7 +510,6 @@ public: real_t sphere_at = n1.dot(n2); if (sphere_at >= 0 && sphere_at < n1.dot(n1)) { - r_triangle_contact = p_sphere_pos - axis * (axis.dot(n2)); r_sphere_contact = p_sphere_pos - axis * p_sphere_radius; // Point inside here. @@ -639,7 +519,6 @@ public: real_t r2 = p_sphere_radius * p_sphere_radius; if (n2.length_squared() < r2) { - Vector3 n = (p_sphere_pos - verts[i + 1]).normalized(); r_triangle_contact = verts[i + 1]; @@ -661,51 +540,16 @@ 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, LOC_BOUNDARY = 0, LOC_OUTSIDE = -1 }; - if (polygon.size() == 0) + if (polygon.size() == 0) { return polygon; + } int *location_cache = (int *)alloca(sizeof(int) * polygon.size()); int inside_count = 0; @@ -727,11 +571,8 @@ public: } if (outside_count == 0) { - return polygon; // No changes. - } else if (inside_count == 0) { - return Vector<Vector3>(); // Empty. } @@ -771,141 +612,12 @@ 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. static Vector<Face3> wrap_geometry(Vector<Face3> p_array, real_t *p_error = nullptr); struct MeshData { - struct Face { Plane plane; Vector<int> indices; @@ -914,7 +626,6 @@ public: Vector<Face> faces; struct Edge { - int a, b; }; @@ -925,100 +636,277 @@ public: void optimize_vertices(); }; - _FORCE_INLINE_ static int get_uv84_normal_bit(const Vector3 &p_vector) { + 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); - int lat = Math::fast_ftoi(Math::floor(Math::acos(p_vector.dot(Vector3(0, 1, 0))) * 4.0 / Math_PI + 0.5)); + static Vector<Vector3> compute_convex_mesh_points(const Plane *p_planes, int p_plane_count); - if (lat == 0) { - return 24; - } else if (lat == 4) { - return 25; +#define FINDMINMAX(x0, x1, x2, min, max) \ + min = max = x0; \ + if (x1 < min) { \ + min = x1; \ + } \ + if (x1 > max) { \ + max = x1; \ + } \ + if (x2 < min) { \ + min = x2; \ + } \ + if (x2 > max) { \ + max = x2; \ + } + + _FORCE_INLINE_ static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) { + int q; + Vector3 vmin, vmax; + for (q = 0; q <= 2; q++) { + if (normal[q] > 0.0f) { + vmin[q] = -maxbox[q]; + vmax[q] = maxbox[q]; + } else { + vmin[q] = maxbox[q]; + vmax[q] = -maxbox[q]; + } + } + if (normal.dot(vmin) + d > 0.0f) { + return false; + } + if (normal.dot(vmax) + d >= 0.0f) { + return true; } - int lon = Math::fast_ftoi(Math::floor((Math_PI + Math::atan2(p_vector.x, p_vector.z)) * 8.0 / (Math_PI * 2.0) + 0.5)) % 8; - - return lon + (lat - 1) * 8; + return false; } - _FORCE_INLINE_ static int get_uv84_normal_bit_neighbors(int p_idx) { - - if (p_idx == 24) { - return 1 | 2 | 4 | 8; - } else if (p_idx == 25) { - return (1 << 23) | (1 << 22) | (1 << 21) | (1 << 20); - } else { - - int ret = 0; - if ((p_idx % 8) == 0) - ret |= (1 << (p_idx + 7)); - else - ret |= (1 << (p_idx - 1)); - if ((p_idx % 8) == 7) - ret |= (1 << (p_idx - 7)); - else - ret |= (1 << (p_idx + 1)); - - int mask = ret | (1 << p_idx); - if (p_idx < 8) - ret |= 24; - else - ret |= mask >> 8; - - if (p_idx >= 16) - ret |= 25; - else - ret |= mask << 8; - - return ret; +/*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a * v0.y - b * v0.z; \ + p2 = a * v2.y - b * v2.z; \ + if (p0 < p2) { \ + min = p0; \ + max = p2; \ + } else { \ + min = p2; \ + max = p0; \ + } \ + rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ + if (min > rad || max < -rad) { \ + return false; \ + } + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a * v0.y - b * v0.z; \ + p1 = a * v1.y - b * v1.z; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ + if (min > rad || max < -rad) { \ + return false; \ + } + +/*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a * v0.x + b * v0.z; \ + p2 = -a * v2.x + b * v2.z; \ + if (p0 < p2) { \ + min = p0; \ + max = p2; \ + } else { \ + min = p2; \ + max = p0; \ + } \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ + if (min > rad || max < -rad) { \ + return false; \ + } + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a * v0.x + b * v0.z; \ + p1 = -a * v1.x + b * v1.z; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ + if (min > rad || max < -rad) { \ + return false; \ + } + + /*======================== Z-tests ========================*/ + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a * v1.x - b * v1.y; \ + p2 = a * v2.x - b * v2.y; \ + if (p2 < p1) { \ + min = p2; \ + max = p1; \ + } else { \ + min = p1; \ + max = p2; \ + } \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \ + if (min > rad || max < -rad) { \ + return false; \ + } + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a * v0.x - b * v0.y; \ + p1 = a * v1.x - b * v1.y; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \ + if (min > rad || max < -rad) { \ + return false; \ + } + + _FORCE_INLINE_ static bool triangle_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) { + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + Vector3 v0, v1, v2; + float min, max, d, p0, p1, p2, rad, fex, fey, fez; + Vector3 normal, e0, e1, e2; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + + v0 = triverts[0] - boxcenter; + v1 = triverts[1] - boxcenter; + v2 = triverts[2] - boxcenter; + + /* compute triangle edges */ + e0 = v1 - v0; /* tri edge 0 */ + e1 = v2 - v1; /* tri edge 1 */ + e2 = v0 - v2; /* tri edge 2 */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = Math::abs(e0.x); + fey = Math::abs(e0.y); + fez = Math::abs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = Math::abs(e1.x); + fey = Math::abs(e1.y); + fez = Math::abs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0(e1.y, e1.x, fey, fex); + + fex = Math::abs(e2.x); + fey = Math::abs(e2.y); + fez = Math::abs(e2.z); + AXISTEST_X2(e2.z, e2.y, fez, fey); + AXISTEST_Y1(e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in X-direction */ + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if (min > boxhalfsize.x || max < -boxhalfsize.x) { + return false; } - } - - 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]; + /* test in Y-direction */ + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if (min > boxhalfsize.y || max < -boxhalfsize.y) { + return false; } - // 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]; + /* test in Z-direction */ + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if (min > boxhalfsize.z || max < -boxhalfsize.z) { + return false; } - H.resize(k); - return H; + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + normal = e0.cross(e1); + d = -normal.dot(v0); /* plane eq: normal.x+d=0 */ + return planeBoxOverlap(normal, d, boxhalfsize); /* if true, box and triangle overlaps */ + } + + 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); + + static Vector3 triangle_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_pos) { + Vector3 v0 = p_b - p_a; + Vector3 v1 = p_c - p_a; + Vector3 v2 = p_pos - p_a; + + float d00 = v0.dot(v0); + float d01 = v0.dot(v1); + float d11 = v1.dot(v1); + float d20 = v2.dot(v0); + float d21 = v2.dot(v1); + float denom = (d00 * d11 - d01 * d01); + if (denom == 0) { + return Vector3(); //invalid triangle, return empty + } + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + float u = 1.0f - v - w; + return Vector3(u, v, w); + } + + static Color tetrahedron_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_d, const Vector3 &p_pos) { + Vector3 vap = p_pos - p_a; + Vector3 vbp = p_pos - p_b; + + Vector3 vab = p_b - p_a; + Vector3 vac = p_c - p_a; + Vector3 vad = p_d - p_a; + + Vector3 vbc = p_c - p_b; + Vector3 vbd = p_d - p_b; + // ScTP computes the scalar triple product +#define STP(m_a, m_b, m_c) ((m_a).dot((m_b).cross((m_c)))) + float va6 = STP(vbp, vbd, vbc); + float vb6 = STP(vap, vac, vad); + float vc6 = STP(vap, vad, vab); + float vd6 = STP(vap, vab, vac); + float v6 = 1 / STP(vab, vac, vad); + return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6); +#undef STP + } + + _FORCE_INLINE_ static Vector3 octahedron_map_decode(const Vector2 &p_uv) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + Vector2 f = p_uv * 2.0 - Vector2(1.0, 1.0); + Vector3 n = Vector3(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y)); + float t = CLAMP(-n.z, 0.0, 1.0); + n.x += n.x >= 0 ? -t : t; + n.y += n.y >= 0 ? -t : t; + return n.normalized(); } - 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); - -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/math_defs.h b/core/math/math_defs.h index 4928c96abd..df2223fb78 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -66,35 +66,31 @@ enum ClockDirection { }; enum Orientation { - HORIZONTAL, VERTICAL }; enum HAlign { - HALIGN_LEFT, HALIGN_CENTER, - HALIGN_RIGHT + HALIGN_RIGHT, + HALIGN_FILL, }; enum VAlign { - VALIGN_TOP, VALIGN_CENTER, VALIGN_BOTTOM }; -enum Margin { - - MARGIN_LEFT, - MARGIN_TOP, - MARGIN_RIGHT, - MARGIN_BOTTOM +enum Side { + SIDE_LEFT, + SIDE_TOP, + SIDE_RIGHT, + SIDE_BOTTOM }; enum Corner { - CORNER_TOP_LEFT, CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, diff --git a/core/math/math_fieldwise.cpp b/core/math/math_fieldwise.cpp index a47d4ef7ad..0985a727f2 100644 --- a/core/math/math_fieldwise.cpp +++ b/core/math/math_fieldwise.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -41,16 +41,13 @@ } Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const String &p_field) { - ERR_FAIL_COND_V(p_target.get_type() != p_source.get_type(), p_target); /* clang-format makes a mess of this macro usage */ /* clang-format off */ switch (p_source.get_type()) { - case Variant::VECTOR2: { - SETUP_TYPE(Vector2) /**/ TRY_TRANSFER_FIELD("x", x) @@ -60,7 +57,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const } case Variant::RECT2: { - SETUP_TYPE(Rect2) /**/ TRY_TRANSFER_FIELD("x", position.x) @@ -72,7 +68,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const } case Variant::VECTOR3: { - SETUP_TYPE(Vector3) /**/ TRY_TRANSFER_FIELD("x", x) @@ -83,7 +78,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const } case Variant::PLANE: { - SETUP_TYPE(Plane) /**/ TRY_TRANSFER_FIELD("x", normal.x) @@ -95,7 +89,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const } case Variant::QUAT: { - SETUP_TYPE(Quat) /**/ TRY_TRANSFER_FIELD("x", x) @@ -107,7 +100,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const } case Variant::AABB: { - SETUP_TYPE(AABB) /**/ TRY_TRANSFER_FIELD("px", position.x) @@ -121,7 +113,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const } case Variant::TRANSFORM2D: { - SETUP_TYPE(Transform2D) /**/ TRY_TRANSFER_FIELD("xx", elements[0][0]) @@ -135,7 +126,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const } case Variant::BASIS: { - SETUP_TYPE(Basis) /**/ TRY_TRANSFER_FIELD("xx", elements[0][0]) @@ -152,7 +142,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const } case Variant::TRANSFORM: { - SETUP_TYPE(Transform) /**/ TRY_TRANSFER_FIELD("xx", basis.elements[0][0]) diff --git a/core/math/math_fieldwise.h b/core/math/math_fieldwise.h index c1ee9ec8f0..fe44d09900 100644 --- a/core/math/math_fieldwise.h +++ b/core/math/math_fieldwise.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,7 +33,7 @@ #ifdef TOOLS_ENABLED -#include "core/variant.h" +#include "core/variant/variant.h" Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const String &p_field); diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index 7417e64ac1..e92bb9f4aa 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,12 +30,10 @@ #include "math_funcs.h" -#include "core/error_macros.h" +#include "core/error/error_macros.h" RandomPCG Math::default_rand(RandomPCG::DEFAULT_SEED, RandomPCG::DEFAULT_INC); -#define PHI 0x9e3779b9 - uint32_t Math::rand_from_seed(uint64_t *seed) { RandomPCG rng = RandomPCG(*seed, RandomPCG::DEFAULT_INC); uint32_t r = rng.rand(); @@ -94,16 +92,18 @@ double Math::dectime(double p_value, double p_amount, double p_step) { double sgn = p_value < 0 ? -1.0 : 1.0; double val = Math::abs(p_value); val -= p_amount * p_step; - if (val < 0.0) + if (val < 0.0) { val = 0.0; + } return val * sgn; } double Math::ease(double p_x, double p_c) { - if (p_x < 0) + if (p_x < 0) { p_x = 0; - else if (p_x > 1.0) + } else if (p_x > 1.0) { p_x = 1.0; + } if (p_c > 0) { if (p_c < 1.0) { return 1.0 - Math::pow(1.0 - p_x, 1.0 / p_c); @@ -118,11 +118,12 @@ double Math::ease(double p_x, double p_c) { } else { return (1.0 - Math::pow(1.0 - (p_x - 0.5) * 2.0, -p_c)) * 0.5 + 0.5; } - } else + } else { return 0; // no ease (raw) + } } -double Math::stepify(double p_value, double p_step) { +double Math::snapped(double p_value, double p_step) { if (p_step != 0) { p_value = Math::floor(p_value / p_step + 0.5) * p_step; } @@ -130,7 +131,6 @@ double Math::stepify(double p_value, double p_step) { } uint32_t Math::larger_prime(uint32_t p_val) { - static const uint32_t primes[] = { 5, 13, @@ -166,10 +166,10 @@ uint32_t Math::larger_prime(uint32_t p_val) { int idx = 0; while (true) { - ERR_FAIL_COND_V(primes[idx] == 0, 0); - if (primes[idx] > p_val) + if (primes[idx] > p_val) { return primes[idx]; + } idx++; } } @@ -181,3 +181,7 @@ double Math::random(double from, double to) { float Math::random(float from, float to) { return default_rand.random(from, to); } + +int Math::random(int from, int to) { + return default_rand.random(from, to); +} diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 3e1eb14a6a..c7d24e9c58 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -41,13 +41,13 @@ #include <math.h> class Math { - static RandomPCG default_rand; public: Math() {} // useless to instance - static const uint64_t RANDOM_MAX = 0xFFFFFFFF; + // Not using 'RANDOM_MAX' to avoid conflict with system headers on some OSes (at least NetBSD). + static const uint64_t RANDOM_32BIT_MAX = 0xFFFFFFFF; static _ALWAYS_INLINE_ double sin(double p_x) { return ::sin(p_x); } static _ALWAYS_INLINE_ float sin(float p_x) { return ::sinf(p_x); } @@ -198,6 +198,23 @@ public: value += 0.0; return value; } + static _ALWAYS_INLINE_ float fposmodp(float p_x, float p_y) { + float value = Math::fmod(p_x, p_y); + if (value < 0) { + value += p_y; + } + value += 0.0; + return value; + } + static _ALWAYS_INLINE_ double fposmodp(double p_x, double p_y) { + double value = Math::fmod(p_x, p_y); + if (value < 0) { + value += p_y; + } + value += 0.0; + return value; + } + static _ALWAYS_INLINE_ int posmod(int p_x, int p_y) { int value = p_x % p_y; if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) { @@ -232,15 +249,19 @@ public: static _ALWAYS_INLINE_ double range_lerp(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } static _ALWAYS_INLINE_ float range_lerp(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } - static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_weight) { - if (is_equal_approx(p_from, p_to)) return p_from; - double x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0, 1.0); - return x * x * (3.0 - 2.0 * x); + static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) { + if (is_equal_approx(p_from, p_to)) { + return p_from; + } + double s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0, 1.0); + return s * s * (3.0 - 2.0 * s); } - static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_weight) { - if (is_equal_approx(p_from, p_to)) return p_from; - float x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0f, 1.0f); - return x * x * (3.0f - 2.0f * x); + static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_s) { + if (is_equal_approx(p_from, p_to)) { + return p_from; + } + float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f); + return s * s * (3.0f - 2.0f * s); } static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; } static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; } @@ -271,7 +292,7 @@ public: static double ease(double p_x, double p_c); static int step_decimals(double p_step); static int range_step_decimals(double p_step); - static double stepify(double p_value, double p_step); + static double snapped(double p_value, double p_step); static double dectime(double p_value, double p_amount, double p_step); static uint32_t larger_prime(uint32_t p_val); @@ -280,24 +301,12 @@ public: static void randomize(); static uint32_t rand_from_seed(uint64_t *seed); static uint32_t rand(); - static _ALWAYS_INLINE_ double randd() { return (double)rand() / (double)Math::RANDOM_MAX; } - static _ALWAYS_INLINE_ float randf() { return (float)rand() / (float)Math::RANDOM_MAX; } + static _ALWAYS_INLINE_ double randd() { return (double)rand() / (double)Math::RANDOM_32BIT_MAX; } + static _ALWAYS_INLINE_ float randf() { return (float)rand() / (float)Math::RANDOM_32BIT_MAX; } static double random(double from, double to); static float random(float from, float to); - static real_t random(int from, int to) { return (real_t)random((real_t)from, (real_t)to); } - - static _ALWAYS_INLINE_ bool is_equal_approx_ratio(real_t a, real_t b, real_t epsilon = CMP_EPSILON, real_t min_epsilon = CMP_EPSILON) { - // this is an approximate way to check that numbers are close, as a ratio of their average size - // helps compare approximate numbers that may be very big or very small - real_t diff = abs(a - b); - if (diff == 0.0 || diff < min_epsilon) { - return true; - } - real_t avg_size = (abs(a) + abs(b)) / 2.0; - diff /= avg_size; - return diff < epsilon; - } + static int random(int from, int to); static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) { // Check for exact equality first, required to handle "infinity" values. @@ -326,7 +335,6 @@ public: } static _ALWAYS_INLINE_ float absf(float g) { - union { float f; uint32_t i; @@ -338,7 +346,6 @@ public: } static _ALWAYS_INLINE_ double absd(double g) { - union { double d; uint64_t i; @@ -350,7 +357,6 @@ public: //this function should be as fast as possible and rounding mode should not matter static _ALWAYS_INLINE_ int fast_ftoi(float a) { - static int b; #if (defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0603) || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP // windows 8 phone? @@ -405,7 +411,6 @@ public: } static _ALWAYS_INLINE_ float halfptr_to_float(const uint16_t *h) { - union { uint32_t u32; float f32; @@ -420,7 +425,6 @@ public: } static _ALWAYS_INLINE_ uint16_t make_half_float(float f) { - union { float fv; uint32_t ui; @@ -451,7 +455,6 @@ public: } // check if exponent is <= -15 else if (exp <= 0x38000000) { - /*// store a denorm half-float value or zero exp = (0x38000000 - exp) >> 23; mantissa >>= (14 + exp); @@ -469,17 +472,18 @@ public: } static _ALWAYS_INLINE_ float snap_scalar(float p_offset, float p_step, float p_target) { - return p_step != 0 ? Math::stepify(p_target - p_offset, p_step) + p_offset : p_target; + return p_step != 0 ? Math::snapped(p_target - p_offset, p_step) + p_offset : p_target; } static _ALWAYS_INLINE_ float snap_scalar_separation(float p_offset, float p_step, float p_target, float p_separation) { if (p_step != 0) { - float a = Math::stepify(p_target - p_offset, p_step + p_separation) + p_offset; + float a = Math::snapped(p_target - p_offset, p_step + p_separation) + p_offset; float b = a; - if (p_target >= 0) + if (p_target >= 0) { b -= p_separation; - else + } else { b += p_step; + } return (Math::abs(p_target - a) < Math::abs(p_target - b)) ? a : b; } return p_target; diff --git a/core/math/octree.h b/core/math/octree.h index ffb405bd0f..493a63aa2e 100644 --- a/core/math/octree.h +++ b/core/math/octree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,13 +31,13 @@ #ifndef OCTREE_H #define OCTREE_H -#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" +#include "core/string/print_string.h" +#include "core/templates/list.h" +#include "core/templates/map.h" +#include "core/variant/variant.h" typedef uint32_t OctreeElementID; @@ -52,7 +52,6 @@ public: private: enum { - NEG = 0, POS = 1, }; @@ -69,7 +68,6 @@ private: }; struct PairKey { - union { struct { OctreeElementID A; @@ -79,18 +77,14 @@ private: }; _FORCE_INLINE_ bool operator<(const PairKey &p_pair) const { - return key < p_pair.key; } _FORCE_INLINE_ PairKey(OctreeElementID p_A, OctreeElementID p_B) { - if (p_A < p_B) { - A = p_A; B = p_B; } else { - B = p_A; A = p_B; } @@ -102,53 +96,37 @@ private: struct Element; struct Octant { - // cached for FAST plane check AABB aabb; - uint64_t last_pass; - Octant *parent; - Octant *children[8]; + uint64_t last_pass = 0; + Octant *parent = nullptr; + Octant *children[8] = { nullptr }; - int children_count; // cache for amount of childrens (fast check for removal) - int parent_index; // cache for parent index (fast check for removal) + int children_count = 0; // cache for amount of childrens (fast check for removal) + int parent_index = -1; // cache for parent index (fast check for removal) List<Element *, AL> pairable_elements; List<Element *, AL> elements; - Octant() { - children_count = 0; - parent_index = -1; - last_pass = 0; - parent = nullptr; - for (int i = 0; i < 8; i++) - children[i] = nullptr; - } - - ~Octant() { - - /* - for (int i=0;i<8;i++) - memdelete_notnull(children[i]); - */ - } + Octant() {} + ~Octant() {} }; struct PairData; struct Element { + Octree *octree = nullptr; - Octree *octree; - - T *userdata; - int subindex; - bool pairable; - uint32_t pairable_mask; - uint32_t pairable_type; + T *userdata = nullptr; + int subindex = 0; + bool pairable = false; + uint32_t pairable_mask = 0; + uint32_t pairable_type = 0; - uint64_t last_pass; - OctreeElementID _id; - Octant *common_parent; + uint64_t last_pass = 0; + OctreeElementID _id = 0; + Octant *common_parent = nullptr; AABB aabb; AABB container_aabb; @@ -156,28 +134,16 @@ private: List<PairData *, AL> pair_list; struct OctantOwner { - Octant *octant; typename List<Element *, AL>::Element *E; }; // an element can be in max 8 octants List<OctantOwner, AL> octant_owners; - Element() { - last_pass = 0; - _id = 0; - pairable = false; - subindex = 0; - userdata = 0; - octree = 0; - pairable_mask = 0; - pairable_type = 0; - common_parent = nullptr; - } + Element() {} }; struct PairData { - int refcount; bool intersect; Element *A, *B; @@ -204,19 +170,15 @@ private: int pair_count; _FORCE_INLINE_ void _pair_check(PairData *p_pair) { - bool intersect = p_pair->A->aabb.intersects_inclusive(p_pair->B->aabb); if (intersect != p_pair->intersect) { - if (intersect) { - if (pair_callback) { p_pair->ud = pair_callback(pair_callback_userdata, p_pair->A->_id, p_pair->A->userdata, p_pair->A->subindex, p_pair->B->_id, p_pair->B->userdata, p_pair->B->subindex); } pair_count++; } else { - if (unpair_callback) { unpair_callback(pair_callback_userdata, p_pair->A->_id, p_pair->A->userdata, p_pair->A->subindex, p_pair->B->_id, p_pair->B->userdata, p_pair->B->subindex, p_pair->ud); } @@ -228,19 +190,19 @@ private: } _FORCE_INLINE_ void _pair_reference(Element *p_A, Element *p_B) { - - if (p_A == p_B || (p_A->userdata == p_B->userdata && p_A->userdata)) + if (p_A == p_B || (p_A->userdata == p_B->userdata && p_A->userdata)) { return; + } if (!(p_A->pairable_type & p_B->pairable_mask) && - !(p_B->pairable_type & p_A->pairable_mask)) + !(p_B->pairable_type & p_A->pairable_mask)) { return; // none can pair with none + } PairKey key(p_A->_id, p_B->_id); typename PairMap::Element *E = pair_map.find(key); if (!E) { - PairData pdata; pdata.refcount = 1; pdata.A = p_A; @@ -255,15 +217,14 @@ private: pair_callback(pair_callback_userdata,p_A->userdata,p_B->userdata); */ } else { - E->get().refcount++; } } _FORCE_INLINE_ void _pair_unreference(Element *p_A, Element *p_B) { - - if (p_A == p_B) + if (p_A == p_B) { return; + } PairKey key(p_A->_id, p_B->_id); typename PairMap::Element *E = pair_map.find(key); @@ -296,24 +257,18 @@ private: } _FORCE_INLINE_ void _element_check_pairs(Element *p_element) { - typename List<PairData *, AL>::Element *E = p_element->pair_list.front(); while (E) { - _pair_check(E->get()); E = E->next(); } } _FORCE_INLINE_ void _optimize() { - while (root && root->children_count < 2 && !root->elements.size() && !(use_pairs && root->pairable_elements.size())) { - Octant *new_root = nullptr; if (root->children_count == 1) { - for (int i = 0; i < 8; i++) { - if (root->children[i]) { new_root = root->children[i]; root->children[i] = nullptr; @@ -339,7 +294,6 @@ private: void _unpair_element(Element *p_element, Octant *p_octant); struct _CullConvexData { - const Plane *planes; int plane_count; const Vector3 *points; @@ -356,14 +310,14 @@ private: void _cull_point(Octant *p_octant, const Vector3 &p_point, T **p_result_array, int *p_result_idx, int p_result_max, int *p_subindex_array, uint32_t p_mask); void _remove_tree(Octant *p_octant) { - - if (!p_octant) + if (!p_octant) { return; + } for (int i = 0; i < 8; i++) { - - if (p_octant->children[i]) + if (p_octant->children[i]) { _remove_tree(p_octant->children[i]); + } } memdelete_allocator<Octant, AL>(p_octant); @@ -405,7 +359,6 @@ T *Octree<T, use_pairs, AL>::get(OctreeElementID p_id) const { template <class T, bool use_pairs, class AL> bool Octree<T, use_pairs, AL>::is_pairable(OctreeElementID p_id) const { - const typename ElementMap::Element *E = element_map.find(p_id); ERR_FAIL_COND_V(!E, false); return E->get().pairable; @@ -413,7 +366,6 @@ bool Octree<T, use_pairs, AL>::is_pairable(OctreeElementID p_id) const { template <class T, bool use_pairs, class AL> int Octree<T, use_pairs, AL>::get_subindex(OctreeElementID p_id) const { - const typename ElementMap::Element *E = element_map.find(p_id); ERR_FAIL_COND_V(!E, -1); return E->get().subindex; @@ -423,22 +375,18 @@ int Octree<T, use_pairs, AL>::get_subindex(OctreeElementID p_id) const { template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_insert_element(Element *p_element, Octant *p_octant) { - real_t element_size = p_element->aabb.get_longest_axis_size() * 1.01; // avoid precision issues if (p_octant->aabb.size.x / OCTREE_DIVISOR < element_size) { //if (p_octant->aabb.size.x*0.5 < element_size) { - /* at smallest possible size for the element */ typename Element::OctantOwner owner; owner.octant = p_octant; if (use_pairs && p_element->pairable) { - p_octant->pairable_elements.push_back(p_element); owner.E = p_octant->pairable_elements.back(); } else { - p_octant->elements.push_back(p_element); owner.E = p_octant->elements.back(); } @@ -453,11 +401,9 @@ void Octree<T, use_pairs, AL>::_insert_element(Element *p_element, Octant *p_oct } if (use_pairs && p_octant->children_count > 0) { - pass++; //elements below this only get ONE reference added for (int i = 0; i < 8; i++) { - if (p_octant->children[i]) { _pair_element(p_element, p_octant->children[i]); } @@ -469,7 +415,6 @@ void Octree<T, use_pairs, AL>::_insert_element(Element *p_element, Octant *p_oct bool candidate = p_element->common_parent == nullptr; for (int i = 0; i < 8; i++) { - if (p_octant->children[i]) { /* element exists, go straight to it */ if (p_octant->children[i]->aabb.intersects_inclusive(p_element->aabb)) { @@ -482,12 +427,15 @@ void Octree<T, use_pairs, AL>::_insert_element(Element *p_element, Octant *p_oct AABB aabb = p_octant->aabb; aabb.size *= 0.5; - if (i & 1) + if (i & 1) { aabb.position.x += aabb.size.x; - if (i & 2) + } + if (i & 2) { aabb.position.y += aabb.size.y; - if (i & 4) + } + if (i & 4) { aabb.position.z += aabb.size.z; + } if (aabb.intersects_inclusive(p_element->aabb)) { /* if actually intersects, create the child */ @@ -509,13 +457,11 @@ void Octree<T, use_pairs, AL>::_insert_element(Element *p_element, Octant *p_oct } if (candidate && splits > 1) { - p_element->common_parent = p_octant; } } if (use_pairs) { - typename List<Element *, AL>::Element *E = p_octant->pairable_elements.front(); while (E) { @@ -536,14 +482,12 @@ void Octree<T, use_pairs, AL>::_insert_element(Element *p_element, Octant *p_oct template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_ensure_valid_root(const AABB &p_aabb) { - if (!root) { // octre is empty AABB base(Vector3(), Vector3(1.0, 1.0, 1.0) * unit_size); while (!base.encloses(p_aabb)) { - if (ABS(base.position.x + base.size.x) <= ABS(base.position.x)) { /* grow towards positive */ base.size *= 2.0; @@ -562,11 +506,9 @@ void Octree<T, use_pairs, AL>::_ensure_valid_root(const AABB &p_aabb) { octant_count++; } else { - AABB base = root->aabb; while (!base.encloses(p_aabb)) { - ERR_FAIL_COND_MSG(base.size.x > OCTREE_SIZE_LIMIT, "Octree upper size limit reached, does the AABB supplied contain NAN?"); Octant *gp = memnew_allocator(Octant, AL); @@ -595,15 +537,14 @@ void Octree<T, use_pairs, AL>::_ensure_valid_root(const AABB &p_aabb) { template <class T, bool use_pairs, class AL> bool Octree<T, use_pairs, AL>::_remove_element_from_octant(Element *p_element, Octant *p_octant, Octant *p_limit) { - bool octant_removed = false; while (true) { - // check all exit conditions - if (p_octant == p_limit) // reached limit, nothing to erase, exit + if (p_octant == p_limit) { // reached limit, nothing to erase, exit return octant_removed; + } bool unpaired = false; @@ -631,8 +572,7 @@ bool Octree<T, use_pairs, AL>::_remove_element_from_octant(Element *p_element, O Octant *parent = p_octant->parent; - if (p_octant->children_count == 0 && p_octant->elements.empty() && p_octant->pairable_elements.empty()) { - + if (p_octant->children_count == 0 && p_octant->elements.is_empty() && p_octant->pairable_elements.is_empty()) { // erase octant if (p_octant == root) { // won't have a parent, just erase @@ -651,8 +591,9 @@ bool Octree<T, use_pairs, AL>::_remove_element_from_octant(Element *p_element, O octant_removed = true; } - if (!removed && !unpaired) + if (!removed && !unpaired) { return octant_removed; // no reason to keep going up anymore! was already visited and was not removed + } p_octant = parent; } @@ -662,7 +603,6 @@ bool Octree<T, use_pairs, AL>::_remove_element_from_octant(Element *p_element, O template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_unpair_element(Element *p_element, Octant *p_octant) { - // always test pairable typename List<Element *, AL>::Element *E = p_octant->pairable_elements.front(); while (E) { @@ -687,25 +627,24 @@ void Octree<T, use_pairs, AL>::_unpair_element(Element *p_element, Octant *p_oct p_octant->last_pass = pass; - if (p_octant->children_count == 0) + if (p_octant->children_count == 0) { return; // small optimization for leafs + } for (int i = 0; i < 8; i++) { - - if (p_octant->children[i]) + if (p_octant->children[i]) { _unpair_element(p_element, p_octant->children[i]); + } } } template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_pair_element(Element *p_element, Octant *p_octant) { - // always test pairable typename List<Element *, AL>::Element *E = p_octant->pairable_elements.front(); while (E) { - if (E->get()->last_pass != pass) { // only get ONE reference _pair_reference(p_element, E->get()); E->get()->last_pass = pass; @@ -726,30 +665,30 @@ void Octree<T, use_pairs, AL>::_pair_element(Element *p_element, Octant *p_octan } p_octant->last_pass = pass; - if (p_octant->children_count == 0) + if (p_octant->children_count == 0) { return; // small optimization for leafs + } for (int i = 0; i < 8; i++) { - - if (p_octant->children[i]) + if (p_octant->children[i]) { _pair_element(p_element, p_octant->children[i]); + } } } template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_remove_element(Element *p_element) { - pass++; // will do a new pass for this typename List<typename Element::OctantOwner, AL>::Element *I = p_element->octant_owners.front(); /* FIRST remove going up normally */ for (; I; I = I->next()) { - Octant *o = I->get().octant; - if (!use_pairs) // small speedup + if (!use_pairs) { // small speedup o->elements.erase(I->get().E); + } _remove_element_from_octant(p_element, o); } @@ -759,30 +698,28 @@ void Octree<T, use_pairs, AL>::_remove_element(Element *p_element) { I = p_element->octant_owners.front(); if (use_pairs) { - for (; I; I = I->next()) { - Octant *o = I->get().octant; // erase children pairs, they are erased ONCE even if repeated pass++; for (int i = 0; i < 8; i++) { - - if (o->children[i]) + if (o->children[i]) { _unpair_element(p_element, o->children[i]); + } } - if (p_element->pairable) + if (p_element->pairable) { o->pairable_elements.erase(I->get().E); - else + } else { o->elements.erase(I->get().E); + } } } p_element->octant_owners.clear(); if (use_pairs) { - int remaining = p_element->pair_list.size(); //p_element->pair_list.clear(); ERR_FAIL_COND(remaining); @@ -791,7 +728,6 @@ void Octree<T, use_pairs, AL>::_remove_element(Element *p_element) { template <class T, bool use_pairs, class AL> OctreeElementID Octree<T, use_pairs, AL>::create(T *p_userdata, const AABB &p_aabb, int p_subindex, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask) { - // check for AABB validity #ifdef DEBUG_ENABLED ERR_FAIL_COND_V(p_aabb.position.x > 1e15 || p_aabb.position.x < -1e15, 0); @@ -822,8 +758,9 @@ OctreeElementID Octree<T, use_pairs, AL>::create(T *p_userdata, const AABB &p_aa if (!e.aabb.has_no_surface()) { _ensure_valid_root(p_aabb); _insert_element(&e, root); - if (use_pairs) + if (use_pairs) { _element_check_pairs(&e); + } } return last_element_id - 1; @@ -831,7 +768,6 @@ OctreeElementID Octree<T, use_pairs, AL>::create(T *p_userdata, const AABB &p_aa template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) { - #ifdef DEBUG_ENABLED // check for AABB validity ERR_FAIL_COND(p_aabb.position.x > 1e15 || p_aabb.position.x < -1e15); @@ -852,7 +788,6 @@ void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) { bool new_has_surf = !p_aabb.has_no_surface(); if (old_has_surf != new_has_surf) { - if (old_has_surf) { _remove_element(&e); // removing e.common_parent = nullptr; @@ -863,22 +798,24 @@ void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) { e.common_parent = nullptr; e.aabb = p_aabb; _insert_element(&e, root); - if (use_pairs) + if (use_pairs) { _element_check_pairs(&e); + } } return; } - if (!old_has_surf) // doing nothing + if (!old_has_surf) { // doing nothing return; + } // it still is enclosed in the same AABB it was assigned to if (e.container_aabb.encloses(p_aabb)) { - e.aabb = p_aabb; - if (use_pairs) + if (use_pairs) { _element_check_pairs(&e); // must check pairs anyway + } return; } @@ -898,8 +835,9 @@ void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) { //src is now the place towards where insertion is going to happen pass++; - while (common_parent && !common_parent->aabb.encloses(p_aabb)) + while (common_parent && !common_parent->aabb.encloses(p_aabb)) { common_parent = common_parent->parent; + } ERR_FAIL_COND(!common_parent); @@ -913,7 +851,6 @@ void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) { pass++; for (typename List<typename Element::OctantOwner, AL>::Element *F = owners.front(); F;) { - Octant *o = F->get().octant; typename List<typename Element::OctantOwner, AL>::Element *N = F->next(); @@ -922,13 +859,13 @@ void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) { o->elements.erase( F->get().E ); */ - if (use_pairs && e.pairable) + if (use_pairs && e.pairable) { o->pairable_elements.erase(F->get().E); - else + } else { o->elements.erase(F->get().E); + } if (_remove_element_from_octant(&e, o, common_parent->parent)) { - owners.erase(F); } @@ -938,15 +875,14 @@ void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) { if (use_pairs) { //unpair child elements in anything that survived for (typename List<typename Element::OctantOwner, AL>::Element *F = owners.front(); F; F = F->next()) { - Octant *o = F->get().octant; // erase children pairs, unref ONCE pass++; for (int i = 0; i < 8; i++) { - - if (o->children[i]) + if (o->children[i]) { _unpair_element(&e, o->children[i]); + } } } @@ -958,14 +894,14 @@ void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) { template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::set_pairable(OctreeElementID p_id, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask) { - typename ElementMap::Element *E = element_map.find(p_id); ERR_FAIL_COND(!E); Element &e = E->get(); - if (p_pairable == e.pairable && e.pairable_type == p_pairable_type && e.pairable_mask == p_pairable_mask) + if (p_pairable == e.pairable && e.pairable_type == p_pairable_type && e.pairable_mask == p_pairable_mask) { return; // no changes, return + } if (!e.aabb.has_no_surface()) { _remove_element(&e); @@ -979,21 +915,20 @@ void Octree<T, use_pairs, AL>::set_pairable(OctreeElementID p_id, bool p_pairabl if (!e.aabb.has_no_surface()) { _ensure_valid_root(e.aabb); _insert_element(&e, root); - if (use_pairs) + if (use_pairs) { _element_check_pairs(&e); + } } } template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::erase(OctreeElementID p_id) { - typename ElementMap::Element *E = element_map.find(p_id); ERR_FAIL_COND(!E); Element &e = E->get(); if (!e.aabb.has_no_surface()) { - _remove_element(&e); } @@ -1003,21 +938,20 @@ void Octree<T, use_pairs, AL>::erase(OctreeElementID p_id) { template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_cull_convex(Octant *p_octant, _CullConvexData *p_cull) { - - if (*p_cull->result_idx == p_cull->result_max) + if (*p_cull->result_idx == p_cull->result_max) { return; //pointless + } - if (!p_octant->elements.empty()) { - + if (!p_octant->elements.is_empty()) { typename List<Element *, AL>::Element *I; I = p_octant->elements.front(); for (; I; I = I->next()) { - Element *e = I->get(); - if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_cull->mask))) + if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_cull->mask))) { continue; + } e->last_pass = pass; if (e->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count, p_cull->points, p_cull->point_count)) { @@ -1025,34 +959,29 @@ void Octree<T, use_pairs, AL>::_cull_convex(Octant *p_octant, _CullConvexData *p p_cull->result_array[*p_cull->result_idx] = e->userdata; (*p_cull->result_idx)++; } else { - return; // pointless to continue } } } } - if (use_pairs && !p_octant->pairable_elements.empty()) { - + if (use_pairs && !p_octant->pairable_elements.is_empty()) { typename List<Element *, AL>::Element *I; I = p_octant->pairable_elements.front(); for (; I; I = I->next()) { - Element *e = I->get(); - if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_cull->mask))) + if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_cull->mask))) { continue; + } e->last_pass = pass; if (e->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count, p_cull->points, p_cull->point_count)) { - if (*p_cull->result_idx < p_cull->result_max) { - p_cull->result_array[*p_cull->result_idx] = e->userdata; (*p_cull->result_idx)++; } else { - return; // pointless to continue } } @@ -1060,7 +989,6 @@ void Octree<T, use_pairs, AL>::_cull_convex(Octant *p_octant, _CullConvexData *p } for (int i = 0; i < 8; i++) { - if (p_octant->children[i] && p_octant->children[i]->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count, p_cull->points, p_cull->point_count)) { _cull_convex(p_octant->children[i], p_cull); } @@ -1069,61 +997,55 @@ void Octree<T, use_pairs, AL>::_cull_convex(Octant *p_octant, _CullConvexData *p template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_cull_aabb(Octant *p_octant, const AABB &p_aabb, T **p_result_array, int *p_result_idx, int p_result_max, int *p_subindex_array, uint32_t p_mask) { - - if (*p_result_idx == p_result_max) + if (*p_result_idx == p_result_max) { return; //pointless + } - if (!p_octant->elements.empty()) { - + if (!p_octant->elements.is_empty()) { typename List<Element *, AL>::Element *I; I = p_octant->elements.front(); for (; I; I = I->next()) { - Element *e = I->get(); - if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) + if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) { continue; + } e->last_pass = pass; if (p_aabb.intersects_inclusive(e->aabb)) { - if (*p_result_idx < p_result_max) { - p_result_array[*p_result_idx] = e->userdata; - if (p_subindex_array) + if (p_subindex_array) { p_subindex_array[*p_result_idx] = e->subindex; + } (*p_result_idx)++; } else { - return; // pointless to continue } } } } - if (use_pairs && !p_octant->pairable_elements.empty()) { - + if (use_pairs && !p_octant->pairable_elements.is_empty()) { typename List<Element *, AL>::Element *I; I = p_octant->pairable_elements.front(); for (; I; I = I->next()) { - Element *e = I->get(); - if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) + if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) { continue; + } e->last_pass = pass; if (p_aabb.intersects_inclusive(e->aabb)) { - if (*p_result_idx < p_result_max) { - p_result_array[*p_result_idx] = e->userdata; - if (p_subindex_array) + if (p_subindex_array) { p_subindex_array[*p_result_idx] = e->subindex; + } (*p_result_idx)++; } else { - return; // pointless to continue } } @@ -1131,7 +1053,6 @@ void Octree<T, use_pairs, AL>::_cull_aabb(Octant *p_octant, const AABB &p_aabb, } for (int i = 0; i < 8; i++) { - if (p_octant->children[i] && p_octant->children[i]->aabb.intersects_inclusive(p_aabb)) { _cull_aabb(p_octant->children[i], p_aabb, p_result_array, p_result_idx, p_result_max, p_subindex_array, p_mask); } @@ -1140,64 +1061,58 @@ void Octree<T, use_pairs, AL>::_cull_aabb(Octant *p_octant, const AABB &p_aabb, template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_cull_segment(Octant *p_octant, const Vector3 &p_from, const Vector3 &p_to, T **p_result_array, int *p_result_idx, int p_result_max, int *p_subindex_array, uint32_t p_mask) { - - if (*p_result_idx == p_result_max) + if (*p_result_idx == p_result_max) { return; //pointless + } - if (!p_octant->elements.empty()) { - + if (!p_octant->elements.is_empty()) { typename List<Element *, AL>::Element *I; I = p_octant->elements.front(); for (; I; I = I->next()) { - Element *e = I->get(); - if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) + if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) { continue; + } e->last_pass = pass; if (e->aabb.intersects_segment(p_from, p_to)) { - if (*p_result_idx < p_result_max) { - p_result_array[*p_result_idx] = e->userdata; - if (p_subindex_array) + if (p_subindex_array) { p_subindex_array[*p_result_idx] = e->subindex; + } (*p_result_idx)++; } else { - return; // pointless to continue } } } } - if (use_pairs && !p_octant->pairable_elements.empty()) { - + if (use_pairs && !p_octant->pairable_elements.is_empty()) { typename List<Element *, AL>::Element *I; I = p_octant->pairable_elements.front(); for (; I; I = I->next()) { - Element *e = I->get(); - if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) + if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) { continue; + } e->last_pass = pass; if (e->aabb.intersects_segment(p_from, p_to)) { - if (*p_result_idx < p_result_max) { - p_result_array[*p_result_idx] = e->userdata; - if (p_subindex_array) + if (p_subindex_array) { p_subindex_array[*p_result_idx] = e->subindex; + } (*p_result_idx)++; } else { - return; // pointless to continue } } @@ -1205,7 +1120,6 @@ void Octree<T, use_pairs, AL>::_cull_segment(Octant *p_octant, const Vector3 &p_ } for (int i = 0; i < 8; i++) { - if (p_octant->children[i] && p_octant->children[i]->aabb.intersects_segment(p_from, p_to)) { _cull_segment(p_octant->children[i], p_from, p_to, p_result_array, p_result_idx, p_result_max, p_subindex_array, p_mask); } @@ -1214,64 +1128,58 @@ void Octree<T, use_pairs, AL>::_cull_segment(Octant *p_octant, const Vector3 &p_ template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::_cull_point(Octant *p_octant, const Vector3 &p_point, T **p_result_array, int *p_result_idx, int p_result_max, int *p_subindex_array, uint32_t p_mask) { - - if (*p_result_idx == p_result_max) + if (*p_result_idx == p_result_max) { return; //pointless + } - if (!p_octant->elements.empty()) { - + if (!p_octant->elements.is_empty()) { typename List<Element *, AL>::Element *I; I = p_octant->elements.front(); for (; I; I = I->next()) { - Element *e = I->get(); - if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) + if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) { continue; + } e->last_pass = pass; if (e->aabb.has_point(p_point)) { - if (*p_result_idx < p_result_max) { - p_result_array[*p_result_idx] = e->userdata; - if (p_subindex_array) + if (p_subindex_array) { p_subindex_array[*p_result_idx] = e->subindex; + } (*p_result_idx)++; } else { - return; // pointless to continue } } } } - if (use_pairs && !p_octant->pairable_elements.empty()) { - + if (use_pairs && !p_octant->pairable_elements.is_empty()) { typename List<Element *, AL>::Element *I; I = p_octant->pairable_elements.front(); for (; I; I = I->next()) { - Element *e = I->get(); - if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) + if (e->last_pass == pass || (use_pairs && !(e->pairable_type & p_mask))) { continue; + } e->last_pass = pass; if (e->aabb.has_point(p_point)) { - if (*p_result_idx < p_result_max) { - p_result_array[*p_result_idx] = e->userdata; - if (p_subindex_array) + if (p_subindex_array) { p_subindex_array[*p_result_idx] = e->subindex; + } (*p_result_idx)++; } else { - return; // pointless to continue } } @@ -1279,7 +1187,6 @@ void Octree<T, use_pairs, AL>::_cull_point(Octant *p_octant, const Vector3 &p_po } for (int i = 0; i < 8; i++) { - //could be optimized.. if (p_octant->children[i] && p_octant->children[i]->aabb.has_point(p_point)) { _cull_point(p_octant->children[i], p_point, p_result_array, p_result_idx, p_result_max, p_subindex_array, p_mask); @@ -1289,13 +1196,14 @@ void Octree<T, use_pairs, AL>::_cull_point(Octant *p_octant, const Vector3 &p_po template <class T, bool use_pairs, class AL> int Octree<T, use_pairs, AL>::cull_convex(const Vector<Plane> &p_convex, T **p_result_array, int p_result_max, uint32_t p_mask) { - - if (!root || p_convex.size() == 0) + if (!root || p_convex.size() == 0) { return 0; + } - Vector<Vector3> convex_points = Geometry::compute_convex_mesh_points(&p_convex[0], p_convex.size()); - if (convex_points.size() == 0) + Vector<Vector3> convex_points = Geometry3D::compute_convex_mesh_points(&p_convex[0], p_convex.size()); + if (convex_points.size() == 0) { return 0; + } int result_count = 0; pass++; @@ -1316,9 +1224,9 @@ int Octree<T, use_pairs, AL>::cull_convex(const Vector<Plane> &p_convex, T **p_r template <class T, bool use_pairs, class AL> int Octree<T, use_pairs, AL>::cull_aabb(const AABB &p_aabb, T **p_result_array, int p_result_max, int *p_subindex_array, uint32_t p_mask) { - - if (!root) + if (!root) { return 0; + } int result_count = 0; pass++; @@ -1329,9 +1237,9 @@ int Octree<T, use_pairs, AL>::cull_aabb(const AABB &p_aabb, T **p_result_array, template <class T, bool use_pairs, class AL> int Octree<T, use_pairs, AL>::cull_segment(const Vector3 &p_from, const Vector3 &p_to, T **p_result_array, int p_result_max, int *p_subindex_array, uint32_t p_mask) { - - if (!root) + if (!root) { return 0; + } int result_count = 0; pass++; @@ -1342,9 +1250,9 @@ int Octree<T, use_pairs, AL>::cull_segment(const Vector3 &p_from, const Vector3 template <class T, bool use_pairs, class AL> int Octree<T, use_pairs, AL>::cull_point(const Vector3 &p_point, T **p_result_array, int p_result_max, int *p_subindex_array, uint32_t p_mask) { - - if (!root) + if (!root) { return 0; + } int result_count = 0; pass++; @@ -1355,20 +1263,18 @@ int Octree<T, use_pairs, AL>::cull_point(const Vector3 &p_point, T **p_result_ar template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::set_pair_callback(PairCallback p_callback, void *p_userdata) { - pair_callback = p_callback; pair_callback_userdata = p_userdata; } + template <class T, bool use_pairs, class AL> void Octree<T, use_pairs, AL>::set_unpair_callback(UnpairCallback p_callback, void *p_userdata) { - unpair_callback = p_callback; unpair_callback_userdata = p_userdata; } template <class T, bool use_pairs, class AL> Octree<T, use_pairs, AL>::Octree(real_t p_unit_size) { - last_element_id = 1; pass = 1; unit_size = p_unit_size; diff --git a/core/math/plane.cpp b/core/math/plane.cpp index a3818698bc..f1d3bbbd54 100644 --- a/core/math/plane.cpp +++ b/core/math/plane.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,14 +31,13 @@ #include "plane.h" #include "core/math/math_funcs.h" +#include "core/variant/variant.h" void Plane::set_normal(const Vector3 &p_normal) { - normal = p_normal; } void Plane::normalize() { - real_t l = normal.length(); if (l == 0) { *this = Plane(0, 0, 0, 0); @@ -49,27 +48,21 @@ void Plane::normalize() { } Plane Plane::normalized() const { - Plane p = *this; p.normalize(); return p; } -Vector3 Plane::get_any_point() const { - - return get_normal() * d; -} - Vector3 Plane::get_any_perpendicular_normal() const { - static const Vector3 p1 = Vector3(1, 0, 0); static const Vector3 p2 = Vector3(0, 1, 0); Vector3 p; - if (ABS(normal.dot(p1)) > 0.99) // if too similar to p1 + if (ABS(normal.dot(p1)) > 0.99) { // if too similar to p1 p = p2; // use p2 - else + } else { p = p1; // use p1 + } p -= normal * normal.dot(p); p.normalize(); @@ -80,7 +73,6 @@ Vector3 Plane::get_any_perpendicular_normal() const { /* intersections */ bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result) const { - const Plane &p_plane0 = *this; Vector3 normal0 = p_plane0.normal; Vector3 normal1 = p_plane1.normal; @@ -88,8 +80,9 @@ bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r real_t denom = vec3_cross(normal0, normal1).dot(normal2); - if (Math::is_zero_approx(denom)) + if (Math::is_zero_approx(denom)) { return false; + } if (r_result) { *r_result = ((vec3_cross(normal1, normal2) * p_plane0.d) + @@ -102,13 +95,11 @@ bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r } bool Plane::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection) const { - Vector3 segment = p_dir; real_t den = normal.dot(segment); //printf("den is %i\n",den); if (Math::is_zero_approx(den)) { - return false; } @@ -127,13 +118,11 @@ bool Plane::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 } bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 *p_intersection) const { - Vector3 segment = p_begin - p_end; real_t den = normal.dot(segment); //printf("den is %i\n",den); if (Math::is_zero_approx(den)) { - return false; } @@ -141,7 +130,6 @@ bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vec //printf("dist is %i\n",dist); if (dist < -CMP_EPSILON || dist > (1.0 + CMP_EPSILON)) { - return false; } @@ -151,14 +139,41 @@ bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vec return true; } +Variant Plane::intersect_3_bind(const Plane &p_plane1, const Plane &p_plane2) const { + Vector3 inters; + if (intersect_3(p_plane1, p_plane2, &inters)) { + return inters; + } else { + return Variant(); + } +} +Variant Plane::intersects_ray_bind(const Vector3 &p_from, const Vector3 &p_dir) const { + Vector3 inters; + if (intersects_ray(p_from, p_dir, &inters)) { + return inters; + } else { + return Variant(); + } +} +Variant Plane::intersects_segment_bind(const Vector3 &p_begin, const Vector3 &p_end) const { + Vector3 inters; + if (intersects_segment(p_begin, p_end, &inters)) { + return inters; + } else { + return Variant(); + } +} + /* misc */ -bool Plane::is_equal_approx(const Plane &p_plane) const { +bool Plane::is_equal_approx_any_side(const Plane &p_plane) const { + return (normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d)) || (normal.is_equal_approx(-p_plane.normal) && Math::is_equal_approx(d, -p_plane.d)); +} +bool Plane::is_equal_approx(const Plane &p_plane) const { return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d); } Plane::operator String() const { - return normal.operator String() + ", " + rtos(d); } diff --git a/core/math/plane.h b/core/math/plane.h index 771c8fc705..2267b28c53 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,10 +33,12 @@ #include "core/math/vector3.h" +class Variant; + class Plane { public: Vector3 normal; - real_t d; + real_t d = 0; void set_normal(const Vector3 &p_normal); _FORCE_INLINE_ Vector3 get_normal() const { return normal; }; ///Point is coplanar, CMP_EPSILON for precision @@ -47,7 +49,6 @@ public: /* Plane-Point operations */ _FORCE_INLINE_ Vector3 center() const { return normal * d; } - Vector3 get_any_point() const; Vector3 get_any_perpendicular_normal() const; _FORCE_INLINE_ bool is_point_over(const Vector3 &p_point) const; ///< Point is over plane @@ -56,12 +57,16 @@ public: /* intersections */ - bool intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result = 0) const; + bool intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result = nullptr) const; bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection) const; bool intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 *p_intersection) const; - _FORCE_INLINE_ Vector3 project(const Vector3 &p_point) const { + // For Variant bindings. + Variant intersect_3_bind(const Plane &p_plane1, const Plane &p_plane2) const; + Variant intersects_ray_bind(const Vector3 &p_from, const Vector3 &p_dir) const; + Variant intersects_segment_bind(const Vector3 &p_begin, const Vector3 &p_end) const; + _FORCE_INLINE_ Vector3 project(const Vector3 &p_point) const { return p_point - normal * distance_to(p_point); } @@ -69,13 +74,13 @@ public: Plane operator-() const { return Plane(-normal, -d); } bool is_equal_approx(const Plane &p_plane) const; + bool is_equal_approx_any_side(const Plane &p_plane) const; _FORCE_INLINE_ bool operator==(const Plane &p_plane) const; _FORCE_INLINE_ bool operator!=(const Plane &p_plane) const; operator String() const; - _FORCE_INLINE_ Plane() : - d(0) {} + _FORCE_INLINE_ Plane() {} _FORCE_INLINE_ Plane(real_t p_a, real_t p_b, real_t p_c, real_t p_d) : normal(p_a, p_b, p_c), d(p_d) {} @@ -86,17 +91,14 @@ public: }; bool Plane::is_point_over(const Vector3 &p_point) const { - return (normal.dot(p_point) > d); } real_t Plane::distance_to(const Vector3 &p_point) const { - return (normal.dot(p_point) - d); } bool Plane::has_point(const Vector3 &p_point, real_t _epsilon) const { - real_t dist = normal.dot(p_point) - d; dist = ABS(dist); return (dist <= _epsilon); @@ -113,23 +115,21 @@ Plane::Plane(const Vector3 &p_point, const Vector3 &p_normal) : } Plane::Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_point3, ClockDirection p_dir) { - - if (p_dir == CLOCKWISE) + if (p_dir == CLOCKWISE) { normal = (p_point1 - p_point3).cross(p_point1 - p_point2); - else + } else { normal = (p_point1 - p_point2).cross(p_point1 - p_point3); + } normal.normalize(); d = normal.dot(p_point1); } bool Plane::operator==(const Plane &p_plane) const { - return normal == p_plane.normal && d == p_plane.d; } bool Plane::operator!=(const Plane &p_plane) const { - return normal != p_plane.normal || d != p_plane.d; } diff --git a/core/math/quat.cpp b/core/math/quat.cpp index 61cd41b23d..4cecc20fef 100644 --- a/core/math/quat.cpp +++ b/core/math/quat.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #include "quat.h" #include "core/math/basis.h" -#include "core/print_string.h" +#include "core/string/print_string.h" // set_euler_xyz expects a vector containing the Euler angles in the format // (ax,ay,az), where ax is the angle of rotation around x axis, @@ -106,28 +106,24 @@ Vector3 Quat::get_euler_yxz() const { return m.get_euler_yxz(); } -void Quat::operator*=(const Quat &q) { - - set(w * q.x + x * q.w + y * q.z - z * q.y, - w * q.y + y * q.w + z * q.x - x * q.z, - w * q.z + z * q.w + x * q.y - y * q.x, - w * q.w - x * q.x - y * q.y - z * q.z); +void Quat::operator*=(const Quat &p_q) { + set(w * p_q.x + x * p_q.w + y * p_q.z - z * p_q.y, + w * p_q.y + y * p_q.w + z * p_q.x - x * p_q.z, + w * p_q.z + z * p_q.w + x * p_q.y - y * p_q.x, + w * p_q.w - x * p_q.x - y * p_q.y - z * p_q.z); } -Quat Quat::operator*(const Quat &q) const { - +Quat Quat::operator*(const Quat &p_q) const { Quat r = *this; - r *= q; + r *= p_q; return r; } bool Quat::is_equal_approx(const Quat &p_quat) const { - return Math::is_equal_approx(x, p_quat.x) && Math::is_equal_approx(y, p_quat.y) && Math::is_equal_approx(z, p_quat.z) && Math::is_equal_approx(w, p_quat.w); } real_t Quat::length() const { - return Math::sqrt(length_squared()); } @@ -150,29 +146,29 @@ Quat Quat::inverse() const { return Quat(-x, -y, -z, w); } -Quat Quat::slerp(const Quat &q, const real_t &t) const { +Quat Quat::slerp(const Quat &p_to, const real_t &p_weight) const { #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(!is_normalized(), Quat(), "The start quaternion must be normalized."); - ERR_FAIL_COND_V_MSG(!q.is_normalized(), Quat(), "The end quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quat(), "The end quaternion must be normalized."); #endif Quat to1; real_t omega, cosom, sinom, scale0, scale1; // calc cosine - cosom = dot(q); + cosom = dot(p_to); // adjust signs (if necessary) if (cosom < 0.0) { cosom = -cosom; - to1.x = -q.x; - to1.y = -q.y; - to1.z = -q.z; - to1.w = -q.w; + to1.x = -p_to.x; + to1.y = -p_to.y; + to1.z = -p_to.z; + to1.w = -p_to.w; } else { - to1.x = q.x; - to1.y = q.y; - to1.z = q.z; - to1.w = q.w; + to1.x = p_to.x; + to1.y = p_to.y; + to1.z = p_to.z; + to1.w = p_to.w; } // calculate coefficients @@ -181,13 +177,13 @@ Quat Quat::slerp(const Quat &q, const real_t &t) const { // standard case (slerp) omega = Math::acos(cosom); sinom = Math::sin(omega); - scale0 = Math::sin((1.0 - t) * omega) / sinom; - scale1 = Math::sin(t * omega) / sinom; + scale0 = Math::sin((1.0 - p_weight) * omega) / sinom; + scale1 = Math::sin(p_weight * omega) / sinom; } else { // "from" and "to" quaternions are very close // ... so we can do a linear interpolation - scale0 = 1.0 - t; - scale1 = t; + scale0 = 1.0 - p_weight; + scale1 = p_weight; } // calculate final values return Quat( @@ -197,42 +193,43 @@ Quat Quat::slerp(const Quat &q, const real_t &t) const { scale0 * w + scale1 * to1.w); } -Quat Quat::slerpni(const Quat &q, const real_t &t) const { +Quat Quat::slerpni(const Quat &p_to, const real_t &p_weight) const { #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(!is_normalized(), Quat(), "The start quaternion must be normalized."); - ERR_FAIL_COND_V_MSG(!q.is_normalized(), Quat(), "The end quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quat(), "The end quaternion must be normalized."); #endif const Quat &from = *this; - real_t dot = from.dot(q); + real_t dot = from.dot(p_to); - if (Math::absf(dot) > 0.9999) return from; + if (Math::absf(dot) > 0.9999) { + return from; + } real_t theta = Math::acos(dot), sinT = 1.0 / Math::sin(theta), - newFactor = Math::sin(t * theta) * sinT, - invFactor = Math::sin((1.0 - t) * theta) * sinT; + newFactor = Math::sin(p_weight * theta) * sinT, + invFactor = Math::sin((1.0 - p_weight) * theta) * sinT; - return Quat(invFactor * from.x + newFactor * q.x, - invFactor * from.y + newFactor * q.y, - invFactor * from.z + newFactor * q.z, - invFactor * from.w + newFactor * q.w); + return Quat(invFactor * from.x + newFactor * p_to.x, + invFactor * from.y + newFactor * p_to.y, + invFactor * from.z + newFactor * p_to.z, + invFactor * from.w + newFactor * p_to.w); } -Quat Quat::cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const { +Quat Quat::cubic_slerp(const Quat &p_b, const Quat &p_pre_a, const Quat &p_post_b, const real_t &p_weight) const { #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(!is_normalized(), Quat(), "The start quaternion must be normalized."); - ERR_FAIL_COND_V_MSG(!q.is_normalized(), Quat(), "The end quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quat(), "The end quaternion must be normalized."); #endif //the only way to do slerp :| - real_t t2 = (1.0 - t) * t * 2; - Quat sp = this->slerp(q, t); - Quat sq = prep.slerpni(postq, t); + real_t t2 = (1.0 - p_weight) * p_weight * 2; + Quat sp = this->slerp(p_b, p_weight); + Quat sq = p_pre_a.slerpni(p_post_b, p_weight); return sp.slerpni(sq, t2); } Quat::operator String() const { - return String::num(x) + ", " + String::num(y) + ", " + String::num(z) + ", " + String::num(w); } @@ -241,9 +238,9 @@ void Quat::set_axis_angle(const Vector3 &axis, const real_t &angle) { ERR_FAIL_COND_MSG(!axis.is_normalized(), "The axis Vector3 must be normalized."); #endif real_t d = axis.length(); - if (d == 0) + if (d == 0) { set(0, 0, 0, 0); - else { + } else { real_t sin_angle = Math::sin(angle * 0.5); real_t cos_angle = Math::cos(angle * 0.5); real_t s = sin_angle / d; diff --git a/core/math/quat.h b/core/math/quat.h index b3135ad1ca..423a7f8dfe 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -36,12 +36,26 @@ #include "core/math/math_defs.h" #include "core/math/math_funcs.h" -#include "core/ustring.h" +#include "core/string/ustring.h" class Quat { public: - real_t x, y, z, w; - + union { + struct { + real_t x; + real_t y; + real_t z; + real_t w; + }; + real_t components[4] = { 0, 0, 0, 1.0 }; + }; + + _FORCE_INLINE_ real_t &operator[](int idx) { + return components[idx]; + } + _FORCE_INLINE_ const real_t &operator[](int idx) const { + return components[idx]; + } _FORCE_INLINE_ real_t length_squared() const; bool is_equal_approx(const Quat &p_quat) const; real_t length() const; @@ -49,7 +63,7 @@ public: Quat normalized() const; bool is_normalized() const; Quat inverse() const; - _FORCE_INLINE_ real_t dot(const Quat &q) const; + _FORCE_INLINE_ real_t dot(const Quat &p_q) const; void set_euler_xyz(const Vector3 &p_euler); Vector3 get_euler_xyz() const; @@ -59,9 +73,9 @@ public: void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); }; Vector3 get_euler() const { return get_euler_yxz(); }; - Quat slerp(const Quat &q, const real_t &t) const; - Quat slerpni(const Quat &q, const real_t &t) const; - Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const; + Quat slerp(const Quat &p_to, const real_t &p_weight) const; + Quat slerpni(const Quat &p_to, const real_t &p_weight) const; + Quat cubic_slerp(const Quat &p_b, const Quat &p_pre_a, const Quat &p_post_b, const real_t &p_weight) const; void set_axis_angle(const Vector3 &axis, const real_t &angle); _FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { @@ -72,8 +86,8 @@ public: r_axis.z = z * r; } - void operator*=(const Quat &q); - Quat operator*(const Quat &q) const; + void operator*=(const Quat &p_q); + Quat operator*(const Quat &p_q) const; Quat operator*(const Vector3 &v) const { return Quat(w * v.x + y * v.z - z * v.y, @@ -91,8 +105,12 @@ public: return v + ((uv * w) + u.cross(uv)) * ((real_t)2); } - _FORCE_INLINE_ void operator+=(const Quat &q); - _FORCE_INLINE_ void operator-=(const Quat &q); + _FORCE_INLINE_ Vector3 xform_inv(const Vector3 &v) const { + return inverse().xform(v); + } + + _FORCE_INLINE_ void operator+=(const Quat &p_q); + _FORCE_INLINE_ void operator-=(const Quat &p_q); _FORCE_INLINE_ void operator*=(const real_t &s); _FORCE_INLINE_ void operator/=(const real_t &s); _FORCE_INLINE_ Quat operator+(const Quat &q2) const; @@ -112,7 +130,9 @@ public: z = p_z; w = p_w; } - inline Quat(real_t p_x, real_t p_y, real_t p_z, real_t p_w) : + + _FORCE_INLINE_ Quat() {} + _FORCE_INLINE_ Quat(real_t p_x, real_t p_y, real_t p_z, real_t p_w) : x(p_x), y(p_y), z(p_z), @@ -121,18 +141,18 @@ public: Quat(const Vector3 &axis, const real_t &angle) { set_axis_angle(axis, angle); } Quat(const Vector3 &euler) { set_euler(euler); } - Quat(const Quat &q) : - x(q.x), - y(q.y), - z(q.z), - w(q.w) { + Quat(const Quat &p_q) : + x(p_q.x), + y(p_q.y), + z(p_q.z), + w(p_q.w) { } - Quat operator=(const Quat &q) { - x = q.x; - y = q.y; - z = q.z; - w = q.w; + Quat &operator=(const Quat &p_q) { + x = p_q.x; + y = p_q.y; + z = p_q.z; + w = p_q.w; return *this; } @@ -147,7 +167,6 @@ public: z = 0; w = 0; } else { - real_t s = Math::sqrt((1.0 + d) * 2.0); real_t rs = 1.0 / s; @@ -157,35 +176,28 @@ public: w = s * 0.5; } } - - inline Quat() : - x(0), - y(0), - z(0), - w(1) { - } }; -real_t Quat::dot(const Quat &q) const { - return x * q.x + y * q.y + z * q.z + w * q.w; +real_t Quat::dot(const Quat &p_q) const { + return x * p_q.x + y * p_q.y + z * p_q.z + w * p_q.w; } real_t Quat::length_squared() const { return dot(*this); } -void Quat::operator+=(const Quat &q) { - x += q.x; - y += q.y; - z += q.z; - w += q.w; +void Quat::operator+=(const Quat &p_q) { + x += p_q.x; + y += p_q.y; + z += p_q.z; + w += p_q.w; } -void Quat::operator-=(const Quat &q) { - x -= q.x; - y -= q.y; - z -= q.z; - w -= q.w; +void Quat::operator-=(const Quat &p_q) { + x -= p_q.x; + y -= p_q.y; + z -= p_q.z; + w -= p_q.w; } void Quat::operator*=(const real_t &s) { @@ -196,7 +208,6 @@ void Quat::operator*=(const real_t &s) { } void Quat::operator/=(const real_t &s) { - *this *= 1.0 / s; } @@ -231,4 +242,8 @@ bool Quat::operator!=(const Quat &p_quat) const { return x != p_quat.x || y != p_quat.y || z != p_quat.z || w != p_quat.w; } +_FORCE_INLINE_ Quat operator*(const real_t &p_real, const Quat &p_quat) { + return p_quat * p_real; +} + #endif // QUAT_H diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 7fbb26c377..ad28967d7a 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,17 +30,15 @@ #include "quick_hull.h" -#include "core/map.h" +#include "core/templates/map.h" 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; for (int i = 0; i < p_points.size(); i++) { - if (i == 0) { aabb.position = p_points[i]; } else { @@ -57,7 +55,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Set<Vector3> valid_cache; for (int i = 0; i < p_points.size(); i++) { - Vector3 sp = p_points[i].snapped(Vector3(0.0001, 0.0001, 0.0001)); if (valid_cache.has(sp)) { valid_points.write[i] = false; @@ -78,12 +75,11 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me real_t max = 0, min = 0; for (int i = 0; i < p_points.size(); i++) { - - if (!valid_points[i]) + if (!valid_points[i]) { continue; + } real_t d = p_points[i][longest_axis]; if (i == 0 || d < min) { - simplex[0] = i; min = d; } @@ -102,15 +98,14 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Vector3 rel12 = p_points[simplex[0]] - p_points[simplex[1]]; for (int i = 0; i < p_points.size(); i++) { - - if (!valid_points[i]) + if (!valid_points[i]) { continue; + } Vector3 n = rel12.cross(p_points[simplex[0]] - p_points[i]).cross(rel12).normalized(); real_t d = Math::abs(n.dot(p_points[simplex[0]]) - n.dot(p_points[i])); if (i == 0 || d > maxd) { - maxd = d; simplex[2] = i; } @@ -124,14 +119,13 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Plane p(p_points[simplex[0]], p_points[simplex[1]], p_points[simplex[2]]); for (int i = 0; i < p_points.size(); i++) { - - if (!valid_points[i]) + if (!valid_points[i]) { continue; + } real_t d = Math::abs(p.distance_to(p_points[i])); if (i == 0 || d > maxd) { - maxd = d; simplex[3] = i; } @@ -152,7 +146,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me List<Face> faces; for (int i = 0; i < 4; i++) { - static const int face_order[4][3] = { { 0, 1, 2 }, { 0, 1, 3 }, @@ -183,22 +176,24 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me /* COMPUTE AVAILABLE VERTICES */ for (int i = 0; i < p_points.size(); i++) { - - if (i == simplex[0]) + if (i == simplex[0]) { continue; - if (i == simplex[1]) + } + if (i == simplex[1]) { continue; - if (i == simplex[2]) + } + if (i == simplex[2]) { continue; - if (i == simplex[3]) + } + if (i == simplex[3]) { continue; - if (!valid_points[i]) + } + if (!valid_points[i]) { continue; + } for (List<Face>::Element *E = faces.front(); E; E = E->next()) { - if (E->get().plane.distance_to(p_points[i]) > over_tolerance) { - E->get().points_over.push_back(i); break; } @@ -219,7 +214,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me uint32_t debug_stop = debug_stop_after; while (debug_stop > 0 && faces.back()->get().points_over.size()) { - debug_stop--; Face &f = faces.back()->get(); @@ -228,7 +222,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me real_t next_d = 0; for (int i = 0; i < f.points_over.size(); i++) { - real_t d = f.plane.distance_to(p_points[f.points_over[i]]); if (d > next_d) { @@ -247,9 +240,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Map<Edge, FaceConnect> lit_edges; //create this on the flight, should not be that bad for performance and simplifies code a lot for (List<Face>::Element *E = faces.front(); E; E = E->next()) { - if (E->get().plane.distance_to(v) > 0) { - lit_faces.push_back(E); for (int i = 0; i < 3; i++) { @@ -265,7 +256,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me //left F->get().left = E; } else { - F->get().right = E; } } @@ -276,7 +266,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me List<List<Face>::Element *> new_faces; //new faces for (Map<Edge, FaceConnect>::Element *E = lit_edges.front(); E; E = E->next()) { - FaceConnect &fc = E->get(); if (fc.left && fc.right) { continue; //edge is uninteresting, not on horizont @@ -304,17 +293,15 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me //distribute points into new faces for (List<List<Face>::Element *>::Element *F = lit_faces.front(); F; F = F->next()) { - Face &lf = F->get()->get(); for (int i = 0; i < lf.points_over.size(); i++) { - - if (lf.points_over[i] == f.points_over[next]) //do not add current one + if (lf.points_over[i] == f.points_over[next]) { //do not add current one continue; + } Vector3 p = p_points[lf.points_over[i]]; for (List<List<Face>::Element *>::Element *E = new_faces.front(); E; E = E->next()) { - Face &f2 = E->get()->get(); if (f2.plane.distance_to(p) > over_tolerance) { f2.points_over.push_back(lf.points_over[i]); @@ -327,7 +314,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me //erase lit faces while (lit_faces.size()) { - faces.erase(lit_faces.front()->get()); lit_faces.pop_front(); } @@ -335,7 +321,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me //put faces that contain no points on the front for (List<List<Face>::Element *>::Element *E = new_faces.front(); E; E = E->next()) { - Face &f2 = E->get()->get(); if (f2.points_over.size() == 0) { faces.move_to_front(E->get()); @@ -349,21 +334,19 @@ 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]; uint32_t b = E->get().vertices[(i + 1) % 3]; Edge e(a, b); @@ -376,7 +359,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me //left G->get().left = F; } else { - G->get().right = F; } } @@ -384,12 +366,10 @@ 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]; int b = E->get().indices[(i + 1) % f.indices.size()]; Edge e(a, b); @@ -397,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); @@ -411,7 +391,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me if (O->get().indices[j] == a) { //append the rest for (int k = 0; k < ois; k++) { - int idx = O->get().indices[(k + j) % ois]; int idxn = O->get().indices[(k + j + 1) % ois]; if (idx == b && idxn == a) { //already have b! @@ -427,10 +406,11 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Map<Edge, RetFaceConnect>::Element *F2 = ret_edges.find(e2); ERR_CONTINUE(!F2); //change faceconnect, point to this face instead - if (F2->get().left == O) + if (F2->get().left == O) { F2->get().left = E; - else if (F2->get().right == O) + } else if (F2->get().right == O) { F2->get().right = E; + } } break; @@ -439,11 +419,13 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me // remove all edge connections to this face for (Map<Edge, RetFaceConnect>::Element *G = ret_edges.front(); G; G = G->next()) { - if (G->get().left == O) + if (G->get().left == O) { G->get().left = nullptr; + } - if (G->get().right == O) + if (G->get().right == O) { G->get().right = nullptr; + } } ret_edges.erase(F); //remove the edge @@ -457,14 +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 173f919a73..48ea139cc9 100644 --- a/core/math/quick_hull.h +++ b/core/math/quick_hull.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,19 +31,17 @@ #ifndef QUICK_HULL_H #define QUICK_HULL_H -#include "core/list.h" #include "core/math/aabb.h" -#include "core/math/geometry.h" -#include "core/set.h" +#include "core/math/geometry_3d.h" +#include "core/templates/list.h" +#include "core/templates/set.h" class QuickHull { - public: struct Edge { - union { uint32_t vertices[2]; - uint64_t id; + uint64_t id = 0; }; bool operator<(const Edge &p_edge) const { @@ -51,7 +49,6 @@ public: } Edge(int p_vtx_a = 0, int p_vtx_b = 0) { - if (p_vtx_a > p_vtx_b) { SWAP(p_vtx_a, p_vtx_b); } @@ -62,36 +59,30 @@ public: }; struct Face { - Plane plane; - uint32_t vertices[3]; + uint32_t vertices[3] = { 0 }; Vector<int> points_over; bool operator<(const Face &p_face) const { - return points_over.size() < p_face.points_over.size(); } }; private: struct FaceConnect { - List<Face>::Element *left, *right; - FaceConnect() { - left = nullptr; - right = nullptr; - } + List<Face>::Element *left = nullptr; + List<Face>::Element *right = nullptr; + FaceConnect() {} }; struct RetFaceConnect { - List<Geometry::MeshData::Face>::Element *left, *right; - RetFaceConnect() { - left = nullptr; - right = nullptr; - } + List<Geometry3D::MeshData::Face>::Element *left = nullptr; + List<Geometry3D::MeshData::Face>::Element *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/math/random_number_generator.cpp b/core/math/random_number_generator.cpp index 1a1bffb562..b40d010219 100644 --- a/core/math/random_number_generator.cpp +++ b/core/math/random_number_generator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,12 +30,12 @@ #include "random_number_generator.h" -RandomNumberGenerator::RandomNumberGenerator() {} - void RandomNumberGenerator::_bind_methods() { ClassDB::bind_method(D_METHOD("set_seed", "seed"), &RandomNumberGenerator::set_seed); ClassDB::bind_method(D_METHOD("get_seed"), &RandomNumberGenerator::get_seed); - ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); + + ClassDB::bind_method(D_METHOD("set_state", "state"), &RandomNumberGenerator::set_state); + ClassDB::bind_method(D_METHOD("get_state"), &RandomNumberGenerator::get_state); ClassDB::bind_method(D_METHOD("randi"), &RandomNumberGenerator::randi); ClassDB::bind_method(D_METHOD("randf"), &RandomNumberGenerator::randf); @@ -43,4 +43,10 @@ void RandomNumberGenerator::_bind_methods() { ClassDB::bind_method(D_METHOD("randf_range", "from", "to"), &RandomNumberGenerator::randf_range); ClassDB::bind_method(D_METHOD("randi_range", "from", "to"), &RandomNumberGenerator::randi_range); ClassDB::bind_method(D_METHOD("randomize"), &RandomNumberGenerator::randomize); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "state"), "set_state", "get_state"); + // Default values are non-deterministic, override for doc generation purposes. + ADD_PROPERTY_DEFAULT("seed", 0); + ADD_PROPERTY_DEFAULT("state", 0); } diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h index e7f188bb42..a396c2b7d7 100644 --- a/core/math/random_number_generator.h +++ b/core/math/random_number_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,40 +32,32 @@ #define RANDOM_NUMBER_GENERATOR_H #include "core/math/random_pcg.h" -#include "core/reference.h" +#include "core/object/reference.h" class RandomNumberGenerator : public Reference { GDCLASS(RandomNumberGenerator, Reference); +protected: RandomPCG randbase; -protected: static void _bind_methods(); public: - _FORCE_INLINE_ void set_seed(uint64_t seed) { randbase.seed(seed); } - + _FORCE_INLINE_ void set_seed(uint64_t p_seed) { randbase.seed(p_seed); } _FORCE_INLINE_ uint64_t get_seed() { return randbase.get_seed(); } + _FORCE_INLINE_ void set_state(uint64_t p_state) { randbase.set_state(p_state); } + _FORCE_INLINE_ uint64_t get_state() const { return randbase.get_state(); } + _FORCE_INLINE_ void randomize() { randbase.randomize(); } _FORCE_INLINE_ uint32_t randi() { return randbase.rand(); } - _FORCE_INLINE_ real_t randf() { return randbase.randf(); } + _FORCE_INLINE_ real_t randf_range(real_t p_from, real_t p_to) { return randbase.random(p_from, p_to); } + _FORCE_INLINE_ real_t randfn(real_t p_mean = 0.0, real_t p_deviation = 1.0) { return randbase.randfn(p_mean, p_deviation); } + _FORCE_INLINE_ int randi_range(int p_from, int p_to) { return randbase.random(p_from, p_to); } - _FORCE_INLINE_ real_t randf_range(real_t from, real_t to) { return randbase.random(from, to); } - - _FORCE_INLINE_ real_t randfn(real_t mean = 0.0, real_t deviation = 1.0) { return randbase.randfn(mean, deviation); } - - _FORCE_INLINE_ int randi_range(int from, int to) { - unsigned int ret = randbase.rand(); - if (to < from) - return ret % (from - to + 1) + to; - else - return ret % (to - from + 1) + from; - } - - RandomNumberGenerator(); + RandomNumberGenerator() {} }; #endif // RANDOM_NUMBER_GENERATOR_H diff --git a/core/math/random_pcg.cpp b/core/math/random_pcg.cpp index 02257c38d9..9609620469 100644 --- a/core/math/random_pcg.cpp +++ b/core/math/random_pcg.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -49,3 +49,10 @@ double RandomPCG::random(double p_from, double p_to) { float RandomPCG::random(float p_from, float p_to) { return randf() * (p_to - p_from) + p_from; } + +int RandomPCG::random(int p_from, int p_to) { + if (p_from == p_to) { + return p_from; + } + return rand(abs(p_from - p_to) + 1) + MIN(p_from, p_to); +} diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h index 8fd5a056fa..5a03b758ce 100644 --- a/core/math/random_pcg.h +++ b/core/math/random_pcg.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,12 +31,12 @@ #ifndef RANDOM_PCG_H #define RANDOM_PCG_H -#include <math.h> - #include "core/math/math_defs.h" #include "thirdparty/misc/pcg.h" +#include <math.h> + #if defined(__GNUC__) #define CLZ32(x) __builtin_clz(x) #elif defined(_MSC_VER) @@ -61,13 +61,12 @@ static int __bsr_clz32(uint32_t x) { class RandomPCG { pcg32_random_t pcg; - uint64_t current_seed; // seed with this to get the same state + uint64_t current_seed; // The seed the current generator state started from. uint64_t current_inc; public: static const uint64_t DEFAULT_SEED = 12047754176567800795U; static const uint64_t DEFAULT_INC = PCG_DEFAULT_INC_64; - static const uint64_t RANDOM_MAX = 0xFFFFFFFF; RandomPCG(uint64_t p_seed = DEFAULT_SEED, uint64_t p_inc = DEFAULT_INC); @@ -77,11 +76,16 @@ public: } _FORCE_INLINE_ uint64_t get_seed() { return current_seed; } + _FORCE_INLINE_ void set_state(uint64_t p_state) { pcg.state = p_state; } + _FORCE_INLINE_ uint64_t get_state() const { return pcg.state; } + void randomize(); _FORCE_INLINE_ uint32_t rand() { - current_seed = pcg.state; return pcg32_random_r(&pcg); } + _FORCE_INLINE_ uint32_t rand(uint32_t bounds) { + return pcg32_boundedrand_r(&pcg, bounds); + } // Obtaining floating point numbers in [0, 1] range with "good enough" uniformity. // These functions sample the output of rand() as the fraction part of an infinite binary number, @@ -130,7 +134,7 @@ public: double random(double p_from, double p_to); float random(float p_from, float p_to); - real_t random(int p_from, int p_to) { return (real_t)random((real_t)p_from, (real_t)p_to); } + int random(int p_from, int p_to); }; #endif // RANDOM_PCG_H diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp index 12b9904c88..60c44999f7 100644 --- a/core/math/rect2.cpp +++ b/core/math/rect2.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,12 +31,10 @@ #include "core/math/transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D bool Rect2::is_equal_approx(const Rect2 &p_rect) const { - return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size); } bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { - real_t min = 0, max = 1; int axis = 0; real_t sign = 0; @@ -50,18 +48,18 @@ bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 real_t csign; if (seg_from < seg_to) { - - if (seg_from > box_end || seg_to < box_begin) + if (seg_from > box_end || seg_to < box_begin) { return false; + } real_t length = seg_to - seg_from; cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; csign = -1.0; } else { - - if (seg_to > box_end || seg_from < box_begin) + if (seg_to > box_end || seg_from < box_begin) { return false; + } real_t length = seg_to - seg_from; cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; @@ -73,10 +71,12 @@ bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 axis = i; sign = csign; } - if (cmax < max) + if (cmax < max) { max = cmax; - if (max < min) + } + if (max < min) { return false; + } } Vector2 rel = p_to - p_from; @@ -87,14 +87,14 @@ bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_normal = normal; } - if (r_pos) + if (r_pos) { *r_pos = p_from + rel * min; + } return true; } bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { - //SAT intersection between local and transformed rect2 Vector2 xf_points[4] = { @@ -108,14 +108,18 @@ bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_re //base rect2 first (faster) - if (xf_points[0].y > position.y) + if (xf_points[0].y > position.y) { goto next1; - if (xf_points[1].y > position.y) + } + if (xf_points[1].y > position.y) { goto next1; - if (xf_points[2].y > position.y) + } + if (xf_points[2].y > position.y) { goto next1; - if (xf_points[3].y > position.y) + } + if (xf_points[3].y > position.y) { goto next1; + } return false; @@ -123,27 +127,35 @@ next1: low_limit = position.y + size.y; - if (xf_points[0].y < low_limit) + if (xf_points[0].y < low_limit) { goto next2; - if (xf_points[1].y < low_limit) + } + if (xf_points[1].y < low_limit) { goto next2; - if (xf_points[2].y < low_limit) + } + if (xf_points[2].y < low_limit) { goto next2; - if (xf_points[3].y < low_limit) + } + if (xf_points[3].y < low_limit) { goto next2; + } return false; next2: - if (xf_points[0].x > position.x) + if (xf_points[0].x > position.x) { goto next3; - if (xf_points[1].x > position.x) + } + if (xf_points[1].x > position.x) { goto next3; - if (xf_points[2].x > position.x) + } + if (xf_points[2].x > position.x) { goto next3; - if (xf_points[3].x > position.x) + } + if (xf_points[3].x > position.x) { goto next3; + } return false; @@ -151,14 +163,18 @@ next3: low_limit = position.x + size.x; - if (xf_points[0].x < low_limit) + if (xf_points[0].x < low_limit) { goto next4; - if (xf_points[1].x < low_limit) + } + if (xf_points[1].x < low_limit) { goto next4; - if (xf_points[2].x < low_limit) + } + if (xf_points[2].x < low_limit) { goto next4; - if (xf_points[3].x < low_limit) + } + if (xf_points[3].x < low_limit) { goto next4; + } return false; @@ -201,10 +217,12 @@ next4: maxb = MAX(dp, maxb); minb = MIN(dp, minb); - if (mina > maxb) + if (mina > maxb) { return false; - if (minb > maxa) + } + if (minb > maxa) { return false; + } maxa = p_xform.elements[1].dot(xf_points2[0]); mina = maxa; @@ -236,10 +254,12 @@ next4: maxb = MAX(dp, maxb); minb = MIN(dp, minb); - if (mina > maxb) + if (mina > maxb) { return false; - if (minb > maxa) + } + if (minb > maxa) { return false; + } return true; } diff --git a/core/math/rect2.h b/core/math/rect2.h index 30dbfdbbe5..512499bdb2 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -36,7 +36,6 @@ struct Transform2D; struct Rect2 { - Point2 position; Size2 size; @@ -49,30 +48,37 @@ struct Rect2 { inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const { if (p_include_borders) { - if (position.x > (p_rect.position.x + p_rect.size.width)) + if (position.x > (p_rect.position.x + p_rect.size.width)) { return false; - if ((position.x + size.width) < p_rect.position.x) + } + if ((position.x + size.width) < p_rect.position.x) { return false; - if (position.y > (p_rect.position.y + p_rect.size.height)) + } + if (position.y > (p_rect.position.y + p_rect.size.height)) { return false; - if ((position.y + size.height) < p_rect.position.y) + } + if ((position.y + size.height) < p_rect.position.y) { return false; + } } else { - if (position.x >= (p_rect.position.x + p_rect.size.width)) + if (position.x >= (p_rect.position.x + p_rect.size.width)) { return false; - if ((position.x + size.width) <= p_rect.position.x) + } + if ((position.x + size.width) <= p_rect.position.x) { return false; - if (position.y >= (p_rect.position.y + p_rect.size.height)) + } + if (position.y >= (p_rect.position.y + p_rect.size.height)) { return false; - if ((position.y + size.height) <= p_rect.position.y) + } + if ((position.y + size.height) <= p_rect.position.y) { return false; + } } return true; } inline real_t distance_to(const Vector2 &p_point) const { - real_t dist = 0.0; bool inside = true; @@ -97,10 +103,11 @@ struct Rect2 { inside = false; } - if (inside) + if (inside) { return 0; - else + } else { return dist; + } } bool intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const; @@ -108,22 +115,22 @@ struct Rect2 { bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = nullptr, Point2 *r_normal = nullptr) const; inline bool encloses(const Rect2 &p_rect) const { - return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y)); } _FORCE_INLINE_ bool has_no_area() const { - return (size.x <= 0 || size.y <= 0); } - inline Rect2 clip(const Rect2 &p_rect) const { /// return a clipped rect + // Returns the instersection between two Rect2s or an empty Rect2 if there is no intersection + inline Rect2 intersection(const Rect2 &p_rect) const { Rect2 new_rect = p_rect; - if (!intersects(new_rect)) + if (!intersects(new_rect)) { return Rect2(); + } new_rect.position.x = MAX(p_rect.position.x, position.x); new_rect.position.y = MAX(p_rect.position.y, position.y); @@ -150,17 +157,21 @@ struct Rect2 { new_rect.size = new_rect.size - new_rect.position; //make relative again return new_rect; - }; + } inline bool has_point(const Point2 &p_point) const { - if (p_point.x < position.x) + if (p_point.x < position.x) { return false; - if (p_point.y < position.y) + } + if (p_point.y < position.y) { return false; + } - if (p_point.x >= (position.x + size.x)) + if (p_point.x >= (position.x + size.x)) { return false; - if (p_point.y >= (position.y + size.y)) + } + if (p_point.y >= (position.y + size.y)) { return false; + } return true; } @@ -169,27 +180,29 @@ struct Rect2 { bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } - inline Rect2 grow(real_t p_by) const { - + inline Rect2 grow(real_t p_amount) const { Rect2 g = *this; - g.position.x -= p_by; - g.position.y -= p_by; - g.size.width += p_by * 2; - g.size.height += p_by * 2; + g.position.x -= p_amount; + g.position.y -= p_amount; + g.size.width += p_amount * 2; + g.size.height += p_amount * 2; return g; } - inline Rect2 grow_margin(Margin p_margin, real_t p_amount) const { + inline Rect2 grow_side(Side p_side, real_t p_amount) const { Rect2 g = *this; - g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, - (MARGIN_TOP == p_margin) ? p_amount : 0, - (MARGIN_RIGHT == p_margin) ? p_amount : 0, - (MARGIN_BOTTOM == p_margin) ? p_amount : 0); + g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0, + (SIDE_TOP == p_side) ? p_amount : 0, + (SIDE_RIGHT == p_side) ? p_amount : 0, + (SIDE_BOTTOM == p_side) ? p_amount : 0); return g; } - inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { + inline Rect2 grow_side_bind(uint32_t p_side, real_t p_amount) const { + return grow_side(Side(p_side), p_amount); + } + inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { Rect2 g = *this; g.position.x -= p_left; g.position.y -= p_top; @@ -200,7 +213,6 @@ struct Rect2 { } _FORCE_INLINE_ Rect2 expand(const Vector2 &p_vector) const { - Rect2 r = *this; r.expand_to(p_vector); return r; @@ -211,25 +223,99 @@ struct Rect2 { Vector2 begin = position; Vector2 end = position + size; - if (p_vector.x < begin.x) + if (p_vector.x < begin.x) { begin.x = p_vector.x; - if (p_vector.y < begin.y) + } + if (p_vector.y < begin.y) { begin.y = p_vector.y; + } - if (p_vector.x > end.x) + if (p_vector.x > end.x) { end.x = p_vector.x; - if (p_vector.y > end.y) + } + if (p_vector.y > end.y) { end.y = p_vector.y; + } position = begin; size = end - begin; } _FORCE_INLINE_ Rect2 abs() const { - return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); } + Vector2 get_support(const Vector2 &p_normal) const { + Vector2 half_extents = size * 0.5; + Vector2 ofs = position + half_extents; + return Vector2( + (p_normal.x > 0) ? -half_extents.x : half_extents.x, + (p_normal.y > 0) ? -half_extents.y : half_extents.y) + + ofs; + } + + _FORCE_INLINE_ bool intersects_filled_polygon(const Vector2 *p_points, int p_point_count) const { + Vector2 center = position + size * 0.5; + int side_plus = 0; + int side_minus = 0; + Vector2 end = position + size; + + int i_f = p_point_count - 1; + for (int i = 0; i < p_point_count; i++) { + const Vector2 &a = p_points[i_f]; + const Vector2 &b = p_points[i]; + i_f = i; + + Vector2 r = (b - a); + float l = r.length(); + if (l == 0.0) { + continue; + } + + //check inside + Vector2 tg = r.orthogonal(); + float s = tg.dot(center) - tg.dot(a); + if (s < 0.0) { + side_plus++; + } else { + side_minus++; + } + + //check ray box + r /= l; + Vector2 ir(1.0 / r.x, 1.0 / r.y); + + // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner + // r.org is origin of ray + Vector2 t13 = (position - a) * ir; + Vector2 t24 = (end - a) * ir; + + float tmin = MAX(MIN(t13.x, t24.x), MIN(t13.y, t24.y)); + float tmax = MIN(MAX(t13.x, t24.x), MAX(t13.y, t24.y)); + + // if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us + if (tmax < 0 || tmin > tmax || tmin >= l) { + continue; + } + + return true; + } + + if (side_plus * side_minus == 0) { + return true; //all inside + } else { + return false; + } + } + + _FORCE_INLINE_ void set_end(const Vector2 &p_end) { + size = p_end - position; + } + + _FORCE_INLINE_ Vector2 get_end() const { + return position + size; + } + operator String() const { return String(position) + ", " + String(size); } Rect2() {} @@ -244,7 +330,6 @@ struct Rect2 { }; struct Rect2i { - Point2i position; Size2i size; @@ -256,41 +341,45 @@ struct Rect2i { int get_area() const { return size.width * size.height; } inline bool intersects(const Rect2i &p_rect) const { - if (position.x > (p_rect.position.x + p_rect.size.width)) + if (position.x > (p_rect.position.x + p_rect.size.width)) { return false; - if ((position.x + size.width) < p_rect.position.x) + } + if ((position.x + size.width) < p_rect.position.x) { return false; - if (position.y > (p_rect.position.y + p_rect.size.height)) + } + if (position.y > (p_rect.position.y + p_rect.size.height)) { return false; - if ((position.y + size.height) < p_rect.position.y) + } + if ((position.y + size.height) < p_rect.position.y) { return false; + } return true; } inline bool encloses(const Rect2i &p_rect) const { - return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); } _FORCE_INLINE_ bool has_no_area() const { - return (size.x <= 0 || size.y <= 0); } - inline Rect2i clip(const Rect2i &p_rect) const { /// return a clipped rect + // Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection + inline Rect2i intersection(const Rect2i &p_rect) const { Rect2i new_rect = p_rect; - if (!intersects(new_rect)) + if (!intersects(new_rect)) { return Rect2i(); + } new_rect.position.x = MAX(p_rect.position.x, position.x); new_rect.position.y = MAX(p_rect.position.y, position.y); - Point2 p_rect_end = p_rect.position + p_rect.size; - Point2 end = position + size; + Point2i p_rect_end = p_rect.position + p_rect.size; + Point2i end = position + size; new_rect.size.x = (int)(MIN(p_rect_end.x, end.x) - new_rect.position.x); new_rect.size.y = (int)(MIN(p_rect_end.y, end.y) - new_rect.position.y); @@ -311,17 +400,21 @@ struct Rect2i { new_rect.size = new_rect.size - new_rect.position; //make relative again return new_rect; - }; - bool has_point(const Point2 &p_point) const { - if (p_point.x < position.x) + } + bool has_point(const Point2i &p_point) const { + if (p_point.x < position.x) { return false; - if (p_point.y < position.y) + } + if (p_point.y < position.y) { return false; + } - if (p_point.x >= (position.x + size.x)) + if (p_point.x >= (position.x + size.x)) { return false; - if (p_point.y >= (position.y + size.y)) + } + if (p_point.y >= (position.y + size.y)) { return false; + } return true; } @@ -329,27 +422,29 @@ struct Rect2i { bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } - Rect2i grow(int p_by) const { - + Rect2i grow(int p_amount) const { Rect2i g = *this; - g.position.x -= p_by; - g.position.y -= p_by; - g.size.width += p_by * 2; - g.size.height += p_by * 2; + g.position.x -= p_amount; + g.position.y -= p_amount; + g.size.width += p_amount * 2; + g.size.height += p_amount * 2; return g; } - inline Rect2i grow_margin(Margin p_margin, int p_amount) const { + inline Rect2i grow_side(Side p_side, int p_amount) const { Rect2i g = *this; - g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, - (MARGIN_TOP == p_margin) ? p_amount : 0, - (MARGIN_RIGHT == p_margin) ? p_amount : 0, - (MARGIN_BOTTOM == p_margin) ? p_amount : 0); + g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0, + (SIDE_TOP == p_side) ? p_amount : 0, + (SIDE_RIGHT == p_side) ? p_amount : 0, + (SIDE_BOTTOM == p_side) ? p_amount : 0); return g; } - inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const { + inline Rect2i grow_side_bind(uint32_t p_side, int p_amount) const { + return grow_side(Side(p_side), p_amount); + } + inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const { Rect2i g = *this; g.position.x -= p_left; g.position.y -= p_top; @@ -360,49 +455,59 @@ struct Rect2i { } _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const { - Rect2i r = *this; r.expand_to(p_vector); return r; } inline void expand_to(const Point2i &p_vector) { - Point2i begin = position; Point2i end = position + size; - if (p_vector.x < begin.x) + if (p_vector.x < begin.x) { begin.x = p_vector.x; - if (p_vector.y < begin.y) + } + if (p_vector.y < begin.y) { begin.y = p_vector.y; + } - if (p_vector.x > end.x) + if (p_vector.x > end.x) { end.x = p_vector.x; - if (p_vector.y > end.y) + } + if (p_vector.y > end.y) { end.y = p_vector.y; + } position = begin; size = end - begin; } _FORCE_INLINE_ Rect2i abs() const { - return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); } + _FORCE_INLINE_ void set_end(const Vector2i &p_end) { + size = p_end - position; + } + + _FORCE_INLINE_ Vector2i get_end() const { + return position + size; + } + operator String() const { return String(position) + ", " + String(size); } operator Rect2() const { return Rect2(position, size); } + + Rect2i() {} Rect2i(const Rect2 &p_r2) : position(p_r2.position), size(p_r2.size) { } - Rect2i() {} Rect2i(int p_x, int p_y, int p_width, int p_height) : - position(Point2(p_x, p_y)), - size(Size2(p_width, p_height)) { + position(Point2i(p_x, p_y)), + size(Size2i(p_width, p_height)) { } - Rect2i(const Point2 &p_pos, const Size2 &p_size) : + Rect2i(const Point2i &p_pos, const Size2i &p_size) : position(p_pos), size(p_size) { } diff --git a/core/math/transform.cpp b/core/math/transform.cpp index 82e4005d3e..fab5d124fa 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,23 +32,20 @@ #include "core/math/math_funcs.h" #include "core/os/copymem.h" -#include "core/print_string.h" +#include "core/string/print_string.h" void Transform::affine_invert() { - basis.invert(); origin = basis.xform(-origin); } Transform Transform::affine_inverse() const { - Transform ret = *this; ret.affine_invert(); return ret; } void Transform::invert() { - basis.transpose(); origin = basis.xform(-origin); } @@ -62,22 +59,18 @@ Transform Transform::inverse() const { } void Transform::rotate(const Vector3 &p_axis, real_t p_phi) { - *this = rotated(p_axis, p_phi); } Transform Transform::rotated(const Vector3 &p_axis, real_t p_phi) const { - return Transform(Basis(p_axis, p_phi), Vector3()) * (*this); } void Transform::rotate_basis(const Vector3 &p_axis, real_t p_phi) { - basis.rotate(p_axis, p_phi); } Transform Transform::looking_at(const Vector3 &p_target, const Vector3 &p_up) const { - Transform t = *this; t.set_look_at(origin, p_target, p_up); return t; @@ -117,7 +110,6 @@ void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const } Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) const { - /* not sure if very "efficient" but good enough? */ Vector3 src_scale = basis.get_scale(); @@ -136,81 +128,70 @@ Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) } void Transform::scale(const Vector3 &p_scale) { - basis.scale(p_scale); origin *= p_scale; } Transform Transform::scaled(const Vector3 &p_scale) const { - Transform t = *this; t.scale(p_scale); return t; } void Transform::scale_basis(const Vector3 &p_scale) { - basis.scale(p_scale); } void Transform::translate(real_t p_tx, real_t p_ty, real_t p_tz) { translate(Vector3(p_tx, p_ty, p_tz)); } -void Transform::translate(const Vector3 &p_translation) { +void Transform::translate(const Vector3 &p_translation) { for (int i = 0; i < 3; i++) { origin[i] += basis[i].dot(p_translation); } } Transform Transform::translated(const Vector3 &p_translation) const { - Transform t = *this; t.translate(p_translation); return t; } void Transform::orthonormalize() { - basis.orthonormalize(); } Transform Transform::orthonormalized() const { - Transform _copy = *this; _copy.orthonormalize(); return _copy; } bool Transform::is_equal_approx(const Transform &p_transform) const { - return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin); } bool Transform::operator==(const Transform &p_transform) const { - return (basis == p_transform.basis && origin == p_transform.origin); } -bool Transform::operator!=(const Transform &p_transform) const { +bool Transform::operator!=(const Transform &p_transform) const { return (basis != p_transform.basis || origin != p_transform.origin); } void Transform::operator*=(const Transform &p_transform) { - origin = xform(p_transform.origin); basis *= p_transform.basis; } Transform Transform::operator*(const Transform &p_transform) const { - Transform t = *this; t *= p_transform; return t; } Transform::operator String() const { - return basis.operator String() + " - " + origin.operator String(); } @@ -219,6 +200,13 @@ Transform::Transform(const Basis &p_basis, const Vector3 &p_origin) : origin(p_origin) { } +Transform::Transform(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin) : + origin(p_origin) { + basis.set_axis(0, p_x); + basis.set_axis(1, p_y); + basis.set_axis(2, p_z); +} + Transform::Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz) { basis = Basis(xx, xy, xz, yx, yy, yz, zx, zy, zz); origin = Vector3(ox, oy, oz); diff --git a/core/math/transform.h b/core/math/transform.h index c6e3be4c70..60da6f5593 100644 --- a/core/math/transform.h +++ b/core/math/transform.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -92,14 +92,12 @@ public: Transform interpolate_with(const Transform &p_transform, real_t p_c) const; _FORCE_INLINE_ Transform inverse_xform(const Transform &t) const { - Vector3 v = t.origin - origin; return Transform(basis.transpose_xform(t.basis), basis.xform(v)); } void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz) { - basis.set(xx, xy, xz, yx, yy, yz, zx, zy, zz); origin.x = tx; origin.y = ty; @@ -108,20 +106,20 @@ public: operator String() const; - Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz); - Transform(const Basis &p_basis, const Vector3 &p_origin = Vector3()); Transform() {} + Transform(const Basis &p_basis, const Vector3 &p_origin = Vector3()); + Transform(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin); + Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz); }; _FORCE_INLINE_ Vector3 Transform::xform(const Vector3 &p_vector) const { - return Vector3( basis[0].dot(p_vector) + origin.x, basis[1].dot(p_vector) + origin.y, basis[2].dot(p_vector) + origin.z); } -_FORCE_INLINE_ Vector3 Transform::xform_inv(const Vector3 &p_vector) const { +_FORCE_INLINE_ Vector3 Transform::xform_inv(const Vector3 &p_vector) const { Vector3 v = p_vector - origin; return Vector3( @@ -131,7 +129,6 @@ _FORCE_INLINE_ Vector3 Transform::xform_inv(const Vector3 &p_vector) const { } _FORCE_INLINE_ Plane Transform::xform(const Plane &p_plane) const { - Vector3 point = p_plane.normal * p_plane.d; Vector3 point_dir = point + p_plane.normal; point = xform(point); @@ -143,12 +140,12 @@ _FORCE_INLINE_ Plane Transform::xform(const Plane &p_plane) const { return Plane(normal, d); } -_FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const { +_FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const { Vector3 point = p_plane.normal * p_plane.d; Vector3 point_dir = point + p_plane.normal; - xform_inv(point); - xform_inv(point_dir); + point = xform_inv(point); + point_dir = xform_inv(point_dir); Vector3 normal = point_dir - point; normal.normalize(); @@ -158,7 +155,6 @@ _FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const { } _FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const { - /* http://dev.theomader.com/transform-bounding-boxes/ */ Vector3 min = p_aabb.position; Vector3 max = p_aabb.position + p_aabb.size; @@ -184,7 +180,6 @@ _FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const { } _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const { - /* define vertices */ Vector3 vertices[8] = { Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z), @@ -202,7 +197,6 @@ _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const { ret.position = xform_inv(vertices[0]); for (int i = 1; i < 8; i++) { - ret.expand_to(xform_inv(vertices[i])); } @@ -210,7 +204,6 @@ _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const { } Vector<Vector3> Transform::xform(const Vector<Vector3> &p_array) const { - Vector<Vector3> array; array.resize(p_array.size()); @@ -224,7 +217,6 @@ Vector<Vector3> Transform::xform(const Vector<Vector3> &p_array) const { } Vector<Vector3> Transform::xform_inv(const Vector<Vector3> &p_array) const { - Vector<Vector3> array; array.resize(p_array.size()); diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index ed95baa233..4a521b96ae 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -38,14 +38,12 @@ void Transform2D::invert() { } Transform2D Transform2D::inverse() const { - Transform2D inv = *this; inv.invert(); return inv; } void Transform2D::affine_invert() { - real_t det = basis_determinant(); #ifdef MATH_CHECKS ERR_FAIL_COND(det == 0); @@ -60,7 +58,6 @@ void Transform2D::affine_invert() { } Transform2D Transform2D::affine_inverse() const { - Transform2D inv = *this; inv.affine_invert(); return inv; @@ -71,24 +68,17 @@ void Transform2D::rotate(real_t p_phi) { } real_t Transform2D::get_skew() const { - real_t det = basis_determinant(); return Math::acos(elements[0].normalized().dot(SGN(det) * elements[1].normalized())) - Math_PI * 0.5; } void Transform2D::set_skew(float p_angle) { - real_t det = basis_determinant(); elements[1] = SGN(det) * elements[0].rotated((Math_PI * 0.5 + p_angle)).normalized() * elements[1].length(); } real_t Transform2D::get_rotation() const { - real_t det = basis_determinant(); - Transform2D m = orthonormalized(); - if (det < 0) { - m.scale_basis(Size2(1, -1)); // convention to separate rotation and reflection for 2D is to absorb a flip along y into scaling. - } - return Math::atan2(m[0].y, m[0].x); + return Math::atan2(elements[0].y, elements[0].x); } void Transform2D::set_rotation(real_t p_rot) { @@ -103,7 +93,6 @@ void Transform2D::set_rotation(real_t p_rot) { } Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) { - real_t cr = Math::cos(p_rot); real_t sr = Math::sin(p_rot); elements[0][0] = cr; @@ -129,24 +118,23 @@ void Transform2D::scale(const Size2 &p_scale) { scale_basis(p_scale); elements[2] *= p_scale; } -void Transform2D::scale_basis(const Size2 &p_scale) { +void Transform2D::scale_basis(const Size2 &p_scale) { elements[0][0] *= p_scale.x; elements[0][1] *= p_scale.y; elements[1][0] *= p_scale.x; elements[1][1] *= p_scale.y; } -void Transform2D::translate(real_t p_tx, real_t p_ty) { +void Transform2D::translate(real_t p_tx, real_t p_ty) { translate(Vector2(p_tx, p_ty)); } -void Transform2D::translate(const Vector2 &p_translation) { +void Transform2D::translate(const Vector2 &p_translation) { elements[2] += basis_xform(p_translation); } void Transform2D::orthonormalize() { - // Gram-Schmidt Process Vector2 x = elements[0]; @@ -161,39 +149,36 @@ void Transform2D::orthonormalize() { } Transform2D Transform2D::orthonormalized() const { - Transform2D on = *this; on.orthonormalize(); return on; } bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { - return elements[0].is_equal_approx(p_transform.elements[0]) && elements[1].is_equal_approx(p_transform.elements[1]) && elements[2].is_equal_approx(p_transform.elements[2]); } bool Transform2D::operator==(const Transform2D &p_transform) const { - for (int i = 0; i < 3; i++) { - if (elements[i] != p_transform.elements[i]) + if (elements[i] != p_transform.elements[i]) { return false; + } } return true; } bool Transform2D::operator!=(const Transform2D &p_transform) const { - for (int i = 0; i < 3; i++) { - if (elements[i] != p_transform.elements[i]) + if (elements[i] != p_transform.elements[i]) { return true; + } } return false; } void Transform2D::operator*=(const Transform2D &p_transform) { - elements[2] = xform(p_transform.elements[2]); real_t x0, x1, y0, y1; @@ -210,54 +195,46 @@ void Transform2D::operator*=(const Transform2D &p_transform) { } Transform2D Transform2D::operator*(const Transform2D &p_transform) const { - Transform2D t = *this; t *= p_transform; return t; } Transform2D Transform2D::scaled(const Size2 &p_scale) const { - Transform2D copy = *this; copy.scale(p_scale); return copy; } Transform2D Transform2D::basis_scaled(const Size2 &p_scale) const { - Transform2D copy = *this; copy.scale_basis(p_scale); return copy; } Transform2D Transform2D::untranslated() const { - Transform2D copy = *this; copy.elements[2] = Vector2(); return copy; } Transform2D Transform2D::translated(const Vector2 &p_offset) const { - Transform2D copy = *this; copy.translate(p_offset); return copy; } Transform2D Transform2D::rotated(real_t p_phi) const { - Transform2D copy = *this; copy.rotate(p_phi); return copy; } real_t Transform2D::basis_determinant() const { - return elements[0].x * elements[1].y - elements[0].y * elements[1].x; } Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t p_c) const { - //extract parameters Vector2 p1 = get_origin(); Vector2 p2 = p_transform.get_origin(); @@ -274,7 +251,7 @@ Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t real_t dot = v1.dot(v2); - dot = (dot < -1.0) ? -1.0 : ((dot > 1.0) ? 1.0 : dot); //clamp dot to [-1,1] + dot = CLAMP(dot, -1.0, 1.0); Vector2 v; @@ -293,6 +270,5 @@ Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t } Transform2D::operator String() const { - return String(String() + elements[0] + ", " + elements[1] + ", " + elements[2]); } diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 459ceed7a9..327d0f244f 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -120,7 +120,6 @@ struct Transform2D { operator String() const; Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { - elements[0][0] = xx; elements[0][1] = xy; elements[1][0] = yx; @@ -129,6 +128,12 @@ struct Transform2D { elements[2][1] = oy; } + Transform2D(const Vector2 &p_x, const Vector2 &p_y, const Vector2 &p_origin) { + elements[0] = p_x; + elements[1] = p_y; + elements[2] = p_origin; + } + Transform2D(real_t p_rot, const Vector2 &p_pos); Transform2D() { elements[0][0] = 1.0; @@ -137,36 +142,33 @@ struct Transform2D { }; Vector2 Transform2D::basis_xform(const Vector2 &p_vec) const { - return Vector2( tdotx(p_vec), tdoty(p_vec)); } Vector2 Transform2D::basis_xform_inv(const Vector2 &p_vec) const { - return Vector2( elements[0].dot(p_vec), elements[1].dot(p_vec)); } Vector2 Transform2D::xform(const Vector2 &p_vec) const { - return Vector2( tdotx(p_vec), tdoty(p_vec)) + elements[2]; } -Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { +Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { Vector2 v = p_vec - elements[2]; return Vector2( elements[0].dot(v), elements[1].dot(v)); } -Rect2 Transform2D::xform(const Rect2 &p_rect) const { +Rect2 Transform2D::xform(const Rect2 &p_rect) const { Vector2 x = elements[0] * p_rect.size.x; Vector2 y = elements[1] * p_rect.size.y; Vector2 pos = xform(p_rect.position); @@ -180,7 +182,6 @@ Rect2 Transform2D::xform(const Rect2 &p_rect) const { } void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { - elements[0][0] = Math::cos(p_rot) * p_scale.x; elements[1][1] = Math::cos(p_rot) * p_scale.y; elements[1][0] = -Math::sin(p_rot) * p_scale.y; @@ -188,7 +189,6 @@ void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { } void Transform2D::set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew) { - elements[0][0] = Math::cos(p_rot) * p_scale.x; elements[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; elements[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; @@ -196,7 +196,6 @@ void Transform2D::set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale } Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { - Vector2 ends[4] = { xform_inv(p_rect.position), xform_inv(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), @@ -214,7 +213,6 @@ Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { } Vector<Vector2> Transform2D::xform(const Vector<Vector2> &p_array) const { - Vector<Vector2> array; array.resize(p_array.size()); @@ -228,7 +226,6 @@ Vector<Vector2> Transform2D::xform(const Vector<Vector2> &p_array) const { } Vector<Vector2> Transform2D::xform_inv(const Vector<Vector2> &p_array) const { - Vector<Vector2> array; array.resize(p_array.size()); diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index 5c66721b9d..23c0c686a2 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,33 +30,28 @@ #include "triangle_mesh.h" -#include "core/sort_array.h" +#include "core/templates/sort_array.h" int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) { - if (p_depth > max_depth) { max_depth = p_depth; } if (p_size == 1) { - return p_bb[p_from] - p_bvh; } else if (p_size == 0) { - return -1; } AABB aabb; aabb = p_bb[p_from]->aabb; for (int i = 1; i < p_size; i++) { - aabb.merge_with(p_bb[p_from + i]->aabb); } int li = aabb.get_longest_axis_index(); switch (li) { - case Vector3::AXIS_X: { SortArray<BVH *, BVHCmpX> sort_x; sort_x.nth_element(0, p_size, p_size / 2, &p_bb[p_from]); @@ -90,9 +85,9 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in } void TriangleMesh::get_indices(Vector<int> *r_triangles_indices) const { - - if (!valid) + if (!valid) { return; + } const int triangles_num = triangles.size(); @@ -110,7 +105,6 @@ void TriangleMesh::get_indices(Vector<int> *r_triangles_indices) const { } void TriangleMesh::create(const Vector<Vector3> &p_faces) { - valid = false; int fc = p_faces.size(); @@ -122,7 +116,6 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) { BVH *bw = bvh.ptrw(); { - //create faces and indices and base bvh //except for the Set for repeated triangles, everything //goes in-place. @@ -132,12 +125,10 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) { Map<Vector3, int> db; for (int i = 0; i < fc; i++) { - Triangle &f = w[i]; const Vector3 *v = &r[i * 3]; for (int j = 0; j < 3; j++) { - int vidx = -1; Vector3 vs = v[j].snapped(Vector3(0.0001, 0.0001, 0.0001)); Map<Vector3, int>::Element *E = db.find(vs); @@ -149,10 +140,11 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) { } f.indices[j] = vidx; - if (j == 0) + if (j == 0) { bw[i].aabb.position = vs; - else + } else { bw[i].aabb.expand_to(vs); + } } f.normal = Face3(r[i * 3 + 0], r[i * 3 + 1], r[i * 3 + 2]).get_plane().get_normal(); @@ -174,7 +166,6 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) { bwptrs.resize(fc); BVH **bwp = bwptrs.ptrw(); for (int i = 0; i < fc; i++) { - bwp[i] = &bw[i]; } @@ -188,7 +179,6 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) { } Vector3 TriangleMesh::get_area_normal(const AABB &p_aabb) const { - uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); enum { @@ -215,23 +205,18 @@ Vector3 TriangleMesh::get_area_normal(const AABB &p_aabb) const { stack[0] = pos; while (true) { - uint32_t node = stack[level] & NODE_IDX_MASK; const BVH &b = bvhptr[node]; bool done = false; switch (stack[level] >> VISITED_BIT_SHIFT) { case TEST_AABB_BIT: { - bool valid = b.aabb.intersects(p_aabb); if (!valid) { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - if (b.face_index >= 0) { - const Triangle &s = triangleptr[b.face_index]; n += s.normal; n_count++; @@ -239,49 +224,47 @@ Vector3 TriangleMesh::get_area_normal(const AABB &p_aabb) const { stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; } } continue; } case VISIT_LEFT_BIT: { - stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.left | TEST_AABB_BIT; level++; continue; } case VISIT_RIGHT_BIT: { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.right | TEST_AABB_BIT; level++; continue; } case VISIT_DONE_BIT: { - if (level == 0) { done = true; break; - } else + } else { level--; + } continue; } } - if (done) + if (done) { break; + } } - if (n_count > 0) + if (n_count > 0) { n /= n_count; + } return n; } bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const { - uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); enum { @@ -309,35 +292,28 @@ bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_en stack[0] = pos; while (true) { - uint32_t node = stack[level] & NODE_IDX_MASK; const BVH &b = bvhptr[node]; bool done = false; switch (stack[level] >> VISITED_BIT_SHIFT) { case TEST_AABB_BIT: { - bool valid = b.aabb.intersects_segment(p_begin, p_end); //bool valid = b.aabb.intersects(ray_aabb); if (!valid) { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - if (b.face_index >= 0) { - const Triangle &s = triangleptr[b.face_index]; Face3 f3(vertexptr[s.indices[0]], vertexptr[s.indices[1]], vertexptr[s.indices[2]]); Vector3 res; if (f3.intersects_segment(p_begin, p_end, &res)) { - real_t nd = n.dot(res); if (nd < d) { - d = nd; r_point = res; r_normal = f3.get_plane().get_normal(); @@ -348,52 +324,49 @@ bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_en stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; } } continue; } case VISIT_LEFT_BIT: { - stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.left | TEST_AABB_BIT; level++; continue; } case VISIT_RIGHT_BIT: { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.right | TEST_AABB_BIT; level++; continue; } case VISIT_DONE_BIT: { - if (level == 0) { done = true; break; - } else + } else { level--; + } continue; } } - if (done) + if (done) { break; + } } if (inters) { - - if (n.dot(r_normal) > 0) + if (n.dot(r_normal) > 0) { r_normal = -r_normal; + } } return inters; } bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const { - uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); enum { @@ -421,33 +394,26 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V stack[0] = pos; while (true) { - uint32_t node = stack[level] & NODE_IDX_MASK; const BVH &b = bvhptr[node]; bool done = false; switch (stack[level] >> VISITED_BIT_SHIFT) { case TEST_AABB_BIT: { - bool valid = b.aabb.intersects_ray(p_begin, p_dir); if (!valid) { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - if (b.face_index >= 0) { - const Triangle &s = triangleptr[b.face_index]; Face3 f3(vertexptr[s.indices[0]], vertexptr[s.indices[1]], vertexptr[s.indices[2]]); Vector3 res; if (f3.intersects_ray(p_begin, p_dir, &res)) { - real_t nd = n.dot(res); if (nd < d) { - d = nd; r_point = res; r_normal = f3.get_plane().get_normal(); @@ -458,45 +424,43 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; } } continue; } case VISIT_LEFT_BIT: { - stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.left | TEST_AABB_BIT; level++; continue; } case VISIT_RIGHT_BIT: { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.right | TEST_AABB_BIT; level++; continue; } case VISIT_DONE_BIT: { - if (level == 0) { done = true; break; - } else + } else { level--; + } continue; } } - if (done) + if (done) { break; + } } if (inters) { - - if (n.dot(r_normal) > 0) + if (n.dot(r_normal) > 0) { r_normal = -r_normal; + } } return inters; @@ -528,23 +492,18 @@ bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_cou stack[0] = pos; while (true) { - uint32_t node = stack[level] & NODE_IDX_MASK; const BVH &b = bvhptr[node]; bool done = false; switch (stack[level] >> VISITED_BIT_SHIFT) { case TEST_AABB_BIT: { - bool valid = b.aabb.intersects_convex_shape(p_planes, p_plane_count, p_points, p_point_count); if (!valid) { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - if (b.face_index >= 0) { - const Triangle &s = triangleptr[b.face_index]; for (int j = 0; j < 3; ++j) { @@ -558,14 +517,18 @@ bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_cou if (p.intersects_segment(point, next_point, &res)) { bool inisde = true; for (int k = 0; k < p_plane_count; k++) { - if (k == i) continue; + if (k == i) { + continue; + } const Plane &pp = p_planes[k]; if (pp.is_point_over(res)) { inisde = false; break; } } - if (inisde) return true; + if (inisde) { + return true; + } } if (p.is_point_over(point)) { @@ -573,45 +536,45 @@ bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_cou break; } } - if (over) return true; + if (over) { + return true; + } } stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; } } continue; } case VISIT_LEFT_BIT: { - stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.left | TEST_AABB_BIT; level++; continue; } case VISIT_RIGHT_BIT: { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.right | TEST_AABB_BIT; level++; continue; } case VISIT_DONE_BIT: { - if (level == 0) { done = true; break; - } else + } else { level--; + } continue; } } - if (done) + if (done) { break; + } } return false; @@ -643,84 +606,81 @@ bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count, stack[0] = pos; while (true) { - uint32_t node = stack[level] & NODE_IDX_MASK; const BVH &b = bvhptr[node]; bool done = false; switch (stack[level] >> VISITED_BIT_SHIFT) { case TEST_AABB_BIT: { - bool intersects = scale.xform(b.aabb).intersects_convex_shape(p_planes, p_plane_count, p_points, p_point_count); - if (!intersects) return false; + if (!intersects) { + return false; + } bool inside = scale.xform(b.aabb).inside_convex_shape(p_planes, p_plane_count); if (inside) { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - if (b.face_index >= 0) { const Triangle &s = triangleptr[b.face_index]; for (int j = 0; j < 3; ++j) { Vector3 point = scale.xform(vertexptr[s.indices[j]]); for (int i = 0; i < p_plane_count; i++) { const Plane &p = p_planes[i]; - if (p.is_point_over(point)) return false; + if (p.is_point_over(point)) { + return false; + } } } stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; } else { - stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; } } continue; } case VISIT_LEFT_BIT: { - stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.left | TEST_AABB_BIT; level++; continue; } case VISIT_RIGHT_BIT: { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; stack[level + 1] = b.right | TEST_AABB_BIT; level++; continue; } case VISIT_DONE_BIT: { - if (level == 0) { done = true; break; - } else + } else { level--; + } continue; } } - if (done) + if (done) { break; + } } return true; } bool TriangleMesh::is_valid() const { - return valid; } Vector<Face3> TriangleMesh::get_faces() const { - - if (!valid) + if (!valid) { return Vector<Face3>(); + } Vector<Face3> faces; int ts = triangles.size(); @@ -740,7 +700,6 @@ Vector<Face3> TriangleMesh::get_faces() const { } TriangleMesh::TriangleMesh() { - valid = false; max_depth = 0; } diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index 64704477cc..1d1dbc114b 100644 --- a/core/math/triangle_mesh.h +++ b/core/math/triangle_mesh.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,14 +32,12 @@ #define TRIANGLE_MESH_H #include "core/math/face3.h" -#include "core/reference.h" +#include "core/object/reference.h" class TriangleMesh : public Reference { - GDCLASS(TriangleMesh, Reference); struct Triangle { - Vector3 normal; int indices[3]; }; @@ -48,7 +46,6 @@ class TriangleMesh : public Reference { Vector<Vector3> vertices; struct BVH { - AABB aabb; Vector3 center; //used for sorting int left; @@ -58,24 +55,18 @@ class TriangleMesh : public Reference { }; struct BVHCmpX { - bool operator()(const BVH *p_left, const BVH *p_right) const { - return p_left->center.x < p_right->center.x; } }; struct BVHCmpY { - bool operator()(const BVH *p_left, const BVH *p_right) const { - return p_left->center.y < p_right->center.y; } }; struct BVHCmpZ { - bool operator()(const BVH *p_left, const BVH *p_right) const { - return p_left->center.z < p_right->center.z; } }; diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index cbcb232745..0047c0705d 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,6 @@ #include "triangulate.h" real_t Triangulate::get_area(const Vector<Vector2> &contour) { - int n = contour.size(); const Vector2 *c = &contour[0]; @@ -80,7 +79,7 @@ bool Triangulate::is_inside_triangle(real_t Ax, real_t Ay, } else { return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)); } -}; +} bool Triangulate::snip(const Vector<Vector2> &p_contour, int u, int v, int w, int n, const Vector<int> &V, bool relaxed) { int p; @@ -103,13 +102,19 @@ bool Triangulate::snip(const Vector<Vector2> &p_contour, int u, int v, int w, in // To avoid that we allow zero-area triangles if all else failed. float threshold = relaxed ? -CMP_EPSILON : CMP_EPSILON; - if (threshold > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) return false; + if (threshold > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) { + return false; + } for (p = 0; p < n; p++) { - if ((p == u) || (p == v) || (p == w)) continue; + if ((p == u) || (p == v) || (p == w)) { + continue; + } Px = contour[V[p]].x; Py = contour[V[p]].y; - if (is_inside_triangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py, relaxed)) return false; + if (is_inside_triangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py, relaxed)) { + return false; + } } return true; @@ -119,19 +124,24 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul /* allocate and initialize list of Vertices in polygon */ int n = contour.size(); - if (n < 3) return false; + if (n < 3) { + return false; + } Vector<int> V; V.resize(n); /* we want a counter-clockwise polygon in V */ - if (0.0 < get_area(contour)) - for (int v = 0; v < n; v++) + if (0.0 < get_area(contour)) { + for (int v = 0; v < n; v++) { V.write[v] = v; - else - for (int v = 0; v < n; v++) + } + } else { + for (int v = 0; v < n; v++) { V.write[v] = (n - 1) - v; + } + } bool relaxed = false; @@ -161,11 +171,17 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul /* three consecutive vertices in current polygon, <u,v,w> */ int u = v; - if (nv <= u) u = 0; /* previous */ + if (nv <= u) { + u = 0; /* previous */ + } v = u + 1; - if (nv <= v) v = 0; /* new v */ + if (nv <= v) { + v = 0; /* new v */ + } int w = v + 1; - if (nv <= w) w = 0; /* next */ + if (nv <= w) { + w = 0; /* next */ + } if (snip(contour, u, v, w, nv, V, relaxed)) { int a, b, c, s, t; @@ -181,8 +197,9 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul result.push_back(c); /* remove v from remaining polygon */ - for (s = v, t = v + 1; t < nv; s++, t++) + for (s = v, t = v + 1; t < nv; s++, t++) { V.write[s] = V[t]; + } nv--; diff --git a/core/math/triangulate.h b/core/math/triangulate.h index c453b77ecf..55dc4e8e7d 100644 --- a/core/math/triangulate.h +++ b/core/math/triangulate.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index f46badd19e..496e29ebe4 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,25 +31,20 @@ #include "vector2.h" real_t Vector2::angle() const { - return Math::atan2(y, x); } real_t Vector2::length() const { - return Math::sqrt(x * x + y * y); } real_t Vector2::length_squared() const { - return x * x + y * y; } void Vector2::normalize() { - real_t l = x * x + y * y; if (l != 0) { - l = Math::sqrt(l); x /= l; y /= l; @@ -57,7 +52,6 @@ void Vector2::normalize() { } Vector2 Vector2::normalized() const { - Vector2 v = *this; v.normalize(); return v; @@ -69,52 +63,42 @@ bool Vector2::is_normalized() const { } real_t Vector2::distance_to(const Vector2 &p_vector2) const { - return Math::sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y)); } real_t Vector2::distance_squared_to(const Vector2 &p_vector2) const { - return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y); } real_t Vector2::angle_to(const Vector2 &p_vector2) const { - return Math::atan2(cross(p_vector2), dot(p_vector2)); } real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { - return Math::atan2(y - p_vector2.y, x - p_vector2.x); } real_t Vector2::dot(const Vector2 &p_other) const { - return x * p_other.x + y * p_other.y; } real_t Vector2::cross(const Vector2 &p_other) const { - return x * p_other.y - y * p_other.x; } Vector2 Vector2::sign() const { - return Vector2(SGN(x), SGN(y)); } Vector2 Vector2::floor() const { - return Vector2(Math::floor(x), Math::floor(y)); } Vector2 Vector2::ceil() const { - return Vector2(Math::ceil(x), Math::ceil(y)); } Vector2 Vector2::round() const { - return Vector2(Math::round(x), Math::round(y)); } @@ -134,23 +118,20 @@ Vector2 Vector2::posmodv(const Vector2 &p_modv) const { return Vector2(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y)); } -Vector2 Vector2::project(const Vector2 &p_b) const { - return p_b * (dot(p_b) / p_b.length_squared()); +Vector2 Vector2::project(const Vector2 &p_to) const { + return p_to * (dot(p_to) / p_to.length_squared()); } -Vector2 Vector2::snapped(const Vector2 &p_by) const { - +Vector2 Vector2::snapped(const Vector2 &p_step) const { return Vector2( - Math::stepify(x, p_by.x), - Math::stepify(y, p_by.y)); + Math::snapped(x, p_step.x), + Math::snapped(y, p_step.y)); } Vector2 Vector2::clamped(real_t p_len) const { - real_t l = length(); Vector2 v = *this; if (l > 0 && p_len < l) { - v /= l; v *= p_len; } @@ -158,14 +139,13 @@ Vector2 Vector2::clamped(real_t p_len) const { return v; } -Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const { - +Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const { Vector2 p0 = p_pre_a; Vector2 p1 = *this; Vector2 p2 = p_b; Vector2 p3 = p_post_b; - real_t t = p_t; + real_t t = p_weight; real_t t2 = t * t; real_t t3 = t2 * t; @@ -210,65 +190,70 @@ bool Vector2::is_equal_approx(const Vector2 &p_v) const { /* Vector2i */ Vector2i Vector2i::operator+(const Vector2i &p_v) const { - return Vector2i(x + p_v.x, y + p_v.y); } -void Vector2i::operator+=(const Vector2i &p_v) { +void Vector2i::operator+=(const Vector2i &p_v) { x += p_v.x; y += p_v.y; } -Vector2i Vector2i::operator-(const Vector2i &p_v) const { +Vector2i Vector2i::operator-(const Vector2i &p_v) const { return Vector2i(x - p_v.x, y - p_v.y); } -void Vector2i::operator-=(const Vector2i &p_v) { +void Vector2i::operator-=(const Vector2i &p_v) { x -= p_v.x; y -= p_v.y; } Vector2i Vector2i::operator*(const Vector2i &p_v1) const { - return Vector2i(x * p_v1.x, y * p_v1.y); -}; +} Vector2i Vector2i::operator*(const int &rvalue) const { - return Vector2i(x * rvalue, y * rvalue); -}; -void Vector2i::operator*=(const int &rvalue) { +} +void Vector2i::operator*=(const int &rvalue) { x *= rvalue; y *= rvalue; -}; +} Vector2i Vector2i::operator/(const Vector2i &p_v1) const { - return Vector2i(x / p_v1.x, y / p_v1.y); -}; +} Vector2i Vector2i::operator/(const int &rvalue) const { - return Vector2i(x / rvalue, y / rvalue); -}; +} void Vector2i::operator/=(const int &rvalue) { - x /= rvalue; y /= rvalue; -}; +} -Vector2i Vector2i::operator-() const { +Vector2i Vector2i::operator%(const Vector2i &p_v1) const { + return Vector2i(x % p_v1.x, y % p_v1.y); +} + +Vector2i Vector2i::operator%(const int &rvalue) const { + return Vector2i(x % rvalue, y % rvalue); +} +void Vector2i::operator%=(const int &rvalue) { + x %= rvalue; + y %= rvalue; +} + +Vector2i Vector2i::operator-() const { return Vector2i(-x, -y); } bool Vector2i::operator==(const Vector2i &p_vec2) const { - return x == p_vec2.x && y == p_vec2.y; } -bool Vector2i::operator!=(const Vector2i &p_vec2) const { +bool Vector2i::operator!=(const Vector2i &p_vec2) const { return x != p_vec2.x || y != p_vec2.y; } diff --git a/core/math/vector2.h b/core/math/vector2.h index c0057f2543..24795857a3 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,23 +32,22 @@ #define VECTOR2_H #include "core/math/math_funcs.h" -#include "core/ustring.h" +#include "core/string/ustring.h" struct Vector2i; struct Vector2 { - enum Axis { AXIS_X, AXIS_Y, }; union { - real_t x; + real_t x = 0; real_t width; }; union { - real_t y; + real_t y = 0; real_t height; }; @@ -66,25 +65,33 @@ struct Vector2 { real_t length() const; real_t length_squared() const; + Vector2 min(const Vector2 &p_vector2) const { + return Vector2(MIN(x, p_vector2.x), MIN(y, p_vector2.y)); + } + + Vector2 max(const Vector2 &p_vector2) const { + return Vector2(MAX(x, p_vector2.x), MAX(y, p_vector2.y)); + } + real_t distance_to(const Vector2 &p_vector2) const; real_t distance_squared_to(const Vector2 &p_vector2) const; real_t angle_to(const Vector2 &p_vector2) const; real_t angle_to_point(const Vector2 &p_vector2) const; - _FORCE_INLINE_ Vector2 direction_to(const Vector2 &p_b) const; + _FORCE_INLINE_ Vector2 direction_to(const Vector2 &p_to) const; real_t dot(const Vector2 &p_other) const; real_t cross(const Vector2 &p_other) const; Vector2 posmod(const real_t p_mod) const; Vector2 posmodv(const Vector2 &p_modv) const; - Vector2 project(const Vector2 &p_b) const; + Vector2 project(const Vector2 &p_to) const; Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; Vector2 clamped(real_t p_len) const; - _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_b, real_t p_t) const; - _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const; - Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const; + _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, real_t p_weight) const; + Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const; Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const; Vector2 slide(const Vector2 &p_normal) const; @@ -115,21 +122,19 @@ struct Vector2 { bool operator==(const Vector2 &p_vec2) const; bool operator!=(const Vector2 &p_vec2) const; - bool operator<(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } - bool operator>(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } - bool operator<=(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y <= p_vec2.y) : (x < p_vec2.x); } - bool operator>=(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y >= p_vec2.y) : (x > p_vec2.x); } + bool operator<(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator>(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y > p_vec2.y) : (x > p_vec2.x); } + bool operator<=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); } + bool operator>=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); } real_t angle() const; _FORCE_INLINE_ Vector2 abs() const { - return Vector2(Math::abs(x), Math::abs(y)); } Vector2 rotated(real_t p_by) const; - Vector2 tangent() const { - + Vector2 orthogonal() const { return Vector2(y, -x); } @@ -142,107 +147,108 @@ struct Vector2 { operator String() const { return String::num(x) + ", " + String::num(y); } + _FORCE_INLINE_ Vector2() {} _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) { x = p_x; y = p_y; } - _FORCE_INLINE_ Vector2() { x = y = 0; } }; _FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { - return p_vec - *this * (dot(p_vec) - p_d); } -_FORCE_INLINE_ Vector2 operator*(real_t p_scalar, const Vector2 &p_vec) { +_FORCE_INLINE_ Vector2 operator*(float p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; +} +_FORCE_INLINE_ Vector2 operator*(double p_scalar, const Vector2 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const { +_FORCE_INLINE_ Vector2 operator*(int32_t p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector2 operator*(int64_t p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; +} +_FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const { return Vector2(x + p_v.x, y + p_v.y); } -_FORCE_INLINE_ void Vector2::operator+=(const Vector2 &p_v) { +_FORCE_INLINE_ void Vector2::operator+=(const Vector2 &p_v) { x += p_v.x; y += p_v.y; } -_FORCE_INLINE_ Vector2 Vector2::operator-(const Vector2 &p_v) const { +_FORCE_INLINE_ Vector2 Vector2::operator-(const Vector2 &p_v) const { return Vector2(x - p_v.x, y - p_v.y); } -_FORCE_INLINE_ void Vector2::operator-=(const Vector2 &p_v) { +_FORCE_INLINE_ void Vector2::operator-=(const Vector2 &p_v) { x -= p_v.x; y -= p_v.y; } _FORCE_INLINE_ Vector2 Vector2::operator*(const Vector2 &p_v1) const { - return Vector2(x * p_v1.x, y * p_v1.y); -}; +} _FORCE_INLINE_ Vector2 Vector2::operator*(const real_t &rvalue) const { - return Vector2(x * rvalue, y * rvalue); -}; -_FORCE_INLINE_ void Vector2::operator*=(const real_t &rvalue) { +} +_FORCE_INLINE_ void Vector2::operator*=(const real_t &rvalue) { x *= rvalue; y *= rvalue; -}; +} _FORCE_INLINE_ Vector2 Vector2::operator/(const Vector2 &p_v1) const { - return Vector2(x / p_v1.x, y / p_v1.y); -}; +} _FORCE_INLINE_ Vector2 Vector2::operator/(const real_t &rvalue) const { - return Vector2(x / rvalue, y / rvalue); -}; +} _FORCE_INLINE_ void Vector2::operator/=(const real_t &rvalue) { - x /= rvalue; y /= rvalue; -}; +} _FORCE_INLINE_ Vector2 Vector2::operator-() const { - return Vector2(-x, -y); } _FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const { - return x == p_vec2.x && y == p_vec2.y; } -_FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { +_FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { return x != p_vec2.x || y != p_vec2.y; } -Vector2 Vector2::lerp(const Vector2 &p_b, real_t p_t) const { - +Vector2 Vector2::lerp(const Vector2 &p_to, real_t p_weight) const { Vector2 res = *this; - res.x += (p_t * (p_b.x - x)); - res.y += (p_t * (p_b.y - y)); + res.x += (p_weight * (p_to.x - x)); + res.y += (p_weight * (p_to.y - y)); return res; } -Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const { +Vector2 Vector2::slerp(const Vector2 &p_to, real_t p_weight) const { #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(!is_normalized(), Vector2(), "The start Vector2 must be normalized."); #endif - real_t theta = angle_to(p_b); - return rotated(theta * p_t); + real_t theta = angle_to(p_to); + return rotated(theta * p_weight); } -Vector2 Vector2::direction_to(const Vector2 &p_b) const { - Vector2 ret(p_b.x - x, p_b.y - y); +Vector2 Vector2::direction_to(const Vector2 &p_to) const { + Vector2 ret(p_to.x - x, p_to.y - y); ret.normalize(); return ret; } @@ -253,18 +259,17 @@ typedef Vector2 Point2; /* INTEGER STUFF */ struct Vector2i { - enum Axis { AXIS_X, AXIS_Y, }; union { - int x; + int x = 0; int width; }; union { - int y; + int y = 0; int height; }; @@ -285,11 +290,13 @@ struct Vector2i { void operator*=(const int &rvalue); Vector2i operator/(const Vector2i &p_v1) const; - Vector2i operator/(const int &rvalue) const; - void operator/=(const int &rvalue); + Vector2i operator%(const Vector2i &p_v1) const; + Vector2i operator%(const int &rvalue) const; + void operator%=(const int &rvalue); + Vector2i operator-() const; bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } @@ -307,6 +314,8 @@ struct Vector2i { operator String() const { return String::num(x) + ", " + String::num(y); } operator Vector2() const { return Vector2(x, y); } + + inline Vector2i() {} inline Vector2i(const Vector2 &p_vec2) { x = (int)p_vec2.x; y = (int)p_vec2.y; @@ -315,12 +324,24 @@ struct Vector2i { x = p_x; y = p_y; } - inline Vector2i() { - x = 0; - y = 0; - } }; +_FORCE_INLINE_ Vector2i operator*(const int32_t &p_scalar, const Vector2i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector2i operator*(const int64_t &p_scalar, const Vector2i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector2i operator*(const float &p_scalar, const Vector2i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector2i operator*(const double &p_scalar, const Vector2i &p_vector) { + return p_vector * p_scalar; +} + typedef Vector2i Size2i; typedef Vector2i Point2i; diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 353b2acd16..f0629d3db8 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,12 +33,10 @@ #include "core/math/basis.h" void Vector3::rotate(const Vector3 &p_axis, real_t p_phi) { - *this = Basis(p_axis, p_phi).xform(*this); } Vector3 Vector3::rotated(const Vector3 &p_axis, real_t p_phi) const { - Vector3 r = *this; r.rotate(p_axis, p_phi); return r; @@ -48,74 +46,39 @@ void Vector3::set_axis(int p_axis, real_t p_value) { ERR_FAIL_INDEX(p_axis, 3); coord[p_axis] = p_value; } -real_t Vector3::get_axis(int p_axis) const { +real_t Vector3::get_axis(int p_axis) const { ERR_FAIL_INDEX_V(p_axis, 3, 0); return operator[](p_axis); } int Vector3::min_axis() const { - return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2); } -int Vector3::max_axis() const { +int Vector3::max_axis() const { return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0); } -void Vector3::snap(Vector3 p_val) { - - x = Math::stepify(x, p_val.x); - y = Math::stepify(y, p_val.y); - z = Math::stepify(z, p_val.z); +void Vector3::snap(Vector3 p_step) { + x = Math::snapped(x, p_step.x); + y = Math::snapped(y, p_step.y); + z = Math::snapped(z, p_step.z); } -Vector3 Vector3::snapped(Vector3 p_val) const { +Vector3 Vector3::snapped(Vector3 p_step) const { Vector3 v = *this; - v.snap(p_val); + v.snap(p_step); return v; } -Vector3 Vector3::cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const { - - Vector3 p0 = p_pre_a; - Vector3 p1 = *this; - Vector3 p2 = p_b; - Vector3 p3 = p_post_b; - - { - //normalize - - real_t ab = p0.distance_to(p1); - real_t bc = p1.distance_to(p2); - real_t cd = p2.distance_to(p3); - - if (ab > 0) - p0 = p1 + (p0 - p1) * (bc / ab); - if (cd > 0) - p3 = p2 + (p3 - p2) * (bc / cd); - } - - real_t t = p_t; - real_t t2 = t * t; - real_t t3 = t2 * t; - - Vector3 out; - out = 0.5 * ((p1 * 2.0) + - (-p0 + p2) * t + - (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + - (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); - return out; -} - -Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const { - +Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const { Vector3 p0 = p_pre_a; Vector3 p1 = *this; Vector3 p2 = p_b; Vector3 p3 = p_post_b; - real_t t = p_t; + real_t t = p_weight; real_t t2 = t * t; real_t t3 = t2 * t; @@ -135,7 +98,6 @@ Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const { } Basis Vector3::outer(const Vector3 &p_b) const { - Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); @@ -150,11 +112,9 @@ Basis Vector3::to_diagonal_matrix() const { } bool Vector3::is_equal_approx(const Vector3 &p_v) const { - return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z); } Vector3::operator String() const { - return (rtos(x) + ", " + rtos(y) + ", " + rtos(z)); } diff --git a/core/math/vector3.h b/core/math/vector3.h index a5e9d09208..3fdb944729 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,12 +33,11 @@ #include "core/math/math_funcs.h" #include "core/math/vector3i.h" -#include "core/ustring.h" +#include "core/string/ustring.h" class Basis; struct Vector3 { - enum Axis { AXIS_X, AXIS_Y, @@ -52,16 +51,14 @@ struct Vector3 { real_t z; }; - real_t coord[3]; + real_t coord[3] = { 0 }; }; _FORCE_INLINE_ const real_t &operator[](int p_axis) const { - return coord[p_axis]; } _FORCE_INLINE_ real_t &operator[](int p_axis) { - return coord[p_axis]; } @@ -89,10 +86,9 @@ struct Vector3 { /* Static Methods between 2 vector3s */ - _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_b, real_t p_t) const; - _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_b, real_t p_t) const; - Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const; - Vector3 cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const; + _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, real_t p_weight) const; + Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const; Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const; _FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const; @@ -106,15 +102,15 @@ struct Vector3 { _FORCE_INLINE_ Vector3 ceil() const; _FORCE_INLINE_ Vector3 round() const; - _FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const; - _FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const; + _FORCE_INLINE_ real_t distance_to(const Vector3 &p_to) const; + _FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_to) const; _FORCE_INLINE_ Vector3 posmod(const real_t p_mod) const; _FORCE_INLINE_ Vector3 posmodv(const Vector3 &p_modv) const; - _FORCE_INLINE_ Vector3 project(const Vector3 &p_b) const; + _FORCE_INLINE_ Vector3 project(const Vector3 &p_to) const; - _FORCE_INLINE_ real_t angle_to(const Vector3 &p_b) const; - _FORCE_INLINE_ Vector3 direction_to(const Vector3 &p_b) const; + _FORCE_INLINE_ real_t angle_to(const Vector3 &p_to) const; + _FORCE_INLINE_ Vector3 direction_to(const Vector3 &p_to) const; _FORCE_INLINE_ Vector3 slide(const Vector3 &p_normal) const; _FORCE_INLINE_ Vector3 bounce(const Vector3 &p_normal) const; @@ -152,22 +148,20 @@ struct Vector3 { return Vector3i(x, y, z); } + _FORCE_INLINE_ Vector3() {} _FORCE_INLINE_ Vector3(const Vector3i &p_ivec) { x = p_ivec.x; y = p_ivec.y; z = p_ivec.z; } - _FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) { x = p_x; y = p_y; z = p_z; } - _FORCE_INLINE_ Vector3() { x = y = z = 0; } }; Vector3 Vector3::cross(const Vector3 &p_b) const { - Vector3 ret( (y * p_b.z) - (z * p_b.y), (z * p_b.x) - (x * p_b.z), @@ -177,56 +171,47 @@ Vector3 Vector3::cross(const Vector3 &p_b) const { } real_t Vector3::dot(const Vector3 &p_b) const { - return x * p_b.x + y * p_b.y + z * p_b.z; } Vector3 Vector3::abs() const { - return Vector3(Math::abs(x), Math::abs(y), Math::abs(z)); } Vector3 Vector3::sign() const { - return Vector3(SGN(x), SGN(y), SGN(z)); } Vector3 Vector3::floor() const { - return Vector3(Math::floor(x), Math::floor(y), Math::floor(z)); } Vector3 Vector3::ceil() const { - return Vector3(Math::ceil(x), Math::ceil(y), Math::ceil(z)); } Vector3 Vector3::round() const { - return Vector3(Math::round(x), Math::round(y), Math::round(z)); } -Vector3 Vector3::lerp(const Vector3 &p_b, real_t p_t) const { - +Vector3 Vector3::lerp(const Vector3 &p_to, real_t p_weight) const { return Vector3( - x + (p_t * (p_b.x - x)), - y + (p_t * (p_b.y - y)), - z + (p_t * (p_b.z - z))); + x + (p_weight * (p_to.x - x)), + y + (p_weight * (p_to.y - y)), + z + (p_weight * (p_to.z - z))); } -Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const { - real_t theta = angle_to(p_b); - return rotated(cross(p_b).normalized(), theta * p_t); +Vector3 Vector3::slerp(const Vector3 &p_to, real_t p_weight) const { + real_t theta = angle_to(p_to); + return rotated(cross(p_to).normalized(), theta * p_weight); } -real_t Vector3::distance_to(const Vector3 &p_b) const { - - return (p_b - *this).length(); +real_t Vector3::distance_to(const Vector3 &p_to) const { + return (p_to - *this).length(); } -real_t Vector3::distance_squared_to(const Vector3 &p_b) const { - - return (p_b - *this).length_squared(); +real_t Vector3::distance_squared_to(const Vector3 &p_to) const { + return (p_to - *this).length_squared(); } Vector3 Vector3::posmod(const real_t p_mod) const { @@ -237,17 +222,16 @@ Vector3 Vector3::posmodv(const Vector3 &p_modv) const { return Vector3(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y), Math::fposmod(z, p_modv.z)); } -Vector3 Vector3::project(const Vector3 &p_b) const { - return p_b * (dot(p_b) / p_b.length_squared()); +Vector3 Vector3::project(const Vector3 &p_to) const { + return p_to * (dot(p_to) / p_to.length_squared()); } -real_t Vector3::angle_to(const Vector3 &p_b) const { - - return Math::atan2(cross(p_b).length(), dot(p_b)); +real_t Vector3::angle_to(const Vector3 &p_to) const { + return Math::atan2(cross(p_to).length(), dot(p_to)); } -Vector3 Vector3::direction_to(const Vector3 &p_b) const { - Vector3 ret(p_b.x - x, p_b.y - y, p_b.z - z); +Vector3 Vector3::direction_to(const Vector3 &p_to) const { + Vector3 ret(p_to.x - x, p_to.y - y, p_to.z - z); ret.normalize(); return ret; } @@ -255,7 +239,6 @@ Vector3 Vector3::direction_to(const Vector3 &p_b) const { /* Operators */ Vector3 &Vector3::operator+=(const Vector3 &p_v) { - x += p_v.x; y += p_v.y; z += p_v.z; @@ -263,36 +246,32 @@ Vector3 &Vector3::operator+=(const Vector3 &p_v) { } Vector3 Vector3::operator+(const Vector3 &p_v) const { - return Vector3(x + p_v.x, y + p_v.y, z + p_v.z); } Vector3 &Vector3::operator-=(const Vector3 &p_v) { - x -= p_v.x; y -= p_v.y; z -= p_v.z; return *this; } -Vector3 Vector3::operator-(const Vector3 &p_v) const { +Vector3 Vector3::operator-(const Vector3 &p_v) const { return Vector3(x - p_v.x, y - p_v.y, z - p_v.z); } Vector3 &Vector3::operator*=(const Vector3 &p_v) { - x *= p_v.x; y *= p_v.y; z *= p_v.z; return *this; } -Vector3 Vector3::operator*(const Vector3 &p_v) const { +Vector3 Vector3::operator*(const Vector3 &p_v) const { return Vector3(x * p_v.x, y * p_v.y, z * p_v.z); } Vector3 &Vector3::operator/=(const Vector3 &p_v) { - x /= p_v.x; y /= p_v.y; z /= p_v.z; @@ -300,12 +279,10 @@ Vector3 &Vector3::operator/=(const Vector3 &p_v) { } Vector3 Vector3::operator/(const Vector3 &p_v) const { - return Vector3(x / p_v.x, y / p_v.y, z / p_v.z); } Vector3 &Vector3::operator*=(real_t p_scalar) { - x *= p_scalar; y *= p_scalar; z *= p_scalar; @@ -313,17 +290,14 @@ Vector3 &Vector3::operator*=(real_t p_scalar) { } _FORCE_INLINE_ Vector3 operator*(real_t p_scalar, const Vector3 &p_vec) { - return p_vec * p_scalar; } Vector3 Vector3::operator*(real_t p_scalar) const { - return Vector3(x * p_scalar, y * p_scalar, z * p_scalar); } Vector3 &Vector3::operator/=(real_t p_scalar) { - x /= p_scalar; y /= p_scalar; z /= p_scalar; @@ -331,85 +305,78 @@ Vector3 &Vector3::operator/=(real_t p_scalar) { } Vector3 Vector3::operator/(real_t p_scalar) const { - return Vector3(x / p_scalar, y / p_scalar, z / p_scalar); } Vector3 Vector3::operator-() const { - return Vector3(-x, -y, -z); } bool Vector3::operator==(const Vector3 &p_v) const { - return x == p_v.x && y == p_v.y && z == p_v.z; } bool Vector3::operator!=(const Vector3 &p_v) const { - return x != p_v.x || y != p_v.y || z != p_v.z; } bool Vector3::operator<(const Vector3 &p_v) const { - - if (Math::is_equal_approx(x, p_v.x)) { - if (Math::is_equal_approx(y, p_v.y)) + if (x == p_v.x) { + if (y == p_v.y) { return z < p_v.z; - else + } else { return y < p_v.y; + } } else { return x < p_v.x; } } bool Vector3::operator>(const Vector3 &p_v) const { - - if (Math::is_equal_approx(x, p_v.x)) { - if (Math::is_equal_approx(y, p_v.y)) + if (x == p_v.x) { + if (y == p_v.y) { return z > p_v.z; - else + } else { return y > p_v.y; + } } else { return x > p_v.x; } } bool Vector3::operator<=(const Vector3 &p_v) const { - - if (Math::is_equal_approx(x, p_v.x)) { - if (Math::is_equal_approx(y, p_v.y)) + if (x == p_v.x) { + if (y == p_v.y) { return z <= p_v.z; - else + } else { return y < p_v.y; + } } else { return x < p_v.x; } } bool Vector3::operator>=(const Vector3 &p_v) const { - - if (Math::is_equal_approx(x, p_v.x)) { - if (Math::is_equal_approx(y, p_v.y)) + if (x == p_v.x) { + if (y == p_v.y) { return z >= p_v.z; - else + } else { return y > p_v.y; + } } else { return x > p_v.x; } } _FORCE_INLINE_ Vector3 vec3_cross(const Vector3 &p_a, const Vector3 &p_b) { - return p_a.cross(p_b); } _FORCE_INLINE_ real_t vec3_dot(const Vector3 &p_a, const Vector3 &p_b) { - return p_a.dot(p_b); } real_t Vector3::length() const { - real_t x2 = x * x; real_t y2 = y * y; real_t z2 = z * z; @@ -418,7 +385,6 @@ real_t Vector3::length() const { } real_t Vector3::length_squared() const { - real_t x2 = x * x; real_t y2 = y * y; real_t z2 = z * z; @@ -427,7 +393,6 @@ real_t Vector3::length_squared() const { } void Vector3::normalize() { - real_t lengthsq = length_squared(); if (lengthsq == 0) { x = y = z = 0; @@ -440,7 +405,6 @@ void Vector3::normalize() { } Vector3 Vector3::normalized() const { - Vector3 v = *this; v.normalize(); return v; @@ -452,12 +416,10 @@ bool Vector3::is_normalized() const { } Vector3 Vector3::inverse() const { - return Vector3(1.0 / x, 1.0 / y, 1.0 / z); } void Vector3::zero() { - x = y = z = 0; } diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp index 8a4ddf03b9..167fa3221d 100644 --- a/core/math/vector3i.cpp +++ b/core/math/vector3i.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -34,22 +34,20 @@ void Vector3i::set_axis(int p_axis, int32_t p_value) { ERR_FAIL_INDEX(p_axis, 3); coord[p_axis] = p_value; } -int32_t Vector3i::get_axis(int p_axis) const { +int32_t Vector3i::get_axis(int p_axis) const { ERR_FAIL_INDEX_V(p_axis, 3, 0); return operator[](p_axis); } int Vector3i::min_axis() const { - return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2); } -int Vector3i::max_axis() const { +int Vector3i::max_axis() const { return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0); } Vector3i::operator String() const { - return (itos(x) + ", " + itos(y) + ", " + itos(z)); } diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 6f9754d3b9..b0411fb62e 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,11 +31,10 @@ #ifndef VECTOR3I_H #define VECTOR3I_H +#include "core/string/ustring.h" #include "core/typedefs.h" -#include "core/ustring.h" struct Vector3i { - enum Axis { AXIS_X, AXIS_Y, @@ -49,16 +48,14 @@ struct Vector3i { int32_t z; }; - int32_t coord[3]; + int32_t coord[3] = { 0 }; }; _FORCE_INLINE_ const int32_t &operator[](int p_axis) const { - return coord[p_axis]; } _FORCE_INLINE_ int32_t &operator[](int p_axis) { - return coord[p_axis]; } @@ -83,11 +80,15 @@ struct Vector3i { _FORCE_INLINE_ Vector3i operator*(const Vector3i &p_v) const; _FORCE_INLINE_ Vector3i &operator/=(const Vector3i &p_v); _FORCE_INLINE_ Vector3i operator/(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator%=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator%(const Vector3i &p_v) const; _FORCE_INLINE_ Vector3i &operator*=(int32_t p_scalar); _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar) const; _FORCE_INLINE_ Vector3i &operator/=(int32_t p_scalar); _FORCE_INLINE_ Vector3i operator/(int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i &operator%=(int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator%(int32_t p_scalar) const; _FORCE_INLINE_ Vector3i operator-() const; @@ -100,28 +101,25 @@ struct Vector3i { operator String() const; + _FORCE_INLINE_ Vector3i() {} _FORCE_INLINE_ Vector3i(int32_t p_x, int32_t p_y, int32_t p_z) { x = p_x; y = p_y; z = p_z; } - _FORCE_INLINE_ Vector3i() { x = y = z = 0; } }; Vector3i Vector3i::abs() const { - return Vector3i(ABS(x), ABS(y), ABS(z)); } Vector3i Vector3i::sign() const { - return Vector3i(SGN(x), SGN(y), SGN(z)); } /* Operators */ Vector3i &Vector3i::operator+=(const Vector3i &p_v) { - x += p_v.x; y += p_v.y; z += p_v.z; @@ -129,36 +127,32 @@ Vector3i &Vector3i::operator+=(const Vector3i &p_v) { } Vector3i Vector3i::operator+(const Vector3i &p_v) const { - return Vector3i(x + p_v.x, y + p_v.y, z + p_v.z); } Vector3i &Vector3i::operator-=(const Vector3i &p_v) { - x -= p_v.x; y -= p_v.y; z -= p_v.z; return *this; } -Vector3i Vector3i::operator-(const Vector3i &p_v) const { +Vector3i Vector3i::operator-(const Vector3i &p_v) const { return Vector3i(x - p_v.x, y - p_v.y, z - p_v.z); } Vector3i &Vector3i::operator*=(const Vector3i &p_v) { - x *= p_v.x; y *= p_v.y; z *= p_v.z; return *this; } -Vector3i Vector3i::operator*(const Vector3i &p_v) const { +Vector3i Vector3i::operator*(const Vector3i &p_v) const { return Vector3i(x * p_v.x, y * p_v.y, z * p_v.z); } Vector3i &Vector3i::operator/=(const Vector3i &p_v) { - x /= p_v.x; y /= p_v.y; z /= p_v.z; @@ -166,12 +160,21 @@ Vector3i &Vector3i::operator/=(const Vector3i &p_v) { } Vector3i Vector3i::operator/(const Vector3i &p_v) const { - return Vector3i(x / p_v.x, y / p_v.y, z / p_v.z); } -Vector3i &Vector3i::operator*=(int32_t p_scalar) { +Vector3i &Vector3i::operator%=(const Vector3i &p_v) { + x %= p_v.x; + y %= p_v.y; + z %= p_v.z; + return *this; +} + +Vector3i Vector3i::operator%(const Vector3i &p_v) const { + return Vector3i(x % p_v.x, y % p_v.y, z % p_v.z); +} +Vector3i &Vector3i::operator*=(int32_t p_scalar) { x *= p_scalar; y *= p_scalar; z *= p_scalar; @@ -179,17 +182,14 @@ Vector3i &Vector3i::operator*=(int32_t p_scalar) { } _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar, const Vector3i &p_vec) { - return p_vec * p_scalar; } Vector3i Vector3i::operator*(int32_t p_scalar) const { - return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar); } Vector3i &Vector3i::operator/=(int32_t p_scalar) { - x /= p_scalar; y /= p_scalar; z /= p_scalar; @@ -197,75 +197,81 @@ Vector3i &Vector3i::operator/=(int32_t p_scalar) { } Vector3i Vector3i::operator/(int32_t p_scalar) const { - return Vector3i(x / p_scalar, y / p_scalar, z / p_scalar); } -Vector3i Vector3i::operator-() const { +Vector3i &Vector3i::operator%=(int32_t p_scalar) { + x %= p_scalar; + y %= p_scalar; + z %= p_scalar; + return *this; +} +Vector3i Vector3i::operator%(int32_t p_scalar) const { + return Vector3i(x % p_scalar, y % p_scalar, z % p_scalar); +} + +Vector3i Vector3i::operator-() const { return Vector3i(-x, -y, -z); } bool Vector3i::operator==(const Vector3i &p_v) const { - return (x == p_v.x && y == p_v.y && z == p_v.z); } bool Vector3i::operator!=(const Vector3i &p_v) const { - return (x != p_v.x || y != p_v.y || z != p_v.z); } bool Vector3i::operator<(const Vector3i &p_v) const { - if (x == p_v.x) { - if (y == p_v.y) + if (y == p_v.y) { return z < p_v.z; - else + } else { return y < p_v.y; + } } else { return x < p_v.x; } } bool Vector3i::operator>(const Vector3i &p_v) const { - if (x == p_v.x) { - if (y == p_v.y) + if (y == p_v.y) { return z > p_v.z; - else + } else { return y > p_v.y; + } } else { return x > p_v.x; } } bool Vector3i::operator<=(const Vector3i &p_v) const { - if (x == p_v.x) { - if (y == p_v.y) + if (y == p_v.y) { return z <= p_v.z; - else + } else { return y < p_v.y; + } } else { return x < p_v.x; } } bool Vector3i::operator>=(const Vector3i &p_v) const { - if (x == p_v.x) { - if (y == p_v.y) + if (y == p_v.y) { return z >= p_v.z; - else + } else { return y > p_v.y; + } } else { return x > p_v.x; } } void Vector3i::zero() { - x = y = z = 0; } diff --git a/core/method_bind.h b/core/method_bind.h deleted file mode 100644 index d39f107ba6..0000000000 --- a/core/method_bind.h +++ /dev/null @@ -1,409 +0,0 @@ -/*************************************************************************/ -/* method_bind.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 METHOD_BIND_H -#define METHOD_BIND_H - -#ifdef DEBUG_ENABLED -#define DEBUG_METHODS_ENABLED -#endif - -#include "core/list.h" -#include "core/method_ptrcall.h" -#include "core/object.h" -#include "core/type_info.h" -#include "core/typedefs.h" -#include "core/variant.h" - -#include <stdio.h> - -enum MethodFlags { - - METHOD_FLAG_NORMAL = 1, - METHOD_FLAG_EDITOR = 2, - METHOD_FLAG_NOSCRIPT = 4, - METHOD_FLAG_CONST = 8, - METHOD_FLAG_REVERSE = 16, // used for events - METHOD_FLAG_VIRTUAL = 32, - METHOD_FLAG_FROM_SCRIPT = 64, - METHOD_FLAG_VARARG = 128, - METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, -}; - -template <class T> -struct VariantCaster { - - static _FORCE_INLINE_ T cast(const Variant &p_variant) { - - return p_variant; - } -}; - -template <class T> -struct VariantCaster<T &> { - - static _FORCE_INLINE_ T cast(const Variant &p_variant) { - - return p_variant; - } -}; - -template <class T> -struct VariantCaster<const T &> { - - static _FORCE_INLINE_ T cast(const Variant &p_variant) { - - return p_variant; - } -}; - -#define _VC(m_idx) \ - (VariantCaster<P##m_idx>::cast((m_idx - 1) >= p_arg_count ? get_default_argument(m_idx - 1) : *p_args[m_idx - 1])) - -#ifdef PTRCALL_ENABLED - -#define VARIANT_ENUM_CAST(m_enum) \ - MAKE_ENUM_TYPE_INFO(m_enum) \ - template <> \ - struct VariantCaster<m_enum> { \ - \ - static _FORCE_INLINE_ m_enum cast(const Variant &p_variant) { \ - return (m_enum)p_variant.operator int(); \ - } \ - }; \ - template <> \ - struct PtrToArg<m_enum> { \ - _FORCE_INLINE_ static m_enum convert(const void *p_ptr) { \ - return m_enum(*reinterpret_cast<const int *>(p_ptr)); \ - } \ - _FORCE_INLINE_ static void encode(m_enum p_val, const void *p_ptr) { \ - *(int *)p_ptr = p_val; \ - } \ - }; - -#else - -#define VARIANT_ENUM_CAST(m_enum) \ - MAKE_ENUM_TYPE_INFO(m_enum) \ - template <> \ - struct VariantCaster<m_enum> { \ - \ - static _FORCE_INLINE_ m_enum cast(const Variant &p_variant) { \ - return (m_enum)p_variant.operator int(); \ - } \ - }; - -#endif - -// Object enum casts must go here -VARIANT_ENUM_CAST(Object::ConnectFlags); - -template <typename T> -struct VariantObjectClassChecker { - static _FORCE_INLINE_ bool check(const Variant &p_variant) { - return true; - } -}; - -template <> -struct VariantObjectClassChecker<Node *> { - static _FORCE_INLINE_ bool check(const Variant &p_variant) { - Object *obj = p_variant; - Node *node = p_variant; - return node || !obj; - } -}; - -template <> -struct VariantObjectClassChecker<Control *> { - static _FORCE_INLINE_ bool check(const Variant &p_variant) { - Object *obj = p_variant; - Control *control = p_variant; - return control || !obj; - } -}; - -#define CHECK_ARG(m_arg) \ - if ((m_arg - 1) < p_arg_count) { \ - Variant::Type argtype = get_argument_type(m_arg - 1); \ - if (!Variant::can_convert_strict(p_args[m_arg - 1]->get_type(), argtype) || \ - !VariantObjectClassChecker<P##m_arg>::check(*p_args[m_arg - 1])) { \ - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ - r_error.argument = m_arg - 1; \ - r_error.expected = argtype; \ - return Variant(); \ - } \ - } - -#define CHECK_NOARG(m_arg) \ - { \ - if (p_arg##m_arg.get_type() != Variant::NIL) { \ - if (r_argerror) *r_argerror = (m_arg - 1); \ - return CALL_ERROR_EXTRA_ARGUMENT; \ - } \ - } - -// some helpers - -VARIANT_ENUM_CAST(Vector3::Axis); - -VARIANT_ENUM_CAST(Error); -VARIANT_ENUM_CAST(Margin); -VARIANT_ENUM_CAST(Corner); -VARIANT_ENUM_CAST(Orientation); -VARIANT_ENUM_CAST(HAlign); -VARIANT_ENUM_CAST(VAlign); -VARIANT_ENUM_CAST(PropertyHint); -VARIANT_ENUM_CAST(PropertyUsageFlags); -VARIANT_ENUM_CAST(MethodFlags); -VARIANT_ENUM_CAST(Variant::Type); -VARIANT_ENUM_CAST(Variant::Operator); - -template <> -struct VariantCaster<wchar_t> { - static _FORCE_INLINE_ wchar_t cast(const Variant &p_variant) { - return (wchar_t)p_variant.operator int(); - } -}; -#ifdef PTRCALL_ENABLED -template <> -struct PtrToArg<wchar_t> { - _FORCE_INLINE_ static wchar_t convert(const void *p_ptr) { - return wchar_t(*reinterpret_cast<const int *>(p_ptr)); - } - _FORCE_INLINE_ static void encode(wchar_t p_val, const void *p_ptr) { - *(int *)p_ptr = p_val; - } -}; -#endif - -class MethodBind { - - int method_id; - uint32_t hint_flags; - StringName name; - Vector<Variant> default_arguments; - int default_argument_count; - int argument_count; - - bool _const; - bool _returns; - -protected: -#ifdef DEBUG_METHODS_ENABLED - Variant::Type *argument_types; - Vector<StringName> arg_names; -#endif - void _set_const(bool p_const); - void _set_returns(bool p_returns); -#ifdef DEBUG_METHODS_ENABLED - virtual Variant::Type _gen_argument_type(int p_arg) const = 0; - virtual PropertyInfo _gen_argument_type_info(int p_arg) const = 0; - void _generate_argument_types(int p_count); - -#endif - void set_argument_count(int p_count) { argument_count = p_count; } - -public: - Vector<Variant> get_default_arguments() const { return default_arguments; } - _FORCE_INLINE_ int get_default_argument_count() const { return default_argument_count; } - - _FORCE_INLINE_ Variant has_default_argument(int p_arg) const { - - int idx = argument_count - p_arg - 1; - - if (idx < 0 || idx >= default_arguments.size()) - return false; - else - return true; - } - - _FORCE_INLINE_ Variant get_default_argument(int p_arg) const { - - int idx = argument_count - p_arg - 1; - - if (idx < 0 || idx >= default_arguments.size()) - return Variant(); - else - return default_arguments[idx]; - } - -#ifdef DEBUG_METHODS_ENABLED - - _FORCE_INLINE_ Variant::Type get_argument_type(int p_argument) const { - - ERR_FAIL_COND_V(p_argument < -1 || p_argument > argument_count, Variant::NIL); - return argument_types[p_argument + 1]; - } - - PropertyInfo get_argument_info(int p_argument) const; - PropertyInfo get_return_info() const; - - void set_argument_names(const Vector<StringName> &p_names); //set by class, db, can't be inferred otherwise - Vector<StringName> get_argument_names() const; - - virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const = 0; - -#endif - void set_hint_flags(uint32_t p_hint) { hint_flags = p_hint; } - uint32_t get_hint_flags() const { return hint_flags | (is_const() ? METHOD_FLAG_CONST : 0) | (is_vararg() ? METHOD_FLAG_VARARG : 0); } - virtual String get_instance_class() const = 0; - - _FORCE_INLINE_ int get_argument_count() const { return argument_count; }; - - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) = 0; - -#ifdef PTRCALL_ENABLED - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) = 0; -#endif - - StringName get_name() const; - void set_name(const StringName &p_name); - _FORCE_INLINE_ int get_method_id() const { return method_id; } - _FORCE_INLINE_ bool is_const() const { return _const; } - _FORCE_INLINE_ bool has_return() const { return _returns; } - virtual bool is_vararg() const { return false; } - - void set_default_arguments(const Vector<Variant> &p_defargs); - - MethodBind(); - virtual ~MethodBind(); -}; - -template <class T> -class MethodBindVarArg : public MethodBind { -public: - typedef Variant (T::*NativeCall)(const Variant **, int, Callable::CallError &); - -protected: - NativeCall call_method; -#ifdef DEBUG_METHODS_ENABLED - - MethodInfo arguments; - -#endif -public: -#ifdef DEBUG_METHODS_ENABLED - - virtual PropertyInfo _gen_argument_type_info(int p_arg) const { - - if (p_arg < 0) { - return arguments.return_val; - } else if (p_arg < arguments.arguments.size()) { - return arguments.arguments[p_arg]; - } else { - return PropertyInfo(Variant::NIL, "arg_" + itos(p_arg), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); - } - } - - virtual Variant::Type _gen_argument_type(int p_arg) const { - return _gen_argument_type_info(p_arg).type; - } - - virtual GodotTypeInfo::Metadata get_argument_meta(int) const { - return GodotTypeInfo::METADATA_NONE; - } - -#else - - virtual Variant::Type _gen_argument_type(int p_arg) const { - return Variant::NIL; - } - -#endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - - T *instance = static_cast<T *>(p_object); - return (instance->*call_method)(p_args, p_arg_count, r_error); - } - - void set_method_info(const MethodInfo &p_info, bool p_return_nil_is_variant) { - - set_argument_count(p_info.arguments.size()); -#ifdef DEBUG_METHODS_ENABLED - Variant::Type *at = memnew_arr(Variant::Type, p_info.arguments.size() + 1); - at[0] = p_info.return_val.type; - if (p_info.arguments.size()) { - - Vector<StringName> names; - names.resize(p_info.arguments.size()); - for (int i = 0; i < p_info.arguments.size(); i++) { - - at[i + 1] = p_info.arguments[i].type; - names.write[i] = p_info.arguments[i].name; - } - - set_argument_names(names); - } - argument_types = at; - arguments = p_info; - if (p_return_nil_is_variant) { - arguments.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - } -#endif - } - -#ifdef PTRCALL_ENABLED - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { - ERR_FAIL(); //can't call - } //todo -#endif - - void set_method(NativeCall p_method) { call_method = p_method; } - virtual bool is_const() const { return false; } - virtual String get_instance_class() const { return T::get_class_static(); } - - virtual bool is_vararg() const { return true; } - - MethodBindVarArg() { - call_method = nullptr; - _set_returns(true); - } -}; - -template <class T> -MethodBind *create_vararg_method_bind(Variant (T::*p_method)(const Variant **, int, Callable::CallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { - - MethodBindVarArg<T> *a = memnew((MethodBindVarArg<T>)); - a->set_method(p_method); - a->set_method_info(p_info, p_return_nil_is_variant); - return a; -} - -/** This amazing hack is based on the FastDelegates theory */ - -// tale of an amazing hack.. // - -// if you declare a nonexistent class.. -class __UnexistingClass; - -#include "method_bind.gen.inc" - -#endif // METHOD_BIND_H diff --git a/core/object/SCsub b/core/object/SCsub new file mode 100644 index 0000000000..5d429960e5 --- /dev/null +++ b/core/object/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +env_object = env.Clone() + +env_object.add_source_files(env.core_sources, "*.cpp") diff --git a/core/callable_method_pointer.cpp b/core/object/callable_method_pointer.cpp index 8774af6add..2bfaef744d 100644 --- a/core/callable_method_pointer.cpp +++ b/core/object/callable_method_pointer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -48,7 +48,6 @@ bool CallableCustomMethodPointerBase::compare_equal(const CallableCustom *p_a, c } bool CallableCustomMethodPointerBase::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { - const CallableCustomMethodPointerBase *a = static_cast<const CallableCustomMethodPointerBase *>(p_a); const CallableCustomMethodPointerBase *b = static_cast<const CallableCustomMethodPointerBase *>(p_b); diff --git a/core/callable_method_pointer.h b/core/object/callable_method_pointer.h index fb809c2b44..115797a00c 100644 --- a/core/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,14 +31,14 @@ #ifndef CALLABLE_METHOD_POINTER_H #define CALLABLE_METHOD_POINTER_H -#include "core/callable.h" -#include "core/hashfuncs.h" -#include "core/object.h" +#include "core/object/object.h" #include "core/os/copymem.h" -#include "core/simple_type.h" +#include "core/templates/hashfuncs.h" +#include "core/templates/simple_type.h" +#include "core/variant/binder_common.h" +#include "core/variant/callable.h" class CallableCustomMethodPointerBase : public CallableCustom { - uint32_t *comp_ptr; uint32_t comp_size; uint32_t h; @@ -70,116 +70,39 @@ public: virtual uint32_t hash() const; }; -#ifdef DEBUG_METHODS_ENABLED - -template <class T> -struct VariantCasterAndValidate { - - static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { - Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; - if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = p_arg_idx; - r_error.expected = argtype; - } - - return VariantCaster<T>::cast(*p_args[p_arg_idx]); - } -}; - -template <class T> -struct VariantCasterAndValidate<T &> { - - static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { - Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; - if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = p_arg_idx; - r_error.expected = argtype; - } - - return VariantCaster<T>::cast(*p_args[p_arg_idx]); - } -}; - -template <class T> -struct VariantCasterAndValidate<const T &> { - - static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { - Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; - if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = p_arg_idx; - r_error.expected = argtype; - } - - return VariantCaster<T>::cast(*p_args[p_arg_idx]); - } -}; - -#endif // DEBUG_METHODS_ENABLED - -// GCC 8 raises "parameter 'p_args' set but not used" here, probably using a -// template version that does not have arguments and thus sees it unused, but -// obviously the template can be used for functions with and without them, and -// the optimizer will get rid of it anyway. -#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-but-set-parameter" -#endif - -template <class T, class... P, size_t... Is> -void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { - r_error.error = Callable::CallError::CALL_OK; - -#ifdef DEBUG_METHODS_ENABLED - (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...); -#else - (p_instance->*p_method)(VariantCaster<P>::cast(p_args[Is])...); -#endif -} - -#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - -template <class T, class... P> -void call_with_variant_args(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) { -#ifdef DEBUG_METHODS_ENABLED - if ((size_t)p_argcount > sizeof...(P)) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = sizeof...(P); - return; - } - - if ((size_t)p_argcount < sizeof...(P)) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = sizeof...(P); - return; - } -#endif - call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); -} - template <class T, class... P> class CallableCustomMethodPointer : public CallableCustomMethodPointerBase { - struct Data { T *instance; +#ifdef DEBUG_ENABLED + uint64_t object_id; +#endif void (T::*method)(P...); } data; public: - virtual ObjectID get_object() const { return data.instance->get_instance_id(); } + virtual ObjectID get_object() const { +#ifdef DEBUG_ENABLED + if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { + return ObjectID(); + } +#endif + return data.instance->get_instance_id(); + } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { - +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); +#endif call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); } CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) { zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; +#ifdef DEBUG_ENABLED + data.object_id = p_instance->get_instance_id(); +#endif data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } @@ -191,7 +114,6 @@ Callable create_custom_callable_function_pointer(T *p_instance, const char *p_func_text, #endif void (T::*p_method)(P...)) { - typedef CallableCustomMethodPointer<T, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); #ifdef DEBUG_METHODS_ENABLED @@ -202,68 +124,95 @@ Callable create_custom_callable_function_pointer(T *p_instance, // VERSION WITH RETURN -// GCC 8 raises "parameter 'p_args' set but not used" here, probably using a -// template version that does not have arguments and thus sees it unused, but -// obviously the template can be used for functions with and without them, and -// the optimizer will get rid of it anyway. -#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +template <class T, class R, class... P> +class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { + struct Data { + T *instance; +#ifdef DEBUG_ENABLED + uint64_t object_id; #endif + R(T::*method) + (P...); + } data; -template <class T, class R, class... P, size_t... Is> -void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) { - r_error.error = Callable::CallError::CALL_OK; +public: + virtual ObjectID get_object() const { +#ifdef DEBUG_ENABLED + if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { + return ObjectID(); + } +#endif + return data.instance->get_instance_id(); + } -#ifdef DEBUG_METHODS_ENABLED - r_ret = (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...); -#else - (p_instance->*p_method)(VariantCaster<P>::cast(p_args[Is])...); + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); #endif -} + call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } -#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop + CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { + zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes. + data.instance = p_instance; +#ifdef DEBUG_ENABLED + data.object_id = p_instance->get_instance_id(); #endif + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); + } +}; template <class T, class R, class... P> -void call_with_variant_args_ret(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { +Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED - if ((size_t)p_argcount > sizeof...(P)) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = sizeof...(P); - return; - } - - if ((size_t)p_argcount < sizeof...(P)) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = sizeof...(P); - return; - } + const char *p_func_text, #endif - call_with_variant_args_ret_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); + R (T::*p_method)(P...)) { + typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_instance, p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); } -template <class T, class R, class... P> -class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { +// CONST VERSION WITH RETURN +template <class T, class R, class... P> +class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase { struct Data { T *instance; +#ifdef DEBUG_ENABLED + uint64_t object_id; +#endif R(T::*method) - (P...); + (P...) const; } data; public: - virtual ObjectID get_object() const { return data.instance->get_instance_id(); } + virtual ObjectID get_object() const { +#ifdef DEBUG_ENABLED + if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { + return ObjectID(); + } +#endif + return data.instance->get_instance_id(); + } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { - - call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); +#endif + call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); } - CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { + CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) { zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; +#ifdef DEBUG_ENABLED + data.object_id = p_instance->get_instance_id(); +#endif data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } @@ -274,9 +223,8 @@ Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif - R (T::*p_method)(P...)) { - - typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise. + R (T::*p_method)(P...) const) { + typedef CallableCustomMethodPointerRetC<T, R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. diff --git a/core/class_db.cpp b/core/object/class_db.cpp index 5e49688e9b..6bc6d653d1 100644 --- a/core/class_db.cpp +++ b/core/object/class_db.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "class_db.h" -#include "core/engine.h" +#include "core/config/engine.h" #include "core/os/mutex.h" #include "core/version.h" @@ -40,14 +40,12 @@ #ifdef DEBUG_METHODS_ENABLED MethodDefinition D_METHOD(const char *p_name) { - MethodDefinition md; md.name = StaticCString::create(p_name); return md; } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.push_back(StaticCString::create(p_arg1)); @@ -55,7 +53,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1) { } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(2); @@ -65,7 +62,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(3); @@ -76,7 +72,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(4); @@ -88,7 +83,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(5); @@ -101,7 +95,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(6); @@ -115,7 +108,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(7); @@ -130,7 +122,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(8); @@ -146,7 +137,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(9); @@ -163,7 +153,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(10); @@ -181,7 +170,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(11); @@ -200,7 +188,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(12); @@ -220,7 +207,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ } MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13) { - MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(13); @@ -245,12 +231,10 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ ClassDB::APIType ClassDB::current_api = API_CORE; void ClassDB::set_current_api(APIType p_api) { - current_api = p_api; } ClassDB::APIType ClassDB::get_current_api() { - return current_api; } @@ -258,42 +242,34 @@ HashMap<StringName, ClassDB::ClassInfo> ClassDB::classes; HashMap<StringName, StringName> ClassDB::resource_base_extensions; HashMap<StringName, StringName> ClassDB::compat_classes; -ClassDB::ClassInfo::ClassInfo() { - - api = API_NONE; - class_ptr = nullptr; - creation_func = nullptr; - inherits_ptr = nullptr; - disabled = false; - exposed = false; -} - -ClassDB::ClassInfo::~ClassInfo() { -} - -bool ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inherits) { - - OBJTYPE_RLOCK; +bool ClassDB::_is_parent_class(const StringName &p_class, const StringName &p_inherits) { + if (!classes.has(p_class)) { + return false; + } StringName inherits = p_class; - while (inherits.operator String().length()) { - - if (inherits == p_inherits) + if (inherits == p_inherits) { return true; - inherits = get_parent_class(inherits); + } + inherits = _get_parent_class(inherits); } return false; } -void ClassDB::get_class_list(List<StringName> *p_classes) { +bool ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inherits) { + OBJTYPE_RLOCK; + + return _is_parent_class(p_class, p_inherits); +} + +void ClassDB::get_class_list(List<StringName> *p_classes) { OBJTYPE_RLOCK; const StringName *k = nullptr; while ((k = classes.next(k))) { - p_classes->push_back(*k); } @@ -301,43 +277,40 @@ void ClassDB::get_class_list(List<StringName> *p_classes) { } void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { - OBJTYPE_RLOCK; const StringName *k = nullptr; while ((k = classes.next(k))) { - - if (*k != p_class && is_parent_class(*k, p_class)) + if (*k != p_class && _is_parent_class(*k, p_class)) { p_classes->push_back(*k); + } } } void ClassDB::get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { - OBJTYPE_RLOCK; const StringName *k = nullptr; while ((k = classes.next(k))) { - - if (*k != p_class && get_parent_class(*k) == p_class) + if (*k != p_class && _get_parent_class(*k) == p_class) { p_classes->push_back(*k); + } } } StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) { - OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - if (!ti) + if (!ti) { return StringName(); + } return ti->inherits; } StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) { - if (classes.has(p_class)) { return p_class; } @@ -349,17 +322,19 @@ StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) return p_class; } -StringName ClassDB::get_parent_class(const StringName &p_class) { - - OBJTYPE_RLOCK; - +StringName ClassDB::_get_parent_class(const StringName &p_class) { ClassInfo *ti = classes.getptr(p_class); ERR_FAIL_COND_V_MSG(!ti, StringName(), "Cannot get class '" + String(p_class) + "'."); return ti->inherits; } -ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { +StringName ClassDB::get_parent_class(const StringName &p_class) { + OBJTYPE_RLOCK; + + return _get_parent_class(p_class); +} +ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -369,7 +344,6 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { } uint64_t ClassDB::get_api_hash(APIType p_api) { - OBJTYPE_RLOCK; #ifdef DEBUG_METHODS_ENABLED @@ -380,18 +354,17 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { const StringName *k = nullptr; while ((k = classes.next(k))) { - names.push_back(*k); } //must be alphabetically sorted for hash to compute names.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - ClassInfo *t = classes.getptr(E->get()); ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E->get()) + "'."); - if (t->api != p_api || !t->exposed) + if (t->api != p_api || !t->exposed) { continue; + } hash = hash_djb2_one_64(t->name.hash(), hash); hash = hash_djb2_one_64(t->inherits.hash(), hash); @@ -402,13 +375,13 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { k = nullptr; while ((k = t->method_map.next(k))) { - String name = k->operator String(); - ERR_CONTINUE(name.empty()); + ERR_CONTINUE(name.is_empty()); - if (name[0] == '_') + if (name[0] == '_') { continue; // Ignore non-virtual methods that start with an underscore + } snames.push_back(*k); } @@ -416,7 +389,6 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - MethodBind *mb = t->method_map[F->get()]; hash = hash_djb2_one_64(mb->get_name().hash(), hash); hash = hash_djb2_one_64(mb->get_argument_count(), hash); @@ -449,14 +421,12 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { k = nullptr; while ((k = t->constant_map.next(k))) { - snames.push_back(*k); } snames.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - hash = hash_djb2_one_64(F->get().hash(), hash); hash = hash_djb2_one_64(t->constant_map[F->get()], hash); } @@ -469,14 +439,12 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { k = nullptr; while ((k = t->signal_map.next(k))) { - snames.push_back(*k); } snames.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - MethodInfo &mi = t->signal_map[F->get()]; hash = hash_djb2_one_64(F->get().hash(), hash); for (int i = 0; i < mi.arguments.size(); i++) { @@ -492,14 +460,12 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { k = nullptr; while ((k = t->property_setget.next(k))) { - snames.push_back(*k); } snames.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - PropertySetGet *psg = t->property_setget.getptr(F->get()); ERR_FAIL_COND_V(!psg, 0); @@ -511,7 +477,6 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { //property list for (List<PropertyInfo>::Element *F = t->property_list.front(); F; F = F->next()) { - hash = hash_djb2_one_64(F->get().name.hash(), hash); hash = hash_djb2_one_64(F->get().type, hash); hash = hash_djb2_one_64(F->get().hint, hash); @@ -527,19 +492,16 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { } bool ClassDB::class_exists(const StringName &p_class) { - OBJTYPE_RLOCK; return classes.has(p_class); } void ClassDB::add_compatibility_class(const StringName &p_class, const StringName &p_fallback) { - OBJTYPE_WLOCK; compat_classes[p_class] = p_fallback; } Object *ClassDB::instance(const StringName &p_class) { - ClassInfo *ti; { OBJTYPE_RLOCK; @@ -561,8 +523,8 @@ Object *ClassDB::instance(const StringName &p_class) { #endif return ti->creation_func(); } -bool ClassDB::can_instance(const StringName &p_class) { +bool ClassDB::can_instance(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -576,7 +538,6 @@ bool ClassDB::can_instance(const StringName &p_class) { } void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) { - OBJTYPE_WLOCK; const StringName &name = p_class; @@ -590,7 +551,6 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit ti.api = current_api; if (ti.inherits) { - ERR_FAIL_COND(!classes.has(ti.inherits)); //it MUST be registered. ti.inherits_ptr = &classes[ti.inherits]; @@ -599,18 +559,39 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit } } -void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) { +#ifdef DEBUG_METHODS_ENABLED +static MethodInfo info_from_bind(MethodBind *p_method) { + MethodInfo minfo; + minfo.name = p_method->get_name(); + minfo.id = p_method->get_method_id(); + for (int i = 0; i < p_method->get_argument_count(); i++) { + minfo.arguments.push_back(p_method->get_argument_info(i)); + } + + minfo.return_val = p_method->get_return_info(); + minfo.flags = p_method->get_hint_flags(); + + for (int i = 0; i < p_method->get_argument_count(); i++) { + if (p_method->has_default_argument(i)) { + minfo.default_arguments.push_back(p_method->get_default_argument(i)); + } + } + + return minfo; +} +#endif + +void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); while (type) { - if (type->disabled) { - - if (p_no_inheritance) + if (p_no_inheritance) { break; + } type = type->inherits_ptr; continue; @@ -619,34 +600,16 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b #ifdef DEBUG_METHODS_ENABLED for (List<MethodInfo>::Element *E = type->virtual_methods.front(); E; E = E->next()) { - p_methods->push_back(E->get()); } for (List<StringName>::Element *E = type->method_order.front(); E; E = E->next()) { - - MethodBind *method = type->method_map.get(E->get()); - MethodInfo minfo; - minfo.name = E->get(); - minfo.id = method->get_method_id(); - - if (p_exclude_from_properties && type->methods_in_properties.has(minfo.name)) + if (p_exclude_from_properties && type->methods_in_properties.has(E->get())) { continue; - - for (int i = 0; i < method->get_argument_count(); i++) { - - //Variant::Type t=method->get_argument_type(i); - - minfo.arguments.push_back(method->get_argument_info(i)); } - minfo.return_val = method->get_return_info(); - minfo.flags = method->get_hint_flags(); - - for (int i = 0; i < method->get_argument_count(); i++) { - if (method->has_default_argument(i)) - minfo.default_arguments.push_back(method->get_default_argument(i)); - } + MethodBind *method = type->method_map.get(E->get()); + MethodInfo minfo = info_from_bind(method); p_methods->push_back(minfo); } @@ -656,7 +619,6 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b const StringName *K = nullptr; while ((K = type->method_map.next(K))) { - MethodBind *m = type->method_map[*K]; MethodInfo mi; mi.name = m->get_name(); @@ -665,31 +627,81 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b #endif - if (p_no_inheritance) + if (p_no_inheritance) { break; + } type = type->inherits_ptr; } } -MethodBind *ClassDB::get_method(StringName p_class, StringName p_name) { - +bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); while (type) { + if (type->disabled) { + if (p_no_inheritance) { + break; + } + type = type->inherits_ptr; + continue; + } + +#ifdef DEBUG_METHODS_ENABLED + MethodBind **method = type->method_map.getptr(p_method); + if (method && *method) { + if (r_info != nullptr) { + MethodInfo minfo = info_from_bind(*method); + *r_info = minfo; + } + return true; + } else if (type->virtual_methods_map.has(p_method)) { + if (r_info) { + *r_info = type->virtual_methods_map[p_method]; + } + return true; + } +#else + if (type->method_map.has(p_method)) { + if (r_info) { + MethodBind *m = type->method_map[p_method]; + MethodInfo mi; + mi.name = m->get_name(); + *r_info = mi; + } + return true; + } +#endif + + if (p_no_inheritance) { + break; + } + + type = type->inherits_ptr; + } + + return false; +} + +MethodBind *ClassDB::get_method(StringName p_class, StringName p_name) { + OBJTYPE_RLOCK; + + ClassInfo *type = classes.getptr(p_class); + + while (type) { MethodBind **method = type->method_map.getptr(p_name); - if (method && *method) + if (method && *method) { return *method; + } type = type->inherits_ptr; } return nullptr; } void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant) { - OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); @@ -697,7 +709,6 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName ERR_FAIL_COND(!type); if (type->constant_map.has(p_name)) { - ERR_FAIL(); } @@ -726,16 +737,15 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName } void ClassDB::get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance) { - OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); while (type) { - #ifdef DEBUG_METHODS_ENABLED - for (List<StringName>::Element *E = type->constant_order.front(); E; E = E->next()) + for (List<StringName>::Element *E = type->constant_order.front(); E; E = E->next()) { p_constants->push_back(E->get()); + } #else const StringName *K = nullptr; @@ -744,57 +754,75 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String> } #endif - if (p_no_inheritance) + if (p_no_inheritance) { break; + } type = type->inherits_ptr; } } int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success) { - OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); while (type) { - int *constant = type->constant_map.getptr(p_name); if (constant) { - - if (p_success) + if (p_success) { *p_success = true; + } return *constant; } type = type->inherits_ptr; } - if (p_success) + if (p_success) { *p_success = false; + } return 0; } -StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { - +bool ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); while (type) { + if (type->constant_map.has(p_name)) { + return true; + } + if (p_no_inheritance) { + return false; + } + type = type->inherits_ptr; + } + + return false; +} + +StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { + OBJTYPE_RLOCK; + + ClassInfo *type = classes.getptr(p_class); + + while (type) { const StringName *k = nullptr; while ((k = type->enum_map.next(k))) { - List<StringName> &constants_list = type->enum_map.get(*k); const List<StringName>::Element *found = constants_list.find(p_name); - if (found) + if (found) { return *k; + } } - if (p_no_inheritance) + if (p_no_inheritance) { break; + } type = type->inherits_ptr; } @@ -803,33 +831,30 @@ StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const S } void ClassDB::get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance) { - OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); while (type) { - const StringName *k = nullptr; while ((k = type->enum_map.next(k))) { p_enums->push_back(*k); } - if (p_no_inheritance) + if (p_no_inheritance) { break; + } type = type->inherits_ptr; } } void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance) { - OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); while (type) { - const List<StringName> *constants = type->enum_map.getptr(p_enum); if (constants) { @@ -838,15 +863,34 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_ } } - if (p_no_inheritance) + if (p_no_inheritance) { break; + } type = type->inherits_ptr; } } -void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { +bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { + OBJTYPE_RLOCK; + + ClassInfo *type = classes.getptr(p_class); + + while (type) { + if (type->enum_map.has(p_name)) { + return true; + } + if (p_no_inheritance) { + return false; + } + type = type->inherits_ptr; + } + + return false; +} + +void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); @@ -866,7 +910,6 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { } void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) { - OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -875,28 +918,30 @@ void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, b ClassInfo *check = type; while (check) { - const StringName *S = nullptr; while ((S = check->signal_map.next(S))) { - p_signals->push_back(check->signal_map[*S]); } - if (p_no_inheritance) + if (p_no_inheritance) { return; + } check = check->inherits_ptr; } } -bool ClassDB::has_signal(StringName p_class, StringName p_signal) { - +bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - if (check->signal_map.has(p_signal)) + if (check->signal_map.has(p_signal)) { return true; + } + if (p_no_inheritance) { + return false; + } check = check->inherits_ptr; } @@ -904,7 +949,6 @@ bool ClassDB::has_signal(StringName p_class, StringName p_signal) { } bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal) { - OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -922,7 +966,6 @@ bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_ } void ClassDB::add_property_group(StringName p_class, const String &p_name, const String &p_prefix) { - OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); @@ -931,7 +974,6 @@ void ClassDB::add_property_group(StringName p_class, const String &p_name, const } void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix) { - OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); @@ -939,8 +981,8 @@ void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, co type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP)); } +// NOTE: For implementation simplicity reasons, this method doesn't allow setters to have optional arguments at the end. void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) { - lock->read_lock(); ClassInfo *type = classes.getptr(p_class); lock->read_unlock(); @@ -961,7 +1003,6 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons MethodBind *mb_get = nullptr; if (p_getter) { - mb_get = get_method(p_class, p_getter); #ifdef DEBUG_METHODS_ENABLED @@ -979,6 +1020,7 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons OBJTYPE_WLOCK type->property_list.push_back(p_pinfo); + type->property_map[p_pinfo.name] = p_pinfo; #ifdef DEBUG_METHODS_ENABLED if (mb_get) { type->methods_in_properties.insert(p_getter); @@ -1006,15 +1048,12 @@ void ClassDB::set_property_default_value(StringName p_class, const StringName &p } void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) { - OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - for (List<PropertyInfo>::Element *E = check->property_list.front(); E; E = E->next()) { - if (p_validator) { PropertyInfo pi = E->get(); p_validator->_validate_property(pi); @@ -1024,22 +1063,47 @@ void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, } } - if (p_no_inheritance) + if (p_no_inheritance) { return; + } check = check->inherits_ptr; } } -bool ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid) { +bool ClassDB::get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) { + OBJTYPE_RLOCK; + + ClassInfo *check = classes.getptr(p_class); + while (check) { + if (check->property_map.has(p_property)) { + PropertyInfo pinfo = check->property_map[p_property]; + if (p_validator) { + p_validator->_validate_property(pinfo); + } + if (r_info) { + *r_info = pinfo; + } + return true; + } + if (p_no_inheritance) { + break; + } + check = check->inherits_ptr; + } + + return false; +} + +bool ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid) { ClassInfo *type = classes.getptr(p_object->get_class_name()); ClassInfo *check = type; while (check) { const PropertySetGet *psg = check->property_setget.getptr(p_property); if (psg) { - if (!psg->setter) { - if (r_valid) + if (r_valid) { *r_valid = false; + } return true; //return true but do nothing } @@ -1064,8 +1128,9 @@ bool ClassDB::set_property(Object *p_object, const StringName &p_property, const } } - if (r_valid) + if (r_valid) { *r_valid = ce.error == Callable::CallError::CALL_OK; + } return true; } @@ -1075,15 +1140,16 @@ bool ClassDB::set_property(Object *p_object, const StringName &p_property, const return false; } -bool ClassDB::get_property(Object *p_object, const StringName &p_property, Variant &r_value) { +bool ClassDB::get_property(Object *p_object, const StringName &p_property, Variant &r_value) { ClassInfo *type = classes.getptr(p_object->get_class_name()); ClassInfo *check = type; while (check) { const PropertySetGet *psg = check->property_setget.getptr(p_property); if (psg) { - if (!psg->getter) + if (!psg->getter) { return true; //return true but do nothing + } if (psg->index >= 0) { Variant index = psg->index; @@ -1092,10 +1158,8 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia r_value = p_object->call(psg->getter, arg, 1, ce); } else { - Callable::CallError ce; if (psg->_getptr) { - r_value = psg->_getptr->call(p_object, nullptr, 0, ce); } else { r_value = p_object->call(psg->getter, nullptr, 0, ce); @@ -1106,7 +1170,6 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia const int *c = check->constant_map.getptr(p_property); //constants count if (c) { - r_value = *c; return true; } @@ -1128,57 +1191,55 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia } int ClassDB::get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid) { - ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { const PropertySetGet *psg = check->property_setget.getptr(p_property); if (psg) { - - if (r_is_valid) + if (r_is_valid) { *r_is_valid = true; + } return psg->index; } check = check->inherits_ptr; } - if (r_is_valid) + if (r_is_valid) { *r_is_valid = false; + } return -1; } Variant::Type ClassDB::get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid) { - ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { const PropertySetGet *psg = check->property_setget.getptr(p_property); if (psg) { - - if (r_is_valid) + if (r_is_valid) { *r_is_valid = true; + } return psg->type; } check = check->inherits_ptr; } - if (r_is_valid) + if (r_is_valid) { *r_is_valid = false; + } return Variant::NIL; } StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_property) { - ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { const PropertySetGet *psg = check->property_setget.getptr(p_property); if (psg) { - return psg->setter; } @@ -1189,13 +1250,11 @@ StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_ } StringName ClassDB::get_property_getter(StringName p_class, const StringName &p_property) { - ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { const PropertySetGet *psg = check->property_setget.getptr(p_property); if (psg) { - return psg->getter; } @@ -1206,15 +1265,16 @@ StringName ClassDB::get_property_getter(StringName p_class, const StringName &p_ } bool ClassDB::has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance) { - ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - if (check->property_setget.has(p_property)) + if (check->property_setget.has(p_property)) { return true; + } - if (p_no_inheritance) + if (p_no_inheritance) { break; + } check = check->inherits_ptr; } @@ -1222,7 +1282,6 @@ bool ClassDB::has_property(const StringName &p_class, const StringName &p_proper } void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_flags) { - OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -1232,14 +1291,15 @@ void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_fl } bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) { - ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - if (check->method_map.has(p_method)) + if (check->method_map.has(p_method)) { return true; - if (p_no_inheritance) + } + if (p_no_inheritance) { return false; + } check = check->inherits_ptr; } @@ -1295,8 +1355,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c defvals.resize(p_defcount); for (int i = 0; i < p_defcount; i++) { - - defvals.write[i] = *p_defs[p_defcount - i - 1]; + defvals.write[i] = *p_defs[i]; } p_bind->set_default_arguments(defvals); @@ -1311,15 +1370,16 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_ #ifdef DEBUG_METHODS_ENABLED MethodInfo mi = p_method; - if (p_virtual) + if (p_virtual) { mi.flags |= METHOD_FLAG_VIRTUAL; + } classes[p_class].virtual_methods.push_back(mi); + classes[p_class].virtual_methods_map[p_method.name] = mi; #endif } void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance) { - ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); #ifdef DEBUG_METHODS_ENABLED @@ -1327,13 +1387,13 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - for (List<MethodInfo>::Element *E = check->virtual_methods.front(); E; E = E->next()) { p_methods->push_back(E->get()); } - if (p_no_inheritance) + if (p_no_inheritance) { return; + } check = check->inherits_ptr; } @@ -1341,7 +1401,6 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p } void ClassDB::set_class_enabled(StringName p_class, bool p_enable) { - OBJTYPE_WLOCK; ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); @@ -1349,7 +1408,6 @@ void ClassDB::set_class_enabled(StringName p_class, bool p_enable) { } bool ClassDB::is_class_enabled(StringName p_class) { - OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -1364,7 +1422,6 @@ bool ClassDB::is_class_enabled(StringName p_class) { } bool ClassDB::is_class_exposed(StringName p_class) { - OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -1373,7 +1430,6 @@ bool ClassDB::is_class_exposed(StringName p_class) { } StringName ClassDB::get_category(const StringName &p_node) { - ERR_FAIL_COND_V(!classes.has(p_node), StringName()); #ifdef DEBUG_ENABLED return classes[p_node].category; @@ -1383,31 +1439,29 @@ StringName ClassDB::get_category(const StringName &p_node) { } void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) { - - if (resource_base_extensions.has(p_extension)) + if (resource_base_extensions.has(p_extension)) { return; + } resource_base_extensions[p_extension] = p_class; } void ClassDB::get_resource_base_extensions(List<String> *p_extensions) { - const StringName *K = nullptr; while ((K = resource_base_extensions.next(K))) { - p_extensions->push_back(*K); } } void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) { - const StringName *K = nullptr; while ((K = resource_base_extensions.next(K))) { StringName cmp = resource_base_extensions[*K]; - if (is_parent_class(p_class, cmp) || is_parent_class(cmp, p_class)) + if (is_parent_class(p_class, cmp) || is_parent_class(cmp, p_class)) { p_extensions->push_back(*K); + } } } @@ -1415,9 +1469,7 @@ HashMap<StringName, HashMap<StringName, Variant>> ClassDB::default_values; Set<StringName> ClassDB::default_values_cached; Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid) { - if (!default_values_cached.has(p_class)) { - if (!default_values.has(p_class)) { default_values[p_class] = HashMap<StringName, Variant>(); } @@ -1434,12 +1486,10 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con } if (c) { - List<PropertyInfo> plist; c->get_property_list(&plist); for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { if (E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) { - if (!default_values[p_class].has(E->get().name)) { Variant v = c->get(E->get().name); default_values[p_class][E->get().name] = v; @@ -1456,45 +1506,62 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con } if (!default_values.has(p_class)) { - if (r_valid != nullptr) *r_valid = false; + if (r_valid != nullptr) { + *r_valid = false; + } return Variant(); } if (!default_values[p_class].has(p_property)) { - if (r_valid != nullptr) *r_valid = false; + if (r_valid != nullptr) { + *r_valid = false; + } return Variant(); } - if (r_valid != nullptr) *r_valid = true; - return default_values[p_class][p_property]; + if (r_valid != nullptr) { + *r_valid = true; + } + + Variant var = default_values[p_class][p_property]; + +#ifdef DEBUG_ENABLED + // Some properties may have an instantiated Object as default value, + // (like Path2D's `curve` used to have), but that's not a good practice. + // Instead, those properties should use PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT + // to be auto-instantiated when created in the editor. + if (var.get_type() == Variant::OBJECT) { + Object *obj = var.get_validated_object(); + if (obj) { + WARN_PRINT(vformat("Instantiated %s used as default value for %s's \"%s\" property.", obj->get_class(), p_class, p_property)); + } + } +#endif + + return var; } RWLock *ClassDB::lock = nullptr; void ClassDB::init() { - lock = RWLock::create(); } void ClassDB::cleanup_defaults() { - default_values.clear(); default_values_cached.clear(); } void ClassDB::cleanup() { - //OBJTYPE_LOCK; hah not here const StringName *k = nullptr; while ((k = classes.next(k))) { - ClassInfo &ti = classes[*k]; const StringName *m = nullptr; while ((m = ti.method_map.next(m))) { - memdelete(ti.method_map[*m]); } } diff --git a/core/class_db.h b/core/object/class_db.h index f760aa1738..0591676b92 100644 --- a/core/class_db.h +++ b/core/object/class_db.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,24 +31,23 @@ #ifndef CLASS_DB_H #define CLASS_DB_H -#include "core/method_bind.h" -#include "core/object.h" -#include "core/print_string.h" +#include "core/object/method_bind.h" +#include "core/object/object.h" +#include "core/string/print_string.h" /** To bind more then 6 parameters include this: - * #include "core/method_bind_ext.gen.inc" + * */ // Makes callable_mp readily available in all classes connecting signals. // Needs to come after method_bind and object have been included. -#include "core/callable_method_pointer.h" +#include "core/object/callable_method_pointer.h" #define DEFVAL(m_defval) (m_defval) #ifdef DEBUG_METHODS_ENABLED struct MethodDefinition { - StringName name; Vector<StringName> args; MethodDefinition() {} @@ -103,7 +102,6 @@ public: public: struct PropertySetGet { - int index; StringName setter; StringName getter; @@ -113,31 +111,34 @@ public: }; struct ClassInfo { + APIType api = API_NONE; + ClassInfo *inherits_ptr = nullptr; + void *class_ptr = nullptr; - APIType api; - ClassInfo *inherits_ptr; - void *class_ptr; HashMap<StringName, MethodBind *> method_map; HashMap<StringName, int> constant_map; HashMap<StringName, List<StringName>> enum_map; HashMap<StringName, MethodInfo> signal_map; List<PropertyInfo> property_list; + HashMap<StringName, PropertyInfo> property_map; #ifdef DEBUG_METHODS_ENABLED List<StringName> constant_order; List<StringName> method_order; Set<StringName> methods_in_properties; List<MethodInfo> virtual_methods; + Map<StringName, MethodInfo> virtual_methods_map; StringName category; #endif HashMap<StringName, PropertySetGet> property_setget; StringName inherits; StringName name; - bool disabled; - bool exposed; - Object *(*creation_func)(); - ClassInfo(); - ~ClassInfo(); + bool disabled = false; + bool exposed = false; + Object *(*creation_func)() = nullptr; + + ClassInfo() {} + ~ClassInfo() {} }; template <class T> @@ -163,17 +164,20 @@ public: static HashMap<StringName, HashMap<StringName, Variant>> default_values; static Set<StringName> default_values_cached; +private: + // Non-locking variants of get_parent_class and is_parent_class. + static StringName _get_parent_class(const StringName &p_class); + static bool _is_parent_class(const StringName &p_class, const StringName &p_inherits); + public: // DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!! template <class T> static void _add_class() { - _add_class2(T::get_class_static(), T::get_parent_class_static()); } template <class T> static void register_class() { - GLOBAL_LOCK_FUNCTION; T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); @@ -186,7 +190,6 @@ public: template <class T> static void register_virtual_class() { - GLOBAL_LOCK_FUNCTION; T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); @@ -198,13 +201,11 @@ public: template <class T> static Object *_create_ptr_func() { - return T::create(); } template <class T> static void register_custom_instance_class() { - GLOBAL_LOCK_FUNCTION; T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); @@ -231,7 +232,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method) { - MethodBind *bind = create_method_bind(p_method); return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, nullptr, 0); //use static function, much smaller binary usage @@ -239,7 +239,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1) { - MethodBind *bind = create_method_bind(p_method); const Variant *ptr[1] = { &p_def1 }; @@ -248,7 +247,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2) { - MethodBind *bind = create_method_bind(p_method); const Variant *ptr[2] = { &p_def1, &p_def2 }; @@ -257,7 +255,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3) { - MethodBind *bind = create_method_bind(p_method); const Variant *ptr[3] = { &p_def1, &p_def2, &p_def3 }; @@ -266,7 +263,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4) { - MethodBind *bind = create_method_bind(p_method); const Variant *ptr[4] = { &p_def1, &p_def2, &p_def3, &p_def4 }; @@ -275,7 +271,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5) { - MethodBind *bind = create_method_bind(p_method); const Variant *ptr[5] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5 }; @@ -284,7 +279,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6) { - MethodBind *bind = create_method_bind(p_method); const Variant *ptr[6] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6 }; @@ -293,7 +287,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6, const Variant &p_def7) { - MethodBind *bind = create_method_bind(p_method); const Variant *ptr[7] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6, &p_def7 }; @@ -302,7 +295,6 @@ public: template <class N, class M> static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6, const Variant &p_def7, const Variant &p_def8) { - MethodBind *bind = create_method_bind(p_method); const Variant *ptr[8] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6, &p_def7, &p_def8 }; @@ -311,7 +303,6 @@ public: template <class M> static MethodBind *bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>(), bool p_return_nil_is_variant = true) { - GLOBAL_LOCK_FUNCTION; MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); @@ -344,7 +335,7 @@ public: } static void add_signal(StringName p_class, const MethodInfo &p_signal); - static bool has_signal(StringName p_class, StringName p_signal); + static bool has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance = false); static bool get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal); static void get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false); @@ -353,6 +344,7 @@ public: static void add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1); static void set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default); static void get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr); + static bool get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr); static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr); static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value); static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false); @@ -365,6 +357,7 @@ public: static void set_method_flags(StringName p_class, StringName p_method, int p_flags); static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false); + static bool get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false); static MethodBind *get_method(StringName p_class, StringName p_name); static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true); @@ -373,10 +366,12 @@ public: static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant); static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false); static int get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = nullptr); + static bool has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false); static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false); + static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid = nullptr); diff --git a/core/message_queue.cpp b/core/object/message_queue.cpp index 652c424492..4751c69f1e 100644 --- a/core/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,31 +30,29 @@ #include "message_queue.h" +#include "core/config/project_settings.h" #include "core/core_string_names.h" -#include "core/project_settings.h" -#include "core/script_language.h" +#include "core/object/script_language.h" MessageQueue *MessageQueue::singleton = nullptr; MessageQueue *MessageQueue::get_singleton() { - return singleton; } Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) { - return push_callable(Callable(p_id, p_method), p_args, p_argcount, p_show_error); } Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; int argc = 0; for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) + if (argptr[i]->get_type() == Variant::NIL) { break; + } argc++; } @@ -62,15 +60,15 @@ Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, VARIANT } Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value) { - _THREAD_SAFE_METHOD_ uint8_t room_needed = sizeof(Message) + sizeof(Variant); if ((buffer_end + room_needed) >= buffer_size) { String type; - if (ObjectDB::get_instance(p_id)) + if (ObjectDB::get_instance(p_id)) { type = ObjectDB::get_instance(p_id)->get_class(); + } print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id)); statistics(); ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings."); @@ -91,7 +89,6 @@ Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Vari } Error MessageQueue::push_notification(ObjectID p_id, int p_notification) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(p_notification < 0, ERR_INVALID_PARAMETER); @@ -117,21 +114,18 @@ Error MessageQueue::push_notification(ObjectID p_id, int p_notification) { } Error MessageQueue::push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) { - return push_call(p_object->get_instance_id(), p_method, VARIANT_ARG_PASS); } Error MessageQueue::push_notification(Object *p_object, int p_notification) { - return push_notification(p_object->get_instance_id(), p_notification); } -Error MessageQueue::push_set(Object *p_object, const StringName &p_prop, const Variant &p_value) { +Error MessageQueue::push_set(Object *p_object, const StringName &p_prop, const Variant &p_value) { return push_set(p_object->get_instance_id(), p_prop, p_value); } Error MessageQueue::push_callable(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error) { - _THREAD_SAFE_METHOD_ int room_needed = sizeof(Message) + sizeof(Variant) * p_argcount; @@ -146,13 +140,13 @@ Error MessageQueue::push_callable(const Callable &p_callable, const Variant **p_ msg->args = p_argcount; msg->callable = p_callable; msg->type = TYPE_CALL; - if (p_show_error) + if (p_show_error) { msg->type |= FLAG_SHOW_ERROR; + } buffer_end += sizeof(Message); for (int i = 0; i < p_argcount; i++) { - Variant *v = memnew_placement(&buffer[buffer_end], Variant); buffer_end += sizeof(Variant); *v = *p_args[i]; @@ -161,8 +155,22 @@ Error MessageQueue::push_callable(const Callable &p_callable, const Variant **p_ return OK; } -void MessageQueue::statistics() { +Error MessageQueue::push_callable(const Callable &p_callable, VARIANT_ARG_DECLARE) { + VARIANT_ARGPTRS; + + int argc = 0; + for (int i = 0; i < VARIANT_ARG_MAX; i++) { + if (argptr[i]->get_type() == Variant::NIL) { + break; + } + argc++; + } + + return push_callable(p_callable, argptr, argc); +} + +void MessageQueue::statistics() { Map<StringName, int> set_count; Map<int, int> notify_count; Map<Callable, int> call_count; @@ -175,30 +183,28 @@ void MessageQueue::statistics() { Object *target = message->callable.get_object(); if (target != nullptr) { - switch (message->type & FLAG_MASK) { - case TYPE_CALL: { - - if (!call_count.has(message->callable)) + if (!call_count.has(message->callable)) { call_count[message->callable] = 0; + } call_count[message->callable]++; } break; case TYPE_NOTIFICATION: { - - if (!notify_count.has(message->notification)) + if (!notify_count.has(message->notification)) { notify_count[message->notification] = 0; + } notify_count[message->notification]++; } break; case TYPE_SET: { - StringName t = message->callable.get_method(); - if (!set_count.has(t)) + if (!set_count.has(t)) { set_count[t] = 0; + } set_count[t]++; @@ -213,8 +219,9 @@ void MessageQueue::statistics() { } read_pos += sizeof(Message); - if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) + if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) { read_pos += sizeof(Variant) * message->args; + } } print_line("TOTAL BYTES: " + itos(buffer_end)); @@ -234,12 +241,10 @@ void MessageQueue::statistics() { } int MessageQueue::get_max_buffer_usage() const { - return buffer_max_used; } void MessageQueue::_call_function(const Callable &p_callable, const Variant *p_args, int p_argcount, bool p_show_error) { - const Variant **argptrs = nullptr; if (p_argcount) { argptrs = (const Variant **)alloca(sizeof(Variant *) * p_argcount); @@ -252,13 +257,11 @@ void MessageQueue::_call_function(const Callable &p_callable, const Variant *p_a Variant ret; p_callable.call(argptrs, p_argcount, ret, ce); if (p_show_error && ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT("Error calling deferred method: " + Variant::get_callable_error_text(p_callable, argptrs, p_argcount, ce) + "."); } } void MessageQueue::flush() { - if (buffer_end > buffer_max_used) { buffer_max_used = buffer_end; } @@ -275,14 +278,14 @@ void MessageQueue::flush() { flushing = true; while (read_pos < buffer_end) { - //lock on each iteration, so a call can re-add itself to the message queue Message *message = (Message *)&buffer[read_pos]; uint32_t advance = sizeof(Message); - if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) + if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) { advance += sizeof(Variant) * message->args; + } //pre-advance so this function is reentrant read_pos += advance; @@ -292,10 +295,8 @@ void MessageQueue::flush() { Object *target = message->callable.get_object(); if (target != nullptr) { - switch (message->type & FLAG_MASK) { case TYPE_CALL: { - Variant *args = (Variant *)(message + 1); // messages don't expect a return value @@ -304,13 +305,11 @@ void MessageQueue::flush() { } break; case TYPE_NOTIFICATION: { - // messages don't expect a return value target->notification(message->notification); } break; case TYPE_SET: { - Variant *arg = (Variant *)(message + 1); // messages don't expect a return value target->set(message->callable.get_method(), *arg); @@ -337,18 +336,13 @@ void MessageQueue::flush() { } bool MessageQueue::is_flushing() const { - return flushing; } MessageQueue::MessageQueue() { - ERR_FAIL_COND_MSG(singleton != nullptr, "A MessageQueue singleton already exists."); singleton = this; - flushing = false; - buffer_end = 0; - buffer_max_used = 0; buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); buffer_size *= 1024; @@ -356,23 +350,23 @@ MessageQueue::MessageQueue() { } MessageQueue::~MessageQueue() { - uint32_t read_pos = 0; while (read_pos < buffer_end) { - Message *message = (Message *)&buffer[read_pos]; Variant *args = (Variant *)(message + 1); int argc = message->args; if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) { - for (int i = 0; i < argc; i++) + for (int i = 0; i < argc; i++) { args[i].~Variant(); + } } message->~Message(); read_pos += sizeof(Message); - if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) + if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) { read_pos += sizeof(Variant) * message->args; + } } singleton = nullptr; diff --git a/core/message_queue.h b/core/object/message_queue.h index 9ba748bb42..1b8def62d3 100644 --- a/core/message_queue.h +++ b/core/object/message_queue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,16 +31,14 @@ #ifndef MESSAGE_QUEUE_H #define MESSAGE_QUEUE_H -#include "core/object.h" +#include "core/object/class_db.h" #include "core/os/thread_safe.h" class MessageQueue { - _THREAD_SAFE_CLASS_ enum { - - DEFAULT_QUEUE_SIZE_KB = 1024 + DEFAULT_QUEUE_SIZE_KB = 4096 }; enum { @@ -53,7 +51,6 @@ class MessageQueue { }; struct Message { - Callable callable; int16_t type; union { @@ -63,15 +60,15 @@ class MessageQueue { }; uint8_t *buffer; - uint32_t buffer_end; - uint32_t buffer_max_used; + uint32_t buffer_end = 0; + uint32_t buffer_max_used = 0; uint32_t buffer_size; void _call_function(const Callable &p_callable, const Variant *p_args, int p_argcount, bool p_show_error); static MessageQueue *singleton; - bool flushing; + bool flushing = false; public: static MessageQueue *get_singleton(); @@ -81,6 +78,7 @@ public: Error push_notification(ObjectID p_id, int p_notification); Error push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value); Error push_callable(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error = false); + Error push_callable(const Callable &p_callable, VARIANT_ARG_LIST); Error push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST); Error push_notification(Object *p_object, int p_notification); diff --git a/core/method_bind.cpp b/core/object/method_bind.cpp index c513de9ca0..9c5ed60708 100644 --- a/core/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,13 +30,12 @@ // object.h needs to be the first include *before* method_bind.h // FIXME: Find out why and fix potential cyclical dependencies. -#include "core/object.h" +#include "core/object/object.h" #include "method_bind.h" #ifdef DEBUG_METHODS_ENABLED PropertyInfo MethodBind::get_argument_info(int p_argument) const { - ERR_FAIL_INDEX_V(p_argument, get_argument_count(), PropertyInfo()); PropertyInfo info = _gen_argument_type_info(p_argument); @@ -45,35 +44,32 @@ PropertyInfo MethodBind::get_argument_info(int p_argument) const { } PropertyInfo MethodBind::get_return_info() const { - return _gen_argument_type_info(-1); } #endif void MethodBind::_set_const(bool p_const) { - _const = p_const; } void MethodBind::_set_returns(bool p_returns) { - _returns = p_returns; } StringName MethodBind::get_name() const { return name; } + void MethodBind::set_name(const StringName &p_name) { name = p_name; } #ifdef DEBUG_METHODS_ENABLED void MethodBind::set_argument_names(const Vector<StringName> &p_names) { - arg_names = p_names; } -Vector<StringName> MethodBind::get_argument_names() const { +Vector<StringName> MethodBind::get_argument_names() const { return arg_names; } @@ -86,7 +82,6 @@ void MethodBind::set_default_arguments(const Vector<Variant> &p_defargs) { #ifdef DEBUG_METHODS_ENABLED void MethodBind::_generate_argument_types(int p_count) { - set_argument_count(p_count); Variant::Type *argt = memnew_arr(Variant::Type, p_count + 1); @@ -104,19 +99,12 @@ void MethodBind::_generate_argument_types(int p_count) { MethodBind::MethodBind() { static int last_id = 0; method_id = last_id++; - hint_flags = METHOD_FLAGS_DEFAULT; - argument_count = 0; - default_argument_count = 0; -#ifdef DEBUG_METHODS_ENABLED - argument_types = nullptr; -#endif - _const = false; - _returns = false; } MethodBind::~MethodBind() { #ifdef DEBUG_METHODS_ENABLED - if (argument_types) + if (argument_types) { memdelete_arr(argument_types); + } #endif } diff --git a/core/object/method_bind.h b/core/object/method_bind.h new file mode 100644 index 0000000000..7cf4f8d4e8 --- /dev/null +++ b/core/object/method_bind.h @@ -0,0 +1,577 @@ +/*************************************************************************/ +/* method_bind.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 METHOD_BIND_H +#define METHOD_BIND_H + +#include "core/variant/binder_common.h" + +enum MethodFlags { + METHOD_FLAG_NORMAL = 1, + METHOD_FLAG_EDITOR = 2, + METHOD_FLAG_NOSCRIPT = 4, + METHOD_FLAG_CONST = 8, + METHOD_FLAG_REVERSE = 16, // used for events + METHOD_FLAG_VIRTUAL = 32, + METHOD_FLAG_FROM_SCRIPT = 64, + METHOD_FLAG_VARARG = 128, + METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, +}; + +VARIANT_ENUM_CAST(MethodFlags) + +// some helpers + +class MethodBind { + int method_id; + uint32_t hint_flags = METHOD_FLAGS_DEFAULT; + StringName name; + StringName instance_class; + Vector<Variant> default_arguments; + int default_argument_count = 0; + int argument_count = 0; + + bool _const = false; + bool _returns = false; + +protected: +#ifdef DEBUG_METHODS_ENABLED + Variant::Type *argument_types = nullptr; + Vector<StringName> arg_names; +#endif + void _set_const(bool p_const); + void _set_returns(bool p_returns); +#ifdef DEBUG_METHODS_ENABLED + virtual Variant::Type _gen_argument_type(int p_arg) const = 0; + virtual PropertyInfo _gen_argument_type_info(int p_arg) const = 0; + void _generate_argument_types(int p_count); + +#endif + void set_argument_count(int p_count) { argument_count = p_count; } + +public: + _FORCE_INLINE_ const Vector<Variant> &get_default_arguments() const { return default_arguments; } + _FORCE_INLINE_ int get_default_argument_count() const { return default_argument_count; } + + _FORCE_INLINE_ Variant has_default_argument(int p_arg) const { + int idx = p_arg - (argument_count - default_arguments.size()); + + if (idx < 0 || idx >= default_arguments.size()) { + return false; + } else { + return true; + } + } + + _FORCE_INLINE_ Variant get_default_argument(int p_arg) const { + int idx = p_arg - (argument_count - default_arguments.size()); + + if (idx < 0 || idx >= default_arguments.size()) { + return Variant(); + } else { + return default_arguments[idx]; + } + } + +#ifdef DEBUG_METHODS_ENABLED + _FORCE_INLINE_ Variant::Type get_argument_type(int p_argument) const { + ERR_FAIL_COND_V(p_argument < -1 || p_argument > argument_count, Variant::NIL); + return argument_types[p_argument + 1]; + } + + PropertyInfo get_argument_info(int p_argument) const; + PropertyInfo get_return_info() const; + + void set_argument_names(const Vector<StringName> &p_names); // Set by ClassDB, can't be inferred otherwise. + Vector<StringName> get_argument_names() const; + + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const = 0; +#endif + + void set_hint_flags(uint32_t p_hint) { hint_flags = p_hint; } + uint32_t get_hint_flags() const { return hint_flags | (is_const() ? METHOD_FLAG_CONST : 0) | (is_vararg() ? METHOD_FLAG_VARARG : 0); } + _FORCE_INLINE_ StringName get_instance_class() const { return instance_class; } + _FORCE_INLINE_ void set_instance_class(const StringName &p_class) { instance_class = p_class; } + + _FORCE_INLINE_ int get_argument_count() const { return argument_count; }; + + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) = 0; + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) = 0; + + StringName get_name() const; + void set_name(const StringName &p_name); + _FORCE_INLINE_ int get_method_id() const { return method_id; } + _FORCE_INLINE_ bool is_const() const { return _const; } + _FORCE_INLINE_ bool has_return() const { return _returns; } + virtual bool is_vararg() const { return false; } + + void set_default_arguments(const Vector<Variant> &p_defargs); + + MethodBind(); + virtual ~MethodBind(); +}; + +template <class T> +class MethodBindVarArg : public MethodBind { +public: + typedef Variant (T::*NativeCall)(const Variant **, int, Callable::CallError &); + +protected: + NativeCall call_method = nullptr; +#ifdef DEBUG_METHODS_ENABLED + MethodInfo arguments; +#endif + +public: +#ifdef DEBUG_METHODS_ENABLED + virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + if (p_arg < 0) { + return arguments.return_val; + } else if (p_arg < arguments.arguments.size()) { + return arguments.arguments[p_arg]; + } else { + return PropertyInfo(Variant::NIL, "arg_" + itos(p_arg), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); + } + } + + virtual Variant::Type _gen_argument_type(int p_arg) const { + return _gen_argument_type_info(p_arg).type; + } + + virtual GodotTypeInfo::Metadata get_argument_meta(int) const { + return GodotTypeInfo::METADATA_NONE; + } +#else + virtual Variant::Type _gen_argument_type(int p_arg) const { + return Variant::NIL; + } +#endif + + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + T *instance = static_cast<T *>(p_object); + return (instance->*call_method)(p_args, p_arg_count, r_error); + } + + void set_method_info(const MethodInfo &p_info, bool p_return_nil_is_variant) { + set_argument_count(p_info.arguments.size()); +#ifdef DEBUG_METHODS_ENABLED + Variant::Type *at = memnew_arr(Variant::Type, p_info.arguments.size() + 1); + at[0] = p_info.return_val.type; + if (p_info.arguments.size()) { + Vector<StringName> names; + names.resize(p_info.arguments.size()); + for (int i = 0; i < p_info.arguments.size(); i++) { + at[i + 1] = p_info.arguments[i].type; + names.write[i] = p_info.arguments[i].name; + } + + set_argument_names(names); + } + argument_types = at; + arguments = p_info; + if (p_return_nil_is_variant) { + arguments.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } +#endif + } + + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { + ERR_FAIL(); // Can't call. + } + + void set_method(NativeCall p_method) { call_method = p_method; } + virtual bool is_const() const { return false; } + + virtual bool is_vararg() const { return true; } + + MethodBindVarArg() { + _set_returns(true); + } +}; + +template <class T> +MethodBind *create_vararg_method_bind(Variant (T::*p_method)(const Variant **, int, Callable::CallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { + MethodBindVarArg<T> *a = memnew((MethodBindVarArg<T>)); + a->set_method(p_method); + a->set_method_info(p_info, p_return_nil_is_variant); + a->set_instance_class(T::get_class_static()); + return a; +} + +/**** VARIADIC TEMPLATES ****/ + +#ifndef TYPED_METHOD_BIND +class __UnexistingClass; +#define MB_T __UnexistingClass +#else +#define MB_T T +#endif + +// no return, not const +#ifdef TYPED_METHOD_BIND +template <class T, class... P> +#else +template <class... P> +#endif +class MethodBindT : public MethodBind { + void (MB_T::*method)(P...); + +protected: +#ifdef DEBUG_METHODS_ENABLED +// GCC raises warnings in the case P = {} as the comparison is always false... +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlogical-op" +#endif + virtual Variant::Type _gen_argument_type(int p_arg) const { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + return call_get_argument_type<P...>(p_arg); + } else { + return Variant::NIL; + } + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + PropertyInfo pi; + call_get_argument_type_info<P...>(p_arg, pi); + return pi; + } +#endif + +public: +#ifdef DEBUG_METHODS_ENABLED + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + return call_get_argument_metadata<P...>(p_arg); + } + +#endif + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { +#ifdef TYPED_METHOD_BIND + call_with_variant_args_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); +#else + call_with_variant_args_dv((MB_T *)(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); +#endif + return Variant(); + } + + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { +#ifdef TYPED_METHOD_BIND + call_with_ptr_args<T, P...>(static_cast<T *>(p_object), method, p_args); +#else + call_with_ptr_args<MB_T, P...>((MB_T *)(p_object), method, p_args); +#endif + } + + MethodBindT(void (MB_T::*p_method)(P...)) { + method = p_method; +#ifdef DEBUG_METHODS_ENABLED + _generate_argument_types(sizeof...(P)); +#endif + set_argument_count(sizeof...(P)); + } +}; + +template <class T, class... P> +MethodBind *create_method_bind(void (T::*p_method)(P...)) { +#ifdef TYPED_METHOD_BIND + MethodBind *a = memnew((MethodBindT<T, P...>)(p_method)); +#else + MethodBind *a = memnew((MethodBindT<P...>)(reinterpret_cast<void (MB_T::*)(P...)>(p_method))); +#endif + a->set_instance_class(T::get_class_static()); + return a; +} + +// no return, not const + +#ifdef TYPED_METHOD_BIND +template <class T, class... P> +#else +template <class... P> +#endif +class MethodBindTC : public MethodBind { + void (MB_T::*method)(P...) const; + +protected: +#ifdef DEBUG_METHODS_ENABLED +// GCC raises warnings in the case P = {} as the comparison is always false... +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlogical-op" +#endif + virtual Variant::Type _gen_argument_type(int p_arg) const { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + return call_get_argument_type<P...>(p_arg); + } else { + return Variant::NIL; + } + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + PropertyInfo pi; + call_get_argument_type_info<P...>(p_arg, pi); + return pi; + } +#endif + +public: +#ifdef DEBUG_METHODS_ENABLED + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + return call_get_argument_metadata<P...>(p_arg); + } + +#endif + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { +#ifdef TYPED_METHOD_BIND + call_with_variant_argsc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); +#else + call_with_variant_argsc_dv((MB_T *)(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); +#endif + return Variant(); + } + + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { +#ifdef TYPED_METHOD_BIND + call_with_ptr_argsc<T, P...>(static_cast<T *>(p_object), method, p_args); +#else + call_with_ptr_argsc<MB_T, P...>((MB_T *)(p_object), method, p_args); +#endif + } + + MethodBindTC(void (MB_T::*p_method)(P...) const) { + method = p_method; + _set_const(true); +#ifdef DEBUG_METHODS_ENABLED + _generate_argument_types(sizeof...(P)); +#endif + set_argument_count(sizeof...(P)); + } +}; + +template <class T, class... P> +MethodBind *create_method_bind(void (T::*p_method)(P...) const) { +#ifdef TYPED_METHOD_BIND + MethodBind *a = memnew((MethodBindTC<T, P...>)(p_method)); +#else + MethodBind *a = memnew((MethodBindTC<P...>)(reinterpret_cast<void (MB_T::*)(P...) const>(p_method))); +#endif + a->set_instance_class(T::get_class_static()); + return a; +} + +// return, not const + +#ifdef TYPED_METHOD_BIND +template <class T, class R, class... P> +#else +template <class R, class... P> +#endif +class MethodBindTR : public MethodBind { + R(MB_T::*method) + (P...); + +protected: +#ifdef DEBUG_METHODS_ENABLED +// GCC raises warnings in the case P = {} as the comparison is always false... +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlogical-op" +#endif + virtual Variant::Type _gen_argument_type(int p_arg) const { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + return call_get_argument_type<P...>(p_arg); + } else { + return GetTypeInfo<R>::VARIANT_TYPE; + } + } + + virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + PropertyInfo pi; + call_get_argument_type_info<P...>(p_arg, pi); + return pi; + } else { + return GetTypeInfo<R>::get_class_info(); + } + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +#endif + +public: +#ifdef DEBUG_METHODS_ENABLED + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + if (p_arg >= 0) { + return call_get_argument_metadata<P...>(p_arg); + } else { + return GetTypeInfo<R>::METADATA; + } + } +#endif + + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + Variant ret; +#ifdef TYPED_METHOD_BIND + call_with_variant_args_ret_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); +#else + call_with_variant_args_ret_dv((MB_T *)p_object, method, p_args, p_arg_count, ret, r_error, get_default_arguments()); +#endif + return ret; + } + + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { +#ifdef TYPED_METHOD_BIND + call_with_ptr_args_ret<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret); +#else + call_with_ptr_args_ret<MB_T, R, P...>((MB_T *)(p_object), method, p_args, r_ret); +#endif + } + + MethodBindTR(R (MB_T::*p_method)(P...)) { + method = p_method; + _set_returns(true); +#ifdef DEBUG_METHODS_ENABLED + _generate_argument_types(sizeof...(P)); +#endif + set_argument_count(sizeof...(P)); + } +}; + +template <class T, class R, class... P> +MethodBind *create_method_bind(R (T::*p_method)(P...)) { +#ifdef TYPED_METHOD_BIND + MethodBind *a = memnew((MethodBindTR<T, R, P...>)(p_method)); +#else + MethodBind *a = memnew((MethodBindTR<R, P...>)(reinterpret_cast<R (MB_T::*)(P...)>(p_method))); +#endif + + a->set_instance_class(T::get_class_static()); + return a; +} + +// return, const + +#ifdef TYPED_METHOD_BIND +template <class T, class R, class... P> +#else +template <class R, class... P> +#endif +class MethodBindTRC : public MethodBind { + R(MB_T::*method) + (P...) const; + +protected: +#ifdef DEBUG_METHODS_ENABLED +// GCC raises warnings in the case P = {} as the comparison is always false... +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlogical-op" +#endif + virtual Variant::Type _gen_argument_type(int p_arg) const { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + return call_get_argument_type<P...>(p_arg); + } else { + return GetTypeInfo<R>::VARIANT_TYPE; + } + } + + virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + PropertyInfo pi; + call_get_argument_type_info<P...>(p_arg, pi); + return pi; + } else { + return GetTypeInfo<R>::get_class_info(); + } + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +#endif + +public: +#ifdef DEBUG_METHODS_ENABLED + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + if (p_arg >= 0) { + return call_get_argument_metadata<P...>(p_arg); + } else { + return GetTypeInfo<R>::METADATA; + } + } +#endif + + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + Variant ret; +#ifdef TYPED_METHOD_BIND + call_with_variant_args_retc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); +#else + call_with_variant_args_retc_dv((MB_T *)(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); +#endif + return ret; + } + + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { +#ifdef TYPED_METHOD_BIND + call_with_ptr_args_retc<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret); +#else + call_with_ptr_args_retc<MB_T, R, P...>((MB_T *)(p_object), method, p_args, r_ret); +#endif + } + + MethodBindTRC(R (MB_T::*p_method)(P...) const) { + method = p_method; + _set_returns(true); + _set_const(true); +#ifdef DEBUG_METHODS_ENABLED + _generate_argument_types(sizeof...(P)); +#endif + set_argument_count(sizeof...(P)); + } +}; + +template <class T, class R, class... P> +MethodBind *create_method_bind(R (T::*p_method)(P...) const) { +#ifdef TYPED_METHOD_BIND + MethodBind *a = memnew((MethodBindTRC<T, R, P...>)(p_method)); +#else + MethodBind *a = memnew((MethodBindTRC<R, P...>)(reinterpret_cast<R (MB_T::*)(P...) const>(p_method))); +#endif + a->set_instance_class(T::get_class_static()); + return a; +} + +#endif // METHOD_BIND_H diff --git a/core/object.cpp b/core/object/object.cpp index b0e6f2bdae..171bc4dc2c 100644 --- a/core/object.cpp +++ b/core/object/object.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,19 +30,18 @@ #include "object.h" -#include "core/class_db.h" #include "core/core_string_names.h" -#include "core/message_queue.h" +#include "core/io/resource.h" +#include "core/object/class_db.h" +#include "core/object/message_queue.h" +#include "core/object/script_language.h" #include "core/os/os.h" -#include "core/print_string.h" -#include "core/resource.h" -#include "core/script_language.h" -#include "core/translation.h" +#include "core/string/print_string.h" +#include "core/string/translation.h" #ifdef DEBUG_ENABLED struct _ObjectDebugLock { - Object *obj; _ObjectDebugLock(Object *p_obj) { @@ -63,7 +62,6 @@ struct _ObjectDebugLock { #endif PropertyInfo::operator Dictionary() const { - Dictionary d; d["name"] = name; d["class_name"] = class_name; @@ -75,36 +73,38 @@ PropertyInfo::operator Dictionary() const { } PropertyInfo PropertyInfo::from_dict(const Dictionary &p_dict) { - PropertyInfo pi; - if (p_dict.has("type")) + if (p_dict.has("type")) { pi.type = Variant::Type(int(p_dict["type"])); + } - if (p_dict.has("name")) + if (p_dict.has("name")) { pi.name = p_dict["name"]; + } - if (p_dict.has("class_name")) + if (p_dict.has("class_name")) { pi.class_name = p_dict["class_name"]; + } - if (p_dict.has("hint")) + if (p_dict.has("hint")) { pi.hint = PropertyHint(int(p_dict["hint"])); + } - if (p_dict.has("hint_string")) - + if (p_dict.has("hint_string")) { pi.hint_string = p_dict["hint_string"]; + } - if (p_dict.has("usage")) + if (p_dict.has("usage")) { pi.usage = p_dict["usage"]; + } return pi; } Array convert_property_list(const List<PropertyInfo> *p_list) { - Array va; for (const List<PropertyInfo>::Element *E = p_list->front(); E; E = E->next()) { - va.push_back(Dictionary(E->get())); } @@ -112,13 +112,13 @@ Array convert_property_list(const List<PropertyInfo> *p_list) { } MethodInfo::operator Dictionary() const { - Dictionary d; d["name"] = name; d["args"] = convert_property_list(&arguments); Array da; - for (int i = 0; i < default_arguments.size(); i++) + for (int i = 0; i < default_arguments.size(); i++) { da.push_back(default_arguments[i]); + } d["default_args"] = da; d["flags"] = flags; d["id"] = id; @@ -127,17 +127,12 @@ MethodInfo::operator Dictionary() const { return d; } -MethodInfo::MethodInfo() : - flags(METHOD_FLAG_NORMAL), - id(0) { -} - MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) { - MethodInfo mi; - if (p_dict.has("name")) + if (p_dict.has("name")) { mi.name = p_dict["name"]; + } Array args; if (p_dict.has("args")) { args = p_dict["args"]; @@ -159,35 +154,37 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) { mi.return_val = PropertyInfo::from_dict(p_dict["return"]); } - if (p_dict.has("flags")) + if (p_dict.has("flags")) { mi.flags = p_dict["flags"]; + } return mi; } +MethodInfo::MethodInfo() : + flags(METHOD_FLAG_NORMAL) {} + MethodInfo::MethodInfo(const String &p_name) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { } + MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); } + MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); arguments.push_back(p_param2); } MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); arguments.push_back(p_param2); arguments.push_back(p_param3); @@ -195,8 +192,7 @@ MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); arguments.push_back(p_param2); arguments.push_back(p_param3); @@ -205,8 +201,7 @@ MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); arguments.push_back(p_param2); arguments.push_back(p_param3); @@ -215,28 +210,26 @@ MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const } MethodInfo::MethodInfo(Variant::Type ret) : - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { return_val.type = ret; } MethodInfo::MethodInfo(Variant::Type ret, const String &p_name) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { return_val.type = ret; } + MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { return_val.type = ret; arguments.push_back(p_param1); } + MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { return_val.type = ret; arguments.push_back(p_param1); arguments.push_back(p_param2); @@ -244,8 +237,7 @@ MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyIn MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { return_val.type = ret; arguments.push_back(p_param1); arguments.push_back(p_param2); @@ -254,8 +246,7 @@ MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyIn MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { return_val.type = ret; arguments.push_back(p_param1); arguments.push_back(p_param2); @@ -265,8 +256,7 @@ MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyIn MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) : name(p_name), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { return_val.type = ret; arguments.push_back(p_param1); arguments.push_back(p_param2); @@ -278,23 +268,20 @@ MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyIn MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name) : name(p_name), return_val(p_ret), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { } MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1) : name(p_name), return_val(p_ret), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); } MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) : name(p_name), return_val(p_ret), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); arguments.push_back(p_param2); } @@ -302,8 +289,7 @@ MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const Pr MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) : name(p_name), return_val(p_ret), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); arguments.push_back(p_param2); arguments.push_back(p_param3); @@ -312,8 +298,7 @@ MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const Pr MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) : name(p_name), return_val(p_ret), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); arguments.push_back(p_param2); arguments.push_back(p_param3); @@ -323,8 +308,7 @@ MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const Pr MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) : name(p_name), return_val(p_ret), - flags(METHOD_FLAG_NORMAL), - id(0) { + flags(METHOD_FLAG_NORMAL) { arguments.push_back(p_param1); arguments.push_back(p_param2); arguments.push_back(p_param3); @@ -333,7 +317,6 @@ MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const Pr } Object::Connection::operator Variant() const { - Dictionary d; d["signal"] = signal; d["callable"] = callable; @@ -343,28 +326,30 @@ Object::Connection::operator Variant() const { } bool Object::Connection::operator<(const Connection &p_conn) const { - if (signal == p_conn.signal) { return callable < p_conn.callable; } else { return signal < p_conn.signal; } } -Object::Connection::Connection(const Variant &p_variant) { +Object::Connection::Connection(const Variant &p_variant) { Dictionary d = p_variant; - if (d.has("signal")) + if (d.has("signal")) { signal = d["signal"]; - if (d.has("callable")) + } + if (d.has("callable")) { callable = d["callable"]; - if (d.has("flags")) + } + if (d.has("flags")) { flags = d["flags"]; - if (d.has("binds")) + } + if (d.has("binds")) { binds = d["binds"]; + } } bool Object::_predelete() { - _predelete_ok = 1; notification(NOTIFICATION_PREDELETE, true); if (_predelete_ok) { @@ -381,21 +366,21 @@ void Object::_postinitialize() { void Object::get_valid_parents_static(List<String> *p_parents) { } + void Object::_get_valid_parents_static(List<String> *p_parents) { } void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid) { - #ifdef TOOLS_ENABLED _edited = true; #endif if (script_instance) { - if (script_instance->set(p_name, p_value)) { - if (r_valid) + if (r_valid) { *r_valid = true; + } return; } } @@ -413,34 +398,27 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid if (p_name == CoreStringNames::get_singleton()->_script) { set_script(p_value); - if (r_valid) + if (r_valid) { *r_valid = true; + } return; } else if (p_name == CoreStringNames::get_singleton()->_meta) { //set_meta(p_name,p_value); metadata = p_value.duplicate(); - if (r_valid) + if (r_valid) { *r_valid = true; + } return; } //something inside the object... :| bool success = _setv(p_name, p_value); if (success) { - if (r_valid) + if (r_valid) { *r_valid = true; - return; - } - - { - bool valid; - setvar(p_name, p_value, &valid); - if (valid) { - if (r_valid) - *r_valid = true; - return; } + return; } #ifdef TOOLS_ENABLED @@ -448,26 +426,27 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid bool valid; script_instance->property_set_fallback(p_name, p_value, &valid); if (valid) { - if (r_valid) + if (r_valid) { *r_valid = true; + } return; } } #endif - if (r_valid) + if (r_valid) { *r_valid = false; + } } Variant Object::get(const StringName &p_name, bool *r_valid) const { - Variant ret; if (script_instance) { - if (script_instance->get(p_name, ret)) { - if (r_valid) + if (r_valid) { *r_valid = true; + } return ret; } } @@ -475,42 +454,35 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { //try built-in setgetter { if (ClassDB::get_property(const_cast<Object *>(this), p_name, ret)) { - if (r_valid) + if (r_valid) { *r_valid = true; + } return ret; } } if (p_name == CoreStringNames::get_singleton()->_script) { ret = get_script(); - if (r_valid) + if (r_valid) { *r_valid = true; + } return ret; } else if (p_name == CoreStringNames::get_singleton()->_meta) { ret = metadata; - if (r_valid) + if (r_valid) { *r_valid = true; + } return ret; } else { //something inside the object... :| bool success = _getv(p_name, ret); if (success) { - if (r_valid) + if (r_valid) { *r_valid = true; - return ret; - } - - //if nothing else, use getvar - { - bool valid; - ret = getvar(p_name, &valid); - if (valid) { - if (r_valid) - *r_valid = true; - return ret; } + return ret; } #ifdef TOOLS_ENABLED @@ -518,23 +490,26 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { bool valid; ret = script_instance->property_get_fallback(p_name, &valid); if (valid) { - if (r_valid) + if (r_valid) { *r_valid = true; + } return ret; } } #endif - if (r_valid) + if (r_valid) { *r_valid = false; + } return Variant(); } } void Object::set_indexed(const Vector<StringName> &p_names, const Variant &p_value, bool *r_valid) { - if (p_names.empty()) { - if (r_valid) + if (p_names.is_empty()) { + if (r_valid) { *r_valid = false; + } return; } if (p_names.size() == 1) { @@ -543,7 +518,9 @@ void Object::set_indexed(const Vector<StringName> &p_names, const Variant &p_val } bool valid = false; - if (!r_valid) r_valid = &valid; + if (!r_valid) { + r_valid = &valid; + } List<Variant> value_stack; @@ -555,9 +532,12 @@ void Object::set_indexed(const Vector<StringName> &p_names, const Variant &p_val } for (int i = 1; i < p_names.size() - 1; i++) { - value_stack.push_back(value_stack.back()->get().get_named(p_names[i], r_valid)); + value_stack.push_back(value_stack.back()->get().get_named(p_names[i], valid)); + if (r_valid) { + *r_valid = valid; + } - if (!*r_valid) { + if (!valid) { value_stack.clear(); return; } @@ -566,11 +546,13 @@ void Object::set_indexed(const Vector<StringName> &p_names, const Variant &p_val value_stack.push_back(p_value); // p_names[p_names.size() - 1] for (int i = p_names.size() - 1; i > 0; i--) { - - value_stack.back()->prev()->get().set_named(p_names[i], value_stack.back()->get(), r_valid); + value_stack.back()->prev()->get().set_named(p_names[i], value_stack.back()->get(), valid); value_stack.pop_back(); - if (!*r_valid) { + if (r_valid) { + *r_valid = valid; + } + if (!valid) { value_stack.clear(); return; } @@ -579,32 +561,34 @@ void Object::set_indexed(const Vector<StringName> &p_names, const Variant &p_val set(p_names[0], value_stack.back()->get(), r_valid); value_stack.pop_back(); - ERR_FAIL_COND(!value_stack.empty()); + ERR_FAIL_COND(!value_stack.is_empty()); } Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) const { - if (p_names.empty()) { - if (r_valid) + if (p_names.is_empty()) { + if (r_valid) { *r_valid = false; + } return Variant(); } bool valid = false; Variant current_value = get(p_names[0], &valid); for (int i = 1; i < p_names.size(); i++) { - current_value = current_value.get_named(p_names[i], &valid); + current_value = current_value.get_named(p_names[i], valid); - if (!valid) + if (!valid) { break; + } } - if (r_valid) + if (r_valid) { *r_valid = valid; + } return current_value; } void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) const { - if (script_instance && p_reversed) { p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); script_instance->get_property_list(p_list); @@ -613,12 +597,9 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons _get_property_listv(p_list, p_reversed); if (!is_class("Script")) { // can still be set, but this is for userfriendlyness -#ifdef TOOLS_ENABLED - p_list->push_back(PropertyInfo(Variant::NIL, "Script", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); -#endif p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT)); } - if (!metadata.empty()) { + if (!metadata.is_empty()) { p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } if (script_instance && !p_reversed) { @@ -631,7 +612,6 @@ void Object::_validate_property(PropertyInfo &property) const { } void Object::get_method_list(List<MethodInfo> *p_list) const { - ClassDB::get_method_list(get_class_name(), p_list); if (script_instance) { script_instance->get_method_list(p_list); @@ -639,7 +619,6 @@ void Object::get_method_list(List<MethodInfo> *p_list) const { } Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 0; @@ -659,7 +638,6 @@ Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::Cal } Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 0; @@ -677,100 +655,12 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Call StringName method = *p_args[0]; - MessageQueue::get_singleton()->push_call(get_instance_id(), method, &p_args[1], p_argcount - 1); + MessageQueue::get_singleton()->push_call(get_instance_id(), method, &p_args[1], p_argcount - 1, true); return Variant(); } -#ifdef DEBUG_ENABLED -static void _test_call_error(const StringName &p_func, const Callable::CallError &error) { - - switch (error.error) { - - case Callable::CallError::CALL_OK: - case Callable::CallError::CALL_ERROR_INVALID_METHOD: - break; - case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { - - ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(Variant::Type(error.expected)) + "."); - break; - } - case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: { - - ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument) + "."); - break; - } - case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: { - - ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument) + "."); - break; - } - case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: - break; - } -} -#else - -#define _test_call_error(m_str, m_err) - -#endif - -void Object::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) { - - if (p_method == CoreStringNames::get_singleton()->_free) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(Object::cast_to<Reference>(this), "Can't 'free' a reference."); - - ERR_FAIL_COND_MSG(_lock_index.get() > 1, "Object is locked and can't be freed."); -#endif - - //must be here, must be before everything, - memdelete(this); - return; - } - - //Variant ret; - OBJ_DEBUG_LOCK - - Callable::CallError error; - - if (script_instance) { - script_instance->call_multilevel(p_method, p_args, p_argcount); - //_test_call_error(p_method,error); - } - - MethodBind *method = ClassDB::get_method(get_class_name(), p_method); - - if (method) { - - method->call(this, p_args, p_argcount, error); - _test_call_error(p_method, error); - } -} - -void Object::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) { - - MethodBind *method = ClassDB::get_method(get_class_name(), p_method); - - Callable::CallError error; - OBJ_DEBUG_LOCK - - if (method) { - - method->call(this, p_args, p_argcount, error); - _test_call_error(p_method, error); - } - - //Variant ret; - - if (script_instance) { - script_instance->call_multilevel_reversed(p_method, p_args, p_argcount); - //_test_call_error(p_method,error); - } -} - bool Object::has_method(const StringName &p_method) const { - if (p_method == CoreStringNames::get_singleton()->_free) { return true; } @@ -785,15 +675,23 @@ bool Object::has_method(const StringName &p_method) const { } Variant Object::getvar(const Variant &p_key, bool *r_valid) const { - - if (r_valid) + if (r_valid) { *r_valid = false; + } + + if (p_key.get_type() == Variant::STRING_NAME || p_key.get_type() == Variant::STRING) { + return get(p_key, r_valid); + } return Variant(); } -void Object::setvar(const Variant &p_key, const Variant &p_value, bool *r_valid) { - if (r_valid) +void Object::setvar(const Variant &p_key, const Variant &p_value, bool *r_valid) { + if (r_valid) { *r_valid = false; + } + if (p_key.get_type() == Variant::STRING_NAME || p_key.get_type() == Variant::STRING) { + return set(p_key, p_value, r_valid); + } } Variant Object::callv(const StringName &p_method, const Array &p_args) { @@ -815,13 +713,13 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) { } Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; int argc = 0; for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) + if (argptr[i]->get_type() == Variant::NIL) { break; + } argc++; } @@ -831,23 +729,7 @@ Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) { return ret; } -void Object::call_multilevel(const StringName &p_name, VARIANT_ARG_DECLARE) { - - VARIANT_ARGPTRS; - - int argc = 0; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) - break; - argc++; - } - - //Callable::CallError error; - call_multilevel(p_name, argptr, argc); -} - Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; if (p_method == CoreStringNames::get_singleton()->_free) { @@ -883,7 +765,6 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a ret = script_instance->call(p_method, p_args, p_argcount, r_error); //force jumptable switch (r_error.error) { - case Callable::CallError::CALL_OK: return ret; case Callable::CallError::CALL_ERROR_INVALID_METHOD: @@ -909,7 +790,6 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a } void Object::notification(int p_notification, bool p_reversed) { - _notificationv(p_notification, p_reversed); if (script_instance) { @@ -921,8 +801,9 @@ String Object::to_string() { if (script_instance) { bool valid; String ret = script_instance->to_string(&valid); - if (valid) + if (valid) { return ret; + } } return "[" + get_class() + ":" + itos(get_instance_id()) + "]"; } @@ -931,27 +812,18 @@ void Object::_changed_callback(Object *p_changed, const char *p_prop) { } void Object::add_change_receptor(Object *p_receptor) { - change_receptors.insert(p_receptor); } void Object::remove_change_receptor(Object *p_receptor) { - change_receptors.erase(p_receptor); } void Object::property_list_changed_notify() { - _change_notify(); } -void Object::cancel_delete() { - - _predelete_ok = true; -} - void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) { - //this function is not meant to be used in any of these ways ERR_FAIL_COND(p_script.is_null()); ERR_FAIL_COND(!p_instance); @@ -962,9 +834,9 @@ void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_ } void Object::set_script(const Variant &p_script) { - - if (script == p_script) + if (script == p_script) { return; + } if (script_instance) { memdelete(script_instance); @@ -989,43 +861,41 @@ void Object::set_script(const Variant &p_script) { } void Object::set_script_instance(ScriptInstance *p_instance) { - - if (script_instance == p_instance) + if (script_instance == p_instance) { return; + } - if (script_instance) + if (script_instance) { memdelete(script_instance); + } script_instance = p_instance; - if (p_instance) + if (p_instance) { script = p_instance->get_script(); - else + } else { script = Variant(); + } } Variant Object::get_script() const { - return script; } bool Object::has_meta(const String &p_name) const { - return metadata.has(p_name); } void Object::set_meta(const String &p_name, const Variant &p_value) { - if (p_value.get_type() == Variant::NIL) { metadata.erase(p_name); return; - }; + } metadata[p_name] = p_value; } Variant Object::get_meta(const String &p_name) const { - ERR_FAIL_COND_V(!metadata.has(p_name), Variant()); return metadata[p_name]; } @@ -1035,20 +905,17 @@ void Object::remove_meta(const String &p_name) { } Array Object::_get_property_list_bind() const { - List<PropertyInfo> lpi; get_property_list(&lpi); return convert_property_list(&lpi); } Array Object::_get_method_list_bind() const { - List<MethodInfo> ml; get_method_list(&ml); Array ret; for (List<MethodInfo>::Element *E = ml.front(); E; E = E->next()) { - Dictionary d = E->get(); //va.push_back(d); ret.push_back(d); @@ -1058,30 +925,26 @@ Array Object::_get_method_list_bind() const { } Vector<String> Object::_get_meta_list_bind() const { - Vector<String> _metaret; List<Variant> keys; metadata.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _metaret.push_back(E->get()); } return _metaret; } -void Object::get_meta_list(List<String> *p_list) const { +void Object::get_meta_list(List<String> *p_list) const { List<Variant> keys; metadata.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - p_list->push_back(E->get()); } } void Object::add_user_signal(const MethodInfo &p_signal) { - ERR_FAIL_COND_MSG(p_signal.name == "", "Signal name cannot be empty."); ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), "User signal's name conflicts with a built-in signal of '" + get_class_name() + "'."); ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'."); @@ -1091,20 +954,18 @@ void Object::add_user_signal(const MethodInfo &p_signal) { } bool Object::_has_user_signal(const StringName &p_name) const { - - if (!signal_map.has(p_name)) + if (!signal_map.has(p_name)) { return false; + } return signal_map[p_name].user.name.length() > 0; } struct _ObjectSignalDisconnectData { - StringName signal; Callable callable; }; Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; ERR_FAIL_COND_V(p_argcount < 1, Variant()); @@ -1132,9 +993,9 @@ Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::C } Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount) { - - if (_block_signals) + if (_block_signals) { return ERR_CANT_ACQUIRE_RESOURCE; //no emit, signals blocked + } SignalData *s = signal_map.getptr(p_name); if (!s) { @@ -1163,7 +1024,6 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int Error err = OK; for (int i = 0; i < ssize; i++) { - const Connection &c = slot_map.getv(i).conn; Object *target = c.callable.get_object(); @@ -1201,8 +1061,9 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int if (ce.error != Callable::CallError::CALL_OK) { #ifdef DEBUG_ENABLED - if (c.flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool())) + if (c.flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool())) { continue; + } #endif if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) { //most likely object is not initialized yet, do not throw error. @@ -1221,7 +1082,6 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int } #endif if (disconnect) { - _ObjectSignalDisconnectData dd; dd.signal = p_name; dd.callable = c.callable; @@ -1229,8 +1089,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int } } - while (!disconnect_data.empty()) { - + while (!disconnect_data.is_empty()) { const _ObjectSignalDisconnectData &dd = disconnect_data.front()->get(); _disconnect(dd.signal, dd.callable); @@ -1241,15 +1100,14 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int } Error Object::emit_signal(const StringName &p_name, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; int argc = 0; for (int i = 0; i < VARIANT_ARG_MAX; i++) { - - if (argptr[i]->get_type() == Variant::NIL) + if (argptr[i]->get_type() == Variant::NIL) { break; + } argc++; } @@ -1257,7 +1115,6 @@ Error Object::emit_signal(const StringName &p_name, VARIANT_ARG_DECLARE) { } void Object::_add_user_signal(const String &p_name, const Array &p_args) { - // this version of add_user_signal is meant to be used from scripts or external apis // without access to ADD_SIGNAL in bind_methods // added events are per instance, as opposed to the other ones, which are global @@ -1266,14 +1123,15 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) { mi.name = p_name; for (int i = 0; i < p_args.size(); i++) { - Dictionary d = p_args[i]; PropertyInfo param; - if (d.has("name")) + if (d.has("name")) { param.name = d["name"]; - if (d.has("type")) + } + if (d.has("type")) { param.type = (Variant::Type)(int)d["type"]; + } mi.arguments.push_back(param); } @@ -1282,13 +1140,11 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) { } Array Object::_get_signal_list() const { - List<MethodInfo> signal_list; get_signal_list(&signal_list); Array ret; for (List<MethodInfo>::Element *E = signal_list.front(); E; E = E->next()) { - ret.push_back(Dictionary(E->get())); } @@ -1296,14 +1152,12 @@ Array Object::_get_signal_list() const { } Array Object::_get_signal_connection_list(const String &p_signal) const { - List<Connection> conns; get_all_signal_connections(&conns); Array ret; for (List<Connection>::Element *E = conns.front(); E; E = E->next()) { - Connection &c = E->get(); if (c.signal.get_name() == p_signal) { ret.push_back(c); @@ -1314,7 +1168,6 @@ Array Object::_get_signal_connection_list(const String &p_signal) const { } Array Object::_get_incoming_connections() const { - Array ret; int connections_amount = connections.size(); for (int idx_conn = 0; idx_conn < connections_amount; idx_conn++) { @@ -1344,7 +1197,6 @@ bool Object::has_signal(const StringName &p_name) const { } void Object::get_signal_list(List<MethodInfo> *p_signals) const { - if (!script.is_null()) { Ref<Script> scr = script; if (scr.is_valid()) { @@ -1357,7 +1209,6 @@ void Object::get_signal_list(List<MethodInfo> *p_signals) const { const StringName *S = nullptr; while ((S = signal_map.next(S))) { - if (signal_map[*S].user.name != "") { //user signal p_signals->push_back(signal_map[*S].user); @@ -1366,37 +1217,33 @@ void Object::get_signal_list(List<MethodInfo> *p_signals) const { } void Object::get_all_signal_connections(List<Connection> *p_connections) const { - const StringName *S = nullptr; while ((S = signal_map.next(S))) { - const SignalData *s = &signal_map[*S]; for (int i = 0; i < s->slot_map.size(); i++) { - p_connections->push_back(s->slot_map.getv(i).conn); } } } void Object::get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const { - const SignalData *s = signal_map.getptr(p_signal); - if (!s) + if (!s) { return; //nothing + } - for (int i = 0; i < s->slot_map.size(); i++) + for (int i = 0; i < s->slot_map.size(); i++) { p_connections->push_back(s->slot_map.getv(i).conn); + } } int Object::get_persistent_signal_connection_count() const { - int count = 0; const StringName *S = nullptr; while ((S = signal_map.next(S))) { - const SignalData *s = &signal_map[*S]; for (int i = 0; i < s->slot_map.size(); i++) { @@ -1410,18 +1257,12 @@ int Object::get_persistent_signal_connection_count() const { } void Object::get_signals_connected_to_this(List<Connection> *p_connections) const { - for (const List<Connection>::Element *E = connections.front(); E; E = E->next()) { p_connections->push_back(E->get()); } } -Error Object::connect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds, uint32_t p_flags) { - - return connect(p_signal, Callable(p_to_object, p_to_method), p_binds, p_flags); -} Error Object::connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) { - ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER); Object *target_object = p_callable.get_object(); @@ -1432,7 +1273,6 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal); //check in script if (!signal_is_valid && !script.is_null()) { - if (Ref<Script>(script)->has_script_signal(p_signal)) { signal_is_valid = true; } @@ -1454,9 +1294,10 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co Callable target = p_callable; - if (s->slot_map.has(target)) { + //compare with the base callable, so binds can be ignored + if (s->slot_map.has(*target.get_base_comparator())) { if (p_flags & CONNECT_REFERENCE_COUNTED) { - s->slot_map[target].reference_count++; + s->slot_map[*target.get_base_comparator()].reference_count++; return OK; } else { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given callable '" + p_callable + "' in that object."); @@ -1476,58 +1317,54 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co slot.reference_count = 1; } - s->slot_map[target] = slot; + //use callable version as key, so binds can be ignored + s->slot_map[*target.get_base_comparator()] = slot; return OK; } -bool Object::is_connected_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const { - - return is_connected(p_signal, Callable(p_to_object, p_to_method)); -} - bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const { - ERR_FAIL_COND_V(p_callable.is_null(), false); const SignalData *s = signal_map.getptr(p_signal); if (!s) { bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal); - if (signal_is_valid) + if (signal_is_valid) { return false; + } - if (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)) + if (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)) { return false; + } ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + "."); } Callable target = p_callable; - return s->slot_map.has(target); + return s->slot_map.has(*target.get_base_comparator()); //const Map<Signal::Target,Signal::Slot>::Element *E = s->slot_map.find(target); //return (E!=nullptr ); } -void Object::disconnect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) { - - _disconnect(p_signal, Callable(p_to_object, p_to_method)); -} - void Object::disconnect(const StringName &p_signal, const Callable &p_callable) { _disconnect(p_signal, p_callable); } void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) { - ERR_FAIL_COND(p_callable.is_null()); Object *target_object = p_callable.get_object(); ERR_FAIL_COND(!target_object); SignalData *s = signal_map.getptr(p_signal); - ERR_FAIL_COND_MSG(!s, vformat("Nonexistent signal '%s' in %s.", p_signal, to_string())); + if (!s) { + bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) || + (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)); + ERR_FAIL_COND_MSG(signal_is_valid, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. signal: '" + p_signal + "', callable: '" + p_callable + "'."); + } + ERR_FAIL_COND_MSG(!s, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string())); - ERR_FAIL_COND_MSG(!s->slot_map.has(p_callable), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + "."); + ERR_FAIL_COND_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + "."); SignalData::Slot *slot = &s->slot_map[p_callable]; @@ -1539,74 +1376,79 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, } target_object->connections.erase(slot->cE); - s->slot_map.erase(p_callable); + s->slot_map.erase(*p_callable.get_base_comparator()); - if (s->slot_map.empty() && ClassDB::has_signal(get_class_name(), p_signal)) { + if (s->slot_map.is_empty() && ClassDB::has_signal(get_class_name(), p_signal)) { //not user signal, delete signal_map.erase(p_signal); } } void Object::_set_bind(const String &p_set, const Variant &p_value) { - set(p_set, p_value); } Variant Object::_get_bind(const String &p_name) const { - return get(p_name); } void Object::_set_indexed_bind(const NodePath &p_name, const Variant &p_value) { - set_indexed(p_name.get_as_property_path().get_subnames(), p_value); } Variant Object::_get_indexed_bind(const NodePath &p_name) const { - return get_indexed(p_name.get_as_property_path().get_subnames()); } void Object::initialize_class() { - static bool initialized = false; - if (initialized) + if (initialized) { return; + } ClassDB::_add_class<Object>(); _bind_methods(); initialized = true; } -StringName Object::tr(const StringName &p_message) const { - - if (!_can_translate || !TranslationServer::get_singleton()) +String Object::tr(const StringName &p_message, const StringName &p_context) const { + if (!_can_translate || !TranslationServer::get_singleton()) { return p_message; + } + return TranslationServer::get_singleton()->translate(p_message, p_context); +} - return TranslationServer::get_singleton()->translate(p_message); +String Object::tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (!_can_translate || !TranslationServer::get_singleton()) { + // Return message based on English plural rule if translation is not possible. + if (p_n == 1) { + return p_message; + } + return p_message_plural; + } + return TranslationServer::get_singleton()->translate_plural(p_message, p_message_plural, p_n, p_context); } void Object::_clear_internal_resource_paths(const Variant &p_var) { - switch (p_var.get_type()) { - case Variant::OBJECT: { - RES r = p_var; - if (!r.is_valid()) + if (!r.is_valid()) { return; + } - if (!r->get_path().begins_with("res://") || r->get_path().find("::") == -1) + if (!r->get_path().begins_with("res://") || r->get_path().find("::") == -1) { return; //not an internal resource + } Object *object = p_var; - if (!object) + if (!object) { return; + } r->set_path(""); r->clear_internal_resource_paths(); } break; case Variant::ARRAY: { - Array a = p_var; for (int i = 0; i < a.size(); i++) { _clear_internal_resource_paths(a[i]); @@ -1614,13 +1456,11 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) { } break; case Variant::DICTIONARY: { - Dictionary d = p_var; List<Variant> keys; d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _clear_internal_resource_paths(E->get()); _clear_internal_resource_paths(d[E->get()]); } @@ -1632,35 +1472,31 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) { #ifdef TOOLS_ENABLED void Object::editor_set_section_unfold(const String &p_section, bool p_unfolded) { - set_edited(true); - if (p_unfolded) + if (p_unfolded) { editor_section_folding.insert(p_section); - else + } else { editor_section_folding.erase(p_section); + } } bool Object::editor_is_section_unfolded(const String &p_section) { - return editor_section_folding.has(p_section); } #endif void Object::clear_internal_resource_paths() { - List<PropertyInfo> pinfo; get_property_list(&pinfo); for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - _clear_internal_resource_paths(get(E->get().name)); } } void Object::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_class"), &Object::get_class); ClassDB::bind_method(D_METHOD("is_class", "class"), &Object::is_class); ClassDB::bind_method(D_METHOD("set", "property", "value"), &Object::_set_bind); @@ -1730,7 +1566,8 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("set_message_translation", "enable"), &Object::set_message_translation); ClassDB::bind_method(D_METHOD("can_translate_messages"), &Object::can_translate_messages); - ClassDB::bind_method(D_METHOD("tr", "message"), &Object::tr); + ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL("")); + ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL("")); ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion); @@ -1765,7 +1602,6 @@ void Object::_bind_methods() { } void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) { - MessageQueue::get_singleton()->push_call(this, p_method, VARIANT_ARG_PASS); } @@ -1774,58 +1610,57 @@ void Object::set_deferred(const StringName &p_property, const Variant &p_value) } void Object::set_block_signals(bool p_block) { - _block_signals = p_block; } bool Object::is_blocking_signals() const { - return _block_signals; } void Object::get_translatable_strings(List<String> *p_strings) const { - List<PropertyInfo> plist; get_property_list(&plist); for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_INTERNATIONALIZED)) + if (!(E->get().usage & PROPERTY_USAGE_INTERNATIONALIZED)) { continue; + } String text = get(E->get().name); - if (text == "") + if (text == "") { continue; + } p_strings->push_back(text); } } Variant::Type Object::get_static_property_type(const StringName &p_property, bool *r_valid) const { - bool valid; Variant::Type t = ClassDB::get_property_type(get_class_name(), p_property, &valid); if (valid) { - if (r_valid) + if (r_valid) { *r_valid = true; + } return t; } if (get_script_instance()) { return get_script_instance()->get_property_type(p_property, r_valid); } - if (r_valid) + if (r_valid) { *r_valid = false; + } return Variant::NIL; } Variant::Type Object::get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid) const { - if (p_path.size() == 0) { - if (r_valid) + if (r_valid) { *r_valid = false; + } return Variant::NIL; } @@ -1833,34 +1668,39 @@ Variant::Type Object::get_static_property_type_indexed(const Vector<StringName> bool valid = false; Variant::Type t = get_static_property_type(p_path[0], &valid); if (!valid) { - if (r_valid) + if (r_valid) { *r_valid = false; + } return Variant::NIL; } Callable::CallError ce; - Variant check = Variant::construct(t, nullptr, 0, ce); + Variant check; + Variant::construct(t, check, nullptr, 0, ce); for (int i = 1; i < p_path.size(); i++) { if (check.get_type() == Variant::OBJECT || check.get_type() == Variant::DICTIONARY || check.get_type() == Variant::ARRAY) { // We cannot be sure about the type of properties this types can have - if (r_valid) + if (r_valid) { *r_valid = false; + } return Variant::NIL; } - check = check.get_named(p_path[i], &valid); + check = check.get_named(p_path[i], valid); if (!valid) { - if (r_valid) + if (r_valid) { *r_valid = false; + } return Variant::NIL; } } - if (r_valid) + if (r_valid) { *r_valid = true; + } return check.get_type(); } @@ -1871,18 +1711,15 @@ bool Object::is_queued_for_deletion() const { #ifdef TOOLS_ENABLED void Object::set_edited(bool p_edited) { - _edited = p_edited; _edited_version++; } bool Object::is_edited() const { - return _edited; } uint32_t Object::get_edited_version() const { - return _edited_version; } #endif @@ -1909,7 +1746,6 @@ void *Object::get_script_instance_binding(int p_script_language_index) { } bool Object::has_script_instance_binding(int p_script_language_index) { - return _script_instance_bindings[p_script_language_index] != nullptr; } @@ -1922,39 +1758,26 @@ void Object::set_script_instance_binding(int p_script_language_index, void *p_da void Object::_construct_object(bool p_reference) { type_is_reference = p_reference; - _class_ptr = nullptr; - _block_signals = false; - _predelete_ok = 0; _instance_id = ObjectDB::add_instance(this); - _can_translate = true; - _is_queued_for_deletion = false; - _emitting = false; - instance_binding_count = 0; memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS); - script_instance = nullptr; -#ifdef TOOLS_ENABLED - - _edited = false; - _edited_version = 0; -#endif #ifdef DEBUG_ENABLED _lock_index.init(1); #endif } + Object::Object(bool p_reference) { _construct_object(p_reference); } Object::Object() { - _construct_object(false); } Object::~Object() { - - if (script_instance) + if (script_instance) { memdelete(script_instance); + } script_instance = nullptr; const StringName *S = nullptr; @@ -1965,7 +1788,6 @@ Object::~Object() { } while ((S = signal_map.next(nullptr))) { - SignalData *s = &signal_map[*S]; //brute force disconnect for performance @@ -1973,7 +1795,6 @@ Object::~Object() { const VMap<Callable, SignalData::Slot>::Pair *slot_list = s->slot_map.get_array(); for (int i = 0; i < slot_count; i++) { - slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE); } @@ -1982,7 +1803,6 @@ Object::~Object() { //signals from nodes that connect to this node while (connections.size()) { - Connection c = connections.front()->get(); c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true); } @@ -2001,21 +1821,22 @@ Object::~Object() { } bool predelete_handler(Object *p_object) { - return p_object->_predelete(); } void postinitialize_handler(Object *p_object) { - p_object->_postinitialize(); } void ObjectDB::debug_objects(DebugFunc p_func) { - spin_lock.lock(); - for (uint32_t i = 0; i < slot_count; i++) { - uint32_t slot = object_slots[i].next_free; - p_func(object_slots[slot].object); + + for (uint32_t i = 0, count = slot_count; i < slot_max && count != 0; i++) { + if (object_slots[i].validator) { + p_func(object_slots[i].object); + + count--; + } } spin_lock.unlock(); } @@ -2030,15 +1851,12 @@ ObjectDB::ObjectSlot *ObjectDB::object_slots = nullptr; uint64_t ObjectDB::validator_counter = 0; int ObjectDB::get_object_count() { - return slot_count; } ObjectID ObjectDB::add_instance(Object *p_object) { - spin_lock.lock(); if (unlikely(slot_count == slot_max)) { - CRASH_COND(slot_count == (1 << OBJECTDB_SLOT_MAX_COUNT_BITS)); uint32_t new_slot_max = slot_max > 0 ? slot_max * 2 : 1; @@ -2114,30 +1932,41 @@ void ObjectDB::remove_instance(Object *p_object) { } void ObjectDB::setup() { - //nothing to do now } void ObjectDB::cleanup() { - if (slot_count > 0) { spin_lock.lock(); - WARN_PRINT("ObjectDB Instances still exist!"); + WARN_PRINT("ObjectDB instances leaked at exit (run with --verbose for details)."); if (OS::get_singleton()->is_stdout_verbose()) { - for (uint32_t i = 0; i < slot_count; i++) { - uint32_t slot = object_slots[i].next_free; - Object *obj = object_slots[slot].object; - - String node_name; - if (obj->is_class("Node")) - node_name = " - Node name: " + String(obj->call("get_name")); - if (obj->is_class("Resource")) - node_name = " - Resource name: " + String(obj->call("get_name")) + " Path: " + String(obj->call("get_path")); - - uint64_t id = uint64_t(slot) | (uint64_t(object_slots[slot].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[slot].is_reference ? OBJECTDB_REFERENCE_BIT : 0); - print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + node_name); + // Ensure calling the native classes because if a leaked instance has a script + // that overrides any of those methods, it'd not be OK to call them at this point, + // now the scripting languages have already been terminated. + MethodBind *node_get_name = ClassDB::get_method("Node", "get_name"); + MethodBind *resource_get_path = ClassDB::get_method("Resource", "get_path"); + Callable::CallError call_error; + + for (uint32_t i = 0, count = slot_count; i < slot_max && count != 0; i++) { + if (object_slots[i].validator) { + Object *obj = object_slots[i].object; + + String extra_info; + if (obj->is_class("Node")) { + extra_info = " - Node name: " + String(node_get_name->call(obj, nullptr, 0, call_error)); + } + if (obj->is_class("Resource")) { + extra_info = " - Resource path: " + String(resource_get_path->call(obj, nullptr, 0, call_error)); + } + + uint64_t id = uint64_t(i) | (uint64_t(object_slots[i].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[i].is_reference ? OBJECTDB_REFERENCE_BIT : 0); + print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + extra_info); + + count--; + } } + print_line("Hint: Leaked instances typically happen when nodes are removed from the scene tree (with `remove_child()`) but not freed (with `free()` or `queue_free()`)."); } spin_lock.unlock(); } diff --git a/core/object.h b/core/object/object.h index b40aef2a42..7e460462cf 100644 --- a/core/object.h +++ b/core/object/object.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,15 +31,16 @@ #ifndef OBJECT_H #define OBJECT_H -#include "core/hash_map.h" -#include "core/list.h" -#include "core/map.h" -#include "core/object_id.h" +#include "core/object/object_id.h" #include "core/os/rw_lock.h" -#include "core/set.h" -#include "core/spin_lock.h" -#include "core/variant.h" -#include "core/vmap.h" +#include "core/os/spin_lock.h" +#include "core/templates/hash_map.h" +#include "core/templates/list.h" +#include "core/templates/map.h" +#include "core/templates/set.h" +#include "core/templates/vmap.h" +#include "core/variant/callable_bind.h" +#include "core/variant/variant.h" #define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant() #define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5 @@ -97,7 +98,6 @@ enum PropertyHint { }; enum PropertyUsageFlags { - PROPERTY_USAGE_STORAGE = 1, PROPERTY_USAGE_EDITOR = 2, PROPERTY_USAGE_NETWORK = 4, @@ -124,6 +124,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24, PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading + PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor. PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, @@ -138,13 +139,12 @@ enum PropertyUsageFlags { #define ADD_SUBGROUP(m_name, m_prefix) ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix) struct PropertyInfo { - - Variant::Type type; + Variant::Type type = Variant::NIL; String name; StringName class_name; //for classes - PropertyHint hint; + PropertyHint hint = PROPERTY_HINT_NONE; String hint_string; - uint32_t usage; + uint32_t usage = PROPERTY_USAGE_DEFAULT; _FORCE_INLINE_ PropertyInfo added_usage(int p_fl) const { PropertyInfo pi = *this; @@ -156,11 +156,7 @@ struct PropertyInfo { static PropertyInfo from_dict(const Dictionary &p_dict); - PropertyInfo() : - type(Variant::NIL), - hint(PROPERTY_HINT_NONE), - usage(PROPERTY_USAGE_DEFAULT) { - } + PropertyInfo() {} PropertyInfo(Variant::Type p_type, const String p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = StringName()) : type(p_type), @@ -168,7 +164,6 @@ struct PropertyInfo { hint(p_hint), hint_string(p_hint_string), usage(p_usage) { - if (hint == PROPERTY_HINT_RESOURCE_TYPE) { class_name = hint_string; } else { @@ -178,10 +173,7 @@ struct PropertyInfo { PropertyInfo(const StringName &p_class_name) : type(Variant::OBJECT), - class_name(p_class_name), - hint(PROPERTY_HINT_NONE), - usage(PROPERTY_USAGE_DEFAULT) { - } + class_name(p_class_name) {} bool operator==(const PropertyInfo &p_info) const { return ((type == p_info.type) && @@ -200,11 +192,10 @@ struct PropertyInfo { Array convert_property_list(const List<PropertyInfo> *p_list); struct MethodInfo { - String name; PropertyInfo return_val; - uint32_t flags; - int id; + uint32_t flags; // NOLINT - prevent clang-tidy to assign method_bind.h constant here, it should stay in .cpp. + int id = 0; List<PropertyInfo> arguments; Vector<Variant> default_arguments; @@ -214,6 +205,7 @@ struct MethodInfo { operator Dictionary() const; static MethodInfo from_dict(const Dictionary &p_dict); + MethodInfo(); MethodInfo(const String &p_name); MethodInfo(const String &p_name, const PropertyInfo &p_param1); @@ -258,132 +250,142 @@ public: \ \ private: -#define GDCLASS(m_class, m_inherits) \ -private: \ - void operator=(const m_class &p_rval) {} \ - mutable StringName _class_name; \ - friend class ClassDB; \ - \ -public: \ - virtual String get_class() const { \ - return String(#m_class); \ - } \ - virtual const StringName *_get_class_namev() const { \ - if (!_class_name) \ - _class_name = get_class_static(); \ - return &_class_name; \ - } \ - static _FORCE_INLINE_ void *get_class_ptr_static() { \ - static int ptr; \ - return &ptr; \ - } \ - static _FORCE_INLINE_ String get_class_static() { \ - return String(#m_class); \ - } \ - static _FORCE_INLINE_ String get_parent_class_static() { \ - return m_inherits::get_class_static(); \ - } \ - static void get_inheritance_list_static(List<String> *p_inheritance_list) { \ - m_inherits::get_inheritance_list_static(p_inheritance_list); \ - p_inheritance_list->push_back(String(#m_class)); \ - } \ - static String get_category_static() { \ - String category = m_inherits::get_category_static(); \ - if (_get_category != m_inherits::_get_category) { \ - if (category != "") \ - category += "/"; \ - category += _get_category(); \ - } \ - return category; \ - } \ - static String inherits_static() { \ - return String(#m_inherits); \ - } \ - virtual bool is_class(const String &p_class) const { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \ - virtual bool is_class_ptr(void *p_ptr) const { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \ - \ - static void get_valid_parents_static(List<String> *p_parents) { \ - \ - if (m_class::_get_valid_parents_static != m_inherits::_get_valid_parents_static) { \ - m_class::_get_valid_parents_static(p_parents); \ - } \ - \ - m_inherits::get_valid_parents_static(p_parents); \ - } \ - \ -protected: \ - _FORCE_INLINE_ static void (*_get_bind_methods())() { \ - return &m_class::_bind_methods; \ - } \ - \ -public: \ - static void initialize_class() { \ - static bool initialized = false; \ - if (initialized) \ - return; \ - m_inherits::initialize_class(); \ - ClassDB::_add_class<m_class>(); \ - if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) \ - _bind_methods(); \ - initialized = true; \ - } \ - \ -protected: \ - virtual void _initialize_classv() { \ - initialize_class(); \ - } \ - _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \ - return (bool (Object::*)(const StringName &, Variant &) const) & m_class::_get; \ - } \ - virtual bool _getv(const StringName &p_name, Variant &r_ret) const { \ - if (m_class::_get_get() != m_inherits::_get_get()) { \ - if (_get(p_name, r_ret)) \ - return true; \ - } \ - return m_inherits::_getv(p_name, r_ret); \ - } \ - _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \ - return (bool (Object::*)(const StringName &, const Variant &)) & m_class::_set; \ - } \ - virtual bool _setv(const StringName &p_name, const Variant &p_property) { \ - if (m_inherits::_setv(p_name, p_property)) return true; \ - if (m_class::_get_set() != m_inherits::_get_set()) { \ - return _set(p_name, p_property); \ - } \ - return false; \ - } \ - _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> * p_list) const { \ - return (void (Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \ - } \ - virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const { \ - if (!p_reversed) { \ - m_inherits::_get_property_listv(p_list, p_reversed); \ - } \ - p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); \ - if (!_is_gpl_reversed()) \ - ClassDB::get_property_list(#m_class, p_list, true, this); \ - if (m_class::_get_get_property_list() != m_inherits::_get_get_property_list()) { \ - _get_property_list(p_list); \ - } \ - if (_is_gpl_reversed()) \ - ClassDB::get_property_list(#m_class, p_list, true, this); \ - if (p_reversed) { \ - m_inherits::_get_property_listv(p_list, p_reversed); \ - } \ - } \ - _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \ - return (void (Object::*)(int)) & m_class::_notification; \ - } \ - virtual void _notificationv(int p_notification, bool p_reversed) { \ - if (!p_reversed) \ - m_inherits::_notificationv(p_notification, p_reversed); \ - if (m_class::_get_notification() != m_inherits::_get_notification()) { \ - _notification(p_notification); \ - } \ - if (p_reversed) \ - m_inherits::_notificationv(p_notification, p_reversed); \ - } \ - \ +#define GDCLASS(m_class, m_inherits) \ +private: \ + void operator=(const m_class &p_rval) {} \ + mutable StringName _class_name; \ + friend class ClassDB; \ + \ +public: \ + virtual String get_class() const override { \ + return String(#m_class); \ + } \ + virtual const StringName *_get_class_namev() const override { \ + if (!_class_name) { \ + _class_name = get_class_static(); \ + } \ + return &_class_name; \ + } \ + static _FORCE_INLINE_ void *get_class_ptr_static() { \ + static int ptr; \ + return &ptr; \ + } \ + static _FORCE_INLINE_ String get_class_static() { \ + return String(#m_class); \ + } \ + static _FORCE_INLINE_ String get_parent_class_static() { \ + return m_inherits::get_class_static(); \ + } \ + static void get_inheritance_list_static(List<String> *p_inheritance_list) { \ + m_inherits::get_inheritance_list_static(p_inheritance_list); \ + p_inheritance_list->push_back(String(#m_class)); \ + } \ + static String get_category_static() { \ + String category = m_inherits::get_category_static(); \ + if (_get_category != m_inherits::_get_category) { \ + if (category != "") { \ + category += "/"; \ + } \ + category += _get_category(); \ + } \ + return category; \ + } \ + static String inherits_static() { \ + return String(#m_inherits); \ + } \ + virtual bool is_class(const String &p_class) const override { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \ + virtual bool is_class_ptr(void *p_ptr) const override { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \ + \ + static void get_valid_parents_static(List<String> *p_parents) { \ + if (m_class::_get_valid_parents_static != m_inherits::_get_valid_parents_static) { \ + m_class::_get_valid_parents_static(p_parents); \ + } \ + \ + m_inherits::get_valid_parents_static(p_parents); \ + } \ + \ +protected: \ + _FORCE_INLINE_ static void (*_get_bind_methods())() { \ + return &m_class::_bind_methods; \ + } \ + \ +public: \ + static void initialize_class() { \ + static bool initialized = false; \ + if (initialized) { \ + return; \ + } \ + m_inherits::initialize_class(); \ + ClassDB::_add_class<m_class>(); \ + if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \ + _bind_methods(); \ + } \ + initialized = true; \ + } \ + \ +protected: \ + virtual void _initialize_classv() override { \ + initialize_class(); \ + } \ + _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \ + return (bool (Object::*)(const StringName &, Variant &) const) & m_class::_get; \ + } \ + virtual bool _getv(const StringName &p_name, Variant &r_ret) const override { \ + if (m_class::_get_get() != m_inherits::_get_get()) { \ + if (_get(p_name, r_ret)) { \ + return true; \ + } \ + } \ + return m_inherits::_getv(p_name, r_ret); \ + } \ + _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \ + return (bool (Object::*)(const StringName &, const Variant &)) & m_class::_set; \ + } \ + virtual bool _setv(const StringName &p_name, const Variant &p_property) override { \ + if (m_inherits::_setv(p_name, p_property)) { \ + return true; \ + } \ + if (m_class::_get_set() != m_inherits::_get_set()) { \ + return _set(p_name, p_property); \ + } \ + return false; \ + } \ + _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> * p_list) const { \ + return (void (Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \ + } \ + virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const override { \ + if (!p_reversed) { \ + m_inherits::_get_property_listv(p_list, p_reversed); \ + } \ + p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); \ + if (!_is_gpl_reversed()) { \ + ClassDB::get_property_list(#m_class, p_list, true, this); \ + } \ + if (m_class::_get_get_property_list() != m_inherits::_get_get_property_list()) { \ + _get_property_list(p_list); \ + } \ + if (_is_gpl_reversed()) { \ + ClassDB::get_property_list(#m_class, p_list, true, this); \ + } \ + if (p_reversed) { \ + m_inherits::_get_property_listv(p_list, p_reversed); \ + } \ + } \ + _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \ + return (void (Object::*)(int)) & m_class::_notification; \ + } \ + virtual void _notificationv(int p_notification, bool p_reversed) override { \ + if (!p_reversed) { \ + m_inherits::_notificationv(p_notification, p_reversed); \ + } \ + if (m_class::_get_notification() != m_inherits::_get_notification()) { \ + _notification(p_notification); \ + } \ + if (p_reversed) { \ + m_inherits::_notificationv(p_notification, p_reversed); \ + } \ + } \ + \ private: #define OBJ_CATEGORY(m_category) \ @@ -392,10 +394,10 @@ protected: \ \ private: -#define OBJ_SAVE_TYPE(m_class) \ -public: \ - virtual String get_save_class() const { return #m_class; } \ - \ +#define OBJ_SAVE_TYPE(m_class) \ +public: \ + virtual String get_save_class() const override { return #m_class; } \ + \ private: class ScriptInstance; @@ -403,7 +405,6 @@ class ScriptInstance; class Object { public: enum ConnectFlags { - CONNECT_DEFERRED = 1, CONNECT_PERSIST = 2, // hint for scene to save this connection CONNECT_ONESHOT = 4, @@ -411,18 +412,16 @@ public: }; struct Connection { - ::Signal signal; Callable callable; - uint32_t flags; + uint32_t flags = 0; Vector<Variant> binds; bool operator<(const Connection &p_conn) const; operator Variant() const; - Connection() { - flags = 0; - } + + Connection() {} Connection(const Variant &p_variant); }; @@ -438,18 +437,14 @@ private: friend void postinitialize_handler(Object *); struct SignalData { - struct Slot { - - int reference_count; + int reference_count = 0; Connection conn; - List<Connection>::Element *cE; - Slot() { reference_count = 0; } + List<Connection>::Element *cE = nullptr; }; MethodInfo user; VMap<Callable, Slot> slot_map; - SignalData() {} }; HashMap<StringName, SignalData> signal_map; @@ -457,24 +452,24 @@ private: #ifdef DEBUG_ENABLED SafeRefCount _lock_index; #endif - bool _block_signals; - int _predelete_ok; + bool _block_signals = false; + int _predelete_ok = 0; Set<Object *> change_receptors; ObjectID _instance_id; bool _predelete(); void _postinitialize(); - bool _can_translate; - bool _emitting; + bool _can_translate = true; + bool _emitting = false; #ifdef TOOLS_ENABLED - bool _edited; - uint32_t _edited_version; + bool _edited = false; + uint32_t _edited_version = 0; Set<String> editor_section_folding; #endif - ScriptInstance *script_instance; + ScriptInstance *script_instance = nullptr; Variant script; //reference does not yet exist, store it in a Dictionary metadata; mutable StringName _class_name; - mutable const StringName *_class_ptr; + mutable const StringName *_class_ptr = nullptr; void _add_user_signal(const String &p_name, const Array &p_args = Array()); bool _has_user_signal(const StringName &p_name) const; @@ -487,14 +482,13 @@ private: void _set_indexed_bind(const NodePath &p_name, const Variant &p_value); Variant _get_indexed_bind(const NodePath &p_name) const; - void property_list_changed_notify(); - _FORCE_INLINE_ void _construct_object(bool p_reference); friend class Reference; bool type_is_reference = false; - uint32_t instance_binding_count; + uint32_t instance_binding_count = 0; void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; + Object(bool p_reference); protected: @@ -502,14 +496,14 @@ protected: virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; }; virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; }; virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const {}; - virtual void _notificationv(int p_notification, bool p_reversed){}; + virtual void _notificationv(int p_notification, bool p_reversed) {} static String _get_category() { return ""; } static void _bind_methods(); bool _set(const StringName &p_name, const Variant &p_property) { return false; }; bool _get(const StringName &p_name, Variant &r_property) const { return false; }; void _get_property_list(List<PropertyInfo> *p_list) const {}; - void _notification(int p_notification){}; + void _notification(int p_notification) {} _FORCE_INLINE_ static void (*_get_bind_methods())() { return &Object::_bind_methods; @@ -529,8 +523,7 @@ protected: static void get_valid_parents_static(List<String> *p_parents); static void _get_valid_parents_static(List<String> *p_parents); - void cancel_delete(); - + void property_list_changed_notify(); virtual void _changed_callback(Object *p_changed, const char *p_prop); //Variant _call_bind(const StringName& p_name, const Variant& p_arg1 = Variant(), const Variant& p_arg2 = Variant(), const Variant& p_arg3 = Variant(), const Variant& p_arg4 = Variant()); @@ -540,8 +533,9 @@ protected: Variant _call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); virtual const StringName *_get_class_namev() const { - if (!_class_name) + if (!_class_name) { _class_name = get_class_static(); + } return &_class_name; } @@ -558,14 +552,15 @@ protected: public: //should be protected, but bug in clang++ static void initialize_class(); - _FORCE_INLINE_ static void register_custom_data_to_otdb(){}; + _FORCE_INLINE_ static void register_custom_data_to_otdb() {} public: #ifdef TOOLS_ENABLED _FORCE_INLINE_ void _change_notify(const char *p_property = "") { _edited = true; - for (Set<Object *>::Element *E = change_receptors.front(); E; E = E->next()) + for (Set<Object *>::Element *E = change_receptors.front(); E; E = E->next()) { ((Object *)(E->get()))->_changed_callback(this, p_property); + } } #else _FORCE_INLINE_ void _change_notify(const char *p_what = "") {} @@ -578,8 +573,8 @@ public: bool _is_gpl_reversed() const { return false; } _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; } - // this is used for editors + // this is used for editors void add_change_receptor(Object *p_receptor); void remove_change_receptor(Object *p_receptor); @@ -588,12 +583,14 @@ public: #ifndef NO_SAFE_CAST return dynamic_cast<T *>(p_object); #else - if (!p_object) + if (!p_object) { return nullptr; - if (p_object->is_class_ptr(T::get_class_ptr_static())) + } + if (p_object->is_class_ptr(T::get_class_ptr_static())) { return static_cast<T *>(p_object); - else + } else { return nullptr; + } #endif } @@ -602,17 +599,18 @@ public: #ifndef NO_SAFE_CAST return dynamic_cast<const T *>(p_object); #else - if (!p_object) + if (!p_object) { return nullptr; - if (p_object->is_class_ptr(T::get_class_ptr_static())) + } + if (p_object->is_class_ptr(T::get_class_ptr_static())) { return static_cast<const T *>(p_object); - else + } else { return nullptr; + } #endif } enum { - NOTIFICATION_POSTINITIALIZE = 0, NOTIFICATION_PREDELETE = 1 }; @@ -653,13 +651,10 @@ public: void get_method_list(List<MethodInfo> *p_list) const; Variant callv(const StringName &p_method, const Array &p_args); virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); - virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount); - virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); Variant call(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper - void call_multilevel(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper void notification(int p_notification, bool p_reversed = false); - String to_string(); + virtual String to_string(); //used mainly by script, get and set all INCLUDING string virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const; @@ -699,10 +694,6 @@ public: int get_persistent_signal_connection_count() const; void get_signals_connected_to_this(List<Connection> *p_connections) const; - Error connect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0); - void disconnect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method); - bool is_connected_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const; - Error connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0); void disconnect(const StringName &p_signal, const Callable &p_callable); bool is_connected(const StringName &p_signal, const Callable &p_callable) const; @@ -720,9 +711,10 @@ public: virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; - StringName tr(const StringName &p_message) const; // translate message (internationalization) + String tr(const StringName &p_message, const StringName &p_context = "") const; // translate message (internationalization) + String tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; - bool _is_queued_for_deletion; // set to true by SceneTree::queue_delete() + bool _is_queued_for_deletion = false; // set to true by SceneTree::queue_delete() bool is_queued_for_deletion() const; _FORCE_INLINE_ void set_message_translation(bool p_enable) { _can_translate = p_enable; } @@ -744,6 +736,7 @@ public: void clear_internal_resource_paths(); _ALWAYS_INLINE_ bool is_reference() const { return type_is_reference; } + Object(); virtual ~Object(); }; @@ -752,7 +745,6 @@ bool predelete_handler(Object *p_object); void postinitialize_handler(Object *p_object); class ObjectDB { - //this needs to add up to 63, 1 bit is for reference #define OBJECTDB_VALIDATOR_BITS 39 #define OBJECTDB_VALIDATOR_MASK ((uint64_t(1) << OBJECTDB_VALIDATOR_BITS) - 1) @@ -787,7 +779,6 @@ public: typedef void (*DebugFunc)(Object *p_obj); _ALWAYS_INLINE_ static Object *get_instance(ObjectID p_instance_id) { - uint64_t id = p_instance_id; uint32_t slot = id & OBJECTDB_SLOT_MAX_COUNT_MASK; @@ -812,7 +803,4 @@ public: static int get_object_count(); }; -//needed by macros -#include "core/class_db.h" - #endif // OBJECT_H diff --git a/core/object_id.h b/core/object/object_id.h index 63b0c27af8..7f2496ad48 100644 --- a/core/object_id.h +++ b/core/object/object_id.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/reference.cpp b/core/object/reference.cpp index dd65ccce69..71a52a9ba5 100644 --- a/core/reference.cpp +++ b/core/object/reference.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,25 +30,21 @@ #include "reference.h" -#include "core/script_language.h" +#include "core/object/script_language.h" bool Reference::init_ref() { - if (reference()) { - if (!is_referenced() && refcount_init.unref()) { unreference(); // first referencing is already 1, so compensate for the ref above } return true; } else { - return false; } } void Reference::_bind_methods() { - ClassDB::bind_method(D_METHOD("init_ref"), &Reference::init_ref); ClassDB::bind_method(D_METHOD("reference"), &Reference::reference); ClassDB::bind_method(D_METHOD("unreference"), &Reference::unreference); @@ -59,7 +55,6 @@ int Reference::reference_get_count() const { } bool Reference::reference() { - uint32_t rc_val = refcount.refval(); bool success = rc_val != 0; @@ -80,7 +75,6 @@ bool Reference::reference() { } bool Reference::unreference() { - uint32_t rc_val = refcount.unrefval(); bool die = rc_val == 0; @@ -104,25 +98,21 @@ bool Reference::unreference() { Reference::Reference() : Object(true) { - refcount.init(); refcount_init.init(); } -Reference::~Reference() { -} - Variant WeakRef::get_ref() const { - - if (ref.is_null()) + if (ref.is_null()) { return Variant(); + } Object *obj = ObjectDB::get_instance(ref); - if (!obj) + if (!obj) { return Variant(); + } Reference *r = cast_to<Reference>(obj); if (r) { - return REF(r); } @@ -134,14 +124,9 @@ void WeakRef::set_obj(Object *p_object) { } void WeakRef::set_ref(const REF &p_ref) { - ref = p_ref.is_valid() ? p_ref->get_instance_id() : ObjectID(); } -WeakRef::WeakRef() { -} - void WeakRef::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_ref"), &WeakRef::get_ref); } diff --git a/core/reference.h b/core/object/reference.h index 30a93d82a6..d02cb12069 100644 --- a/core/reference.h +++ b/core/object/reference.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,12 +31,10 @@ #ifndef REFERENCE_H #define REFERENCE_H -#include "core/class_db.h" -#include "core/object.h" -#include "core/safe_refcount.h" +#include "core/object/class_db.h" +#include "core/templates/safe_refcount.h" class Reference : public Object { - GDCLASS(Reference, Object); SafeRefCount refcount; SafeRefCount refcount_init; @@ -52,32 +50,32 @@ public: int reference_get_count() const; Reference(); - ~Reference(); + ~Reference() {} }; template <class T> class Ref { - - T *reference; + T *reference = nullptr; void ref(const Ref &p_from) { - - if (p_from.reference == reference) + if (p_from.reference == reference) { return; + } unref(); reference = p_from.reference; - if (reference) + if (reference) { reference->reference(); + } } void ref_pointer(T *p_ref) { - ERR_FAIL_COND(!p_ref); - if (p_ref->init_ref()) + if (p_ref->init_ref()) { reference = p_ref; + } } //virtual Reference * get_reference() const { return reference; } @@ -90,60 +88,48 @@ public: } _FORCE_INLINE_ bool operator<(const Ref<T> &p_r) const { - return reference < p_r.reference; } _FORCE_INLINE_ bool operator==(const Ref<T> &p_r) const { - return reference == p_r.reference; } _FORCE_INLINE_ bool operator!=(const Ref<T> &p_r) const { - return reference != p_r.reference; } _FORCE_INLINE_ T *operator->() { - return reference; } _FORCE_INLINE_ T *operator*() { - return reference; } _FORCE_INLINE_ const T *operator->() const { - return reference; } _FORCE_INLINE_ const T *ptr() const { - return reference; } _FORCE_INLINE_ T *ptr() { - return reference; } _FORCE_INLINE_ const T *operator*() const { - return reference; } operator Variant() const { - return Variant(reference); } void operator=(const Ref &p_from) { - ref(p_from); } template <class T_Other> void operator=(const Ref<T_Other> &p_from) { - Reference *refb = const_cast<Reference *>(static_cast<const Reference *>(p_from.ptr())); if (!refb) { unref(); @@ -156,7 +142,6 @@ public: } void operator=(const Variant &p_variant) { - Object *object = p_variant.get_validated_object(); if (object == reference) { @@ -189,15 +174,11 @@ public: } Ref(const Ref &p_from) { - - reference = nullptr; ref(p_from); } template <class T_Other> Ref(const Ref<T_Other> &p_from) { - - reference = nullptr; Reference *refb = const_cast<Reference *>(static_cast<const Reference *>(p_from.ptr())); if (!refb) { unref(); @@ -210,26 +191,21 @@ public: } Ref(T *p_reference) { - - reference = nullptr; - if (p_reference) + if (p_reference) { ref_pointer(p_reference); + } } Ref(const Variant &p_variant) { - Object *object = p_variant.get_validated_object(); if (!object) { - reference = nullptr; return; } T *r = Object::cast_to<T>(object); if (r && r->reference()) { reference = r; - } else { - reference = nullptr; } } @@ -242,7 +218,6 @@ public: // mutexes will avoid more crashes? if (reference && reference->unreference()) { - memdelete(reference); } reference = nullptr; @@ -252,13 +227,9 @@ public: ref(memnew(T)); } - Ref() { - - reference = nullptr; - } + Ref() {} ~Ref() { - unref(); } }; @@ -266,7 +237,6 @@ public: typedef Ref<Reference> REF; class WeakRef : public Reference { - GDCLASS(WeakRef, Reference); ObjectID ref; @@ -279,36 +249,27 @@ public: void set_obj(Object *p_object); void set_ref(const REF &p_ref); - WeakRef(); + WeakRef() {} }; -#ifdef PTRCALL_ENABLED - template <class T> struct PtrToArg<Ref<T>> { - _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { - return Ref<T>(const_cast<T *>(reinterpret_cast<const T *>(p_ptr))); } _FORCE_INLINE_ static void encode(Ref<T> p_val, const void *p_ptr) { - *(Ref<Reference> *)p_ptr = p_val; } }; template <class T> struct PtrToArg<const Ref<T> &> { - _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { - return Ref<T>((T *)p_ptr); } }; -#endif // PTRCALL_ENABLED - #ifdef DEBUG_METHODS_ENABLED template <class T> diff --git a/core/script_language.cpp b/core/object/script_language.cpp index 82cac6bc9a..c3f109a147 100644 --- a/core/script_language.cpp +++ b/core/object/script_language.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,11 @@ #include "script_language.h" +#include "core/config/project_settings.h" #include "core/core_string_names.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" -#include "core/project_settings.h" + #include <stdint.h> ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; @@ -45,11 +46,10 @@ bool ScriptServer::languages_finished = false; ScriptEditRequestFunction ScriptServer::edit_request_func = nullptr; void Script::_notification(int p_what) { - if (p_what == NOTIFICATION_POSTINITIALIZE) { - - if (EngineDebugger::is_active()) + if (EngineDebugger::is_active()) { EngineDebugger::get_script_debugger()->set_break_language(get_language()); + } } } @@ -100,7 +100,6 @@ Dictionary Script::_get_script_constant_map() { } void Script::_bind_methods() { - ClassDB::bind_method(D_METHOD("can_instance"), &Script::can_instance); //ClassDB::bind_method(D_METHOD("instance_create","base_object"),&Script::instance_create); ClassDB::bind_method(D_METHOD("instance_has", "base_object"), &Script::instance_has); @@ -125,30 +124,25 @@ void Script::_bind_methods() { } void ScriptServer::set_scripting_enabled(bool p_enabled) { - scripting_enabled = p_enabled; } bool ScriptServer::is_scripting_enabled() { - return scripting_enabled; } ScriptLanguage *ScriptServer::get_language(int p_idx) { - ERR_FAIL_INDEX_V(p_idx, _language_count, nullptr); return _languages[p_idx]; } void ScriptServer::register_language(ScriptLanguage *p_language) { - ERR_FAIL_COND(_language_count >= MAX_LANGUAGES); _languages[_language_count++] = p_language; } void ScriptServer::unregister_language(ScriptLanguage *p_language) { - for (int i = 0; i < _language_count; i++) { if (_languages[i] == p_language) { _language_count--; @@ -161,7 +155,6 @@ void ScriptServer::unregister_language(ScriptLanguage *p_language) { } void ScriptServer::init_languages() { - { //load global classes global_classes_clear(); if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { @@ -169,8 +162,9 @@ void ScriptServer::init_languages() { for (int i = 0; i < script_classes.size(); i++) { Dictionary c = script_classes[i]; - if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) + if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) { continue; + } add_global_class(c["class"], c["base"], c["language"], c["path"]); } } @@ -182,7 +176,6 @@ void ScriptServer::init_languages() { } void ScriptServer::finish_languages() { - for (int i = 0; i < _language_count; i++) { _languages[i]->finish(); } @@ -191,24 +184,20 @@ void ScriptServer::finish_languages() { } void ScriptServer::set_reload_scripts_on_save(bool p_enable) { - reload_scripts_on_save = p_enable; } bool ScriptServer::is_reload_scripts_on_save_enabled() { - return reload_scripts_on_save; } void ScriptServer::thread_enter() { - for (int i = 0; i < _language_count; i++) { _languages[i]->thread_enter(); } } void ScriptServer::thread_exit() { - for (int i = 0; i < _language_count; i++) { _languages[i]->thread_exit(); } @@ -228,16 +217,20 @@ void ScriptServer::add_global_class(const StringName &p_class, const StringName g.base = p_base; global_classes[p_class] = g; } + void ScriptServer::remove_global_class(const StringName &p_class) { global_classes.erase(p_class); } + bool ScriptServer::is_global_class(const StringName &p_class) { return global_classes.has(p_class); } + StringName ScriptServer::get_global_class_language(const StringName &p_class) { ERR_FAIL_COND_V(!global_classes.has(p_class), StringName()); return global_classes[p_class].language; } + String ScriptServer::get_global_class_path(const String &p_class) { ERR_FAIL_COND_V(!global_classes.has(p_class), String()); return global_classes[p_class].path; @@ -247,6 +240,7 @@ StringName ScriptServer::get_global_class_base(const String &p_class) { ERR_FAIL_COND_V(!global_classes.has(p_class), String()); return global_classes[p_class].base; } + StringName ScriptServer::get_global_class_native_base(const String &p_class) { ERR_FAIL_COND_V(!global_classes.has(p_class), String()); String base = global_classes[p_class].base; @@ -255,6 +249,7 @@ StringName ScriptServer::get_global_class_native_base(const String &p_class) { } return base; } + void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { const StringName *K = nullptr; List<StringName> classes; @@ -266,6 +261,7 @@ void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { r_global_classes->push_back(E->get()); } } + void ScriptServer::save_global_classes() { List<StringName> gc; get_global_class_list(&gc); @@ -279,33 +275,38 @@ void ScriptServer::save_global_classes() { gcarr.push_back(d); } - ProjectSettings::get_singleton()->set("_global_script_classes", gcarr); + if (gcarr.is_empty()) { + if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { + ProjectSettings::get_singleton()->clear("_global_script_classes"); + } + } else { + ProjectSettings::get_singleton()->set("_global_script_classes", gcarr); + } ProjectSettings::get_singleton()->save(); } //////////////////// void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) { - List<PropertyInfo> pinfo; get_property_list(&pinfo); for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE) { Pair<StringName, Variant> p; p.first = E->get().name; - if (get(p.first, p.second)) + if (get(p.first, p.second)) { state.push_back(p); + } } } } Variant ScriptInstance::call(const StringName &p_method, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; int argc = 0; for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) + if (argptr[i]->get_type() == Variant::NIL) { break; + } argc++; } @@ -313,38 +314,17 @@ Variant ScriptInstance::call(const StringName &p_method, VARIANT_ARG_DECLARE) { return call(p_method, argptr, argc, error); } -void ScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) { - Callable::CallError ce; - call(p_method, p_args, p_argcount, ce); // script may not support multilevel calls -} - -void ScriptInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) { - Callable::CallError ce; - call(p_method, p_args, p_argcount, ce); // script may not support multilevel calls -} - void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) { - if (r_valid) + if (r_valid) { *r_valid = false; + } } Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid) { - if (r_valid) + if (r_valid) { *r_valid = false; - return Variant(); -} - -void ScriptInstance::call_multilevel(const StringName &p_method, VARIANT_ARG_DECLARE) { - - VARIANT_ARGPTRS; - int argc = 0; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) - break; - argc++; } - - call_multilevel(p_method, argptr, argc); + return Variant(); } ScriptInstance::~ScriptInstance() { @@ -355,13 +335,46 @@ ScriptCodeCompletionCache::ScriptCodeCompletionCache() { singleton = this; } +void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const { + p_core_type_words->push_back("String"); + p_core_type_words->push_back("Vector2"); + p_core_type_words->push_back("Vector2i"); + p_core_type_words->push_back("Rect2"); + p_core_type_words->push_back("Rect2i"); + p_core_type_words->push_back("Vector3"); + p_core_type_words->push_back("Vector3i"); + p_core_type_words->push_back("Transform2D"); + p_core_type_words->push_back("Plane"); + p_core_type_words->push_back("Quat"); + p_core_type_words->push_back("AABB"); + p_core_type_words->push_back("Basis"); + p_core_type_words->push_back("Transform"); + p_core_type_words->push_back("Color"); + p_core_type_words->push_back("StringName"); + p_core_type_words->push_back("NodePath"); + p_core_type_words->push_back("RID"); + p_core_type_words->push_back("Callable"); + p_core_type_words->push_back("Signal"); + p_core_type_words->push_back("Dictionary"); + p_core_type_words->push_back("Array"); + p_core_type_words->push_back("PackedByteArray"); + p_core_type_words->push_back("PackedInt32Array"); + p_core_type_words->push_back("PackedInt64Array"); + p_core_type_words->push_back("PackedFloat32Array"); + p_core_type_words->push_back("PackedFloat64Array"); + p_core_type_words->push_back("PackedStringArray"); + p_core_type_words->push_back("PackedVector2Array"); + p_core_type_words->push_back("PackedVector3Array"); + p_core_type_words->push_back("PackedColorArray"); +} + void ScriptLanguage::frame() { } bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) { - - if (script->is_placeholder_fallback_enabled()) + if (script->is_placeholder_fallback_enabled()) { return false; + } if (values.has(p_name)) { Variant defval; @@ -384,8 +397,8 @@ bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_v } return false; } -bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) const { +bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) const { if (values.has(p_name)) { r_ret = values[p_name]; return true; @@ -408,7 +421,6 @@ bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) co } void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { - if (script->is_placeholder_fallback_enabled()) { for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { p_properties->push_back(E->get()); @@ -425,38 +437,41 @@ void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properti } Variant::Type PlaceHolderScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { - if (values.has(p_name)) { - if (r_is_valid) + if (r_is_valid) { *r_is_valid = true; + } return values[p_name].get_type(); } if (constants.has(p_name)) { - if (r_is_valid) + if (r_is_valid) { *r_is_valid = true; + } return constants[p_name].get_type(); } - if (r_is_valid) + if (r_is_valid) { *r_is_valid = false; + } return Variant::NIL; } void PlaceHolderScriptInstance::get_method_list(List<MethodInfo> *p_list) const { - - if (script->is_placeholder_fallback_enabled()) + if (script->is_placeholder_fallback_enabled()) { return; + } if (script.is_valid()) { script->get_script_method_list(p_list); } } -bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { - if (script->is_placeholder_fallback_enabled()) +bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { + if (script->is_placeholder_fallback_enabled()) { return false; + } if (script.is_valid()) { return script->has_method(p_method); @@ -465,17 +480,15 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { } void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) { - Set<StringName> new_values; for (const List<PropertyInfo>::Element *E = p_properties.front(); E; E = E->next()) { - StringName n = E->get().name; new_values.insert(n); if (!values.has(n) || values[n].get_type() != E->get().type) { - - if (p_values.has(n)) + if (p_values.has(n)) { values[n] = p_values[n]; + } } } @@ -483,9 +496,9 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c List<StringName> to_remove; for (Map<StringName, Variant>::Element *E = values.front(); E; E = E->next()) { - - if (!new_values.has(E->key())) + if (!new_values.has(E->key())) { to_remove.push_back(E->key()); + } Variant defval; if (script->get_property_default_value(E->key(), defval)) { @@ -497,13 +510,11 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c } while (to_remove.size()) { - values.erase(to_remove.front()->get()); to_remove.pop_front(); } if (owner && owner->get_script_instance() == this) { - owner->_change_notify(); } //change notify @@ -513,7 +524,6 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c } void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid) { - if (script->is_placeholder_fallback_enabled()) { Map<StringName, Variant>::Element *E = values.find(p_name); @@ -535,31 +545,34 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, } } - if (r_valid) + if (r_valid) { *r_valid = false; // Cannot change the value in either case + } } Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_name, bool *r_valid) { - if (script->is_placeholder_fallback_enabled()) { const Map<StringName, Variant>::Element *E = values.find(p_name); if (E) { - if (r_valid) + if (r_valid) { *r_valid = true; + } return E->value(); } E = constants.find(p_name); if (E) { - if (r_valid) + if (r_valid) { *r_valid = true; + } return E->value(); } } - if (r_valid) + if (r_valid) { *r_valid = false; + } return Variant(); } @@ -579,7 +592,6 @@ PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, } PlaceHolderScriptInstance::~PlaceHolderScriptInstance() { - if (script.is_valid()) { script->_placeholder_erased(this); } diff --git a/core/script_language.h b/core/object/script_language.h index 5cc240efcb..f9898ccd0c 100644 --- a/core/script_language.h +++ b/core/object/script_language.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,10 +31,11 @@ #ifndef SCRIPT_LANGUAGE_H #define SCRIPT_LANGUAGE_H +#include "core/doc_data.h" #include "core/io/multiplayer_api.h" -#include "core/map.h" -#include "core/pair.h" -#include "core/resource.h" +#include "core/io/resource.h" +#include "core/templates/map.h" +#include "core/templates/pair.h" class ScriptLanguage; @@ -57,7 +58,6 @@ struct SortNetData { class ScriptServer { enum { - MAX_LANGUAGES = 16 }; @@ -112,12 +112,11 @@ class ScriptInstance; class PlaceHolderScriptInstance; class Script : public Resource { - GDCLASS(Script, Resource); OBJ_SAVE_TYPE(Script); protected: - virtual bool editor_can_reload_from_file() { return false; } // this is handled by editor better + virtual bool editor_can_reload_from_file() override { return false; } // this is handled by editor better void _notification(int p_what); static void _bind_methods(); @@ -147,6 +146,10 @@ public: virtual void set_source_code(const String &p_code) = 0; virtual Error reload(bool p_keep_state = false) = 0; +#ifdef TOOLS_ENABLED + virtual const Vector<DocData::ClassDoc> &get_documentation() const = 0; +#endif // TOOLS_ENABLED + virtual bool has_method(const StringName &p_method) const = 0; virtual MethodInfo get_method_info(const StringName &p_method) const = 0; @@ -200,13 +203,11 @@ public: virtual bool has_method(const StringName &p_method) const = 0; virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST); virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0; - virtual void call_multilevel(const StringName &p_method, VARIANT_ARG_LIST); - virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount); - virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); virtual void notification(int p_notification) = 0; virtual String to_string(bool *r_valid) { - if (r_valid) + if (r_valid) { *r_valid = false; + } return String(); } @@ -253,14 +254,14 @@ struct ScriptCodeCompletionOption { KIND_FILE_PATH, KIND_PLAIN_TEXT, }; - Kind kind; + Kind kind = KIND_PLAIN_TEXT; String display; String insert_text; + Color font_color; RES icon; + Variant default_value; - ScriptCodeCompletionOption() { - kind = KIND_PLAIN_TEXT; - } + ScriptCodeCompletionOption() {} ScriptCodeCompletionOption(const String &p_text, Kind p_kind) { display = p_text; @@ -270,12 +271,9 @@ struct ScriptCodeCompletionOption { }; class ScriptCodeCompletionCache { - static ScriptCodeCompletionCache *singleton; public: - virtual RES get_cached_resource(const String &p_path) = 0; - static ScriptCodeCompletionCache *get_singleton() { return singleton; } ScriptCodeCompletionCache(); @@ -296,12 +294,14 @@ public: /* EDITOR FUNCTIONS */ struct Warning { - int line; + int start_line = -1, end_line = -1; + int leftmost_column = -1, rightmost_column = -1; int code; String string_code; String message; }; + void get_core_type_words(List<String> *p_core_type_words) const; virtual void get_reserved_words(List<String> *p_words) const = 0; virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0; virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; @@ -313,7 +313,8 @@ public: virtual Script *create_script() const = 0; virtual bool has_named_classes() const = 0; virtual bool supports_builtin_mode() const = 0; - virtual bool can_inherit_from_file() { return false; } + virtual bool supports_documentation() const { return false; } + virtual bool can_inherit_from_file() const { return false; } virtual int find_function(const String &p_function, const String &p_code) const = 0; virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const = 0; virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; } @@ -408,7 +409,6 @@ public: extern uint8_t script_encryption_key[32]; class PlaceHolderScriptInstance : public ScriptInstance { - Object *owner; List<PropertyInfo> properties; Map<StringName, Variant> values; @@ -429,8 +429,6 @@ public: r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - //virtual void call_multilevel(const StringName& p_method,VARIANT_ARG_LIST) { return Variant(); } - //virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount,Callable::CallError &r_error) { return Variant(); } virtual void notification(int p_notification) {} virtual Ref<Script> get_script() const { return script; } diff --git a/core/undo_redo.cpp b/core/object/undo_redo.cpp index 62ad3e9f98..3b1165b8f6 100644 --- a/core/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,22 +30,21 @@ #include "undo_redo.h" +#include "core/io/resource.h" #include "core/os/os.h" void UndoRedo::_discard_redo() { - - if (current_action == actions.size() - 1) + if (current_action == actions.size() - 1) { return; + } for (int i = current_action + 1; i < actions.size(); i++) { - for (List<Operation>::Element *E = actions.write[i].do_ops.front(); E; E = E->next()) { - if (E->get().type == Operation::TYPE_REFERENCE) { - Object *obj = ObjectDB::get_instance(E->get().object); - if (obj) + if (obj) { memdelete(obj); + } } } //ERASE do data @@ -54,32 +53,44 @@ void UndoRedo::_discard_redo() { actions.resize(current_action + 1); } -void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { +bool UndoRedo::_redo(bool p_execute) { + ERR_FAIL_COND_V(action_level > 0, false); + + if ((current_action + 1) >= actions.size()) { + return false; //nothing to redo + } + + current_action++; + if (p_execute) { + _process_operation_list(actions.write[current_action].do_ops.front()); + } + version++; + emit_signal("version_changed"); + return true; +} + +void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { uint32_t ticks = OS::get_singleton()->get_ticks_msec(); if (action_level == 0) { - _discard_redo(); // Check if the merge operation is valid if (p_mode != MERGE_DISABLE && actions.size() && actions[actions.size() - 1].name == p_name && actions[actions.size() - 1].last_tick + 800 > ticks) { - current_action = actions.size() - 2; if (p_mode == MERGE_ENDS) { - // Clear all do ops from last action, and delete all object references List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); while (E) { - if (E->get().type == Operation::TYPE_REFERENCE) { - Object *obj = ObjectDB::get_instance(E->get().object); - if (obj) + if (obj) { memdelete(obj); + } } E = E->next(); @@ -92,7 +103,6 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { merge_mode = p_mode; merging = true; } else { - Action new_action; new_action.name = p_name; new_action.last_tick = ticks; @@ -106,15 +116,15 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { } void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); Operation do_op; do_op.object = p_object->get_instance_id(); - if (Object::cast_to<Resource>(p_object)) - do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); + if (Object::cast_to<Reference>(p_object)) { + do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + } do_op.type = Operation::TYPE_METHOD; do_op.name = p_method; @@ -126,20 +136,21 @@ void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIA } void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) + if (merge_mode == MERGE_ENDS) { return; + } Operation undo_op; undo_op.object = p_object->get_instance_id(); - if (Object::cast_to<Resource>(p_object)) - undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); + if (Object::cast_to<Reference>(p_object)) { + undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + } undo_op.type = Operation::TYPE_METHOD; undo_op.name = p_method; @@ -149,87 +160,92 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR } actions.write[current_action + 1].undo_ops.push_back(undo_op); } -void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value) { +void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value) { ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); Operation do_op; do_op.object = p_object->get_instance_id(); - if (Object::cast_to<Resource>(p_object)) - do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); + if (Object::cast_to<Reference>(p_object)) { + do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + } do_op.type = Operation::TYPE_PROPERTY; do_op.name = p_property; do_op.args[0] = p_value; actions.write[current_action + 1].do_ops.push_back(do_op); } -void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value) { +void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value) { ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) + if (merge_mode == MERGE_ENDS) { return; + } Operation undo_op; undo_op.object = p_object->get_instance_id(); - if (Object::cast_to<Resource>(p_object)) - undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); + if (Object::cast_to<Reference>(p_object)) { + undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + } undo_op.type = Operation::TYPE_PROPERTY; undo_op.name = p_property; undo_op.args[0] = p_value; actions.write[current_action + 1].undo_ops.push_back(undo_op); } -void UndoRedo::add_do_reference(Object *p_object) { +void UndoRedo::add_do_reference(Object *p_object) { ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); Operation do_op; do_op.object = p_object->get_instance_id(); - if (Object::cast_to<Resource>(p_object)) - do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); + if (Object::cast_to<Reference>(p_object)) { + do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + } do_op.type = Operation::TYPE_REFERENCE; actions.write[current_action + 1].do_ops.push_back(do_op); } -void UndoRedo::add_undo_reference(Object *p_object) { +void UndoRedo::add_undo_reference(Object *p_object) { ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) + if (merge_mode == MERGE_ENDS) { return; + } Operation undo_op; undo_op.object = p_object->get_instance_id(); - if (Object::cast_to<Resource>(p_object)) - undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); + if (Object::cast_to<Reference>(p_object)) { + undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + } undo_op.type = Operation::TYPE_REFERENCE; actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::_pop_history_tail() { - _discard_redo(); - if (!actions.size()) + if (!actions.size()) { return; + } for (List<Operation>::Element *E = actions.write[0].undo_ops.front(); E; E = E->next()) { - if (E->get().type == Operation::TYPE_REFERENCE) { - Object *obj = ObjectDB::get_instance(E->get().object); - if (obj) + if (obj) { memdelete(obj); + } } } @@ -243,12 +259,12 @@ bool UndoRedo::is_committing_action() const { return committing > 0; } -void UndoRedo::commit_action() { - +void UndoRedo::commit_action(bool p_execute) { ERR_FAIL_COND(action_level <= 0); action_level--; - if (action_level > 0) + if (action_level > 0) { return; //still nested + } if (merging) { version--; @@ -256,27 +272,25 @@ void UndoRedo::commit_action() { } committing++; - redo(); // perform action + _redo(p_execute); // perform action committing--; + if (callback && actions.size() > 0) { callback(callback_ud, actions[actions.size() - 1].name); } } void UndoRedo::_process_operation_list(List<Operation>::Element *E) { - for (; E; E = E->next()) { - Operation &op = E->get(); Object *obj = ObjectDB::get_instance(op.object); - if (!obj) //may have been deleted and this is fine + if (!obj) { //may have been deleted and this is fine continue; + } switch (op.type) { - case Operation::TYPE_METHOD: { - Vector<const Variant *> argptrs; argptrs.resize(VARIANT_ARG_MAX); int argc = 0; @@ -297,8 +311,9 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { } #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); - if (res) + if (res) { res->set_edited(true); + } #endif @@ -307,12 +322,12 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { } } break; case Operation::TYPE_PROPERTY: { - obj->set(op.name, op.args[0]); #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); - if (res) + if (res) { res->set_edited(true); + } #endif if (property_callback) { property_callback(prop_callback_ud, obj, op.name, op.args[0]); @@ -326,26 +341,14 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { } bool UndoRedo::redo() { - - ERR_FAIL_COND_V(action_level > 0, false); - - if ((current_action + 1) >= actions.size()) - return false; //nothing to redo - - current_action++; - - _process_operation_list(actions.write[current_action].do_ops.front()); - version++; - emit_signal("version_changed"); - - return true; + return _redo(true); } bool UndoRedo::undo() { - ERR_FAIL_COND_V(action_level > 0, false); - if (current_action < 0) + if (current_action < 0) { return false; //nothing to redo + } _process_operation_list(actions.write[current_action].undo_ops.front()); current_action--; version--; @@ -354,13 +357,31 @@ bool UndoRedo::undo() { return true; } -void UndoRedo::clear_history(bool p_increase_version) { +int UndoRedo::get_history_count() { + ERR_FAIL_COND_V(action_level > 0, -1); + + return actions.size(); +} + +int UndoRedo::get_current_action() { + ERR_FAIL_COND_V(action_level > 0, -1); + return current_action; +} + +String UndoRedo::get_action_name(int p_id) { + ERR_FAIL_INDEX_V(p_id, actions.size(), ""); + + return actions[p_id].name; +} + +void UndoRedo::clear_history(bool p_increase_version) { ERR_FAIL_COND(action_level > 0); _discard_redo(); - while (actions.size()) + while (actions.size()) { _pop_history_tail(); + } if (p_increase_version) { version++; @@ -369,70 +390,45 @@ void UndoRedo::clear_history(bool p_increase_version) { } String UndoRedo::get_current_action_name() const { - ERR_FAIL_COND_V(action_level > 0, ""); - if (current_action < 0) + if (current_action < 0) { return ""; + } return actions[current_action].name; } bool UndoRedo::has_undo() { - return current_action >= 0; } bool UndoRedo::has_redo() { - return (current_action + 1) < actions.size(); } uint64_t UndoRedo::get_version() const { - return version; } void UndoRedo::set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud) { - callback = p_callback; callback_ud = p_ud; } void UndoRedo::set_method_notify_callback(MethodNotifyCallback p_method_callback, void *p_ud) { - method_callback = p_method_callback; method_callbck_ud = p_ud; } void UndoRedo::set_property_notify_callback(PropertyNotifyCallback p_property_callback, void *p_ud) { - property_callback = p_property_callback; prop_callback_ud = p_ud; } -UndoRedo::UndoRedo() { - - committing = 0; - version = 1; - action_level = 0; - current_action = -1; - merge_mode = MERGE_DISABLE; - merging = false; - callback = nullptr; - callback_ud = nullptr; - - method_callbck_ud = nullptr; - prop_callback_ud = nullptr; - method_callback = nullptr; - property_callback = nullptr; -} - UndoRedo::~UndoRedo() { - clear_history(); } Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 0; @@ -461,16 +457,15 @@ Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callabl Variant v[VARIANT_ARG_MAX]; for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) { - v[i] = *p_args[i + 2]; } + static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5"); add_do_method(object, method, v[0], v[1], v[2], v[3], v[4]); return Variant(); } Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 0; @@ -499,18 +494,17 @@ Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Calla Variant v[VARIANT_ARG_MAX]; for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) { - v[i] = *p_args[i + 2]; } + static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5"); add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4]); return Variant(); } void UndoRedo::_bind_methods() { - ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE)); - ClassDB::bind_method(D_METHOD("commit_action"), &UndoRedo::commit_action); + ClassDB::bind_method(D_METHOD("commit_action", "execute"), &UndoRedo::commit_action, DEFVAL(true)); ClassDB::bind_method(D_METHOD("is_committing_action"), &UndoRedo::is_committing_action); { @@ -535,8 +529,14 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property); ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference); ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &UndoRedo::add_undo_reference); + + ClassDB::bind_method(D_METHOD("get_history_count"), &UndoRedo::get_history_count); + ClassDB::bind_method(D_METHOD("get_current_action"), &UndoRedo::get_current_action); + ClassDB::bind_method(D_METHOD("get_action_name"), &UndoRedo::get_action_name); ClassDB::bind_method(D_METHOD("clear_history", "increase_version"), &UndoRedo::clear_history, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_current_action_name"), &UndoRedo::get_current_action_name); + ClassDB::bind_method(D_METHOD("has_undo"), &UndoRedo::has_undo); ClassDB::bind_method(D_METHOD("has_redo"), &UndoRedo::has_redo); ClassDB::bind_method(D_METHOD("get_version"), &UndoRedo::get_version); diff --git a/core/undo_redo.h b/core/object/undo_redo.h index 3b91e9ce36..a08ca7792f 100644 --- a/core/undo_redo.h +++ b/core/object/undo_redo.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,11 +31,10 @@ #ifndef UNDO_REDO_H #define UNDO_REDO_H -#include "core/object.h" -#include "core/resource.h" +#include "core/object/class_db.h" +#include "core/object/reference.h" class UndoRedo : public Object { - GDCLASS(UndoRedo, Object); OBJ_SAVE_TYPE(UndoRedo); @@ -55,7 +54,6 @@ public: private: struct Operation { - enum Type { TYPE_METHOD, TYPE_PROPERTY, @@ -63,7 +61,7 @@ private: }; Type type; - Ref<Resource> resref; + Ref<Reference> ref; ObjectID object; StringName name; Variant args[VARIANT_ARG_MAX]; @@ -77,25 +75,26 @@ private: }; Vector<Action> actions; - int current_action; - int action_level; - MergeMode merge_mode; - bool merging; - uint64_t version; + int current_action = -1; + int action_level = 0; + MergeMode merge_mode = MERGE_DISABLE; + bool merging = false; + uint64_t version = 1; void _pop_history_tail(); void _process_operation_list(List<Operation>::Element *E); void _discard_redo(); + bool _redo(bool p_execute); - CommitNotifyCallback callback; - void *callback_ud; - void *method_callbck_ud; - void *prop_callback_ud; + CommitNotifyCallback callback = nullptr; + void *callback_ud = nullptr; + void *method_callbck_ud = nullptr; + void *prop_callback_ud = nullptr; - MethodNotifyCallback method_callback; - PropertyNotifyCallback property_callback; + MethodNotifyCallback method_callback = nullptr; + PropertyNotifyCallback property_callback = nullptr; - int committing; + int committing = 0; protected: static void _bind_methods(); @@ -111,11 +110,15 @@ public: void add_undo_reference(Object *p_object); bool is_committing_action() const; - void commit_action(); + void commit_action(bool p_execute = true); bool redo(); bool undo(); String get_current_action_name() const; + + int get_history_count(); + int get_current_action(); + String get_action_name(int p_id); void clear_history(bool p_increase_version = true); bool has_undo(); @@ -128,7 +131,7 @@ public: void set_method_notify_callback(MethodNotifyCallback p_method_callback, void *p_ud); void set_property_notify_callback(PropertyNotifyCallback p_property_callback, void *p_ud); - UndoRedo(); + UndoRedo() {} ~UndoRedo(); }; diff --git a/core/os/copymem.h b/core/os/copymem.h index 04ea3caeff..6fd559356c 100644 --- a/core/os/copymem.h +++ b/core/os/copymem.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp index 94c8cd5d73..fb9da9fbed 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,62 +30,62 @@ #include "dir_access.h" +#include "core/config/project_settings.h" #include "core/os/file_access.h" #include "core/os/memory.h" #include "core/os/os.h" -#include "core/project_settings.h" String DirAccess::_get_root_path() const { - switch (_access_type) { - - case ACCESS_RESOURCES: return ProjectSettings::get_singleton()->get_resource_path(); - case ACCESS_USERDATA: return OS::get_singleton()->get_user_data_dir(); - default: return ""; + case ACCESS_RESOURCES: + return ProjectSettings::get_singleton()->get_resource_path(); + case ACCESS_USERDATA: + return OS::get_singleton()->get_user_data_dir(); + default: + return ""; } } -String DirAccess::_get_root_string() const { +String DirAccess::_get_root_string() const { switch (_access_type) { - - case ACCESS_RESOURCES: return "res://"; - case ACCESS_USERDATA: return "user://"; - default: return ""; + case ACCESS_RESOURCES: + return "res://"; + case ACCESS_USERDATA: + return "user://"; + default: + return ""; } } int DirAccess::get_current_drive() { - String path = get_current_dir().to_lower(); for (int i = 0; i < get_drive_count(); i++) { String d = get_drive(i).to_lower(); - if (path.begins_with(d)) + if (path.begins_with(d)) { return i; + } } return 0; } bool DirAccess::drives_are_shortcuts() { - return false; } static Error _erase_recursive(DirAccess *da) { - List<String> dirs; List<String> files; da->list_dir_begin(); String n = da->get_next(); while (n != String()) { - if (n != "." && n != "..") { - - if (da->current_is_dir()) + if (da->current_is_dir()) { dirs.push_back(n); - else + } else { files.push_back(n); + } } n = da->get_next(); @@ -94,10 +94,8 @@ static Error _erase_recursive(DirAccess *da) { da->list_dir_end(); for (List<String>::Element *E = dirs.front(); E; E = E->next()) { - Error err = da->change_dir(E->get()); if (err == OK) { - err = _erase_recursive(da); if (err) { da->change_dir(".."); @@ -117,7 +115,6 @@ static Error _erase_recursive(DirAccess *da) { } for (List<String>::Element *E = files.front(); E; E = E->next()) { - Error err = da->remove(da->get_current_dir().plus_file(E->get())); if (err) { return err; @@ -128,15 +125,13 @@ static Error _erase_recursive(DirAccess *da) { } Error DirAccess::erase_contents_recursive() { - return _erase_recursive(this); } Error DirAccess::make_dir_recursive(String p_dir) { - if (p_dir.length() < 1) { return OK; - }; + } String full_dir; @@ -154,13 +149,13 @@ Error DirAccess::make_dir_recursive(String p_dir) { String base; - if (full_dir.begins_with("res://")) + if (full_dir.begins_with("res://")) { base = "res://"; - else if (full_dir.begins_with("user://")) + } else if (full_dir.begins_with("user://")) { base = "user://"; - else if (full_dir.begins_with("/")) + } else if (full_dir.begins_with("/")) { base = "/"; - else if (full_dir.find(":/") != -1) { + } else if (full_dir.find(":/") != -1) { base = full_dir.substr(0, full_dir.find(":/") + 2); } else { ERR_FAIL_V(ERR_INVALID_PARAMETER); @@ -172,11 +167,9 @@ Error DirAccess::make_dir_recursive(String p_dir) { String curpath = base; for (int i = 0; i < subdirs.size(); i++) { - curpath = curpath.plus_file(subdirs[i]); Error err = make_dir(curpath); if (err != OK && err != ERR_ALREADY_EXISTS) { - ERR_FAIL_V(err); } } @@ -185,42 +178,34 @@ Error DirAccess::make_dir_recursive(String p_dir) { } String DirAccess::fix_path(String p_path) const { - switch (_access_type) { - case ACCESS_RESOURCES: { - if (ProjectSettings::get_singleton()) { if (p_path.begins_with("res://")) { - String resource_path = ProjectSettings::get_singleton()->get_resource_path(); if (resource_path != "") { - return p_path.replace_first("res:/", resource_path); - }; + } return p_path.replace_first("res://", ""); } } } break; case ACCESS_USERDATA: { - if (p_path.begins_with("user://")) { - String data_dir = OS::get_singleton()->get_user_data_dir(); if (data_dir != "") { - return p_path.replace_first("user:/", data_dir); - }; + } return p_path.replace_first("user://", ""); } } break; case ACCESS_FILESYSTEM: { - return p_path; } break; - case ACCESS_MAX: break; // Can't happen, but silences warning + case ACCESS_MAX: + break; // Can't happen, but silences warning } return p_path; @@ -229,16 +214,12 @@ String DirAccess::fix_path(String p_path) const { DirAccess::CreateFunc DirAccess::create_func[ACCESS_MAX] = { nullptr, nullptr, nullptr }; DirAccess *DirAccess::create_for_path(const String &p_path) { - DirAccess *da = nullptr; if (p_path.begins_with("res://")) { - da = create(ACCESS_RESOURCES); } else if (p_path.begins_with("user://")) { - da = create(ACCESS_USERDATA); } else { - da = create(ACCESS_FILESYSTEM); } @@ -246,13 +227,13 @@ DirAccess *DirAccess::create_for_path(const String &p_path) { } DirAccess *DirAccess::open(const String &p_path, Error *r_error) { - DirAccess *da = create_for_path(p_path); ERR_FAIL_COND_V_MSG(!da, nullptr, "Cannot create DirAccess for path '" + p_path + "'."); Error err = da->change_dir(p_path); - if (r_error) + if (r_error) { *r_error = err; + } if (err != OK) { memdelete(da); return nullptr; @@ -262,20 +243,19 @@ DirAccess *DirAccess::open(const String &p_path, Error *r_error) { } DirAccess *DirAccess::create(AccessType p_access) { - DirAccess *da = create_func[p_access] ? create_func[p_access]() : nullptr; if (da) { da->_access_type = p_access; } return da; -}; +} String DirAccess::get_full_path(const String &p_path, AccessType p_access) { - DirAccess *d = DirAccess::create(p_access); - if (!d) + if (!d) { return p_path; + } d->change_dir(p_path); String full = d->get_current_dir(); @@ -284,7 +264,6 @@ String DirAccess::get_full_path(const String &p_path, AccessType p_access) { } Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { - //printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data()); Error err; FileAccess *fsrc = FileAccess::open(p_from, FileAccess::READ, &err); @@ -296,7 +275,6 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { FileAccess *fdst = FileAccess::open(p_to, FileAccess::WRITE, &err); if (err) { - fsrc->close(); memdelete(fsrc); ERR_PRINT("Failed to open " + p_to); @@ -308,7 +286,6 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { fsrc->seek(0); err = OK; while (size--) { - if (fsrc->get_error() != OK) { err = fsrc->get_error(); break; @@ -325,8 +302,9 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { fdst->close(); err = FileAccess::set_unix_permissions(p_to, p_chmod_flags); // If running on a platform with no chmod support (i.e., Windows), don't fail - if (err == ERR_UNAVAILABLE) + if (err == ERR_UNAVAILABLE) { err = OK; + } } memdelete(fsrc); @@ -360,12 +338,10 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag list_dir_begin(); String n = get_next(); while (n != String()) { - if (n != "." && n != "..") { - - if (current_is_dir()) + if (current_is_dir()) { dirs.push_back(n); - else { + } else { const String &rel_path = n; if (!n.is_rel_path()) { list_dir_end(); @@ -433,17 +409,8 @@ Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) { } bool DirAccess::exists(String p_dir) { - DirAccess *da = DirAccess::create_for_path(p_dir); bool valid = da->change_dir(p_dir) == OK; memdelete(da); return valid; } - -DirAccess::DirAccess() { - - _access_type = ACCESS_FILESYSTEM; -} - -DirAccess::~DirAccess() { -} diff --git a/core/os/dir_access.h b/core/os/dir_access.h index 60eb553968..c49c4cc4b8 100644 --- a/core/os/dir_access.h +++ b/core/os/dir_access.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,8 +31,8 @@ #ifndef DIR_ACCESS_H #define DIR_ACCESS_H +#include "core/string/ustring.h" #include "core/typedefs.h" -#include "core/ustring.h" //@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies class DirAccess { @@ -47,7 +47,7 @@ public: typedef DirAccess *(*CreateFunc)(); private: - AccessType _access_type; + AccessType _access_type = ACCESS_FILESYSTEM; static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags); @@ -57,11 +57,9 @@ protected: String _get_root_string() const; String fix_path(String p_path) const; - bool next_is_dir; template <class T> static DirAccess *_create_builtin() { - return memnew(T); } @@ -110,42 +108,33 @@ public: static String get_full_path(const String &p_path, AccessType p_access); static DirAccess *create_for_path(const String &p_path); - /* - enum DirType { - - FILE_TYPE_INVALID, - FILE_TYPE_FILE, - FILE_TYPE_DIR, - }; - - //virtual DirType get_file_type() const=0; -*/ static DirAccess *create(AccessType p_access); template <class T> static void make_default(AccessType p_access) { - create_func[p_access] = _create_builtin<T>; } static DirAccess *open(const String &p_path, Error *r_error = nullptr); - DirAccess(); - virtual ~DirAccess(); + DirAccess() {} + virtual ~DirAccess() {} }; struct DirAccessRef { - _FORCE_INLINE_ DirAccess *operator->() { - return f; } operator bool() const { return f != nullptr; } + DirAccess *f; + DirAccessRef(DirAccess *fa) { f = fa; } ~DirAccessRef() { - if (f) memdelete(f); + if (f) { + memdelete(f); + } } }; diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 3922f031b7..5a3df88619 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,11 +30,11 @@ #include "file_access.h" +#include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" #include "core/os/os.h" -#include "core/project_settings.h" FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { nullptr, nullptr }; @@ -43,7 +43,6 @@ FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr; bool FileAccess::backup_save = false; FileAccess *FileAccess::create(AccessType p_access) { - ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr); FileAccess *ret = create_func[p_access](); @@ -52,34 +51,30 @@ FileAccess *FileAccess::create(AccessType p_access) { } bool FileAccess::exists(const String &p_name) { - - if (PackedData::get_singleton() && PackedData::get_singleton()->has_path(p_name)) + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_name)) { return true; + } FileAccess *f = open(p_name, READ); - if (!f) + if (!f) { return false; + } memdelete(f); return true; } void FileAccess::_set_access_type(AccessType p_access) { - _access_type = p_access; -}; +} FileAccess *FileAccess::create_for_path(const String &p_path) { - FileAccess *ret = nullptr; if (p_path.begins_with("res://")) { - ret = create(ACCESS_RESOURCES); } else if (p_path.begins_with("user://")) { - ret = create(ACCESS_USERDATA); } else { - ret = create(ACCESS_FILESYSTEM); } @@ -87,20 +82,19 @@ FileAccess *FileAccess::create_for_path(const String &p_path) { } Error FileAccess::reopen(const String &p_path, int p_mode_flags) { - return _open(p_path, p_mode_flags); -}; +} FileAccess *FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) { - //try packed data first FileAccess *ret = nullptr; if (!(p_mode_flags & WRITE) && PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled()) { ret = PackedData::get_singleton()->try_open_path(p_path); if (ret) { - if (r_error) + if (r_error) { *r_error = OK; + } return ret; } } @@ -108,10 +102,10 @@ FileAccess *FileAccess::open(const String &p_path, int p_mode_flags, Error *r_er ret = create_for_path(p_path); Error err = ret->_open(p_path, p_mode_flags); - if (r_error) + if (r_error) { *r_error = err; + } if (err != OK) { - memdelete(ret); ret = nullptr; } @@ -120,9 +114,8 @@ FileAccess *FileAccess::open(const String &p_path, int p_mode_flags, Error *r_er } FileAccess::CreateFunc FileAccess::get_create_func(AccessType p_access) { - return create_func[p_access]; -}; +} String FileAccess::fix_path(const String &p_path) const { //helper used by file accesses that use a single filesystem @@ -130,40 +123,33 @@ String FileAccess::fix_path(const String &p_path) const { String r_path = p_path.replace("\\", "/"); switch (_access_type) { - case ACCESS_RESOURCES: { - if (ProjectSettings::get_singleton()) { if (r_path.begins_with("res://")) { - String resource_path = ProjectSettings::get_singleton()->get_resource_path(); if (resource_path != "") { - return r_path.replace("res:/", resource_path); - }; + } return r_path.replace("res://", ""); } } } break; case ACCESS_USERDATA: { - if (r_path.begins_with("user://")) { - String data_dir = OS::get_singleton()->get_user_data_dir(); if (data_dir != "") { - return r_path.replace("user:/", data_dir); - }; + } return r_path.replace("user://", ""); } } break; case ACCESS_FILESYSTEM: { - return r_path; } break; - case ACCESS_MAX: break; // Can't happen, but silences warning + case ACCESS_MAX: + break; // Can't happen, but silences warning } return r_path; @@ -172,7 +158,6 @@ String FileAccess::fix_path(const String &p_path) const { /* these are all implemented for ease of porting, then can later be optimized */ uint16_t FileAccess::get_16() const { - uint16_t res; uint8_t a, b; @@ -180,7 +165,6 @@ uint16_t FileAccess::get_16() const { b = get_8(); if (endian_swap) { - SWAP(a, b); } @@ -190,8 +174,8 @@ uint16_t FileAccess::get_16() const { return res; } -uint32_t FileAccess::get_32() const { +uint32_t FileAccess::get_32() const { uint32_t res; uint16_t a, b; @@ -199,7 +183,6 @@ uint32_t FileAccess::get_32() const { b = get_16(); if (endian_swap) { - SWAP(a, b); } @@ -209,8 +192,8 @@ uint32_t FileAccess::get_32() const { return res; } -uint64_t FileAccess::get_64() const { +uint64_t FileAccess::get_64() const { uint64_t res; uint32_t a, b; @@ -218,7 +201,6 @@ uint64_t FileAccess::get_64() const { b = get_32(); if (endian_swap) { - SWAP(a, b); } @@ -230,38 +212,35 @@ uint64_t FileAccess::get_64() const { } float FileAccess::get_float() const { - MarshallFloat m; m.i = get_32(); return m.f; -}; +} real_t FileAccess::get_real() const { - - if (real_is_double) + if (real_is_double) { return get_double(); - else + } else { return get_float(); + } } double FileAccess::get_double() const { - MarshallDouble m; m.l = get_64(); return m.d; -}; +} String FileAccess::get_token() const { - CharString token; - CharType c = get_8(); + char32_t c = get_8(); while (!eof_reached()) { - if (c <= ' ') { - if (token.length()) + if (token.length()) { break; + } } else { token += c; } @@ -275,21 +254,18 @@ class CharBuffer { Vector<char> vector; char stack_buffer[256]; - char *buffer; - int capacity; - int written; + char *buffer = nullptr; + int capacity = 0; + int written = 0; bool grow() { - if (vector.resize(next_power_of_2(1 + written)) != OK) { - return false; } if (buffer == stack_buffer) { // first chunk? for (int i = 0; i < written; i++) { - vector.write[i] = stack_buffer[i]; } } @@ -304,14 +280,11 @@ class CharBuffer { public: _FORCE_INLINE_ CharBuffer() : buffer(stack_buffer), - capacity(sizeof(stack_buffer) / sizeof(char)), - written(0) { + capacity(sizeof(stack_buffer) / sizeof(char)) { } _FORCE_INLINE_ void push_back(char c) { - if (written >= capacity) { - ERR_FAIL_COND(!grow()); } @@ -319,24 +292,22 @@ public: } _FORCE_INLINE_ const char *get_data() const { - return buffer; } }; String FileAccess::get_line() const { - CharBuffer line; - CharType c = get_8(); + char32_t c = get_8(); while (!eof_reached()) { - if (c == '\n' || c == '\0') { line.push_back(0); return String::utf8(line.get_data()); - } else if (c != '\r') + } else if (c != '\r') { line.push_back(c); + } c = get_8(); } @@ -345,21 +316,21 @@ String FileAccess::get_line() const { } Vector<String> FileAccess::get_csv_line(const String &p_delim) const { - ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>()); String l; int qc = 0; do { - if (eof_reached()) + if (eof_reached()) { break; + } l += get_line() + "\n"; qc = 0; for (int i = 0; i < l.length(); i++) { - - if (l[i] == '"') + if (l[i] == '"') { qc++; + } } } while (qc % 2); @@ -371,20 +342,18 @@ Vector<String> FileAccess::get_csv_line(const String &p_delim) const { bool in_quote = false; String current; for (int i = 0; i < l.length(); i++) { - - CharType c = l[i]; - CharType s[2] = { 0, 0 }; + char32_t c = l[i]; + char32_t s[2] = { 0, 0 }; if (!in_quote && c == p_delim[0]) { strings.push_back(current); current = String(); } else if (c == '"') { - if (l[i + 1] == '"') { + if (l[i + 1] == '"' && in_quote) { s[0] = '"'; current += s; i++; } else { - in_quote = !in_quote; } } else { @@ -399,10 +368,10 @@ Vector<String> FileAccess::get_csv_line(const String &p_delim) const { } int FileAccess::get_buffer(uint8_t *p_dst, int p_length) const { - int i = 0; - for (i = 0; i < p_length && !eof_reached(); i++) + for (i = 0; i < p_length && !eof_reached(); i++) { p_dst[i] = get_8(); + } return i; } @@ -425,44 +394,40 @@ String FileAccess::get_as_utf8_string() const { } void FileAccess::store_16(uint16_t p_dest) { - uint8_t a, b; a = p_dest & 0xFF; b = p_dest >> 8; if (endian_swap) { - SWAP(a, b); } store_8(a); store_8(b); } -void FileAccess::store_32(uint32_t p_dest) { +void FileAccess::store_32(uint32_t p_dest) { uint16_t a, b; a = p_dest & 0xFFFF; b = p_dest >> 16; if (endian_swap) { - SWAP(a, b); } store_16(a); store_16(b); } -void FileAccess::store_64(uint64_t p_dest) { +void FileAccess::store_64(uint64_t p_dest) { uint32_t a, b; a = p_dest & 0xFFFFFFFF; b = p_dest >> 32; if (endian_swap) { - SWAP(a, b); } @@ -471,31 +436,29 @@ void FileAccess::store_64(uint64_t p_dest) { } void FileAccess::store_real(real_t p_real) { - - if (sizeof(real_t) == 4) + if (sizeof(real_t) == 4) { store_float(p_real); - else + } else { store_double(p_real); + } } void FileAccess::store_float(float p_dest) { - MarshallFloat m; m.f = p_dest; store_32(m.i); -}; +} void FileAccess::store_double(double p_dest) { - MarshallDouble m; m.d = p_dest; store_64(m.l); -}; +} uint64_t FileAccess::get_modified_time(const String &p_file) { - - if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_file)) + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { return 0; + } FileAccess *fa = create_for_path(p_file); ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'."); @@ -506,9 +469,9 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { } uint32_t FileAccess::get_unix_permissions(const String &p_file) { - - if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_file)) + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { return 0; + } FileAccess *fa = create_for_path(p_file); ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'."); @@ -519,6 +482,9 @@ uint32_t FileAccess::get_unix_permissions(const String &p_file) { } Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return ERR_UNAVAILABLE; + } FileAccess *fa = create_for_path(p_file); ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); @@ -529,23 +495,21 @@ Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissi } void FileAccess::store_string(const String &p_string) { - - if (p_string.length() == 0) + if (p_string.length() == 0) { return; + } CharString cs = p_string.utf8(); store_buffer((uint8_t *)&cs[0], cs.length()); } void FileAccess::store_pascal_string(const String &p_string) { - CharString cs = p_string.utf8(); store_32(cs.length()); store_buffer((uint8_t *)&cs[0], cs.length()); -}; +} String FileAccess::get_pascal_string() { - uint32_t sl = get_32(); CharString cs; cs.resize(sl + 1); @@ -556,16 +520,14 @@ String FileAccess::get_pascal_string() { ret.parse_utf8(cs.ptr()); return ret; -}; +} void FileAccess::store_line(const String &p_line) { - store_string(p_line); store_8('\n'); } void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) { - ERR_FAIL_COND(p_delim.length() != 1); String line = ""; @@ -587,13 +549,12 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_ } void FileAccess::store_buffer(const uint8_t *p_src, int p_length) { - - for (int i = 0; i < p_length; i++) + for (int i = 0; i < p_length; i++) { store_8(p_src[i]); + } } Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) { - FileAccess *f = FileAccess::open(p_path, READ, r_error); if (!f) { if (r_error) { // if error requested, do not throw error @@ -609,7 +570,6 @@ Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_err } String FileAccess::get_file_as_string(const String &p_path, Error *r_error) { - Error err; Vector<uint8_t> array = get_file_as_array(p_path, &err); if (r_error) { @@ -628,10 +588,10 @@ String FileAccess::get_file_as_string(const String &p_path, Error *r_error) { } String FileAccess::get_md5(const String &p_file) { - FileAccess *f = FileAccess::open(p_file, READ); - if (!f) + if (!f) { return String(); + } CryptoCore::MD5Context ctx; ctx.start(); @@ -639,14 +599,13 @@ String FileAccess::get_md5(const String &p_file) { unsigned char step[32768]; while (true) { - int br = f->get_buffer(step, 32768); if (br > 0) { - ctx.update(step, br); } - if (br < 4096) + if (br < 4096) { break; + } } unsigned char hash[16]; @@ -658,7 +617,6 @@ String FileAccess::get_md5(const String &p_file) { } String FileAccess::get_multiple_md5(const Vector<String> &p_file) { - CryptoCore::MD5Context ctx; ctx.start(); @@ -669,14 +627,13 @@ String FileAccess::get_multiple_md5(const Vector<String> &p_file) { unsigned char step[32768]; while (true) { - int br = f->get_buffer(step, 32768); if (br > 0) { - ctx.update(step, br); } - if (br < 4096) + if (br < 4096) { break; + } } memdelete(f); } @@ -688,10 +645,10 @@ String FileAccess::get_multiple_md5(const Vector<String> &p_file) { } String FileAccess::get_sha256(const String &p_file) { - FileAccess *f = FileAccess::open(p_file, READ); - if (!f) + if (!f) { return String(); + } CryptoCore::SHA256Context ctx; ctx.start(); @@ -699,14 +656,13 @@ String FileAccess::get_sha256(const String &p_file) { unsigned char step[32768]; while (true) { - int br = f->get_buffer(step, 32768); if (br > 0) { - ctx.update(step, br); } - if (br < 4096) + if (br < 4096) { break; + } } unsigned char hash[32]; @@ -715,10 +671,3 @@ String FileAccess::get_sha256(const String &p_file) { memdelete(f); return String::hex_encode_buffer(hash, 32); } - -FileAccess::FileAccess() { - - endian_swap = false; - real_is_double = false; - _access_type = ACCESS_FILESYSTEM; -}; diff --git a/core/os/file_access.h b/core/os/file_access.h index 010cc74a87..1c78204c1d 100644 --- a/core/os/file_access.h +++ b/core/os/file_access.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,15 +33,14 @@ #include "core/math/math_defs.h" #include "core/os/memory.h" +#include "core/string/ustring.h" #include "core/typedefs.h" -#include "core/ustring.h" /** * Multi-Platform abstraction for accessing to files. */ class FileAccess { - public: enum AccessType { ACCESS_RESOURCES, @@ -53,8 +52,8 @@ public: typedef void (*FileCloseFailNotify)(const String &); typedef FileAccess *(*CreateFunc)(); - bool endian_swap; - bool real_is_double; + bool endian_swap = false; + bool real_is_double = false; virtual uint32_t _get_unix_permissions(const String &p_file) = 0; virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0; @@ -69,11 +68,10 @@ protected: private: static bool backup_save; - AccessType _access_type; + AccessType _access_type = ACCESS_FILESYSTEM; static CreateFunc create_func[ACCESS_MAX]; /** default file access creation function for a platform */ template <class T> static FileAccess *_create_builtin() { - return memnew(T); } @@ -83,7 +81,6 @@ public: virtual void _set_access_type(AccessType p_access); enum ModeFlags { - READ = 1, WRITE = 2, READ_WRITE = 3, @@ -172,27 +169,29 @@ public: template <class T> static void make_default(AccessType p_access) { - create_func[p_access] = _create_builtin<T>; } - FileAccess(); + FileAccess() {} virtual ~FileAccess() {} }; struct FileAccessRef { - _FORCE_INLINE_ FileAccess *operator->() { - return f; } operator bool() const { return f != nullptr; } + FileAccess *f; + operator FileAccess *() { return f; } + FileAccessRef(FileAccess *fa) { f = fa; } ~FileAccessRef() { - if (f) memdelete(f); + if (f) { + memdelete(f); + } } }; diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index c65d3fefc2..4b2cafd8fe 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -38,7 +38,6 @@ struct _KeyCodeText { }; static const _KeyCodeText _keycodes[] = { - /* clang-format off */ {KEY_ESCAPE ,"Escape"}, {KEY_TAB ,"Tab"}, @@ -293,9 +292,7 @@ static const _KeyCodeText _keycodes[] = { }; bool keycode_has_unicode(uint32_t p_keycode) { - switch (p_keycode) { - case KEY_ESCAPE: case KEY_TAB: case KEY_BACKTAB: @@ -394,7 +391,6 @@ bool keycode_has_unicode(uint32_t p_keycode) { } String keycode_get_string(uint32_t p_code) { - String codestr; if (p_code & KEY_MASK_SHIFT) { codestr += find_keycode_name(KEY_SHIFT); @@ -418,9 +414,7 @@ String keycode_get_string(uint32_t p_code) { const _KeyCodeText *kct = &_keycodes[0]; while (kct->text) { - if (kct->code == (int)p_code) { - codestr += kct->text; return codestr; } @@ -433,11 +427,9 @@ String keycode_get_string(uint32_t p_code) { } int find_keycode(const String &p_code) { - const _KeyCodeText *kct = &_keycodes[0]; while (kct->text) { - if (p_code.nocasecmp_to(kct->text) == 0) { return kct->code; } @@ -448,11 +440,9 @@ int find_keycode(const String &p_code) { } const char *find_keycode_name(int p_keycode) { - const _KeyCodeText *kct = &_keycodes[0]; while (kct->text) { - if (kct->code == p_keycode) { return kct->text; } @@ -463,12 +453,10 @@ const char *find_keycode_name(int p_keycode) { } int keycode_get_count() { - const _KeyCodeText *kct = &_keycodes[0]; int count = 0; while (kct->text) { - count++; kct++; } diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 5d11e6a378..3ef70e786f 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef KEYBOARD_H #define KEYBOARD_H -#include "core/ustring.h" +#include "core/string/ustring.h" /* Special Key: @@ -294,11 +294,9 @@ enum KeyList { KEY_DIVISION = 0x00F7, KEY_YDIAERESIS = 0x00FF, - }; enum KeyModifierMask { - KEY_CODE_MASK = ((1 << 25) - 1), ///< Apply this mask to any keycode to remove modifiers. KEY_MODIFIER_MASK = (0xFF << 24), ///< Apply this mask to isolate modifiers. KEY_MASK_SHIFT = (1 << 25), @@ -314,7 +312,6 @@ enum KeyModifierMask { KEY_MASK_KPAD = (1 << 29), KEY_MASK_GROUP_SWITCH = (1 << 30) // bit 31 can't be used because variant uses regular 32 bits int as datatype - }; String keycode_get_string(uint32_t p_code); diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index 0d1a080682..016d9d0a09 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,18 +30,12 @@ #include "main_loop.h" -#include "core/script_language.h" +#include "core/object/script_language.h" void MainLoop::_bind_methods() { - - ClassDB::bind_method(D_METHOD("init"), &MainLoop::init); - ClassDB::bind_method(D_METHOD("iteration", "delta"), &MainLoop::iteration); - ClassDB::bind_method(D_METHOD("idle", "delta"), &MainLoop::idle); - ClassDB::bind_method(D_METHOD("finish"), &MainLoop::finish); - BIND_VMETHOD(MethodInfo("_initialize")); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_iteration", PropertyInfo(Variant::FLOAT, "delta"))); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_idle", PropertyInfo(Variant::FLOAT, "delta"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_physics_process", PropertyInfo(Variant::FLOAT, "delta"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_process", PropertyInfo(Variant::FLOAT, "delta"))); BIND_VMETHOD(MethodInfo("_finalize")); BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING); @@ -49,48 +43,46 @@ void MainLoop::_bind_methods() { BIND_CONSTANT(NOTIFICATION_WM_ABOUT); BIND_CONSTANT(NOTIFICATION_CRASH); BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE); - BIND_CONSTANT(NOTIFICATION_APP_RESUMED); - BIND_CONSTANT(NOTIFICATION_APP_PAUSED); + BIND_CONSTANT(NOTIFICATION_APPLICATION_RESUMED); + BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED); + BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN); + BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT); + BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED); ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted"))); }; -void MainLoop::set_init_script(const Ref<Script> &p_init_script) { - - init_script = p_init_script; -} - -MainLoop::MainLoop() { -} - -MainLoop::~MainLoop() { +void MainLoop::set_initialize_script(const Ref<Script> &p_initialize_script) { + initialize_script = p_initialize_script; } -void MainLoop::init() { - - if (init_script.is_valid()) - set_script(init_script); +void MainLoop::initialize() { + if (initialize_script.is_valid()) { + set_script(initialize_script); + } - if (get_script_instance()) + if (get_script_instance()) { get_script_instance()->call("_initialize"); + } } -bool MainLoop::iteration(float p_time) { - if (get_script_instance()) - return get_script_instance()->call("_iteration", p_time); +bool MainLoop::physics_process(float p_time) { + if (get_script_instance()) { + return get_script_instance()->call("_physics_process", p_time); + } return false; } -bool MainLoop::idle(float p_time) { - if (get_script_instance()) - return get_script_instance()->call("_idle", p_time); +bool MainLoop::process(float p_time) { + if (get_script_instance()) { + return get_script_instance()->call("_process", p_time); + } return false; } -void MainLoop::finish() { - +void MainLoop::finalize() { if (get_script_instance()) { get_script_instance()->call("_finalize"); set_script(Variant()); //clear script diff --git a/core/os/main_loop.h b/core/os/main_loop.h index 8f6c8c91b1..25a09fe98f 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,15 +32,14 @@ #define MAIN_LOOP_H #include "core/input/input_event.h" -#include "core/reference.h" -#include "core/script_language.h" +#include "core/object/reference.h" +#include "core/object/script_language.h" class MainLoop : public Object { - GDCLASS(MainLoop, Object); OBJ_CATEGORY("Main Loop"); - Ref<Script> init_script; + Ref<Script> initialize_script; protected: static void _bind_methods(); @@ -53,19 +52,22 @@ public: NOTIFICATION_WM_ABOUT = 2011, NOTIFICATION_CRASH = 2012, NOTIFICATION_OS_IME_UPDATE = 2013, - NOTIFICATION_APP_RESUMED = 2014, - NOTIFICATION_APP_PAUSED = 2015, + NOTIFICATION_APPLICATION_RESUMED = 2014, + NOTIFICATION_APPLICATION_PAUSED = 2015, + NOTIFICATION_APPLICATION_FOCUS_IN = 2016, + NOTIFICATION_APPLICATION_FOCUS_OUT = 2017, + NOTIFICATION_TEXT_SERVER_CHANGED = 2018, }; - virtual void init(); - virtual bool iteration(float p_time); - virtual bool idle(float p_time); - virtual void finish(); + virtual void initialize(); + virtual bool physics_process(float p_time); + virtual bool process(float p_time); + virtual void finalize(); - void set_init_script(const Ref<Script> &p_init_script); + void set_initialize_script(const Ref<Script> &p_initialize_script); - MainLoop(); - virtual ~MainLoop(); + MainLoop() {} + virtual ~MainLoop() {} }; #endif // MAIN_LOOP_H diff --git a/core/os/memory.cpp b/core/os/memory.cpp index d921c10ad4..14808c2ce6 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,36 +30,31 @@ #include "memory.h" -#include "core/error_macros.h" +#include "core/error/error_macros.h" #include "core/os/copymem.h" -#include "core/safe_refcount.h" +#include "core/templates/safe_refcount.h" #include <stdio.h> #include <stdlib.h> void *operator new(size_t p_size, const char *p_description) { - return Memory::alloc_static(p_size, false); } void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)) { - return p_allocfunc(p_size); } #ifdef _MSC_VER void operator delete(void *p_mem, const char *p_description) { - CRASH_NOW_MSG("Call to placement delete should not happen."); } void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size)) { - CRASH_NOW_MSG("Call to placement delete should not happen."); } void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description) { - CRASH_NOW_MSG("Call to placement delete should not happen."); } #endif @@ -72,7 +67,6 @@ uint64_t Memory::max_usage = 0; uint64_t Memory::alloc_count = 0; void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { - #ifdef DEBUG_ENABLED bool prepad = true; #else @@ -102,7 +96,6 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { } void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { - if (p_memory == nullptr) { return alloc_static(p_bytes, p_pad_align); } @@ -144,7 +137,6 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { return mem + PAD_ALIGN; } } else { - mem = (uint8_t *)realloc(mem, p_bytes); ERR_FAIL_COND_V(mem == nullptr && p_bytes > 0, nullptr); @@ -154,7 +146,6 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { } void Memory::free_static(void *p_ptr, bool p_pad_align) { - ERR_FAIL_COND(p_ptr == nullptr); uint8_t *mem = (uint8_t *)p_ptr; @@ -177,13 +168,11 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) { free(mem); } else { - free(mem); } } uint64_t Memory::get_mem_available() { - return -1; // 0xFFFF... } @@ -204,8 +193,6 @@ uint64_t Memory::get_mem_max_usage() { } _GlobalNil::_GlobalNil() { - - color = 1; left = this; right = this; parent = this; diff --git a/core/os/memory.h b/core/os/memory.h index dcaedd92ba..c2ae3f4ae7 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,8 +31,8 @@ #ifndef MEMORY_H #define MEMORY_H -#include "core/error_macros.h" -#include "core/safe_refcount.h" +#include "core/error/error_macros.h" +#include "core/templates/safe_refcount.h" #include <stddef.h> @@ -41,7 +41,6 @@ #endif class Memory { - Memory(); #ifdef DEBUG_ENABLED static uint64_t mem_usage; @@ -87,7 +86,6 @@ _ALWAYS_INLINE_ void postinitialize_handler(void *) {} template <class T> _ALWAYS_INLINE_ T *_post_initialize(T *p_obj) { - postinitialize_handler(p_obj); return p_obj; } @@ -110,44 +108,48 @@ _ALWAYS_INLINE_ bool predelete_handler(void *) { template <class T> void memdelete(T *p_class) { - - if (!predelete_handler(p_class)) + if (!predelete_handler(p_class)) { return; // doesn't want to be deleted - if (!__has_trivial_destructor(T)) + } + if (!__has_trivial_destructor(T)) { p_class->~T(); + } Memory::free_static(p_class, false); } template <class T, class A> void memdelete_allocator(T *p_class) { - - if (!predelete_handler(p_class)) + if (!predelete_handler(p_class)) { return; // doesn't want to be deleted - if (!__has_trivial_destructor(T)) + } + if (!__has_trivial_destructor(T)) { p_class->~T(); + } A::free(p_class); } -#define memdelete_notnull(m_v) \ - { \ - if (m_v) memdelete(m_v); \ +#define memdelete_notnull(m_v) \ + { \ + if (m_v) { \ + memdelete(m_v); \ + } \ } #define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count) template <typename T> T *memnew_arr_template(size_t p_elements, const char *p_descr = "") { - - if (p_elements == 0) - return 0; + if (p_elements == 0) { + return nullptr; + } /** overloading operator new[] cannot be done , because it may not return the real allocated address (it may pad the 'element count' before the actual array). Because of that, it must be done by hand. This is the same strategy used by std::vector, and the Vector class, so it should be safe.*/ size_t len = sizeof(T) * p_elements; uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true); - T *failptr = 0; //get rid of a warning + T *failptr = nullptr; //get rid of a warning ERR_FAIL_COND_V(!mem, failptr); *(mem - 1) = p_elements; @@ -170,14 +172,12 @@ T *memnew_arr_template(size_t p_elements, const char *p_descr = "") { template <typename T> size_t memarr_len(const T *p_class) { - uint64_t *ptr = (uint64_t *)p_class; return *(ptr - 1); } template <typename T> void memdelete_arr(T *p_class) { - uint64_t *ptr = (uint64_t *)p_class; if (!__has_trivial_destructor(T)) { @@ -192,8 +192,7 @@ void memdelete_arr(T *p_class) { } struct _GlobalNil { - - int color; + int color = 1; _GlobalNil *right; _GlobalNil *left; _GlobalNil *parent; @@ -202,7 +201,6 @@ struct _GlobalNil { }; struct _GlobalNilClass { - static _GlobalNil _nil; }; diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp index efd87d3ab6..a8be84c56c 100644 --- a/core/os/midi_driver.cpp +++ b/core/os/midi_driver.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -36,17 +36,14 @@ uint8_t MIDIDriver::last_received_message = 0x00; MIDIDriver *MIDIDriver::singleton = nullptr; MIDIDriver *MIDIDriver::get_singleton() { - return singleton; } void MIDIDriver::set_singleton() { - singleton = this; } void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) { - Ref<InputEventMIDI> event; event.instance(); uint32_t param_position = 1; @@ -122,12 +119,10 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_ } PackedStringArray MIDIDriver::get_connected_inputs() { - PackedStringArray list; return list; } MIDIDriver::MIDIDriver() { - set_singleton(); } diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h index b7377a8a40..ccf624e07e 100644 --- a/core/os/midi_driver.h +++ b/core/os/midi_driver.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,14 +32,13 @@ #define MIDI_DRIVER_H #include "core/typedefs.h" -#include "core/variant.h" +#include "core/variant/variant.h" /** * Multi-Platform abstraction for accessing to MIDI. */ class MIDIDriver { - static MIDIDriver *singleton; static uint8_t last_received_message; diff --git a/core/os/mutex.cpp b/core/os/mutex.cpp index 31a0dc2bfa..b7d7752d35 100644 --- a/core/os/mutex.cpp +++ b/core/os/mutex.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/os/mutex.h b/core/os/mutex.h index 526549dd93..d77ec362a1 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef MUTEX_H #define MUTEX_H -#include "core/error_list.h" +#include "core/error/error_list.h" #include "core/typedefs.h" #if !defined(NO_THREADS) @@ -82,8 +82,7 @@ extern template class MutexLock<MutexImpl<std::mutex>>; #else class FakeMutex { - - FakeMutex(){}; + FakeMutex() {} }; template <class MutexT> diff --git a/core/os/os.cpp b/core/os/os.cpp index 425132fbec..182bab4058 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,20 +30,20 @@ #include "os.h" +#include "core/config/project_settings.h" #include "core/input/input.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/os/midi_driver.h" -#include "core/project_settings.h" #include "core/version_generated.gen.h" #include "servers/audio_server.h" #include <stdarg.h> OS *OS::singleton = nullptr; +uint64_t OS::target_ticks = 0; OS *OS::get_singleton() { - return singleton; } @@ -80,23 +80,13 @@ String OS::get_iso_date_time(bool local) const { timezone; } -uint64_t OS::get_splash_tick_msec() const { - return _msec_splash; -} -uint64_t OS::get_unix_time() const { - - return 0; -}; -uint64_t OS::get_system_time_secs() const { - return 0; -} -uint64_t OS::get_system_time_msecs() const { +double OS::get_unix_time() const { return 0; } -void OS::debug_break(){ +void OS::debug_break() { // something -}; +} void OS::_set_logger(CompositeLogger *p_logger) { if (_logger) { @@ -116,19 +106,17 @@ void OS::add_logger(Logger *p_logger) { } void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type) { - _logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type); } void OS::print(const char *p_format, ...) { - va_list argp; va_start(argp, p_format); _logger->logv(p_format, argp, false); va_end(argp); -}; +} void OS::printerr(const char *p_format, ...) { va_list argp; @@ -137,73 +125,67 @@ void OS::printerr(const char *p_format, ...) { _logger->logv(p_format, argp, true); va_end(argp); -}; +} void OS::set_low_processor_usage_mode(bool p_enabled) { - low_processor_usage_mode = p_enabled; } bool OS::is_in_low_processor_usage_mode() const { - return low_processor_usage_mode; } void OS::set_low_processor_usage_mode_sleep_usec(int p_usec) { - low_processor_usage_mode_sleep_usec = p_usec; } int OS::get_low_processor_usage_mode_sleep_usec() const { - return low_processor_usage_mode_sleep_usec; } String OS::get_executable_path() const { - return _execpath; } int OS::get_process_id() const { - return -1; -}; +} void OS::vibrate_handheld(int p_duration_ms) { - WARN_PRINT("vibrate_handheld() only works with Android and iOS"); } bool OS::is_stdout_verbose() const { - return _verbose_stdout; } -void OS::dump_memory_to_file(const char *p_file) { +bool OS::is_stdout_debug_enabled() const { + return _debug_stdout; +} +void OS::dump_memory_to_file(const char *p_file) { //Memory::dump_static_mem_to_file(p_file); } static FileAccess *_OSPRF = nullptr; static void _OS_printres(Object *p_obj) { - Resource *res = Object::cast_to<Resource>(p_obj); - if (!res) + if (!res) { return; + } String str = itos(res->get_instance_id()) + String(res->get_class()) + ":" + String(res->get_name()) + " - " + res->get_path(); - if (_OSPRF) + if (_OSPRF) { _OSPRF->store_line(str); - else + } else { print_line(str); + } } void OS::print_all_resources(String p_to_file) { - ERR_FAIL_COND(p_to_file != "" && _OSPRF); if (p_to_file != "") { - Error err; _OSPRF = FileAccess::open(p_to_file, FileAccess::WRITE, &err); if (err != OK) { @@ -215,50 +197,43 @@ void OS::print_all_resources(String p_to_file) { ObjectDB::debug_objects(_OS_printres); if (p_to_file != "") { - - if (_OSPRF) + if (_OSPRF) { memdelete(_OSPRF); + } _OSPRF = nullptr; } } void OS::print_resources_in_use(bool p_short) { - ResourceCache::dump(nullptr, p_short); } void OS::dump_resources_to_file(const char *p_file) { - ResourceCache::dump(p_file); } void OS::set_no_window_mode(bool p_enable) { - _no_window = p_enable; } bool OS::is_no_window_mode_enabled() const { - return _no_window; } int OS::get_exit_code() const { - return _exit_code; } -void OS::set_exit_code(int p_code) { +void OS::set_exit_code(int p_code) { _exit_code = p_code; } String OS::get_locale() const { - return "en"; } // Helper function to ensure that a dir name/path will be valid on the OS String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator) const { - Vector<String> invalid_chars = String(": * ? \" < > |").split(" "); if (p_allow_dir_separator) { // Dir separators are allowed, but disallow ".." to avoid going up the filesystem @@ -278,76 +253,64 @@ String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separato // Get properly capitalized engine name for system paths String OS::get_godot_dir_name() const { - // Default to lowercase, so only override when different case is needed return String(VERSION_SHORT_NAME).to_lower(); } // OS equivalent of XDG_DATA_HOME String OS::get_data_path() const { - return "."; } // OS equivalent of XDG_CONFIG_HOME String OS::get_config_path() const { - return "."; } // OS equivalent of XDG_CACHE_HOME String OS::get_cache_path() const { - return "."; } // Path to macOS .app bundle resources String OS::get_bundle_resource_dir() const { - return "."; -}; +} // OS specific path for user:// String OS::get_user_data_dir() const { - return "."; -}; +} // Absolute path to res:// String OS::get_resource_dir() const { - return ProjectSettings::get_singleton()->get_resource_path(); } // Access system-specific dirs like Documents, Downloads, etc. String OS::get_system_dir(SystemDir p_dir) const { - return "."; } Error OS::shell_open(String p_uri) { return ERR_UNAVAILABLE; -}; +} // implement these with the canvas? uint64_t OS::get_static_memory_usage() const { - return Memory::get_mem_usage(); } uint64_t OS::get_static_memory_peak_usage() const { - return Memory::get_mem_max_usage(); } Error OS::set_cwd(const String &p_cwd) { - return ERR_CANT_OPEN; } uint64_t OS::get_free_static_memory() const { - return Memory::get_mem_available(); } @@ -355,7 +318,6 @@ void OS::yield() { } void OS::ensure_user_data_dir() { - String dd = get_user_data_dir(); DirAccess *da = DirAccess::open(dd); if (da) { @@ -371,28 +333,23 @@ void OS::ensure_user_data_dir() { } String OS::get_model_name() const { - return "GenericDevice"; } void OS::set_cmdline(const char *p_execpath, const List<String> &p_args) { - _execpath = p_execpath; _cmdline = p_args; -}; +} String OS::get_unique_id() const { - ERR_FAIL_V(""); } int OS::get_processor_count() const { - return 1; } bool OS::can_use_threads() const { - #ifdef NO_THREADS return false; #else @@ -401,24 +358,25 @@ bool OS::can_use_threads() const { } void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) { - has_server_feature_callback = p_callback; } bool OS::has_feature(const String &p_feature) { - - if (p_feature == get_name()) + if (p_feature == get_name()) { return true; + } #ifdef DEBUG_ENABLED - if (p_feature == "debug") + if (p_feature == "debug") { return true; + } #else if (p_feature == "release") return true; #endif #ifdef TOOLS_ENABLED - if (p_feature == "editor") + if (p_feature == "editor") { return true; + } #else if (p_feature == "standalone") return true; @@ -458,15 +416,17 @@ bool OS::has_feature(const String &p_feature) { } #endif - if (_check_internal_feature_support(p_feature)) + if (_check_internal_feature_support(p_feature)) { return true; + } if (has_server_feature_callback && has_server_feature_callback(p_feature)) { return true; } - if (ProjectSettings::get_singleton()->has_custom_feature(p_feature)) + if (ProjectSettings::get_singleton()->has_custom_feature(p_feature)) { return true; + } return false; } @@ -485,47 +445,67 @@ List<String> OS::get_restart_on_exit_arguments() const { } PackedStringArray OS::get_connected_midi_inputs() { - - if (MIDIDriver::get_singleton()) + if (MIDIDriver::get_singleton()) { return MIDIDriver::get_singleton()->get_connected_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()) + if (MIDIDriver::get_singleton()) { MIDIDriver::get_singleton()->open(); + } else { + ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name())); + } } void OS::close_midi_inputs() { - - if (MIDIDriver::get_singleton()) + if (MIDIDriver::get_singleton()) { MIDIDriver::get_singleton()->close(); + } else { + ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name())); + } } -OS::OS() { - void *volatile stack_bottom; +void OS::add_frame_delay(bool p_can_draw) { + const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay(); + if (frame_delay) { + // Add fixed frame delay to decrease CPU/GPU usage. This doesn't take + // the actual frame time into account. + // Due to the high fluctuation of the actual sleep duration, it's not recommended + // to use this as a FPS limiter. + delay_usec(frame_delay * 1000); + } - restart_on_exit = false; - singleton = this; - _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0. - low_processor_usage_mode = false; - low_processor_usage_mode_sleep_usec = 10000; - _verbose_stdout = false; - _no_window = false; - _exit_code = 0; + // Add a dynamic frame delay to decrease CPU/GPU usage. This takes the + // previous frame time into account for a smoother result. + uint64_t dynamic_delay = 0; + if (is_in_low_processor_usage_mode() || !p_can_draw) { + dynamic_delay = get_low_processor_usage_mode_sleep_usec(); + } + const int target_fps = Engine::get_singleton()->get_target_fps(); + if (target_fps > 0 && !Engine::get_singleton()->is_editor_hint()) { + // Override the low processor usage mode sleep delay if the target FPS is lower. + dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / target_fps)); + } - _render_thread_mode = RENDER_THREAD_SAFE; + if (dynamic_delay > 0) { + target_ticks += dynamic_delay; + uint64_t current_ticks = get_ticks_usec(); - _allow_hidpi = false; - _allow_layered = false; - _stack_bottom = (void *)(&stack_bottom); + if (current_ticks < target_ticks) { + delay_usec(target_ticks - current_ticks); + } - _logger = nullptr; + current_ticks = get_ticks_usec(); + target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay); + } +} - has_server_feature_callback = nullptr; +OS::OS() { + singleton = this; Vector<Logger *> loggers; loggers.push_back(memnew(StdLogger)); diff --git a/core/os/os.h b/core/os/os.h index 38114e6814..e02ce7d5ec 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,43 +31,40 @@ #ifndef OS_H #define OS_H -#include "core/engine.h" -#include "core/image.h" +#include "core/config/engine.h" +#include "core/io/image.h" #include "core/io/logger.h" -#include "core/list.h" #include "core/os/main_loop.h" -#include "core/ustring.h" -#include "core/vector.h" +#include "core/string/ustring.h" +#include "core/templates/list.h" +#include "core/templates/vector.h" #include <stdarg.h> class OS { - static OS *singleton; + static uint64_t target_ticks; String _execpath; List<String> _cmdline; - bool _keep_screen_on; - bool low_processor_usage_mode; - int low_processor_usage_mode_sleep_usec; - bool _verbose_stdout; + bool _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0. + bool low_processor_usage_mode = false; + int low_processor_usage_mode_sleep_usec = 10000; + bool _verbose_stdout = false; + bool _debug_stdout = false; String _local_clipboard; - uint64_t _msec_splash; - bool _no_window; - int _exit_code; + bool _no_window = false; + int _exit_code = 0; int _orientation; - bool _allow_hidpi; - bool _allow_layered; + bool _allow_hidpi = false; + bool _allow_layered = false; bool _use_vsync; bool _vsync_via_compositor; - bool _disable_wintab; char *last_error; - void *_stack_bottom; - - CompositeLogger *_logger; + CompositeLogger *_logger = nullptr; - bool restart_on_exit; + bool restart_on_exit = false; List<String> restart_commandline; protected: @@ -78,7 +75,6 @@ public: typedef bool (*HasServerFeatureCallback)(const String &p_feature); enum RenderThreadMode { - RENDER_THREAD_UNSAFE, RENDER_THREAD_SAFE, RENDER_SEPARATE_THREAD @@ -86,11 +82,13 @@ public: protected: friend class Main; + // Needed by tests to setup command-line args. + friend int test_main(int argc, char *argv[]); - HasServerFeatureCallback has_server_feature_callback; - RenderThreadMode _render_thread_mode; + HasServerFeatureCallback has_server_feature_callback = nullptr; + RenderThreadMode _render_thread_mode = RENDER_THREAD_SAFE; - // functions used by main to initialize/deinitialize the OS + // Functions used by Main to initialize/deinitialize the OS. void add_logger(Logger *p_logger); virtual void initialize() = 0; @@ -131,7 +129,8 @@ public: virtual int get_low_processor_usage_mode_sleep_usec() const; virtual String get_executable_path() const; - virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) = 0; + virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) = 0; + virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) = 0; virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; virtual void vibrate_handheld(int p_duration_ms = 500); @@ -149,7 +148,11 @@ public: bool is_layered_allowed() const { return _allow_layered; } bool is_hidpi_allowed() const { return _allow_hidpi; } - bool is_wintab_disabled() const { return _disable_wintab; } + + virtual int get_tablet_driver_count() const { return 0; }; + virtual String get_tablet_driver_name(int p_driver) const { return ""; }; + virtual String get_current_tablet_driver() const { return ""; }; + virtual void set_current_tablet_driver(const String &p_driver){}; void ensure_user_data_dir(); @@ -185,7 +188,6 @@ public: }; struct Date { - int year; Month month; int day; @@ -194,7 +196,6 @@ public: }; struct Time { - int hour; int min; int sec; @@ -209,18 +210,18 @@ public: virtual Time get_time(bool local = false) const = 0; virtual TimeZoneInfo get_time_zone_info() const = 0; virtual String get_iso_date_time(bool local = false) const; - virtual uint64_t get_unix_time() const; - virtual uint64_t get_system_time_secs() const; - virtual uint64_t get_system_time_msecs() const; + virtual double get_unix_time() const; virtual void delay_usec(uint32_t p_usec) const = 0; + virtual void add_frame_delay(bool p_can_draw); + virtual uint64_t get_ticks_usec() const = 0; uint32_t get_ticks_msec() const; - uint64_t get_splash_tick_msec() const; virtual bool is_userfs_persistent() const { return true; } bool is_stdout_verbose() const; + bool is_stdout_debug_enabled() const; virtual void disable_crash_handler() {} virtual bool is_disable_crash_handler() const { return false; } diff --git a/core/pool_allocator.cpp b/core/os/pool_allocator.cpp index b74540395c..b538ca0c96 100644 --- a/core/pool_allocator.cpp +++ b/core/os/pool_allocator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,11 +30,11 @@ #include "pool_allocator.h" -#include "core/error_macros.h" +#include "core/error/error_macros.h" #include "core/os/copymem.h" #include "core/os/memory.h" #include "core/os/os.h" -#include "core/print_string.h" +#include "core/string/print_string.h" #include <assert.h> @@ -53,12 +53,11 @@ void PoolAllocator::mt_unlock() const { } bool PoolAllocator::get_free_entry(EntryArrayPos *p_pos) { - - if (entry_count == entry_max) + if (entry_count == entry_max) { return false; + } for (int i = 0; i < entry_max; i++) { - if (entry_array[i].len == 0) { *p_pos = i; return true; @@ -77,13 +76,11 @@ bool PoolAllocator::get_free_entry(EntryArrayPos *p_pos) { * @return false if hole found, true if no hole found */ bool PoolAllocator::find_hole(EntryArrayPos *p_pos, int p_for_size) { - /* position where previous entry ends. Defaults to zero (begin of pool) */ int prev_entry_end_pos = 0; for (int i = 0; i < entry_count; i++) { - Entry &entry = entry_array[entry_indices[i]]; /* determine hole size to previous entry */ @@ -111,13 +108,12 @@ bool PoolAllocator::find_hole(EntryArrayPos *p_pos, int p_for_size) { } void PoolAllocator::compact(int p_up_to) { - uint32_t prev_entry_end_pos = 0; - if (p_up_to < 0) + if (p_up_to < 0) { p_up_to = entry_count; + } for (int i = 0; i < p_up_to; i++) { - Entry &entry = entry_array[entry_indices[i]]; /* determine hole size to previous entry */ @@ -126,7 +122,6 @@ void PoolAllocator::compact(int p_up_to) { /* if we can compact, do it */ if (hole_size > 0 && !entry.lock) { - COMPACT_CHUNK(entry, prev_entry_end_pos); } @@ -136,11 +131,9 @@ void PoolAllocator::compact(int p_up_to) { } void PoolAllocator::compact_up(int p_from) { - uint32_t next_entry_end_pos = pool_size; // - static_area_size; for (int i = entry_count - 1; i >= p_from; i--) { - Entry &entry = entry_array[entry_indices[i]]; /* determine hole size to nextious entry */ @@ -149,7 +142,6 @@ void PoolAllocator::compact_up(int p_from) { /* if we can compact, do it */ if (hole_size > 0 && !entry.lock) { - COMPACT_CHUNK(entry, (next_entry_end_pos - aligned(entry.len))); } @@ -159,30 +151,29 @@ void PoolAllocator::compact_up(int p_from) { } bool PoolAllocator::find_entry_index(EntryIndicesPos *p_map_pos, Entry *p_entry) { - EntryArrayPos entry_pos = entry_max; for (int i = 0; i < entry_count; i++) { - if (&entry_array[entry_indices[i]] == p_entry) { - entry_pos = i; break; } } - if (entry_pos == entry_max) + if (entry_pos == entry_max) { return false; + } *p_map_pos = entry_pos; return true; } PoolAllocator::ID PoolAllocator::alloc(int p_size) { - ERR_FAIL_COND_V(p_size < 1, POOL_ALLOCATOR_INVALID_ID); #ifdef DEBUG_ENABLED - if (p_size > free_mem) OS::get_singleton()->debug_break(); + if (p_size > free_mem) { + OS::get_singleton()->debug_break(); + } #endif ERR_FAIL_COND_V(p_size > free_mem, POOL_ALLOCATOR_INVALID_ID); @@ -220,7 +211,6 @@ PoolAllocator::ID PoolAllocator::alloc(int p_size) { /* move all entry indices up, make room for this one */ for (int i = entry_count; i > new_entry_indices_pos; i--) { - entry_indices[i] = entry_indices[i - 1]; } @@ -235,8 +225,9 @@ PoolAllocator::ID PoolAllocator::alloc(int p_size) { entry.lock = 0; entry.check = (check_count++) & CHECK_MASK; free_mem -= size_to_alloc; - if (free_mem < free_mem_peak) + if (free_mem < free_mem_peak) { free_mem_peak = free_mem; + } ID retval = (entry_indices[new_entry_indices_pos] << CHECK_BITS) | entry.check; mt_unlock(); @@ -247,7 +238,6 @@ PoolAllocator::ID PoolAllocator::alloc(int p_size) { } PoolAllocator::Entry *PoolAllocator::get_entry(ID p_mem) { - unsigned int check = p_mem & CHECK_MASK; int entry = p_mem >> CHECK_BITS; ERR_FAIL_INDEX_V(entry, entry_max, nullptr); @@ -258,7 +248,6 @@ PoolAllocator::Entry *PoolAllocator::get_entry(ID p_mem) { } const PoolAllocator::Entry *PoolAllocator::get_entry(ID p_mem) const { - unsigned int check = p_mem & CHECK_MASK; int entry = p_mem >> CHECK_BITS; ERR_FAIL_INDEX_V(entry, entry_max, nullptr); @@ -269,7 +258,6 @@ const PoolAllocator::Entry *PoolAllocator::get_entry(ID p_mem) const { } void PoolAllocator::free(ID p_mem) { - mt_lock(); Entry *e = get_entry(p_mem); if (!e) { @@ -287,13 +275,11 @@ void PoolAllocator::free(ID p_mem) { bool index_found = find_entry_index(&entry_indices_pos, e); if (!index_found) { - mt_unlock(); ERR_FAIL_COND(!index_found); } for (int i = entry_indices_pos; i < (entry_count - 1); i++) { - entry_indices[i] = entry_indices[i + 1]; } @@ -304,13 +290,11 @@ void PoolAllocator::free(ID p_mem) { } int PoolAllocator::get_size(ID p_mem) const { - int size; mt_lock(); const Entry *e = get_entry(p_mem); if (!e) { - mt_unlock(); ERR_PRINT("!e"); return 0; @@ -324,7 +308,6 @@ int PoolAllocator::get_size(ID p_mem) const { } Error PoolAllocator::resize(ID p_mem, int p_new_size) { - mt_lock(); Entry *e = get_entry(p_mem); @@ -341,12 +324,10 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) { uint32_t alloc_size = aligned(p_new_size); if ((uint32_t)aligned(e->len) == alloc_size) { - e->len = p_new_size; mt_unlock(); return OK; } else if (e->len > (uint32_t)p_new_size) { - free_mem += aligned(e->len); free_mem -= alloc_size; e->len = p_new_size; @@ -360,14 +341,13 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) { if (uint32_t(_free + aligned(e->len)) < alloc_size) { mt_unlock(); ERR_FAIL_V(ERR_OUT_OF_MEMORY); - }; + } EntryIndicesPos entry_indices_pos; bool index_found = find_entry_index(&entry_indices_pos, e); if (!index_found) { - mt_unlock(); ERR_FAIL_COND_V(!index_found, ERR_BUG); } @@ -378,7 +358,7 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) { next_pos = pool_size; // - static_area_size; } else { next_pos = entry_array[entry_indices[entry_indices_pos + 1]].pos; - }; + } if ((next_pos - e->pos) > alloc_size) { free_mem += aligned(e->len); @@ -397,8 +377,9 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) { e->len = p_new_size; free_mem -= alloc_size; mt_unlock(); - if (free_mem < free_mem_peak) + if (free_mem < free_mem_peak) { free_mem_peak = free_mem; + } return OK; } @@ -412,8 +393,9 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) { e->len = p_new_size; free_mem -= alloc_size; mt_unlock(); - if (free_mem < free_mem_peak) + if (free_mem < free_mem_peak) { free_mem_peak = free_mem; + } return OK; } @@ -422,13 +404,12 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) { } Error PoolAllocator::lock(ID p_mem) { - - if (!needs_locking) + if (!needs_locking) { return OK; + } mt_lock(); Entry *e = get_entry(p_mem); if (!e) { - mt_unlock(); ERR_PRINT("!e"); return ERR_INVALID_PARAMETER; @@ -439,14 +420,13 @@ Error PoolAllocator::lock(ID p_mem) { } bool PoolAllocator::is_locked(ID p_mem) const { - - if (!needs_locking) + if (!needs_locking) { return false; + } mt_lock(); const Entry *e = ((PoolAllocator *)(this))->get_entry(p_mem); if (!e) { - mt_unlock(); ERR_PRINT("!e"); return false; @@ -457,9 +437,7 @@ bool PoolAllocator::is_locked(ID p_mem) const { } const void *PoolAllocator::get(ID p_mem) const { - if (!needs_locking) { - const Entry *e = get_entry(p_mem); ERR_FAIL_COND_V(!e, nullptr); return &pool[e->pos]; @@ -469,19 +447,16 @@ const void *PoolAllocator::get(ID p_mem) const { const Entry *e = get_entry(p_mem); if (!e) { - mt_unlock(); ERR_FAIL_COND_V(!e, nullptr); } if (e->lock == 0) { - mt_unlock(); ERR_PRINT("e->lock == 0"); return nullptr; } if ((int)e->pos >= pool_size) { - mt_unlock(); ERR_PRINT("e->pos<0 || e->pos>=pool_size"); return nullptr; @@ -494,9 +469,7 @@ const void *PoolAllocator::get(ID p_mem) const { } void *PoolAllocator::get(ID p_mem) { - if (!needs_locking) { - Entry *e = get_entry(p_mem); ERR_FAIL_COND_V(!e, nullptr); return &pool[e->pos]; @@ -506,12 +479,10 @@ void *PoolAllocator::get(ID p_mem) { Entry *e = get_entry(p_mem); if (!e) { - mt_unlock(); ERR_FAIL_COND_V(!e, nullptr); } if (e->lock == 0) { - //assert(0); mt_unlock(); ERR_PRINT("e->lock == 0"); @@ -519,7 +490,6 @@ void *PoolAllocator::get(ID p_mem) { } if ((int)e->pos >= pool_size) { - mt_unlock(); ERR_PRINT("e->pos<0 || e->pos>=pool_size"); return nullptr; @@ -530,10 +500,11 @@ void *PoolAllocator::get(ID p_mem) { return ptr; } -void PoolAllocator::unlock(ID p_mem) { - if (!needs_locking) +void PoolAllocator::unlock(ID p_mem) { + if (!needs_locking) { return; + } mt_lock(); Entry *e = get_entry(p_mem); if (!e) { @@ -550,22 +521,18 @@ void PoolAllocator::unlock(ID p_mem) { } int PoolAllocator::get_used_mem() const { - return pool_size - free_mem; } int PoolAllocator::get_free_peak() { - return free_mem_peak; } int PoolAllocator::get_free_mem() { - return free_mem; } void PoolAllocator::create_pool(void *p_mem, int p_size, int p_max_entries) { - pool = (uint8_t *)p_mem; pool_size = p_size; @@ -581,7 +548,6 @@ void PoolAllocator::create_pool(void *p_mem, int p_size, int p_max_entries) { } PoolAllocator::PoolAllocator(int p_size, bool p_needs_locking, int p_max_entries) { - mem_ptr = memalloc(p_size); ERR_FAIL_COND(!mem_ptr); align = 1; @@ -590,9 +556,7 @@ PoolAllocator::PoolAllocator(int p_size, bool p_needs_locking, int p_max_entries } PoolAllocator::PoolAllocator(void *p_mem, int p_size, int p_align, bool p_needs_locking, int p_max_entries) { - if (p_align > 1) { - uint8_t *mem8 = (uint8_t *)p_mem; uint64_t ofs = (uint64_t)mem8; if (ofs % p_align) { @@ -600,8 +564,8 @@ PoolAllocator::PoolAllocator(void *p_mem, int p_size, int p_align, bool p_needs_ mem8 += p_align - (ofs % p_align); p_size -= dif; p_mem = (void *)mem8; - }; - }; + } + } create_pool(p_mem, p_size, p_max_entries); needs_locking = p_needs_locking; @@ -610,22 +574,22 @@ PoolAllocator::PoolAllocator(void *p_mem, int p_size, int p_align, bool p_needs_ } PoolAllocator::PoolAllocator(int p_align, int p_size, bool p_needs_locking, int p_max_entries) { - ERR_FAIL_COND(p_align < 1); mem_ptr = Memory::alloc_static(p_size + p_align, true); uint8_t *mem8 = (uint8_t *)mem_ptr; uint64_t ofs = (uint64_t)mem8; - if (ofs % p_align) + if (ofs % p_align) { mem8 += p_align - (ofs % p_align); + } create_pool(mem8, p_size, p_max_entries); needs_locking = p_needs_locking; align = p_align; } PoolAllocator::~PoolAllocator() { - - if (mem_ptr) + if (mem_ptr) { memfree(mem_ptr); + } memdelete_arr(entry_array); memdelete_arr(entry_indices); diff --git a/core/pool_allocator.h b/core/os/pool_allocator.h index 8c1710ebe0..15e50dac90 100644 --- a/core/pool_allocator.h +++ b/core/os/pool_allocator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -43,7 +43,6 @@ */ enum { - POOL_ALLOCATOR_INVALID_ID = -1 ///< default invalid value. use INVALID_ID( id ) to test }; @@ -60,11 +59,10 @@ private: }; struct Entry { - - unsigned int pos; - unsigned int len; - unsigned int lock; - unsigned int check; + unsigned int pos = 0; + unsigned int len = 0; + unsigned int lock = 0; + unsigned int check = 0; inline void clear() { pos = 0; @@ -72,7 +70,7 @@ private: lock = 0; check = 0; } - Entry() { clear(); } + Entry() {} }; typedef int EntryArrayPos; @@ -99,10 +97,10 @@ private: return p_entry.pos + aligned(p_entry.len); } inline int aligned(int p_size) const { - int rem = p_size % align; - if (rem) + if (rem) { p_size += align - rem; + } return p_size; } diff --git a/core/os/rw_lock.cpp b/core/os/rw_lock.cpp index 1dd2c3bccb..26db0aab30 100644 --- a/core/os/rw_lock.cpp +++ b/core/os/rw_lock.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,18 +30,14 @@ #include "rw_lock.h" -#include "core/error_macros.h" +#include "core/error/error_macros.h" #include <stddef.h> RWLock *(*RWLock::create_func)() = nullptr; RWLock *RWLock::create() { - ERR_FAIL_COND_V(!create_func, nullptr); return create_func(); } - -RWLock::~RWLock() { -} diff --git a/core/os/rw_lock.h b/core/os/rw_lock.h index 64dfbef20c..ff6ad3cc4a 100644 --- a/core/os/rw_lock.h +++ b/core/os/rw_lock.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef RW_LOCK_H #define RW_LOCK_H -#include "core/error_list.h" +#include "core/error/error_list.h" class RWLock { protected: @@ -48,34 +48,40 @@ public: static RWLock *create(); ///< Create a rwlock - virtual ~RWLock(); + virtual ~RWLock() {} }; class RWLockRead { - RWLock *lock; public: RWLockRead(const RWLock *p_lock) { lock = const_cast<RWLock *>(p_lock); - if (lock) lock->read_lock(); + if (lock) { + lock->read_lock(); + } } ~RWLockRead() { - if (lock) lock->read_unlock(); + if (lock) { + lock->read_unlock(); + } } }; class RWLockWrite { - RWLock *lock; public: RWLockWrite(RWLock *p_lock) { lock = p_lock; - if (lock) lock->write_lock(); + if (lock) { + lock->write_lock(); + } } ~RWLockWrite() { - if (lock) lock->write_unlock(); + if (lock) { + lock->write_unlock(); + } } }; diff --git a/core/os/semaphore.cpp b/core/os/semaphore.cpp deleted file mode 100644 index 93f1e2dff4..0000000000 --- a/core/os/semaphore.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/*************************************************************************/ -/* semaphore.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 "semaphore.h" diff --git a/core/os/semaphore.h b/core/os/semaphore.h index 3d9d1ab984..01ae7a3c65 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef SEMAPHORE_H #define SEMAPHORE_H -#include "core/error_list.h" +#include "core/error/error_list.h" #include "core/typedefs.h" #if !defined(NO_THREADS) @@ -54,8 +54,9 @@ public: _ALWAYS_INLINE_ void wait() const { std::unique_lock<decltype(mutex_)> lock(mutex_); - while (!count_) // Handle spurious wake-ups. + while (!count_) { // Handle spurious wake-ups. condition_.wait(lock); + } --count_; } diff --git a/core/spin_lock.h b/core/os/spin_lock.h index c48631f94a..929e8b9a58 100644 --- a/core/spin_lock.h +++ b/core/os/spin_lock.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,6 +32,7 @@ #define SPIN_LOCK_H #include "core/typedefs.h" + #include <atomic> class SpinLock { diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 294b52f00c..0ed8825452 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -38,37 +38,29 @@ Error (*Thread::set_name_func)(const String &) = nullptr; Thread::ID Thread::_main_thread_id = 0; Thread::ID Thread::get_caller_id() { - - if (get_thread_id_func) + if (get_thread_id_func) { return get_thread_id_func(); + } return 0; } Thread *Thread::create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings) { - if (create_func) { - return create_func(p_callback, p_user, p_settings); } return nullptr; } void Thread::wait_to_finish(Thread *p_thread) { - - if (wait_to_finish_func) + if (wait_to_finish_func) { wait_to_finish_func(p_thread); + } } Error Thread::set_name(const String &p_name) { - - if (set_name_func) + if (set_name_func) { return set_name_func(p_name); + } return ERR_UNAVAILABLE; -}; - -Thread::Thread() { -} - -Thread::~Thread() { } diff --git a/core/os/thread.h b/core/os/thread.h index 76d296bcf7..993c7ad33d 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,22 +31,20 @@ #ifndef THREAD_H #define THREAD_H +#include "core/string/ustring.h" #include "core/typedefs.h" -#include "core/ustring.h" typedef void (*ThreadCreateCallback)(void *p_userdata); class Thread { public: enum Priority { - PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH }; struct Settings { - Priority priority; Settings() { priority = PRIORITY_NORMAL; } }; @@ -63,7 +61,7 @@ protected: static ID _main_thread_id; - Thread(); + Thread() {} public: virtual ID get_id() const = 0; @@ -74,7 +72,7 @@ public: static void wait_to_finish(Thread *p_thread); ///< waits until thread is finished, and deallocates it. static Thread *create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings = Settings()); ///< Static function to create a thread, will call p_callback - virtual ~Thread(); + virtual ~Thread() {} }; #endif // THREAD_H diff --git a/core/os/thread_dummy.cpp b/core/os/thread_dummy.cpp index 9dcddcae11..006757b8e4 100644 --- a/core/os/thread_dummy.cpp +++ b/core/os/thread_dummy.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -34,16 +34,16 @@ Thread *ThreadDummy::create(ThreadCreateCallback p_callback, void *p_user, const Thread::Settings &p_settings) { return memnew(ThreadDummy); -}; +} void ThreadDummy::make_default() { Thread::create_func = &ThreadDummy::create; -}; +} RWLock *RWLockDummy::create() { return memnew(RWLockDummy); -}; +} void RWLockDummy::make_default() { RWLock::create_func = &RWLockDummy::create; -}; +} diff --git a/core/os/thread_dummy.h b/core/os/thread_dummy.h index 066ee498ac..35e19732bf 100644 --- a/core/os/thread_dummy.h +++ b/core/os/thread_dummy.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -36,7 +36,6 @@ #include "core/os/thread.h" class ThreadDummy : public Thread { - static Thread *create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings = Settings()); public: @@ -46,7 +45,6 @@ public: }; class RWLockDummy : public RWLock { - static RWLock *create(); public: diff --git a/core/os/thread_safe.h b/core/os/thread_safe.h index 670ee8b125..81de079bf3 100644 --- a/core/os/thread_safe.h +++ b/core/os/thread_safe.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h index 00dc53286e..57f3de20bf 100644 --- a/core/os/threaded_array_processor.h +++ b/core/os/threaded_array_processor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -35,7 +35,7 @@ #include "core/os/os.h" #include "core/os/thread.h" #include "core/os/thread_safe.h" -#include "core/safe_refcount.h" +#include "core/templates/safe_refcount.h" template <class C, class U> struct ThreadArrayProcessData { @@ -54,19 +54,18 @@ struct ThreadArrayProcessData { template <class T> void process_array_thread(void *ud) { - T &data = *(T *)ud; while (true) { uint32_t index = atomic_increment(&data.index); - if (index >= data.elements) + if (index >= data.elements) { break; + } data.process(index); } } template <class C, class M, class U> void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) { - ThreadArrayProcessData<C, U> data; data.method = p_method; data.instance = p_instance; @@ -93,7 +92,6 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us template <class C, class M, class U> void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) { - ThreadArrayProcessData<C, U> data; data.method = p_method; data.instance = p_instance; diff --git a/core/path_remap.cpp b/core/path_remap.cpp deleted file mode 100644 index e1708e0350..0000000000 --- a/core/path_remap.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/*************************************************************************/ -/* path_remap.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 "path_remap.h" diff --git a/core/path_remap.h b/core/path_remap.h deleted file mode 100644 index 1580e88625..0000000000 --- a/core/path_remap.h +++ /dev/null @@ -1,34 +0,0 @@ -/*************************************************************************/ -/* path_remap.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 PATH_REMAP_H -#define PATH_REMAP_H - -#endif // PATH_REMAP_H diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 23f549be1a..9eef7700f9 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,23 +30,24 @@ #include "register_core_types.h" -#include "core/bind/core_bind.h" -#include "core/class_db.h" -#include "core/compressed_translation.h" +#include "core/config/engine.h" +#include "core/config/project_settings.h" +#include "core/core_bind.h" #include "core/core_string_names.h" +#include "core/crypto/aes_context.h" #include "core/crypto/crypto.h" #include "core/crypto/hashing_context.h" -#include "core/engine.h" -#include "core/func_ref.h" #include "core/input/input.h" #include "core/input/input_map.h" #include "core/io/config_file.h" #include "core/io/dtls_server.h" #include "core/io/http_client.h" #include "core/io/image_loader.h" +#include "core/io/json.h" #include "core/io/marshalls.h" #include "core/io/multiplayer_api.h" #include "core/io/networked_multiplayer_peer.h" +#include "core/io/packed_data_container.h" #include "core/io/packet_peer.h" #include "core/io/packet_peer_dtls.h" #include "core/io/packet_peer_udp.h" @@ -60,15 +61,15 @@ #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/object/class_db.h" +#include "core/object/undo_redo.h" #include "core/os/main_loop.h" -#include "core/packed_data_container.h" -#include "core/path_remap.h" -#include "core/project_settings.h" -#include "core/translation.h" -#include "core/undo_redo.h" +#include "core/string/compressed_translation.h" +#include "core/string/translation.h" static Ref<ResourceFormatSaverBinary> resource_saver_binary; static Ref<ResourceFormatLoaderBinary> resource_loader_binary; @@ -85,20 +86,19 @@ static _Engine *_engine = nullptr; static _ClassDB *_classdb = nullptr; static _Marshalls *_marshalls = nullptr; static _JSON *_json = nullptr; +static _EngineDebugger *_engine_debugger = nullptr; static IP *ip = nullptr; -static _Geometry *_geometry = nullptr; +static _Geometry2D *_geometry_2d = nullptr; +static _Geometry3D *_geometry_3d = nullptr; extern Mutex _global_mutex; extern void register_global_constants(); extern void unregister_global_constants(); -extern void register_variant_methods(); -extern void unregister_variant_methods(); void register_core_types() { - //consistency check static_assert(sizeof(Callable) <= 16); @@ -109,7 +109,8 @@ void register_core_types() { ResourceLoader::initialize(); register_global_constants(); - register_variant_methods(); + + Variant::register_types(); CoreStringNames::create(); @@ -153,7 +154,6 @@ void register_core_types() { ClassDB::register_class<InputEventPanGesture>(); ClassDB::register_class<InputEventMIDI>(); - ClassDB::register_class<FuncRef>(); ClassDB::register_virtual_class<StreamPeer>(); ClassDB::register_class<StreamPeerBuffer>(); ClassDB::register_class<StreamPeerTCP>(); @@ -165,8 +165,10 @@ void register_core_types() { // Crypto ClassDB::register_class<HashingContext>(); + ClassDB::register_class<AESContext>(); ClassDB::register_custom_instance_class<X509Certificate>(); ClassDB::register_custom_instance_class<CryptoKey>(); + ClassDB::register_custom_instance_class<HMACContext>(); ClassDB::register_custom_instance_class<Crypto>(); ClassDB::register_custom_instance_class<StreamPeerSSL>(); @@ -197,6 +199,7 @@ void register_core_types() { ClassDB::register_class<_Semaphore>(); ClassDB::register_class<XMLParser>(); + ClassDB::register_class<JSONParser>(); ClassDB::register_class<ConfigFile>(); @@ -215,7 +218,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); @@ -224,24 +228,25 @@ void register_core_types() { _classdb = memnew(_ClassDB); _marshalls = memnew(_Marshalls); _json = memnew(_JSON); + _engine_debugger = memnew(_EngineDebugger); } void register_core_settings() { - //since in register core types, globals may not e present + // Since in register core types, globals may not be present. GLOBAL_DEF("network/limits/tcp/connect_timeout_seconds", (30)); ProjectSettings::get_singleton()->set_custom_property_info("network/limits/tcp/connect_timeout_seconds", PropertyInfo(Variant::INT, "network/limits/tcp/connect_timeout_seconds", PROPERTY_HINT_RANGE, "1,1800,1")); GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); ProjectSettings::get_singleton()->set_custom_property_info("network/limits/packet_peer_stream/max_buffer_po2", PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater")); - GLOBAL_DEF("network/ssl/certificates", ""); - ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); + GLOBAL_DEF("network/ssl/certificate_bundle_override", ""); + ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificate_bundle_override", PropertyInfo(Variant::STRING, "network/ssl/certificate_bundle_override", PROPERTY_HINT_FILE, "*.crt")); } 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,10 +258,12 @@ void register_core_singletons() { ClassDB::register_class<InputMap>(); ClassDB::register_class<_JSON>(); ClassDB::register_class<Expression>(); + ClassDB::register_class<_EngineDebugger>(); 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())); @@ -267,10 +274,10 @@ void register_core_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("Input", Input::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("JSON", _JSON::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", _EngineDebugger::get_singleton())); } void unregister_core_types() { - memdelete(_resource_loader); memdelete(_resource_saver); memdelete(_os); @@ -278,8 +285,10 @@ void unregister_core_types() { memdelete(_classdb); memdelete(_marshalls); memdelete(_json); + memdelete(_engine_debugger); - memdelete(_geometry); + memdelete(_geometry_2d); + memdelete(_geometry_3d); ResourceLoader::remove_resource_format_loader(resource_format_image); resource_format_image.unref(); @@ -301,15 +310,17 @@ void unregister_core_types() { ResourceLoader::remove_resource_format_loader(resource_format_loader_crypto); resource_format_loader_crypto.unref(); - if (ip) + if (ip) { memdelete(ip); + } ResourceLoader::finalize(); ClassDB::cleanup_defaults(); ObjectDB::cleanup(); - unregister_variant_methods(); + Variant::unregister_types(); + unregister_global_constants(); ClassDB::cleanup(); diff --git a/core/register_core_types.h b/core/register_core_types.h index bdd335f33c..baf7ddbe65 100644 --- a/core/register_core_types.h +++ b/core/register_core_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/string/SCsub b/core/string/SCsub new file mode 100644 index 0000000000..3217166f18 --- /dev/null +++ b/core/string/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +env_string = env.Clone() + +env_string.add_source_files(env.core_sources, "*.cpp") diff --git a/core/compressed_translation.cpp b/core/string/compressed_translation.cpp index 0225524bc8..ad90924293 100644 --- a/core/compressed_translation.cpp +++ b/core/string/compressed_translation.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,20 +30,21 @@ #include "compressed_translation.h" -#include "core/pair.h" +#include "core/templates/pair.h" extern "C" { #include "thirdparty/misc/smaz.h" } struct _PHashTranslationCmp { - int orig_len; CharString compressed; int offset; }; void PHashTranslation::generate(const Ref<Translation> &p_from) { + // This method compresses a Translation instance. + // Right now it doesn't handle context or plurals, so Translation subclasses using plurals or context (i.e TranslationPO) shouldn't be compressed. #ifdef TOOLS_ENABLED List<StringName> keys; p_from->get_message_list(&keys); @@ -65,7 +66,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { int total_string_size = 0; for (List<StringName>::Element *E = keys.front(); E; E = E->next()) { - //hash string CharString cs = E->get().operator String().utf8(); uint32_t h = hash(0, cs.get_data()); @@ -108,21 +108,19 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { int bucket_table_size = 0; for (int i = 0; i < size; i++) { - const Vector<Pair<int, CharString>> &b = buckets[i]; Map<uint32_t, int> &t = table.write[i]; - if (b.size() == 0) + if (b.size() == 0) { continue; + } int d = 1; int item = 0; while (item < b.size()) { - uint32_t slot = hash(d, b[item].second.get_data()); if (t.has(slot)) { - item = 0; d++; t.clear(); @@ -151,7 +149,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { int collisions = 0; for (int i = 0; i < size; i++) { - const Map<uint32_t, int> &t = table[i]; if (t.size() == 0) { htw[i] = 0xFFFFFFFF; //nothing @@ -165,7 +162,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { btw[btindex++] = hfunc_table[i]; for (Map<uint32_t, int>::Element *E = t.front(); E; E = E->next()) { - btw[btindex++] = E->key(); btw[btindex++] = compressed[E->get()].offset; btw[btindex++] = compressed[E->get()].compressed.size(); @@ -187,7 +183,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { } bool PHashTranslation::_set(const StringName &p_name, const Variant &p_value) { - String name = p_name.operator String(); if (name == "hash_table") { hash_table = p_value; @@ -197,33 +192,36 @@ bool PHashTranslation::_set(const StringName &p_name, const Variant &p_value) { strings = p_value; } else if (name == "load_from") { generate(p_value); - } else + } else { return false; + } return true; } bool PHashTranslation::_get(const StringName &p_name, Variant &r_ret) const { - String name = p_name.operator String(); - if (name == "hash_table") + if (name == "hash_table") { r_ret = hash_table; - else if (name == "bucket_table") + } else if (name == "bucket_table") { r_ret = bucket_table; - else if (name == "strings") + } else if (name == "strings") { r_ret = strings; - else + } else { return false; + } return true; } -StringName PHashTranslation::get_message(const StringName &p_src_text) const { +StringName PHashTranslation::get_message(const StringName &p_src_text, const StringName &p_context) const { + // p_context passed in is ignore. The use of context is not yet supported in PHashTranslation. int htsize = hash_table.size(); - if (htsize == 0) + if (htsize == 0) { return StringName(); + } CharString str = p_src_text.operator String().utf8(); uint32_t h = hash(0, str.get_data()); @@ -248,9 +246,7 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { int idx = -1; for (int i = 0; i < bucket.size; i++) { - if (bucket.elem[i].key == h) { - idx = i; break; } @@ -261,13 +257,11 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { } if (bucket.elem[idx].comp_size == bucket.elem[idx].uncomp_size) { - String rstr; rstr.parse_utf8(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].uncomp_size); return rstr; } else { - CharString uncomp; uncomp.resize(bucket.elem[idx].uncomp_size + 1); smaz_decompress(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].comp_size, uncomp.ptrw(), bucket.elem[idx].uncomp_size); @@ -277,17 +271,18 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { } } -void PHashTranslation::_get_property_list(List<PropertyInfo> *p_list) const { +StringName PHashTranslation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const { + // The use of plurals translation is not yet supported in PHashTranslation. + return get_message(p_src_text, p_context); +} +void PHashTranslation::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "hash_table")); p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "bucket_table")); p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "strings")); p_list->push_back(PropertyInfo(Variant::OBJECT, "load_from", PROPERTY_HINT_RESOURCE_TYPE, "Translation", PROPERTY_USAGE_EDITOR)); } -void PHashTranslation::_bind_methods() { +void PHashTranslation::_bind_methods() { ClassDB::bind_method(D_METHOD("generate", "from"), &PHashTranslation::generate); } - -PHashTranslation::PHashTranslation() { -} diff --git a/core/compressed_translation.h b/core/string/compressed_translation.h index d599240dfe..0abb770178 100644 --- a/core/compressed_translation.h +++ b/core/string/compressed_translation.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,10 +31,9 @@ #ifndef COMPRESSED_TRANSLATION_H #define COMPRESSED_TRANSLATION_H -#include "core/translation.h" +#include "core/string/translation.h" class PHashTranslation : public Translation { - GDCLASS(PHashTranslation, Translation); //this translation uses a sort of modified perfect hash algorithm @@ -48,12 +47,10 @@ class PHashTranslation : public Translation { Vector<uint8_t> strings; struct Bucket { - int size; uint32_t func; struct Elem { - uint32_t key; uint32_t str_offset; uint32_t comp_size; @@ -64,11 +61,10 @@ class PHashTranslation : public Translation { }; _FORCE_INLINE_ uint32_t hash(uint32_t d, const char *p_str) const { - - if (d == 0) + if (d == 0) { d = 0x1000193; + } while (*p_str) { - d = (d * 0x1000193) ^ uint32_t(*p_str); p_str++; } @@ -83,10 +79,11 @@ protected: static void _bind_methods(); public: - virtual StringName get_message(const StringName &p_src_text) const; //overridable for other implementations + virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override; //overridable for other implementations + virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override; void generate(const Ref<Translation> &p_from); - PHashTranslation(); + PHashTranslation() {} }; #endif // COMPRESSED_TRANSLATION_H diff --git a/core/node_path.cpp b/core/string/node_path.cpp index 83233622a0..d3afa7b4dd 100644 --- a/core/node_path.cpp +++ b/core/string/node_path.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,9 @@ #include "node_path.h" -#include "core/print_string.h" +#include "core/string/print_string.h" void NodePath::_update_hash_cache() const { - uint32_t h = data->absolute ? 1 : 0; int pc = data->path.size(); const StringName *sn = data->path.ptr(); @@ -51,7 +50,6 @@ void NodePath::_update_hash_cache() const { } void NodePath::prepend_period() { - if (data->path.size() && data->path[0].operator String() != ".") { data->path.insert(0, "."); data->hash_cache_valid = false; @@ -59,59 +57,60 @@ void NodePath::prepend_period() { } bool NodePath::is_absolute() const { - - if (!data) + if (!data) { return false; + } return data->absolute; } -int NodePath::get_name_count() const { - if (!data) +int NodePath::get_name_count() const { + if (!data) { return 0; + } return data->path.size(); } -StringName NodePath::get_name(int p_idx) const { +StringName NodePath::get_name(int p_idx) const { ERR_FAIL_COND_V(!data, StringName()); ERR_FAIL_INDEX_V(p_idx, data->path.size(), StringName()); return data->path[p_idx]; } int NodePath::get_subname_count() const { - - if (!data) + if (!data) { return 0; + } return data->subpath.size(); } -StringName NodePath::get_subname(int p_idx) const { +StringName NodePath::get_subname(int p_idx) const { ERR_FAIL_COND_V(!data, StringName()); ERR_FAIL_INDEX_V(p_idx, data->subpath.size(), StringName()); return data->subpath[p_idx]; } void NodePath::unref() { - if (data && data->refcount.unref()) { - memdelete(data); } data = nullptr; } bool NodePath::operator==(const NodePath &p_path) const { - - if (data == p_path.data) + if (data == p_path.data) { return true; + } - if (!data || !p_path.data) + if (!data || !p_path.data) { return false; + } - if (data->absolute != p_path.data->absolute) + if (data->absolute != p_path.data->absolute) { return false; + } int path_size = data->path.size(); @@ -129,85 +128,74 @@ bool NodePath::operator==(const NodePath &p_path) const { const StringName *r_path_ptr = p_path.data->path.ptr(); for (int i = 0; i < path_size; i++) { - - if (l_path_ptr[i] != r_path_ptr[i]) + if (l_path_ptr[i] != r_path_ptr[i]) { return false; + } } const StringName *l_subpath_ptr = data->subpath.ptr(); const StringName *r_subpath_ptr = p_path.data->subpath.ptr(); for (int i = 0; i < subpath_size; i++) { - - if (l_subpath_ptr[i] != r_subpath_ptr[i]) + if (l_subpath_ptr[i] != r_subpath_ptr[i]) { return false; + } } return true; } -bool NodePath::operator!=(const NodePath &p_path) const { +bool NodePath::operator!=(const NodePath &p_path) const { return (!(*this == p_path)); } void NodePath::operator=(const NodePath &p_path) { - - if (this == &p_path) + if (this == &p_path) { return; + } unref(); if (p_path.data && p_path.data->refcount.ref()) { - data = p_path.data; } } NodePath::operator String() const { - - if (!data) + if (!data) { return String(); + } String ret; - if (data->absolute) + if (data->absolute) { ret = "/"; + } for (int i = 0; i < data->path.size(); i++) { - - if (i > 0) + if (i > 0) { ret += "/"; + } ret += data->path[i].operator String(); } for (int i = 0; i < data->subpath.size(); i++) { - ret += ":" + data->subpath[i].operator String(); } return ret; } -NodePath::NodePath(const NodePath &p_path) { - - data = nullptr; - - if (p_path.data && p_path.data->refcount.ref()) { - - data = p_path.data; - } -} - Vector<StringName> NodePath::get_names() const { - - if (data) + if (data) { return data->path; + } return Vector<StringName>(); } Vector<StringName> NodePath::get_subnames() const { - - if (data) + if (data) { return data->subpath; + } return Vector<StringName>(); } @@ -227,7 +215,6 @@ StringName NodePath::get_concatenated_subnames() const { } NodePath NodePath::rel_path_to(const NodePath &p_np) const { - ERR_FAIL_COND_V(!is_absolute(), NodePath()); ERR_FAIL_COND_V(!p_np.is_absolute(), NodePath()); @@ -238,12 +225,15 @@ NodePath NodePath::rel_path_to(const NodePath &p_np) const { int common_parent = 0; while (true) { - if (src_dirs.size() == common_parent) + if (src_dirs.size() == common_parent) { break; - if (dst_dirs.size() == common_parent) + } + if (dst_dirs.size() == common_parent) { break; - if (src_dirs[common_parent] != dst_dirs[common_parent]) + } + if (src_dirs[common_parent] != dst_dirs[common_parent]) { break; + } common_parent++; } @@ -252,23 +242,21 @@ NodePath NodePath::rel_path_to(const NodePath &p_np) const { Vector<StringName> relpath; for (int i = src_dirs.size() - 1; i > common_parent; i--) { - relpath.push_back(".."); } for (int i = common_parent + 1; i < dst_dirs.size(); i++) { - relpath.push_back(dst_dirs[i]); } - if (relpath.size() == 0) + if (relpath.size() == 0) { relpath.push_back("."); + } return NodePath(relpath, p_np.get_subnames(), false); } NodePath NodePath::get_as_property_path() const { - if (!data || !data->path.size()) { return *this; } else { @@ -285,48 +273,22 @@ NodePath NodePath::get_as_property_path() const { } } -NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) { - - data = nullptr; - - if (p_path.size() == 0) - return; - - data = memnew(Data); - data->refcount.init(); - data->absolute = p_absolute; - data->path = p_path; - data->has_slashes = true; - data->hash_cache_valid = false; -} - -NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) { - - data = nullptr; - - if (p_path.size() == 0 && p_subpath.size() == 0) - return; - - data = memnew(Data); - data->refcount.init(); - data->absolute = p_absolute; - data->path = p_path; - data->subpath = p_subpath; - data->has_slashes = true; - data->hash_cache_valid = false; +bool NodePath::is_empty() const { + return !data; } void NodePath::simplify() { - - if (!data) + if (!data) { return; + } for (int i = 0; i < data->path.size(); i++) { - if (data->path.size() == 1) + if (data->path.size() == 1) { break; + } if (data->path[i].operator String() == ".") { data->path.remove(i); i--; - } else if (data->path[i].operator String() == ".." && i > 0 && data->path[i - 1].operator String() != "." && data->path[i - 1].operator String() != "..") { + } else if (i > 0 && data->path[i].operator String() == ".." && data->path[i - 1].operator String() != "." && data->path[i - 1].operator String() != "..") { //remove both data->path.remove(i - 1); data->path.remove(i - 1); @@ -341,18 +303,48 @@ void NodePath::simplify() { } NodePath NodePath::simplified() const { - NodePath np = *this; np.simplify(); return np; } -NodePath::NodePath(const String &p_path) { +NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) { + if (p_path.size() == 0) { + return; + } - data = nullptr; + data = memnew(Data); + data->refcount.init(); + data->absolute = p_absolute; + data->path = p_path; + data->has_slashes = true; + data->hash_cache_valid = false; +} - if (p_path.length() == 0) +NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) { + if (p_path.size() == 0 && p_subpath.size() == 0) { return; + } + + data = memnew(Data); + data->refcount.init(); + data->absolute = p_absolute; + data->path = p_path; + data->subpath = p_subpath; + data->has_slashes = true; + data->hash_cache_valid = false; +} + +NodePath::NodePath(const NodePath &p_path) { + if (p_path.data && p_path.data->refcount.ref()) { + data = p_path.data; + } +} + +NodePath::NodePath(const String &p_path) { + if (p_path.length() == 0) { + return; + } String path = p_path; Vector<StringName> subpath; @@ -364,16 +356,15 @@ NodePath::NodePath(const String &p_path) { int subpath_pos = path.find(":"); if (subpath_pos != -1) { - int from = subpath_pos + 1; for (int i = from; i <= path.length(); i++) { - if (path[i] == ':' || path[i] == 0) { - String str = path.substr(from, i - from); if (str == "") { - if (path[i] == 0) continue; // Allow end-of-path : + if (path[i] == 0) { + continue; // Allow end-of-path : + } ERR_FAIL_MSG("Invalid NodePath '" + p_path + "'."); } @@ -387,22 +378,21 @@ NodePath::NodePath(const String &p_path) { } for (int i = (int)absolute; i < path.length(); i++) { - if (path[i] == '/') { - last_is_slash = true; has_slashes = true; } else { - - if (last_is_slash) + if (last_is_slash) { slices++; + } last_is_slash = false; } } - if (slices == 0 && !absolute && !subpath.size()) + if (slices == 0 && !absolute && !subpath.size()) { return; + } data = memnew(Data); data->refcount.init(); @@ -411,19 +401,17 @@ NodePath::NodePath(const String &p_path) { data->subpath = subpath; data->hash_cache_valid = false; - if (slices == 0) + if (slices == 0) { return; + } data->path.resize(slices); last_is_slash = true; int from = (int)absolute; int slice = 0; for (int i = (int)absolute; i < path.length() + 1; i++) { - if (path[i] == '/' || path[i] == 0) { - if (!last_is_slash) { - String name = path.substr(from, i - from); ERR_FAIL_INDEX(slice, data->path.size()); data->path.write[slice++] = name; @@ -436,16 +424,6 @@ NodePath::NodePath(const String &p_path) { } } -bool NodePath::is_empty() const { - - return !data; -} -NodePath::NodePath() { - - data = nullptr; -} - NodePath::~NodePath() { - unref(); } diff --git a/core/node_path.h b/core/string/node_path.h index 76de36cd9f..26e15636d9 100644 --- a/core/node_path.h +++ b/core/string/node_path.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,13 +31,11 @@ #ifndef NODE_PATH_H #define NODE_PATH_H -#include "core/string_name.h" -#include "core/ustring.h" +#include "core/string/string_name.h" +#include "core/string/ustring.h" class NodePath { - struct Data { - SafeRefCount refcount; Vector<StringName> path; Vector<StringName> subpath; @@ -48,7 +46,7 @@ class NodePath { mutable uint32_t hash_cache; }; - mutable Data *data; + mutable Data *data = nullptr; void unref(); void _update_hash_cache() const; @@ -71,8 +69,9 @@ public: NodePath get_parent() const; _FORCE_INLINE_ uint32_t hash() const { - if (!data) + if (!data) { return 0; + } if (!data->hash_cache_valid) { _update_hash_cache(); } @@ -93,7 +92,7 @@ public: NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute); NodePath(const NodePath &p_path); NodePath(const String &p_path); - NodePath(); + NodePath() {} ~NodePath(); }; diff --git a/core/print_string.cpp b/core/string/print_string.cpp index 8eb1d9e86a..345371d733 100644 --- a/core/print_string.cpp +++ b/core/string/print_string.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -39,7 +39,6 @@ bool _print_line_enabled = true; bool _print_error_enabled = true; void add_print_handler(PrintHandlerList *p_handler) { - _global_lock(); p_handler->next = print_handler_list; print_handler_list = p_handler; @@ -47,20 +46,18 @@ void add_print_handler(PrintHandlerList *p_handler) { } void remove_print_handler(PrintHandlerList *p_handler) { - _global_lock(); PrintHandlerList *prev = nullptr; PrintHandlerList *l = print_handler_list; while (l) { - if (l == p_handler) { - - if (prev) + if (prev) { prev->next = l->next; - else + } else { print_handler_list = l->next; + } break; } prev = l; @@ -73,16 +70,15 @@ void remove_print_handler(PrintHandlerList *p_handler) { } void print_line(String p_string) { - - if (!_print_line_enabled) + if (!_print_line_enabled) { return; + } OS::get_singleton()->print("%s\n", p_string.utf8().get_data()); _global_lock(); PrintHandlerList *l = print_handler_list; while (l) { - l->printfunc(l->userdata, p_string, false); l = l->next; } @@ -91,16 +87,15 @@ void print_line(String p_string) { } void print_error(String p_string) { - - if (!_print_error_enabled) + if (!_print_error_enabled) { return; + } OS::get_singleton()->printerr("%s\n", p_string.utf8().get_data()); _global_lock(); PrintHandlerList *l = print_handler_list; while (l) { - l->printfunc(l->userdata, p_string, true); l = l->next; } @@ -109,7 +104,6 @@ void print_error(String p_string) { } void print_verbose(String p_string) { - if (OS::get_singleton()->is_stdout_verbose()) { print_line(p_string); } diff --git a/core/print_string.h b/core/string/print_string.h index d83cc35dd6..1a9ff1efd6 100644 --- a/core/print_string.h +++ b/core/string/print_string.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,24 +31,19 @@ #ifndef PRINT_STRING_H #define PRINT_STRING_H -#include "core/ustring.h" +#include "core/string/ustring.h" extern void (*_print_func)(String); typedef void (*PrintHandlerFunc)(void *, const String &p_string, bool p_error); struct PrintHandlerList { + PrintHandlerFunc printfunc = nullptr; + void *userdata = nullptr; - PrintHandlerFunc printfunc; - void *userdata; + PrintHandlerList *next = nullptr; - PrintHandlerList *next; - - PrintHandlerList() { - printfunc = 0; - next = 0; - userdata = 0; - } + PrintHandlerList() {} }; void add_print_handler(PrintHandlerList *p_handler); diff --git a/core/string_buffer.h b/core/string/string_buffer.h index a140f0abf7..33897c3674 100644 --- a/core/string_buffer.h +++ b/core/string/string_buffer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,26 +31,25 @@ #ifndef STRING_BUFFER_H #define STRING_BUFFER_H -#include "core/ustring.h" +#include "core/string/ustring.h" template <int SHORT_BUFFER_SIZE = 64> class StringBuffer { - - CharType short_buffer[SHORT_BUFFER_SIZE]; + char32_t short_buffer[SHORT_BUFFER_SIZE]; String buffer; - int string_length; + int string_length = 0; - _FORCE_INLINE_ CharType *current_buffer_ptr() { - return static_cast<String &>(buffer).empty() ? short_buffer : buffer.ptrw(); + _FORCE_INLINE_ char32_t *current_buffer_ptr() { + return static_cast<String &>(buffer).is_empty() ? short_buffer : buffer.ptrw(); } public: - StringBuffer &append(CharType p_char); + StringBuffer &append(char32_t p_char); StringBuffer &append(const String &p_string); StringBuffer &append(const char *p_str); - StringBuffer &append(const CharType *p_str, int p_clip_to_len = -1); + StringBuffer &append(const char32_t *p_str, int p_clip_to_len = -1); - _FORCE_INLINE_ void operator+=(CharType p_char) { + _FORCE_INLINE_ void operator+=(char32_t p_char) { append(p_char); } @@ -62,7 +61,7 @@ public: append(p_str); } - _FORCE_INLINE_ void operator+=(const CharType *p_str) { + _FORCE_INLINE_ void operator+=(const char32_t *p_str) { append(p_str); } @@ -78,14 +77,10 @@ public: _FORCE_INLINE_ operator String() { return as_string(); } - - StringBuffer() { - string_length = 0; - } }; template <int SHORT_BUFFER_SIZE> -StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(CharType p_char) { +StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(char32_t p_char) { reserve(string_length + 2); current_buffer_ptr()[string_length++] = p_char; return *this; @@ -93,7 +88,7 @@ StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(CharTyp template <int SHORT_BUFFER_SIZE> StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const String &p_string) { - return append(p_string.c_str()); + return append(p_string.get_data()); } template <int SHORT_BUFFER_SIZE> @@ -101,7 +96,7 @@ StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const c int len = strlen(p_str); reserve(string_length + len + 1); - CharType *buf = current_buffer_ptr(); + char32_t *buf = current_buffer_ptr(); for (const char *c_ptr = p_str; *c_ptr; ++c_ptr) { buf[string_length++] = *c_ptr; } @@ -109,13 +104,13 @@ StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const c } template <int SHORT_BUFFER_SIZE> -StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const CharType *p_str, int p_clip_to_len) { +StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const char32_t *p_str, int p_clip_to_len) { int len = 0; while ((p_clip_to_len < 0 || len < p_clip_to_len) && p_str[len]) { ++len; } reserve(string_length + len + 1); - memcpy(&(current_buffer_ptr()[string_length]), p_str, len * sizeof(CharType)); + memcpy(&(current_buffer_ptr()[string_length]), p_str, len * sizeof(char32_t)); string_length += len; return *this; @@ -123,13 +118,14 @@ StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const C template <int SHORT_BUFFER_SIZE> StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::reserve(int p_size) { - if (p_size < SHORT_BUFFER_SIZE || p_size < buffer.size()) + if (p_size < SHORT_BUFFER_SIZE || p_size < buffer.size()) { return *this; + } - bool need_copy = string_length > 0 && buffer.empty(); + bool need_copy = string_length > 0 && buffer.is_empty(); buffer.resize(next_power_of_2(p_size)); if (need_copy) { - memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(CharType)); + memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(char32_t)); } return *this; @@ -143,7 +139,7 @@ int StringBuffer<SHORT_BUFFER_SIZE>::length() const { template <int SHORT_BUFFER_SIZE> String StringBuffer<SHORT_BUFFER_SIZE>::as_string() { current_buffer_ptr()[string_length] = '\0'; - if (buffer.empty()) { + if (buffer.is_empty()) { return String(short_buffer); } else { buffer.resize(string_length + 1); @@ -154,7 +150,7 @@ String StringBuffer<SHORT_BUFFER_SIZE>::as_string() { template <int SHORT_BUFFER_SIZE> double StringBuffer<SHORT_BUFFER_SIZE>::as_double() { current_buffer_ptr()[string_length] = '\0'; - return String::to_double(current_buffer_ptr()); + return String::to_float(current_buffer_ptr()); } template <int SHORT_BUFFER_SIZE> diff --git a/core/string_builder.cpp b/core/string/string_builder.cpp index 46c7e1c53f..834c87c845 100644 --- a/core/string_builder.cpp +++ b/core/string/string_builder.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,9 +33,9 @@ #include <string.h> StringBuilder &StringBuilder::append(const String &p_string) { - - if (p_string == String()) + if (p_string == String()) { return *this; + } strings.push_back(p_string); appended_strings.push_back(-1); @@ -46,7 +46,6 @@ StringBuilder &StringBuilder::append(const String &p_string) { } StringBuilder &StringBuilder::append(const char *p_cstring) { - int32_t len = strlen(p_cstring); c_strings.push_back(p_cstring); @@ -58,11 +57,11 @@ StringBuilder &StringBuilder::append(const char *p_cstring) { } String StringBuilder::as_string() const { - - if (string_length == 0) + if (string_length == 0) { return ""; + } - CharType *buffer = memnew_arr(CharType, string_length); + char32_t *buffer = memnew_arr(char32_t, string_length); int current_position = 0; @@ -74,13 +73,12 @@ String StringBuilder::as_string() const { // Godot string const String &s = strings[godot_string_elem]; - memcpy(buffer + current_position, s.ptr(), s.length() * sizeof(CharType)); + memcpy(buffer + current_position, s.ptr(), s.length() * sizeof(char32_t)); current_position += s.length(); godot_string_elem++; } else { - const char *s = c_strings[c_string_elem]; for (int32_t j = 0; j < appended_strings[i]; j++) { diff --git a/core/string_builder.h b/core/string/string_builder.h index dd8a154890..30ce2a06f7 100644 --- a/core/string_builder.h +++ b/core/string/string_builder.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,13 +31,11 @@ #ifndef STRING_BUILDER_H #define STRING_BUILDER_H -#include "core/ustring.h" - -#include "core/vector.h" +#include "core/string/ustring.h" +#include "core/templates/vector.h" class StringBuilder { - - uint32_t string_length; + uint32_t string_length = 0; Vector<String> strings; Vector<const char *> c_strings; @@ -80,9 +78,7 @@ public: return as_string(); } - StringBuilder() { - string_length = 0; - } + StringBuilder() {} }; #endif // STRING_BUILDER_H diff --git a/core/string_name.cpp b/core/string/string_name.cpp index 9cbac97a7c..14b87072bb 100644 --- a/core/string_name.cpp +++ b/core/string/string_name.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #include "string_name.h" #include "core/os/os.h" -#include "core/print_string.h" +#include "core/string/print_string.h" StaticCString StaticCString::create(const char *p_ptr) { StaticCString scs; @@ -42,7 +42,6 @@ StaticCString StaticCString::create(const char *p_ptr) { StringName::_Data *StringName::_table[STRING_TABLE_LEN]; StringName _scs_create(const char *p_chr) { - return (p_chr[0] ? StringName(StaticCString::create(p_chr)) : StringName()); } @@ -50,24 +49,19 @@ bool StringName::configured = false; Mutex StringName::mutex; void StringName::setup() { - ERR_FAIL_COND(configured); for (int i = 0; i < STRING_TABLE_LEN; i++) { - _table[i] = nullptr; } configured = true; } void StringName::cleanup() { - MutexLock lock(mutex); int lost_strings = 0; for (int i = 0; i < STRING_TABLE_LEN; i++) { - while (_table[i]) { - _Data *d = _table[i]; lost_strings++; if (OS::get_singleton()->is_stdout_verbose()) { @@ -88,11 +82,9 @@ void StringName::cleanup() { } void StringName::unref() { - ERR_FAIL_COND(!configured); if (_data && _data->refcount.unref()) { - MutexLock lock(mutex); if (_data->prev) { @@ -114,9 +106,7 @@ void StringName::unref() { } bool StringName::operator==(const String &p_name) const { - if (!_data) { - return (p_name.length() == 0); } @@ -124,9 +114,7 @@ bool StringName::operator==(const String &p_name) const { } bool StringName::operator==(const char *p_name) const { - if (!_data) { - return (p_name[0] == 0); } @@ -134,32 +122,28 @@ bool StringName::operator==(const char *p_name) const { } bool StringName::operator!=(const String &p_name) const { - return !(operator==(p_name)); } bool StringName::operator!=(const StringName &p_name) const { - // the real magic of all this mess happens here. // this is why path comparisons are very fast return _data != p_name._data; } void StringName::operator=(const StringName &p_name) { - - if (this == &p_name) + if (this == &p_name) { return; + } unref(); if (p_name._data && p_name._data->refcount.ref()) { - _data = p_name._data; } } StringName::StringName(const StringName &p_name) { - _data = nullptr; ERR_FAIL_COND(!configured); @@ -170,13 +154,13 @@ StringName::StringName(const StringName &p_name) { } StringName::StringName(const char *p_name) { - _data = nullptr; ERR_FAIL_COND(!configured); - if (!p_name || p_name[0] == 0) + if (!p_name || p_name[0] == 0) { return; //empty, ignore + } MutexLock lock(mutex); @@ -187,10 +171,10 @@ StringName::StringName(const char *p_name) { _data = _table[idx]; while (_data) { - // compare hash first - if (_data->hash == hash && _data->get_name() == p_name) + if (_data->hash == hash && _data->get_name() == p_name) { break; + } _data = _data->next; } @@ -209,13 +193,13 @@ StringName::StringName(const char *p_name) { _data->cname = nullptr; _data->next = _table[idx]; _data->prev = nullptr; - if (_table[idx]) + if (_table[idx]) { _table[idx]->prev = _data; + } _table[idx] = _data; } StringName::StringName(const StaticCString &p_static_string) { - _data = nullptr; ERR_FAIL_COND(!configured); @@ -231,10 +215,10 @@ StringName::StringName(const StaticCString &p_static_string) { _data = _table[idx]; while (_data) { - // compare hash first - if (_data->hash == hash && _data->get_name() == p_static_string.ptr) + if (_data->hash == hash && _data->get_name() == p_static_string.ptr) { break; + } _data = _data->next; } @@ -253,19 +237,20 @@ StringName::StringName(const StaticCString &p_static_string) { _data->cname = p_static_string.ptr; _data->next = _table[idx]; _data->prev = nullptr; - if (_table[idx]) + if (_table[idx]) { _table[idx]->prev = _data; + } _table[idx] = _data; } StringName::StringName(const String &p_name) { - _data = nullptr; ERR_FAIL_COND(!configured); - if (p_name == String()) + if (p_name == String()) { return; + } MutexLock lock(mutex); @@ -275,9 +260,9 @@ StringName::StringName(const String &p_name) { _data = _table[idx]; while (_data) { - - if (_data->hash == hash && _data->get_name() == p_name) + if (_data->hash == hash && _data->get_name() == p_name) { break; + } _data = _data->next; } @@ -296,18 +281,19 @@ StringName::StringName(const String &p_name) { _data->cname = nullptr; _data->next = _table[idx]; _data->prev = nullptr; - if (_table[idx]) + if (_table[idx]) { _table[idx]->prev = _data; + } _table[idx] = _data; } StringName StringName::search(const char *p_name) { - ERR_FAIL_COND_V(!configured, StringName()); ERR_FAIL_COND_V(!p_name, StringName()); - if (!p_name[0]) + if (!p_name[0]) { return StringName(); + } MutexLock lock(mutex); @@ -317,10 +303,10 @@ StringName StringName::search(const char *p_name) { _Data *_data = _table[idx]; while (_data) { - // compare hash first - if (_data->hash == hash && _data->get_name() == p_name) + if (_data->hash == hash && _data->get_name() == p_name) { break; + } _data = _data->next; } @@ -331,13 +317,13 @@ StringName StringName::search(const char *p_name) { return StringName(); //does not exist } -StringName StringName::search(const CharType *p_name) { - +StringName StringName::search(const char32_t *p_name) { ERR_FAIL_COND_V(!configured, StringName()); ERR_FAIL_COND_V(!p_name, StringName()); - if (!p_name[0]) + if (!p_name[0]) { return StringName(); + } MutexLock lock(mutex); @@ -348,10 +334,10 @@ StringName StringName::search(const CharType *p_name) { _Data *_data = _table[idx]; while (_data) { - // compare hash first - if (_data->hash == hash && _data->get_name() == p_name) + if (_data->hash == hash && _data->get_name() == p_name) { break; + } _data = _data->next; } @@ -361,8 +347,8 @@ StringName StringName::search(const CharType *p_name) { return StringName(); //does not exist } -StringName StringName::search(const String &p_name) { +StringName StringName::search(const String &p_name) { ERR_FAIL_COND_V(p_name == "", StringName()); MutexLock lock(mutex); @@ -374,10 +360,10 @@ StringName StringName::search(const String &p_name) { _Data *_data = _table[idx]; while (_data) { - // compare hash first - if (_data->hash == hash && p_name == _data->get_name()) + if (_data->hash == hash && p_name == _data->get_name()) { break; + } _data = _data->next; } @@ -388,12 +374,20 @@ StringName StringName::search(const String &p_name) { return StringName(); //does not exist } -StringName::StringName() { - - _data = nullptr; +StringName::~StringName() { + unref(); } -StringName::~StringName() { +bool operator==(const String &p_name, const StringName &p_string_name) { + return p_name == p_string_name.operator String(); +} +bool operator!=(const String &p_name, const StringName &p_string_name) { + return p_name != p_string_name.operator String(); +} - unref(); +bool operator==(const char *p_name, const StringName &p_string_name) { + return p_name == p_string_name.operator String(); +} +bool operator!=(const char *p_name, const StringName &p_string_name) { + return p_name != p_string_name.operator String(); } diff --git a/core/string_name.h b/core/string/string_name.h index aec87b8e66..44d0ea14fa 100644 --- a/core/string_name.h +++ b/core/string/string_name.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,19 +32,18 @@ #define STRING_NAME_H #include "core/os/mutex.h" -#include "core/safe_refcount.h" -#include "core/ustring.h" +#include "core/string/ustring.h" +#include "core/templates/safe_refcount.h" -struct StaticCString { +class Main; +struct StaticCString { const char *ptr; static StaticCString create(const char *p_ptr); }; class StringName { - enum { - STRING_TABLE_BITS = 12, STRING_TABLE_LEN = 1 << STRING_TABLE_BITS, STRING_TABLE_MASK = STRING_TABLE_LEN - 1 @@ -52,28 +51,22 @@ class StringName { struct _Data { SafeRefCount refcount; - const char *cname; + const char *cname = nullptr; String name; String get_name() const { return cname ? String(cname) : name; } - int idx; - uint32_t hash; - _Data *prev; - _Data *next; - _Data() { - cname = nullptr; - next = prev = nullptr; - idx = 0; - hash = 0; - } + int idx = 0; + uint32_t hash = 0; + _Data *prev = nullptr; + _Data *next = nullptr; + _Data() {} }; static _Data *_table[STRING_TABLE_LEN]; - _Data *_data; + _Data *_data = nullptr; union _HashUnion { - _Data *ptr; uint32_t hash; }; @@ -81,7 +74,7 @@ class StringName { void unref(); friend void register_core_types(); friend void unregister_core_types(); - + friend class Main; static Mutex mutex; static void setup(); static void cleanup(); @@ -90,13 +83,12 @@ class StringName { StringName(_Data *p_data) { _data = p_data; } public: - operator const void *() const { return (_data && (_data->cname || !_data->name.empty())) ? (void *)1 : 0; } + operator const void *() const { return (_data && (_data->cname || !_data->name.is_empty())) ? (void *)1 : nullptr; } bool operator==(const String &p_name) const; bool operator==(const char *p_name) const; bool operator!=(const String &p_name) const; _FORCE_INLINE_ bool operator<(const StringName &p_name) const { - return _data < p_name._data; } _FORCE_INLINE_ bool operator==(const StringName &p_name) const { @@ -105,11 +97,11 @@ public: return _data == p_name._data; } _FORCE_INLINE_ uint32_t hash() const { - - if (_data) + if (_data) { return _data->hash; - else + } else { return 0; + } } _FORCE_INLINE_ const void *data_unique_pointer() const { return (void *)_data; @@ -117,40 +109,38 @@ public: bool operator!=(const StringName &p_name) const; _FORCE_INLINE_ operator String() const { - if (_data) { - if (_data->cname) + if (_data->cname) { return String(_data->cname); - else + } else { return _data->name; + } } return String(); } static StringName search(const char *p_name); - static StringName search(const CharType *p_name); + static StringName search(const char32_t *p_name); static StringName search(const String &p_name); struct AlphCompare { - _FORCE_INLINE_ bool operator()(const StringName &l, const StringName &r) const { - const char *l_cname = l._data ? l._data->cname : ""; const char *r_cname = r._data ? r._data->cname : ""; if (l_cname) { - - if (r_cname) + if (r_cname) { return is_str_less(l_cname, r_cname); - else + } else { return is_str_less(l_cname, r._data->name.ptr()); + } } else { - - if (r_cname) + if (r_cname) { return is_str_less(l._data->name.ptr(), r_cname); - else + } else { return is_str_less(l._data->name.ptr(), r._data->name.ptr()); + } } } }; @@ -160,10 +150,15 @@ public: StringName(const StringName &p_name); StringName(const String &p_name); StringName(const StaticCString &p_static_string); - StringName(); + StringName() {} ~StringName(); }; +bool operator==(const String &p_name, const StringName &p_string_name); +bool operator!=(const String &p_name, const StringName &p_string_name); +bool operator==(const char *p_name, const StringName &p_string_name); +bool operator!=(const char *p_name, const StringName &p_string_name); + StringName _scs_create(const char *p_chr); #endif // STRING_NAME_H diff --git a/core/translation.cpp b/core/string/translation.cpp index 3f45bb17c9..5337d46aa3 100644 --- a/core/translation.cpp +++ b/core/string/translation.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,14 @@ #include "translation.h" +#include "core/config/project_settings.h" #include "core/io/resource_loader.h" #include "core/os/os.h" -#include "core/project_settings.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#include "main/main.h" +#endif // ISO 639-1 language codes, with the addition of glibc locales with their // regional identifiers. This list must match the language names (in English) @@ -794,28 +799,19 @@ static const char *locale_renames[][2] = { /////////////////////////////////////////////// -Vector<String> Translation::_get_messages() const { - - Vector<String> msgs; - msgs.resize(translation_map.size() * 2); - int idx = 0; +Dictionary Translation::_get_messages() const { + Dictionary d; for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { - - msgs.set(idx + 0, E->key()); - msgs.set(idx + 1, E->get()); - idx += 2; + d[E->key()] = E->value(); } - - return msgs; + return d; } Vector<String> Translation::_get_message_list() const { - Vector<String> msgs; msgs.resize(translation_map.size()); int idx = 0; for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { - msgs.set(idx, E->key()); idx += 1; } @@ -823,21 +819,15 @@ Vector<String> Translation::_get_message_list() const { return msgs; } -void Translation::_set_messages(const Vector<String> &p_messages) { - - int msg_count = p_messages.size(); - ERR_FAIL_COND(msg_count % 2); - - const String *r = p_messages.ptr(); - - for (int i = 0; i < msg_count; i += 2) { - - add_message(r[i + 0], r[i + 1]); +void Translation::_set_messages(const Dictionary &p_messages) { + List<Variant> keys; + p_messages.get_key_list(&keys); + for (auto E = keys.front(); E; E = E->next()) { + translation_map[E->get()] = p_messages[E->get()]; } } void Translation::set_locale(const String &p_locale) { - String univ_locale = TranslationServer::standardize_locale(p_locale); if (!TranslationServer::is_locale_valid(univ_locale)) { @@ -855,67 +845,78 @@ void Translation::set_locale(const String &p_locale) { } } -void Translation::add_message(const StringName &p_src_text, const StringName &p_xlated_text) { - +void Translation::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) { translation_map[p_src_text] = p_xlated_text; } -StringName Translation::get_message(const StringName &p_src_text) const { + +void Translation::add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context) { + WARN_PRINT("Translation class doesn't handle plural messages. Calling add_plural_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles plurals, such as TranslationPO class"); + ERR_FAIL_COND_MSG(p_plural_xlated_texts.is_empty(), "Parameter vector p_plural_xlated_texts passed in is empty."); + translation_map[p_src_text] = p_plural_xlated_texts[0]; +} + +StringName Translation::get_message(const StringName &p_src_text, const StringName &p_context) const { + if (p_context != StringName()) { + WARN_PRINT("Translation class doesn't handle context. Using context in get_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles context, such as TranslationPO class"); + } const Map<StringName, StringName>::Element *E = translation_map.find(p_src_text); - if (!E) + if (!E) { return StringName(); + } return E->get(); } -void Translation::erase_message(const StringName &p_src_text) { +StringName Translation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const { + WARN_PRINT("Translation class doesn't handle plural messages. Calling get_plural_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles plurals, such as TranslationPO class"); + return get_message(p_src_text); +} + +void Translation::erase_message(const StringName &p_src_text, const StringName &p_context) { + if (p_context != StringName()) { + WARN_PRINT("Translation class doesn't handle context. Using context in erase_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles context, such as TranslationPO class"); + } translation_map.erase(p_src_text); } void Translation::get_message_list(List<StringName> *r_messages) const { - for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { - r_messages->push_back(E->key()); } } int Translation::get_message_count() const { - return translation_map.size(); -}; +} void Translation::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_locale", "locale"), &Translation::set_locale); ClassDB::bind_method(D_METHOD("get_locale"), &Translation::get_locale); - ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message"), &Translation::add_message); - ClassDB::bind_method(D_METHOD("get_message", "src_message"), &Translation::get_message); - ClassDB::bind_method(D_METHOD("erase_message", "src_message"), &Translation::erase_message); + ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL("")); + ClassDB::bind_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL("")); + ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_message_list"), &Translation::_get_message_list); ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count); ClassDB::bind_method(D_METHOD("_set_messages"), &Translation::_set_messages); ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale"); } -Translation::Translation() : - locale("en") { -} - /////////////////////////////////////////////// bool TranslationServer::is_locale_valid(const String &p_locale) { - const char **ptr = locale_list; while (*ptr) { - - if (*ptr == p_locale) + if (*ptr == p_locale) { return true; + } ptr++; } @@ -923,7 +924,6 @@ bool TranslationServer::is_locale_valid(const String &p_locale) { } String TranslationServer::standardize_locale(const String &p_locale) { - // Replaces '-' with '_' for macOS Sierra-style locales String univ_locale = p_locale.replace("-", "_"); @@ -941,7 +941,6 @@ String TranslationServer::standardize_locale(const String &p_locale) { } String TranslationServer::get_language_code(const String &p_locale) { - ERR_FAIL_COND_V_MSG(p_locale.length() < 2, p_locale, "Invalid locale '" + p_locale + "'."); // Most language codes are two letters, but some are three, // so we have to look for a regional code separator ('_' or '-') @@ -958,7 +957,6 @@ String TranslationServer::get_language_code(const String &p_locale) { } void TranslationServer::set_locale(const String &p_locale) { - String univ_locale = standardize_locale(p_locale); if (!is_locale_valid(univ_locale)) { @@ -983,20 +981,19 @@ void TranslationServer::set_locale(const String &p_locale) { } String TranslationServer::get_locale() const { - return locale; } String TranslationServer::get_locale_name(const String &p_locale) const { - - if (!locale_name_map.has(p_locale)) return String(); + if (!locale_name_map.has(p_locale)) { + return String(); + } return locale_name_map[p_locale]; } Array TranslationServer::get_loaded_locales() const { Array locales; for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { - const Ref<Translation> &t = E->get(); ERR_FAIL_COND_V(t.is_null(), Array()); String l = t->get_locale(); @@ -1008,7 +1005,6 @@ Array TranslationServer::get_loaded_locales() const { } Vector<String> TranslationServer::get_all_locales() { - Vector<String> locales; const char **ptr = locale_list; @@ -1022,7 +1018,6 @@ Vector<String> TranslationServer::get_all_locales() { } Vector<String> TranslationServer::get_all_locale_names() { - Vector<String> locales; const char **ptr = locale_names; @@ -1036,28 +1031,90 @@ Vector<String> TranslationServer::get_all_locale_names() { } void TranslationServer::add_translation(const Ref<Translation> &p_translation) { - translations.insert(p_translation); } -void TranslationServer::remove_translation(const Ref<Translation> &p_translation) { +void TranslationServer::remove_translation(const Ref<Translation> &p_translation) { translations.erase(p_translation); } -void TranslationServer::clear() { +Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) { + Ref<Translation> res; + String lang = get_language_code(p_locale); + bool near_match_found = false; - translations.clear(); -}; + for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { + const Ref<Translation> &t = E->get(); + ERR_FAIL_COND_V(t.is_null(), nullptr); + String l = t->get_locale(); -StringName TranslationServer::translate(const StringName &p_message) const { + // Exact match. + if (l == p_locale) { + return t; + } + // If near match found, keep that match, but keep looking to try to look for perfect match. + if (get_language_code(l) == lang && !near_match_found) { + res = t; + near_match_found = true; + } + } + return res; +} + +void TranslationServer::clear() { + translations.clear(); +} + +StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const { // Match given message against the translation catalog for the project locale. - if (!enabled) + if (!enabled) { return p_message; + } ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); + StringName res = _get_message_from_translations(p_message, p_context, locale, false); + + if (!res && fallback.length() >= 2) { + res = _get_message_from_translations(p_message, p_context, fallback, false); + } + + if (!res) { + return p_message; + } + + return res; +} + +StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (!enabled) { + if (p_n == 1) { + return p_message; + } + return p_message_plural; + } + + ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); + + StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n); + + if (!res && fallback.length() >= 2) { + res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n); + } + + if (!res) { + if (p_n == 1) { + return p_message; + } + return p_message_plural; + } + + return res; +} + +StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const { // Locale can be of the form 'll_CC', i.e. language code and regional code, // e.g. 'en_US', 'en_GB', etc. It might also be simply 'll', e.g. 'en'. // To find the relevant translation, we look for those with locale starting @@ -1069,7 +1126,7 @@ StringName TranslationServer::translate(const StringName &p_message) const { // logic, so be sure to propagate changes there when changing things here. StringName res; - String lang = get_language_code(locale); + String lang = get_language_code(p_locale); bool near_match = false; for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { @@ -1077,7 +1134,7 @@ StringName TranslationServer::translate(const StringName &p_message) const { ERR_FAIL_COND_V(t.is_null(), p_message); String l = t->get_locale(); - bool exact_match = (l == locale); + bool exact_match = (l == p_locale); if (!exact_match) { if (near_match) { continue; // Only near-match once, but keep looking for exact matches. @@ -1087,7 +1144,13 @@ StringName TranslationServer::translate(const StringName &p_message) const { } } - StringName r = t->get_message(p_message); + StringName r; + if (!plural) { + r = t->get_message(p_message, p_context); + } else { + r = t->get_plural_message(p_message, p_message_plural, p_n, p_context); + } + if (!r) { continue; } @@ -1100,51 +1163,12 @@ StringName TranslationServer::translate(const StringName &p_message) const { } } - if (!res && fallback.length() >= 2) { - // Try again with the fallback locale. - String fallback_lang = get_language_code(fallback); - near_match = false; - - for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { - const Ref<Translation> &t = E->get(); - ERR_FAIL_COND_V(t.is_null(), p_message); - String l = t->get_locale(); - - bool exact_match = (l == fallback); - if (!exact_match) { - if (near_match) { - continue; // Only near-match once, but keep looking for exact matches. - } - if (get_language_code(l) != fallback_lang) { - continue; // Language code does not match. - } - } - - StringName r = t->get_message(p_message); - if (!r) { - continue; - } - res = r; - - if (exact_match) { - break; - } else { - near_match = true; - } - } - } - - if (!res) { - return p_message; - } - return res; } TranslationServer *TranslationServer::singleton = nullptr; bool TranslationServer::_load_translations(const String &p_from) { - if (ProjectSettings::get_singleton()->has_setting(p_from)) { Vector<String> translations = ProjectSettings::get_singleton()->get(p_from); @@ -1154,10 +1178,10 @@ bool TranslationServer::_load_translations(const String &p_from) { const String *r = translations.ptr(); for (int i = 0; i < tcount; i++) { - Ref<Translation> tr = ResourceLoader::load(r[i]); - if (tr.is_valid()) + if (tr.is_valid()) { add_translation(tr); + } } } return true; @@ -1167,21 +1191,22 @@ bool TranslationServer::_load_translations(const String &p_from) { } void TranslationServer::setup() { - String test = GLOBAL_DEF("locale/test", ""); test = test.strip_edges(); - if (test != "") + if (test != "") { set_locale(test); - else + } else { set_locale(OS::get_singleton()->get_locale()); + } fallback = GLOBAL_DEF("locale/fallback", "en"); #ifdef TOOLS_ENABLED { String options = ""; int idx = 0; while (locale_list[idx]) { - if (idx > 0) + if (idx > 0) { options += ","; + } options += locale_list[idx]; idx++; } @@ -1194,9 +1219,25 @@ void TranslationServer::set_tool_translation(const Ref<Translation> &p_translati tool_translation = p_translation; } -StringName TranslationServer::tool_translate(const StringName &p_message) const { +Ref<Translation> TranslationServer::get_tool_translation() const { + return tool_translation; +} + +String TranslationServer::get_tool_locale() { +#ifdef TOOLS_ENABLED + if (TranslationServer::get_singleton()->get_tool_translation().is_valid() && (Engine::get_singleton()->is_editor_hint() || Main::is_project_manager())) { + return tool_translation->get_locale(); + } else { +#else + { +#endif + return get_locale(); + } +} + +StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const { if (tool_translation.is_valid()) { - StringName r = tool_translation->get_message(p_message); + StringName r = tool_translation->get_message(p_message, p_context); if (r) { return r; } @@ -1204,13 +1245,27 @@ StringName TranslationServer::tool_translate(const StringName &p_message) const return p_message; } +StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (tool_translation.is_valid()) { + StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); + if (r) { + return r; + } + } + + if (p_n == 1) { + return p_message; + } + return p_message_plural; +} + void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) { doc_translation = p_translation; } -StringName TranslationServer::doc_translate(const StringName &p_message) const { +StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const { if (doc_translation.is_valid()) { - StringName r = doc_translation->get_message(p_message); + StringName r = doc_translation->get_message(p_message, p_context); if (r) { return r; } @@ -1218,17 +1273,32 @@ StringName TranslationServer::doc_translate(const StringName &p_message) const { return p_message; } -void TranslationServer::_bind_methods() { +StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (doc_translation.is_valid()) { + StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); + if (r) { + return r; + } + } + if (p_n == 1) { + return p_message; + } + return p_message_plural; +} + +void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name); - ClassDB::bind_method(D_METHOD("translate", "message"), &TranslationServer::translate); + ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL("")); + ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL("")); ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation); ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation); + ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object); ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear); @@ -1236,7 +1306,6 @@ void TranslationServer::_bind_methods() { } void TranslationServer::load_translations() { - String locale = get_locale(); _load_translations("locale/translations"); //all _load_translations("locale/translations_" + locale.substr(0, 2)); @@ -1246,13 +1315,10 @@ void TranslationServer::load_translations() { } } -TranslationServer::TranslationServer() : - locale("en"), - enabled(true) { +TranslationServer::TranslationServer() { singleton = this; for (int i = 0; locale_list[i]; ++i) { - locale_name_map.insert(locale_list[i], String::utf8(locale_names[i])); } } diff --git a/core/translation.h b/core/string/translation.h index 29a068f450..72a828227e 100644 --- a/core/translation.h +++ b/core/string/translation.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,21 +31,19 @@ #ifndef TRANSLATION_H #define TRANSLATION_H -#include "core/resource.h" +#include "core/io/resource.h" class Translation : public Resource { - GDCLASS(Translation, Resource); OBJ_SAVE_TYPE(Translation); RES_BASE_EXTENSION("translation"); - String locale; + String locale = "en"; Map<StringName, StringName> translation_map; - Vector<String> _get_message_list() const; - - Vector<String> _get_messages() const; - void _set_messages(const Vector<String> &p_messages); + virtual Vector<String> _get_message_list() const; + virtual Dictionary _get_messages() const; + virtual void _set_messages(const Dictionary &p_messages); protected: static void _bind_methods(); @@ -54,21 +52,21 @@ public: void set_locale(const String &p_locale); _FORCE_INLINE_ String get_locale() const { return locale; } - void add_message(const StringName &p_src_text, const StringName &p_xlated_text); - virtual StringName get_message(const StringName &p_src_text) const; //overridable for other implementations - void erase_message(const StringName &p_src_text); - - void get_message_list(List<StringName> *r_messages) const; - int get_message_count() const; + virtual void add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context = ""); + virtual void add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context = ""); + virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const; //overridable for other implementations + virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const; + virtual void erase_message(const StringName &p_src_text, const StringName &p_context = ""); + virtual void get_message_list(List<StringName> *r_messages) const; + virtual int get_message_count() const; - Translation(); + Translation() {} }; class TranslationServer : public Object { - GDCLASS(TranslationServer, Object); - String locale; + String locale = "en"; String fallback; Set<Ref<Translation>> translations; @@ -77,11 +75,13 @@ class TranslationServer : public Object { Map<String, String> locale_name_map; - bool enabled; + bool enabled = true; static TranslationServer *singleton; bool _load_translations(const String &p_from); + StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const; + static void _bind_methods(); public: @@ -92,6 +92,7 @@ public: void set_locale(const String &p_locale); String get_locale() const; + Ref<Translation> get_translation_object(const String &p_locale); String get_locale_name(const String &p_locale) const; @@ -100,7 +101,8 @@ public: void add_translation(const Ref<Translation> &p_translation); void remove_translation(const Ref<Translation> &p_translation); - StringName translate(const StringName &p_message) const; + StringName translate(const StringName &p_message, const StringName &p_context = "") const; + StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; static Vector<String> get_all_locales(); static Vector<String> get_all_locale_names(); @@ -108,10 +110,14 @@ public: static String standardize_locale(const String &p_locale); static String get_language_code(const String &p_locale); + String get_tool_locale(); void set_tool_translation(const Ref<Translation> &p_translation); - StringName tool_translate(const StringName &p_message) const; + Ref<Translation> get_tool_translation() const; + StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const; + StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; void set_doc_translation(const Ref<Translation> &p_translation); - StringName doc_translate(const StringName &p_message) const; + StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const; + StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; void setup(); diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp new file mode 100644 index 0000000000..846afe761b --- /dev/null +++ b/core/string/translation_po.cpp @@ -0,0 +1,312 @@ +/*************************************************************************/ +/* translation_po.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "translation_po.h" + +#include "core/os/file_access.h" + +#ifdef DEBUG_TRANSLATION_PO +void TranslationPO::print_translation_map() { + Error err; + FileAccess *file = FileAccess::open("translation_map_print_test.txt", FileAccess::WRITE, &err); + if (err != OK) { + ERR_PRINT("Failed to open translation_map_print_test.txt"); + return; + } + + file->store_line("NPlural : " + String::num_int64(this->get_plural_forms())); + file->store_line("Plural rule : " + this->get_plural_rule()); + file->store_line(""); + + List<StringName> context_l; + translation_map.get_key_list(&context_l); + for (auto E = context_l.front(); E; E = E->next()) { + StringName ctx = E->get(); + file->store_line(" ===== Context: " + String::utf8(String(ctx).utf8()) + " ===== "); + const HashMap<StringName, Vector<StringName>> &inner_map = translation_map[ctx]; + + List<StringName> id_l; + inner_map.get_key_list(&id_l); + for (auto E2 = id_l.front(); E2; E2 = E2->next()) { + StringName id = E2->get(); + file->store_line("msgid: " + String::utf8(String(id).utf8())); + for (int i = 0; i < inner_map[id].size(); i++) { + file->store_line("msgstr[" + String::num_int64(i) + "]: " + String::utf8(String(inner_map[id][i]).utf8())); + } + file->store_line(""); + } + } + file->close(); +} +#endif + +Dictionary TranslationPO::_get_messages() const { + // Return translation_map as a Dictionary. + + Dictionary d; + + List<StringName> context_l; + translation_map.get_key_list(&context_l); + for (auto E = context_l.front(); E; E = E->next()) { + StringName ctx = E->get(); + const HashMap<StringName, Vector<StringName>> &id_str_map = translation_map[ctx]; + + Dictionary d2; + List<StringName> id_l; + id_str_map.get_key_list(&id_l); + // Save list of id and strs associated with a context in a temporary dictionary. + for (auto E2 = id_l.front(); E2; E2 = E2->next()) { + StringName id = E2->get(); + d2[id] = id_str_map[id]; + } + + d[ctx] = d2; + } + + return d; +} + +void TranslationPO::_set_messages(const Dictionary &p_messages) { + // Construct translation_map from a Dictionary. + + List<Variant> context_l; + p_messages.get_key_list(&context_l); + for (auto E = context_l.front(); E; E = E->next()) { + StringName ctx = E->get(); + const Dictionary &id_str_map = p_messages[ctx]; + + HashMap<StringName, Vector<StringName>> temp_map; + List<Variant> id_l; + id_str_map.get_key_list(&id_l); + for (auto E2 = id_l.front(); E2; E2 = E2->next()) { + StringName id = E2->get(); + temp_map[id] = id_str_map[id]; + } + + translation_map[ctx] = temp_map; + } +} + +Vector<String> TranslationPO::_get_message_list() const { + // Return all keys in translation_map. + + List<StringName> msgs; + get_message_list(&msgs); + + Vector<String> v; + for (auto E = msgs.front(); E; E = E->next()) { + v.push_back(E->get()); + } + + return v; +} + +int TranslationPO::_get_plural_index(int p_n) const { + // Get a number between [0;number of plural forms). + + input_val.clear(); + input_val.push_back(p_n); + + Variant result; + for (int i = 0; i < equi_tests.size(); i++) { + Error err = expr->parse(equi_tests[i], input_name); + ERR_FAIL_COND_V_MSG(err != OK, 0, "Cannot parse expression. Error: " + expr->get_error_text()); + + result = expr->execute(input_val); + ERR_FAIL_COND_V_MSG(expr->has_execute_failed(), 0, "Cannot evaluate expression."); + + // Last expression. Variant result will either map to a bool or an integer, in both cases returning it will give the correct plural index. + if (i + 1 == equi_tests.size()) { + return result; + } + + if (bool(result)) { + return i; + } + } + + ERR_FAIL_V_MSG(0, "Unexpected. Function should have returned. Please report this bug."); +} + +void TranslationPO::_cache_plural_tests(const String &p_plural_rule) { + // Some examples of p_plural_rule passed in can have the form: + // "n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5" (Arabic) + // "n >= 2" (French) // When evaluating the last, esp careful with this one. + // "n != 1" (English) + int first_ques_mark = p_plural_rule.find("?"); + if (first_ques_mark == -1) { + equi_tests.push_back(p_plural_rule.strip_edges()); + return; + } + + String equi_test = p_plural_rule.substr(0, first_ques_mark).strip_edges(); + equi_tests.push_back(equi_test); + + String after_colon = p_plural_rule.substr(p_plural_rule.find(":") + 1, p_plural_rule.length()); + _cache_plural_tests(after_colon); +} + +void TranslationPO::set_plural_rule(const String &p_plural_rule) { + // Set plural_forms and plural_rule. + // p_plural_rule passed in has the form "Plural-Forms: nplurals=2; plural=(n >= 2);". + + int first_semi_col = p_plural_rule.find(";"); + plural_forms = p_plural_rule.substr(p_plural_rule.find("=") + 1, first_semi_col - (p_plural_rule.find("=") + 1)).to_int(); + + int expression_start = p_plural_rule.find("=", first_semi_col) + 1; + int second_semi_col = p_plural_rule.rfind(";"); + plural_rule = p_plural_rule.substr(expression_start, second_semi_col - expression_start); + + // Setup the cache to make evaluating plural rule faster later on. + plural_rule = plural_rule.replacen("(", ""); + plural_rule = plural_rule.replacen(")", ""); + _cache_plural_tests(plural_rule); + expr.instance(); + input_name.push_back("n"); +} + +void TranslationPO::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) { + HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context]; + + if (map_id_str.has(p_src_text)) { + WARN_PRINT("Double translations for \"" + String(p_src_text) + "\" under the same context \"" + String(p_context) + "\" for locale \"" + get_locale() + "\".\nThere should only be one unique translation for a given string under the same context."); + map_id_str[p_src_text].set(0, p_xlated_text); + } else { + map_id_str[p_src_text].push_back(p_xlated_text); + } +} + +void TranslationPO::add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context) { + ERR_FAIL_COND_MSG(p_plural_xlated_texts.size() != plural_forms, "Trying to add plural texts that don't match the required number of plural forms for locale \"" + get_locale() + "\""); + + HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context]; + + if (map_id_str.has(p_src_text)) { + WARN_PRINT("Double translations for \"" + p_src_text + "\" under the same context \"" + p_context + "\" for locale " + get_locale() + ".\nThere should only be one unique translation for a given string under the same context."); + map_id_str[p_src_text].clear(); + } + + for (int i = 0; i < p_plural_xlated_texts.size(); i++) { + map_id_str[p_src_text].push_back(p_plural_xlated_texts[i]); + } +} + +int TranslationPO::get_plural_forms() const { + return plural_forms; +} + +String TranslationPO::get_plural_rule() const { + return plural_rule; +} + +StringName TranslationPO::get_message(const StringName &p_src_text, const StringName &p_context) const { + if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) { + return StringName(); + } + ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug."); + + return translation_map[p_context][p_src_text][0]; +} + +StringName TranslationPO::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const { + ERR_FAIL_COND_V_MSG(p_n < 0, StringName(), "N passed into translation to get a plural message should not be negative. For negative numbers, use singular translation please. Search \"gettext PO Plural Forms\" online for the documentation on translating negative numbers."); + + // If the query is the same as last time, return the cached result. + if (p_n == last_plural_n && p_context == last_plural_context && p_src_text == last_plural_key) { + return translation_map[p_context][p_src_text][last_plural_mapped_index]; + } + + if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) { + return StringName(); + } + ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug."); + + if (translation_map[p_context][p_src_text].size() == 1) { + WARN_PRINT("Source string \"" + String(p_src_text) + "\" doesn't have plural translations. Use singular translation API for such as tr(), TTR() to translate \"" + String(p_src_text) + "\""); + return translation_map[p_context][p_src_text][0]; + } + + int plural_index = _get_plural_index(p_n); + ERR_FAIL_COND_V_MSG(plural_index < 0 || translation_map[p_context][p_src_text].size() < plural_index + 1, StringName(), "Plural index returned or number of plural translations is not valid. Please report this bug."); + + // Cache result so that if the next entry is the same, we can return directly. + // _get_plural_index(p_n) can get very costly, especially when evaluating long plural-rule (Arabic) + last_plural_key = p_src_text; + last_plural_context = p_context; + last_plural_n = p_n; + last_plural_mapped_index = plural_index; + + return translation_map[p_context][p_src_text][plural_index]; +} + +void TranslationPO::erase_message(const StringName &p_src_text, const StringName &p_context) { + if (!translation_map.has(p_context)) { + return; + } + + translation_map[p_context].erase(p_src_text); +} + +void TranslationPO::get_message_list(List<StringName> *r_messages) const { + // PHashTranslation uses this function to get the list of msgid. + // Return all the keys of translation_map under "" context. + + List<StringName> context_l; + translation_map.get_key_list(&context_l); + + for (auto E = context_l.front(); E; E = E->next()) { + if (String(E->get()) != "") { + continue; + } + + List<StringName> msgid_l; + translation_map[E->get()].get_key_list(&msgid_l); + + for (auto E2 = msgid_l.front(); E2; E2 = E2->next()) { + r_messages->push_back(E2->get()); + } + } +} + +int TranslationPO::get_message_count() const { + List<StringName> context_l; + translation_map.get_key_list(&context_l); + + int count = 0; + for (auto E = context_l.front(); E; E = E->next()) { + count += translation_map[E->get()].size(); + } + return count; +} + +void TranslationPO::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_plural_forms"), &TranslationPO::get_plural_forms); + ClassDB::bind_method(D_METHOD("get_plural_rule"), &TranslationPO::get_plural_rule); +} diff --git a/core/string/translation_po.h b/core/string/translation_po.h new file mode 100644 index 0000000000..0e1d03d6ca --- /dev/null +++ b/core/string/translation_po.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* translation_po.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 TRANSLATION_PO_H +#define TRANSLATION_PO_H + +//#define DEBUG_TRANSLATION_PO + +#include "core/math/expression.h" +#include "core/string/translation.h" + +class TranslationPO : public Translation { + GDCLASS(TranslationPO, Translation); + + // TLDR: Maps context to a list of source strings and translated strings. In PO terms, maps msgctxt to a list of msgid and msgstr. + // The first key corresponds to context, and the second key (of the contained HashMap) corresponds to source string. + // The value Vector<StringName> in the second map stores the translated strings. Index 0, 1, 2 matches msgstr[0], msgstr[1], msgstr[2]... in the case of plurals. + // Otherwise index 0 matches to msgstr in a singular translation. + // Strings without context have "" as first key. + HashMap<StringName, HashMap<StringName, Vector<StringName>>> translation_map; + + int plural_forms = 0; // 0 means no "Plural-Forms" is given in the PO header file. The min for all languages is 1. + String plural_rule; + + // Cache temporary variables related to _get_plural_index() to make it faster + Vector<String> equi_tests; + Vector<String> input_name; + mutable Ref<Expression> expr; + mutable Array input_val; + mutable StringName last_plural_key; + mutable StringName last_plural_context; + mutable int last_plural_n = -1; // Set it to an impossible value at the beginning. + mutable int last_plural_mapped_index = 0; + + void _cache_plural_tests(const String &p_plural_rule); + int _get_plural_index(int p_n) const; + + Vector<String> _get_message_list() const override; + Dictionary _get_messages() const override; + void _set_messages(const Dictionary &p_messages) override; + +protected: + static void _bind_methods(); + +public: + void get_message_list(List<StringName> *r_messages) const override; + int get_message_count() const override; + void add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context = "") override; + void add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context = "") override; + StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override; + StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override; + void erase_message(const StringName &p_src_text, const StringName &p_context = "") override; + + void set_plural_rule(const String &p_plural_rule); + int get_plural_forms() const; + String get_plural_rule() const; + +#ifdef DEBUG_TRANSLATION_PO + void print_translation_map(); +#endif + + TranslationPO() {} +}; + +#endif // TRANSLATION_PO_H diff --git a/core/ucaps.h b/core/string/ucaps.h index ad71731617..b785ac7879 100644 --- a/core/ucaps.h +++ b/core/string/ucaps.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -1373,7 +1373,6 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { }; static int _find_upper(int ch) { - int low = 0; int high = CAPS_LEN - 1; int middle; @@ -1394,7 +1393,6 @@ static int _find_upper(int ch) { } static int _find_lower(int ch) { - int low = 0; int high = CAPS_LEN - 2; int middle; diff --git a/core/ustring.cpp b/core/string/ustring.cpp index fbe3fcb1b2..21336a99ec 100644 --- a/core/ustring.cpp +++ b/core/string/ustring.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,22 +28,17 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef _MSC_VER -#define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy -#endif - #include "ustring.h" -#include "core/color.h" #include "core/crypto/crypto_core.h" +#include "core/math/color.h" #include "core/math/math_funcs.h" #include "core/os/memory.h" -#include "core/print_string.h" -#include "core/translation.h" -#include "core/ucaps.h" -#include "core/variant.h" +#include "core/string/print_string.h" +#include "core/string/translation.h" +#include "core/string/ucaps.h" +#include "core/variant/variant.h" -#include <wchar.h> #include <cstdint> #ifndef NO_USE_STDLIB @@ -51,6 +46,10 @@ #include <stdlib.h> #endif +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy +#endif + #if defined(MINGW_ENABLED) || defined(_MSC_VER) #define snprintf _snprintf_s #endif @@ -62,20 +61,19 @@ #define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F')) const char CharString::_null = 0; -const CharType String::_null = 0; +const char16_t Char16String::_null = 0; +const char32_t String::_null = 0; -bool is_symbol(CharType c) { +bool is_symbol(char32_t c) { return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); } bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) { - const String &s = p_s; int beg = CLAMP(p_col, 0, s.length()); int end = beg; if (s[beg] > 32 || beg == s.length()) { - bool symbol = beg < s.length() && is_symbol(s[beg]); while (beg > 0 && s[beg - 1] > 32 && (symbol == is_symbol(s[beg - 1]))) { @@ -85,23 +83,24 @@ bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) { end++; } - if (end < s.length()) + if (end < s.length()) { end += 1; + } r_beg = beg; r_end = end; return true; } else { - return false; } } -/** STRING **/ - -bool CharString::operator<(const CharString &p_right) const { +/*************************************************************************/ +/* Char16String */ +/*************************************************************************/ +bool Char16String::operator<(const Char16String &p_right) const { if (length() == 0) { return p_right.length() != 0; } @@ -109,8 +108,7 @@ bool CharString::operator<(const CharString &p_right) const { return is_str_less(get_data(), p_right.get_data()); } -CharString &CharString::operator+=(char p_char) { - +Char16String &Char16String::operator+=(char16_t p_char) { resize(size() ? size() + 1 : 2); set(length(), 0); set(length() - 1, p_char); @@ -118,22 +116,76 @@ CharString &CharString::operator+=(char p_char) { return *this; } -const char *CharString::get_data() const { +Char16String &Char16String::operator=(const char16_t *p_cstr) { + copy_from(p_cstr); + return *this; +} - if (size()) +const char16_t *Char16String::get_data() const { + if (size()) { return &operator[](0); - else - return ""; + } else { + return u""; + } } -CharString &CharString::operator=(const char *p_cstr) { +void Char16String::copy_from(const char16_t *p_cstr) { + if (!p_cstr) { + resize(0); + return; + } + + const char16_t *s = p_cstr; + for (; *s; s++) { + } + size_t len = s - p_cstr; + + if (len == 0) { + resize(0); + return; + } + + Error err = resize(++len); // include terminating null char + + ERR_FAIL_COND_MSG(err != OK, "Failed to copy char16_t string."); + + memcpy(ptrw(), p_cstr, len * sizeof(char16_t)); +} + +/*************************************************************************/ +/* CharString */ +/*************************************************************************/ + +bool CharString::operator<(const CharString &p_right) const { + if (length() == 0) { + return p_right.length() != 0; + } + + return is_str_less(get_data(), p_right.get_data()); +} + +CharString &CharString::operator+=(char p_char) { + resize(size() ? size() + 1 : 2); + set(length(), 0); + set(length() - 1, p_char); + return *this; +} + +CharString &CharString::operator=(const char *p_cstr) { copy_from(p_cstr); return *this; } -void CharString::copy_from(const char *p_cstr) { +const char *CharString::get_data() const { + if (size()) { + return &operator[](0); + } else { + return ""; + } +} +void CharString::copy_from(const char *p_cstr) { if (!p_cstr) { resize(0); return; @@ -153,50 +205,160 @@ void CharString::copy_from(const char *p_cstr) { memcpy(ptrw(), p_cstr, len); } -void String::copy_from(const char *p_cstr) { +/*************************************************************************/ +/* String */ +/*************************************************************************/ - if (!p_cstr) { +//kind of poor should be rewritten properly +String String::word_wrap(int p_chars_per_line) const { + int from = 0; + int last_space = 0; + String ret; + for (int i = 0; i < length(); i++) { + if (i - from >= p_chars_per_line) { + if (last_space == -1) { + ret += substr(from, i - from + 1) + "\n"; + } else { + ret += substr(from, last_space - from) + "\n"; + i = last_space; //rewind + } + from = i + 1; + last_space = -1; + } else if (operator[](i) == ' ' || operator[](i) == '\t') { + last_space = i; + } else if (operator[](i) == '\n') { + ret += substr(from, i - from) + "\n"; + from = i + 1; + last_space = -1; + } + } + + if (from < length()) { + ret += substr(from, length()); + } + return ret; +} + +void String::copy_from(const char *p_cstr) { + // copy Latin-1 encoded c-string directly + if (!p_cstr) { resize(0); return; } int len = 0; const char *ptr = p_cstr; - while (*(ptr++) != 0) + while (*(ptr++) != 0) { len++; + } if (len == 0) { - resize(0); return; } resize(len + 1); // include 0 - CharType *dst = this->ptrw(); + char32_t *dst = this->ptrw(); for (int i = 0; i < len + 1; i++) { + dst[i] = p_cstr[i]; + } +} + +void String::copy_from(const char *p_cstr, const int p_clip_to) { + // copy Latin-1 encoded c-string directly + if (!p_cstr) { + resize(0); + return; + } + + int len = 0; + const char *ptr = p_cstr; + while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) { + len++; + } + if (len == 0) { + resize(0); + return; + } + + resize(len + 1); // include 0 + + char32_t *dst = this->ptrw(); + + for (int i = 0; i < len; i++) { dst[i] = p_cstr[i]; } + dst[len] = 0; +} + +void String::copy_from(const wchar_t *p_cstr) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + parse_utf16((const char16_t *)p_cstr); +#else + // wchar_t is 32-bit, copy directly + copy_from((const char32_t *)p_cstr); +#endif } -void String::copy_from(const CharType *p_cstr, const int p_clip_to) { +void String::copy_from(const wchar_t *p_cstr, const int p_clip_to) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + parse_utf16((const char16_t *)p_cstr, p_clip_to); +#else + // wchar_t is 32-bit, copy directly + copy_from((const char32_t *)p_cstr, p_clip_to); +#endif +} - if (!p_cstr) { +void String::copy_from(const char32_t &p_char) { + resize(2); + if ((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff)) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(p_char, 16) + "."); + set(0, 0xfffd); + } else { + set(0, p_char); + } + set(1, 0); +} +void String::copy_from(const char32_t *p_cstr) { + if (!p_cstr) { resize(0); return; } int len = 0; - const CharType *ptr = p_cstr; - while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) + const char32_t *ptr = p_cstr; + while (*(ptr++) != 0) { len++; + } if (len == 0) { + resize(0); + return; + } + copy_from_unchecked(p_cstr, len); +} + +void String::copy_from(const char32_t *p_cstr, const int p_clip_to) { + if (!p_cstr) { + resize(0); + return; + } + + int len = 0; + const char32_t *ptr = p_cstr; + while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) { + len++; + } + + if (len == 0) { resize(0); return; } @@ -208,280 +370,381 @@ void String::copy_from(const CharType *p_cstr, const int p_clip_to) { // p_char != nullptr // p_length > 0 // p_length <= p_char strlen -void String::copy_from_unchecked(const CharType *p_char, const int p_length) { +void String::copy_from_unchecked(const char32_t *p_char, const int p_length) { resize(p_length + 1); set(p_length, 0); - CharType *dst = ptrw(); + char32_t *dst = ptrw(); for (int i = 0; i < p_length; i++) { - dst[i] = p_char[i]; + if ((p_char[i] >= 0xd800 && p_char[i] <= 0xdfff) || (p_char[i] > 0x10ffff)) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(p_char[i], 16) + "."); + dst[i] = 0xfffd; + } else { + dst[i] = p_char[i]; + } } } -void String::copy_from(const CharType &p_char) { - - resize(2); - set(0, p_char); - set(1, 0); +void String::operator=(const char *p_str) { + copy_from(p_str); } -bool String::operator==(const String &p_str) const { - - if (length() != p_str.length()) - return false; - if (empty()) - return true; - - int l = length(); - - const CharType *src = c_str(); - const CharType *dst = p_str.c_str(); - - /* Compare char by char */ - for (int i = 0; i < l; i++) { - - if (src[i] != dst[i]) - return false; - } - - return true; +void String::operator=(const char32_t *p_str) { + copy_from(p_str); } -bool String::operator!=(const String &p_str) const { - - return !(*this == p_str); +void String::operator=(const wchar_t *p_str) { + copy_from(p_str); } String String::operator+(const String &p_str) const { - String res = *this; res += p_str; return res; } -/* -String String::operator+(CharType p_chr) const { +String operator+(const char *p_chr, const String &p_str) { + String tmp = p_chr; + tmp += p_str; + return tmp; +} + +String operator+(const wchar_t *p_chr, const String &p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + String tmp = String::utf16((const char16_t *)p_chr); +#else + // wchar_t is 32-bi + String tmp = (const char32_t *)p_chr; +#endif + tmp += p_str; + return tmp; +} - String res=*this; - res+=p_chr; - return res; +String operator+(char32_t p_chr, const String &p_str) { + return (String::chr(p_chr) + p_str); } -*/ -String &String::operator+=(const String &p_str) { - if (empty()) { +String &String::operator+=(const String &p_str) { + if (is_empty()) { *this = p_str; return *this; } - if (p_str.empty()) + if (p_str.is_empty()) { return *this; + } int from = length(); resize(length() + p_str.size()); - const CharType *src = p_str.c_str(); - CharType *dst = ptrw(); + const char32_t *src = p_str.get_data(); + char32_t *dst = ptrw(); set(length(), 0); - for (int i = 0; i < p_str.length(); i++) + for (int i = 0; i < p_str.length(); i++) { dst[from + i] = src[i]; - - return *this; -} - -String &String::operator+=(const CharType *p_str) { - - *this += String(p_str); - return *this; -} - -String &String::operator+=(CharType p_char) { - - resize(size() ? size() + 1 : 2); - set(length(), 0); - set(length() - 1, p_char); + } return *this; } String &String::operator+=(const char *p_str) { - - if (!p_str || p_str[0] == 0) + if (!p_str || p_str[0] == 0) { return *this; + } int src_len = 0; const char *ptr = p_str; - while (*(ptr++) != 0) + while (*(ptr++) != 0) { src_len++; + } int from = length(); resize(from + src_len + 1); - CharType *dst = ptrw(); + char32_t *dst = ptrw(); set(length(), 0); - for (int i = 0; i < src_len; i++) + for (int i = 0; i < src_len; i++) { dst[from + i] = p_str[i]; + } return *this; } -void String::operator=(const char *p_str) { +String &String::operator+=(const wchar_t *p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + *this += String::utf16((const char16_t *)p_str); +#else + // wchar_t is 32-bit + *this += String((const char32_t *)p_str); +#endif + return *this; +} - copy_from(p_str); +String &String::operator+=(const char32_t *p_str) { + *this += String(p_str); + return *this; } -void String::operator=(const CharType *p_str) { +String &String::operator+=(char32_t p_char) { + resize(size() ? size() + 1 : 2); + set(length(), 0); + if ((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff)) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(p_char, 16) + "."); + set(length() - 1, 0xfffd); + } else { + set(length() - 1, p_char); + } - copy_from(p_str); + return *this; } -bool String::operator==(const StrRange &p_str_range) const { +bool String::operator==(const char *p_str) const { + // compare Latin-1 encoded c-string + int len = 0; + const char *aux = p_str; - int len = p_str_range.len; + while (*(aux++) != 0) { + len++; + } - if (length() != len) + if (length() != len) { return false; - if (empty()) + } + if (is_empty()) { return true; + } - const CharType *c_str = p_str_range.c_str; - const CharType *dst = &operator[](0); + int l = length(); - /* Compare char by char */ - for (int i = 0; i < len; i++) { + const char32_t *dst = get_data(); - if (c_str[i] != dst[i]) + // Compare char by char + for (int i = 0; i < l; i++) { + if ((char32_t)p_str[i] != dst[i]) { return false; + } } return true; } -bool String::operator==(const char *p_str) const { +bool String::operator==(const wchar_t *p_str) const { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + return *this == String::utf16((const char16_t *)p_str); +#else + // wchar_t is 32-bit, compare char by char + return *this == (const char32_t *)p_str; +#endif +} +bool String::operator==(const char32_t *p_str) const { int len = 0; - const char *aux = p_str; + const char32_t *aux = p_str; - while (*(aux++) != 0) + while (*(aux++) != 0) { len++; + } - if (length() != len) + if (length() != len) { return false; - if (empty()) + } + if (is_empty()) { return true; + } int l = length(); - const CharType *dst = c_str(); + const char32_t *dst = get_data(); /* Compare char by char */ for (int i = 0; i < l; i++) { - - if (p_str[i] != dst[i]) + if (p_str[i] != dst[i]) { return false; + } } return true; } -bool String::operator==(const CharType *p_str) const { - - int len = 0; - const CharType *aux = p_str; - - while (*(aux++) != 0) - len++; - - if (length() != len) +bool String::operator==(const String &p_str) const { + if (length() != p_str.length()) { return false; - if (empty()) + } + if (is_empty()) { return true; + } int l = length(); - const CharType *dst = c_str(); + const char32_t *src = get_data(); + const char32_t *dst = p_str.get_data(); /* Compare char by char */ for (int i = 0; i < l; i++) { + if (src[i] != dst[i]) { + return false; + } + } - if (p_str[i] != dst[i]) + return true; +} + +bool String::operator==(const StrRange &p_str_range) const { + int len = p_str_range.len; + + if (length() != len) { + return false; + } + if (is_empty()) { + return true; + } + + const char32_t *c_str = p_str_range.c_str; + const char32_t *dst = &operator[](0); + + /* Compare char by char */ + for (int i = 0; i < len; i++) { + if (c_str[i] != dst[i]) { return false; + } } return true; } -bool String::operator!=(const char *p_str) const { +bool operator==(const char *p_chr, const String &p_str) { + return p_str == p_chr; +} - return (!(*this == p_str)); +bool operator==(const wchar_t *p_chr, const String &p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + return p_str == String::utf16((const char16_t *)p_chr); +#else + // wchar_t is 32-bi + return p_str == String((const char32_t *)p_chr); +#endif +} + +bool operator!=(const char *p_chr, const String &p_str) { + return !(p_str == p_chr); } -bool String::operator!=(const CharType *p_str) const { +bool operator!=(const wchar_t *p_chr, const String &p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + return !(p_str == String::utf16((const char16_t *)p_chr)); +#else + // wchar_t is 32-bi + return !(p_str == String((const char32_t *)p_chr)); +#endif +} +bool String::operator!=(const char *p_str) const { return (!(*this == p_str)); } -bool String::operator<(const CharType *p_str) const { +bool String::operator!=(const wchar_t *p_str) const { + return (!(*this == p_str)); +} - if (empty() && p_str[0] == 0) - return false; - if (empty()) - return true; +bool String::operator!=(const char32_t *p_str) const { + return (!(*this == p_str)); +} - return is_str_less(c_str(), p_str); +bool String::operator!=(const String &p_str) const { + return !((*this == p_str)); } bool String::operator<=(const String &p_str) const { + return !(p_str < *this); +} - return (*this < p_str) || (*this == p_str); +bool String::operator>(const String &p_str) const { + return p_str < *this; +} +bool String::operator>=(const String &p_str) const { + return !(*this < p_str); } bool String::operator<(const char *p_str) const { + if (is_empty() && p_str[0] == 0) { + return false; + } + if (is_empty()) { + return true; + } + return is_str_less(get_data(), p_str); +} - if (empty() && p_str[0] == 0) +bool String::operator<(const wchar_t *p_str) const { + if (is_empty() && p_str[0] == 0) { return false; - if (empty()) + } + if (is_empty()) { return true; + } - return is_str_less(c_str(), p_str); +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + return is_str_less(get_data(), String::utf16((const char16_t *)p_str).get_data()); +#else + // wchar_t is 32-bit + return is_str_less(get_data(), (const char32_t *)p_str); +#endif } -bool String::operator<(const String &p_str) const { +bool String::operator<(const char32_t *p_str) const { + if (is_empty() && p_str[0] == 0) { + return false; + } + if (is_empty()) { + return true; + } - return operator<(p_str.c_str()); + return is_str_less(get_data(), p_str); } -signed char String::nocasecmp_to(const String &p_str) const { +bool String::operator<(const String &p_str) const { + return operator<(p_str.get_data()); +} - if (empty() && p_str.empty()) +signed char String::nocasecmp_to(const String &p_str) const { + if (is_empty() && p_str.is_empty()) { return 0; - if (empty()) + } + if (is_empty()) { return -1; - if (p_str.empty()) + } + if (p_str.is_empty()) { return 1; + } - const CharType *that_str = p_str.c_str(); - const CharType *this_str = c_str(); + const char32_t *that_str = p_str.get_data(); + const char32_t *this_str = get_data(); while (true) { - - if (*that_str == 0 && *this_str == 0) + if (*that_str == 0 && *this_str == 0) { return 0; //we're equal - else if (*this_str == 0) + } else if (*this_str == 0) { return -1; //if this is empty, and the other one is not, then we're less.. I think? - else if (*that_str == 0) + } else if (*that_str == 0) { return 1; //otherwise the other one is smaller.. - else if (_find_upper(*this_str) < _find_upper(*that_str)) //more than + } else if (_find_upper(*this_str) < _find_upper(*that_str)) { //more than return -1; - else if (_find_upper(*this_str) > _find_upper(*that_str)) //less than + } else if (_find_upper(*this_str) > _find_upper(*that_str)) { //less than return 1; + } this_str++; that_str++; @@ -489,29 +752,31 @@ signed char String::nocasecmp_to(const String &p_str) const { } signed char String::casecmp_to(const String &p_str) const { - - if (empty() && p_str.empty()) + if (is_empty() && p_str.is_empty()) { return 0; - if (empty()) + } + if (is_empty()) { return -1; - if (p_str.empty()) + } + if (p_str.is_empty()) { return 1; + } - const CharType *that_str = p_str.c_str(); - const CharType *this_str = c_str(); + const char32_t *that_str = p_str.get_data(); + const char32_t *this_str = get_data(); while (true) { - - if (*that_str == 0 && *this_str == 0) + if (*that_str == 0 && *this_str == 0) { return 0; //we're equal - else if (*this_str == 0) + } else if (*this_str == 0) { return -1; //if this is empty, and the other one is not, then we're less.. I think? - else if (*that_str == 0) + } else if (*that_str == 0) { return 1; //otherwise the other one is smaller.. - else if (*this_str < *that_str) //more than + } else if (*this_str < *that_str) { //more than return -1; - else if (*this_str > *that_str) //less than + } else if (*this_str > *that_str) { //less than return 1; + } this_str++; that_str++; @@ -519,84 +784,109 @@ signed char String::casecmp_to(const String &p_str) const { } signed char String::naturalnocasecmp_to(const String &p_str) const { - - const CharType *this_str = c_str(); - const CharType *that_str = p_str.c_str(); + const char32_t *this_str = get_data(); + const char32_t *that_str = p_str.get_data(); if (this_str && that_str) { - while (*this_str == '.' || *that_str == '.') { - if (*this_str++ != '.') + if (*this_str++ != '.') { return 1; - if (*that_str++ != '.') + } + if (*that_str++ != '.') { return -1; - if (!*that_str) + } + if (!*that_str) { return 1; - if (!*this_str) + } + if (!*this_str) { return -1; + } } while (*this_str) { - - if (!*that_str) + if (!*that_str) { return 1; - else if (IS_DIGIT(*this_str)) { - - int64_t this_int, that_int; - - if (!IS_DIGIT(*that_str)) + } else if (IS_DIGIT(*this_str)) { + if (!IS_DIGIT(*that_str)) { return -1; + } - /* Compare the numbers */ - this_int = to_int(this_str); - that_int = to_int(that_str); + // Keep ptrs to start of numerical sequences + const char32_t *this_substr = this_str; + const char32_t *that_substr = that_str; - if (this_int < that_int) + // Compare lengths of both numerical sequences, ignoring leading zeros + while (IS_DIGIT(*this_str)) { + this_str++; + } + while (IS_DIGIT(*that_str)) { + that_str++; + } + while (*this_substr == '0') { + this_substr++; + } + while (*that_substr == '0') { + that_substr++; + } + int this_len = this_str - this_substr; + int that_len = that_str - that_substr; + + if (this_len < that_len) { return -1; - else if (this_int > that_int) + } else if (this_len > that_len) { return 1; + } - /* Skip */ - while (IS_DIGIT(*this_str)) - this_str++; - while (IS_DIGIT(*that_str)) - that_str++; - } else if (IS_DIGIT(*that_str)) + // If lengths equal, compare lexicographically + while (this_substr != this_str && that_substr != that_str) { + if (*this_substr < *that_substr) { + return -1; + } else if (*this_substr > *that_substr) { + return 1; + } + this_substr++; + that_substr++; + } + } else if (IS_DIGIT(*that_str)) { return 1; - else { - if (_find_upper(*this_str) < _find_upper(*that_str)) //more than + } else { + if (_find_upper(*this_str) < _find_upper(*that_str)) { //more than return -1; - else if (_find_upper(*this_str) > _find_upper(*that_str)) //less than + } else if (_find_upper(*this_str) > _find_upper(*that_str)) { //less than return 1; + } this_str++; that_str++; } } - if (*that_str) + if (*that_str) { return -1; + } } return 0; } -void String::erase(int p_pos, int p_chars) { +const char32_t *String::get_data() const { + static const char32_t zero = 0; + return size() ? &operator[](0) : &zero; +} +void String::erase(int p_pos, int p_chars) { *this = left(p_pos) + substr(p_pos + p_chars, length() - ((p_pos + p_chars))); } String String::capitalize() const { - String aux = this->camelcase_to_underscore(true).replace("_", " ").strip_edges(); String cap; for (int i = 0; i < aux.get_slice_count(" "); i++) { - String slice = aux.get_slicec(' ', i); if (slice.length() > 0) { - slice[0] = _find_upper(slice[0]); - if (i > 0) + if (i > 0) { cap += " "; + } cap += slice; } } @@ -605,7 +895,7 @@ String String::capitalize() const { } String String::camelcase_to_underscore(bool lowercase) const { - const CharType *cstr = c_str(); + const char32_t *cstr = get_data(); String new_string; const char A = 'A', Z = 'Z'; const char a = 'a', z = 'z'; @@ -657,18 +947,19 @@ String String::get_with_code_lines() const { } return ret; } -int String::get_slice_count(String p_splitter) const { - if (empty()) +int String::get_slice_count(String p_splitter) const { + if (is_empty()) { return 0; - if (p_splitter.empty()) + } + if (p_splitter.is_empty()) { return 0; + } int pos = 0; int slices = 1; while ((pos = find(p_splitter, pos)) >= 0) { - slices++; pos += p_splitter.length(); } @@ -677,35 +968,37 @@ int String::get_slice_count(String p_splitter) const { } String String::get_slice(String p_splitter, int p_slice) const { - - if (empty() || p_splitter.empty()) + if (is_empty() || p_splitter.is_empty()) { return ""; + } int pos = 0; int prev_pos = 0; //int slices=1; - if (p_slice < 0) + if (p_slice < 0) { return ""; - if (find(p_splitter) == -1) + } + if (find(p_splitter) == -1) { return *this; + } int i = 0; while (true) { - pos = find(p_splitter, pos); - if (pos == -1) + if (pos == -1) { pos = length(); //reached end + } int from = prev_pos; //int to=pos; if (p_slice == i) { - return substr(from, pos - from); } - if (pos == length()) //reached end and no find + if (pos == length()) { //reached end and no find break; + } pos += p_splitter.length(); prev_pos = pos; i++; @@ -714,24 +1007,22 @@ String String::get_slice(String p_splitter, int p_slice) const { return ""; //no find! } -String String::get_slicec(CharType p_splitter, int p_slice) const { - - if (empty()) +String String::get_slicec(char32_t p_splitter, int p_slice) const { + if (is_empty()) { return String(); + } - if (p_slice < 0) + if (p_slice < 0) { return String(); + } - const CharType *c = this->ptr(); + const char32_t *c = this->ptr(); int i = 0; int prev = 0; int count = 0; while (true) { - if (c[i] == 0 || c[i] == p_splitter) { - if (p_slice == count) { - return substr(prev, i - prev); } else if (c[i] == 0) { return String(); @@ -746,22 +1037,22 @@ String String::get_slicec(CharType p_splitter, int p_slice) const { } Vector<String> String::split_spaces() const { - Vector<String> ret; int from = 0; int i = 0; int len = length(); - if (len == 0) + if (len == 0) { return ret; + } bool inside = false; while (true) { - bool empty = operator[](i) < 33; - if (i == 0) + if (i == 0) { inside = !empty; + } if (!empty && !inside) { inside = true; @@ -769,13 +1060,13 @@ Vector<String> String::split_spaces() const { } if (empty && inside) { - ret.push_back(substr(from, i - from)); inside = false; } - if (i == len) + if (i == len) { break; + } i++; } @@ -783,21 +1074,19 @@ Vector<String> String::split_spaces() const { } Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const { - Vector<String> ret; int from = 0; int len = length(); while (true) { - int end = find(p_splitter, from); - if (end < 0) + if (end < 0) { end = len; + } if (p_allow_empty || (end > from)) { - if (p_maxsplit <= 0) + if (p_maxsplit <= 0) { ret.push_back(substr(from, end - from)); - else { - + } else { // Put rest of the string and leave cycle. if (p_maxsplit == ret.size()) { ret.push_back(substr(from, len)); @@ -809,8 +1098,9 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p } } - if (end == len) + if (end == len) { break; + } from = end + p_splitter.length(); } @@ -819,13 +1109,11 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p } Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const { - Vector<String> ret; const int len = length(); int remaining_len = len; while (true) { - if (remaining_len < p_splitter.length() || (p_maxsplit > 0 && p_maxsplit == ret.size())) { // no room for another splitter or hit max splits, push what's left and we're done if (p_allow_empty || remaining_len > 0) { @@ -855,21 +1143,22 @@ Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int } Vector<float> String::split_floats(const String &p_splitter, bool p_allow_empty) const { - Vector<float> ret; int from = 0; int len = length(); while (true) { - int end = find(p_splitter, from); - if (end < 0) + if (end < 0) { end = len; - if (p_allow_empty || (end > from)) - ret.push_back(String::to_double(&c_str()[from])); + } + if (p_allow_empty || (end > from)) { + ret.push_back(String::to_float(&get_data()[from])); + } - if (end == len) + if (end == len) { break; + } from = end + p_splitter.length(); } @@ -878,13 +1167,11 @@ Vector<float> String::split_floats(const String &p_splitter, bool p_allow_empty) } Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty) const { - Vector<float> ret; int from = 0; int len = length(); while (true) { - int idx; int end = findmk(p_splitters, from, &idx); int spl_len = 1; @@ -895,11 +1182,12 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_ } if (p_allow_empty || (end > from)) { - ret.push_back(String::to_double(&c_str()[from])); + ret.push_back(String::to_float(&get_data()[from])); } - if (end == len) + if (end == len) { break; + } from = end + spl_len; } @@ -908,21 +1196,22 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_ } Vector<int> String::split_ints(const String &p_splitter, bool p_allow_empty) const { - Vector<int> ret; int from = 0; int len = length(); while (true) { - int end = find(p_splitter, from); - if (end < 0) + if (end < 0) { end = len; - if (p_allow_empty || (end > from)) - ret.push_back(String::to_int(&c_str()[from], end - from)); + } + if (p_allow_empty || (end > from)) { + ret.push_back(String::to_int(&get_data()[from], end - from)); + } - if (end == len) + if (end == len) { break; + } from = end + p_splitter.length(); } @@ -931,13 +1220,11 @@ Vector<int> String::split_ints(const String &p_splitter, bool p_allow_empty) con } Vector<int> String::split_ints_mk(const Vector<String> &p_splitters, bool p_allow_empty) const { - Vector<int> ret; int from = 0; int len = length(); while (true) { - int idx; int end = findmk(p_splitters, from, &idx); int spl_len = 1; @@ -947,11 +1234,13 @@ Vector<int> String::split_ints_mk(const Vector<String> &p_splitters, bool p_allo spl_len = p_splitters[idx].length(); } - if (p_allow_empty || (end > from)) - ret.push_back(String::to_int(&c_str()[from], end - from)); + if (p_allow_empty || (end > from)) { + ret.push_back(String::to_int(&get_data()[from], end - from)); + } - if (end == len) + if (end == len) { break; + } from = end + spl_len; } @@ -959,7 +1248,7 @@ Vector<int> String::split_ints_mk(const Vector<String> &p_splitters, bool p_allo return ret; } -String String::join(Vector<String> parts) { +String String::join(Vector<String> parts) const { String ret; for (int i = 0; i < parts.size(); ++i) { if (i > 0) { @@ -970,91 +1259,70 @@ String String::join(Vector<String> parts) { return ret; } -CharType String::char_uppercase(CharType p_char) { - +char32_t String::char_uppercase(char32_t p_char) { return _find_upper(p_char); } -CharType String::char_lowercase(CharType p_char) { - +char32_t String::char_lowercase(char32_t p_char) { return _find_lower(p_char); } String String::to_upper() const { - String upper = *this; for (int i = 0; i < upper.size(); i++) { - - const CharType s = upper[i]; - const CharType t = _find_upper(s); - if (s != t) // avoid copy on write + const char32_t s = upper[i]; + const char32_t t = _find_upper(s); + if (s != t) { // avoid copy on write upper[i] = t; + } } return upper; } String String::to_lower() const { - String lower = *this; for (int i = 0; i < lower.size(); i++) { - - const CharType s = lower[i]; - const CharType t = _find_lower(s); - if (s != t) // avoid copy on write + const char32_t s = lower[i]; + const char32_t t = _find_lower(s); + if (s != t) { // avoid copy on write lower[i] = t; + } } return lower; } -const CharType *String::c_str() const { - - static const CharType zero = 0; - - return size() ? &operator[](0) : &zero; -} - -String String::md5(const uint8_t *p_md5) { - return String::hex_encode_buffer(p_md5, 16); -} - -String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { - static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - - String ret; - char v[2] = { 0, 0 }; - - for (int i = 0; i < p_len; i++) { - v[0] = hex[p_buffer[i] >> 4]; - ret += v; - v[0] = hex[p_buffer[i] & 0xF]; - ret += v; - } - - return ret; -} - -String String::chr(CharType p_char) { - - CharType c[2] = { p_char, 0 }; +String String::chr(char32_t p_char) { + char32_t c[2] = { p_char, 0 }; return String(c); } + String String::num(double p_num, int p_decimals) { + if (Math::is_nan(p_num)) { + return "nan"; + } + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } #ifndef NO_USE_STDLIB - if (p_decimals > 16) + if (p_decimals > 16) { p_decimals = 16; + } char fmt[7]; fmt[0] = '%'; fmt[1] = '.'; if (p_decimals < 0) { - fmt[1] = 'l'; fmt[2] = 'f'; fmt[3] = 0; @@ -1082,28 +1350,24 @@ String String::num(double p_num, int p_decimals) { buf[255] = 0; //destroy trailing zeroes { - bool period = false; int z = 0; while (buf[z]) { - if (buf[z] == '.') + if (buf[z] == '.') { period = true; + } z++; } if (period) { z--; while (z > 0) { - if (buf[z] == '0') { - buf[z] = 0; } else if (buf[z] == '.') { - buf[z] = 0; break; } else { - break; } @@ -1126,8 +1390,7 @@ String String::num(double p_num, int p_decimals) { /* decimal part */ if (p_decimals > 0 || (p_decimals == -1 && (int)p_num != p_num)) { - - double dec = p_num - (float)((int)p_num); + double dec = p_num - (double)((int)p_num); int digit = 0; if (p_decimals > MAX_DIGITS) @@ -1137,18 +1400,16 @@ String String::num(double p_num, int p_decimals) { int dec_max = 0; while (true) { - dec *= 10.0; dec_int = dec_int * 10 + (int)dec % 10; dec_max = dec_max * 10 + 9; digit++; if (p_decimals == -1) { - if (digit == MAX_DIGITS) //no point in going to infinite break; - if ((dec - (float)((int)dec)) < 1e-6) + if ((dec - (double)((int)dec)) < 1e-6) break; } @@ -1160,18 +1421,15 @@ String String::num(double p_num, int p_decimals) { if (last > 5) { if (dec_int == dec_max) { - dec_int = 0; intn++; } else { - dec_int++; } } String decimal; for (int i = 0; i < digit; i++) { - char num[2] = { 0, 0 }; num[0] = '0' + dec_int % 10; decimal = num + decimal; @@ -1185,8 +1443,7 @@ String String::num(double p_num, int p_decimals) { s = "0"; else { while (intn) { - - CharType num = '0' + (intn % 10); + char32_t num = '0' + (intn % 10); intn /= 10; s = num + s; } @@ -1200,7 +1457,6 @@ String String::num(double p_num, int p_decimals) { } String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { - bool sign = p_num < 0; int64_t n = p_num; @@ -1211,11 +1467,12 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { chars++; } while (n); - if (sign) + if (sign) { chars++; + } String s; s.resize(chars + 1); - CharType *c = s.ptrw(); + char32_t *c = s.ptrw(); c[chars] = 0; n = p_num; do { @@ -1230,14 +1487,14 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { n /= base; } while (n); - if (sign) + if (sign) { c[0] = '-'; + } return s; } String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { - uint64_t n = p_num; int chars = 0; @@ -1248,7 +1505,7 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { String s; s.resize(chars + 1); - CharType *c = s.ptrw(); + char32_t *c = s.ptrw(); c[chars] = 0; n = p_num; do { @@ -1267,6 +1524,17 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { } String String::num_real(double p_num) { + if (Math::is_nan(p_num)) { + return "nan"; + } + + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } String s; String sd; @@ -1279,8 +1547,7 @@ String String::num_real(double p_num) { /* decimal part */ if ((int)p_num != p_num) { - - double dec = p_num - (float)((int)p_num); + double dec = p_num - (double)((int)p_num); int digit = 0; int decimals = MAX_DIGITS; @@ -1289,17 +1556,18 @@ String String::num_real(double p_num) { int dec_max = 0; while (true) { - dec *= 10.0; dec_int = dec_int * 10 + (int)dec % 10; dec_max = dec_max * 10 + 9; digit++; - if ((dec - (float)((int)dec)) < 1e-6) + if ((dec - (double)((int)dec)) < 1e-6) { break; + } - if (digit == decimals) + if (digit == decimals) { break; + } } dec *= 10; @@ -1307,18 +1575,15 @@ String String::num_real(double p_num) { if (last > 5) { if (dec_int == dec_max) { - dec_int = 0; intn++; } else { - dec_int++; } } String decimal; for (int i = 0; i < digit; i++) { - char num[2] = { 0, 0 }; num[0] = '0' + dec_int % 10; decimal = num + decimal; @@ -1329,26 +1594,35 @@ String String::num_real(double p_num) { sd = ".0"; } - if (intn == 0) - + if (intn == 0) { s = "0"; - else { + } else { while (intn) { - - CharType num = '0' + (intn % 10); + char32_t num = '0' + (intn % 10); intn /= 10; s = num + s; } } s = s + sd; - if (neg) + if (neg) { s = "-" + s; + } return s; } String String::num_scientific(double p_num) { + if (Math::is_nan(p_num)) { + return "nan"; + } + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } #ifndef NO_USE_STDLIB char buf[256]; @@ -1378,34 +1652,60 @@ String String::num_scientific(double p_num) { #endif } -CharString String::ascii(bool p_allow_extended) const { +String String::md5(const uint8_t *p_md5) { + return String::hex_encode_buffer(p_md5, 16); +} + +String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + String ret; + char v[2] = { 0, 0 }; - if (!length()) + for (int i = 0; i < p_len; i++) { + v[0] = hex[p_buffer[i] >> 4]; + ret += v; + v[0] = hex[p_buffer[i] & 0xF]; + ret += v; + } + + return ret; +} + +CharString String::ascii(bool p_allow_extended) const { + if (!length()) { return CharString(); + } CharString cs; cs.resize(size()); - for (int i = 0; i < size(); i++) - cs[i] = operator[](i); + for (int i = 0; i < size(); i++) { + char32_t c = operator[](i); + if ((c <= 0x7f) || (c <= 0xff && p_allow_extended)) { + cs[i] = c; + } else { + print_error("Unicode parsing error: Cannot represent " + num_int64(c, 16) + " as ASCII/Latin-1 character."); + cs[i] = 0x20; + } + } return cs; } String String::utf8(const char *p_utf8, int p_len) { - String ret; ret.parse_utf8(p_utf8, p_len); return ret; -}; +} bool String::parse_utf8(const char *p_utf8, int p_len) { +#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?"); -#define _UNICERROR(m_err) print_line("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?"); - - if (!p_utf8) + if (!p_utf8) { return true; + } String aux; @@ -1414,13 +1714,12 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { /* HANDLE BOM (Byte Order Mark) */ if (p_len < 0 || p_len >= 3) { - - bool has_bom = uint8_t(p_utf8[0]) == 0xEF && uint8_t(p_utf8[1]) == 0xBB && uint8_t(p_utf8[2]) == 0xBF; + bool has_bom = uint8_t(p_utf8[0]) == 0xef && uint8_t(p_utf8[1]) == 0xbb && uint8_t(p_utf8[2]) == 0xbf; if (has_bom) { - - //just skip it - if (p_len >= 0) + //8-bit encoding, byte order has no meaning in UTF-8, just skip it + if (p_len >= 0) { p_len -= 3; + } p_utf8 += 3; } } @@ -1430,39 +1729,31 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { const char *ptrtmp_limit = &p_utf8[p_len]; int skip = 0; while (ptrtmp != ptrtmp_limit && *ptrtmp) { - if (skip == 0) { - uint8_t c = *ptrtmp >= 0 ? *ptrtmp : uint8_t(256 + *ptrtmp); /* Determine the number of characters in sequence */ - if ((c & 0x80) == 0) + if ((c & 0x80) == 0) { skip = 0; - else if ((c & 0xE0) == 0xC0) + } else if ((c & 0xe0) == 0xc0) { skip = 1; - else if ((c & 0xF0) == 0xE0) + } else if ((c & 0xf0) == 0xe0) { skip = 2; - else if ((c & 0xF8) == 0xF0) + } else if ((c & 0xf8) == 0xf0) { skip = 3; - else if ((c & 0xFC) == 0xF8) - skip = 4; - else if ((c & 0xFE) == 0xFC) - skip = 5; - else { - _UNICERROR("invalid skip"); + } else { + _UNICERROR("invalid skip at " + num_int64(cstr_size)); return true; //invalid utf8 } - if (skip == 1 && (c & 0x1E) == 0) { - //printf("overlong rejected\n"); - _UNICERROR("overlong rejected"); + if (skip == 1 && (c & 0x1e) == 0) { + _UNICERROR("overlong rejected at " + num_int64(cstr_size)); return true; //reject overlong } str_size++; } else { - --skip; } @@ -1482,29 +1773,23 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } resize(str_size + 1); - CharType *dst = ptrw(); + char32_t *dst = ptrw(); dst[str_size] = 0; while (cstr_size) { - int len = 0; /* Determine the number of characters in sequence */ - if ((*p_utf8 & 0x80) == 0) + if ((*p_utf8 & 0x80) == 0) { len = 1; - else if ((*p_utf8 & 0xE0) == 0xC0) + } else if ((*p_utf8 & 0xe0) == 0xc0) { len = 2; - else if ((*p_utf8 & 0xF0) == 0xE0) + } else if ((*p_utf8 & 0xf0) == 0xe0) { len = 3; - else if ((*p_utf8 & 0xF8) == 0xF0) + } else if ((*p_utf8 & 0xf8) == 0xf0) { len = 4; - else if ((*p_utf8 & 0xFC) == 0xF8) - len = 5; - else if ((*p_utf8 & 0xFE) == 0xFC) - len = 6; - else { + } else { _UNICERROR("invalid len"); - return true; //invalid UTF8 } @@ -1514,7 +1799,6 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } if (len == 2 && (*p_utf8 & 0x1E) == 0) { - //printf("overlong rejected\n"); _UNICERROR("no space left"); return true; //reject overlong } @@ -1523,29 +1807,26 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { uint32_t unichar = 0; - if (len == 1) + if (len == 1) { unichar = *p_utf8; - else { - - unichar = (0xFF >> (len + 1)) & *p_utf8; + } else { + unichar = (0xff >> (len + 1)) & *p_utf8; for (int i = 1; i < len; i++) { - - if ((p_utf8[i] & 0xC0) != 0x80) { + if ((p_utf8[i] & 0xc0) != 0x80) { _UNICERROR("invalid utf8"); return true; //invalid utf8 } - if (unichar == 0 && i == 2 && ((p_utf8[i] & 0x7F) >> (7 - len)) == 0) { + if (unichar == 0 && i == 2 && ((p_utf8[i] & 0x7f) >> (7 - len)) == 0) { _UNICERROR("invalid utf8 overlong"); return true; //no overlong } - unichar = (unichar << 6) | (p_utf8[i] & 0x3F); + unichar = (unichar << 6) | (p_utf8[i] & 0x3f); } } - - //printf("char %i, len %i\n",unichar,len); - if (sizeof(wchar_t) == 2 && unichar > 0xFFFF) { - unichar = ' '; //too long for windows + if (unichar >= 0xd800 && unichar <= 0xdfff) { + _UNICERROR("invalid code point"); + return CharString(); } *(dst++) = unichar; @@ -1554,32 +1835,34 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } return false; +#undef _UNICERROR } CharString String::utf8() const { - int l = length(); - if (!l) + if (!l) { return CharString(); + } - const CharType *d = &operator[](0); + const char32_t *d = &operator[](0); int fl = 0; for (int i = 0; i < l; i++) { - uint32_t c = d[i]; - if (c <= 0x7f) // 7 bits. + if (c <= 0x7f) { // 7 bits. fl += 1; - else if (c <= 0x7ff) { // 11 bits + } else if (c <= 0x7ff) { // 11 bits fl += 2; } else if (c <= 0xffff) { // 16 bits fl += 3; - } else if (c <= 0x001fffff) { // 21 bits + } else if (c <= 0x0010ffff) { // 21 bits fl += 4; - - } else if (c <= 0x03ffffff) { // 26 bits - fl += 5; - } else if (c <= 0x7fffffff) { // 31 bits - fl += 6; + } else { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(c, 16) + "."); + return CharString(); + } + if (c >= 0xd800 && c <= 0xdfff) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(c, 16) + "."); + return CharString(); } } @@ -1594,41 +1877,22 @@ CharString String::utf8() const { #define APPEND_CHAR(m_c) *(cdst++) = m_c for (int i = 0; i < l; i++) { - uint32_t c = d[i]; - if (c <= 0x7f) // 7 bits. + if (c <= 0x7f) { // 7 bits. APPEND_CHAR(c); - else if (c <= 0x7ff) { // 11 bits - + } else if (c <= 0x7ff) { // 11 bits APPEND_CHAR(uint32_t(0xc0 | ((c >> 6) & 0x1f))); // Top 5 bits. APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. } else if (c <= 0xffff) { // 16 bits - APPEND_CHAR(uint32_t(0xe0 | ((c >> 12) & 0x0f))); // Top 4 bits. APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Middle 6 bits. APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. - } else if (c <= 0x001fffff) { // 21 bits - + } else { // 21 bits APPEND_CHAR(uint32_t(0xf0 | ((c >> 18) & 0x07))); // Top 3 bits. APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // Upper middle 6 bits. APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower middle 6 bits. APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. - } else if (c <= 0x03ffffff) { // 26 bits - - APPEND_CHAR(uint32_t(0xf8 | ((c >> 24) & 0x03))); // Top 2 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 18) & 0x3f))); // Upper middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. - } else if (c <= 0x7fffffff) { // 31 bits - - APPEND_CHAR(uint32_t(0xfc | ((c >> 30) & 0x01))); // Top 1 bit. - APPEND_CHAR(uint32_t(0x80 | ((c >> 24) & 0x3f))); // Upper upper middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 18) & 0x3f))); // Lower upper middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // Upper lower middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower lower middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. } } #undef APPEND_CHAR @@ -1637,80 +1901,207 @@ CharString String::utf8() const { return utf8s; } -/* -String::String(CharType p_char) { +String String::utf16(const char16_t *p_utf16, int p_len) { + String ret; + ret.parse_utf16(p_utf16, p_len); - shared=nullptr; - copy_from(p_char); + return ret; } -*/ -String::String(const char *p_str) { +bool String::parse_utf16(const char16_t *p_utf16, int p_len) { +#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?"); - copy_from(p_str); -} + if (!p_utf16) { + return true; + } -String::String(const CharType *p_str, int p_clip_to_len) { + String aux; - copy_from(p_str, p_clip_to_len); -} + int cstr_size = 0; + int str_size = 0; -String::String(const StrRange &p_range) { + /* HANDLE BOM (Byte Order Mark) */ + bool byteswap = false; // assume correct endianness if no BOM found + if (p_len < 0 || p_len >= 1) { + bool has_bom = false; + if (uint16_t(p_utf16[0]) == 0xfeff) { // correct BOM, read as is + has_bom = true; + byteswap = false; + } else if (uint16_t(p_utf16[0]) == 0xfffe) { // backwards BOM, swap bytes + has_bom = true; + byteswap = true; + } + if (has_bom) { + if (p_len >= 0) { + p_len -= 1; + } + p_utf16 += 1; + } + } - if (!p_range.c_str) - return; + { + const char16_t *ptrtmp = p_utf16; + const char16_t *ptrtmp_limit = &p_utf16[p_len]; + int skip = 0; + while (ptrtmp != ptrtmp_limit && *ptrtmp) { + uint32_t c = (byteswap) ? BSWAP16(*ptrtmp) : *ptrtmp; + if (skip == 0) { + if ((c & 0xfffffc00) == 0xd800) { + skip = 1; // lead surrogate + } else if ((c & 0xfffffc00) == 0xdc00) { + _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); + return true; // invalid UTF16 + } else { + skip = 0; + } + str_size++; + } else { + if ((c & 0xfffffc00) == 0xdc00) { // trail surrogate + --skip; + } else { + _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); + return true; // invalid UTF16 + } + } - copy_from(p_range.c_str, p_range.len); -} + cstr_size++; + ptrtmp++; + } -int String::hex_to_int(bool p_with_prefix) const { + if (skip) { + _UNICERROR("no space left"); + return true; // not enough space + } + } - if (p_with_prefix && length() < 3) - return 0; + if (str_size == 0) { + clear(); + return false; + } - const CharType *s = ptr(); + resize(str_size + 1); + char32_t *dst = ptrw(); + dst[str_size] = 0; - int sign = s[0] == '-' ? -1 : 1; + while (cstr_size) { + int len = 0; + uint32_t c = (byteswap) ? BSWAP16(*p_utf16) : *p_utf16; - if (sign < 0) { - s++; - } + if ((c & 0xfffffc00) == 0xd800) { + len = 2; + } else { + len = 1; + } - if (p_with_prefix) { - if (s[0] != '0' || s[1] != 'x') - return 0; - s += 2; + if (len > cstr_size) { + _UNICERROR("no space left"); + return true; //not enough space + } + + uint32_t unichar = 0; + if (len == 1) { + unichar = c; + } else { + uint32_t c2 = (byteswap) ? BSWAP16(p_utf16[1]) : p_utf16[1]; + unichar = (c << 10UL) + c2 - ((0xd800 << 10UL) + 0xdc00 - 0x10000); + } + + *(dst++) = unichar; + cstr_size -= len; + p_utf16 += len; } - int hex = 0; + return false; +#undef _UNICERROR +} - while (*s) { +Char16String String::utf16() const { + int l = length(); + if (!l) { + return Char16String(); + } - CharType c = LOWERCASE(*s); - int n; - if (c >= '0' && c <= '9') { - n = c - '0'; - } else if (c >= 'a' && c <= 'f') { - n = (c - 'a') + 10; + const char32_t *d = &operator[](0); + int fl = 0; + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + if (c <= 0xffff) { // 16 bits. + fl += 1; + } else if (c <= 0x10ffff) { // 32 bits. + fl += 2; } else { - return 0; + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(c, 16) + "."); + return Char16String(); + } + if (c >= 0xd800 && c <= 0xdfff) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(c, 16) + "."); + return Char16String(); } + } - ERR_FAIL_COND_V_MSG(hex > INT32_MAX / 16, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); - hex *= 16; - hex += n; - s++; + Char16String utf16s; + if (fl == 0) { + return utf16s; } - return hex * sign; + utf16s.resize(fl + 1); + uint16_t *cdst = (uint16_t *)utf16s.get_data(); + +#define APPEND_CHAR(m_c) *(cdst++) = m_c + + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + + if (c <= 0xffff) { // 16 bits. + APPEND_CHAR(c); + } else { // 32 bits. + APPEND_CHAR(uint32_t((c >> 10) + 0xd7c0)); // lead surrogate. + APPEND_CHAR(uint32_t((c & 0x3ff) | 0xdc00)); // trail surrogate. + } + } +#undef APPEND_CHAR + *cdst = 0; //trailing zero + + return utf16s; } -int64_t String::hex_to_int64(bool p_with_prefix) const { +String::String(const char *p_str) { + copy_from(p_str); +} - if (p_with_prefix && length() < 3) +String::String(const wchar_t *p_str) { + copy_from(p_str); +} + +String::String(const char32_t *p_str) { + copy_from(p_str); +} + +String::String(const char *p_str, int p_clip_to_len) { + copy_from(p_str, p_clip_to_len); +} + +String::String(const wchar_t *p_str, int p_clip_to_len) { + copy_from(p_str, p_clip_to_len); +} + +String::String(const char32_t *p_str, int p_clip_to_len) { + copy_from(p_str, p_clip_to_len); +} + +String::String(const StrRange &p_range) { + if (!p_range.c_str) { + return; + } + copy_from(p_range.c_str, p_range.len); +} + +int64_t String::hex_to_int(bool p_with_prefix) const { + if (p_with_prefix && length() < 3) { return 0; + } - const CharType *s = ptr(); + const char32_t *s = ptr(); int64_t sign = s[0] == '-' ? -1 : 1; @@ -1719,16 +2110,16 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { } if (p_with_prefix) { - if (s[0] != '0' || s[1] != 'x') + if (s[0] != '0' || s[1] != 'x') { return 0; + } s += 2; } int64_t hex = 0; while (*s) { - - CharType c = LOWERCASE(*s); + char32_t c = LOWERCASE(*s); int64_t n; if (c >= '0' && c <= '9') { n = c - '0'; @@ -1737,8 +2128,9 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { } else { return 0; } - - ERR_FAIL_COND_V_MSG(hex > INT64_MAX / 16, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error + bool overflow = ((hex > INT64_MAX / 16) && (sign == 1 || (sign == -1 && hex != (INT64_MAX >> 4) + 1))) || (sign == -1 && hex == (INT64_MAX >> 4) + 1 && c > '0'); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); hex *= 16; hex += n; s++; @@ -1747,12 +2139,12 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { return hex * sign; } -int64_t String::bin_to_int64(bool p_with_prefix) const { - - if (p_with_prefix && length() < 3) +int64_t String::bin_to_int(bool p_with_prefix) const { + if (p_with_prefix && length() < 3) { return 0; + } - const CharType *s = ptr(); + const char32_t *s = ptr(); int64_t sign = s[0] == '-' ? -1 : 1; @@ -1761,24 +2153,25 @@ int64_t String::bin_to_int64(bool p_with_prefix) const { } if (p_with_prefix) { - if (s[0] != '0' || s[1] != 'b') + if (s[0] != '0' || s[1] != 'b') { return 0; + } s += 2; } int64_t binary = 0; while (*s) { - - CharType c = LOWERCASE(*s); + char32_t c = LOWERCASE(*s); int64_t n; if (c == '0' || c == '1') { n = c - '0'; } else { return 0; } - - ERR_FAIL_COND_V_MSG(binary > INT64_MAX / 2, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error + bool overflow = ((binary > INT64_MAX / 2) && (sign == 1 || (sign == -1 && binary != (INT64_MAX >> 1) + 1))) || (sign == -1 && binary == (INT64_MAX >> 1) + 1 && c > '0'); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); binary *= 2; binary += n; s++; @@ -1787,27 +2180,25 @@ int64_t String::bin_to_int64(bool p_with_prefix) const { return binary * sign; } -int String::to_int() const { - - if (length() == 0) +int64_t String::to_int() const { + if (length() == 0) { return 0; + } int to = (find(".") >= 0) ? find(".") : length(); - int integer = 0; - int sign = 1; + int64_t integer = 0; + int64_t sign = 1; for (int i = 0; i < to; i++) { - - CharType c = operator[](i); + char32_t c = operator[](i); if (c >= '0' && c <= '9') { - - ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; } else if (integer == 0 && c == '-') { - sign = -sign; } } @@ -1815,91 +2206,92 @@ int String::to_int() const { return integer * sign; } -int64_t String::to_int64() const { - - if (length() == 0) - return 0; - - int to = (find(".") >= 0) ? find(".") : length(); +int64_t String::to_int(const char *p_str, int p_len) { + int to = 0; + if (p_len >= 0) { + to = p_len; + } else { + while (p_str[to] != 0 && p_str[to] != '.') { + to++; + } + } int64_t integer = 0; int64_t sign = 1; for (int i = 0; i < to; i++) { - - CharType c = operator[](i); + char c = p_str[i]; if (c >= '0' && c <= '9') { - - ERR_FAIL_COND_V_MSG(integer > INT64_MAX / 10, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; - } else if (integer == 0 && c == '-') { - + } else if (c == '-' && integer == 0) { sign = -sign; + } else if (c != ' ') { + break; } } return integer * sign; } -int String::to_int(const char *p_str, int p_len) { - +int64_t String::to_int(const wchar_t *p_str, int p_len) { int to = 0; - if (p_len >= 0) + if (p_len >= 0) { to = p_len; - else { - while (p_str[to] != 0 && p_str[to] != '.') + } else { + while (p_str[to] != 0 && p_str[to] != '.') { to++; + } } - int integer = 0; - int sign = 1; + int64_t integer = 0; + int64_t sign = 1; for (int i = 0; i < to; i++) { - - char c = p_str[i]; + wchar_t c = p_str[i]; if (c >= '0' && c <= '9') { - - ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; } else if (c == '-' && integer == 0) { - sign = -sign; - } else if (c != ' ') + } else if (c != ' ') { break; + } } return integer * sign; } bool String::is_numeric() const { - if (length() == 0) { return false; - }; + } int s = 0; - if (operator[](0) == '-') ++s; + if (operator[](0) == '-') { + ++s; + } bool dot = false; for (int i = s; i < length(); i++) { - - CharType c = operator[](i); + char32_t c = operator[](i); if (c == '.') { if (dot) { return false; - }; + } dot = true; - } - if (c < '0' || c > '9') { + } else if (c < '0' || c > '9') { return false; - }; - }; + } + } return true; // TODO: Use the parser below for this instead -}; +} template <class C> static double built_in_strtod(const C *string, /* A decimal ASCII floating-point number, @@ -1916,7 +2308,6 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point C **endPtr = nullptr) /* If non-nullptr, store terminating Cacter's * address here. */ { - static const int maxExponent = 511; /* Largest possible base 10 exponent. Any * exponent larger than this will already * produce underflow or overflow, so there's @@ -2056,11 +2447,11 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point } expSign = false; } - if (!IS_DIGIT(CharType(*p))) { + if (!IS_DIGIT(char32_t(*p))) { p = pExp; goto done; } - while (IS_DIGIT(CharType(*p))) { + while (IS_DIGIT(char32_t(*p))) { exp = exp * 10 + (*p - '0'); p += 1; } @@ -2117,42 +2508,33 @@ done: #define READING_EXP 3 #define READING_DONE 4 -double String::to_double(const char *p_str) { - -#ifndef NO_USE_STDLIB - return built_in_strtod<char>(p_str); -//return atof(p_str); DOES NOT WORK ON ANDROID(??) -#else +double String::to_float(const char *p_str) { return built_in_strtod<char>(p_str); -#endif } -float String::to_float() const { - - return to_double(); +double String::to_float(const char32_t *p_str, const char32_t **r_end) { + return built_in_strtod<char32_t>(p_str, (char32_t **)r_end); } -double String::to_double(const CharType *p_str, const CharType **r_end) { - - return built_in_strtod<CharType>(p_str, (CharType **)r_end); +double String::to_float(const wchar_t *p_str, const wchar_t **r_end) { + return built_in_strtod<wchar_t>(p_str, (wchar_t **)r_end); } -int64_t String::to_int(const CharType *p_str, int p_len) { - - if (p_len == 0 || !p_str[0]) +int64_t String::to_int(const char32_t *p_str, int p_len, bool p_clamp) { + if (p_len == 0 || !p_str[0]) { return 0; + } ///@todo make more exact so saving and loading does not lose precision int64_t integer = 0; int64_t sign = 1; int reading = READING_SIGN; - const CharType *str = p_str; - const CharType *limit = &p_str[p_len]; + const char32_t *str = p_str; + const char32_t *limit = &p_str[p_len]; while (*str && reading != READING_DONE && str != limit) { - - CharType c = *(str++); + char32_t c = *(str++); switch (reading) { case READING_SIGN: { if (c >= '0' && c <= '9') { @@ -2172,16 +2554,22 @@ int64_t String::to_int(const CharType *p_str, int p_len) { [[fallthrough]]; } case READING_INT: { - if (c >= '0' && c <= '9') { - if (integer > INT64_MAX / 10) { String number(""); str = p_str; while (*str && str != limit) { number += *(str++); } - ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + if (p_clamp) { + if (sign == 1) { + return INT64_MAX; + } else { + return INT64_MIN; + } + } else { + ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + } } integer *= 10; integer += c - '0'; @@ -2196,104 +2584,102 @@ int64_t String::to_int(const CharType *p_str, int p_len) { return sign * integer; } -double String::to_double() const { - - if (empty()) +double String::to_float() const { + if (is_empty()) { return 0; -#ifndef NO_USE_STDLIB - return built_in_strtod<CharType>(c_str()); -//return wcstod(c_str(),nullptr ); DOES NOT WORK ON ANDROID :( -#else - return built_in_strtod<CharType>(c_str()); -#endif -} - -bool operator==(const char *p_chr, const String &p_str) { - - return p_str == p_chr; -} - -String operator+(const char *p_chr, const String &p_str) { - - String tmp = p_chr; - tmp += p_str; - return tmp; -} -String operator+(CharType p_chr, const String &p_str) { - - return (String::chr(p_chr) + p_str); + } + return built_in_strtod<char32_t>(get_data()); } uint32_t String::hash(const char *p_cstr) { - uint32_t hashv = 5381; uint32_t c; - while ((c = *p_cstr++)) + while ((c = *p_cstr++)) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } return hashv; } uint32_t String::hash(const char *p_cstr, int p_len) { + uint32_t hashv = 5381; + for (int i = 0; i < p_len; i++) { + hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */ + } + return hashv; +} + +uint32_t String::hash(const wchar_t *p_cstr, int p_len) { uint32_t hashv = 5381; - for (int i = 0; i < p_len; i++) + for (int i = 0; i < p_len; i++) { hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */ + } return hashv; } -uint32_t String::hash(const CharType *p_cstr, int p_len) { +uint32_t String::hash(const wchar_t *p_cstr) { + uint32_t hashv = 5381; + uint32_t c; + while ((c = *p_cstr++)) { + hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash(const char32_t *p_cstr, int p_len) { uint32_t hashv = 5381; - for (int i = 0; i < p_len; i++) + for (int i = 0; i < p_len; i++) { hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */ + } return hashv; } -uint32_t String::hash(const CharType *p_cstr) { - +uint32_t String::hash(const char32_t *p_cstr) { uint32_t hashv = 5381; uint32_t c; - while ((c = *p_cstr++)) + while ((c = *p_cstr++)) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } return hashv; } uint32_t String::hash() const { - /* simple djb2 hashing */ - const CharType *chr = c_str(); + const char32_t *chr = get_data(); uint32_t hashv = 5381; uint32_t c; - while ((c = *chr++)) + while ((c = *chr++)) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } return hashv; } uint64_t String::hash64() const { - /* simple djb2 hashing */ - const CharType *chr = c_str(); + const char32_t *chr = get_data(); uint64_t hashv = 5381; uint64_t c; - while ((c = *chr++)) + while ((c = *chr++)) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } return hashv; } String String::md5_text() const { - CharString cs = utf8(); unsigned char hash[16]; CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash); @@ -2315,7 +2701,6 @@ String String::sha256_text() const { } Vector<uint8_t> String::md5_buffer() const { - CharString cs = utf8(); unsigned char hash[16]; CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash); @@ -2326,7 +2711,7 @@ Vector<uint8_t> String::md5_buffer() const { ret.write[i] = hash[i]; } return ret; -}; +} Vector<uint8_t> String::sha1_buffer() const { CharString cs = utf8(); @@ -2356,87 +2741,74 @@ Vector<uint8_t> String::sha256_buffer() const { } String String::insert(int p_at_pos, const String &p_string) const { - - if (p_at_pos < 0) + if (p_at_pos < 0) { return *this; + } - if (p_at_pos > length()) + if (p_at_pos > length()) { p_at_pos = length(); + } String pre; - if (p_at_pos > 0) + if (p_at_pos > 0) { pre = substr(0, p_at_pos); + } String post; - if (p_at_pos < length()) + if (p_at_pos < length()) { post = substr(p_at_pos, length() - p_at_pos); + } return pre + p_string + post; } -String String::substr(int p_from, int p_chars) const { - if (p_chars == -1) +String String::substr(int p_from, int p_chars) const { + if (p_chars == -1) { p_chars = length() - p_from; + } - if (empty() || p_from < 0 || p_from >= length() || p_chars <= 0) + if (is_empty() || p_from < 0 || p_from >= length() || p_chars <= 0) { return ""; + } if ((p_from + p_chars) > length()) { - p_chars = length() - p_from; } if (p_from == 0 && p_chars >= length()) { - return String(*this); } String s = String(); - s.copy_from_unchecked(&c_str()[p_from], p_chars); + s.copy_from_unchecked(&get_data()[p_from], p_chars); return s; } -int String::find_last(const String &p_str) const { - - int pos = -1; - int findfrom = 0; - int findres = -1; - while ((findres = find(p_str, findfrom)) != -1) { - - pos = findres; - findfrom = pos + 1; - } - - return pos; -} - int String::find(const String &p_str, int p_from) const { - - if (p_from < 0) + if (p_from < 0) { return -1; + } const int src_len = p_str.length(); const int len = length(); - if (src_len == 0 || len == 0) + if (src_len == 0 || len == 0) { return -1; // won't find anything! + } - const CharType *src = c_str(); - const CharType *str = p_str.c_str(); + const char32_t *src = get_data(); + const char32_t *str = p_str.get_data(); for (int i = p_from; i <= (len - src_len); i++) { - bool found = true; for (int j = 0; j < src_len; j++) { - int read_pos = i + j; if (read_pos >= len) { - ERR_PRINT("read_pos>=len"); return -1; - }; + } if (src[read_pos] != str[j]) { found = false; @@ -2444,154 +2816,151 @@ int String::find(const String &p_str, int p_from) const { } } - if (found) + if (found) { return i; + } } return -1; } int String::find(const char *p_str, int p_from) const { - - if (p_from < 0) + if (p_from < 0) { return -1; + } const int len = length(); - if (len == 0) + if (len == 0) { return -1; // won't find anything! + } - const CharType *src = c_str(); + const char32_t *src = get_data(); int src_len = 0; - while (p_str[src_len] != '\0') + while (p_str[src_len] != '\0') { src_len++; + } if (src_len == 1) { - - const char needle = p_str[0]; + const char32_t needle = p_str[0]; for (int i = p_from; i < len; i++) { - if (src[i] == needle) { return i; } } } else { - for (int i = p_from; i <= (len - src_len); i++) { - bool found = true; for (int j = 0; j < src_len; j++) { - int read_pos = i + j; if (read_pos >= len) { - ERR_PRINT("read_pos>=len"); return -1; - }; + } - if (src[read_pos] != p_str[j]) { + if (src[read_pos] != (char32_t)p_str[j]) { found = false; break; } } - if (found) + if (found) { return i; + } } } return -1; } -int String::find_char(const CharType &p_char, int p_from) const { +int String::find_char(const char32_t &p_char, int p_from) const { return _cowdata.find(p_char, p_from); } int String::findmk(const Vector<String> &p_keys, int p_from, int *r_key) const { - - if (p_from < 0) + if (p_from < 0) { return -1; - if (p_keys.size() == 0) + } + if (p_keys.size() == 0) { return -1; + } //int src_len=p_str.length(); const String *keys = &p_keys[0]; int key_count = p_keys.size(); int len = length(); - if (len == 0) + if (len == 0) { return -1; // won't find anything! + } - const CharType *src = c_str(); + const char32_t *src = get_data(); for (int i = p_from; i < len; i++) { - bool found = true; for (int k = 0; k < key_count; k++) { - found = true; - if (r_key) + if (r_key) { *r_key = k; - const CharType *cmp = keys[k].c_str(); + } + const char32_t *cmp = keys[k].get_data(); int l = keys[k].length(); for (int j = 0; j < l; j++) { - int read_pos = i + j; if (read_pos >= len) { - found = false; break; - }; + } if (src[read_pos] != cmp[j]) { found = false; break; } } - if (found) + if (found) { break; + } } - if (found) + if (found) { return i; + } } return -1; } int String::findn(const String &p_str, int p_from) const { - - if (p_from < 0) + if (p_from < 0) { return -1; + } int src_len = p_str.length(); - if (src_len == 0 || length() == 0) + if (src_len == 0 || length() == 0) { return -1; // won't find anything! + } - const CharType *srcd = c_str(); + const char32_t *srcd = get_data(); for (int i = p_from; i <= (length() - src_len); i++) { - bool found = true; for (int j = 0; j < src_len; j++) { - int read_pos = i + j; if (read_pos >= length()) { - ERR_PRINT("read_pos>=length()"); return -1; - }; + } - CharType src = _find_lower(srcd[read_pos]); - CharType dst = _find_lower(p_str[j]); + char32_t src = _find_lower(srcd[read_pos]); + char32_t dst = _find_lower(p_str[j]); if (src != dst) { found = false; @@ -2599,46 +2968,46 @@ int String::findn(const String &p_str, int p_from) const { } } - if (found) + if (found) { return i; + } } return -1; } int String::rfind(const String &p_str, int p_from) const { - // establish a limit int limit = length() - p_str.length(); - if (limit < 0) + if (limit < 0) { return -1; + } // establish a starting point - if (p_from < 0) + if (p_from < 0) { p_from = limit; - else if (p_from > limit) + } else if (p_from > limit) { p_from = limit; + } int src_len = p_str.length(); int len = length(); - if (src_len == 0 || len == 0) + if (src_len == 0 || len == 0) { return -1; // won't find anything! + } - const CharType *src = c_str(); + const char32_t *src = get_data(); for (int i = p_from; i >= 0; i--) { - bool found = true; for (int j = 0; j < src_len; j++) { - int read_pos = i + j; if (read_pos >= len) { - ERR_PRINT("read_pos>=len"); return -1; - }; + } if (src[read_pos] != p_str[j]) { found = false; @@ -2646,48 +3015,49 @@ int String::rfind(const String &p_str, int p_from) const { } } - if (found) + if (found) { return i; + } } return -1; } -int String::rfindn(const String &p_str, int p_from) const { +int String::rfindn(const String &p_str, int p_from) const { // establish a limit int limit = length() - p_str.length(); - if (limit < 0) + if (limit < 0) { return -1; + } // establish a starting point - if (p_from < 0) + if (p_from < 0) { p_from = limit; - else if (p_from > limit) + } else if (p_from > limit) { p_from = limit; + } int src_len = p_str.length(); int len = length(); - if (src_len == 0 || len == 0) + if (src_len == 0 || len == 0) { return -1; // won't find anything! + } - const CharType *src = c_str(); + const char32_t *src = get_data(); for (int i = p_from; i >= 0; i--) { - bool found = true; for (int j = 0; j < src_len; j++) { - int read_pos = i + j; if (read_pos >= len) { - ERR_PRINT("read_pos>=len"); return -1; - }; + } - CharType srcc = _find_lower(src[read_pos]); - CharType dstc = _find_lower(p_str[j]); + char32_t srcc = _find_lower(src[read_pos]); + char32_t dstc = _find_lower(p_str[j]); if (srcc != dstc) { found = false; @@ -2695,56 +3065,59 @@ int String::rfindn(const String &p_str, int p_from) const { } } - if (found) + if (found) { return i; + } } return -1; } bool String::ends_with(const String &p_string) const { - - int pos = find_last(p_string); - if (pos == -1) + int pos = rfind(p_string); + if (pos == -1) { return false; + } return pos + p_string.length() == length(); } bool String::begins_with(const String &p_string) const { - - if (p_string.length() > length()) + if (p_string.length() > length()) { return false; + } int l = p_string.length(); - if (l == 0) + if (l == 0) { return true; + } - const CharType *src = &p_string[0]; - const CharType *str = &operator[](0); + const char32_t *src = &p_string[0]; + const char32_t *str = &operator[](0); int i = 0; for (; i < l; i++) { - - if (src[i] != str[i]) + if (src[i] != str[i]) { return false; + } } // only if i == l the p_string matches the beginning return i == l; } -bool String::begins_with(const char *p_string) const { +bool String::begins_with(const char *p_string) const { int l = length(); - if (l == 0 || !p_string) + if (l == 0 || !p_string) { return false; + } - const CharType *str = &operator[](0); + const char32_t *str = &operator[](0); int i = 0; while (*p_string && i < l) { - - if (*p_string != str[i]) + if ((char32_t)*p_string != str[i]) { return false; + } i++; p_string++; } @@ -2753,27 +3126,23 @@ bool String::begins_with(const char *p_string) const { } bool String::is_enclosed_in(const String &p_string) const { - return begins_with(p_string) && ends_with(p_string); } bool String::is_subsequence_of(const String &p_string) const { - return _base_is_subsequence_of(p_string, false); } bool String::is_subsequence_ofi(const String &p_string) const { - return _base_is_subsequence_of(p_string, true); } bool String::is_quoted() const { - return is_enclosed_in("\"") || is_enclosed_in("'"); } int String::_count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const { - if (p_string.empty()) { + if (p_string.is_empty()) { return 0; } int len = length(); @@ -2790,7 +3159,7 @@ int String::_count(const String &p_string, int p_from, int p_to, bool p_case_ins } if (p_from == 0 && p_to == len) { str = String(); - str.copy_from_unchecked(&c_str()[0], len); + str.copy_from_unchecked(&get_data()[0], len); } else { str = substr(p_from, p_to - p_from); } @@ -2818,7 +3187,6 @@ int String::countn(const String &p_string, int p_from, int p_to) const { } bool String::_base_is_subsequence_of(const String &p_string, bool case_insensitive) const { - int len = length(); if (len == 0) { // Technically an empty string is subsequence of any string @@ -2829,14 +3197,14 @@ bool String::_base_is_subsequence_of(const String &p_string, bool case_insensiti return false; } - const CharType *src = &operator[](0); - const CharType *tgt = &p_string[0]; + const char32_t *src = &operator[](0); + const char32_t *tgt = &p_string[0]; for (; *src && *tgt; tgt++) { bool match = false; if (case_insensitive) { - CharType srcc = _find_lower(*src); - CharType tgtc = _find_lower(*tgt); + char32_t srcc = _find_lower(*src); + char32_t tgtc = _find_lower(*tgt); match = srcc == tgtc; } else { match = *src == *tgt; @@ -2882,8 +3250,8 @@ float String::similarity(const String &p_string) const { int src_size = src_bigrams.size(); int tgt_size = tgt_bigrams.size(); - float sum = src_size + tgt_size; - float inter = 0; + double sum = src_size + tgt_size; + double inter = 0; for (int i = 0; i < src_size; i++) { for (int j = 0; j < tgt_size; j++) { if (src_bigrams[i] == tgt_bigrams[j]) { @@ -2896,7 +3264,7 @@ float String::similarity(const String &p_string) const { return (2.0f * inter) / sum; } -static bool _wildcard_match(const CharType *p_pattern, const CharType *p_string, bool p_case_sensitive) { +static bool _wildcard_match(const char32_t *p_pattern, const char32_t *p_string, bool p_case_sensitive) { switch (*p_pattern) { case '\0': return !*p_string; @@ -2911,22 +3279,21 @@ static bool _wildcard_match(const CharType *p_pattern, const CharType *p_string, } bool String::match(const String &p_wildcard) const { - - if (!p_wildcard.length() || !length()) + if (!p_wildcard.length() || !length()) { return false; + } - return _wildcard_match(p_wildcard.c_str(), c_str(), true); + return _wildcard_match(p_wildcard.get_data(), get_data(), true); } bool String::matchn(const String &p_wildcard) const { - - if (!p_wildcard.length() || !length()) + if (!p_wildcard.length() || !length()) { return false; - return _wildcard_match(p_wildcard.c_str(), c_str(), false); + } + return _wildcard_match(p_wildcard.get_data(), get_data(), false); } String String::format(const Variant &values, String placeholder) const { - String new_string = String(this->ptr()); if (values.get_type() == Variant::ARRAY) { @@ -2998,20 +3365,17 @@ String String::format(const Variant &values, String placeholder) const { } String String::replace(const String &p_key, const String &p_with) const { - String new_string; int search_from = 0; int result = 0; while ((result = find(p_key, search_from)) >= 0) { - new_string += substr(search_from, result - search_from); new_string += p_with; search_from = result + p_key.length(); } if (search_from == 0) { - return *this; } @@ -3021,23 +3385,21 @@ String String::replace(const String &p_key, const String &p_with) const { } String String::replace(const char *p_key, const char *p_with) const { - String new_string; int search_from = 0; int result = 0; while ((result = find(p_key, search_from)) >= 0) { - new_string += substr(search_from, result - search_from); new_string += p_with; int k = 0; - while (p_key[k] != '\0') + while (p_key[k] != '\0') { k++; + } search_from = result + k; } if (search_from == 0) { - return *this; } @@ -3047,7 +3409,6 @@ String String::replace(const char *p_key, const char *p_with) const { } String String::replace_first(const String &p_key, const String &p_with) const { - int pos = find(p_key); if (pos >= 0) { return substr(0, pos) + p_with + substr(pos + p_key.length(), length()); @@ -3055,21 +3416,19 @@ String String::replace_first(const String &p_key, const String &p_with) const { return *this; } -String String::replacen(const String &p_key, const String &p_with) const { +String String::replacen(const String &p_key, const String &p_with) const { String new_string; int search_from = 0; int result = 0; while ((result = findn(p_key, search_from)) >= 0) { - new_string += substr(search_from, result - search_from); new_string += p_with; search_from = result + p_key.length(); } if (search_from == 0) { - return *this; } @@ -3078,51 +3437,53 @@ String String::replacen(const String &p_key, const String &p_with) const { } String String::repeat(int p_count) const { - ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); String new_string; - const CharType *src = this->c_str(); + const char32_t *src = this->get_data(); new_string.resize(length() * p_count + 1); + new_string[length() * p_count] = 0; - for (int i = 0; i < p_count; i++) - for (int j = 0; j < length(); j++) + for (int i = 0; i < p_count; i++) { + for (int j = 0; j < length(); j++) { new_string[i * length() + j] = src[j]; + } + } return new_string; } String String::left(int p_pos) const { - - if (p_pos <= 0) + if (p_pos <= 0) { return ""; + } - if (p_pos >= length()) + if (p_pos >= length()) { return *this; + } return substr(0, p_pos); } String String::right(int p_pos) const { - - if (p_pos >= length()) + if (p_pos >= length()) { return ""; + } - if (p_pos <= 0) + if (p_pos <= 0) { return *this; + } return substr(p_pos, (length() - p_pos)); } -CharType String::ord_at(int p_idx) const { - +char32_t String::ord_at(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, length(), 0); return operator[](p_idx); } String String::dedent() const { - String new_string; String indent; bool has_indent = false; @@ -3131,11 +3492,11 @@ String String::dedent() const { int indent_stop = -1; for (int i = 0; i < length(); i++) { - - CharType c = operator[](i); + char32_t c = operator[](i); if (c == '\n') { - if (has_text) + if (has_text) { new_string += substr(indent_stop, i - indent_stop); + } new_string += "\n"; has_text = false; line_start = i + 1; @@ -3151,57 +3512,58 @@ String String::dedent() const { } if (has_indent && indent_stop < 0) { int j = i - line_start; - if (j >= indent.length() || c != indent[j]) + if (j >= indent.length() || c != indent[j]) { indent_stop = i; + } } } } - if (has_text) + if (has_text) { new_string += substr(indent_stop, length() - indent_stop); + } return new_string; } String String::strip_edges(bool left, bool right) const { - int len = length(); int beg = 0, end = len; if (left) { for (int i = 0; i < len; i++) { - - if (operator[](i) <= 32) + if (operator[](i) <= 32) { beg++; - else + } else { break; + } } } if (right) { for (int i = (int)(len - 1); i >= 0; i--) { - - if (operator[](i) <= 32) + if (operator[](i) <= 32) { end--; - else + } else { break; + } } } - if (beg == 0 && end == len) + if (beg == 0 && end == len) { return *this; + } return substr(beg, end - beg); } String String::strip_escapes() const { - String new_string; for (int i = 0; i < length(); i++) { - // Escape characters on first page of the ASCII table, before 32 (Space). - if (operator[](i) < 32) + if (operator[](i) < 32) { continue; + } new_string += operator[](i); } @@ -3209,65 +3571,60 @@ String String::strip_escapes() const { } String String::lstrip(const String &p_chars) const { - int len = length(); int beg; for (beg = 0; beg < len; beg++) { - - if (p_chars.find_char(get(beg)) == -1) + if (p_chars.find_char(get(beg)) == -1) { break; + } } - if (beg == 0) + if (beg == 0) { return *this; + } return substr(beg, len - beg); } String String::rstrip(const String &p_chars) const { - int len = length(); int end; for (end = len - 1; end >= 0; end--) { - - if (p_chars.find_char(get(end)) == -1) + if (p_chars.find_char(get(end)) == -1) { break; + } } - if (end == len - 1) + if (end == len - 1) { return *this; + } return substr(0, end + 1); } String String::simplify_path() const { - String s = *this; String drive; if (s.begins_with("local://")) { drive = "local://"; s = s.substr(8, s.length()); } else if (s.begins_with("res://")) { - drive = "res://"; s = s.substr(6, s.length()); } else if (s.begins_with("user://")) { - drive = "user://"; s = s.substr(7, s.length()); } else if (s.begins_with("/") || s.begins_with("\\")) { - drive = s.substr(0, 1); s = s.substr(1, s.length() - 1); } else { - int p = s.find(":/"); - if (p == -1) + if (p == -1) { p = s.find(":\\"); + } if (p != -1 && p < s.find("/")) { - drive = s.substr(0, p + 2); s = s.substr(p + 2, s.length()); } @@ -3276,21 +3633,20 @@ String String::simplify_path() const { s = s.replace("\\", "/"); while (true) { // in case of using 2 or more slash String compare = s.replace("//", "/"); - if (s == compare) + if (s == compare) { break; - else + } else { s = compare; + } } Vector<String> dirs = s.split("/", false); for (int i = 0; i < dirs.size(); i++) { - String d = dirs[i]; if (d == ".") { dirs.remove(i); i--; } else if (d == "..") { - if (i == 0) { dirs.remove(i); i--; @@ -3305,9 +3661,9 @@ String String::simplify_path() const { s = ""; for (int i = 0; i < dirs.size(); i++) { - - if (i > 0) + if (i > 0) { s += "/"; + } s += dirs[i]; } @@ -3315,17 +3671,16 @@ String String::simplify_path() const { } static int _humanize_digits(int p_num) { - - if (p_num < 100) + if (p_num < 100) { return 2; - else if (p_num < 1024) + } else if (p_num < 1024) { return 1; - else + } else { return 0; + } } String String::humanize_size(uint64_t p_size) { - uint64_t _div = 1; Vector<String> prefixes; prefixes.push_back(RTR("B")); @@ -3348,72 +3703,51 @@ String String::humanize_size(uint64_t p_size) { return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx]; } -bool String::is_abs_path() const { - if (length() > 1) +bool String::is_abs_path() const { + if (length() > 1) { return (operator[](0) == '/' || operator[](0) == '\\' || find(":/") != -1 || find(":\\") != -1); - else if ((length()) == 1) + } else if ((length()) == 1) { return (operator[](0) == '/' || operator[](0) == '\\'); - else + } else { return false; + } } bool String::is_valid_identifier() const { - int len = length(); - if (len == 0) + if (len == 0) { return false; + } - const wchar_t *str = &operator[](0); + const char32_t *str = &operator[](0); for (int i = 0; i < len; i++) { - if (i == 0) { - if (str[0] >= '0' && str[0] <= '9') + if (str[0] >= '0' && str[0] <= '9') { return false; // no start with number plz + } } bool valid_char = (str[i] >= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z') || str[i] == '_'; - if (!valid_char) + if (!valid_char) { return false; + } } return true; } -//kind of poor should be rewritten properly - -String String::word_wrap(int p_chars_per_line) const { - - int from = 0; - int last_space = 0; - String ret; - for (int i = 0; i < length(); i++) { - if (i - from >= p_chars_per_line) { - if (last_space == -1) { - ret += substr(from, i - from + 1) + "\n"; - } else { - ret += substr(from, last_space - from) + "\n"; - i = last_space; //rewind - } - from = i + 1; - last_space = -1; - } else if (operator[](i) == ' ' || operator[](i) == '\t') { - last_space = i; - } else if (operator[](i) == '\n') { - ret += substr(from, i - from) + "\n"; - from = i + 1; - last_space = -1; - } - } - - if (from < length()) { - ret += substr(from, length()); +bool String::is_valid_string() const { + int l = length(); + const char32_t *src = get_data(); + bool valid = true; + for (int i = 0; i < l; i++) { + valid = valid && (src[i] < 0xd800 || (src[i] > 0xdfff && src[i] <= 0x10ffff)); } - - return ret; + return valid; } String String::http_escape() const { @@ -3444,9 +3778,9 @@ String String::http_unescape() const { String res; for (int i = 0; i < length(); ++i) { if (ord_at(i) == '%' && i + 2 < length()) { - CharType ord1 = ord_at(i + 1); + char32_t ord1 = ord_at(i + 1); if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) { - CharType ord2 = ord_at(i + 2); + char32_t ord2 = ord_at(i + 2); if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) { char bytes[3] = { (char)ord1, (char)ord2, 0 }; res += (char)strtol(bytes, nullptr, 16); @@ -3463,7 +3797,6 @@ String String::http_unescape() const { } String String::c_unescape() const { - String escaped = *this; escaped = escaped.replace("\\a", "\a"); escaped = escaped.replace("\\b", "\b"); @@ -3481,7 +3814,6 @@ String String::c_unescape() const { } String String::c_escape() const { - String escaped = *this; escaped = escaped.replace("\\", "\\\\"); escaped = escaped.replace("\a", "\\a"); @@ -3499,7 +3831,6 @@ String String::c_escape() const { } String String::c_escape_multiline() const { - String escaped = *this; escaped = escaped.replace("\\", "\\\\"); escaped = escaped.replace("\"", "\\\""); @@ -3508,7 +3839,6 @@ String String::c_escape_multiline() const { } String String::json_escape() const { - String escaped = *this; escaped = escaped.replace("\\", "\\\\"); escaped = escaped.replace("\b", "\\b"); @@ -3523,7 +3853,6 @@ String String::json_escape() const { } String String::xml_escape(bool p_escape_quotes) const { - String str = *this; str = str.replace("&", "&"); str = str.replace("<", "<"); @@ -3533,31 +3862,25 @@ String String::xml_escape(bool p_escape_quotes) const { str = str.replace("\"", """); } /* - for (int i=1;i<32;i++) { - - char chr[2]={i,0}; - str=str.replace(chr,"&#"+String::num(i)+";"); - }*/ +for (int i=1;i<32;i++) { + char chr[2]={i,0}; + str=str.replace(chr,"&#"+String::num(i)+";"); +}*/ return str; } -static _FORCE_INLINE_ int _xml_unescape(const CharType *p_src, int p_src_len, CharType *p_dst) { - +static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, char32_t *p_dst) { int len = 0; while (p_src_len) { - if (*p_src == '&') { - int eat = 0; if (p_src_len >= 4 && p_src[1] == '#') { - - CharType c = 0; + char32_t c = 0; for (int i = 2; i < p_src_len; i++) { - eat = i + 1; - CharType ct = p_src[i]; + char32_t ct = p_src[i]; if (ct == ';') { break; } else if (ct >= '0' && ct <= '9') { @@ -3573,49 +3896,50 @@ static _FORCE_INLINE_ int _xml_unescape(const CharType *p_src, int p_src_len, Ch c |= ct; } - if (p_dst) + if (p_dst) { *p_dst = c; + } } else if (p_src_len >= 4 && p_src[1] == 'g' && p_src[2] == 't' && p_src[3] == ';') { - - if (p_dst) + if (p_dst) { *p_dst = '>'; + } eat = 4; } else if (p_src_len >= 4 && p_src[1] == 'l' && p_src[2] == 't' && p_src[3] == ';') { - - if (p_dst) + if (p_dst) { *p_dst = '<'; + } eat = 4; } else if (p_src_len >= 5 && p_src[1] == 'a' && p_src[2] == 'm' && p_src[3] == 'p' && p_src[4] == ';') { - - if (p_dst) + if (p_dst) { *p_dst = '&'; + } eat = 5; } else if (p_src_len >= 6 && p_src[1] == 'q' && p_src[2] == 'u' && p_src[3] == 'o' && p_src[4] == 't' && p_src[5] == ';') { - - if (p_dst) + if (p_dst) { *p_dst = '"'; + } eat = 6; } else if (p_src_len >= 6 && p_src[1] == 'a' && p_src[2] == 'p' && p_src[3] == 'o' && p_src[4] == 's' && p_src[5] == ';') { - - if (p_dst) + if (p_dst) { *p_dst = '\''; + } eat = 6; } else { - - if (p_dst) + if (p_dst) { *p_dst = *p_src; + } eat = 1; } - if (p_dst) + if (p_dst) { p_dst++; + } len++; p_src += eat; p_src_len -= eat; } else { - if (p_dst) { *p_dst = *p_src; p_dst++; @@ -3630,20 +3954,19 @@ static _FORCE_INLINE_ int _xml_unescape(const CharType *p_src, int p_src_len, Ch } String String::xml_unescape() const { - String str; int l = length(); - int len = _xml_unescape(c_str(), l, nullptr); - if (len == 0) + int len = _xml_unescape(get_data(), l, nullptr); + if (len == 0) { return String(); + } str.resize(len + 1); - _xml_unescape(c_str(), l, str.ptrw()); + _xml_unescape(get_data(), l, str.ptrw()); str[len] = 0; return str; } String String::pad_decimals(int p_digits) const { - String s = *this; int c = s.find("."); @@ -3670,7 +3993,6 @@ String String::pad_decimals(int p_digits) const { } String String::pad_zeros(int p_digits) const { - String s = *this; int end = s.find("."); @@ -3678,8 +4000,9 @@ String String::pad_zeros(int p_digits) const { end = s.length(); } - if (end == 0) + if (end == 0) { return s; + } int begin = 0; @@ -3687,11 +4010,11 @@ String String::pad_zeros(int p_digits) const { begin++; } - if (begin >= end) + if (begin >= end) { return s; + } while (end - begin < p_digits) { - s = s.insert(begin, "0"); end++; } @@ -3700,7 +4023,6 @@ String String::pad_zeros(int p_digits) const { } String String::trim_prefix(const String &p_prefix) const { - String s = *this; if (s.begins_with(p_prefix)) { return s.substr(p_prefix.length(), s.length() - p_prefix.length()); @@ -3709,7 +4031,6 @@ String String::trim_prefix(const String &p_prefix) const { } String String::trim_suffix(const String &p_suffix) const { - String s = *this; if (s.ends_with(p_suffix)) { return s.substr(0, s.length() - p_suffix.length()); @@ -3718,40 +4039,42 @@ String String::trim_suffix(const String &p_suffix) const { } bool String::is_valid_integer() const { - int len = length(); - if (len == 0) + if (len == 0) { return false; + } int from = 0; - if (len != 1 && (operator[](0) == '+' || operator[](0) == '-')) + if (len != 1 && (operator[](0) == '+' || operator[](0) == '-')) { from++; + } for (int i = from; i < len; i++) { - - if (operator[](i) < '0' || operator[](i) > '9') + if (operator[](i) < '0' || operator[](i) > '9') { return false; // no start with number plz + } } return true; } bool String::is_valid_hex_number(bool p_with_prefix) const { - int len = length(); - if (len == 0) + if (len == 0) { return false; + } int from = 0; - if (len != 1 && (operator[](0) == '+' || operator[](0) == '-')) + if (len != 1 && (operator[](0) == '+' || operator[](0) == '-')) { from++; + } if (p_with_prefix) { - - if (len < 3) + if (len < 3) { return false; + } if (operator[](from) != '0' || operator[](from + 1) != 'x') { return false; } @@ -3759,22 +4082,22 @@ bool String::is_valid_hex_number(bool p_with_prefix) const { } for (int i = from; i < len; i++) { - - CharType c = operator[](i); - if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) + char32_t c = operator[](i); + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { continue; + } return false; } return true; -}; +} bool String::is_valid_float() const { - int len = length(); - if (len == 0) + if (len == 0) { return false; + } int from = 0; if (operator[](0) == '+' || operator[](0) == '-') { @@ -3788,71 +4111,70 @@ bool String::is_valid_float() const { bool numbers_found = false; for (int i = from; i < len; i++) { - if (operator[](i) >= '0' && operator[](i) <= '9') { - - if (exponent_found) + if (exponent_found) { exponent_values_found = true; - else + } else { numbers_found = true; + } } else if (numbers_found && !exponent_found && operator[](i) == 'e') { exponent_found = true; } else if (!period_found && !exponent_found && operator[](i) == '.') { period_found = true; } else if ((operator[](i) == '-' || operator[](i) == '+') && exponent_found && !exponent_values_found && !sign_found) { sign_found = true; - } else + } else { return false; // no start with number plz + } } return numbers_found; } String String::path_to_file(const String &p_path) const { - // Don't get base dir for src, this is expected to be a dir already. String src = this->replace("\\", "/"); String dst = p_path.replace("\\", "/").get_base_dir(); String rel = src.path_to(dst); - if (rel == dst) // failed + if (rel == dst) { // failed return p_path; - else + } else { return rel + p_path.get_file(); + } } String String::path_to(const String &p_path) const { - String src = this->replace("\\", "/"); String dst = p_path.replace("\\", "/"); - if (!src.ends_with("/")) + if (!src.ends_with("/")) { src += "/"; - if (!dst.ends_with("/")) + } + if (!dst.ends_with("/")) { dst += "/"; + } String base; if (src.begins_with("res://") && dst.begins_with("res://")) { - base = "res:/"; src = src.replace("res://", "/"); dst = dst.replace("res://", "/"); } else if (src.begins_with("user://") && dst.begins_with("user://")) { - base = "user:/"; src = src.replace("user://", "/"); dst = dst.replace("user://", "/"); } else if (src.begins_with("/") && dst.begins_with("/")) { - //nothing } else { //dos style String src_begin = src.get_slicec('/', 0); String dst_begin = dst.get_slicec('/', 0); - if (src_begin != dst_begin) + if (src_begin != dst_begin) { return p_path; //impossible to do this + } base = src_begin; src = src.substr(src_begin.length(), src.length()); @@ -3867,12 +4189,15 @@ String String::path_to(const String &p_path) const { int common_parent = 0; while (true) { - if (src_dirs.size() == common_parent) + if (src_dirs.size() == common_parent) { break; - if (dst_dirs.size() == common_parent) + } + if (dst_dirs.size() == common_parent) { break; - if (src_dirs[common_parent] != dst_dirs[common_parent]) + } + if (src_dirs[common_parent] != dst_dirs[common_parent]) { break; + } common_parent++; } @@ -3881,27 +4206,24 @@ String String::path_to(const String &p_path) const { String dir; for (int i = src_dirs.size() - 1; i > common_parent; i--) { - dir += "../"; } for (int i = common_parent + 1; i < dst_dirs.size(); i++) { - dir += dst_dirs[i] + "/"; } - if (dir.length() == 0) + if (dir.length() == 0) { dir = "./"; + } return dir; } bool String::is_valid_html_color() const { - return Color::html_is_valid(*this); } bool String::is_valid_filename() const { - String stripped = strip_edges(); if (*this != stripped) { return false; @@ -3915,56 +4237,58 @@ bool String::is_valid_filename() const { } bool String::is_valid_ip_address() const { - if (find(":") >= 0) { - Vector<String> ip = split(":"); for (int i = 0; i < ip.size(); i++) { - String n = ip[i]; - if (n.empty()) + if (n.is_empty()) { continue; + } if (n.is_valid_hex_number(false)) { - int nint = n.hex_to_int(false); - if (nint < 0 || nint > 0xffff) + int64_t nint = n.hex_to_int(false); + if (nint < 0 || nint > 0xffff) { return false; + } continue; - }; - if (!n.is_valid_ip_address()) + } + if (!n.is_valid_ip_address()) { return false; - }; + } + } } else { Vector<String> ip = split("."); - if (ip.size() != 4) + if (ip.size() != 4) { return false; + } for (int i = 0; i < ip.size(); i++) { - String n = ip[i]; - if (!n.is_valid_integer()) + if (!n.is_valid_integer()) { return false; + } int val = n.to_int(); - if (val < 0 || val > 255) + if (val < 0 || val > 255) { return false; + } } - }; + } return true; } bool String::is_resource_file() const { - return begins_with("res://") && find("::") == -1; } bool String::is_rel_path() const { - return !is_abs_path(); } String String::get_base_dir() const { - - int basepos = find("://"); + int basepos = find(":/"); + if (basepos == -1) { + basepos = find(":\\"); + } String rs; String base; if (basepos != -1) { @@ -3976,52 +4300,52 @@ String String::get_base_dir() const { rs = substr(1, length()); base = "/"; } else { - rs = *this; } } - int sep = MAX(rs.find_last("/"), rs.find_last("\\")); - if (sep == -1) + int sep = MAX(rs.rfind("/"), rs.rfind("\\")); + if (sep == -1) { return base; + } return base + rs.substr(0, sep); } String String::get_file() const { - - int sep = MAX(find_last("/"), find_last("\\")); - if (sep == -1) + int sep = MAX(rfind("/"), rfind("\\")); + if (sep == -1) { return *this; + } return substr(sep + 1, length()); } String String::get_extension() const { - - int pos = find_last("."); - if (pos < 0 || pos < MAX(find_last("/"), find_last("\\"))) + int pos = rfind("."); + if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) { return ""; + } return substr(pos + 1, length()); } String String::plus_file(const String &p_file) const { - if (empty()) + if (is_empty()) { return p_file; - if (operator[](length() - 1) == '/' || (p_file.size() > 0 && p_file.operator[](0) == '/')) + } + if (operator[](length() - 1) == '/' || (p_file.size() > 0 && p_file.operator[](0) == '/')) { return *this + p_file; + } return *this + "/" + p_file; } String String::percent_encode() const { - CharString cs = utf8(); String encoded; for (int i = 0; i < cs.length(); i++) { uint8_t c = cs[i]; if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '~' || c == '.') { - char p[2] = { (char)c, 0 }; encoded += p; } else { @@ -4036,34 +4360,34 @@ String String::percent_encode() const { return encoded; } -String String::percent_decode() const { +String String::percent_decode() const { CharString pe; CharString cs = utf8(); for (int i = 0; i < cs.length(); i++) { - uint8_t c = cs[i]; if (c == '%' && i < length() - 2) { - uint8_t a = LOWERCASE(cs[i + 1]); uint8_t b = LOWERCASE(cs[i + 2]); - if (a >= '0' && a <= '9') + if (a >= '0' && a <= '9') { c = (a - '0') << 4; - else if (a >= 'a' && a <= 'f') + } else if (a >= 'a' && a <= 'f') { c = (a - 'a' + 10) << 4; - else + } else { continue; + } uint8_t d = 0; - if (b >= '0' && b <= '9') + if (b >= '0' && b <= '9') { d = (b - '0'); - else if (b >= 'a' && b <= 'f') + } else if (b >= 'a' && b <= 'f') { d = (b - 'a' + 10); - else + } else { continue; + } c += d; i += 2; } @@ -4076,7 +4400,7 @@ String String::percent_decode() const { String String::property_name_encode() const { // Escape and quote strings with extended ASCII or further Unicode characters // as well as '"', '=' or ' ' (32) - const CharType *cstr = c_str(); + const char32_t *cstr = get_data(); for (int i = 0; cstr[i]; i++) { if (cstr[i] == '=' || cstr[i] == '"' || cstr[i] < 33 || cstr[i] > 126) { return "\"" + c_escape_multiline() + "\""; @@ -4087,31 +4411,27 @@ String String::property_name_encode() const { } String String::get_basename() const { - - int pos = find_last("."); - if (pos < 0 || pos < MAX(find_last("/"), find_last("\\"))) + int pos = rfind("."); + if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) { return *this; + } return substr(0, pos); } String itos(int64_t p_val) { - return String::num_int64(p_val); } String uitos(uint64_t p_val) { - return String::num_uint64(p_val); } String rtos(double p_val) { - return String::num(p_val); } String rtoss(double p_val) { - return String::num_scientific(p_val); } @@ -4120,19 +4440,22 @@ String String::rpad(int min_length, const String &character) const { String s = *this; int padding = min_length - s.length(); if (padding > 0) { - for (int i = 0; i < padding; i++) + for (int i = 0; i < padding; i++) { s = s + character; + } } return s; } + // Left-pad with a character. String String::lpad(int min_length, const String &character) const { String s = *this; int padding = min_length - s.length(); if (padding > 0) { - for (int i = 0; i < padding; i++) + for (int i = 0; i < padding; i++) { s = character + s; + } } return s; @@ -4144,7 +4467,7 @@ String String::lpad(int min_length, const String &character) const { // In case of an error, the string returned is the error description and "error" is true. String String::sprintf(const Array &values, bool *error) const { String formatted; - CharType *self = (CharType *)c_str(); + char32_t *self = (char32_t *)get_data(); bool in_format = false; int value_index = 0; int min_chars = 0; @@ -4154,10 +4477,12 @@ String String::sprintf(const Array &values, bool *error) const { bool left_justified = false; bool show_sign = false; - *error = true; + if (error) { + *error = true; + } for (; *self; self++) { - const CharType c = *self; + const char32_t c = *self; if (in_format) { // We have % - lets see what else we get. switch (c) { @@ -4182,9 +4507,14 @@ String String::sprintf(const Array &values, bool *error) const { int base = 16; bool capitalize = false; switch (c) { - case 'd': base = 10; break; - case 'o': base = 8; break; - case 'x': break; + case 'd': + base = 10; + break; + case 'o': + base = 8; + break; + case 'x': + break; case 'X': base = 16; capitalize = true; @@ -4195,11 +4525,12 @@ String String::sprintf(const Array &values, bool *error) const { int number_len = str.length(); // Padding. + int pad_chars_count = (value < 0 || show_sign) ? min_chars - 1 : min_chars; String pad_char = pad_with_zeroes ? String("0") : String(" "); if (left_justified) { - str = str.rpad(min_chars, pad_char); + str = str.rpad(pad_chars_count, pad_char); } else { - str = str.lpad(min_chars, pad_char); + str = str.lpad(pad_chars_count, pad_char); } // Sign. @@ -4225,27 +4556,40 @@ String String::sprintf(const Array &values, bool *error) const { } double value = values[value_index]; - String str = String::num(value, min_decimals); + bool is_negative = (value < 0); + String str = String::num(ABS(value), min_decimals); // Pad decimals out. str = str.pad_decimals(min_decimals); - // Show sign - if (show_sign && str.left(1) != "-") { - str = str.insert(0, "+"); - } + int initial_len = str.length(); - // Padding + // Padding. Leave room for sign later if required. + int pad_chars_count = (is_negative || show_sign) ? min_chars - 1 : min_chars; + String pad_char = pad_with_zeroes ? String("0") : String(" "); if (left_justified) { - str = str.rpad(min_chars); + if (pad_with_zeroes) { + return "left justification cannot be used with zeros as the padding"; + } else { + str = str.rpad(pad_chars_count, pad_char); + } } else { - str = str.lpad(min_chars); + str = str.lpad(pad_chars_count, pad_char); + } + + // Add sign if needed. + if (show_sign || is_negative) { + String sign_char = is_negative ? "-" : "+"; + if (left_justified) { + str = str.insert(0, sign_char); + } else { + str = str.insert(pad_with_zeroes ? 0 : str.length() - initial_len, sign_char); + } } formatted += str; ++value_index; in_format = false; - break; } case 's': { // String @@ -4276,9 +4620,11 @@ String String::sprintf(const Array &values, bool *error) const { if (values[value_index].is_num()) { int value = values[value_index]; if (value < 0) { - return "unsigned byte integer is lower than maximum"; - } else if (value > 255) { - return "unsigned byte integer is greater than maximum"; + return "unsigned integer is lower than minimum"; + } else if (value >= 0xd800 && value <= 0xdfff) { + return "unsigned integer is invalid Unicode character"; + } else if (value > 0x10ffff) { + return "unsigned integer is greater than maximum"; } str = chr(values[value_index]); } else if (values[value_index].get_type() == Variant::STRING) { @@ -4394,7 +4740,9 @@ String String::sprintf(const Array &values, bool *error) const { return "not all arguments converted during string formatting"; } - *error = false; + if (error) { + *error = false; + } return formatted; } @@ -4410,31 +4758,122 @@ String String::unquote() const { return substr(1, length() - 2); } +Vector<uint8_t> String::to_ascii_buffer() const { + const String *s = this; + if (s->is_empty()) { + return Vector<uint8_t>(); + } + CharString charstr = s->ascii(); + + Vector<uint8_t> retval; + size_t len = charstr.length(); + retval.resize(len); + uint8_t *w = retval.ptrw(); + copymem(w, charstr.ptr(), len); + + return retval; +} + +Vector<uint8_t> String::to_utf8_buffer() const { + const String *s = this; + if (s->is_empty()) { + return Vector<uint8_t>(); + } + CharString charstr = s->utf8(); + + Vector<uint8_t> retval; + size_t len = charstr.length(); + retval.resize(len); + uint8_t *w = retval.ptrw(); + copymem(w, charstr.ptr(), len); + + return retval; +} + +Vector<uint8_t> String::to_utf16_buffer() const { + const String *s = this; + if (s->is_empty()) { + return Vector<uint8_t>(); + } + Char16String charstr = s->utf16(); + + Vector<uint8_t> retval; + size_t len = charstr.length() * sizeof(char16_t); + retval.resize(len); + uint8_t *w = retval.ptrw(); + copymem(w, (const void *)charstr.ptr(), len); + + return retval; +} + +Vector<uint8_t> String::to_utf32_buffer() const { + const String *s = this; + if (s->is_empty()) { + return Vector<uint8_t>(); + } + + Vector<uint8_t> retval; + size_t len = s->length() * sizeof(char32_t); + retval.resize(len); + uint8_t *w = retval.ptrw(); + copymem(w, (const void *)s->ptr(), len); + + return retval; +} + #ifdef TOOLS_ENABLED -String TTR(const String &p_text) { +String TTR(const String &p_text, const String &p_context) { if (TranslationServer::get_singleton()) { - return TranslationServer::get_singleton()->tool_translate(p_text); + return TranslationServer::get_singleton()->tool_translate(p_text, p_context); } return p_text; } -String DTR(const String &p_text) { +String TTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) { 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 TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context); } - return p_text; + // Return message based on English plural rule if translation is not possible. + if (p_n == 1) { + return p_text; + } + return p_text_plural; +} + +String DTR(const String &p_text, const String &p_context) { + // Comes straight from the XML, so remove indentation and any trailing whitespace. + const String text = p_text.dedent().strip_edges(); + + if (TranslationServer::get_singleton()) { + return TranslationServer::get_singleton()->doc_translate(text, p_context); + } + + return text; +} + +String DTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) { + const String text = p_text.dedent().strip_edges(); + const String text_plural = p_text_plural.dedent().strip_edges(); + + if (TranslationServer::get_singleton()) { + return TranslationServer::get_singleton()->doc_translate_plural(text, text_plural, p_n, p_context); + } + + // Return message based on English plural rule if translation is not possible. + if (p_n == 1) { + return text; + } + return text_plural; } #endif -String RTR(const String &p_text) { +String RTR(const String &p_text, const String &p_context) { if (TranslationServer::get_singleton()) { - String rtr = TranslationServer::get_singleton()->tool_translate(p_text); + String rtr = TranslationServer::get_singleton()->tool_translate(p_text, p_context); if (rtr == String() || rtr == p_text) { - return TranslationServer::get_singleton()->translate(p_text); + return TranslationServer::get_singleton()->translate(p_text, p_context); } else { return rtr; } @@ -4442,3 +4881,20 @@ String RTR(const String &p_text) { return p_text; } + +String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) { + if (TranslationServer::get_singleton()) { + String rtr = TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context); + if (rtr == String() || rtr == p_text || rtr == p_text_plural) { + return TranslationServer::get_singleton()->translate_plural(p_text, p_text_plural, p_n, p_context); + } else { + return rtr; + } + } + + // Return message based on English plural rule if translation is not possible. + if (p_n == 1) { + return p_text; + } + return p_text_plural; +} diff --git a/core/ustring.h b/core/string/ustring.h index ee7e3b1e16..654e327320 100644 --- a/core/ustring.h +++ b/core/string/ustring.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,16 +28,22 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef USTRING_H -#define USTRING_H +#ifndef USTRING_GODOT_H +#define USTRING_GODOT_H +// Note: Renamed to avoid conflict with ICU header with the same name. -#include "core/array.h" -#include "core/cowdata.h" +#include "core/templates/cowdata.h" +#include "core/templates/vector.h" #include "core/typedefs.h" -#include "core/vector.h" +#include "core/variant/array.h" + +/*************************************************************************/ +/* CharProxy */ +/*************************************************************************/ template <class T> class CharProxy { + friend class Char16String; friend class CharString; friend class String; @@ -51,8 +57,9 @@ class CharProxy { public: _FORCE_INLINE_ operator T() const { - if (unlikely(_index == _cowdata.size())) + if (unlikely(_index == _cowdata.size())) { return _null; + } return _cowdata.get(_index); } @@ -70,8 +77,55 @@ public: } }; -class CharString { +/*************************************************************************/ +/* Char16String */ +/*************************************************************************/ + +class Char16String { + CowData<char16_t> _cowdata; + static const char16_t _null; + +public: + _FORCE_INLINE_ char16_t *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const char16_t *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + + _FORCE_INLINE_ char16_t get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const char16_t &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ const char16_t &operator[](int p_index) const { + if (unlikely(p_index == _cowdata.size())) { + return _null; + } + + return _cowdata.get(p_index); + } + _FORCE_INLINE_ CharProxy<char16_t> operator[](int p_index) { return CharProxy<char16_t>(p_index, _cowdata); } + _FORCE_INLINE_ Char16String() {} + _FORCE_INLINE_ Char16String(const Char16String &p_str) { _cowdata._ref(p_str._cowdata); } + _FORCE_INLINE_ Char16String &operator=(const Char16String &p_str) { + _cowdata._ref(p_str._cowdata); + return *this; + } + _FORCE_INLINE_ Char16String(const char16_t *p_cstr) { copy_from(p_cstr); } + + Char16String &operator=(const char16_t *p_cstr); + bool operator<(const Char16String &p_right) const; + Char16String &operator+=(char16_t p_char); + int length() const { return size() ? size() - 1 : 0; } + const char16_t *get_data() const; + operator const char16_t *() const { return get_data(); }; + +protected: + void copy_from(const char16_t *p_cstr); +}; + +/*************************************************************************/ +/* CharString */ +/*************************************************************************/ + +class CharString { CowData<char> _cowdata; static const char _null; @@ -84,8 +138,9 @@ public: _FORCE_INLINE_ char get(int p_index) const { return _cowdata.get(p_index); } _FORCE_INLINE_ void set(int p_index, const char &p_elem) { _cowdata.set(p_index, p_elem); } _FORCE_INLINE_ const char &operator[](int p_index) const { - if (unlikely(p_index == _cowdata.size())) + if (unlikely(p_index == _cowdata.size())) { return _null; + } return _cowdata.get(p_index); } @@ -93,7 +148,7 @@ public: _FORCE_INLINE_ CharString() {} _FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); } - _FORCE_INLINE_ CharString operator=(const CharString &p_str) { + _FORCE_INLINE_ CharString &operator=(const CharString &p_str) { _cowdata._ref(p_str._cowdata); return *this; } @@ -110,86 +165,103 @@ protected: void copy_from(const char *p_cstr); }; -typedef wchar_t CharType; +/*************************************************************************/ +/* String */ +/*************************************************************************/ struct StrRange { - - const CharType *c_str; + const char32_t *c_str; int len; - StrRange(const CharType *p_c_str = nullptr, int p_len = 0) { + StrRange(const char32_t *p_c_str = nullptr, int p_len = 0) { c_str = p_c_str; len = p_len; } }; class String { - - CowData<CharType> _cowdata; - static const CharType _null; + CowData<char32_t> _cowdata; + static const char32_t _null; void copy_from(const char *p_cstr); - void copy_from(const CharType *p_cstr, const int p_clip_to = -1); - void copy_from(const CharType &p_char); - void copy_from_unchecked(const CharType *p_char, const int p_length); + void copy_from(const char *p_cstr, const int p_clip_to); + void copy_from(const wchar_t *p_cstr); + void copy_from(const wchar_t *p_cstr, const int p_clip_to); + void copy_from(const char32_t *p_cstr); + void copy_from(const char32_t *p_cstr, const int p_clip_to); + + void copy_from(const char32_t &p_char); + + void copy_from_unchecked(const char32_t *p_char, const int p_length); + bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const; public: enum { - npos = -1 ///<for "some" compatibility with std::string (npos is a huge value in std::string) }; - _FORCE_INLINE_ CharType *ptrw() { return _cowdata.ptrw(); } - _FORCE_INLINE_ const CharType *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ char32_t *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const char32_t *ptr() const { return _cowdata.ptr(); } void remove(int p_index) { _cowdata.remove(p_index); } _FORCE_INLINE_ void clear() { resize(0); } - _FORCE_INLINE_ CharType get(int p_index) const { return _cowdata.get(p_index); } - _FORCE_INLINE_ void set(int p_index, const CharType &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ char32_t get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const char32_t &p_elem) { _cowdata.set(p_index, p_elem); } _FORCE_INLINE_ int size() const { return _cowdata.size(); } Error resize(int p_size) { return _cowdata.resize(p_size); } - _FORCE_INLINE_ const CharType &operator[](int p_index) const { - if (unlikely(p_index == _cowdata.size())) + _FORCE_INLINE_ const char32_t &operator[](int p_index) const { + if (unlikely(p_index == _cowdata.size())) { return _null; + } return _cowdata.get(p_index); } - _FORCE_INLINE_ CharProxy<CharType> operator[](int p_index) { return CharProxy<CharType>(p_index, _cowdata); } + _FORCE_INLINE_ CharProxy<char32_t> operator[](int p_index) { return CharProxy<char32_t>(p_index, _cowdata); } bool operator==(const String &p_str) const; bool operator!=(const String &p_str) const; String operator+(const String &p_str) const; - //String operator+(CharType p_char) const; String &operator+=(const String &); - String &operator+=(CharType p_char); + String &operator+=(char32_t p_char); String &operator+=(const char *p_str); - String &operator+=(const CharType *p_str); + String &operator+=(const wchar_t *p_str); + String &operator+=(const char32_t *p_str); /* Compatibility Operators */ void operator=(const char *p_str); - void operator=(const CharType *p_str); + void operator=(const wchar_t *p_str); + void operator=(const char32_t *p_str); + bool operator==(const char *p_str) const; - bool operator==(const CharType *p_str) const; + bool operator==(const wchar_t *p_str) const; + bool operator==(const char32_t *p_str) const; bool operator==(const StrRange &p_str_range) const; + bool operator!=(const char *p_str) const; - bool operator!=(const CharType *p_str) const; - bool operator<(const CharType *p_str) const; + bool operator!=(const wchar_t *p_str) const; + bool operator!=(const char32_t *p_str) const; + + bool operator<(const char32_t *p_str) const; bool operator<(const char *p_str) const; + bool operator<(const wchar_t *p_str) const; + bool operator<(const String &p_str) const; bool operator<=(const String &p_str) const; + bool operator>(const String &p_str) const; + bool operator>=(const String &p_str) const; signed char casecmp_to(const String &p_str) const; signed char nocasecmp_to(const String &p_str) const; signed char naturalnocasecmp_to(const String &p_str) const; - const CharType *c_str() const; + const char32_t *get_data() const; /* standard size stuff */ _FORCE_INLINE_ int length() const { @@ -197,12 +269,13 @@ public: return s ? (s - 1) : 0; // length does not include zero } + bool is_valid_string() const; + /* complex helpers */ String substr(int p_from, int p_chars = -1) const; int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed - int find_char(const CharType &p_char, int p_from = 0) const; ///< return <0 if failed - int find_last(const String &p_str) const; ///< return <0 if failed + int find_char(const char32_t &p_char, int p_from = 0) const; ///< return <0 if failed int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed int rfindn(const String &p_str, int p_from = -1) const; ///< return <0 if failed, case insensitive @@ -239,29 +312,31 @@ public: static String num_real(double p_num); static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false); static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false); - static String chr(CharType p_char); + static String chr(char32_t p_char); static String md5(const uint8_t *p_md5); static String hex_encode_buffer(const uint8_t *p_buffer, int p_len); bool is_numeric() const; - double to_double() const; - float to_float() const; - int hex_to_int(bool p_with_prefix = true) const; - int to_int() const; - - int64_t hex_to_int64(bool p_with_prefix = true) const; - int64_t bin_to_int64(bool p_with_prefix = true) const; - int64_t to_int64() const; - static int to_int(const char *p_str, int p_len = -1); - static double to_double(const char *p_str); - static double to_double(const CharType *p_str, const CharType **r_end = nullptr); - static int64_t to_int(const CharType *p_str, int p_len = -1); + + double to_float() const; + int64_t hex_to_int(bool p_with_prefix = true) const; + int64_t bin_to_int(bool p_with_prefix = true) const; + int64_t to_int() const; + + static int64_t to_int(const char *p_str, int p_len = -1); + static int64_t to_int(const wchar_t *p_str, int p_len = -1); + static int64_t to_int(const char32_t *p_str, int p_len = -1, bool p_clamp = false); + + static double to_float(const char *p_str); + static double to_float(const wchar_t *p_str, const wchar_t **r_end = nullptr); + static double to_float(const char32_t *p_str, const char32_t **r_end = nullptr); + String capitalize() const; String camelcase_to_underscore(bool lowercase = true) const; String get_with_code_lines() const; int get_slice_count(String p_splitter) const; String get_slice(String p_splitter, int p_slice) const; - String get_slicec(CharType p_splitter, int p_slice) const; + String get_slicec(char32_t p_splitter, int p_slice) const; Vector<String> split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; Vector<String> rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; @@ -271,10 +346,10 @@ public: Vector<int> split_ints(const String &p_splitter, bool p_allow_empty = true) const; Vector<int> split_ints_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const; - String join(Vector<String> parts); + String join(Vector<String> parts) const; - static CharType char_uppercase(CharType p_char); - static CharType char_lowercase(CharType p_char); + static char32_t char_uppercase(char32_t p_char); + static char32_t char_lowercase(char32_t p_char); String to_upper() const; String to_lower() const; @@ -291,7 +366,7 @@ public: String get_extension() const; String get_basename() const; String plus_file(const String &p_file) const; - CharType ord_at(int p_idx) const; + char32_t ord_at(int p_idx) const; void erase(int p_pos, int p_chars); @@ -300,8 +375,14 @@ public: bool parse_utf8(const char *p_utf8, int p_len = -1); //return true on error static String utf8(const char *p_utf8, int p_len = -1); - static uint32_t hash(const CharType *p_cstr, int p_len); /* hash the string */ - static uint32_t hash(const CharType *p_cstr); /* hash the string */ + Char16String utf16() const; + bool parse_utf16(const char16_t *p_utf16, int p_len = -1); //return true on error + static String utf16(const char16_t *p_utf16, int p_len = -1); + + static uint32_t hash(const char32_t *p_cstr, int p_len); /* hash the string */ + static uint32_t hash(const char32_t *p_cstr); /* hash the string */ + static uint32_t hash(const wchar_t *p_cstr, int p_len); /* hash the string */ + static uint32_t hash(const wchar_t *p_cstr); /* hash the string */ static uint32_t hash(const char *p_cstr, int p_len); /* hash the string */ static uint32_t hash(const char *p_cstr); /* hash the string */ uint32_t hash() const; /* hash the string */ @@ -313,7 +394,7 @@ public: Vector<uint8_t> sha1_buffer() const; Vector<uint8_t> sha256_buffer() const; - _FORCE_INLINE_ bool empty() const { return length() == 0; } + _FORCE_INLINE_ bool is_empty() const { return length() == 0; } // path functions bool is_abs_path() const; @@ -352,24 +433,37 @@ public: /** * The constructors must not depend on other overloads */ - /* String(CharType p_char);*/ _FORCE_INLINE_ String() {} _FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); } - String operator=(const String &p_str) { + + String &operator=(const String &p_str) { _cowdata._ref(p_str._cowdata); return *this; } + Vector<uint8_t> to_ascii_buffer() const; + Vector<uint8_t> to_utf8_buffer() const; + Vector<uint8_t> to_utf16_buffer() const; + Vector<uint8_t> to_utf32_buffer() const; + String(const char *p_str); - String(const CharType *p_str, int p_clip_to_len = -1); + String(const wchar_t *p_str); + String(const char32_t *p_str); + String(const char *p_str, int p_clip_to_len); + String(const wchar_t *p_str, int p_clip_to_len); + String(const char32_t *p_str, int p_clip_to_len); String(const StrRange &p_range); }; bool operator==(const char *p_chr, const String &p_str); +bool operator==(const wchar_t *p_chr, const String &p_str); +bool operator!=(const char *p_chr, const String &p_str); +bool operator!=(const wchar_t *p_chr, const String &p_str); String operator+(const char *p_chr, const String &p_str); -String operator+(CharType p_chr, const String &p_str); +String operator+(const wchar_t *p_chr, const String &p_str); +String operator+(char32_t p_chr, const String &p_str); String itos(int64_t p_val); String uitos(uint64_t p_val); @@ -377,36 +471,34 @@ String rtos(double p_val); String rtoss(double p_val); //scientific version struct NoCaseComparator { - bool operator()(const String &p_a, const String &p_b) const { - return p_a.nocasecmp_to(p_b) < 0; } }; struct NaturalNoCaseComparator { - bool operator()(const String &p_a, const String &p_b) const { - return p_a.naturalnocasecmp_to(p_b) < 0; } }; template <typename L, typename R> _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) { - while (true) { + const char32_t l = *l_ptr; + const char32_t r = *r_ptr; - if (*l_ptr == 0 && *r_ptr == 0) + if (l == 0 && r == 0) { return false; - else if (*l_ptr == 0) + } else if (l == 0) { return true; - else if (*r_ptr == 0) + } else if (r == 0) { return false; - else if (*l_ptr < *r_ptr) + } else if (l < r) { return true; - else if (*l_ptr > *r_ptr) + } else if (l > r) { return false; + } l_ptr++; r_ptr++; @@ -419,8 +511,10 @@ _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) { // and doc translate for the class reference (DTR). #ifdef TOOLS_ENABLED // Gets parsed. -String TTR(const String &); -String DTR(const String &); +String TTR(const String &p_text, const String &p_context = ""); +String TTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = ""); +String DTR(const String &p_text, const String &p_context = ""); +String DTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = ""); // Use for C strings. #define TTRC(m_value) (m_value) // Use to avoid parsing (for use later with C strings). @@ -428,15 +522,38 @@ String DTR(const String &); #else #define TTR(m_value) (String()) +#define TTRN(m_value) (String()) #define DTR(m_value) (String()) +#define DTRN(m_value) (String()) #define TTRC(m_value) (m_value) #define TTRGET(m_value) (m_value) #endif // Runtime translate for the public node API. -String RTR(const String &); +String RTR(const String &p_text, const String &p_context = ""); +String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = ""); -bool is_symbol(CharType c); +bool is_symbol(char32_t c); bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); -#endif // USTRING_H +_FORCE_INLINE_ void sarray_add_str(Vector<String> &arr) { +} + +_FORCE_INLINE_ void sarray_add_str(Vector<String> &arr, const String &p_str) { + arr.push_back(p_str); +} + +template <class... P> +_FORCE_INLINE_ void sarray_add_str(Vector<String> &arr, const String &p_str, P... p_args) { + arr.push_back(p_str); + sarray_add_str(arr, p_args...); +} + +template <class... P> +_FORCE_INLINE_ Vector<String> sarray(P... p_args) { + Vector<String> arr; + sarray_add_str(arr, p_args...); + return arr; +} + +#endif // USTRING_GODOT_H diff --git a/core/templates/SCsub b/core/templates/SCsub new file mode 100644 index 0000000000..8c4c843a33 --- /dev/null +++ b/core/templates/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +env_templates = env.Clone() + +env_templates.add_source_files(env.core_sources, "*.cpp") diff --git a/core/command_queue_mt.cpp b/core/templates/command_queue_mt.cpp index 3ce769c72c..238bf3975c 100644 --- a/core/command_queue_mt.cpp +++ b/core/templates/command_queue_mt.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,33 +30,28 @@ #include "command_queue_mt.h" +#include "core/config/project_settings.h" #include "core/os/os.h" void CommandQueueMT::lock() { - mutex.lock(); } void CommandQueueMT::unlock() { - mutex.unlock(); } void CommandQueueMT::wait_for_flush() { - // wait one millisecond for a flush to happen OS::get_singleton()->delay_usec(1000); } CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() { - int idx = -1; while (true) { - lock(); for (int i = 0; i < SYNC_SEMAPHORES; i++) { - if (!sync_sems[i].in_use) { sync_sems[i].in_use = true; idx = i; @@ -77,7 +72,7 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() { bool CommandQueueMT::dealloc_one() { tryagain: - if (dealloc_ptr == write_ptr) { + if (dealloc_ptr == (write_ptr_and_epoch >> 1)) { // The queue is empty return false; } @@ -100,25 +95,18 @@ tryagain: } CommandQueueMT::CommandQueueMT(bool p_sync) { - - read_ptr = 0; - write_ptr = 0; - dealloc_ptr = 0; - command_mem = (uint8_t *)memalloc(COMMAND_MEM_SIZE); - - for (int i = 0; i < SYNC_SEMAPHORES; i++) { - - sync_sems[i].in_use = false; - } - if (p_sync) + command_mem_size = GLOBAL_DEF_RST("memory/limits/command_queue/multithreading_queue_size_kb", DEFAULT_COMMAND_MEM_SIZE_KB); + ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/command_queue/multithreading_queue_size_kb", PropertyInfo(Variant::INT, "memory/limits/command_queue/multithreading_queue_size_kb", PROPERTY_HINT_RANGE, "1,4096,1,or_greater")); + command_mem_size *= 1024; + command_mem = (uint8_t *)memalloc(command_mem_size); + if (p_sync) { sync = memnew(Semaphore); - else - sync = nullptr; + } } CommandQueueMT::~CommandQueueMT() { - - if (sync) + if (sync) { memdelete(sync); + } memfree(command_mem); } diff --git a/core/command_queue_mt.h b/core/templates/command_queue_mt.h index 558453bdf5..7861c76153 100644 --- a/core/command_queue_mt.h +++ b/core/templates/command_queue_mt.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -34,7 +34,7 @@ #include "core/os/memory.h" #include "core/os/mutex.h" #include "core/os/semaphore.h" -#include "core/simple_type.h" +#include "core/templates/simple_type.h" #include "core/typedefs.h" #define COMMA(N) _COMMA_##N @@ -253,7 +253,8 @@ cmd->method = p_method; \ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ unlock(); \ - if (sync) sync->post(); \ + if (sync) \ + sync->post(); \ } #define CMD_RET_TYPE(N) CommandRet##N<T, M, COMMA_SEP_LIST(TYPE_ARG, N) COMMA(N) R> @@ -269,7 +270,8 @@ cmd->ret = r_ret; \ cmd->sync_sem = ss; \ unlock(); \ - if (sync) sync->post(); \ + if (sync) \ + sync->post(); \ ss->sem.wait(); \ ss->in_use = false; \ } @@ -286,7 +288,8 @@ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ cmd->sync_sem = ss; \ unlock(); \ - if (sync) sync->post(); \ + if (sync) \ + sync->post(); \ ss->sem.wait(); \ ss->in_use = false; \ } @@ -294,22 +297,18 @@ #define MAX_CMD_PARAMS 15 class CommandQueueMT { - struct SyncSemaphore { - Semaphore sem; - bool in_use; + bool in_use = false; }; struct CommandBase { - virtual void call() = 0; - virtual void post(){}; - virtual ~CommandBase(){}; + virtual void post() {} + virtual ~CommandBase() {} }; struct SyncCommand : public CommandBase { - SyncSemaphore *sync_sem; virtual void post() { @@ -331,31 +330,33 @@ class CommandQueueMT { /***** BASE *******/ enum { - COMMAND_MEM_SIZE_KB = 256, - COMMAND_MEM_SIZE = COMMAND_MEM_SIZE_KB * 1024, + DEFAULT_COMMAND_MEM_SIZE_KB = 256, SYNC_SEMAPHORES = 8 }; - uint8_t *command_mem; - uint32_t read_ptr; - uint32_t write_ptr; - uint32_t dealloc_ptr; + uint8_t *command_mem = nullptr; + uint32_t read_ptr_and_epoch = 0; + uint32_t write_ptr_and_epoch = 0; + uint32_t dealloc_ptr = 0; + uint32_t command_mem_size = 0; SyncSemaphore sync_sems[SYNC_SEMAPHORES]; Mutex mutex; - Semaphore *sync; + Semaphore *sync = nullptr; template <class T> T *allocate() { - // alloc size is size+T+safeguard uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1)) + 8; + // Assert that the buffer is big enough to hold at least two messages. + ERR_FAIL_COND_V(alloc_size * 2 + sizeof(uint32_t) > command_mem_size, nullptr); + tryagain: + uint32_t write_ptr = write_ptr_and_epoch >> 1; if (write_ptr < dealloc_ptr) { // behind dealloc_ptr, check that there is room if ((dealloc_ptr - write_ptr) <= alloc_size) { - // There is no more room, try to deallocate something if (dealloc_one()) { goto tryagain; @@ -365,7 +366,7 @@ class CommandQueueMT { } else { // ahead of dealloc_ptr, check that there is room - if ((COMMAND_MEM_SIZE - write_ptr) < alloc_size + sizeof(uint32_t)) { + if ((command_mem_size - write_ptr) < alloc_size + sizeof(uint32_t)) { // no room at the end, wrap down; if (dealloc_ptr == 0) { // don't want write_ptr to become dealloc_ptr @@ -378,12 +379,17 @@ class CommandQueueMT { } // if this happens, it's a bug - ERR_FAIL_COND_V((COMMAND_MEM_SIZE - write_ptr) < 8, nullptr); + ERR_FAIL_COND_V((command_mem_size - write_ptr) < 8, nullptr); // zero means, wrap to beginning uint32_t *p = (uint32_t *)&command_mem[write_ptr]; - *p = 0; - write_ptr = 0; + *p = 1; + write_ptr_and_epoch = 0 | (1 & ~write_ptr_and_epoch); // Invert epoch. + // See if we can get the thread to run and clear up some more space while we wait. + // This is required if alloc_size * 2 + 4 > COMMAND_MEM_SIZE + if (sync) { + sync->post(); + } goto tryagain; } } @@ -397,17 +403,16 @@ class CommandQueueMT { // allocate the command T *cmd = memnew_placement(&command_mem[write_ptr], T); write_ptr += size; + write_ptr_and_epoch = (write_ptr << 1) | (write_ptr_and_epoch & 1); return cmd; } template <class T> T *allocate_and_lock() { - lock(); T *ret; while ((ret = allocate<T>()) == nullptr) { - unlock(); // sleep a little until fetch happened and some room is made wait_for_flush(); @@ -418,21 +423,27 @@ class CommandQueueMT { } bool flush_one(bool p_lock = true) { - if (p_lock) lock(); + if (p_lock) { + lock(); + } tryagain: // tried to read an empty queue - if (read_ptr == write_ptr) { - if (p_lock) unlock(); + if (read_ptr_and_epoch == write_ptr_and_epoch) { + if (p_lock) { + unlock(); + } return false; } + uint32_t read_ptr = read_ptr_and_epoch >> 1; uint32_t size_ptr = read_ptr; uint32_t size = *(uint32_t *)&command_mem[read_ptr] >> 1; if (size == 0) { + *(uint32_t *)&command_mem[read_ptr] = 0; // clear in-use bit. //end of ringbuffer, wrap - read_ptr = 0; + read_ptr_and_epoch = 0 | (1 & ~read_ptr_and_epoch); // Invert epoch. goto tryagain; } @@ -442,15 +453,23 @@ class CommandQueueMT { read_ptr += size; - if (p_lock) unlock(); + read_ptr_and_epoch = (read_ptr << 1) | (read_ptr_and_epoch & 1); + + if (p_lock) { + unlock(); + } cmd->call(); - if (p_lock) lock(); + if (p_lock) { + lock(); + } cmd->post(); cmd->~CommandBase(); *(uint32_t *)&command_mem[size_ptr] &= ~1; - if (p_lock) unlock(); + if (p_lock) { + unlock(); + } return true; } @@ -480,11 +499,10 @@ public: } void flush_all() { - //ERR_FAIL_COND(sync); lock(); - while (flush_one(false)) - ; + while (flush_one(false)) { + } unlock(); } diff --git a/core/cowdata.h b/core/templates/cowdata.h index 975a572906..4becb1be59 100644 --- a/core/cowdata.h +++ b/core/templates/cowdata.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,15 +31,16 @@ #ifndef COWDATA_H #define COWDATA_H -#include "core/error_macros.h" +#include "core/error/error_macros.h" #include "core/os/memory.h" -#include "core/safe_refcount.h" +#include "core/templates/safe_refcount.h" #include <string.h> template <class T> class Vector; class String; +class Char16String; class CharString; template <class T, class V> class VMap; @@ -49,35 +50,36 @@ class CowData { template <class TV> friend class Vector; friend class String; + friend class Char16String; friend class CharString; template <class TV, class VV> friend class VMap; private: - mutable T *_ptr; + mutable T *_ptr = nullptr; // internal helpers _FORCE_INLINE_ uint32_t *_get_refcount() const { - - if (!_ptr) + if (!_ptr) { return nullptr; + } return reinterpret_cast<uint32_t *>(_ptr) - 2; } _FORCE_INLINE_ uint32_t *_get_size() const { - - if (!_ptr) + if (!_ptr) { return nullptr; + } return reinterpret_cast<uint32_t *>(_ptr) - 1; } _FORCE_INLINE_ T *_get_data() const { - - if (!_ptr) + if (!_ptr) { return nullptr; + } return reinterpret_cast<T *>(_ptr); } @@ -125,31 +127,29 @@ public: _FORCE_INLINE_ int size() const { uint32_t *size = (uint32_t *)_get_size(); - if (size) + if (size) { return *size; - else + } else { return 0; + } } _FORCE_INLINE_ void clear() { resize(0); } - _FORCE_INLINE_ bool empty() const { return _ptr == 0; } + _FORCE_INLINE_ bool is_empty() const { return _ptr == nullptr; } _FORCE_INLINE_ void set(int p_index, const T &p_elem) { - CRASH_BAD_INDEX(p_index, size()); _copy_on_write(); _get_data()[p_index] = p_elem; } _FORCE_INLINE_ T &get_m(int p_index) { - CRASH_BAD_INDEX(p_index, size()); _copy_on_write(); return _get_data()[p_index]; } _FORCE_INLINE_ const T &get(int p_index) const { - CRASH_BAD_INDEX(p_index, size()); return _get_data()[p_index]; @@ -158,46 +158,45 @@ public: Error resize(int p_size); _FORCE_INLINE_ void remove(int p_index) { - ERR_FAIL_INDEX(p_index, size()); T *p = ptrw(); int len = size(); for (int i = p_index; i < len - 1; i++) { - p[i] = p[i + 1]; - }; + } resize(len - 1); - }; + } Error insert(int p_pos, const T &p_val) { - ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); resize(size() + 1); - for (int i = (size() - 1); i > p_pos; i--) + for (int i = (size() - 1); i > p_pos; i--) { set(i, get(i - 1)); + } set(p_pos, p_val); return OK; - }; + } int find(const T &p_val, int p_from = 0) const; - _FORCE_INLINE_ CowData(); + _FORCE_INLINE_ CowData() {} _FORCE_INLINE_ ~CowData(); _FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); }; }; template <class T> void CowData<T>::_unref(void *p_data) { - - if (!p_data) + if (!p_data) { return; + } uint32_t *refc = _get_refcount(); - if (atomic_decrement(refc) > 0) + if (atomic_decrement(refc) > 0) { return; // still in use + } // clean up if (!__has_trivial_destructor(T)) { @@ -216,9 +215,9 @@ void CowData<T>::_unref(void *p_data) { template <class T> void CowData<T>::_copy_on_write() { - - if (!_ptr) + if (!_ptr) { return; + } uint32_t *refc = _get_refcount(); @@ -250,13 +249,13 @@ void CowData<T>::_copy_on_write() { template <class T> Error CowData<T>::resize(int p_size) { - ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); int current_size = size(); - if (p_size == current_size) + if (p_size == current_size) { return OK; + } if (p_size == 0) { // wants to clean up @@ -273,7 +272,6 @@ Error CowData<T>::resize(int p_size) { ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); if (p_size > current_size) { - if (alloc_size != current_alloc_size) { if (current_size == 0) { // alloc from scratch @@ -304,7 +302,6 @@ Error CowData<T>::resize(int p_size) { *_get_size() = p_size; } else if (p_size < current_size) { - if (!__has_trivial_destructor(T)) { // deinitialize no longer needed elements for (uint32_t i = p_size; i < *_get_size(); i++) { @@ -351,15 +348,16 @@ void CowData<T>::_ref(const CowData *p_from) { template <class T> void CowData<T>::_ref(const CowData &p_from) { - - if (_ptr == p_from._ptr) + if (_ptr == p_from._ptr) { return; // self assign, do nothing. + } _unref(_ptr); _ptr = nullptr; - if (!p_from._ptr) + if (!p_from._ptr) { return; //nothing to do + } if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference _ptr = p_from._ptr; @@ -367,14 +365,7 @@ void CowData<T>::_ref(const CowData &p_from) { } template <class T> -CowData<T>::CowData() { - - _ptr = nullptr; -} - -template <class T> CowData<T>::~CowData() { - _unref(_ptr); } diff --git a/core/hash_map.h b/core/templates/hash_map.h index f27a86cc02..dc378aed69 100644 --- a/core/hash_map.h +++ b/core/templates/hash_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,12 +31,12 @@ #ifndef HASH_MAP_H #define HASH_MAP_H -#include "core/error_macros.h" -#include "core/hashfuncs.h" -#include "core/list.h" +#include "core/error/error_macros.h" #include "core/math/math_funcs.h" #include "core/os/memory.h" -#include "core/ustring.h" +#include "core/string/ustring.h" +#include "core/templates/hashfuncs.h" +#include "core/templates/list.h" /** * @class HashMap @@ -59,7 +59,6 @@ template <class TKey, class TData, class Hasher = HashMapHasherDefault, class Co class HashMap { public: struct Pair { - TKey key; TData data; @@ -74,9 +73,9 @@ public: private: friend class HashMap; - uint32_t hash; - Element *next; - Element() { next = 0; } + uint32_t hash = 0; + Element *next = nullptr; + Element() {} Pair pair; public: @@ -94,34 +93,32 @@ public: }; private: - Element **hash_table; - uint8_t hash_table_power; - uint32_t elements; + Element **hash_table = nullptr; + uint8_t hash_table_power = 0; + uint32_t elements = 0; void make_hash_table() { - ERR_FAIL_COND(hash_table); hash_table = memnew_arr(Element *, (1 << MIN_HASH_TABLE_POWER)); hash_table_power = MIN_HASH_TABLE_POWER; elements = 0; - for (int i = 0; i < (1 << MIN_HASH_TABLE_POWER); i++) - hash_table[i] = 0; + for (int i = 0; i < (1 << MIN_HASH_TABLE_POWER); i++) { + hash_table[i] = nullptr; + } } void erase_hash_table() { - ERR_FAIL_COND_MSG(elements, "Cannot erase hash table if there are still elements inside."); memdelete_arr(hash_table); - hash_table = 0; + hash_table = nullptr; hash_table_power = 0; elements = 0; } void check_hash_table() { - int new_hash_table_power = -1; if ((int)elements > ((1 << hash_table_power) * RELATIONSHIP)) { @@ -129,40 +126,36 @@ private: new_hash_table_power = hash_table_power + 1; while ((int)elements > ((1 << new_hash_table_power) * RELATIONSHIP)) { - new_hash_table_power++; } } else if ((hash_table_power > (int)MIN_HASH_TABLE_POWER) && ((int)elements < ((1 << (hash_table_power - 1)) * RELATIONSHIP))) { - /* rehash down */ new_hash_table_power = hash_table_power - 1; while ((int)elements < ((1 << (new_hash_table_power - 1)) * RELATIONSHIP)) { - new_hash_table_power--; } - if (new_hash_table_power < (int)MIN_HASH_TABLE_POWER) + if (new_hash_table_power < (int)MIN_HASH_TABLE_POWER) { new_hash_table_power = MIN_HASH_TABLE_POWER; + } } - if (new_hash_table_power == -1) + if (new_hash_table_power == -1) { return; + } Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power)); ERR_FAIL_COND_MSG(!new_hash_table, "Out of memory."); for (int i = 0; i < (1 << new_hash_table_power); i++) { - - new_hash_table[i] = 0; + new_hash_table[i] = nullptr; } if (hash_table) { for (int i = 0; i < (1 << hash_table_power); i++) { - while (hash_table[i]) { - Element *se = hash_table[i]; hash_table[i] = se->next; int new_pos = se->hash & ((1 << new_hash_table_power) - 1); @@ -179,17 +172,14 @@ private: /* I want to have only one function.. */ _FORCE_INLINE_ const Element *get_element(const TKey &p_key) const { - uint32_t hash = Hasher::hash(p_key); uint32_t index = hash & ((1 << hash_table_power) - 1); Element *e = hash_table[index]; while (e) { - /* checking hash first avoids comparing key, which may take longer */ if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) { - /* the pair exists in this hashtable, so just update data */ return e; } @@ -201,7 +191,6 @@ private: } Element *create_element(const TKey &p_key) { - /* if element doesn't exist, create it */ Element *e = memnew(Element); ERR_FAIL_COND_V_MSG(!e, nullptr, "Out of memory."); @@ -219,27 +208,26 @@ private: } void copy_from(const HashMap &p_t) { - - if (&p_t == this) + if (&p_t == this) { return; /* much less bother with that */ + } clear(); - if (!p_t.hash_table || p_t.hash_table_power == 0) + if (!p_t.hash_table || p_t.hash_table_power == 0) { return; /* not copying from empty table */ + } hash_table = memnew_arr(Element *, (uint64_t)1 << p_t.hash_table_power); hash_table_power = p_t.hash_table_power; elements = p_t.elements; for (int i = 0; i < (1 << p_t.hash_table_power); i++) { - hash_table[i] = nullptr; const Element *e = p_t.hash_table[i]; while (e) { - Element *le = memnew(Element); /* local element */ *le = *e; /* copy data */ @@ -259,20 +247,20 @@ public: } Element *set(const Pair &p_pair) { - Element *e = nullptr; - if (!hash_table) + if (!hash_table) { make_hash_table(); // if no table, make one - else + } else { e = const_cast<Element *>(get_element(p_pair.key)); + } /* if we made it up to here, the pair doesn't exist, create and assign */ if (!e) { - e = create_element(p_pair.key); - if (!e) + if (!e) { return nullptr; + } check_hash_table(); // perform mantenience routine } @@ -281,7 +269,6 @@ public: } bool has(const TKey &p_key) const { - return getptr(p_key) != nullptr; } @@ -292,16 +279,14 @@ public: */ const TData &get(const TKey &p_key) const { - const TData *res = getptr(p_key); - ERR_FAIL_COND_V(!res, *res); + CRASH_COND_MSG(!res, "Map key not found."); return *res; } TData &get(const TKey &p_key) { - TData *res = getptr(p_key); - ERR_FAIL_COND_V(!res, *res); + CRASH_COND_MSG(!res, "Map key not found."); return *res; } @@ -311,27 +296,29 @@ public: */ _FORCE_INLINE_ TData *getptr(const TKey &p_key) { - - if (unlikely(!hash_table)) + if (unlikely(!hash_table)) { return nullptr; + } Element *e = const_cast<Element *>(get_element(p_key)); - if (e) + if (e) { return &e->pair.data; + } return nullptr; } _FORCE_INLINE_ const TData *getptr(const TKey &p_key) const { - - if (unlikely(!hash_table)) + if (unlikely(!hash_table)) { return nullptr; + } const Element *e = const_cast<Element *>(get_element(p_key)); - if (e) + if (e) { return &e->pair.data; + } return nullptr; } @@ -343,9 +330,9 @@ public: template <class C> _FORCE_INLINE_ TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) { - - if (unlikely(!hash_table)) + if (unlikely(!hash_table)) { return nullptr; + } uint32_t hash = p_custom_hash; uint32_t index = hash & ((1 << hash_table_power) - 1); @@ -353,10 +340,8 @@ public: Element *e = hash_table[index]; while (e) { - /* checking hash first avoids comparing key, which may take longer */ if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) { - /* the pair exists in this hashtable, so just update data */ return &e->pair.data; } @@ -369,9 +354,9 @@ public: template <class C> _FORCE_INLINE_ const TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) const { - - if (unlikely(!hash_table)) + if (unlikely(!hash_table)) { return nullptr; + } uint32_t hash = p_custom_hash; uint32_t index = hash & ((1 << hash_table_power) - 1); @@ -379,10 +364,8 @@ public: const Element *e = hash_table[index]; while (e) { - /* checking hash first avoids comparing key, which may take longer */ if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) { - /* the pair exists in this hashtable, so just update data */ return &e->pair.data; } @@ -398,9 +381,9 @@ public: */ bool erase(const TKey &p_key) { - - if (unlikely(!hash_table)) + if (unlikely(!hash_table)) { return false; + } uint32_t hash = Hasher::hash(p_key); uint32_t index = hash & ((1 << hash_table_power) - 1); @@ -408,12 +391,9 @@ public: Element *e = hash_table[index]; Element *p = nullptr; while (e) { - /* checking hash first avoids comparing key, which may take longer */ if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) { - if (p) { - p->next = e->next; } else { //begin of list @@ -423,10 +403,11 @@ public: memdelete(e); elements--; - if (elements == 0) + if (elements == 0) { erase_hash_table(); - else + } else { check_hash_table(); + } return true; } @@ -444,14 +425,14 @@ public: inline TData &operator[](const TKey &p_key) { //assignment Element *e = nullptr; - if (!hash_table) + if (!hash_table) { make_hash_table(); // if no table, make one - else + } else { e = const_cast<Element *>(get_element(p_key)); + } /* if we made it up to here, the pair doesn't exist, create */ if (!e) { - e = create_element(p_key); CRASH_COND(!e); check_hash_table(); // perform mantenience routine @@ -476,14 +457,13 @@ public: * */ const TKey *next(const TKey *p_key) const { - - if (unlikely(!hash_table)) + if (unlikely(!hash_table)) { return nullptr; + } if (!p_key) { /* get the first key */ for (int i = 0; i < (1 << hash_table_power); i++) { - if (hash_table[i]) { return &hash_table[i]->pair.key; } @@ -501,7 +481,6 @@ public: uint32_t index = e->hash & ((1 << hash_table_power) - 1); index++; for (int i = index; i < (1 << hash_table_power); i++) { - if (hash_table[i]) { return &hash_table[i]->pair.key; } @@ -515,23 +494,18 @@ public: } inline unsigned int size() const { - return elements; } - inline bool empty() const { - + inline bool is_empty() const { return elements == 0; } void clear() { - /* clean up */ if (hash_table) { for (int i = 0; i < (1 << hash_table_power); i++) { - while (hash_table[i]) { - Element *e = hash_table[i]; hash_table[i] = e->next; memdelete(e); @@ -541,60 +515,35 @@ public: memdelete_arr(hash_table); } - hash_table = 0; + hash_table = nullptr; hash_table_power = 0; elements = 0; } void operator=(const HashMap &p_table) { - copy_from(p_table); } - HashMap() { - hash_table = nullptr; - elements = 0; - hash_table_power = 0; - } - - void get_key_value_ptr_array(const Pair **p_pairs) const { - if (unlikely(!hash_table)) + 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; } } } - HashMap(const HashMap &p_table) { - - hash_table = nullptr; - elements = 0; - hash_table_power = 0; + HashMap() {} + HashMap(const HashMap &p_table) { copy_from(p_table); } ~HashMap() { - clear(); } }; diff --git a/core/hashfuncs.h b/core/templates/hashfuncs.h index a41a034843..4572b269cf 100644 --- a/core/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,12 +33,12 @@ #include "core/math/math_defs.h" #include "core/math/math_funcs.h" -#include "core/node_path.h" -#include "core/object_id.h" -#include "core/rid.h" -#include "core/string_name.h" +#include "core/object/object_id.h" +#include "core/string/node_path.h" +#include "core/string/string_name.h" +#include "core/string/ustring.h" +#include "core/templates/rid.h" #include "core/typedefs.h" -#include "core/ustring.h" /** * Hashing functions */ @@ -49,29 +49,28 @@ * @return 32-bits hashcode */ static inline uint32_t hash_djb2(const char *p_cstr) { - const unsigned char *chr = (const unsigned char *)p_cstr; uint32_t hash = 5381; uint32_t c; - while ((c = *chr++)) + while ((c = *chr++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } return hash; } static inline uint32_t hash_djb2_buffer(const uint8_t *p_buff, int p_len, uint32_t p_prev = 5381) { - uint32_t hash = p_prev; - for (int i = 0; i < p_len; i++) + for (int i = 0; i < p_len; i++) { hash = ((hash << 5) + hash) + p_buff[i]; /* hash * 33 + c */ + } return hash; } static inline uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = 5381) { - return ((p_prev << 5) + p_prev) + p_in; } @@ -93,19 +92,19 @@ static inline uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) } u; // Normalize +/- 0.0 and NaN values so they hash the same. - if (p_in == 0.0f) + if (p_in == 0.0f) { u.d = 0.0; - else if (Math::is_nan(p_in)) + } else if (Math::is_nan(p_in)) { u.d = Math_NAN; - else + } else { u.d = p_in; + } return ((p_prev << 5) + p_prev) + hash_one_uint64(u.i); } template <class T> static inline uint32_t make_uint32_t(T p_in) { - union { T t; uint32_t _u32; @@ -115,14 +114,30 @@ static inline uint32_t make_uint32_t(T p_in) { return _u._u32; } -static inline uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev = 5381) { +static inline uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev = 5381) { + union { + double d; + uint64_t i; + } u; + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.d = 0.0; + } else if (Math::is_nan(p_in)) { + u.d = Math_NAN; + } else { + u.d = p_in; + } + + return ((p_prev << 5) + p_prev) + u.i; +} + +static inline uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev = 5381) { return ((p_prev << 5) + p_prev) + p_in; } template <class T> static inline uint64_t make_uint64_t(T p_in) { - union { T t; uint64_t _u64; @@ -134,7 +149,6 @@ static inline uint64_t make_uint64_t(T p_in) { } struct HashMapHasherDefault { - static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); } static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); } @@ -150,6 +164,8 @@ struct HashMapHasherDefault { static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; } static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; } static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; } + static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return (uint32_t)p_uchar; } + static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return (uint32_t)p_uchar; } static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); } static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); } diff --git a/core/list.h b/core/templates/list.h index be2dccd876..eaf1dbb4a0 100644 --- a/core/list.h +++ b/core/templates/list.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,9 +31,9 @@ #ifndef LIST_H #define LIST_H -#include "core/error_macros.h" +#include "core/error/error_macros.h" #include "core/os/memory.h" -#include "core/sort_array.h" +#include "core/templates/sort_array.h" /** * Generic Templatized Linked List Implementation. @@ -49,127 +49,117 @@ class List { public: class Element { - private: friend class List<T, A>; T value; - Element *next_ptr; - Element *prev_ptr; - _Data *data; + Element *next_ptr = nullptr; + Element *prev_ptr = nullptr; + _Data *data = nullptr; public: /** * Get NEXT Element iterator, for constant lists. */ _FORCE_INLINE_ const Element *next() const { - return next_ptr; - }; + } /** * Get NEXT Element iterator, */ _FORCE_INLINE_ Element *next() { - return next_ptr; - }; + } /** * Get PREV Element iterator, for constant lists. */ _FORCE_INLINE_ const Element *prev() const { - return prev_ptr; - }; + } /** * Get PREV Element iterator, */ _FORCE_INLINE_ Element *prev() { - return prev_ptr; - }; + } /** * * operator, for using as *iterator, when iterators are defined on stack. */ _FORCE_INLINE_ const T &operator*() const { return value; - }; + } /** * operator->, for using as iterator->, when iterators are defined on stack, for constant lists. */ _FORCE_INLINE_ const T *operator->() const { - return &value; - }; + } /** * * operator, for using as *iterator, when iterators are defined on stack, */ _FORCE_INLINE_ T &operator*() { return value; - }; + } /** * operator->, for using as iterator->, when iterators are defined on stack, for constant lists. */ _FORCE_INLINE_ T *operator->() { return &value; - }; + } /** * get the value stored in this element. */ _FORCE_INLINE_ T &get() { return value; - }; + } /** * get the value stored in this element, for constant lists */ _FORCE_INLINE_ const T &get() const { return value; - }; + } /** * set the value stored in this element. */ _FORCE_INLINE_ void set(const T &p_value) { value = (T &)p_value; - }; + } void erase() { - data->erase(this); } - _FORCE_INLINE_ Element() { - next_ptr = 0; - prev_ptr = 0; - data = nullptr; - }; + _FORCE_INLINE_ Element() {} }; private: struct _Data { - - Element *first; - Element *last; - int size_cache; + Element *first = nullptr; + Element *last = nullptr; + int size_cache = 0; bool erase(const Element *p_I) { - ERR_FAIL_COND_V(!p_I, false); ERR_FAIL_COND_V(p_I->data != this, false); if (first == p_I) { first = p_I->next_ptr; - }; + } - if (last == p_I) + if (last == p_I) { last = p_I->prev_ptr; + } - if (p_I->prev_ptr) + if (p_I->prev_ptr) { p_I->prev_ptr->next_ptr = p_I->next_ptr; + } - if (p_I->next_ptr) + if (p_I->next_ptr) { p_I->next_ptr->prev_ptr = p_I->prev_ptr; + } memdelete_allocator<Element, A>(const_cast<Element *>(p_I)); size_cache--; @@ -178,47 +168,42 @@ private: } }; - _Data *_data; + _Data *_data = nullptr; public: /** * return a const iterator to the beginning of the list. */ _FORCE_INLINE_ const Element *front() const { - - return _data ? _data->first : 0; - }; + return _data ? _data->first : nullptr; + } /** * return an iterator to the beginning of the list. */ _FORCE_INLINE_ Element *front() { - return _data ? _data->first : 0; - }; + return _data ? _data->first : nullptr; + } /** * return a const iterator to the last member of the list. */ _FORCE_INLINE_ const Element *back() const { - - return _data ? _data->last : 0; - }; + return _data ? _data->last : nullptr; + } /** * return an iterator to the last member of the list. */ _FORCE_INLINE_ Element *back() { - - return _data ? _data->last : 0; - }; + return _data ? _data->last : nullptr; + } /** * store a new element at the end of the list */ Element *push_back(const T &value) { - if (!_data) { - _data = memnew_allocator(_Data, A); _data->first = nullptr; _data->last = nullptr; @@ -229,37 +214,35 @@ public: n->value = (T &)value; n->prev_ptr = _data->last; - n->next_ptr = 0; + n->next_ptr = nullptr; n->data = _data; if (_data->last) { - _data->last->next_ptr = n; } _data->last = n; - if (!_data->first) + if (!_data->first) { _data->first = n; + } _data->size_cache++; return n; - }; + } void pop_back() { - - if (_data && _data->last) + if (_data && _data->last) { erase(_data->last); + } } /** * store a new element at the beginning of the list */ Element *push_front(const T &value) { - if (!_data) { - _data = memnew_allocator(_Data, A); _data->first = nullptr; _data->last = nullptr; @@ -268,29 +251,29 @@ public: Element *n = memnew_allocator(Element, A); n->value = (T &)value; - n->prev_ptr = 0; + n->prev_ptr = nullptr; n->next_ptr = _data->first; n->data = _data; if (_data->first) { - _data->first->prev_ptr = n; } _data->first = n; - if (!_data->last) + if (!_data->last) { _data->last = n; + } _data->size_cache++; return n; - }; + } void pop_front() { - - if (_data && _data->first) + if (_data && _data->first) { erase(_data->first); + } } Element *insert_after(Element *p_element, const T &p_value) { @@ -350,22 +333,22 @@ public: */ template <class T_v> Element *find(const T_v &p_val) { - Element *it = front(); while (it) { - if (it->value == p_val) return it; + if (it->value == p_val) { + return it; + } it = it->next(); - }; + } return nullptr; - }; + } /** * erase an element in the list, by iterator pointing to it. Return true if it was found/erased. */ bool erase(const Element *p_I) { - - if (_data) { + if (_data && p_I) { bool ret = _data->erase(p_I); if (_data->size_cache == 0) { @@ -377,22 +360,20 @@ public: } return false; - }; + } /** * erase the first element in the list, that contains value */ bool erase(const T &value) { - Element *I = find(value); return erase(I); - }; + } /** * return whether the list is empty */ - _FORCE_INLINE_ bool empty() const { - + _FORCE_INLINE_ bool is_empty() const { return (!_data || !_data->size_cache); } @@ -400,58 +381,66 @@ public: * clear the list */ void clear() { - while (front()) { erase(front()); - }; - }; + } + } _FORCE_INLINE_ int size() const { - return _data ? _data->size_cache : 0; } void swap(Element *p_A, Element *p_B) { - ERR_FAIL_COND(!p_A || !p_B); ERR_FAIL_COND(p_A->data != _data); ERR_FAIL_COND(p_B->data != _data); + if (p_A == p_B) { + return; + } Element *A_prev = p_A->prev_ptr; Element *A_next = p_A->next_ptr; + Element *B_prev = p_B->prev_ptr; + Element *B_next = p_B->next_ptr; - p_A->next_ptr = p_B->next_ptr; - p_A->prev_ptr = p_B->prev_ptr; - - p_B->next_ptr = A_next; - p_B->prev_ptr = A_prev; - - if (p_A->prev_ptr) - p_A->prev_ptr->next_ptr = p_A; - if (p_A->next_ptr) - p_A->next_ptr->prev_ptr = p_A; - - if (p_B->prev_ptr) - p_B->prev_ptr->next_ptr = p_B; - if (p_B->next_ptr) - p_B->next_ptr->prev_ptr = p_B; + if (A_prev) { + A_prev->next_ptr = p_B; + } else { + _data->first = p_B; + } + if (B_prev) { + B_prev->next_ptr = p_A; + } else { + _data->first = p_A; + } + if (A_next) { + A_next->prev_ptr = p_B; + } else { + _data->last = p_B; + } + if (B_next) { + B_next->prev_ptr = p_A; + } else { + _data->last = p_A; + } + p_A->prev_ptr = A_next == p_B ? p_B : B_prev; + p_A->next_ptr = B_next == p_A ? p_B : B_next; + p_B->prev_ptr = B_next == p_A ? p_A : A_prev; + p_B->next_ptr = A_next == p_B ? p_A : A_next; } /** * copy the list */ void operator=(const List &p_list) { - clear(); const Element *it = p_list.front(); while (it) { - push_back(it->get()); it = it->next(); } } T &operator[](int p_index) { - CRASH_BAD_INDEX(p_index, size()); Element *I = front(); @@ -465,7 +454,6 @@ public: } const T &operator[](int p_index) const { - CRASH_BAD_INDEX(p_index, size()); const Element *I = front(); @@ -479,20 +467,22 @@ public: } void move_to_back(Element *p_I) { - ERR_FAIL_COND(p_I->data != _data); - if (!p_I->next_ptr) + if (!p_I->next_ptr) { return; + } if (_data->first == p_I) { _data->first = p_I->next_ptr; - }; + } - if (_data->last == p_I) + if (_data->last == p_I) { _data->last = p_I->prev_ptr; + } - if (p_I->prev_ptr) + if (p_I->prev_ptr) { p_I->prev_ptr->next_ptr = p_I->next_ptr; + } p_I->next_ptr->prev_ptr = p_I->prev_ptr; @@ -503,12 +493,10 @@ public: } void invert() { - int s = size() / 2; Element *F = front(); Element *B = back(); for (int i = 0; i < s; i++) { - SWAP(F->value, B->value); F = F->next(); B = B->prev(); @@ -516,22 +504,24 @@ public: } void move_to_front(Element *p_I) { - ERR_FAIL_COND(p_I->data != _data); - if (!p_I->prev_ptr) + if (!p_I->prev_ptr) { return; + } if (_data->first == p_I) { _data->first = p_I->next_ptr; - }; + } - if (_data->last == p_I) + if (_data->last == p_I) { _data->last = p_I->prev_ptr; + } p_I->prev_ptr->next_ptr = p_I->next_ptr; - if (p_I->next_ptr) + if (p_I->next_ptr) { p_I->next_ptr->prev_ptr = p_I->prev_ptr; + } _data->first->prev_ptr = p_I; p_I->next_ptr = _data->first; @@ -540,7 +530,6 @@ public: } void move_before(Element *value, Element *where) { - if (value->prev_ptr) { value->prev_ptr->next_ptr = value->next_ptr; } else { @@ -557,7 +546,7 @@ public: value->prev_ptr = _data->last; _data->last = value; return; - }; + } value->prev_ptr = where->prev_ptr; @@ -565,59 +554,56 @@ public: where->prev_ptr->next_ptr = value; } else { _data->first = value; - }; + } where->prev_ptr = value; - }; + } /** * simple insertion sort */ void sort() { - sort_custom<Comparator<T>>(); } template <class C> void sort_custom_inplace() { - - if (size() < 2) + if (size() < 2) { return; + } Element *from = front(); Element *current = from; Element *to = from; while (current) { - Element *next = current->next_ptr; if (from != current) { - current->prev_ptr = nullptr; current->next_ptr = from; Element *find = from; C less; while (find && less(find->value, current->value)) { - current->prev_ptr = find; current->next_ptr = find->next_ptr; find = find->next_ptr; } - if (current->prev_ptr) + if (current->prev_ptr) { current->prev_ptr->next_ptr = current; - else + } else { from = current; + } - if (current->next_ptr) + if (current->next_ptr) { current->next_ptr->prev_ptr = current; - else + } else { to = current; + } } else { - current->prev_ptr = nullptr; current->next_ptr = nullptr; } @@ -630,29 +616,26 @@ public: template <class C> struct AuxiliaryComparator { - C compare; _FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const { - return compare(a->value, b->value); } }; template <class C> void sort_custom() { - //this version uses auxiliary memory for speed. //if you don't want to use auxiliary memory, use the in_place version int s = size(); - if (s < 2) + if (s < 2) { return; + } Element **aux_buffer = memnew_arr(Element *, s); int idx = 0; for (Element *E = front(); E; E = E->next_ptr) { - aux_buffer[idx] = E; idx++; } @@ -669,7 +652,6 @@ public: aux_buffer[s - 1]->next_ptr = nullptr; for (int i = 1; i < s - 1; i++) { - aux_buffer[i]->prev_ptr = aux_buffer[i - 1]; aux_buffer[i]->next_ptr = aux_buffer[i + 1]; } @@ -685,27 +667,22 @@ public: * copy constructor for the list */ List(const List &p_list) { - - _data = nullptr; const Element *it = p_list.front(); while (it) { - push_back(it->get()); it = it->next(); } } - List() { - _data = nullptr; - }; + List() {} + ~List() { clear(); if (_data) { - ERR_FAIL_COND(_data->size_cache); memdelete_allocator<_Data, A>(_data); } - }; + } }; #endif // LIST_H diff --git a/core/templates/local_vector.h b/core/templates/local_vector.h new file mode 100644 index 0000000000..ffd17b7ee9 --- /dev/null +++ b/core/templates/local_vector.h @@ -0,0 +1,260 @@ +/*************************************************************************/ +/* local_vector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 LOCAL_VECTOR_H +#define LOCAL_VECTOR_H + +#include "core/error/error_macros.h" +#include "core/os/copymem.h" +#include "core/os/memory.h" +#include "core/templates/sort_array.h" +#include "core/templates/vector.h" + +template <class T, class U = uint32_t, bool force_trivial = false> +class LocalVector { +private: + U count = 0; + U capacity = 0; + T *data = nullptr; + +public: + T *ptr() { + return data; + } + + const T *ptr() const { + return data; + } + + _FORCE_INLINE_ void push_back(T p_elem) { + if (unlikely(count == capacity)) { + if (capacity == 0) { + capacity = 1; + } else { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + + if (!__has_trivial_constructor(T) && !force_trivial) { + memnew_placement(&data[count++], T(p_elem)); + } else { + data[count++] = p_elem; + } + } + + void remove(U p_index) { + ERR_FAIL_UNSIGNED_INDEX(p_index, count); + count--; + for (U i = p_index; i < count; i++) { + data[i] = data[i + 1]; + } + if (!__has_trivial_destructor(T) && !force_trivial) { + data[count].~T(); + } + } + + /// Removes the item copying the last value into the position of the one to + /// remove. It's generally faster than `remove`. + void remove_unordered(U p_index) { + ERR_FAIL_INDEX(p_index, count); + count--; + if (count > p_index) { + data[p_index] = data[count]; + } + if (!__has_trivial_destructor(T) && !force_trivial) { + data[count].~T(); + } + } + + void erase(const T &p_val) { + int64_t idx = find(p_val); + if (idx >= 0) { + remove(idx); + } + } + + void invert() { + for (U i = 0; i < count / 2; i++) { + SWAP(data[i], data[count - i - 1]); + } + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ void reset() { + clear(); + if (data) { + memfree(data); + data = nullptr; + capacity = 0; + } + } + _FORCE_INLINE_ bool is_empty() const { return count == 0; } + _FORCE_INLINE_ U get_capacity() const { return capacity; } + _FORCE_INLINE_ void reserve(U p_size) { + p_size = nearest_power_of_2_templated(p_size); + if (p_size > capacity) { + capacity = p_size; + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + } + + _FORCE_INLINE_ U size() const { return count; } + void resize(U p_size) { + if (p_size < count) { + if (!__has_trivial_destructor(T) && !force_trivial) { + for (U i = p_size; i < count; i++) { + data[i].~T(); + } + } + count = p_size; + } else if (p_size > count) { + if (unlikely(p_size > capacity)) { + if (capacity == 0) { + capacity = 1; + } + while (capacity < p_size) { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + if (!__has_trivial_constructor(T) && !force_trivial) { + for (U i = count; i < p_size; i++) { + memnew_placement(&data[i], T); + } + } + count = p_size; + } + } + _FORCE_INLINE_ const T &operator[](U p_index) const { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + _FORCE_INLINE_ T &operator[](U p_index) { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + + void insert(U p_pos, T p_val) { + ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1); + if (p_pos == count) { + push_back(p_val); + } else { + resize(count + 1); + for (U i = count; i > p_pos; i--) { + data[i] = data[i - 1]; + } + data[p_pos] = p_val; + } + } + + int64_t find(const T &p_val, U p_from = 0) const { + for (U i = 0; i < count; i++) { + if (data[i] == p_val) { + return int64_t(i); + } + } + return -1; + } + + template <class C> + void sort_custom() { + U len = count; + if (len == 0) { + return; + } + + SortArray<T, C> sorter; + sorter.sort(data, len); + } + + void sort() { + sort_custom<_DefaultComparator<T>>(); + } + + void ordered_insert(T p_val) { + U i; + for (i = 0; i < count; i++) { + if (p_val < data[i]) { + break; + } + } + insert(i, p_val); + } + + operator Vector<T>() const { + Vector<T> ret; + ret.resize(size()); + T *w = ret.ptrw(); + copymem(w, data, sizeof(T) * count); + return ret; + } + + Vector<uint8_t> to_byte_array() const { //useful to pass stuff to gpu or variant + Vector<uint8_t> ret; + ret.resize(count * sizeof(T)); + uint8_t *w = ret.ptrw(); + copymem(w, data, sizeof(T) * count); + return ret; + } + + _FORCE_INLINE_ LocalVector() {} + _FORCE_INLINE_ LocalVector(const LocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + } + inline LocalVector &operator=(const LocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + return *this; + } + inline LocalVector &operator=(const Vector<T> &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + return *this; + } + + _FORCE_INLINE_ ~LocalVector() { + if (data) { + reset(); + } + } +}; + +#endif // LOCAL_VECTOR_H diff --git a/core/io/file_access_buffered.h b/core/templates/lru.h index a6177c20be..e55e40da48 100644 --- a/core/io/file_access_buffered.h +++ b/core/templates/lru.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* file_access_buffered.h */ +/* lru.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,67 +28,99 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef FILE_ACCESS_BUFFERED_H -#define FILE_ACCESS_BUFFERED_H +#ifndef LRU_H +#define LRU_H -#include "core/os/file_access.h" - -#include "core/ustring.h" - -class FileAccessBuffered : public FileAccess { - -public: - enum { - DEFAULT_CACHE_SIZE = 128 * 1024, - }; +#include "core/math/math_funcs.h" +#include "hash_map.h" +#include "list.h" +template <class TKey, class TData> +class LRUCache { private: - int cache_size; - - int cache_data_left() const; - mutable Error last_error; - -protected: - Error set_error(Error p_error) const; - - mutable struct File { - - bool open; - int size; - int offset; - String name; - int access_flags; - } file; - - mutable struct Cache { - - Vector<uint8_t> buffer; - int offset; - } cache; + struct Pair { + TKey key; + TData data; + + Pair() {} + Pair(const TKey &p_key, const TData &p_data) : + key(p_key), + data(p_data) { + } + }; - virtual int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const = 0; + typedef typename List<Pair>::Element *Element; - void set_cache_size(int p_size); - int get_cache_size(); + List<Pair> _list; + HashMap<TKey, Element> _map; + size_t capacity; public: - virtual size_t get_position() const; ///< get position in the file - virtual size_t get_len() const; ///< get size of the file - - virtual void seek(size_t p_position); ///< seek to a given position - virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file - - virtual bool eof_reached() const; - - virtual uint8_t get_8() const; - virtual int get_buffer(uint8_t *p_dest, int p_length) const; ///< get an array of bytes - - virtual bool is_open() const; - - virtual Error get_error() const; + const TData *insert(const TKey &p_key, const TData &p_value) { + Element *e = _map.getptr(p_key); + Element n = _list.push_front(Pair(p_key, p_value)); + + if (e) { + _list.erase(*e); + _map.erase(p_key); + } + _map[p_key] = _list.front(); + + while (_map.size() > capacity) { + Element d = _list.back(); + _map.erase(d->get().key); + _list.pop_back(); + } + + return &n->get().data; + } + + void clear() { + _map.clear(); + _list.clear(); + } + + bool has(const TKey &p_key) const { + return _map.getptr(p_key); + } + + const TData &get(const TKey &p_key) { + Element *e = _map.getptr(p_key); + CRASH_COND(!e); + _list.move_to_front(*e); + return (*e)->get().data; + }; - FileAccessBuffered(); - virtual ~FileAccessBuffered(); + const TData *getptr(const TKey &p_key) { + Element *e = _map.getptr(p_key); + if (!e) { + return nullptr; + } else { + _list.move_to_front(*e); + return &(*e)->get().data; + } + } + + _FORCE_INLINE_ size_t get_capacity() const { return capacity; } + + void set_capacity(size_t p_capacity) { + if (capacity > 0) { + capacity = p_capacity; + while (_map.size() > capacity) { + Element d = _list.back(); + _map.erase(d->get().key); + _list.pop_back(); + } + } + } + + LRUCache() { + capacity = 64; + } + + LRUCache(int p_capacity) { + capacity = p_capacity; + } }; #endif diff --git a/core/map.h b/core/templates/map.h index 6b9dff51de..51a237472d 100644 --- a/core/map.h +++ b/core/templates/map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,15 +31,14 @@ #ifndef MAP_H #define MAP_H -#include "core/error_macros.h" -#include "core/set.h" +#include "core/error/error_macros.h" +#include "core/templates/set.h" // based on the very nice implementation of rb-trees by: // https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator> class Map { - enum Color { RED, BLACK @@ -48,67 +47,54 @@ class Map { public: class Element { - private: friend class Map<K, V, C, A>; - int color; - Element *right; - Element *left; - Element *parent; - Element *_next; - Element *_prev; + int color = RED; + Element *right = nullptr; + Element *left = nullptr; + Element *parent = nullptr; + Element *_next = nullptr; + Element *_prev = nullptr; K _key; V _value; //_Data *data; public: const Element *next() const { - return _next; } Element *next() { - return _next; } const Element *prev() const { - return _prev; } Element *prev() { - return _prev; } const K &key() const { return _key; - }; + } V &value() { return _value; - }; + } const V &value() const { return _value; - }; + } V &get() { return _value; - }; + } const V &get() const { return _value; - }; - Element() { - color = RED; - right = nullptr; - left = nullptr; - parent = nullptr; - _next = nullptr; - _prev = nullptr; - }; + } + Element() {} }; private: struct _Data { - - Element *_root; + Element *_root = nullptr; Element *_nil; - int size_cache; + int size_cache = 0; _FORCE_INLINE_ _Data() { #ifdef GLOBALNIL_DISABLED @@ -118,19 +104,15 @@ private: #else _nil = (Element *)&_GlobalNilClass::_nil; #endif - _root = nullptr; - size_cache = 0; } void _create_root() { - _root = memnew_allocator(Element, A); _root->parent = _root->left = _root->right = _nil; _root->color = BLACK; } void _free_root() { - if (_root) { memdelete_allocator<Element, A>(_root); _root = nullptr; @@ -138,7 +120,6 @@ private: } ~_Data() { - _free_root(); #ifdef GLOBALNIL_DISABLED @@ -150,62 +131,61 @@ private: _Data _data; inline void _set_color(Element *p_node, int p_color) { - ERR_FAIL_COND(p_node == _data._nil && p_color == RED); p_node->color = p_color; } inline void _rotate_left(Element *p_node) { - Element *r = p_node->right; p_node->right = r->left; - if (r->left != _data._nil) + if (r->left != _data._nil) { r->left->parent = p_node; + } r->parent = p_node->parent; - if (p_node == p_node->parent->left) + if (p_node == p_node->parent->left) { p_node->parent->left = r; - else + } else { p_node->parent->right = r; + } r->left = p_node; p_node->parent = r; } inline void _rotate_right(Element *p_node) { - Element *l = p_node->left; p_node->left = l->right; - if (l->right != _data._nil) + if (l->right != _data._nil) { l->right->parent = p_node; + } l->parent = p_node->parent; - if (p_node == p_node->parent->right) + if (p_node == p_node->parent->right) { p_node->parent->right = l; - else + } else { p_node->parent->left = l; + } l->right = p_node; p_node->parent = l; } inline Element *_successor(Element *p_node) const { - Element *node = p_node; if (node->right != _data._nil) { - node = node->right; while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */ node = node->left; } return node; } else { - while (node == node->parent->right) { node = node->parent; } - if (node->parent == _data._root) + if (node->parent == _data._root) { return nullptr; // No successor, as p_node = last node + } return node->parent; } } @@ -214,43 +194,41 @@ private: Element *node = p_node; if (node->left != _data._nil) { - node = node->left; while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */ node = node->right; } return node; } else { - while (node == node->parent->left) { node = node->parent; } - if (node == _data._root) + if (node == _data._root) { return nullptr; // No predecessor, as p_node = first node + } return node->parent; } } Element *_find(const K &p_key) const { - Element *node = _data._root->left; C less; while (node != _data._nil) { - if (less(p_key, node->_key)) + if (less(p_key, node->_key)) { node = node->left; - else if (less(node->_key, p_key)) + } else if (less(node->_key, p_key)) { node = node->right; - else + } else { return node; // found + } } return nullptr; } Element *_find_closest(const K &p_key) const { - Element *node = _data._root->left; Element *prev = nullptr; C less; @@ -258,25 +236,27 @@ private: while (node != _data._nil) { prev = node; - if (less(p_key, node->_key)) + if (less(p_key, node->_key)) { node = node->left; - else if (less(node->_key, p_key)) + } else if (less(node->_key, p_key)) { node = node->right; - else + } else { return node; // found + } } - if (prev == nullptr) + if (prev == nullptr) { return nullptr; // tree empty + } - if (less(p_key, prev->_key)) + if (less(p_key, prev->_key)) { prev = prev->_prev; + } return prev; } void _insert_rb_fix(Element *p_new_node) { - Element *node = p_new_node; Element *nparent = node->parent; Element *ngrand_parent; @@ -325,20 +305,18 @@ private: } Element *_insert(const K &p_key, const V &p_value) { - Element *new_parent = _data._root; Element *node = _data._root->left; C less; while (node != _data._nil) { - new_parent = node; - if (less(p_key, node->_key)) + if (less(p_key, node->_key)) { node = node->left; - else if (less(node->_key, p_key)) + } else if (less(node->_key, p_key)) { node = node->right; - else { + } else { node->_value = p_value; return node; // Return existing node with new value } @@ -360,10 +338,12 @@ private: new_node->_next = _successor(new_node); new_node->_prev = _predecessor(new_node); - if (new_node->_next) + if (new_node->_next) { new_node->_next->_prev = new_node; - if (new_node->_prev) + } + if (new_node->_prev) { new_node->_prev->_next = new_node; + } _data.size_cache++; _insert_rb_fix(new_node); @@ -371,7 +351,6 @@ private: } void _erase_fix_rb(Element *p_node) { - Element *root = _data._root->left; Element *node = _data._nil; Element *sibling = p_node; @@ -433,7 +412,6 @@ private: } void _erase(Element *p_node) { - Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next; Element *node = (rp->left == _data._nil) ? rp->right : rp->left; @@ -454,17 +432,18 @@ private: } if (rp != p_node) { - ERR_FAIL_COND(rp == _data._nil); rp->left = p_node->left; rp->right = p_node->right; rp->parent = p_node->parent; rp->color = p_node->color; - if (p_node->left != _data._nil) + if (p_node->left != _data._nil) { p_node->left->parent = rp; - if (p_node->right != _data._nil) + } + if (p_node->right != _data._nil) { p_node->right->parent = rp; + } if (p_node == p_node->parent->left) { p_node->parent->left = rp; @@ -473,10 +452,12 @@ private: } } - if (p_node->_next) + if (p_node->_next) { p_node->_next->_prev = p_node->_prev; - if (p_node->_prev) + } + if (p_node->_prev) { p_node->_prev->_next = p_node->_next; + } memdelete_allocator<Element, A>(p_node); _data.size_cache--; @@ -484,21 +465,22 @@ private: } void _calculate_depth(Element *p_element, int &max_d, int d) const { - - if (p_element == _data._nil) + if (p_element == _data._nil) { return; + } _calculate_depth(p_element->left, max_d, d + 1); _calculate_depth(p_element->right, max_d, d + 1); - if (d > max_d) + if (d > max_d) { max_d = d; + } } void _cleanup_tree(Element *p_element) { - - if (p_element == _data._nil) + if (p_element == _data._nil) { return; + } _cleanup_tree(p_element->left); _cleanup_tree(p_element->right); @@ -506,91 +488,90 @@ private: } void _copy_from(const Map &p_map) { - clear(); // not the fastest way, but safeset to write. for (Element *I = p_map.front(); I; I = I->next()) { - insert(I->key(), I->value()); } } public: const Element *find(const K &p_key) const { - - if (!_data._root) + if (!_data._root) { return nullptr; + } const Element *res = _find(p_key); return res; } Element *find(const K &p_key) { - - if (!_data._root) + if (!_data._root) { return nullptr; + } Element *res = _find(p_key); return res; } const Element *find_closest(const K &p_key) const { - - if (!_data._root) + if (!_data._root) { return nullptr; + } const Element *res = _find_closest(p_key); return res; } Element *find_closest(const K &p_key) { - - if (!_data._root) + if (!_data._root) { return nullptr; + } Element *res = _find_closest(p_key); return res; } bool has(const K &p_key) const { - return find(p_key) != nullptr; } Element *insert(const K &p_key, const V &p_value) { - - if (!_data._root) + if (!_data._root) { _data._create_root(); + } return _insert(p_key, p_value); } void erase(Element *p_element) { - - if (!_data._root || !p_element) + if (!_data._root || !p_element) { return; + } _erase(p_element); - if (_data.size_cache == 0 && _data._root) + if (_data.size_cache == 0 && _data._root) { _data._free_root(); + } } bool erase(const K &p_key) { - - if (!_data._root) + if (!_data._root) { return false; + } Element *e = find(p_key); - if (!e) + if (!e) { return false; + } _erase(e); - if (_data.size_cache == 0 && _data._root) + if (_data.size_cache == 0 && _data._root) { _data._free_root(); + } return true; } const V &operator[](const K &p_key) const { - CRASH_COND(!_data._root); const Element *e = find(p_key); CRASH_COND(!e); @@ -598,54 +579,60 @@ public: } V &operator[](const K &p_key) { - - if (!_data._root) + if (!_data._root) { _data._create_root(); + } Element *e = find(p_key); - if (!e) + if (!e) { e = insert(p_key, V()); + } return e->_value; } Element *front() const { - - if (!_data._root) + if (!_data._root) { return nullptr; + } Element *e = _data._root->left; - if (e == _data._nil) + if (e == _data._nil) { return nullptr; + } - while (e->left != _data._nil) + while (e->left != _data._nil) { e = e->left; + } return e; } Element *back() const { - - if (!_data._root) + if (!_data._root) { return nullptr; + } Element *e = _data._root->left; - if (e == _data._nil) + if (e == _data._nil) { return nullptr; + } - while (e->right != _data._nil) + while (e->right != _data._nil) { e = e->right; + } return e; } - inline bool empty() const { return _data.size_cache == 0; } + inline bool is_empty() const { return _data.size_cache == 0; } inline int size() const { return _data.size_cache; } int calculate_depth() const { // used for debug mostly - if (!_data._root) + if (!_data._root) { return 0; + } int max_d = 0; _calculate_depth(_data._root->left, max_d, 0); @@ -653,9 +640,9 @@ public: } void clear() { - - if (!_data._root) + if (!_data._root) { return; + } _cleanup_tree(_data._root->left); _data._root->left = _data._nil; @@ -664,20 +651,16 @@ public: } void operator=(const Map &p_map) { - _copy_from(p_map); } Map(const Map &p_map) { - _copy_from(p_map); } - _FORCE_INLINE_ Map() { - } + _FORCE_INLINE_ Map() {} ~Map() { - clear(); } }; diff --git a/core/oa_hash_map.h b/core/templates/oa_hash_map.h index 71e3ba9068..1d4176eb10 100644 --- a/core/oa_hash_map.h +++ b/core/templates/oa_hash_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,10 +31,10 @@ #ifndef OA_HASH_MAP_H #define OA_HASH_MAP_H -#include "core/hashfuncs.h" #include "core/math/math_funcs.h" #include "core/os/copymem.h" #include "core/os/memory.h" +#include "core/templates/hashfuncs.h" /** * A HashMap implementation that uses open addressing with Robin Hood hashing. @@ -45,20 +45,24 @@ * * The entries are stored inplace, so huge keys or values might fill cache lines * a lot faster. + * + * 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; + uint32_t num_elements = 0; static const uint32_t EMPTY_HASH = 0; @@ -90,7 +94,7 @@ private: uint32_t pos = hash % capacity; uint32_t distance = 0; - while (42) { + while (true) { if (hashes[pos] == EMPTY_HASH) { return false; } @@ -110,7 +114,6 @@ private: } void _insert_with_hash(uint32_t p_hash, const TKey &p_key, const TValue &p_value) { - uint32_t hash = p_hash; uint32_t distance = 0; uint32_t pos = hash % capacity; @@ -118,7 +121,7 @@ private: TKey key = p_key; TValue value = p_value; - while (42) { + while (true) { if (hashes[pos] == EMPTY_HASH) { _construct(pos, hash, key, value); @@ -140,34 +143,43 @@ 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; uint32_t *old_hashes = hashes; num_elements = 0; - keys = memnew_arr(TKey, capacity); - values = memnew_arr(TValue, capacity); - hashes = memnew_arr(uint32_t, 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 < capacity; i++) { 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; } _insert_with_hash(old_hashes[i], old_keys[i], old_values[i]); + + old_keys[i].~TKey(); + old_values[i].~TValue(); } - memdelete_arr(old_keys); - memdelete_arr(old_values); - memdelete_arr(old_hashes); + Memory::free_static(old_keys); + Memory::free_static(old_values); + Memory::free_static(old_hashes); } void _resize_and_rehash() { @@ -178,14 +190,12 @@ public: _FORCE_INLINE_ uint32_t get_capacity() const { return capacity; } _FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; } - bool empty() const { + bool is_empty() const { return num_elements == 0; } void clear() { - for (uint32_t i = 0; i < capacity; i++) { - if (hashes[i] == EMPTY_HASH) { continue; } @@ -199,7 +209,6 @@ public: } void insert(const TKey &p_key, const TValue &p_value) { - if (num_elements + 1 > 0.9 * capacity) { _resize_and_rehash(); } @@ -214,8 +223,7 @@ public: bool exists = _lookup_pos(p_key, pos); if (exists) { - values[pos].~TValue(); - memnew_placement(&values[pos], TValue(p_data)); + values[pos] = p_data; } else { insert(p_key, p_data); } @@ -232,8 +240,7 @@ public: bool exists = _lookup_pos(p_key, pos); if (exists) { - r_data.~TValue(); - memnew_placement(&r_data, TValue(values[pos])); + r_data = values[pos]; return true; } @@ -300,7 +307,7 @@ public: bool valid; const TKey *key; - const TValue *value; + TValue *value; private: uint32_t pos; @@ -317,7 +324,6 @@ public: } Iterator next_iter(const Iterator &p_iter) const { - if (!p_iter.valid) { return p_iter; } @@ -344,28 +350,49 @@ 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(uint32_t p_initial_capacity = 64) { + OAHashMap &operator=(const OAHashMap &p_other) { + if (capacity != 0) { + clear(); + } - capacity = p_initial_capacity; - num_elements = 0; + _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 can't be 0. + capacity = MAX(1, p_initial_capacity); - keys = memnew_arr(TKey, p_initial_capacity); - values = memnew_arr(TValue, p_initial_capacity); - hashes = memnew_arr(uint32_t, 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; } } ~OAHashMap() { + for (uint32_t i = 0; i < capacity; i++) { + if (hashes[i] == EMPTY_HASH) { + continue; + } + + values[i].~TValue(); + keys[i].~TKey(); + } - memdelete_arr(keys); - memdelete_arr(values); - memdelete_arr(hashes); + Memory::free_static(keys); + Memory::free_static(values); + Memory::free_static(hashes); } }; diff --git a/core/ordered_hash_map.h b/core/templates/ordered_hash_map.h index 05debd529f..7a17eeb644 100644 --- a/core/ordered_hash_map.h +++ b/core/templates/ordered_hash_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,9 +31,9 @@ #ifndef ORDERED_HASH_MAP_H #define ORDERED_HASH_MAP_H -#include "core/hash_map.h" -#include "core/list.h" -#include "core/pair.h" +#include "core/templates/hash_map.h" +#include "core/templates/list.h" +#include "core/templates/pair.h" /** * A hash map which allows to iterate elements in insertion order. @@ -55,9 +55,9 @@ public: class Element { friend class OrderedHashMap<K, V, Hasher, Comparator, MIN_HASH_TABLE_POWER, RELATIONSHIP>; - typename InternalList::Element *list_element; - typename InternalList::Element *prev_element; - typename InternalList::Element *next_element; + typename InternalList::Element *list_element = nullptr; + typename InternalList::Element *prev_element = nullptr; + typename InternalList::Element *next_element = nullptr; Element(typename InternalList::Element *p_element) { list_element = p_element; @@ -69,11 +69,7 @@ public: } public: - _FORCE_INLINE_ Element() : - list_element(nullptr), - prev_element(nullptr), - next_element(nullptr) { - } + _FORCE_INLINE_ Element() {} Element next() const { return Element(next_element); @@ -110,42 +106,40 @@ public: const K &key() const { CRASH_COND(!list_element); return *(list_element->get().first); - }; + } V &value() { CRASH_COND(!list_element); return list_element->get().second; - }; + } const V &value() const { CRASH_COND(!list_element); return list_element->get().second; - }; + } V &get() { CRASH_COND(!list_element); return list_element->get().second; - }; + } const V &get() const { CRASH_COND(!list_element); return list_element->get().second; - }; + } }; class ConstElement { friend class OrderedHashMap<K, V, Hasher, Comparator, MIN_HASH_TABLE_POWER, RELATIONSHIP>; - const typename InternalList::Element *list_element; + const typename InternalList::Element *list_element = nullptr; ConstElement(const typename InternalList::Element *p_element) : list_element(p_element) { } public: - _FORCE_INLINE_ ConstElement() : - list_element(nullptr) { - } + _FORCE_INLINE_ ConstElement() {} ConstElement(const ConstElement &other) : list_element(other.list_element) { @@ -178,17 +172,17 @@ public: const K &key() const { CRASH_COND(!list_element); return *(list_element->get().first); - }; + } const V &value() const { CRASH_COND(!list_element); return list_element->get().second; - }; + } const V &get() const { CRASH_COND(!list_element); return list_element->get().second; - }; + } }; ConstElement find(const K &p_key) const { @@ -271,7 +265,7 @@ public: return ConstElement(list.back()); } - inline bool empty() const { return list.empty(); } + inline bool is_empty() const { return list.is_empty(); } inline int size() const { return list.size(); } const void *id() const { @@ -299,8 +293,7 @@ public: _copy_from(p_map); } - _FORCE_INLINE_ OrderedHashMap() { - } + _FORCE_INLINE_ OrderedHashMap() {} }; #endif // ORDERED_HASH_MAP_H diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h new file mode 100644 index 0000000000..7002034710 --- /dev/null +++ b/core/templates/paged_allocator.h @@ -0,0 +1,129 @@ +/*************************************************************************/ +/* paged_allocator.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 PAGED_ALLOCATOR_H +#define PAGED_ALLOCATOR_H + +#include "core/os/memory.h" +#include "core/os/spin_lock.h" +#include "core/typedefs.h" + +template <class T, bool thread_safe = false> +class PagedAllocator { + T **page_pool = nullptr; + T ***available_pool = nullptr; + uint32_t pages_allocated = 0; + uint32_t allocs_available = 0; + + uint32_t page_shift = 0; + uint32_t page_mask = 0; + uint32_t page_size = 0; + SpinLock spin_lock; + +public: + T *alloc() { + if (thread_safe) { + spin_lock.lock(); + } + if (unlikely(allocs_available == 0)) { + uint32_t pages_used = pages_allocated; + + pages_allocated++; + page_pool = (T **)memrealloc(page_pool, sizeof(T *) * pages_allocated); + available_pool = (T ***)memrealloc(available_pool, sizeof(T **) * pages_allocated); + + page_pool[pages_used] = (T *)memalloc(sizeof(T) * page_size); + available_pool[pages_used] = (T **)memalloc(sizeof(T *) * page_size); + + for (uint32_t i = 0; i < page_size; i++) { + available_pool[0][i] = &page_pool[pages_used][i]; + } + allocs_available += page_size; + } + + allocs_available--; + T *alloc = available_pool[allocs_available >> page_shift][allocs_available & page_mask]; + if (thread_safe) { + spin_lock.unlock(); + } + memnew_placement(alloc, T); + return alloc; + } + + void free(T *p_mem) { + if (thread_safe) { + spin_lock.lock(); + } + p_mem->~T(); + available_pool[allocs_available >> page_shift][allocs_available & page_mask] = p_mem; + if (thread_safe) { + spin_lock.unlock(); + } + allocs_available++; + } + + void reset() { + ERR_FAIL_COND(allocs_available < pages_allocated * page_size); + if (pages_allocated) { + for (uint32_t i = 0; i < pages_allocated; i++) { + memfree(page_pool[i]); + memfree(available_pool[i]); + } + memfree(page_pool); + memfree(available_pool); + page_pool = nullptr; + available_pool = nullptr; + pages_allocated = 0; + allocs_available = 0; + } + } + bool is_configured() const { + return page_size > 0; + } + + void configure(uint32_t p_page_size) { + ERR_FAIL_COND(page_pool != nullptr); //sanity check + ERR_FAIL_COND(p_page_size == 0); + page_size = nearest_power_of_2_templated(p_page_size); + page_mask = page_size - 1; + page_shift = get_shift_from_power_of_2(page_size); + } + + PagedAllocator(uint32_t p_page_size = 4096) { // power of 2 recommended because of alignment with OS page sizes. Even if element is bigger, its still a multiple and get rounded amount of pages + configure(p_page_size); + } + + ~PagedAllocator() { + ERR_FAIL_COND_MSG(allocs_available < pages_allocated * page_size, "Pages in use exist at exit in PagedAllocator"); + reset(); + } +}; + +#endif // PAGED_ALLOCATOR_H diff --git a/core/templates/paged_array.h b/core/templates/paged_array.h new file mode 100644 index 0000000000..599d3dde4f --- /dev/null +++ b/core/templates/paged_array.h @@ -0,0 +1,367 @@ +/*************************************************************************/ +/* paged_array.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 PAGED_ARRAY_H +#define PAGED_ARRAY_H + +#include "core/os/memory.h" +#include "core/os/spin_lock.h" +#include "core/typedefs.h" + +// PagedArray is used mainly for filling a very large array from multiple threads efficiently and without causing major fragmentation + +// PageArrayPool manages central page allocation in a thread safe matter + +template <class T> +class PagedArrayPool { + T **page_pool = nullptr; + uint32_t pages_allocated = 0; + + uint32_t *available_page_pool = nullptr; + uint32_t pages_available = 0; + + uint32_t page_size = 0; + SpinLock spin_lock; + +public: + uint32_t alloc_page() { + spin_lock.lock(); + if (unlikely(pages_available == 0)) { + uint32_t pages_used = pages_allocated; + + pages_allocated++; + page_pool = (T **)memrealloc(page_pool, sizeof(T *) * pages_allocated); + available_page_pool = (uint32_t *)memrealloc(available_page_pool, sizeof(uint32_t) * pages_allocated); + + page_pool[pages_used] = (T *)memalloc(sizeof(T) * page_size); + available_page_pool[0] = pages_used; + + pages_available++; + } + + pages_available--; + uint32_t page = available_page_pool[pages_available]; + spin_lock.unlock(); + + return page; + } + T *get_page(uint32_t p_page_id) { + return page_pool[p_page_id]; + } + + void free_page(uint32_t p_page_id) { + spin_lock.lock(); + available_page_pool[pages_available] = p_page_id; + pages_available++; + spin_lock.unlock(); + } + + uint32_t get_page_size_shift() const { + return get_shift_from_power_of_2(page_size); + } + + uint32_t get_page_size_mask() const { + return page_size - 1; + } + + void reset() { + ERR_FAIL_COND(pages_available < pages_allocated); + if (pages_allocated) { + for (uint32_t i = 0; i < pages_allocated; i++) { + memfree(page_pool[i]); + } + memfree(page_pool); + memfree(available_page_pool); + page_pool = nullptr; + available_page_pool = nullptr; + pages_allocated = 0; + pages_available = 0; + } + } + bool is_configured() const { + return page_size > 0; + } + + void configure(uint32_t p_page_size) { + ERR_FAIL_COND(page_pool != nullptr); //sanity check + ERR_FAIL_COND(p_page_size == 0); + page_size = nearest_power_of_2_templated(p_page_size); + } + + PagedArrayPool(uint32_t p_page_size = 4096) { // power of 2 recommended because of alignment with OS page sizes. Even if element is bigger, its still a multiple and get rounded amount of pages + configure(p_page_size); + } + + ~PagedArrayPool() { + ERR_FAIL_COND_MSG(pages_available < pages_allocated, "Pages in use exist at exit in PagedArrayPool"); + reset(); + } +}; + +// PageArray is a local array that is optimized to grow in place, then be cleared often. +// It does so by allocating pages from a PagedArrayPool. +// It is safe to use multiple PagedArrays from different threads, sharing a single PagedArrayPool + +template <class T> +class PagedArray { + PagedArrayPool<T> *page_pool = nullptr; + + T **page_data = nullptr; + uint32_t *page_ids = nullptr; + uint32_t max_pages_used = 0; + uint32_t page_size_shift = 0; + uint32_t page_size_mask = 0; + uint64_t count = 0; + + _FORCE_INLINE_ uint32_t _get_pages_in_use() const { + if (count == 0) { + return 0; + } else { + return ((count - 1) >> page_size_shift) + 1; + } + } + + void _grow_page_array() { + //no more room in the page array to put the new page, make room + if (max_pages_used == 0) { + max_pages_used = 1; + } else { + max_pages_used *= 2; // increase in powers of 2 to keep allocations to minimum + } + page_data = (T **)memrealloc(page_data, sizeof(T *) * max_pages_used); + page_ids = (uint32_t *)memrealloc(page_ids, sizeof(uint32_t) * max_pages_used); + } + +public: + _FORCE_INLINE_ const T &operator[](uint64_t p_index) const { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + uint32_t page = p_index >> page_size_shift; + uint32_t offset = p_index & page_size_mask; + + return page_data[page][offset]; + } + _FORCE_INLINE_ T &operator[](uint64_t p_index) { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + uint32_t page = p_index >> page_size_shift; + uint32_t offset = p_index & page_size_mask; + + return page_data[page][offset]; + } + + _FORCE_INLINE_ void push_back(const T &p_value) { + uint32_t remainder = count & page_size_mask; + if (unlikely(remainder == 0)) { + // at 0, so time to request a new page + uint32_t page_count = _get_pages_in_use(); + uint32_t new_page_count = page_count + 1; + + if (unlikely(new_page_count > max_pages_used)) { + ERR_FAIL_COND(page_pool == nullptr); //sanity check + + _grow_page_array(); //keep out of inline + } + + uint32_t page_id = page_pool->alloc_page(); + page_data[page_count] = page_pool->get_page(page_id); + page_ids[page_count] = page_id; + } + + // place the new value + uint32_t page = count >> page_size_shift; + uint32_t offset = count & page_size_mask; + + if (!__has_trivial_constructor(T)) { + memnew_placement(&page_data[page][offset], T(p_value)); + } else { + page_data[page][offset] = p_value; + } + + count++; + } + + _FORCE_INLINE_ void pop_back() { + ERR_FAIL_COND(count == 0); + + if (!__has_trivial_destructor(T)) { + uint32_t page = (count - 1) >> page_size_shift; + uint32_t offset = (count - 1) & page_size_mask; + page_data[page][offset].~T(); + } + + uint32_t remainder = count & page_size_mask; + if (unlikely(remainder == 1)) { + // one element remained, so page must be freed. + uint32_t last_page = _get_pages_in_use() - 1; + page_pool->free_page(page_ids[last_page]); + } + count--; + } + + void clear() { + //destruct if needed + if (!__has_trivial_destructor(T)) { + for (uint64_t i = 0; i < count; i++) { + uint32_t page = i >> page_size_shift; + uint32_t offset = i & page_size_mask; + page_data[page][offset].~T(); + } + } + + //return the pages to the pagepool, so they can be used by another array eventually + uint32_t pages_used = _get_pages_in_use(); + for (uint32_t i = 0; i < pages_used; i++) { + page_pool->free_page(page_ids[i]); + } + + count = 0; + + //note we leave page_data and page_indices intact for next use. If you really want to clear them call reset() + } + + void reset() { + clear(); + if (page_data) { + memfree(page_data); + memfree(page_ids); + page_data = nullptr; + page_ids = nullptr; + max_pages_used = 0; + } + } + + // This takes the pages from a source array and merges them to this one + // resulting order is undefined, but content is merged very efficiently, + // making it ideal to fill content on several threads to later join it. + + void merge_unordered(PagedArray<T> &p_array) { + ERR_FAIL_COND(page_pool != p_array.page_pool); + + uint32_t remainder = count & page_size_mask; + + T *remainder_page = nullptr; + uint32_t remainder_page_id; + + if (remainder > 0) { + uint32_t last_page = _get_pages_in_use() - 1; + remainder_page = page_data[last_page]; + remainder_page_id = page_ids[last_page]; + } + + count -= remainder; + + uint32_t src_pages = p_array._get_pages_in_use(); + uint32_t page_size = page_size_mask + 1; + + for (uint32_t i = 0; i < src_pages; i++) { + uint32_t page_count = _get_pages_in_use(); + uint32_t new_page_count = page_count + 1; + + if (unlikely(new_page_count > max_pages_used)) { + _grow_page_array(); //keep out of inline + } + + page_data[page_count] = p_array.page_data[i]; + page_ids[page_count] = p_array.page_ids[i]; + if (i == src_pages - 1) { + //last page, only increment with remainder + count += p_array.count & page_size_mask; + } else { + count += page_size; + } + } + p_array.count = 0; //take away the other array pages + + //handle the remainder page if exists + if (remainder_page) { + uint32_t new_remainder = count & page_size_mask; + + if (new_remainder > 0) { + //must merge old remainder with new remainder + + T *dst_page = page_data[_get_pages_in_use() - 1]; + uint32_t to_copy = MIN(page_size - new_remainder, remainder); + + for (uint32_t i = 0; i < to_copy; i++) { + if (!__has_trivial_constructor(T)) { + memnew_placement(&dst_page[i + new_remainder], T(remainder_page[i + remainder - to_copy])); + } else { + dst_page[i + new_remainder] = remainder_page[i + remainder - to_copy]; + } + + if (!__has_trivial_destructor(T)) { + remainder_page[i + remainder - to_copy].~T(); + } + } + + remainder -= to_copy; //subtract what was copied from remainder + count += to_copy; //add what was copied to the count + + if (remainder == 0) { + //entire remainder copied, let go of remainder page + page_pool->free_page(remainder_page_id); + remainder_page = nullptr; + } + } + + if (remainder > 0) { + //there is still remainder, append it + uint32_t page_count = _get_pages_in_use(); + uint32_t new_page_count = page_count + 1; + + if (unlikely(new_page_count > max_pages_used)) { + _grow_page_array(); //keep out of inline + } + + page_data[page_count] = remainder_page; + page_ids[page_count] = remainder_page_id; + + count += remainder; + } + } + } + + _FORCE_INLINE_ uint64_t size() const { + return count; + } + + void set_page_pool(PagedArrayPool<T> *p_page_pool) { + ERR_FAIL_COND(max_pages_used > 0); //sanity check + + page_pool = p_page_pool; + page_size_mask = page_pool->get_page_size_mask(); + page_size_shift = page_pool->get_page_size_shift(); + } + + ~PagedArray() { + reset(); + } +}; + +#endif // PAGED_ARRAY_H diff --git a/core/pair.h b/core/templates/pair.h index 26a317e9d4..bc1a764694 100644 --- a/core/pair.h +++ b/core/templates/pair.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,7 +33,6 @@ template <class F, class S> struct Pair { - F first; S second; @@ -60,7 +59,6 @@ bool operator!=(const Pair<F, S> &pair, const Pair<F, S> &other) { template <class F, class S> struct PairSort { - bool operator()(const Pair<F, S> &A, const Pair<F, S> &B) const { return A.first < B.first; } diff --git a/core/templates/pass_func.h b/core/templates/pass_func.h new file mode 100644 index 0000000000..d2f465e91c --- /dev/null +++ b/core/templates/pass_func.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* pass_func.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 PASS_FUNC_H +#define PASS_FUNC_H + +#define PASS0R(m_r, m_name) \ + m_r m_name() { return PASSBASE->m_name(); } +#define PASS0RC(m_r, m_name) \ + m_r m_name() const { return PASSBASE->m_name(); } +#define PASS1R(m_r, m_name, m_type1) \ + m_r m_name(m_type1 arg1) { return PASSBASE->m_name(arg1); } +#define PASS1RC(m_r, m_name, m_type1) \ + m_r m_name(m_type1 arg1) const { return PASSBASE->m_name(arg1); } +#define PASS2R(m_r, m_name, m_type1, m_type2) \ + m_r m_name(m_type1 arg1, m_type2 arg2) { return PASSBASE->m_name(arg1, arg2); } +#define PASS2RC(m_r, m_name, m_type1, m_type2) \ + m_r m_name(m_type1 arg1, m_type2 arg2) const { return PASSBASE->m_name(arg1, arg2); } +#define PASS3R(m_r, m_name, m_type1, m_type2, m_type3) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) { return PASSBASE->m_name(arg1, arg2, arg3); } +#define PASS3RC(m_r, m_name, m_type1, m_type2, m_type3) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) const { return PASSBASE->m_name(arg1, arg2, arg3); } +#define PASS4R(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) { return PASSBASE->m_name(arg1, arg2, arg3, arg4); } +#define PASS4RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) const { return PASSBASE->m_name(arg1, arg2, arg3, arg4); } +#define PASS5R(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) { return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); } +#define PASS5RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) const { return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); } +#define PASS6R(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) { return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); } +#define PASS6RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) const { return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); } + +#define PASS0(m_name) \ + void m_name() { PASSBASE->m_name(); } +#define PASS1(m_name, m_type1) \ + void m_name(m_type1 arg1) { PASSBASE->m_name(arg1); } +#define PASS1C(m_name, m_type1) \ + void m_name(m_type1 arg1) const { PASSBASE->m_name(arg1); } +#define PASS2(m_name, m_type1, m_type2) \ + void m_name(m_type1 arg1, m_type2 arg2) { PASSBASE->m_name(arg1, arg2); } +#define PASS2C(m_name, m_type1, m_type2) \ + void m_name(m_type1 arg1, m_type2 arg2) const { PASSBASE->m_name(arg1, arg2); } +#define PASS3(m_name, m_type1, m_type2, m_type3) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) { PASSBASE->m_name(arg1, arg2, arg3); } +#define PASS4(m_name, m_type1, m_type2, m_type3, m_type4) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) { PASSBASE->m_name(arg1, arg2, arg3, arg4); } +#define PASS5(m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); } +#define PASS6(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); } +#define PASS7(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7); } +#define PASS8(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } +#define PASS9(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); } +#define PASS10(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); } +#define PASS11(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); } +#define PASS12(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); } +#define PASS13(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); } +#define PASS14(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13, m_type14) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13, m_type14 arg14) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); } +#define PASS15(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13, m_type14, m_type15) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13, m_type14 arg14, m_type15 arg15) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); } + +#endif // PASS_FUNC_H diff --git a/core/rid.h b/core/templates/rid.h index a2f73423a3..4c7119b4ea 100644 --- a/core/rid.h +++ b/core/templates/rid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -37,37 +37,40 @@ class RID_AllocBase; class RID { friend class RID_AllocBase; - uint64_t _id; + uint64_t _id = 0; public: - _FORCE_INLINE_ bool operator==(const RID &p_rid) const { - + _ALWAYS_INLINE_ bool operator==(const RID &p_rid) const { return _id == p_rid._id; } - _FORCE_INLINE_ bool operator<(const RID &p_rid) const { - + _ALWAYS_INLINE_ bool operator<(const RID &p_rid) const { return _id < p_rid._id; } - _FORCE_INLINE_ bool operator<=(const RID &p_rid) const { - + _ALWAYS_INLINE_ bool operator<=(const RID &p_rid) const { return _id <= p_rid._id; } - _FORCE_INLINE_ bool operator>(const RID &p_rid) const { - + _ALWAYS_INLINE_ bool operator>(const RID &p_rid) const { return _id > p_rid._id; } - _FORCE_INLINE_ bool operator!=(const RID &p_rid) const { - + _ALWAYS_INLINE_ bool operator>=(const RID &p_rid) const { + return _id >= p_rid._id; + } + _ALWAYS_INLINE_ bool operator!=(const RID &p_rid) const { return _id != p_rid._id; } - _FORCE_INLINE_ bool is_valid() const { return _id != 0; } - _FORCE_INLINE_ bool is_null() const { return _id == 0; } + _ALWAYS_INLINE_ bool is_valid() const { return _id != 0; } + _ALWAYS_INLINE_ bool is_null() const { return _id == 0; } - _FORCE_INLINE_ uint64_t get_id() const { return _id; } + _ALWAYS_INLINE_ uint32_t get_local_index() const { return _id & 0xFFFFFFFF; } - _FORCE_INLINE_ RID() { - _id = 0; + static _ALWAYS_INLINE_ RID from_uint64(uint64_t p_id) { + RID _rid; + _rid._id = p_id; + return _rid; } + _ALWAYS_INLINE_ uint64_t get_id() const { return _id; } + + _ALWAYS_INLINE_ RID() {} }; #endif // RID_H diff --git a/core/rid_owner.cpp b/core/templates/rid_owner.cpp index a5065f29f8..f75a2eb9df 100644 --- a/core/rid_owner.cpp +++ b/core/templates/rid_owner.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/core/rid_owner.h b/core/templates/rid_owner.h index ad6996b9a7..d26c445eb4 100644 --- a/core/rid_owner.h +++ b/core/templates/rid_owner.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,19 +31,19 @@ #ifndef RID_OWNER_H #define RID_OWNER_H -#include "core/list.h" -#include "core/oa_hash_map.h" #include "core/os/memory.h" -#include "core/print_string.h" -#include "core/rid.h" -#include "core/safe_refcount.h" -#include "core/set.h" -#include "core/spin_lock.h" +#include "core/os/spin_lock.h" +#include "core/string/print_string.h" +#include "core/templates/list.h" +#include "core/templates/oa_hash_map.h" +#include "core/templates/rid.h" +#include "core/templates/safe_refcount.h" +#include "core/templates/set.h" + #include <stdio.h> #include <typeinfo> class RID_AllocBase { - static volatile uint64_t base_id; protected: @@ -67,22 +67,20 @@ public: template <class T, bool THREAD_SAFE = false> class RID_Alloc : public RID_AllocBase { - - T **chunks; - uint32_t **free_list_chunks; - uint32_t **validator_chunks; + T **chunks = nullptr; + uint32_t **free_list_chunks = nullptr; + uint32_t **validator_chunks = nullptr; uint32_t elements_in_chunk; - uint32_t max_alloc; - uint32_t alloc_count; + uint32_t max_alloc = 0; + uint32_t alloc_count = 0; - const char *description; + const char *description = nullptr; SpinLock spin_lock; public: RID make_rid(const T &p_value) { - if (THREAD_SAFE) { spin_lock.lock(); } @@ -136,7 +134,6 @@ public: } _FORCE_INLINE_ T *getornull(const RID &p_rid) { - if (THREAD_SAFE) { spin_lock.lock(); } @@ -171,7 +168,6 @@ public: } _FORCE_INLINE_ bool owns(const RID &p_rid) { - if (THREAD_SAFE) { spin_lock.lock(); } @@ -200,7 +196,6 @@ public: } _FORCE_INLINE_ void free(const RID &p_rid) { - if (THREAD_SAFE) { spin_lock.lock(); } @@ -241,7 +236,7 @@ public: } _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) { - ERR_FAIL_INDEX_V(p_index, alloc_count, nullptr); + ERR_FAIL_UNSIGNED_INDEX_V(p_index, alloc_count, nullptr); if (THREAD_SAFE) { spin_lock.lock(); } @@ -288,14 +283,7 @@ public: } RID_Alloc(uint32_t p_target_chunk_byte_size = 4096) { - chunks = nullptr; - free_list_chunks = nullptr; - validator_chunks = nullptr; - elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T)); - max_alloc = 0; - alloc_count = 0; - description = nullptr; } ~RID_Alloc() { @@ -358,6 +346,18 @@ public: alloc.free(p_rid); } + _FORCE_INLINE_ uint32_t get_rid_count() const { + return alloc.get_rid_count(); + } + + _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) { + return alloc.get_rid_by_index(p_index); + } + + _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) { + return *alloc.get_ptr_by_index(p_index); + } + _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) { return alloc.get_owned_list(p_owned); } @@ -365,6 +365,7 @@ public: void set_description(const char *p_descrption) { alloc.set_description(p_descrption); } + RID_PtrOwner(uint32_t p_target_chunk_byte_size = 4096) : alloc(p_target_chunk_byte_size) {} }; @@ -412,4 +413,5 @@ public: RID_Owner(uint32_t p_target_chunk_byte_size = 4096) : alloc(p_target_chunk_byte_size) {} }; + #endif // RID_OWNER_H diff --git a/core/ring_buffer.h b/core/templates/ring_buffer.h index 620a3a3846..e7b77440f1 100644 --- a/core/ring_buffer.h +++ b/core/templates/ring_buffer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,14 +31,13 @@ #ifndef RING_BUFFER_H #define RING_BUFFER_H -#include "core/vector.h" +#include "core/templates/vector.h" template <typename T> class RingBuffer { - Vector<T> data; - int read_pos; - int write_pos; + int read_pos = 0; + int write_pos = 0; int size_mask; inline int inc(int &p_var, int p_size) const { @@ -46,13 +45,13 @@ class RingBuffer { p_var += p_size; p_var = p_var & size_mask; return ret; - }; + } public: T read() { ERR_FAIL_COND_V(space_left() < 1, T()); return data.ptr()[inc(read_pos, 1)]; - }; + } int read(T *p_buf, int p_size, bool p_advance = true) { int left = data_left(); @@ -67,23 +66,23 @@ public: const T *read = data.ptr(); for (int i = 0; i < total; i++) { p_buf[dst++] = read[pos + i]; - }; + } to_read -= total; pos = 0; - }; + } if (p_advance) { inc(read_pos, p_size); - }; + } return p_size; - }; + } int copy(T *p_buf, int p_offset, int p_size) const { - int left = data_left(); if ((p_offset + p_size) > left) { p_size -= left - p_offset; - if (p_size <= 0) + if (p_size <= 0) { return 0; + } } p_size = MIN(left, p_size); int pos = read_pos; @@ -96,20 +95,20 @@ public: int total = end - pos; for (int i = 0; i < total; i++) { p_buf[dst++] = data[pos + i]; - }; + } to_read -= total; pos = 0; - }; + } return p_size; - }; + } int find(const T &t, int p_offset, int p_max_size) const { - int left = data_left(); if ((p_offset + p_max_size) > left) { p_max_size -= left - p_offset; - if (p_max_size <= 0) + if (p_max_size <= 0) { return 0; + } } p_max_size = MIN(left, p_max_size); int pos = read_pos; @@ -120,9 +119,10 @@ public: end = MIN(end, size()); int total = end - pos; for (int i = 0; i < total; i++) { - if (data[pos + i] == t) + if (data[pos + i] == t) { return i + (p_max_size - to_read); - }; + } + } to_read -= total; pos = 0; } @@ -133,7 +133,7 @@ public: p_n = MIN(p_n, data_left()); inc(read_pos, p_n); return p_n; - }; + } inline int decrease_write(int p_n) { p_n = MIN(p_n, data_left()); @@ -145,10 +145,9 @@ public: ERR_FAIL_COND_V(space_left() < 1, FAILED); data.write[inc(write_pos, 1)] = p_v; return OK; - }; + } int write(const T *p_buf, int p_size) { - int left = space_left(); p_size = MIN(left, p_size); @@ -156,39 +155,38 @@ public: int to_write = p_size; int src = 0; while (to_write) { - int end = pos + to_write; end = MIN(end, size()); int total = end - pos; for (int i = 0; i < total; i++) { data.write[pos + i] = p_buf[src++]; - }; + } to_write -= total; pos = 0; - }; + } inc(write_pos, p_size); return p_size; - }; + } inline int space_left() const { int left = read_pos - write_pos; if (left < 0) { return size() + left - 1; - }; + } if (left == 0) { return size() - 1; - }; + } return left - 1; - }; + } inline int data_left() const { return size() - space_left() - 1; - }; + } inline int size() const { return data.size(); - }; + } inline void clear() { read_pos = 0; @@ -203,22 +201,20 @@ public: if (old_size < new_size && read_pos > write_pos) { for (int i = 0; i < write_pos; i++) { data.write[(old_size + i) & mask] = data[i]; - }; + } write_pos = (old_size + write_pos) & mask; } else { read_pos = read_pos & mask; write_pos = write_pos & mask; - }; + } size_mask = mask; - }; + } RingBuffer<T>(int p_power = 0) { - read_pos = 0; - write_pos = 0; resize(p_power); - }; - ~RingBuffer<T>(){}; + } + ~RingBuffer<T>() {} }; #endif // RING_BUFFER_H diff --git a/core/safe_refcount.cpp b/core/templates/safe_refcount.cpp index e4604faa09..a915ee662f 100644 --- a/core/safe_refcount.cpp +++ b/core/templates/safe_refcount.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -42,79 +42,71 @@ /* taken from boost */ \ while (true) { \ m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw)); \ - if (tmp == 0) \ + if (tmp == 0) { \ return 0; /* if zero, can't add to it anymore */ \ - if (m_win_cmpxchg((m_win_type volatile *)(m_pw), tmp + 1, tmp) == tmp) \ + } \ + if (m_win_cmpxchg((m_win_type volatile *)(m_pw), tmp + 1, tmp) == tmp) { \ return tmp + 1; \ + } \ } #define ATOMIC_EXCHANGE_IF_GREATER_BODY(m_pw, m_val, m_win_type, m_win_cmpxchg, m_cpp_type) \ while (true) { \ m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw)); \ - if (tmp >= m_val) \ + if (tmp >= m_val) { \ return tmp; /* already greater, or equal */ \ - if (m_win_cmpxchg((m_win_type volatile *)(m_pw), m_val, tmp) == tmp) \ + } \ + if (m_win_cmpxchg((m_win_type volatile *)(m_pw), m_val, tmp) == tmp) { \ return m_val; \ + } \ } -_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(volatile uint32_t *pw){ - - ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t) +_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(volatile uint32_t *pw) { + ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t); } _ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(volatile uint32_t *pw) { - return InterlockedDecrement((LONG volatile *)pw); } _ALWAYS_INLINE_ uint32_t _atomic_increment_impl(volatile uint32_t *pw) { - return InterlockedIncrement((LONG volatile *)pw); } _ALWAYS_INLINE_ uint32_t _atomic_sub_impl(volatile uint32_t *pw, volatile uint32_t val) { - return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val; } _ALWAYS_INLINE_ uint32_t _atomic_add_impl(volatile uint32_t *pw, volatile uint32_t val) { - return InterlockedAdd((LONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(volatile uint32_t *pw, volatile uint32_t val){ - - ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t) +_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(volatile uint32_t *pw, volatile uint32_t val) { + ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t); } -_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(volatile uint64_t *pw){ - - ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t) +_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(volatile uint64_t *pw) { + ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t); } _ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(volatile uint64_t *pw) { - return InterlockedDecrement64((LONGLONG volatile *)pw); } _ALWAYS_INLINE_ uint64_t _atomic_increment_impl(volatile uint64_t *pw) { - return InterlockedIncrement64((LONGLONG volatile *)pw); } _ALWAYS_INLINE_ uint64_t _atomic_sub_impl(volatile uint64_t *pw, volatile uint64_t val) { - return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val; } _ALWAYS_INLINE_ uint64_t _atomic_add_impl(volatile uint64_t *pw, volatile uint64_t val) { - return InterlockedAdd64((LONGLONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(volatile uint64_t *pw, volatile uint64_t val){ - - ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t) +_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(volatile uint64_t *pw, volatile uint64_t val) { + ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t); } // The actual advertised functions; they'll call the right implementation diff --git a/core/safe_refcount.h b/core/templates/safe_refcount.h index 953a877397..d94444fad6 100644 --- a/core/safe_refcount.h +++ b/core/templates/safe_refcount.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -43,9 +43,9 @@ template <class T> static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { - - if (*pw == 0) + if (*pw == 0) { return 0; + } (*pw)++; @@ -54,7 +54,6 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { template <class T> static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { - (*pw)--; return *pw; @@ -62,7 +61,6 @@ static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { template <class T> static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { - (*pw)++; return *pw; @@ -70,7 +68,6 @@ static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { template <class T, class V> static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { - (*pw) -= val; return *pw; @@ -78,7 +75,6 @@ static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { template <class T, class V> static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { - (*pw) += val; return *pw; @@ -86,9 +82,9 @@ static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { template <class T, class V> static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { - - if (val > *pw) + if (val > *pw) { *pw = val; + } return *pw; } @@ -102,49 +98,47 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V v template <class T> static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { - while (true) { T tmp = static_cast<T const volatile &>(*pw); - if (tmp == 0) + if (tmp == 0) { return 0; // if zero, can't add to it anymore - if (__sync_val_compare_and_swap(pw, tmp, tmp + 1) == tmp) + } + if (__sync_val_compare_and_swap(pw, tmp, tmp + 1) == tmp) { return tmp + 1; + } } } template <class T> static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { - return __sync_sub_and_fetch(pw, 1); } template <class T> static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { - return __sync_add_and_fetch(pw, 1); } template <class T, class V> static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { - return __sync_sub_and_fetch(pw, val); } template <class T, class V> static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { - return __sync_add_and_fetch(pw, val); } template <class T, class V> static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { - while (true) { T tmp = static_cast<T const volatile &>(*pw); - if (tmp >= val) + if (tmp >= val) { return tmp; // already greater, or equal - if (__sync_val_compare_and_swap(pw, tmp, val) == tmp) + } + if (__sync_val_compare_and_swap(pw, tmp, val) == tmp) { return val; + } } } @@ -171,8 +165,7 @@ uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val #endif struct SafeRefCount { - - uint32_t count; + uint32_t count = 0; public: // destroy() is called when weak_count_ drops to zero. @@ -203,7 +196,6 @@ public: } _ALWAYS_INLINE_ void init(uint32_t p_value = 1) { - count = p_value; } }; diff --git a/core/self_list.h b/core/templates/self_list.h index 19d2783208..e8d36ea358 100644 --- a/core/self_list.h +++ b/core/templates/self_list.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,20 +31,18 @@ #ifndef SELF_LIST_H #define SELF_LIST_H -#include "core/error_macros.h" +#include "core/error/error_macros.h" #include "core/typedefs.h" template <class T> class SelfList { public: class List { - - SelfList<T> *_first; - SelfList<T> *_last; + SelfList<T> *_first = nullptr; + SelfList<T> *_last = nullptr; public: void add(SelfList<T> *p_elem) { - ERR_FAIL_COND(p_elem->_root); p_elem->_root = this; @@ -62,7 +60,6 @@ public: } void add_last(SelfList<T> *p_elem) { - ERR_FAIL_COND(p_elem->_root); p_elem->_root = this; @@ -80,7 +77,6 @@ public: } void remove(SelfList<T> *p_elem) { - ERR_FAIL_COND(p_elem->_root != this); if (p_elem->_next) { p_elem->_next->_prev = p_elem->_prev; @@ -105,21 +101,24 @@ public: _FORCE_INLINE_ SelfList<T> *first() { return _first; } _FORCE_INLINE_ const SelfList<T> *first() const { return _first; } - _FORCE_INLINE_ List() { - _first = nullptr; - _last = nullptr; - } + + _FORCE_INLINE_ List() {} _FORCE_INLINE_ ~List() { ERR_FAIL_COND(_first != nullptr); } }; private: - List *_root; + List *_root = nullptr; T *_self; - SelfList<T> *_next; - SelfList<T> *_prev; + SelfList<T> *_next = nullptr; + SelfList<T> *_prev = nullptr; public: _FORCE_INLINE_ bool in_list() const { return _root; } + _FORCE_INLINE_ void remove_from_list() { + if (_root) { + _root->remove(this); + } + } _FORCE_INLINE_ SelfList<T> *next() { return _next; } _FORCE_INLINE_ SelfList<T> *prev() { return _prev; } _FORCE_INLINE_ const SelfList<T> *next() const { return _next; } @@ -127,17 +126,13 @@ public: _FORCE_INLINE_ T *self() const { return _self; } _FORCE_INLINE_ SelfList(T *p_self) { - _self = p_self; - _next = nullptr; - _prev = nullptr; - _root = nullptr; } _FORCE_INLINE_ ~SelfList() { - - if (_root) + if (_root) { _root->remove(this); + } } }; diff --git a/core/set.h b/core/templates/set.h index c17ee15350..3036ecf27d 100644 --- a/core/set.h +++ b/core/templates/set.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -39,7 +39,6 @@ template <class T, class C = Comparator<T>, class A = DefaultAllocator> class Set { - enum Color { RED, BLACK @@ -48,54 +47,41 @@ class Set { public: class Element { - private: friend class Set<T, C, A>; - int color; - Element *right; - Element *left; - Element *parent; - Element *_next; - Element *_prev; + int color = RED; + Element *right = nullptr; + Element *left = nullptr; + Element *parent = nullptr; + Element *_next = nullptr; + Element *_prev = nullptr; T value; //_Data *data; public: const Element *next() const { - return _next; } Element *next() { - return _next; } const Element *prev() const { - return _prev; } Element *prev() { - return _prev; } const T &get() const { return value; }; - Element() { - color = RED; - right = nullptr; - left = nullptr; - parent = nullptr; - _next = nullptr; - _prev = nullptr; - }; + Element() {} }; private: struct _Data { - - Element *_root; - Element *_nil; - int size_cache; + Element *_root = nullptr; + Element *_nil = nullptr; + int size_cache = 0; _FORCE_INLINE_ _Data() { #ifdef GLOBALNIL_DISABLED @@ -105,19 +91,15 @@ private: #else _nil = (Element *)&_GlobalNilClass::_nil; #endif - _root = nullptr; - size_cache = 0; } void _create_root() { - _root = memnew_allocator(Element, A); _root->parent = _root->left = _root->right = _nil; _root->color = BLACK; } void _free_root() { - if (_root) { memdelete_allocator<Element, A>(_root); _root = nullptr; @@ -125,7 +107,6 @@ private: } ~_Data() { - _free_root(); #ifdef GLOBALNIL_DISABLED @@ -137,62 +118,61 @@ private: _Data _data; inline void _set_color(Element *p_node, int p_color) { - ERR_FAIL_COND(p_node == _data._nil && p_color == RED); p_node->color = p_color; } inline void _rotate_left(Element *p_node) { - Element *r = p_node->right; p_node->right = r->left; - if (r->left != _data._nil) + if (r->left != _data._nil) { r->left->parent = p_node; + } r->parent = p_node->parent; - if (p_node == p_node->parent->left) + if (p_node == p_node->parent->left) { p_node->parent->left = r; - else + } else { p_node->parent->right = r; + } r->left = p_node; p_node->parent = r; } inline void _rotate_right(Element *p_node) { - Element *l = p_node->left; p_node->left = l->right; - if (l->right != _data._nil) + if (l->right != _data._nil) { l->right->parent = p_node; + } l->parent = p_node->parent; - if (p_node == p_node->parent->right) + if (p_node == p_node->parent->right) { p_node->parent->right = l; - else + } else { p_node->parent->left = l; + } l->right = p_node; p_node->parent = l; } inline Element *_successor(Element *p_node) const { - Element *node = p_node; if (node->right != _data._nil) { - node = node->right; while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */ node = node->left; } return node; } else { - while (node == node->parent->right) { node = node->parent; } - if (node->parent == _data._root) + if (node->parent == _data._root) { return nullptr; // No successor, as p_node = last node + } return node->parent; } } @@ -201,43 +181,41 @@ private: Element *node = p_node; if (node->left != _data._nil) { - node = node->left; while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */ node = node->right; } return node; } else { - while (node == node->parent->left) { node = node->parent; } - if (node == _data._root) + if (node == _data._root) { return nullptr; // No predecessor, as p_node = first node. + } return node->parent; } } Element *_find(const T &p_value) const { - Element *node = _data._root->left; C less; while (node != _data._nil) { - if (less(p_value, node->value)) + if (less(p_value, node->value)) { node = node->left; - else if (less(node->value, p_value)) + } else if (less(node->value, p_value)) { node = node->right; - else + } else { return node; // found + } } return nullptr; } Element *_lower_bound(const T &p_value) const { - Element *node = _data._root->left; Element *prev = nullptr; C less; @@ -245,25 +223,27 @@ private: while (node != _data._nil) { prev = node; - if (less(p_value, node->value)) + if (less(p_value, node->value)) { node = node->left; - else if (less(node->value, p_value)) + } else if (less(node->value, p_value)) { node = node->right; - else + } else { return node; // found + } } - if (prev == nullptr) + if (prev == nullptr) { return nullptr; // tree empty + } - if (less(prev->value, p_value)) + if (less(prev->value, p_value)) { prev = prev->_next; + } return prev; } void _insert_rb_fix(Element *p_new_node) { - Element *node = p_new_node; Element *nparent = node->parent; Element *ngrand_parent; @@ -312,20 +292,18 @@ private: } Element *_insert(const T &p_value) { - Element *new_parent = _data._root; Element *node = _data._root->left; C less; while (node != _data._nil) { - new_parent = node; - if (less(p_value, node->value)) + if (less(p_value, node->value)) { node = node->left; - else if (less(node->value, p_value)) + } else if (less(node->value, p_value)) { node = node->right; - else { + } else { return node; // Return existing node } } @@ -345,10 +323,12 @@ private: new_node->_next = _successor(new_node); new_node->_prev = _predecessor(new_node); - if (new_node->_next) + if (new_node->_next) { new_node->_next->_prev = new_node; - if (new_node->_prev) + } + if (new_node->_prev) { new_node->_prev->_next = new_node; + } _data.size_cache++; _insert_rb_fix(new_node); @@ -356,7 +336,6 @@ private: } void _erase_fix_rb(Element *p_node) { - Element *root = _data._root->left; Element *node = _data._nil; Element *sibling = p_node; @@ -418,7 +397,6 @@ private: } void _erase(Element *p_node) { - Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next; Element *node = (rp->left == _data._nil) ? rp->right : rp->left; @@ -439,17 +417,18 @@ private: } if (rp != p_node) { - ERR_FAIL_COND(rp == _data._nil); rp->left = p_node->left; rp->right = p_node->right; rp->parent = p_node->parent; rp->color = p_node->color; - if (p_node->left != _data._nil) + if (p_node->left != _data._nil) { p_node->left->parent = rp; - if (p_node->right != _data._nil) + } + if (p_node->right != _data._nil) { p_node->right->parent = rp; + } if (p_node == p_node->parent->left) { p_node->parent->left = rp; @@ -458,10 +437,12 @@ private: } } - if (p_node->_next) + if (p_node->_next) { p_node->_next->_prev = p_node->_prev; - if (p_node->_prev) + } + if (p_node->_prev) { p_node->_prev->_next = p_node->_next; + } memdelete_allocator<Element, A>(p_node); _data.size_cache--; @@ -469,21 +450,22 @@ private: } void _calculate_depth(Element *p_element, int &max_d, int d) const { - - if (p_element == _data._nil) + if (p_element == _data._nil) { return; + } _calculate_depth(p_element->left, max_d, d + 1); _calculate_depth(p_element->right, max_d, d + 1); - if (d > max_d) + if (d > max_d) { max_d = d; + } } void _cleanup_tree(Element *p_element) { - - if (p_element == _data._nil) + if (p_element == _data._nil) { return; + } _cleanup_tree(p_element->left); _cleanup_tree(p_element->right); @@ -491,113 +473,117 @@ private: } void _copy_from(const Set &p_set) { - clear(); // not the fastest way, but safeset to write. for (Element *I = p_set.front(); I; I = I->next()) { - insert(I->get()); } } public: const Element *find(const T &p_value) const { - - if (!_data._root) + if (!_data._root) { return nullptr; + } const Element *res = _find(p_value); return res; } Element *find(const T &p_value) { - - if (!_data._root) + if (!_data._root) { return nullptr; + } Element *res = _find(p_value); return res; } Element *lower_bound(const T &p_value) const { - return _lower_bound(p_value); } bool has(const T &p_value) const { - return find(p_value) != nullptr; } Element *insert(const T &p_value) { - - if (!_data._root) + if (!_data._root) { _data._create_root(); + } return _insert(p_value); } void erase(Element *p_element) { - - if (!_data._root || !p_element) + if (!_data._root || !p_element) { return; + } _erase(p_element); - if (_data.size_cache == 0 && _data._root) + if (_data.size_cache == 0 && _data._root) { _data._free_root(); + } } bool erase(const T &p_value) { - - if (!_data._root) + if (!_data._root) { return false; + } Element *e = find(p_value); - if (!e) + if (!e) { return false; + } _erase(e); - if (_data.size_cache == 0 && _data._root) + if (_data.size_cache == 0 && _data._root) { _data._free_root(); + } return true; } Element *front() const { - - if (!_data._root) + if (!_data._root) { return nullptr; + } Element *e = _data._root->left; - if (e == _data._nil) + if (e == _data._nil) { return nullptr; + } - while (e->left != _data._nil) + while (e->left != _data._nil) { e = e->left; + } return e; } Element *back() const { - - if (!_data._root) + if (!_data._root) { return nullptr; + } Element *e = _data._root->left; - if (e == _data._nil) + if (e == _data._nil) { return nullptr; + } - while (e->right != _data._nil) + while (e->right != _data._nil) { e = e->right; + } return e; } - inline bool empty() const { return _data.size_cache == 0; } + inline bool is_empty() const { return _data.size_cache == 0; } inline int size() const { return _data.size_cache; } int calculate_depth() const { // used for debug mostly - if (!_data._root) + if (!_data._root) { return 0; + } int max_d = 0; _calculate_depth(_data._root->left, max_d, 0); @@ -605,9 +591,9 @@ public: } void clear() { - - if (!_data._root) + if (!_data._root) { return; + } _cleanup_tree(_data._root->left); _data._root->left = _data._nil; @@ -616,20 +602,16 @@ public: } void operator=(const Set &p_set) { - _copy_from(p_set); } Set(const Set &p_set) { - _copy_from(p_set); } - _FORCE_INLINE_ Set() { - } + _FORCE_INLINE_ Set() {} ~Set() { - clear(); } }; diff --git a/core/simple_type.h b/core/templates/simple_type.h index da031854c6..80bfa83fde 100644 --- a/core/simple_type.h +++ b/core/templates/simple_type.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -35,19 +35,21 @@ template <class T> struct GetSimpleTypeT { - typedef T type_t; }; template <class T> struct GetSimpleTypeT<T &> { - typedef T type_t; }; template <class T> struct GetSimpleTypeT<T const> { + typedef T type_t; +}; +template <class T> +struct GetSimpleTypeT<T const &> { typedef T type_t; }; diff --git a/core/sort_array.h b/core/templates/sort_array.h index 8aff0fb502..1656d2991d 100644 --- a/core/sort_array.h +++ b/core/templates/sort_array.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef SORT_ARRAY_H #define SORT_ARRAY_H -#include "core/error_macros.h" +#include "core/error/error_macros.h" #include "core/typedefs.h" #define ERR_BAD_COMPARE(cond) \ @@ -42,7 +42,6 @@ template <class T> struct _DefaultComparator { - _FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); } }; @@ -54,9 +53,7 @@ struct _DefaultComparator { template <class T, class Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED> class SortArray { - enum { - INTROSORT_THRESHOLD = 16 }; @@ -64,36 +61,36 @@ public: Comparator compare; inline const T &median_of_3(const T &a, const T &b, const T &c) const { - - if (compare(a, b)) - if (compare(b, c)) + if (compare(a, b)) { + if (compare(b, c)) { return b; - else if (compare(a, c)) + } else if (compare(a, c)) { return c; - else + } else { return a; - else if (compare(a, c)) + } + } else if (compare(a, c)) { return a; - else if (compare(b, c)) + } else if (compare(b, c)) { return c; - else + } else { return b; + } } inline int bitlog(int n) const { int k; - for (k = 0; n != 1; n >>= 1) + for (k = 0; n != 1; n >>= 1) { ++k; + } return k; } /* Heap / Heapsort functions */ inline void push_heap(int p_first, int p_hole_idx, int p_top_index, T p_value, T *p_array) const { - int parent = (p_hole_idx - 1) / 2; while (p_hole_idx > p_top_index && compare(p_array[p_first + parent], p_value)) { - p_array[p_first + p_hole_idx] = p_array[p_first + parent]; p_hole_idx = parent; parent = (p_hole_idx - 1) / 2; @@ -102,24 +99,21 @@ public: } inline void pop_heap(int p_first, int p_last, int p_result, T p_value, T *p_array) const { - p_array[p_result] = p_array[p_first]; adjust_heap(p_first, 0, p_last - p_first, p_value, p_array); } inline void pop_heap(int p_first, int p_last, T *p_array) const { - pop_heap(p_first, p_last - 1, p_last - 1, p_array[p_last - 1], p_array); } inline void adjust_heap(int p_first, int p_hole_idx, int p_len, T p_value, T *p_array) const { - int top_index = p_hole_idx; int second_child = 2 * p_hole_idx + 2; while (second_child < p_len) { - - if (compare(p_array[p_first + second_child], p_array[p_first + (second_child - 1)])) + if (compare(p_array[p_first + second_child], p_array[p_first + (second_child - 1)])) { second_child--; + } p_array[p_first + p_hole_idx] = p_array[p_first + second_child]; p_hole_idx = second_child; @@ -134,46 +128,47 @@ public: } inline void sort_heap(int p_first, int p_last, T *p_array) const { - while (p_last - p_first > 1) { - pop_heap(p_first, p_last--, p_array); } } inline void make_heap(int p_first, int p_last, T *p_array) const { - if (p_last - p_first < 2) + if (p_last - p_first < 2) { return; + } int len = p_last - p_first; int parent = (len - 2) / 2; while (true) { adjust_heap(p_first, parent, len, p_array[p_first + parent], p_array); - if (parent == 0) + if (parent == 0) { return; + } parent--; } } inline void partial_sort(int p_first, int p_last, int p_middle, T *p_array) const { - make_heap(p_first, p_middle, p_array); - for (int i = p_middle; i < p_last; i++) - if (compare(p_array[i], p_array[p_first])) + for (int i = p_middle; i < p_last; i++) { + if (compare(p_array[i], p_array[p_first])) { pop_heap(p_first, p_middle, i, p_array[i], p_array); + } + } sort_heap(p_first, p_middle, p_array); } inline void partial_select(int p_first, int p_last, int p_middle, T *p_array) const { - make_heap(p_first, p_middle, p_array); - for (int i = p_middle; i < p_last; i++) - if (compare(p_array[i], p_array[p_first])) + for (int i = p_middle; i < p_last; i++) { + if (compare(p_array[i], p_array[p_first])) { pop_heap(p_first, p_middle, i, p_array[i], p_array); + } + } } inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const { - const int unmodified_first = p_first; const int unmodified_last = p_last; @@ -192,8 +187,9 @@ public: p_last--; } - if (!(p_first < p_last)) + if (!(p_first < p_last)) { return p_first; + } SWAP(p_array[p_first], p_array[p_last]); p_first++; @@ -201,9 +197,7 @@ public: } inline void introsort(int p_first, int p_last, T *p_array, int p_max_depth) const { - while (p_last - p_first > INTROSORT_THRESHOLD) { - if (p_max_depth == 0) { partial_sort(p_first, p_last, p_last, p_array); return; @@ -226,9 +220,7 @@ public: } inline void introselect(int p_first, int p_nth, int p_last, T *p_array, int p_max_depth) const { - while (p_last - p_first > 3) { - if (p_max_depth == 0) { partial_select(p_first, p_nth + 1, p_last, p_array); SWAP(p_first, p_nth); @@ -246,17 +238,17 @@ public: p_array[p_last - 1]), p_array); - if (cut <= p_nth) + if (cut <= p_nth) { p_first = cut; - else + } else { p_last = cut; + } } insertion_sort(p_first, p_last, p_array); } inline void unguarded_linear_insert(int p_last, T p_value, T *p_array) const { - int next = p_last - 1; while (compare(p_value, p_array[next])) { if (Validate) { @@ -270,45 +262,43 @@ public: } inline void linear_insert(int p_first, int p_last, T *p_array) const { - T val = p_array[p_last]; if (compare(val, p_array[p_first])) { - - for (int i = p_last; i > p_first; i--) + for (int i = p_last; i > p_first; i--) { p_array[i] = p_array[i - 1]; + } p_array[p_first] = val; - } else + } else { unguarded_linear_insert(p_last, val, p_array); + } } inline void insertion_sort(int p_first, int p_last, T *p_array) const { - - if (p_first == p_last) + if (p_first == p_last) { return; - for (int i = p_first + 1; i != p_last; i++) + } + for (int i = p_first + 1; i != p_last; i++) { linear_insert(p_first, i, p_array); + } } inline void unguarded_insertion_sort(int p_first, int p_last, T *p_array) const { - - for (int i = p_first; i != p_last; i++) + for (int i = p_first; i != p_last; i++) { unguarded_linear_insert(i, p_array[i], p_array); + } } inline void final_insertion_sort(int p_first, int p_last, T *p_array) const { - if (p_last - p_first > INTROSORT_THRESHOLD) { insertion_sort(p_first, p_first + INTROSORT_THRESHOLD, p_array); unguarded_insertion_sort(p_first + INTROSORT_THRESHOLD, p_last, p_array); } else { - insertion_sort(p_first, p_last, p_array); } } inline void sort_range(int p_first, int p_last, T *p_array) const { - if (p_first != p_last) { introsort(p_first, p_last, p_array, bitlog(p_last - p_first) * 2); final_insertion_sort(p_first, p_last, p_array); @@ -316,14 +306,13 @@ public: } inline void sort(T *p_array, int p_len) const { - sort_range(0, p_len, p_array); } inline void nth_element(int p_first, int p_last, int p_nth, T *p_array) const { - - if (p_first == p_last || p_nth == p_last) + if (p_first == p_last || p_nth == p_last) { return; + } introselect(p_first, p_nth, p_last, p_array, bitlog(p_last - p_first) * 2); } }; diff --git a/core/thread_work_pool.cpp b/core/templates/thread_work_pool.cpp index c8311f102f..ea784e510c 100644 --- a/core/thread_work_pool.cpp +++ b/core/templates/thread_work_pool.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,10 +29,10 @@ /*************************************************************************/ #include "thread_work_pool.h" + #include "core/os/os.h" void ThreadWorkPool::_thread_function(ThreadData *p_thread) { - while (true) { p_thread->start.wait(); if (p_thread->exit.load()) { @@ -59,7 +59,6 @@ void ThreadWorkPool::init(int p_thread_count) { } void ThreadWorkPool::finish() { - if (threads == nullptr) { return; } @@ -78,6 +77,5 @@ void ThreadWorkPool::finish() { } ThreadWorkPool::~ThreadWorkPool() { - finish(); } diff --git a/core/thread_work_pool.h b/core/templates/thread_work_pool.h index 214d2c4aa7..7c3508814f 100644 --- a/core/thread_work_pool.h +++ b/core/templates/thread_work_pool.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,15 +33,16 @@ #include "core/os/memory.h" #include "core/os/semaphore.h" + #include <atomic> #include <thread> -class ThreadWorkPool { +class ThreadWorkPool { std::atomic<uint32_t> index; struct BaseWork { - std::atomic<uint32_t> *index; - uint32_t max_elements; + std::atomic<uint32_t> *index = nullptr; + uint32_t max_elements = 0; virtual void work() = 0; virtual ~BaseWork() = default; }; @@ -52,7 +53,6 @@ class ThreadWorkPool { M method; U userdata; virtual void work() { - while (true) { uint32_t work_index = index->fetch_add(1, std::memory_order_relaxed); if (work_index >= max_elements) { @@ -73,14 +73,15 @@ class ThreadWorkPool { ThreadData *threads = nullptr; uint32_t thread_count = 0; + BaseWork *current_work = nullptr; static void _thread_function(ThreadData *p_thread); public: template <class C, class M, class U> - void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) { - + void begin_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) { ERR_FAIL_COND(!threads); //never initialized + ERR_FAIL_COND(current_work != nullptr); index.store(0); @@ -91,18 +92,40 @@ public: w->index = &index; w->max_elements = p_elements; + current_work = w; + for (uint32_t i = 0; i < thread_count; i++) { threads[i].work = w; threads[i].start.post(); } + } + + bool is_working() const { + return current_work != nullptr; + } + + uint32_t get_work_index() const { + return index; + } + + void end_work() { + ERR_FAIL_COND(current_work == nullptr); for (uint32_t i = 0; i < thread_count; i++) { threads[i].completed.wait(); threads[i].work = nullptr; } - memdelete(w); + memdelete(current_work); + current_work = nullptr; + } + + template <class C, class M, class U> + void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) { + begin_work(p_elements, p_instance, p_method, p_userdata); + end_work(); } + _FORCE_INLINE_ int get_thread_count() const { return thread_count; } void init(int p_thread_count = -1); void finish(); ~ThreadWorkPool(); diff --git a/core/vector.h b/core/templates/vector.h index 7277179621..6a8902707c 100644 --- a/core/vector.h +++ b/core/templates/vector.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -37,10 +37,11 @@ * Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use Vector for large arrays. */ -#include "core/cowdata.h" -#include "core/error_macros.h" +#include "core/error/error_macros.h" +#include "core/os/copymem.h" #include "core/os/memory.h" -#include "core/sort_array.h" +#include "core/templates/cowdata.h" +#include "core/templates/sort_array.h" template <class T> class VectorWriteProxy { @@ -69,17 +70,19 @@ public: void remove(int p_index) { _cowdata.remove(p_index); } void erase(const T &p_val) { int idx = find(p_val); - if (idx >= 0) remove(idx); + if (idx >= 0) { + remove(idx); + } } void invert(); _FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); } _FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); } _FORCE_INLINE_ void clear() { resize(0); } - _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } + _FORCE_INLINE_ bool is_empty() const { return _cowdata.is_empty(); } _FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); } - _FORCE_INLINE_ const T get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ const T &get(int p_index) const { return _cowdata.get(p_index); } _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); } _FORCE_INLINE_ int size() const { return _cowdata.size(); } Error resize(int p_size) { return _cowdata.resize(p_size); } @@ -89,12 +92,16 @@ public: void append_array(Vector<T> p_other); + bool has(const T &p_val) { + return find(p_val, 0) != -1; + } + template <class C> void sort_custom() { - int len = _cowdata.size(); - if (len == 0) + if (len == 0) { return; + } T *data = ptrw(); SortArray<T, C> sorter; @@ -102,30 +109,36 @@ public: } void sort() { - sort_custom<_DefaultComparator<T>>(); } + Vector<T> duplicate() { + return *this; + } + void ordered_insert(const T &p_val) { int i; for (i = 0; i < _cowdata.size(); i++) { - if (p_val < operator[](i)) { break; - }; - }; + } + } insert(i, p_val); } - _FORCE_INLINE_ Vector() {} - _FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); } inline Vector &operator=(const Vector &p_from) { _cowdata._ref(p_from._cowdata); return *this; } - Vector<T> subarray(int p_from, int p_to) const { + Vector<uint8_t> to_byte_array() const { + Vector<uint8_t> ret; + ret.resize(size() * sizeof(T)); + copymem(ret.ptrw(), ptr(), sizeof(T) * size()); + return ret; + } + Vector<T> subarray(int p_from, int p_to) const { if (p_from < 0) { p_from = size() + p_from; } @@ -148,12 +161,40 @@ public: return slice; } + bool operator==(const Vector<T> &p_arr) const { + int s = size(); + if (s != p_arr.size()) { + return false; + } + for (int i = 0; i < s; i++) { + if (operator[](i) != p_arr[i]) { + return false; + } + } + return true; + } + + bool operator!=(const Vector<T> &p_arr) const { + int s = size(); + if (s != p_arr.size()) { + return true; + } + for (int i = 0; i < s; i++) { + if (operator[](i) != p_arr[i]) { + return true; + } + } + return false; + } + + _FORCE_INLINE_ Vector() {} + _FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); } + _FORCE_INLINE_ ~Vector() {} }; template <class T> void Vector<T>::invert() { - for (int i = 0; i < size() / 2; i++) { T *p = ptrw(); SWAP(p[i], p[size() - i - 1]); @@ -163,17 +204,18 @@ void Vector<T>::invert() { template <class T> void Vector<T>::append_array(Vector<T> p_other) { const int ds = p_other.size(); - if (ds == 0) + if (ds == 0) { return; + } const int bs = size(); resize(bs + ds); - for (int i = 0; i < ds; ++i) + for (int i = 0; i < ds; ++i) { ptrw()[bs + i] = p_other[i]; + } } template <class T> bool Vector<T>::push_back(T p_elem) { - Error err = resize(size() + 1); ERR_FAIL_COND_V(err, true); set(size() - 1, p_elem); diff --git a/core/vmap.h b/core/templates/vmap.h index 84ae1aaf66..520e0b3720 100644 --- a/core/vmap.h +++ b/core/templates/vmap.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,21 +31,19 @@ #ifndef VMAP_H #define VMAP_H -#include "core/cowdata.h" +#include "core/templates/cowdata.h" #include "core/typedefs.h" template <class T, class V> class VMap { public: struct Pair { - T key; V value; _FORCE_INLINE_ Pair() {} _FORCE_INLINE_ Pair(const T &p_key, const V &p_value) { - key = p_key; value = p_value; } @@ -55,10 +53,10 @@ private: CowData<Pair> _cowdata; _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { - r_exact = false; - if (_cowdata.empty()) + if (_cowdata.is_empty()) { return 0; + } int low = 0; int high = _cowdata.size() - 1; @@ -66,8 +64,9 @@ private: int middle = 0; #ifdef DEBUG_ENABLED - if (low > high) + if (low > high) { ERR_PRINT("low > high, this may be a bug"); + } #endif while (low <= high) { middle = (low + high) / 2; @@ -83,15 +82,16 @@ private: } //return the position where this would be inserted - if (a[middle].key < p_val) + if (a[middle].key < p_val) { middle++; + } return middle; } _FORCE_INLINE_ int _find_exact(const T &p_val) const { - - if (_cowdata.empty()) + if (_cowdata.is_empty()) { return -1; + } int low = 0; int high = _cowdata.size() - 1; @@ -115,7 +115,6 @@ private: public: int insert(const T &p_key, const V &p_val) { - bool exact; int pos = _find(p_key, exact); if (exact) { @@ -127,64 +126,54 @@ public: } bool has(const T &p_val) const { - return _find_exact(p_val) != -1; } void erase(const T &p_val) { - int pos = _find_exact(p_val); - if (pos < 0) + if (pos < 0) { return; + } _cowdata.remove(pos); } int find(const T &p_val) const { - return _find_exact(p_val); } int find_nearest(const T &p_val) const { - bool exact; return _find(p_val, exact); } _FORCE_INLINE_ int size() const { return _cowdata.size(); } - _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } + _FORCE_INLINE_ bool is_empty() const { return _cowdata.is_empty(); } const Pair *get_array() const { - return _cowdata.ptr(); } Pair *get_array() { - return _cowdata.ptrw(); } const V &getv(int p_index) const { - return _cowdata.get(p_index).value; } V &getv(int p_index) { - return _cowdata.get_m(p_index).value; } const T &getk(int p_index) const { - return _cowdata.get(p_index).key; } T &getk(int p_index) { - return _cowdata.get_m(p_index).key; } inline const V &operator[](const T &p_key) const { - int pos = _find_exact(p_key); CRASH_COND(pos < 0); @@ -193,7 +182,6 @@ public: } inline V &operator[](const T &p_key) { - int pos = _find_exact(p_key); if (pos < 0) { pos = insert(p_key, V()); @@ -202,11 +190,13 @@ public: return _cowdata.get_m(pos).value; } - _FORCE_INLINE_ VMap(){}; + _FORCE_INLINE_ VMap() {} _FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); } + inline VMap &operator=(const VMap &p_from) { _cowdata._ref(p_from._cowdata); return *this; } }; + #endif // VMAP_H diff --git a/core/vset.h b/core/templates/vset.h index b96a115d21..6665651d42 100644 --- a/core/vset.h +++ b/core/templates/vset.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,19 +31,18 @@ #ifndef VSET_H #define VSET_H +#include "core/templates/vector.h" #include "core/typedefs.h" -#include "core/vector.h" template <class T> class VSet { - Vector<T> _data; _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { - r_exact = false; - if (_data.empty()) + if (_data.is_empty()) { return 0; + } int low = 0; int high = _data.size() - 1; @@ -51,8 +50,9 @@ class VSet { int middle = 0; #ifdef DEBUG_ENABLED - if (low > high) + if (low > high) { ERR_PRINT("low > high, this may be a bug"); + } #endif while (low <= high) { @@ -69,15 +69,16 @@ class VSet { } //return the position where this would be inserted - if (a[middle] < p_val) + if (a[middle] < p_val) { middle++; + } return middle; } _FORCE_INLINE_ int _find_exact(const T &p_val) const { - - if (_data.empty()) + if (_data.is_empty()) { return -1; + } int low = 0; int high = _data.size() - 1; @@ -101,43 +102,39 @@ class VSet { public: void insert(const T &p_val) { - bool exact; int pos = _find(p_val, exact); - if (exact) + if (exact) { return; + } _data.insert(pos, p_val); } bool has(const T &p_val) const { - return _find_exact(p_val) != -1; } void erase(const T &p_val) { - int pos = _find_exact(p_val); - if (pos < 0) + if (pos < 0) { return; + } _data.remove(pos); } int find(const T &p_val) const { - return _find_exact(p_val); } - _FORCE_INLINE_ bool empty() const { return _data.empty(); } + _FORCE_INLINE_ bool is_empty() const { return _data.is_empty(); } _FORCE_INLINE_ int size() const { return _data.size(); } inline T &operator[](int p_index) { - return _data.write[p_index]; } inline const T &operator[](int p_index) const { - return _data[p_index]; } }; diff --git a/core/typed_array.cpp b/core/typed_array.cpp deleted file mode 100644 index 55e45f0b3f..0000000000 --- a/core/typed_array.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "typed_array.h" diff --git a/core/typedefs.h b/core/typedefs.h index bafbffcded..cdbfb34e56 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -41,8 +41,8 @@ #include "platform_config.h" // Should be available everywhere. -#include "core/error_list.h" -#include "core/int_types.h" +#include "core/error/error_list.h" +#include <cstdint> // Turn argument to string constant: // https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing @@ -92,7 +92,7 @@ #endif #ifndef SGN -#define SGN(m_v) (((m_v) < 0) ? (-1.0) : (+1.0)) +#define SGN(m_v) (((m_v) == 0) ? (0.0) : (((m_v) < 0) ? (-1.0) : (+1.0))) #endif #ifndef MIN @@ -185,13 +185,28 @@ static _FORCE_INLINE_ T nearest_power_of_2_templated(T x) { // Function to find the nearest (bigger) power of 2 to an integer. static inline unsigned int nearest_shift(unsigned int p_number) { for (int i = 30; i >= 0; i--) { - if (p_number & (1 << i)) + if (p_number & (1 << i)) { return i + 1; + } } return 0; } +// constexpr function to find the floored log2 of a number +template <typename T> +constexpr T floor_log2(T x) { + return x < 2 ? x : 1 + floor_log2(x >> 1); +} + +// Get the number of bits needed to represent the number. +// IE, if you pass in 8, you will get 4. +// If you want to know how many bits are needed to store 8 values however, pass in (8 - 1). +template <typename T> +constexpr T get_num_bits(T x) { + return floor_log2(x); +} + // Swap 16, 32 and 64 bits value for endianness. #if defined(__GNUC__) #define BSWAP16(x) __builtin_bswap16(x) @@ -262,4 +277,8 @@ struct BuildIndexSequence : BuildIndexSequence<N - 1, N - 1, Is...> {}; template <size_t... Is> struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {}; +#ifdef DEBUG_ENABLED +#define DEBUG_METHODS_ENABLED +#endif + #endif // TYPEDEFS_H diff --git a/core/variant/SCsub b/core/variant/SCsub new file mode 100644 index 0000000000..7f4c8b7788 --- /dev/null +++ b/core/variant/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +env_variant = env.Clone() + +env_variant.add_source_files(env.core_sources, "*.cpp") diff --git a/core/array.cpp b/core/variant/array.cpp index 7c0129ffde..48916f941e 100644 --- a/core/array.cpp +++ b/core/variant/array.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,11 +31,11 @@ #include "array.h" #include "container_type_validate.h" -#include "core/hashfuncs.h" -#include "core/object.h" -#include "core/script_language.h" -#include "core/variant.h" -#include "core/vector.h" +#include "core/object/class_db.h" +#include "core/object/script_language.h" +#include "core/templates/hashfuncs.h" +#include "core/templates/vector.h" +#include "core/variant/variant.h" class ArrayPrivate { public: @@ -46,13 +46,13 @@ public: }; void Array::_ref(const Array &p_from) const { - ArrayPrivate *_fp = p_from._p; ERR_FAIL_COND(!_fp); // should NOT happen. - if (_fp == _p) + if (_fp == _p) { return; // whatever it is, nothing to do here move along + } bool success = _fp->refcount.ref(); @@ -64,9 +64,9 @@ void Array::_ref(const Array &p_from) const { } void Array::_unref() const { - - if (!_p) + if (!_p) { return; + } if (_p->refcount.unref()) { memdelete(_p); @@ -75,46 +75,70 @@ void Array::_unref() const { } Variant &Array::operator[](int p_idx) { - return _p->array.write[p_idx]; } const Variant &Array::operator[](int p_idx) const { - return _p->array[p_idx]; } int Array::size() const { - return _p->array.size(); } -bool Array::empty() const { - return _p->array.empty(); +bool Array::is_empty() const { + return _p->array.is_empty(); } -void Array::clear() { +void Array::clear() { _p->array.clear(); } bool Array::operator==(const Array &p_array) const { - return _p == p_array._p; } -uint32_t Array::hash() const { +bool Array::operator!=(const Array &p_array) const { + return !operator==(p_array); +} + +bool Array::operator<(const Array &p_array) const { + int a_len = size(); + int b_len = p_array.size(); + + int min_cmp = MIN(a_len, b_len); + + for (int i = 0; i < min_cmp; i++) { + if (operator[](i) < p_array[i]) { + return true; + } else if (p_array[i] < operator[](i)) { + return false; + } + } + + return a_len < b_len; +} + +bool Array::operator<=(const Array &p_array) const { + return !operator>(p_array); +} +bool Array::operator>(const Array &p_array) const { + return p_array < *this; +} +bool Array::operator>=(const Array &p_array) const { + return !operator<(p_array); +} +uint32_t Array::hash() const { uint32_t h = hash_djb2_one_32(0); for (int i = 0; i < _p->array.size(); i++) { - h = hash_djb2_one_32(_p->array[i].hash(), h); } return h; } void Array::_assign(const Array &p_array) { - if (_p->typed.type != Variant::OBJECT && _p->typed.type == p_array._p->typed.type) { //same type or untyped, just reference, shuold be fine _ref(p_array); @@ -141,7 +165,7 @@ void Array::_assign(const Array &p_array) { } else if (Variant::can_convert_strict(src_val.get_type(), _p->typed.type)) { Variant *ptr = &src_val; Callable::CallError ce; - new_array.write[i] = Variant::construct(_p->typed.type, (const Variant **)&ptr, 1, ce, true); + Variant::construct(_p->typed.type, new_array.write[i], (const Variant **)&ptr, 1, ce); if (ce.error != Callable::CallError::CALL_OK) { ERR_FAIL_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'."); } @@ -162,25 +186,27 @@ void Array::_assign(const Array &p_array) { void Array::operator=(const Array &p_array) { _assign(p_array); } -void Array::push_back(const Variant &p_value) { +void Array::push_back(const Variant &p_value) { ERR_FAIL_COND(!_p->typed.validate(p_value, "push_back")); _p->array.push_back(p_value); } -Error Array::resize(int p_new_size) { +void Array::append_array(const Array &p_array) { + ERR_FAIL_COND(!_p->typed.validate(p_array, "append_array")); + _p->array.append_array(p_array._p->array); +} +Error Array::resize(int p_new_size) { return _p->array.resize(p_new_size); } void Array::insert(int p_pos, const Variant &p_value) { - ERR_FAIL_COND(!_p->typed.validate(p_value, "insert")); _p->array.insert(p_pos, p_value); } void Array::erase(const Variant &p_value) { - ERR_FAIL_COND(!_p->typed.validate(p_value, "erase")); _p->array.erase(p_value); } @@ -196,15 +222,14 @@ Variant Array::back() const { } int Array::find(const Variant &p_value, int p_from) const { - ERR_FAIL_COND_V(!_p->typed.validate(p_value, "find"), -1); return _p->array.find(p_value, p_from); } int Array::rfind(const Variant &p_value, int p_from) const { - - if (_p->array.size() == 0) + if (_p->array.size() == 0) { return -1; + } ERR_FAIL_COND_V(!_p->typed.validate(p_value, "rfind"), -1); if (p_from < 0) { @@ -217,7 +242,6 @@ int Array::rfind(const Variant &p_value, int p_from) const { } for (int i = p_from; i >= 0; i--) { - if (_p->array[i] == p_value) { return i; } @@ -227,20 +251,18 @@ int Array::rfind(const Variant &p_value, int p_from) const { } int Array::find_last(const Variant &p_value) const { - ERR_FAIL_COND_V(!_p->typed.validate(p_value, "find_last"), -1); return rfind(p_value); } int Array::count(const Variant &p_value) const { - ERR_FAIL_COND_V(!_p->typed.validate(p_value, "count"), 0); - if (_p->array.size() == 0) + if (_p->array.size() == 0) { return 0; + } int amount = 0; for (int i = 0; i < _p->array.size(); i++) { - if (_p->array[i] == p_value) { amount++; } @@ -256,24 +278,20 @@ bool Array::has(const Variant &p_value) const { } void Array::remove(int p_pos) { - _p->array.remove(p_pos); } void Array::set(int p_idx, const Variant &p_value) { - ERR_FAIL_COND(!_p->typed.validate(p_value, "set")); operator[](p_idx) = p_value; } const Variant &Array::get(int p_idx) const { - return operator[](p_idx); } Array Array::duplicate(bool p_deep) const { - Array new_arr; int element_count = size(); new_arr.resize(element_count); @@ -286,7 +304,6 @@ Array Array::duplicate(bool p_deep) const { } int Array::_clamp_slice_index(int p_index) const { - int arr_size = size(); int fixed_index = CLAMP(p_index, -arr_size, arr_size - 1); if (fixed_index < 0) { @@ -301,14 +318,17 @@ Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // l ERR_FAIL_COND_V_MSG(p_step == 0, new_arr, "Array slice step size cannot be zero."); - if (empty()) // Don't try to slice empty arrays. + if (is_empty()) { // Don't try to slice empty arrays. return new_arr; + } if (p_step > 0) { - if (p_begin >= size() || p_end < -size()) + if (p_begin >= size() || p_end < -size()) { return new_arr; + } } else { // p_step < 0 - if (p_begin < -size() || p_end >= size()) + if (p_begin < -size() || p_end >= size()) { return new_arr; + } } int begin = _clamp_slice_index(p_begin); @@ -335,54 +355,49 @@ Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // l } struct _ArrayVariantSort { - _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { bool valid = false; Variant res; Variant::evaluate(Variant::OP_LESS, p_l, p_r, res, valid); - if (!valid) + if (!valid) { res = false; + } return res; } }; -Array &Array::sort() { - +void Array::sort() { _p->array.sort_custom<_ArrayVariantSort>(); - return *this; } struct _ArrayVariantSortCustom { - - Object *obj; + Object *obj = nullptr; StringName func; _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { - const Variant *args[2] = { &p_l, &p_r }; Callable::CallError err; bool res = obj->call(func, args, 2, err); - if (err.error != Callable::CallError::CALL_OK) + if (err.error != Callable::CallError::CALL_OK) { res = false; + } return res; } }; -Array &Array::sort_custom(Object *p_obj, const StringName &p_function) { - - ERR_FAIL_NULL_V(p_obj, *this); +void Array::sort_custom(Object *p_obj, const StringName &p_function) { + ERR_FAIL_NULL(p_obj); SortArray<Variant, _ArrayVariantSortCustom, true> avs; avs.compare.obj = p_obj; avs.compare.func = p_function; avs.sort(_p->array.ptrw(), _p->array.size()); - return *this; } void Array::shuffle() { - const int n = _p->array.size(); - if (n < 2) + if (n < 2) { return; + } Variant *data = _p->array.ptrw(); for (int i = n - 1; i >= 1; i--) { const int j = Math::rand() % (i + 1); @@ -394,7 +409,6 @@ void Array::shuffle() { template <typename Less> _FORCE_INLINE_ int bisect(const Vector<Variant> &p_array, const Variant &p_value, bool p_before, const Less &p_less) { - int lo = 0; int hi = p_array.size(); if (p_before) { @@ -420,13 +434,11 @@ _FORCE_INLINE_ int bisect(const Vector<Variant> &p_array, const Variant &p_value } int Array::bsearch(const Variant &p_value, bool p_before) { - ERR_FAIL_COND_V(!_p->typed.validate(p_value, "binary search"), -1); return bisect(_p->array, p_value, p_before, _ArrayVariantSort()); } int Array::bsearch_custom(const Variant &p_value, Object *p_obj, const StringName &p_function, bool p_before) { - ERR_FAIL_COND_V(!_p->typed.validate(p_value, "custom binary search"), -1); ERR_FAIL_NULL_V(p_obj, 0); @@ -437,21 +449,17 @@ int Array::bsearch_custom(const Variant &p_value, Object *p_obj, const StringNam return bisect(_p->array, p_value, p_before, less); } -Array &Array::invert() { - +void Array::invert() { _p->array.invert(); - return *this; } void Array::push_front(const Variant &p_value) { - ERR_FAIL_COND(!_p->typed.validate(p_value, "push_front")); _p->array.insert(0, p_value); } Variant Array::pop_back() { - - if (!_p->array.empty()) { + if (!_p->array.is_empty()) { int n = _p->array.size() - 1; Variant ret = _p->array.get(n); _p->array.resize(n); @@ -461,8 +469,7 @@ Variant Array::pop_back() { } Variant Array::pop_front() { - - if (!_p->array.empty()) { + if (!_p->array.is_empty()) { Variant ret = _p->array.get(0); _p->array.remove(0); return ret; @@ -471,7 +478,6 @@ Variant Array::pop_front() { } Variant Array::min() const { - Variant minval; for (int i = 0; i < size(); i++) { if (i == 0) { @@ -494,7 +500,6 @@ Variant Array::min() const { } Variant Array::max() const { - Variant maxval; for (int i = 0; i < size(); i++) { if (i == 0) { @@ -542,17 +547,15 @@ void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Var } Array::Array(const Array &p_from) { - _p = nullptr; _ref(p_from); } Array::Array() { - _p = memnew(ArrayPrivate); _p->refcount.init(); } -Array::~Array() { +Array::~Array() { _unref(); } diff --git a/core/array.h b/core/variant/array.h index 14db57f15f..26de5477b4 100644 --- a/core/array.h +++ b/core/variant/array.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -39,7 +39,6 @@ class Object; class StringName; class Array { - mutable ArrayPrivate *_p; void _ref(const Array &p_from) const; void _unref() const; @@ -58,16 +57,18 @@ public: const Variant &get(int p_idx) const; int size() const; - bool empty() const; + bool is_empty() const; void clear(); bool operator==(const Array &p_array) const; + bool operator!=(const Array &p_array) const; uint32_t hash() const; void operator=(const Array &p_array); void push_back(const Variant &p_value); _FORCE_INLINE_ void append(const Variant &p_value) { push_back(p_value); } //for python compatibility + void append_array(const Array &p_array); Error resize(int p_new_size); void insert(int p_pos, const Variant &p_value); @@ -76,12 +77,12 @@ public: Variant front() const; Variant back() const; - Array &sort(); - Array &sort_custom(Object *p_obj, const StringName &p_function); + void sort(); + void sort_custom(Object *p_obj, const StringName &p_function); void shuffle(); int bsearch(const Variant &p_value, bool p_before = true); int bsearch_custom(const Variant &p_value, Object *p_obj, const StringName &p_function, bool p_before = true); - Array &invert(); + void invert(); int find(const Variant &p_value, int p_from = 0) const; int rfind(const Variant &p_value, int p_from = -1) const; @@ -99,6 +100,11 @@ public: Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const; + bool operator<(const Array &p_array) const; + bool operator<=(const Array &p_array) const; + bool operator>(const Array &p_array) const; + bool operator>=(const Array &p_array) const; + Variant min() const; Variant max() const; diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h new file mode 100644 index 0000000000..9d8e262cc9 --- /dev/null +++ b/core/variant/binder_common.h @@ -0,0 +1,666 @@ +/*************************************************************************/ +/* binder_common.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 BINDER_COMMON_H +#define BINDER_COMMON_H + +#include "core/object/object.h" +#include "core/templates/list.h" +#include "core/templates/simple_type.h" +#include "core/typedefs.h" +#include "core/variant/method_ptrcall.h" +#include "core/variant/type_info.h" +#include "core/variant/variant.h" +#include "core/variant/variant_internal.h" + +#include <stdio.h> + +template <class T> +struct VariantCaster { + static _FORCE_INLINE_ T cast(const Variant &p_variant) { + return p_variant; + } +}; + +template <class T> +struct VariantCaster<T &> { + static _FORCE_INLINE_ T cast(const Variant &p_variant) { + return p_variant; + } +}; + +template <class T> +struct VariantCaster<const T &> { + static _FORCE_INLINE_ T cast(const Variant &p_variant) { + return p_variant; + } +}; + +#define VARIANT_ENUM_CAST(m_enum) \ + MAKE_ENUM_TYPE_INFO(m_enum) \ + template <> \ + struct VariantCaster<m_enum> { \ + static _FORCE_INLINE_ m_enum cast(const Variant &p_variant) { \ + return (m_enum)p_variant.operator int(); \ + } \ + }; \ + template <> \ + struct PtrToArg<m_enum> { \ + _FORCE_INLINE_ static m_enum convert(const void *p_ptr) { \ + return m_enum(*reinterpret_cast<const int *>(p_ptr)); \ + } \ + _FORCE_INLINE_ static void encode(m_enum p_val, const void *p_ptr) { \ + *(int *)p_ptr = p_val; \ + } \ + }; + +// Object enum casts must go here +VARIANT_ENUM_CAST(Object::ConnectFlags); + +VARIANT_ENUM_CAST(Vector3::Axis); + +VARIANT_ENUM_CAST(Error); +VARIANT_ENUM_CAST(Side); +VARIANT_ENUM_CAST(Corner); +VARIANT_ENUM_CAST(Orientation); +VARIANT_ENUM_CAST(HAlign); +VARIANT_ENUM_CAST(VAlign); +VARIANT_ENUM_CAST(PropertyHint); +VARIANT_ENUM_CAST(PropertyUsageFlags); +VARIANT_ENUM_CAST(Variant::Type); +VARIANT_ENUM_CAST(Variant::Operator); + +template <> +struct VariantCaster<char32_t> { + static _FORCE_INLINE_ char32_t cast(const Variant &p_variant) { + return (char32_t)p_variant.operator int(); + } +}; + +template <> +struct PtrToArg<char32_t> { + _FORCE_INLINE_ static char32_t convert(const void *p_ptr) { + return char32_t(*reinterpret_cast<const int *>(p_ptr)); + } + _FORCE_INLINE_ static void encode(char32_t p_val, const void *p_ptr) { + *(int *)p_ptr = p_val; + } +}; + +template <typename T> +struct VariantObjectClassChecker { + static _FORCE_INLINE_ bool check(const Variant &p_variant) { + return true; + } +}; + +template <> +struct VariantObjectClassChecker<Node *> { + static _FORCE_INLINE_ bool check(const Variant &p_variant) { + Object *obj = p_variant; + Node *node = p_variant; + return node || !obj; + } +}; + +template <> +struct VariantObjectClassChecker<Control *> { + static _FORCE_INLINE_ bool check(const Variant &p_variant) { + Object *obj = p_variant; + Control *control = p_variant; + return control || !obj; + } +}; + +#ifdef DEBUG_METHODS_ENABLED + +template <class T> +struct VariantCasterAndValidate { + static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { + Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; + if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype) || + !VariantObjectClassChecker<T>::check(p_args[p_arg_idx])) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = p_arg_idx; + r_error.expected = argtype; + } + + return VariantCaster<T>::cast(*p_args[p_arg_idx]); + } +}; + +template <class T> +struct VariantCasterAndValidate<T &> { + static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { + Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; + if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype) || + !VariantObjectClassChecker<T>::check(p_args[p_arg_idx])) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = p_arg_idx; + r_error.expected = argtype; + } + + return VariantCaster<T>::cast(*p_args[p_arg_idx]); + } +}; + +template <class T> +struct VariantCasterAndValidate<const T &> { + static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { + Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; + if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype) || + !VariantObjectClassChecker<T>::check(p_args[p_arg_idx])) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = p_arg_idx; + r_error.expected = argtype; + } + + return VariantCaster<T>::cast(*p_args[p_arg_idx]); + } +}; + +#endif // DEBUG_METHODS_ENABLED + +template <class T, class... P, size_t... Is> +void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { + r_error.error = Callable::CallError::CALL_OK; + +#ifdef DEBUG_METHODS_ENABLED + (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...); +#else + (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...); +#endif + (void)(p_args); //avoid warning +} + +template <class T, class... P, size_t... Is> +void call_with_variant_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { + r_error.error = Callable::CallError::CALL_OK; + +#ifdef DEBUG_METHODS_ENABLED + (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...); +#else + (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...); +#endif + (void)(p_args); //avoid warning +} + +template <class T, class... P, size_t... Is> +void call_with_ptr_args_helper(T *p_instance, void (T::*p_method)(P...), const void **p_args, IndexSequence<Is...>) { + (p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...); +} + +template <class T, class... P, size_t... Is> +void call_with_ptr_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const void **p_args, IndexSequence<Is...>) { + (p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...); +} + +template <class T, class R, class... P, size_t... Is> +void call_with_ptr_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const void **p_args, void *r_ret, IndexSequence<Is...>) { + PtrToArg<R>::encode((p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...), r_ret); +} + +template <class T, class R, class... P, size_t... Is> +void call_with_ptr_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const void **p_args, void *r_ret, IndexSequence<Is...>) { + PtrToArg<R>::encode((p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...), r_ret); +} + +template <class T, class R, class... P, size_t... Is> +void call_with_ptr_args_static_retc_helper(T *p_instance, R (*p_method)(T *, P...), const void **p_args, void *r_ret, IndexSequence<Is...>) { + PtrToArg<R>::encode(p_method(p_instance, PtrToArg<P>::convert(p_args[Is])...), r_ret); +} + +template <class T, class... P, size_t... Is> +void call_with_validated_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, IndexSequence<Is...>) { + (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); +} + +template <class T, class... P, size_t... Is> +void call_with_validated_variant_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, IndexSequence<Is...>) { + (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); +} + +template <class T, class R, class... P, size_t... Is> +void call_with_validated_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { + VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); +} + +template <class T, class R, class... P, size_t... Is> +void call_with_validated_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { + VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); +} + +template <class T, class R, class... P, size_t... Is> +void call_with_validated_variant_args_static_retc_helper(T *p_instance, R (*p_method)(T *, P...), const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { + VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, p_method(p_instance, (VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); +} + +template <class T, class... P> +void call_with_variant_args(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +#ifdef DEBUG_METHODS_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class... P> +void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error, const Vector<Variant> &default_values) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + int32_t missing = (int32_t)sizeof...(P) - (int32_t)p_argcount; + + int32_t dvs = default_values.size(); +#ifdef DEBUG_ENABLED + if (missing > dvs) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + const Variant *args[sizeof...(P) == 0 ? 1 : sizeof...(P)]; //avoid zero sized array + for (int32_t i = 0; i < (int32_t)sizeof...(P); i++) { + if (i < p_argcount) { + args[i] = p_args[i]; + } else { + args[i] = &default_values[i - p_argcount + (dvs - missing)]; + } + } + + call_with_variant_args_helper(p_instance, p_method, args, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class... P> +void call_with_variant_argsc(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +#ifdef DEBUG_METHODS_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class... P> +void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Callable::CallError &r_error, const Vector<Variant> &default_values) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + int32_t missing = (int32_t)sizeof...(P) - (int32_t)p_argcount; + + int32_t dvs = default_values.size(); +#ifdef DEBUG_ENABLED + if (missing > dvs) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + const Variant *args[sizeof...(P) == 0 ? 1 : sizeof...(P)]; //avoid zero sized array + for (int32_t i = 0; i < (int32_t)sizeof...(P); i++) { + if (i < p_argcount) { + args[i] = p_args[i]; + } else { + args[i] = &default_values[i - p_argcount + (dvs - missing)]; + } + } + + call_with_variant_argsc_helper(p_instance, p_method, args, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P> +void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error, const Vector<Variant> &default_values) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + int32_t missing = (int32_t)sizeof...(P) - (int32_t)p_argcount; + + int32_t dvs = default_values.size(); +#ifdef DEBUG_ENABLED + if (missing > dvs) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + const Variant *args[sizeof...(P) == 0 ? 1 : sizeof...(P)]; //avoid zero sized array + for (int32_t i = 0; i < (int32_t)sizeof...(P); i++) { + if (i < p_argcount) { + args[i] = p_args[i]; + } else { + args[i] = &default_values[i - p_argcount + (dvs - missing)]; + } + } + + call_with_variant_args_ret_helper(p_instance, p_method, args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P> +void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error, const Vector<Variant> &default_values) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + int32_t missing = (int32_t)sizeof...(P) - (int32_t)p_argcount; + + int32_t dvs = default_values.size(); +#ifdef DEBUG_ENABLED + if (missing > dvs) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + const Variant *args[sizeof...(P) == 0 ? 1 : sizeof...(P)]; //avoid zero sized array + for (int32_t i = 0; i < (int32_t)sizeof...(P); i++) { + if (i < p_argcount) { + args[i] = p_args[i]; + } else { + args[i] = &default_values[i - p_argcount + (dvs - missing)]; + } + } + + call_with_variant_args_retc_helper(p_instance, p_method, args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class... P> +void call_with_ptr_args(T *p_instance, void (T::*p_method)(P...), const void **p_args) { + call_with_ptr_args_helper<T, P...>(p_instance, p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class... P> +void call_with_ptr_argsc(T *p_instance, void (T::*p_method)(P...) const, const void **p_args) { + call_with_ptr_argsc_helper<T, P...>(p_instance, p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P> +void call_with_ptr_args_ret(T *p_instance, R (T::*p_method)(P...), const void **p_args, void *r_ret) { + call_with_ptr_args_ret_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P> +void call_with_ptr_args_retc(T *p_instance, R (T::*p_method)(P...) const, const void **p_args, void *r_ret) { + call_with_ptr_args_retc_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P> +void call_with_ptr_args_static_retc(T *p_instance, R (*p_method)(T *, P...), const void **p_args, void *r_ret) { + call_with_ptr_args_static_retc_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class... P> +void call_with_validated_variant_args(Variant *base, void (T::*p_method)(P...), const Variant **p_args) { + call_with_validated_variant_args_helper<T, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P> +void call_with_validated_variant_args_ret(Variant *base, R (T::*p_method)(P...), const Variant **p_args, Variant *r_ret) { + call_with_validated_variant_args_ret_helper<T, R, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P> +void call_with_validated_variant_args_retc(Variant *base, R (T::*p_method)(P...) const, const Variant **p_args, Variant *r_ret) { + call_with_validated_variant_args_retc_helper<T, R, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P> +void call_with_validated_variant_args_static_retc(Variant *base, R (*p_method)(T *, P...), const Variant **p_args, Variant *r_ret) { + call_with_validated_variant_args_static_retc_helper<T, R, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); +} + +// GCC raises "parameter 'p_args' set but not used" when P = {}, +// it's not clever enough to treat other P values as making this branch valid. +#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +#endif + +#ifdef DEBUG_METHODS_ENABLED + +template <class Q> +void call_get_argument_type_helper(int p_arg, int &index, Variant::Type &type) { + if (p_arg == index) { + type = GetTypeInfo<Q>::VARIANT_TYPE; + } + index++; +} + +template <class... P> +Variant::Type call_get_argument_type(int p_arg) { + Variant::Type type = Variant::NIL; + int index = 0; + // I think rocket science is simpler than modern C++. + using expand_type = int[]; + expand_type a{ 0, (call_get_argument_type_helper<P>(p_arg, index, type), 0)... }; + (void)a; // Suppress (valid, but unavoidable) -Wunused-variable warning. + (void)index; // Suppress GCC warning. + return type; +} + +template <class Q> +void call_get_argument_type_info_helper(int p_arg, int &index, PropertyInfo &info) { + if (p_arg == index) { + info = GetTypeInfo<Q>::get_class_info(); + } + index++; +} + +template <class... P> +void call_get_argument_type_info(int p_arg, PropertyInfo &info) { + int index = 0; + // I think rocket science is simpler than modern C++. + using expand_type = int[]; + expand_type a{ 0, (call_get_argument_type_info_helper<P>(p_arg, index, info), 0)... }; + (void)a; // Suppress (valid, but unavoidable) -Wunused-variable warning. + (void)index; // Suppress GCC warning. +} + +template <class Q> +void call_get_argument_metadata_helper(int p_arg, int &index, GodotTypeInfo::Metadata &md) { + if (p_arg == index) { + md = GetTypeInfo<Q>::METADATA; + } + index++; +} + +template <class... P> +GodotTypeInfo::Metadata call_get_argument_metadata(int p_arg) { + GodotTypeInfo::Metadata md = GodotTypeInfo::METADATA_NONE; + + int index = 0; + // I think rocket science is simpler than modern C++. + using expand_type = int[]; + expand_type a{ 0, (call_get_argument_metadata_helper<P>(p_arg, index, md), 0)... }; + (void)a; // Suppress (valid, but unavoidable) -Wunused-variable warning. + (void)index; + return md; +} + +#else + +template <class... P> +Variant::Type call_get_argument_type(int p_arg) { + return Variant::NIL; +} + +#endif // DEBUG_METHODS_ENABLED + +////////////////////// + +template <class T, class R, class... P, size_t... Is> +void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) { + r_error.error = Callable::CallError::CALL_OK; + +#ifdef DEBUG_METHODS_ENABLED + r_ret = (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...); +#else + r_ret = (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...); +#endif +} + +template <class T, class R, class... P> +void call_with_variant_args_ret(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { +#ifdef DEBUG_METHODS_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + call_with_variant_args_ret_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P, size_t... Is> +void call_with_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) { + r_error.error = Callable::CallError::CALL_OK; + +#ifdef DEBUG_METHODS_ENABLED + r_ret = (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...); +#else + r_ret = (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...); +#endif + (void)p_args; +} + +template <class T, class R, class... P> +void call_with_variant_args_retc(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { +#ifdef DEBUG_METHODS_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + call_with_variant_args_retc_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class T, class R, class... P, size_t... Is> +void call_with_variant_args_retc_static_helper(T *p_instance, R (*p_method)(T *, P...), const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) { + r_error.error = Callable::CallError::CALL_OK; + +#ifdef DEBUG_METHODS_ENABLED + r_ret = (p_method)(p_instance, VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...); +#else + r_ret = (p_method)(p_instance, VariantCaster<P>::cast(*p_args[Is])...); +#endif + + (void)p_args; +} + +template <class T, class R, class... P> +void call_with_variant_args_retc_static_helper_dv(T *p_instance, R (*p_method)(T *, P...), const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &default_values, Callable::CallError &r_error) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + int32_t missing = (int32_t)sizeof...(P) - (int32_t)p_argcount; + + int32_t dvs = default_values.size(); +#ifdef DEBUG_ENABLED + if (missing > dvs) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = sizeof...(P); + return; + } +#endif + + const Variant *args[sizeof...(P) == 0 ? 1 : sizeof...(P)]; //avoid zero sized array + for (int32_t i = 0; i < (int32_t)sizeof...(P); i++) { + if (i < p_argcount) { + args[i] = p_args[i]; + } else { + args[i] = &default_values[i - p_argcount + (dvs - missing)]; + } + } + + call_with_variant_args_retc_static_helper(p_instance, p_method, args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // BINDER_COMMON_H diff --git a/core/callable.cpp b/core/variant/callable.cpp index 6a5dc151e5..bd51e2dd1e 100644 --- a/core/callable.cpp +++ b/core/variant/callable.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,17 +29,18 @@ /*************************************************************************/ #include "callable.h" -#include "core/script_language.h" -#include "message_queue.h" -#include "object.h" -#include "reference.h" + +#include "callable_bind.h" +#include "core/object/message_queue.h" +#include "core/object/object.h" +#include "core/object/reference.h" +#include "core/object/script_language.h" void Callable::call_deferred(const Variant **p_arguments, int p_argcount) const { MessageQueue::get_singleton()->push_callable(*this, p_arguments, p_argcount); } void Callable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, CallError &r_call_error) const { - if (is_null()) { r_call_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL; r_call_error.argument = 0; @@ -53,6 +54,18 @@ void Callable::call(const Variant **p_arguments, int p_argcount, Variant &r_retu } } +Callable Callable::bind(const Variant **p_arguments, int p_argcount) const { + Vector<Variant> args; + args.resize(p_argcount); + for (int i = 0; i < p_argcount; i++) { + args.write[i] = *p_arguments[i]; + } + return Callable(memnew(CallableCustomBind(*this, args))); +} +Callable Callable::unbind(int p_argcount) const { + return Callable(memnew(CallableCustomUnbind(*this, p_argcount))); +} + Object *Callable::get_object() const { if (is_null()) { return nullptr; @@ -72,6 +85,7 @@ ObjectID Callable::get_object_id() const { return ObjectID(object); } } + StringName Callable::get_method() const { ERR_FAIL_COND_V_MSG(is_custom(), StringName(), vformat("Can't get method on CallableCustom \"%s\".", operator String())); @@ -84,6 +98,18 @@ CallableCustom *Callable::get_custom() const { return custom; } +const Callable *Callable::get_base_comparator() const { + const Callable *comparator = nullptr; + if (is_custom()) { + comparator = custom->get_base_comparator(); + } + if (comparator) { + return comparator; + } else { + return this; + } +} + uint32_t Callable::hash() const { if (is_custom()) { return custom->hash(); @@ -117,9 +143,11 @@ bool Callable::operator==(const Callable &p_callable) const { return false; } } + bool Callable::operator!=(const Callable &p_callable) const { return !(*this == p_callable); } + bool Callable::operator<(const Callable &p_callable) const { bool custom_a = is_custom(); bool custom_b = p_callable.is_custom(); @@ -178,7 +206,6 @@ void Callable::operator=(const Callable &p_callable) { } Callable::operator String() const { - if (is_custom()) { return custom->get_as_text(); } else { @@ -191,7 +218,6 @@ Callable::operator String() const { String class_name = base->get_class(); Ref<Script> script = base->get_script(); if (script.is_valid() && script->get_path().is_resource_file()) { - class_name += "(" + script->get_path().get_file() + ")"; } return class_name + "::" + String(method); @@ -224,6 +250,7 @@ Callable::Callable(ObjectID p_object, const StringName &p_method) { object = p_object; method = p_method; } + Callable::Callable(CallableCustom *p_custom) { if (p_custom->referenced) { object = 0; @@ -233,6 +260,7 @@ Callable::Callable(CallableCustom *p_custom) { object = 0; //ensure object is all zero, since pointer may be 32 bits custom = p_custom; } + Callable::Callable(const Callable &p_callable) { if (p_callable.is_custom()) { if (!p_callable.custom->ref_count.ref()) { @@ -255,12 +283,11 @@ Callable::~Callable() { } } -Callable::Callable() { - object = 0; +const Callable *CallableCustom::get_base_comparator() const { + return nullptr; } CallableCustom::CallableCustom() { - referenced = false; ref_count.init(); } @@ -269,9 +296,11 @@ CallableCustom::CallableCustom() { Object *Signal::get_object() const { return ObjectDB::get_instance(object); } + ObjectID Signal::get_object_id() const { return object; } + StringName Signal::get_name() const { return name; } @@ -298,7 +327,6 @@ Signal::operator String() const { String class_name = base->get_class(); Ref<Script> script = base->get_script(); if (script.is_valid() && script->get_path().is_resource_file()) { - class_name += "(" + script->get_path().get_file() + ")"; } return class_name + "::[signal]" + String(name); @@ -315,18 +343,20 @@ Error Signal::emit(const Variant **p_arguments, int p_argcount) const { return obj->emit_signal(name, p_arguments, p_argcount); } -Error Signal::connect(const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) { +Error Signal::connect(const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) { Object *object = get_object(); ERR_FAIL_COND_V(!object, ERR_UNCONFIGURED); return object->connect(name, p_callable, p_binds, p_flags); } + void Signal::disconnect(const Callable &p_callable) { Object *object = get_object(); ERR_FAIL_COND(!object); object->disconnect(name, p_callable); } + bool Signal::is_connected(const Callable &p_callable) const { Object *object = get_object(); ERR_FAIL_COND_V(!object, false); @@ -349,17 +379,15 @@ Array Signal::get_connections() const { } return arr; } -Signal::Signal(const Object *p_object, const StringName &p_name) { +Signal::Signal(const Object *p_object, const StringName &p_name) { ERR_FAIL_COND_MSG(p_object == nullptr, "Object argument to Signal constructor must be non-null"); object = p_object->get_instance_id(); name = p_name; } -Signal::Signal(ObjectID p_object, const StringName &p_name) { +Signal::Signal(ObjectID p_object, const StringName &p_name) { object = p_object; name = p_name; } -Signal::Signal() { -} diff --git a/core/callable.h b/core/variant/callable.h index 7fa024dccd..090fd888e2 100644 --- a/core/callable.h +++ b/core/variant/callable.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,9 +31,9 @@ #ifndef CALLABLE_H #define CALLABLE_H -#include "core/list.h" -#include "core/object_id.h" -#include "core/string_name.h" +#include "core/object/object_id.h" +#include "core/string/string_name.h" +#include "core/templates/list.h" class Object; class Variant; @@ -45,11 +45,10 @@ class CallableCustom; // but can be optimized or customized. class Callable { - //needs to be max 16 bytes in 64 bits StringName method; union { - uint64_t object; + uint64_t object = 0; CallableCustom *custom; }; @@ -63,9 +62,9 @@ public: CALL_ERROR_TOO_FEW_ARGUMENTS, // expected is number of arguments CALL_ERROR_INSTANCE_IS_NULL, }; - Error error; - int argument; - int expected; + Error error = Error::CALL_OK; + int argument = 0; + int expected = 0; }; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, CallError &r_call_error) const; @@ -75,12 +74,15 @@ public: return method == StringName() && object == 0; } _FORCE_INLINE_ bool is_custom() const { - return method == StringName() && custom != 0; + return method == StringName() && custom != nullptr; } _FORCE_INLINE_ bool is_standard() const { return method != StringName(); } + Callable bind(const Variant **p_arguments, int p_argcount) const; + Callable unbind(int p_argcount) const; + Object *get_object() const; ObjectID get_object_id() const; StringName get_method() const; @@ -88,6 +90,8 @@ public: uint32_t hash() const; + const Callable *get_base_comparator() const; //used for bind/unbind to do less precise comparisons (ignoring binds) in signal connect/disconnect + bool operator==(const Callable &p_callable) const; bool operator!=(const Callable &p_callable) const; bool operator<(const Callable &p_callable) const; @@ -100,14 +104,14 @@ public: Callable(ObjectID p_object, const StringName &p_method); Callable(CallableCustom *p_custom); Callable(const Callable &p_callable); - Callable(); + Callable() {} ~Callable(); }; class CallableCustom { friend class Callable; SafeRefCount ref_count; - bool referenced; + bool referenced = false; public: typedef bool (*CompareEqualFunc)(const CallableCustom *p_a, const CallableCustom *p_b); @@ -120,6 +124,7 @@ public: virtual CompareLessFunc get_compare_less_func() const = 0; virtual ObjectID get_object() const = 0; //must always be able to provide an object virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const = 0; + virtual const Callable *get_base_comparator() const; CallableCustom(); virtual ~CallableCustom() {} @@ -156,7 +161,7 @@ public: Array get_connections() const; Signal(const Object *p_object, const StringName &p_name); Signal(ObjectID p_object, const StringName &p_name); - Signal(); + Signal() {} }; #endif // CALLABLE_H diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp new file mode 100644 index 0000000000..10446a5ec1 --- /dev/null +++ b/core/variant/callable_bind.cpp @@ -0,0 +1,193 @@ +/*************************************************************************/ +/* callable_bind.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "callable_bind.h" + +////////////////////////////////// + +uint32_t CallableCustomBind::hash() const { + return callable.hash(); +} +String CallableCustomBind::get_as_text() const { + return callable.operator String(); +} + +bool CallableCustomBind::_equal_func(const CallableCustom *p_a, const CallableCustom *p_b) { + const CallableCustomBind *a = (const CallableCustomBind *)p_a; + const CallableCustomBind *b = (const CallableCustomBind *)p_b; + + if (!(a->callable != b->callable)) { + return false; + } + + if (a->binds.size() != b->binds.size()) { + return false; + } + + return true; +} + +bool CallableCustomBind::_less_func(const CallableCustom *p_a, const CallableCustom *p_b) { + const CallableCustomBind *a = (const CallableCustomBind *)p_a; + const CallableCustomBind *b = (const CallableCustomBind *)p_b; + + if (a->callable < b->callable) { + return true; + } else if (b->callable < a->callable) { + return false; + } + + return a->binds.size() < b->binds.size(); +} + +CallableCustom::CompareEqualFunc CallableCustomBind::get_compare_equal_func() const { + return _equal_func; +} +CallableCustom::CompareLessFunc CallableCustomBind::get_compare_less_func() const { + return _less_func; +} +ObjectID CallableCustomBind::get_object() const { + return callable.get_object_id(); +} +const Callable *CallableCustomBind::get_base_comparator() const { + return &callable; +} + +void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount)); + for (int i = 0; i < p_argcount; i++) { + args[i] = (const Variant *)p_arguments[i]; + } + for (int i = 0; i < binds.size(); i++) { + args[i + p_argcount] = (const Variant *)&binds[i]; + } + + callable.call(args, p_argcount + binds.size(), r_return_value, r_call_error); +} + +CallableCustomBind::CallableCustomBind(const Callable &p_callable, const Vector<Variant> &p_binds) { + callable = p_callable; + binds = p_binds; +} + +CallableCustomBind::~CallableCustomBind() { +} + +////////////////////////////////// + +uint32_t CallableCustomUnbind::hash() const { + return callable.hash(); +} +String CallableCustomUnbind::get_as_text() const { + return callable.operator String(); +} + +bool CallableCustomUnbind::_equal_func(const CallableCustom *p_a, const CallableCustom *p_b) { + const CallableCustomUnbind *a = (const CallableCustomUnbind *)p_a; + const CallableCustomUnbind *b = (const CallableCustomUnbind *)p_b; + + if (!(a->callable != b->callable)) { + return false; + } + + if (a->argcount != b->argcount) { + return false; + } + + return true; +} + +bool CallableCustomUnbind::_less_func(const CallableCustom *p_a, const CallableCustom *p_b) { + const CallableCustomUnbind *a = (const CallableCustomUnbind *)p_a; + const CallableCustomUnbind *b = (const CallableCustomUnbind *)p_b; + + if (a->callable < b->callable) { + return true; + } else if (b->callable < a->callable) { + return false; + } + + return a->argcount < b->argcount; +} + +CallableCustom::CompareEqualFunc CallableCustomUnbind::get_compare_equal_func() const { + return _equal_func; +} +CallableCustom::CompareLessFunc CallableCustomUnbind::get_compare_less_func() const { + return _less_func; +} +ObjectID CallableCustomUnbind::get_object() const { + return callable.get_object_id(); +} +const Callable *CallableCustomUnbind::get_base_comparator() const { + return &callable; +} + +void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + if (argcount > p_argcount) { + r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_call_error.argument = 0; + r_call_error.expected = argcount; + return; + } + callable.call(p_arguments, p_argcount - argcount, r_return_value, r_call_error); +} + +CallableCustomUnbind::CallableCustomUnbind(const Callable &p_callable, int p_argcount) { + callable = p_callable; + argcount = p_argcount; +} + +CallableCustomUnbind::~CallableCustomUnbind() { +} + +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1) { + return p_callable.bind((const Variant **)&p_arg1, 1); +} + +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2) { + const Variant *args[2] = { &p_arg1, &p_arg2 }; + return p_callable.bind(args, 2); +} + +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3) { + const Variant *args[3] = { &p_arg1, &p_arg2, &p_arg3 }; + return p_callable.bind(args, 3); +} + +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4) { + const Variant *args[4] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4 }; + return p_callable.bind(args, 4); +} + +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5) { + const Variant *args[5] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5 }; + return p_callable.bind(args, 5); +} diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h new file mode 100644 index 0000000000..feb40d1de9 --- /dev/null +++ b/core/variant/callable_bind.h @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* callable_bind.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 CALLABLE_BIND_H +#define CALLABLE_BIND_H + +#include "core/variant/callable.h" +#include "core/variant/variant.h" + +class CallableCustomBind : public CallableCustom { + Callable callable; + Vector<Variant> binds; + + static bool _equal_func(const CallableCustom *p_a, const CallableCustom *p_b); + static bool _less_func(const CallableCustom *p_a, const CallableCustom *p_b); + +public: + //for every type that inherits, these must always be the same for this type + virtual uint32_t hash() const; + virtual String get_as_text() const; + virtual CompareEqualFunc get_compare_equal_func() const; + virtual CompareLessFunc get_compare_less_func() const; + virtual ObjectID get_object() const; //must always be able to provide an object + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const; + virtual const Callable *get_base_comparator() const; + + CallableCustomBind(const Callable &p_callable, const Vector<Variant> &p_binds); + virtual ~CallableCustomBind(); +}; + +class CallableCustomUnbind : public CallableCustom { + Callable callable; + int argcount; + + static bool _equal_func(const CallableCustom *p_a, const CallableCustom *p_b); + static bool _less_func(const CallableCustom *p_a, const CallableCustom *p_b); + +public: + //for every type that inherits, these must always be the same for this type + virtual uint32_t hash() const; + virtual String get_as_text() const; + virtual CompareEqualFunc get_compare_equal_func() const; + virtual CompareLessFunc get_compare_less_func() const; + virtual ObjectID get_object() const; //must always be able to provide an object + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const; + virtual const Callable *get_base_comparator() const; + + CallableCustomUnbind(const Callable &p_callable, int p_argcount); + virtual ~CallableCustomUnbind(); +}; + +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1); +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2); +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3); +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4); +Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5); + +#endif // CALLABLE_BIND_H diff --git a/core/container_type_validate.h b/core/variant/container_type_validate.h index 3721668033..f13a37cddd 100644 --- a/core/container_type_validate.h +++ b/core/variant/container_type_validate.h @@ -1,15 +1,44 @@ +/*************************************************************************/ +/* container_type_validate.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 CONTAINER_TYPE_VALIDATE_H #define CONTAINER_TYPE_VALIDATE_H -#include "core/script_language.h" -#include "core/variant.h" +#include "core/object/script_language.h" +#include "core/variant/variant.h" struct ContainerTypeValidate { - Variant::Type type = Variant::NIL; StringName class_name; Ref<Script> script; - const char *where = "conatiner"; + const char *where = "container"; _FORCE_INLINE_ bool can_reference(const ContainerTypeValidate &p_type) const { if (type == p_type.type) { @@ -46,7 +75,6 @@ struct ContainerTypeValidate { } _FORCE_INLINE_ bool validate(const Variant &p_variant, const char *p_operation = "use") { - if (type == Variant::NIL) { return true; } diff --git a/core/dictionary.cpp b/core/variant/dictionary.cpp index bc3b792bd5..b2f7c6aa0a 100644 --- a/core/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,20 +30,19 @@ #include "dictionary.h" -#include "core/ordered_hash_map.h" -#include "core/safe_refcount.h" -#include "core/variant.h" +#include "core/templates/ordered_hash_map.h" +#include "core/templates/safe_refcount.h" +#include "core/variant/variant.h" struct DictionaryPrivate { - SafeRefCount refcount; OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> variant_map; }; void Dictionary::get_key_list(List<Variant> *p_keys) const { - - if (_p->variant_map.empty()) + if (_p->variant_map.is_empty()) { return; + } for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { p_keys->push_back(E.key()); @@ -51,7 +50,6 @@ void Dictionary::get_key_list(List<Variant> *p_keys) const { } Variant Dictionary::get_key_at_index(int p_index) const { - int index = 0; for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { if (index == p_index) { @@ -64,7 +62,6 @@ Variant Dictionary::get_key_at_index(int p_index) const { } Variant Dictionary::get_value_at_index(int p_index) const { - int index = 0; for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { if (index == p_index) { @@ -77,38 +74,37 @@ Variant Dictionary::get_value_at_index(int p_index) const { } Variant &Dictionary::operator[](const Variant &p_key) { - return _p->variant_map[p_key]; } const Variant &Dictionary::operator[](const Variant &p_key) const { - return _p->variant_map[p_key]; } -const Variant *Dictionary::getptr(const Variant &p_key) const { +const Variant *Dictionary::getptr(const Variant &p_key) const { OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key); - if (!E) + if (!E) { return nullptr; + } return &E.get(); } Variant *Dictionary::getptr(const Variant &p_key) { - OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.find(p_key); - if (!E) + if (!E) { return nullptr; + } return &E.get(); } Variant Dictionary::get_valid(const Variant &p_key) const { - OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key); - if (!E) + if (!E) { return Variant(); + } return E.get(); } @@ -122,16 +118,14 @@ Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const { } int Dictionary::size() const { - return _p->variant_map.size(); } -bool Dictionary::empty() const { +bool Dictionary::is_empty() const { return !_p->variant_map.size(); } bool Dictionary::has(const Variant &p_key) const { - return _p->variant_map.has(p_key); } @@ -145,51 +139,47 @@ bool Dictionary::has_all(const Array &p_keys) const { } bool Dictionary::erase(const Variant &p_key) { - return _p->variant_map.erase(p_key); } bool Dictionary::operator==(const Dictionary &p_dictionary) const { - return _p == p_dictionary._p; } bool Dictionary::operator!=(const Dictionary &p_dictionary) const { - return _p != p_dictionary._p; } void Dictionary::_ref(const Dictionary &p_from) const { - //make a copy first (thread safe) - if (!p_from._p->refcount.ref()) + if (!p_from._p->refcount.ref()) { return; // couldn't copy + } //if this is the same, unreference the other one if (p_from._p == _p) { _p->refcount.unref(); return; } - if (_p) + if (_p) { _unref(); + } _p = p_from._p; } void Dictionary::clear() { - _p->variant_map.clear(); } void Dictionary::_unref() const { - ERR_FAIL_COND(!_p); if (_p->refcount.unref()) { memdelete(_p); } _p = nullptr; } -uint32_t Dictionary::hash() const { +uint32_t Dictionary::hash() const { uint32_t h = hash_djb2_one_32(Variant::DICTIONARY); for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { @@ -201,10 +191,10 @@ uint32_t Dictionary::hash() const { } Array Dictionary::keys() const { - Array varr; - if (_p->variant_map.empty()) + if (_p->variant_map.is_empty()) { return varr; + } varr.resize(size()); @@ -218,10 +208,10 @@ Array Dictionary::keys() const { } Array Dictionary::values() const { - Array varr; - if (_p->variant_map.empty()) + if (_p->variant_map.is_empty()) { return varr; + } varr.resize(size()); @@ -235,22 +225,22 @@ Array Dictionary::values() const { } const Variant *Dictionary::next(const Variant *p_key) const { - if (p_key == nullptr) { // caller wants to get the first element - if (_p->variant_map.front()) + if (_p->variant_map.front()) { return &_p->variant_map.front().key(); + } return nullptr; } OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.find(*p_key); - if (E && E.next()) + if (E && E.next()) { return &E.next().key(); + } return nullptr; } Dictionary Dictionary::duplicate(bool p_deep) const { - Dictionary n; for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { @@ -261,7 +251,6 @@ Dictionary Dictionary::duplicate(bool p_deep) const { } void Dictionary::operator=(const Dictionary &p_dictionary) { - _ref(p_dictionary); } @@ -275,11 +264,10 @@ Dictionary::Dictionary(const Dictionary &p_from) { } Dictionary::Dictionary() { - _p = memnew(DictionaryPrivate); _p->refcount.init(); } -Dictionary::~Dictionary() { +Dictionary::~Dictionary() { _unref(); } diff --git a/core/dictionary.h b/core/variant/dictionary.h index c6cbacc144..4067ff9fd9 100644 --- a/core/dictionary.h +++ b/core/variant/dictionary.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,16 +31,15 @@ #ifndef DICTIONARY_H #define DICTIONARY_H -#include "core/array.h" -#include "core/list.h" -#include "core/ustring.h" +#include "core/string/ustring.h" +#include "core/templates/list.h" +#include "core/variant/array.h" class Variant; struct DictionaryPrivate; class Dictionary { - mutable DictionaryPrivate *_p; void _ref(const Dictionary &p_from) const; @@ -61,7 +60,7 @@ public: Variant get(const Variant &p_key, const Variant &p_default) const; int size() const; - bool empty() const; + bool is_empty() const; void clear(); bool has(const Variant &p_key) const; diff --git a/core/method_ptrcall.h b/core/variant/method_ptrcall.h index 7ae0a788bd..c294592b63 100644 --- a/core/method_ptrcall.h +++ b/core/variant/method_ptrcall.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,15 +32,12 @@ #define METHOD_PTRCALL_H #include "core/math/transform_2d.h" -#include "core/object_id.h" +#include "core/object/object_id.h" #include "core/typedefs.h" -#include "core/variant.h" - -#ifdef PTRCALL_ENABLED +#include "core/variant/variant.h" template <class T> -struct PtrToArg { -}; +struct PtrToArg {}; #define MAKE_PTRARG(m_type) \ template <> \ @@ -103,6 +100,7 @@ struct PtrToArg { } MAKE_PTRARG(bool); +// Integer types. MAKE_PTRARGCONV(uint8_t, int64_t); MAKE_PTRARGCONV(int8_t, int64_t); MAKE_PTRARGCONV(uint16_t, int64_t); @@ -111,15 +109,16 @@ MAKE_PTRARGCONV(uint32_t, int64_t); MAKE_PTRARGCONV(int32_t, int64_t); MAKE_PTRARG(int64_t); MAKE_PTRARG(uint64_t); +// Float types MAKE_PTRARGCONV(float, double); MAKE_PTRARG(double); MAKE_PTRARG(String); MAKE_PTRARG(Vector2); -MAKE_PTRARG(Rect2); -MAKE_PTRARG_BY_REFERENCE(Vector3); MAKE_PTRARG(Vector2i); +MAKE_PTRARG(Rect2); MAKE_PTRARG(Rect2i); +MAKE_PTRARG_BY_REFERENCE(Vector3); MAKE_PTRARG_BY_REFERENCE(Vector3i); MAKE_PTRARG(Transform2D); MAKE_PTRARG_BY_REFERENCE(Plane); @@ -131,9 +130,10 @@ MAKE_PTRARG_BY_REFERENCE(Color); MAKE_PTRARG(StringName); MAKE_PTRARG(NodePath); MAKE_PTRARG(RID); -MAKE_PTRARG(Dictionary); +// Object doesn't need this. MAKE_PTRARG(Callable); MAKE_PTRARG(Signal); +MAKE_PTRARG(Dictionary); MAKE_PTRARG(Array); MAKE_PTRARG(PackedByteArray); MAKE_PTRARG(PackedInt32Array); @@ -146,52 +146,44 @@ MAKE_PTRARG(PackedVector3Array); MAKE_PTRARG(PackedColorArray); MAKE_PTRARG_BY_REFERENCE(Variant); -//this is for Object +// This is for Object. template <class T> struct PtrToArg<T *> { - _FORCE_INLINE_ static T *convert(const void *p_ptr) { - return const_cast<T *>(reinterpret_cast<const T *>(p_ptr)); } _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { - *((T **)p_ptr) = p_var; } }; template <class T> struct PtrToArg<const T *> { - _FORCE_INLINE_ static const T *convert(const void *p_ptr) { - return reinterpret_cast<const T *>(p_ptr); } _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { - *((T **)p_ptr) = p_var; } }; -//this is for ObjectID +// This is for ObjectID. template <> struct PtrToArg<ObjectID> { _FORCE_INLINE_ static const ObjectID convert(const void *p_ptr) { - return ObjectID(*reinterpret_cast<const uint64_t *>(p_ptr)); } _FORCE_INLINE_ static void encode(const ObjectID &p_val, void *p_ptr) { - *((uint64_t *)p_ptr) = p_val; } }; -//this is for the special cases used by Variant +// This is for the special cases used by Variant. #define MAKE_VECARG(m_type) \ template <> \ @@ -282,18 +274,11 @@ struct PtrToArg<ObjectID> { return ret; \ } \ } -/* -MAKE_VECARG(String); -MAKE_VECARG(uint8_t); -MAKE_VECARG(int); -MAKE_VECARG(float); -MAKE_VECARG(Vector2); -MAKE_VECARG(Vector3); -MAKE_VECARG(Color); -*/ + MAKE_VECARG_ALT(String, StringName); -//for stuff that gets converted to Array vectors +// For stuff that gets converted to Array vectors. + #define MAKE_VECARR(m_type) \ template <> \ struct PtrToArg<Vector<m_type>> { \ @@ -437,6 +422,7 @@ struct PtrToArg<Vector<Face3>> { } } }; + template <> struct PtrToArg<const Vector<Face3> &> { _FORCE_INLINE_ static Vector<Face3> convert(const void *p_ptr) { @@ -458,4 +444,3 @@ struct PtrToArg<const Vector<Face3> &> { }; #endif // METHOD_PTRCALL_H -#endif diff --git a/core/type_info.h b/core/variant/type_info.h index 816d0d9381..f61ff29b8f 100644 --- a/core/type_info.h +++ b/core/variant/type_info.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,11 +31,10 @@ #ifndef TYPE_INFO_H #define TYPE_INFO_H -#ifdef DEBUG_METHODS_ENABLED +#include "core/typedefs.h" template <bool C, typename T = void> struct EnableIf { - typedef T type; }; @@ -45,19 +44,16 @@ struct EnableIf<false, T> { template <typename, typename> struct TypesAreSame { - static bool const value = false; }; template <typename A> struct TypesAreSame<A, A> { - static bool const value = true; }; template <typename B, typename D> struct TypeInherits { - static D *get_d(); static char (&test(B *))[1]; @@ -136,7 +132,8 @@ MAKE_TYPE_INFO_WITH_META(uint32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_ MAKE_TYPE_INFO_WITH_META(int32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT32) MAKE_TYPE_INFO_WITH_META(uint64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_UINT64) MAKE_TYPE_INFO_WITH_META(int64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT64) -MAKE_TYPE_INFO(wchar_t, Variant::INT) +MAKE_TYPE_INFO(char16_t, Variant::INT) +MAKE_TYPE_INFO(char32_t, Variant::INT) MAKE_TYPE_INFO_WITH_META(float, Variant::FLOAT, GodotTypeInfo::METADATA_REAL_IS_FLOAT) MAKE_TYPE_INFO_WITH_META(double, Variant::FLOAT, GodotTypeInfo::METADATA_REAL_IS_DOUBLE) @@ -156,7 +153,7 @@ MAKE_TYPE_INFO(Transform, Variant::TRANSFORM) MAKE_TYPE_INFO(Color, Variant::COLOR) MAKE_TYPE_INFO(StringName, Variant::STRING_NAME) MAKE_TYPE_INFO(NodePath, Variant::NODE_PATH) -MAKE_TYPE_INFO(RID, Variant::_RID) +MAKE_TYPE_INFO(RID, Variant::RID) MAKE_TYPE_INFO(Callable, Variant::CALLABLE) MAKE_TYPE_INFO(Signal, Variant::SIGNAL) MAKE_TYPE_INFO(Dictionary, Variant::DICTIONARY) @@ -262,18 +259,12 @@ struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>: template <typename T> inline StringName __constant_get_enum_name(T param, const String &p_constant) { - if (GetTypeInfo<T>::VARIANT_TYPE == Variant::NIL) + if (GetTypeInfo<T>::VARIANT_TYPE == Variant::NIL) { ERR_PRINT("Missing VARIANT_ENUM_CAST for constant's enum: " + p_constant); + } return GetTypeInfo<T>::get_class_info().class_name; } #define CLASS_INFO(m_type) (GetTypeInfo<m_type *>::get_class_info()) -#else - -#define MAKE_ENUM_TYPE_INFO(m_enum) -#define CLASS_INFO(m_type) - -#endif // DEBUG_METHODS_ENABLED - #endif // TYPE_INFO_H diff --git a/core/typed_array.h b/core/variant/typed_array.h index 5e95e81ea3..e0309aa3fe 100644 --- a/core/typed_array.h +++ b/core/variant/typed_array.h @@ -1,9 +1,39 @@ +/*************************************************************************/ +/* typed_array.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 TYPED_ARRAY_H #define TYPED_ARRAY_H -#include "core/array.h" -#include "core/method_ptrcall.h" -#include "core/variant.h" +#include "core/variant/array.h" +#include "core/variant/method_ptrcall.h" +#include "core/variant/variant.h" template <class T> class TypedArray : public Array { @@ -75,7 +105,7 @@ MAKE_TYPED_ARRAY(Transform, Variant::TRANSFORM) MAKE_TYPED_ARRAY(Color, Variant::COLOR) MAKE_TYPED_ARRAY(StringName, Variant::STRING_NAME) MAKE_TYPED_ARRAY(NodePath, Variant::NODE_PATH) -MAKE_TYPED_ARRAY(RID, Variant::_RID) +MAKE_TYPED_ARRAY(RID, Variant::RID) MAKE_TYPED_ARRAY(Callable, Variant::CALLABLE) MAKE_TYPED_ARRAY(Signal, Variant::SIGNAL) MAKE_TYPED_ARRAY(Dictionary, Variant::DICTIONARY) @@ -90,33 +120,24 @@ MAKE_TYPED_ARRAY(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY) MAKE_TYPED_ARRAY(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY) MAKE_TYPED_ARRAY(Vector<Color>, Variant::PACKED_COLOR_ARRAY) -#ifdef PTRCALL_ENABLED - template <class T> struct PtrToArg<TypedArray<T>> { - _FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) { - return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr)); } _FORCE_INLINE_ static void encode(TypedArray<T> p_val, void *p_ptr) { - *(Array *)p_ptr = p_val; } }; template <class T> struct PtrToArg<const TypedArray<T> &> { - _FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) { - return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr)); } }; -#endif // PTRCALL_ENABLED - #ifdef DEBUG_METHODS_ENABLED template <class T> @@ -182,7 +203,7 @@ MAKE_TYPED_ARRAY_INFO(Transform, Variant::TRANSFORM) MAKE_TYPED_ARRAY_INFO(Color, Variant::COLOR) MAKE_TYPED_ARRAY_INFO(StringName, Variant::STRING_NAME) MAKE_TYPED_ARRAY_INFO(NodePath, Variant::NODE_PATH) -MAKE_TYPED_ARRAY_INFO(RID, Variant::_RID) +MAKE_TYPED_ARRAY_INFO(RID, Variant::RID) MAKE_TYPED_ARRAY_INFO(Callable, Variant::CALLABLE) MAKE_TYPED_ARRAY_INFO(Signal, Variant::SIGNAL) MAKE_TYPED_ARRAY_INFO(Dictionary, Variant::DICTIONARY) diff --git a/core/variant.cpp b/core/variant/variant.cpp index a91876dd98..7824776fdb 100644 --- a/core/variant.cpp +++ b/core/variant/variant.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,180 +33,144 @@ #include "core/core_string_names.h" #include "core/debugger/engine_debugger.h" #include "core/io/marshalls.h" +#include "core/io/resource.h" #include "core/math/math_funcs.h" -#include "core/print_string.h" -#include "core/resource.h" -#include "core/variant_parser.h" +#include "core/string/print_string.h" +#include "core/variant/variant_parser.h" #include "scene/gui/control.h" #include "scene/main/node.h" String Variant::get_type_name(Variant::Type p_type) { - switch (p_type) { case NIL: { - return "Nil"; } break; // atomic types case BOOL: { - return "bool"; } break; case INT: { - return "int"; } break; case FLOAT: { - return "float"; } break; case STRING: { - return "String"; } break; // math types case VECTOR2: { - return "Vector2"; } break; case VECTOR2I: { - return "Vector2i"; } break; case RECT2: { - return "Rect2"; } break; case RECT2I: { - return "Rect2i"; } break; case TRANSFORM2D: { - return "Transform2D"; } break; case VECTOR3: { - return "Vector3"; } break; case VECTOR3I: { - return "Vector3i"; } break; case PLANE: { - return "Plane"; } break; case AABB: { - return "AABB"; } break; case QUAT: { - return "Quat"; } break; case BASIS: { - return "Basis"; } break; case TRANSFORM: { - return "Transform"; } break; // misc types case COLOR: { - return "Color"; } break; - case _RID: { - + case RID: { return "RID"; } break; case OBJECT: { - return "Object"; } break; case CALLABLE: { - return "Callable"; } break; case SIGNAL: { - return "Signal"; } break; case STRING_NAME: { - return "StringName"; } break; case NODE_PATH: { - return "NodePath"; } break; case DICTIONARY: { - return "Dictionary"; } break; case ARRAY: { - return "Array"; } break; // arrays case PACKED_BYTE_ARRAY: { - return "PackedByteArray"; } break; case PACKED_INT32_ARRAY: { - return "PackedInt32Array"; } break; case PACKED_INT64_ARRAY: { - return "PackedInt64Array"; } break; case PACKED_FLOAT32_ARRAY: { - return "PackedFloat32Array"; } break; case PACKED_FLOAT64_ARRAY: { - return "PackedFloat64Array"; } break; case PACKED_STRING_ARRAY: { - return "PackedStringArray"; } break; case PACKED_VECTOR2_ARRAY: { - return "PackedVector2Array"; } break; case PACKED_VECTOR3_ARRAY: { - return "PackedVector3Array"; } break; case PACKED_COLOR_ARRAY: { - return "PackedColorArray"; } break; @@ -218,22 +182,22 @@ String Variant::get_type_name(Variant::Type p_type) { } bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { - - if (p_type_from == p_type_to) + if (p_type_from == p_type_to) { return true; - if (p_type_to == NIL && p_type_from != NIL) //nil can convert to anything + } + if (p_type_to == NIL && p_type_from != NIL) { //nil can convert to anything return true; + } if (p_type_from == NIL) { return (p_type_to == OBJECT); - }; + } const Type *valid_types = nullptr; const Type *invalid_types = nullptr; switch (p_type_to) { case BOOL: { - static const Type valid[] = { INT, FLOAT, @@ -244,7 +208,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case INT: { - static const Type valid[] = { BOOL, FLOAT, @@ -256,7 +219,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case FLOAT: { - static const Type valid[] = { BOOL, INT, @@ -268,7 +230,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case STRING: { - static const Type invalid[] = { OBJECT, NIL @@ -277,7 +238,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { invalid_types = invalid; } break; case VECTOR2: { - static const Type valid[] = { VECTOR2I, NIL, @@ -287,7 +247,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case VECTOR2I: { - static const Type valid[] = { VECTOR2, NIL, @@ -297,7 +256,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case RECT2: { - static const Type valid[] = { RECT2I, NIL, @@ -307,7 +265,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case RECT2I: { - static const Type valid[] = { RECT2, NIL, @@ -317,7 +274,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case TRANSFORM2D: { - static const Type valid[] = { TRANSFORM, NIL @@ -326,7 +282,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case VECTOR3: { - static const Type valid[] = { VECTOR3I, NIL, @@ -336,7 +291,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case VECTOR3I: { - static const Type valid[] = { VECTOR3, NIL, @@ -347,7 +301,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case QUAT: { - static const Type valid[] = { BASIS, NIL @@ -357,7 +310,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case BASIS: { - static const Type valid[] = { QUAT, VECTOR3, @@ -368,7 +320,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case TRANSFORM: { - static const Type valid[] = { TRANSFORM2D, QUAT, @@ -381,7 +332,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case COLOR: { - static const Type valid[] = { STRING, INT, @@ -392,8 +342,7 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; - case _RID: { - + case RID: { static const Type valid[] = { OBJECT, NIL @@ -402,7 +351,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case OBJECT: { - static const Type valid[] = { NIL }; @@ -410,7 +358,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case STRING_NAME: { - static const Type valid[] = { STRING, NIL @@ -419,7 +366,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case NODE_PATH: { - static const Type valid[] = { STRING, NIL @@ -428,7 +374,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case ARRAY: { - static const Type valid[] = { PACKED_BYTE_ARRAY, PACKED_INT32_ARRAY, @@ -446,7 +391,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; // arrays case PACKED_BYTE_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -455,7 +399,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case PACKED_INT32_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -463,7 +406,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case PACKED_INT64_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -471,7 +413,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case PACKED_FLOAT32_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -480,7 +421,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case PACKED_FLOAT64_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -489,7 +429,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case PACKED_STRING_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -497,7 +436,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; case PACKED_VECTOR2_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -506,7 +444,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case PACKED_VECTOR3_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -515,7 +452,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } break; case PACKED_COLOR_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -529,22 +465,20 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } if (valid_types) { - int i = 0; while (valid_types[i] != NIL) { - - if (p_type_from == valid_types[i]) + if (p_type_from == valid_types[i]) { return true; + } i++; } } else if (invalid_types) { - int i = 0; while (invalid_types[i] != NIL) { - - if (p_type_from == invalid_types[i]) + if (p_type_from == invalid_types[i]) { return false; + } i++; } @@ -555,21 +489,21 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { } bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type_to) { - - if (p_type_from == p_type_to) + if (p_type_from == p_type_to) { return true; - if (p_type_to == NIL && p_type_from != NIL) //nil can convert to anything + } + if (p_type_to == NIL && p_type_from != NIL) { //nil can convert to anything return true; + } if (p_type_from == NIL) { return (p_type_to == OBJECT); - }; + } const Type *valid_types = nullptr; switch (p_type_to) { case BOOL: { - static const Type valid[] = { INT, FLOAT, @@ -580,7 +514,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case INT: { - static const Type valid[] = { BOOL, FLOAT, @@ -592,7 +525,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case FLOAT: { - static const Type valid[] = { BOOL, INT, @@ -604,7 +536,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case STRING: { - static const Type valid[] = { NODE_PATH, STRING_NAME, @@ -614,7 +545,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case VECTOR2: { - static const Type valid[] = { VECTOR2I, NIL, @@ -624,7 +554,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case VECTOR2I: { - static const Type valid[] = { VECTOR2, NIL, @@ -634,7 +563,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case RECT2: { - static const Type valid[] = { RECT2I, NIL, @@ -644,7 +572,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case RECT2I: { - static const Type valid[] = { RECT2, NIL, @@ -654,7 +581,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case TRANSFORM2D: { - static const Type valid[] = { TRANSFORM, NIL @@ -663,7 +589,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case VECTOR3: { - static const Type valid[] = { VECTOR3I, NIL, @@ -673,7 +598,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case VECTOR3I: { - static const Type valid[] = { VECTOR3, NIL, @@ -684,7 +608,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case QUAT: { - static const Type valid[] = { BASIS, NIL @@ -694,7 +617,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case BASIS: { - static const Type valid[] = { QUAT, VECTOR3, @@ -705,7 +627,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case TRANSFORM: { - static const Type valid[] = { TRANSFORM2D, QUAT, @@ -718,7 +639,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case COLOR: { - static const Type valid[] = { STRING, INT, @@ -729,8 +649,7 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; - case _RID: { - + case RID: { static const Type valid[] = { OBJECT, NIL @@ -739,7 +658,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case OBJECT: { - static const Type valid[] = { NIL }; @@ -747,7 +665,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case STRING_NAME: { - static const Type valid[] = { STRING, NIL @@ -756,7 +673,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case NODE_PATH: { - static const Type valid[] = { STRING, NIL @@ -765,7 +681,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case ARRAY: { - static const Type valid[] = { PACKED_BYTE_ARRAY, PACKED_INT32_ARRAY, @@ -783,7 +698,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; // arrays case PACKED_BYTE_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -792,7 +706,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case PACKED_INT32_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -800,7 +713,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case PACKED_INT64_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -808,7 +720,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case PACKED_FLOAT32_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -817,7 +728,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case PACKED_FLOAT64_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -826,7 +736,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case PACKED_STRING_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -834,7 +743,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; case PACKED_VECTOR2_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -843,7 +751,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case PACKED_VECTOR3_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -852,7 +759,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } break; case PACKED_COLOR_ARRAY: { - static const Type valid[] = { ARRAY, NIL @@ -866,12 +772,11 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } if (valid_types) { - int i = 0; while (valid_types[i] != NIL) { - - if (p_type_from == valid_types[i]) + if (p_type_from == valid_types[i]) { return true; + } i++; } } @@ -880,9 +785,9 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type } bool Variant::operator==(const Variant &p_variant) const { - - if (type != p_variant.type) //evaluation of operator== needs to be more strict + if (type != p_variant.type) { //evaluation of operator== needs to be more strict return false; + } bool v; Variant r; evaluate(OP_EQUAL, *this, p_variant, r, v); @@ -890,9 +795,9 @@ bool Variant::operator==(const Variant &p_variant) const { } bool Variant::operator!=(const Variant &p_variant) const { - - if (type != p_variant.type) //evaluation of operator== needs to be more strict + if (type != p_variant.type) { //evaluation of operator== needs to be more strict return true; + } bool v; Variant r; evaluate(OP_NOT_EQUAL, *this, p_variant, r, v); @@ -900,8 +805,9 @@ bool Variant::operator!=(const Variant &p_variant) const { } bool Variant::operator<(const Variant &p_variant) const { - if (type != p_variant.type) //if types differ, then order by type first + if (type != p_variant.type) { //if types differ, then order by type first return type < p_variant.type; + } bool v; Variant r; evaluate(OP_LESS, *this, p_variant, r, v); @@ -909,186 +815,145 @@ bool Variant::operator<(const Variant &p_variant) const { } bool Variant::is_zero() const { - switch (type) { case NIL: { - return true; } break; // atomic types case BOOL: { - return !(_data._bool); } break; case INT: { - return _data._int == 0; } break; case FLOAT: { - return _data._float == 0; } break; case STRING: { - return *reinterpret_cast<const String *>(_data._mem) == String(); } break; // math types case VECTOR2: { - return *reinterpret_cast<const Vector2 *>(_data._mem) == Vector2(); } break; case VECTOR2I: { - return *reinterpret_cast<const Vector2i *>(_data._mem) == Vector2i(); } break; case RECT2: { - return *reinterpret_cast<const Rect2 *>(_data._mem) == Rect2(); } break; case RECT2I: { - return *reinterpret_cast<const Rect2i *>(_data._mem) == Rect2i(); } break; case TRANSFORM2D: { - return *_data._transform2d == Transform2D(); } break; case VECTOR3: { - return *reinterpret_cast<const Vector3 *>(_data._mem) == Vector3(); } break; case VECTOR3I: { - return *reinterpret_cast<const Vector3i *>(_data._mem) == Vector3i(); } break; case PLANE: { - return *reinterpret_cast<const Plane *>(_data._mem) == Plane(); } break; - /* - case QUAT: { - - - } break;*/ case AABB: { - return *_data._aabb == ::AABB(); } break; case QUAT: { - return *reinterpret_cast<const Quat *>(_data._mem) == Quat(); } break; case BASIS: { - return *_data._basis == Basis(); } break; case TRANSFORM: { - return *_data._transform == Transform(); } break; // misc types case COLOR: { - return *reinterpret_cast<const Color *>(_data._mem) == Color(); } break; - case _RID: { - - return *reinterpret_cast<const RID *>(_data._mem) == RID(); + case RID: { + return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID(); } break; case OBJECT: { - return _get_obj().obj == nullptr; } break; case CALLABLE: { - return reinterpret_cast<const Callable *>(_data._mem)->is_null(); } break; case SIGNAL: { - return reinterpret_cast<const Signal *>(_data._mem)->is_null(); } break; case STRING_NAME: { - return *reinterpret_cast<const StringName *>(_data._mem) != StringName(); } break; case NODE_PATH: { - return reinterpret_cast<const NodePath *>(_data._mem)->is_empty(); } break; case DICTIONARY: { - - return reinterpret_cast<const Dictionary *>(_data._mem)->empty(); + return reinterpret_cast<const Dictionary *>(_data._mem)->is_empty(); } break; case ARRAY: { - - return reinterpret_cast<const Array *>(_data._mem)->empty(); + return reinterpret_cast<const Array *>(_data._mem)->is_empty(); } break; // arrays case PACKED_BYTE_ARRAY: { - return PackedArrayRef<uint8_t>::get_array(_data.packed_array).size() == 0; } break; case PACKED_INT32_ARRAY: { - return PackedArrayRef<int32_t>::get_array(_data.packed_array).size() == 0; } break; case PACKED_INT64_ARRAY: { - return PackedArrayRef<int64_t>::get_array(_data.packed_array).size() == 0; } break; case PACKED_FLOAT32_ARRAY: { - return PackedArrayRef<float>::get_array(_data.packed_array).size() == 0; } break; case PACKED_FLOAT64_ARRAY: { - return PackedArrayRef<double>::get_array(_data.packed_array).size() == 0; } break; case PACKED_STRING_ARRAY: { - return PackedArrayRef<String>::get_array(_data.packed_array).size() == 0; } break; case PACKED_VECTOR2_ARRAY: { - return PackedArrayRef<Vector2>::get_array(_data.packed_array).size() == 0; } break; case PACKED_VECTOR3_ARRAY: { - return PackedArrayRef<Vector3>::get_array(_data.packed_array).size() == 0; } break; case PACKED_COLOR_ARRAY: { - return PackedArrayRef<Color>::get_array(_data.packed_array).size() == 0; } break; @@ -1100,65 +965,52 @@ bool Variant::is_zero() const { } bool Variant::is_one() const { - switch (type) { case NIL: { - return true; } break; // atomic types case BOOL: { - return _data._bool; } break; case INT: { - return _data._int == 1; } break; case FLOAT: { - return _data._float == 1; } break; case VECTOR2: { - return *reinterpret_cast<const Vector2 *>(_data._mem) == Vector2(1, 1); } break; case VECTOR2I: { - return *reinterpret_cast<const Vector2i *>(_data._mem) == Vector2i(1, 1); } break; case RECT2: { - return *reinterpret_cast<const Rect2 *>(_data._mem) == Rect2(1, 1, 1, 1); } break; case RECT2I: { - return *reinterpret_cast<const Rect2i *>(_data._mem) == Rect2i(1, 1, 1, 1); } break; case VECTOR3: { - return *reinterpret_cast<const Vector3 *>(_data._mem) == Vector3(1, 1, 1); } break; case VECTOR3I: { - return *reinterpret_cast<const Vector3i *>(_data._mem) == Vector3i(1, 1, 1); } break; case PLANE: { - return *reinterpret_cast<const Plane *>(_data._mem) == Plane(1, 1, 1, 1); } break; case COLOR: { - return *reinterpret_cast<const Color *>(_data._mem) == Color(1, 1, 1, 1); } break; @@ -1180,7 +1032,6 @@ bool Variant::is_null() const { } void Variant::reference(const Variant &p_variant) { - switch (type) { case NIL: case BOOL: @@ -1195,93 +1046,73 @@ void Variant::reference(const Variant &p_variant) { switch (p_variant.type) { case NIL: { - // none } break; // atomic types case BOOL: { - _data._bool = p_variant._data._bool; } break; case INT: { - _data._int = p_variant._data._int; } break; case FLOAT: { - _data._float = p_variant._data._float; } break; case STRING: { - memnew_placement(_data._mem, String(*reinterpret_cast<const String *>(p_variant._data._mem))); } break; // math types case VECTOR2: { - memnew_placement(_data._mem, Vector2(*reinterpret_cast<const Vector2 *>(p_variant._data._mem))); } break; case VECTOR2I: { - memnew_placement(_data._mem, Vector2i(*reinterpret_cast<const Vector2i *>(p_variant._data._mem))); } break; case RECT2: { - memnew_placement(_data._mem, Rect2(*reinterpret_cast<const Rect2 *>(p_variant._data._mem))); } break; case RECT2I: { - memnew_placement(_data._mem, Rect2i(*reinterpret_cast<const Rect2i *>(p_variant._data._mem))); } break; case TRANSFORM2D: { - _data._transform2d = memnew(Transform2D(*p_variant._data._transform2d)); } break; case VECTOR3: { - memnew_placement(_data._mem, Vector3(*reinterpret_cast<const Vector3 *>(p_variant._data._mem))); } break; case VECTOR3I: { - memnew_placement(_data._mem, Vector3i(*reinterpret_cast<const Vector3i *>(p_variant._data._mem))); } break; case PLANE: { - memnew_placement(_data._mem, Plane(*reinterpret_cast<const Plane *>(p_variant._data._mem))); } break; case AABB: { - _data._aabb = memnew(::AABB(*p_variant._data._aabb)); } break; case QUAT: { - memnew_placement(_data._mem, Quat(*reinterpret_cast<const Quat *>(p_variant._data._mem))); } break; case BASIS: { - _data._basis = memnew(Basis(*p_variant._data._basis)); } break; case TRANSFORM: { - _data._transform = memnew(Transform(*p_variant._data._transform)); } break; // misc types case COLOR: { - memnew_placement(_data._mem, Color(*reinterpret_cast<const Color *>(p_variant._data._mem))); } break; - case _RID: { - - memnew_placement(_data._mem, RID(*reinterpret_cast<const RID *>(p_variant._data._mem))); + case RID: { + memnew_placement(_data._mem, ::RID(*reinterpret_cast<const ::RID *>(p_variant._data._mem))); } break; case OBJECT: { - memnew_placement(_data._mem, ObjData); if (p_variant._get_obj().obj && p_variant._get_obj().id.is_reference()) { @@ -1298,37 +1129,30 @@ void Variant::reference(const Variant &p_variant) { } break; case CALLABLE: { - memnew_placement(_data._mem, Callable(*reinterpret_cast<const Callable *>(p_variant._data._mem))); } break; case SIGNAL: { - memnew_placement(_data._mem, Signal(*reinterpret_cast<const Signal *>(p_variant._data._mem))); } break; case STRING_NAME: { - memnew_placement(_data._mem, StringName(*reinterpret_cast<const StringName *>(p_variant._data._mem))); } break; case NODE_PATH: { - memnew_placement(_data._mem, NodePath(*reinterpret_cast<const NodePath *>(p_variant._data._mem))); } break; case DICTIONARY: { - memnew_placement(_data._mem, Dictionary(*reinterpret_cast<const Dictionary *>(p_variant._data._mem))); } break; case ARRAY: { - memnew_placement(_data._mem, Array(*reinterpret_cast<const Array *>(p_variant._data._mem))); } break; // arrays case PACKED_BYTE_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<uint8_t> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<uint8_t>::create(); @@ -1336,7 +1160,6 @@ void Variant::reference(const Variant &p_variant) { } break; case PACKED_INT32_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<int32_t> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<int32_t>::create(); @@ -1344,7 +1167,6 @@ void Variant::reference(const Variant &p_variant) { } break; case PACKED_INT64_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<int64_t> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<int64_t>::create(); @@ -1352,7 +1174,6 @@ void Variant::reference(const Variant &p_variant) { } break; case PACKED_FLOAT32_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<float> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<float>::create(); @@ -1360,7 +1181,6 @@ void Variant::reference(const Variant &p_variant) { } break; case PACKED_FLOAT64_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<double> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<double>::create(); @@ -1368,7 +1188,6 @@ void Variant::reference(const Variant &p_variant) { } break; case PACKED_STRING_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<String> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<String>::create(); @@ -1376,7 +1195,6 @@ void Variant::reference(const Variant &p_variant) { } break; case PACKED_VECTOR2_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<Vector2> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<Vector2>::create(); @@ -1384,7 +1202,6 @@ void Variant::reference(const Variant &p_variant) { } break; case PACKED_VECTOR3_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<Vector3> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<Vector3>::create(); @@ -1392,7 +1209,6 @@ void Variant::reference(const Variant &p_variant) { } break; case PACKED_COLOR_ARRAY: { - _data.packed_array = static_cast<PackedArrayRef<Color> *>(p_variant._data.packed_array)->reference(); if (!_data.packed_array) { _data.packed_array = PackedArrayRef<Color>::create(); @@ -1406,28 +1222,53 @@ void Variant::reference(const Variant &p_variant) { void Variant::zero() { switch (type) { - case NIL: break; - case BOOL: this->_data._bool = false; break; - case INT: this->_data._int = 0; break; - case FLOAT: this->_data._float = 0; break; - case VECTOR2: *reinterpret_cast<Vector2 *>(this->_data._mem) = Vector2(); break; - case VECTOR2I: *reinterpret_cast<Vector2i *>(this->_data._mem) = Vector2i(); break; - case RECT2: *reinterpret_cast<Rect2 *>(this->_data._mem) = Rect2(); break; - case RECT2I: *reinterpret_cast<Rect2i *>(this->_data._mem) = Rect2i(); break; - case VECTOR3: *reinterpret_cast<Vector3 *>(this->_data._mem) = Vector3(); break; - case VECTOR3I: *reinterpret_cast<Vector3i *>(this->_data._mem) = Vector3i(); break; - case PLANE: *reinterpret_cast<Plane *>(this->_data._mem) = Plane(); break; - case QUAT: *reinterpret_cast<Quat *>(this->_data._mem) = Quat(); break; - case COLOR: *reinterpret_cast<Color *>(this->_data._mem) = Color(); break; - default: this->clear(); break; + case NIL: + break; + case BOOL: + this->_data._bool = false; + break; + case INT: + this->_data._int = 0; + break; + case FLOAT: + this->_data._float = 0; + break; + case VECTOR2: + *reinterpret_cast<Vector2 *>(this->_data._mem) = Vector2(); + break; + case VECTOR2I: + *reinterpret_cast<Vector2i *>(this->_data._mem) = Vector2i(); + break; + case RECT2: + *reinterpret_cast<Rect2 *>(this->_data._mem) = Rect2(); + break; + case RECT2I: + *reinterpret_cast<Rect2i *>(this->_data._mem) = Rect2i(); + break; + case VECTOR3: + *reinterpret_cast<Vector3 *>(this->_data._mem) = Vector3(); + break; + case VECTOR3I: + *reinterpret_cast<Vector3i *>(this->_data._mem) = Vector3i(); + break; + case PLANE: + *reinterpret_cast<Plane *>(this->_data._mem) = Plane(); + break; + case QUAT: + *reinterpret_cast<Quat *>(this->_data._mem) = Quat(); + break; + case COLOR: + *reinterpret_cast<Color *>(this->_data._mem) = Color(); + break; + default: + this->clear(); + break; } } -void Variant::clear() { - +void Variant::_clear_internal() { switch (type) { case STRING: { - reinterpret_cast<String *>(_data._mem)->~String(); } break; /* @@ -1438,35 +1279,28 @@ void Variant::clear() { COLOR, VECTOR2, RECT2 - */ + */ case TRANSFORM2D: { - memdelete(_data._transform2d); } break; case AABB: { - memdelete(_data._aabb); } break; case BASIS: { - memdelete(_data._basis); } break; case TRANSFORM: { - memdelete(_data._transform); } break; // misc types case STRING_NAME: { - reinterpret_cast<StringName *>(_data._mem)->~StringName(); } break; case NODE_PATH: { - reinterpret_cast<NodePath *>(_data._mem)->~NodePath(); } break; case OBJECT: { - if (_get_obj().id.is_reference()) { //we are safe that there is a reference here Reference *reference = static_cast<Reference *>(_get_obj().obj); @@ -1477,148 +1311,124 @@ void Variant::clear() { _get_obj().obj = nullptr; _get_obj().id = ObjectID(); } break; - case _RID: { + case RID: { // not much need probably - reinterpret_cast<RID *>(_data._mem)->~RID(); + // Can't seem to use destructor + scoping operator, so hack. + typedef ::RID RID_Class; + reinterpret_cast<RID_Class *>(_data._mem)->~RID_Class(); } break; case CALLABLE: { - reinterpret_cast<Callable *>(_data._mem)->~Callable(); } break; case SIGNAL: { - reinterpret_cast<Signal *>(_data._mem)->~Signal(); } break; case DICTIONARY: { - reinterpret_cast<Dictionary *>(_data._mem)->~Dictionary(); } break; case ARRAY: { - reinterpret_cast<Array *>(_data._mem)->~Array(); } break; // arrays case PACKED_BYTE_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; case PACKED_INT32_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; case PACKED_INT64_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; case PACKED_FLOAT32_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; case PACKED_FLOAT64_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; case PACKED_STRING_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; case PACKED_VECTOR2_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; case PACKED_VECTOR3_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; case PACKED_COLOR_ARRAY: { - PackedArrayRefBase::destroy(_data.packed_array); } break; default: { } /* not needed */ } - - type = NIL; } Variant::operator signed int() const { - switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } } -Variant::operator unsigned int() const { +Variant::operator unsigned int() const { switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } } Variant::operator int64_t() const { - switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_int64(); - default: { - + case NIL: return 0; - } - } -} - -/* -Variant::operator long unsigned int() const { - - switch( type ) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._real; - case STRING: return operator String().to_int(); + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } - - return 0; -}; -*/ +} Variant::operator uint64_t() const { - switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } @@ -1636,141 +1446,159 @@ Variant::operator ObjectID() const { #ifdef NEED_LONG_INT Variant::operator signed long() const { - switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._real; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } return 0; -}; +} Variant::operator unsigned long() const { - switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._real; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } return 0; -}; +} #endif Variant::operator signed short() const { - switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } } -Variant::operator unsigned short() const { +Variant::operator unsigned short() const { switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } } -Variant::operator signed char() const { +Variant::operator signed char() const { switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } } -Variant::operator unsigned char() const { +Variant::operator unsigned char() const { switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1 : 0; - case INT: return _data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_int(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_int(); default: { - return 0; } } } -Variant::operator CharType() const { - +Variant::operator char32_t() const { return operator unsigned int(); } Variant::operator float() const { - switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1.0 : 0.0; - case INT: return (float)_data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_double(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1.0 : 0.0; + case INT: + return (float)_data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_float(); default: { - return 0; } } } -Variant::operator double() const { +Variant::operator double() const { switch (type) { - - case NIL: return 0; - case BOOL: return _data._bool ? 1.0 : 0.0; - case INT: return (double)_data._int; - case FLOAT: return _data._float; - case STRING: return operator String().to_double(); + case NIL: + return 0; + case BOOL: + return _data._bool ? 1.0 : 0.0; + case INT: + return (double)_data._int; + case FLOAT: + return _data._float; + case STRING: + return operator String().to_float(); default: { - return 0; } } } Variant::operator StringName() const { - if (type == STRING_NAME) { return *reinterpret_cast<const StringName *>(_data._mem); } else if (type == STRING) { @@ -1781,12 +1609,10 @@ Variant::operator StringName() const { } struct _VariantStrPair { - String key; String value; bool operator<(const _VariantStrPair &p) const { - return key < p.key; } }; @@ -1799,44 +1625,54 @@ Variant::operator String() const { String Variant::stringify(List<const void *> &stack) const { switch (type) { - - case NIL: return "Null"; - case BOOL: return _data._bool ? "True" : "False"; - case INT: return itos(_data._int); - case FLOAT: return rtos(_data._float); - case STRING: return *reinterpret_cast<const String *>(_data._mem); - case VECTOR2: return "(" + operator Vector2() + ")"; - case VECTOR2I: return "(" + operator Vector2i() + ")"; - case RECT2: return "(" + operator Rect2() + ")"; - case RECT2I: return "(" + operator Rect2i() + ")"; + case NIL: + return "Null"; + case BOOL: + return _data._bool ? "True" : "False"; + case INT: + return itos(_data._int); + case FLOAT: + return rtos(_data._float); + case STRING: + return *reinterpret_cast<const String *>(_data._mem); + case VECTOR2: + return "(" + operator Vector2() + ")"; + case VECTOR2I: + return "(" + operator Vector2i() + ")"; + case RECT2: + return "(" + operator Rect2() + ")"; + case RECT2I: + return "(" + operator Rect2i() + ")"; case TRANSFORM2D: { - Transform2D mat32 = operator Transform2D(); return "(" + Variant(mat32.elements[0]).operator String() + ", " + Variant(mat32.elements[1]).operator String() + ", " + Variant(mat32.elements[2]).operator String() + ")"; } break; - case VECTOR3: return "(" + operator Vector3() + ")"; - case VECTOR3I: return "(" + operator Vector3i() + ")"; + case VECTOR3: + return "(" + operator Vector3() + ")"; + case VECTOR3I: + return "(" + operator Vector3i() + ")"; case PLANE: return operator Plane(); //case QUAT: - case AABB: return operator ::AABB(); - case QUAT: return "(" + operator Quat() + ")"; + case AABB: + return operator ::AABB(); + case QUAT: + return "(" + operator Quat() + ")"; case BASIS: { - Basis mat3 = operator Basis(); String mtx("("); for (int i = 0; i < 3; i++) { - - if (i != 0) + if (i != 0) { mtx += ", "; + } mtx += "("; for (int j = 0; j < 3; j++) { - - if (j != 0) + if (j != 0) { mtx += ", "; + } mtx += Variant(mat3.elements[i][j]).operator String(); } @@ -1846,12 +1682,15 @@ String Variant::stringify(List<const void *> &stack) const { return mtx + ")"; } break; - case TRANSFORM: return operator Transform(); - case STRING_NAME: return operator StringName(); - case NODE_PATH: return operator NodePath(); - case COLOR: return String::num(operator Color().r) + "," + String::num(operator Color().g) + "," + String::num(operator Color().b) + "," + String::num(operator Color().a); + case TRANSFORM: + return operator Transform(); + case STRING_NAME: + return operator StringName(); + case NODE_PATH: + return operator NodePath(); + case COLOR: + return String::num(operator Color().r) + "," + String::num(operator Color().g) + "," + String::num(operator Color().b) + "," + String::num(operator Color().a); case DICTIONARY: { - const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem); if (stack.find(d.id())) { return "{...}"; @@ -1867,7 +1706,6 @@ String Variant::stringify(List<const void *> &stack) const { Vector<_VariantStrPair> pairs; for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _VariantStrPair sp; sp.key = E->get().stringify(stack); sp.value = d[E->get()].stringify(stack); @@ -1878,8 +1716,9 @@ String Variant::stringify(List<const void *> &stack) const { pairs.sort(); for (int i = 0; i < pairs.size(); i++) { - if (i > 0) + if (i > 0) { str += ", "; + } str += pairs[i].key + ":" + pairs[i].value; } str += "}"; @@ -1887,98 +1726,90 @@ String Variant::stringify(List<const void *> &stack) const { return str; } break; case PACKED_VECTOR2_ARRAY: { - Vector<Vector2> vec = operator Vector<Vector2>(); String str("["); for (int i = 0; i < vec.size(); i++) { - - if (i > 0) + if (i > 0) { str += ", "; + } str = str + Variant(vec[i]); } str += "]"; return str; } break; case PACKED_VECTOR3_ARRAY: { - Vector<Vector3> vec = operator Vector<Vector3>(); String str("["); for (int i = 0; i < vec.size(); i++) { - - if (i > 0) + if (i > 0) { str += ", "; + } str = str + Variant(vec[i]); } str += "]"; return str; } break; case PACKED_STRING_ARRAY: { - Vector<String> vec = operator Vector<String>(); String str("["); for (int i = 0; i < vec.size(); i++) { - - if (i > 0) + if (i > 0) { str += ", "; + } str = str + vec[i]; } str += "]"; return str; } break; case PACKED_INT32_ARRAY: { - Vector<int32_t> vec = operator Vector<int32_t>(); String str("["); for (int i = 0; i < vec.size(); i++) { - - if (i > 0) + if (i > 0) { str += ", "; + } str = str + itos(vec[i]); } str += "]"; return str; } break; case PACKED_INT64_ARRAY: { - Vector<int64_t> vec = operator Vector<int64_t>(); String str("["); for (int i = 0; i < vec.size(); i++) { - - if (i > 0) + if (i > 0) { str += ", "; + } str = str + itos(vec[i]); } str += "]"; return str; } break; case PACKED_FLOAT32_ARRAY: { - Vector<float> vec = operator Vector<float>(); String str("["); for (int i = 0; i < vec.size(); i++) { - - if (i > 0) + if (i > 0) { str += ", "; + } str = str + rtos(vec[i]); } str += "]"; return str; } break; case PACKED_FLOAT64_ARRAY: { - Vector<double> vec = operator Vector<double>(); String str("["); for (int i = 0; i < vec.size(); i++) { - - if (i > 0) + if (i > 0) { str += ", "; + } str = str + rtos(vec[i]); } str += "]"; return str; } break; case ARRAY: { - Array arr = operator Array(); if (stack.find(arr.id())) { return "[...]"; @@ -1987,8 +1818,9 @@ String Variant::stringify(List<const void *> &stack) const { String str("["); for (int i = 0; i < arr.size(); i++) { - if (i) + if (i) { str += ", "; + } str += arr[i].stringify(stack); } @@ -1998,16 +1830,15 @@ String Variant::stringify(List<const void *> &stack) const { } break; case OBJECT: { - if (_get_obj().obj) { - if (!_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { return "[Freed Object]"; - }; + } return _get_obj().obj->to_string(); - } else + } else { return "[Object:null]"; + } } break; case CALLABLE: { @@ -2018,8 +1849,8 @@ String Variant::stringify(List<const void *> &stack) const { const Signal &s = *reinterpret_cast<const Signal *>(_data._mem); return s; } break; - case _RID: { - const RID &s = *reinterpret_cast<const RID *>(_data._mem); + case RID: { + const ::RID &s = *reinterpret_cast<const ::RID *>(_data._mem); return "RID(" + itos(s.get_id()) + ")"; } break; default: { @@ -2031,131 +1862,131 @@ String Variant::stringify(List<const void *> &stack) const { } Variant::operator Vector2() const { - - if (type == VECTOR2) + if (type == VECTOR2) { return *reinterpret_cast<const Vector2 *>(_data._mem); - else if (type == VECTOR2I) + } else if (type == VECTOR2I) { return *reinterpret_cast<const Vector2i *>(_data._mem); - else if (type == VECTOR3) + } else if (type == VECTOR3) { return Vector2(reinterpret_cast<const Vector3 *>(_data._mem)->x, reinterpret_cast<const Vector3 *>(_data._mem)->y); - else if (type == VECTOR3I) + } else if (type == VECTOR3I) { return Vector2(reinterpret_cast<const Vector3i *>(_data._mem)->x, reinterpret_cast<const Vector3i *>(_data._mem)->y); - else + } else { return Vector2(); + } } Variant::operator Vector2i() const { - - if (type == VECTOR2I) + if (type == VECTOR2I) { return *reinterpret_cast<const Vector2i *>(_data._mem); - else if (type == VECTOR2) + } else if (type == VECTOR2) { return *reinterpret_cast<const Vector2 *>(_data._mem); - else if (type == VECTOR3) + } else if (type == VECTOR3) { return Vector2(reinterpret_cast<const Vector3 *>(_data._mem)->x, reinterpret_cast<const Vector3 *>(_data._mem)->y); - else if (type == VECTOR3I) + } else if (type == VECTOR3I) { return Vector2(reinterpret_cast<const Vector3i *>(_data._mem)->x, reinterpret_cast<const Vector3i *>(_data._mem)->y); - else + } else { return Vector2i(); + } } Variant::operator Rect2() const { - - if (type == RECT2) + if (type == RECT2) { return *reinterpret_cast<const Rect2 *>(_data._mem); - else if (type == RECT2I) + } else if (type == RECT2I) { return *reinterpret_cast<const Rect2i *>(_data._mem); - else + } else { return Rect2(); + } } Variant::operator Rect2i() const { - - if (type == RECT2I) + if (type == RECT2I) { return *reinterpret_cast<const Rect2i *>(_data._mem); - else if (type == RECT2) + } else if (type == RECT2) { return *reinterpret_cast<const Rect2 *>(_data._mem); - else + } else { return Rect2i(); + } } Variant::operator Vector3() const { - - if (type == VECTOR3) + if (type == VECTOR3) { return *reinterpret_cast<const Vector3 *>(_data._mem); - else if (type == VECTOR3I) + } else if (type == VECTOR3I) { return *reinterpret_cast<const Vector3i *>(_data._mem); - else if (type == VECTOR2) + } else if (type == VECTOR2) { return Vector3(reinterpret_cast<const Vector2 *>(_data._mem)->x, reinterpret_cast<const Vector2 *>(_data._mem)->y, 0.0); - else if (type == VECTOR2I) + } else if (type == VECTOR2I) { return Vector3(reinterpret_cast<const Vector2i *>(_data._mem)->x, reinterpret_cast<const Vector2i *>(_data._mem)->y, 0.0); - else + } else { return Vector3(); + } } Variant::operator Vector3i() const { - - if (type == VECTOR3I) + if (type == VECTOR3I) { return *reinterpret_cast<const Vector3i *>(_data._mem); - else if (type == VECTOR3) + } else if (type == VECTOR3) { return *reinterpret_cast<const Vector3 *>(_data._mem); - else if (type == VECTOR2) + } else if (type == VECTOR2) { return Vector3i(reinterpret_cast<const Vector2 *>(_data._mem)->x, reinterpret_cast<const Vector2 *>(_data._mem)->y, 0.0); - else if (type == VECTOR2I) + } else if (type == VECTOR2I) { return Vector3i(reinterpret_cast<const Vector2i *>(_data._mem)->x, reinterpret_cast<const Vector2i *>(_data._mem)->y, 0.0); - else + } else { return Vector3i(); + } } Variant::operator Plane() const { - - if (type == PLANE) + if (type == PLANE) { return *reinterpret_cast<const Plane *>(_data._mem); - else + } else { return Plane(); + } } -Variant::operator ::AABB() const { - if (type == AABB) +Variant::operator ::AABB() const { + if (type == AABB) { return *_data._aabb; - else + } else { return ::AABB(); + } } Variant::operator Basis() const { - - if (type == BASIS) + if (type == BASIS) { return *_data._basis; - else if (type == QUAT) + } else if (type == QUAT) { return *reinterpret_cast<const Quat *>(_data._mem); - else if (type == VECTOR3) { + } else if (type == VECTOR3) { return Basis(*reinterpret_cast<const Vector3 *>(_data._mem)); - } else if (type == TRANSFORM) // unexposed in Variant::can_convert? + } else if (type == TRANSFORM) { // unexposed in Variant::can_convert? return _data._transform->basis; - else + } else { return Basis(); + } } Variant::operator Quat() const { - - if (type == QUAT) + if (type == QUAT) { return *reinterpret_cast<const Quat *>(_data._mem); - else if (type == BASIS) + } else if (type == BASIS) { return *_data._basis; - else if (type == TRANSFORM) + } else if (type == TRANSFORM) { return _data._transform->basis; - else + } else { return Quat(); + } } Variant::operator Transform() const { - - if (type == TRANSFORM) + if (type == TRANSFORM) { return *_data._transform; - else if (type == BASIS) + } else if (type == BASIS) { return Transform(*_data._basis, Vector3()); - else if (type == QUAT) + } else if (type == QUAT) { return Transform(Basis(*reinterpret_cast<const Quat *>(_data._mem)), Vector3()); - else if (type == TRANSFORM2D) { + } else if (type == TRANSFORM2D) { const Transform2D &t = *_data._transform2d; Transform m; m.basis.elements[0][0] = t.elements[0][0]; @@ -2165,12 +1996,12 @@ Variant::operator Transform() const { m.origin[0] = t.elements[2][0]; m.origin[1] = t.elements[2][1]; return m; - } else + } else { return Transform(); + } } Variant::operator Transform2D() const { - if (type == TRANSFORM2D) { return *_data._transform2d; } else if (type == TRANSFORM) { @@ -2183,61 +2014,61 @@ Variant::operator Transform2D() const { m.elements[2][0] = t.origin[0]; m.elements[2][1] = t.origin[1]; return m; - } else + } else { return Transform2D(); + } } Variant::operator Color() const { - - if (type == COLOR) + if (type == COLOR) { return *reinterpret_cast<const Color *>(_data._mem); - else if (type == STRING) + } else if (type == STRING) { return Color::html(operator String()); - else if (type == INT) + } else if (type == INT) { return Color::hex(operator int()); - else + } else { return Color(); + } } Variant::operator NodePath() const { - - if (type == NODE_PATH) + if (type == NODE_PATH) { return *reinterpret_cast<const NodePath *>(_data._mem); - else if (type == STRING) + } else if (type == STRING) { return NodePath(operator String()); - else + } else { return NodePath(); + } } -Variant::operator RID() const { - - if (type == _RID) - return *reinterpret_cast<const RID *>(_data._mem); - else if (type == OBJECT && _get_obj().obj == nullptr) { - return RID(); +Variant::operator ::RID() const { + if (type == RID) { + return *reinterpret_cast<const ::RID *>(_data._mem); + } else if (type == OBJECT && _get_obj().obj == nullptr) { + return ::RID(); } else if (type == OBJECT && _get_obj().obj) { #ifdef DEBUG_ENABLED if (EngineDebugger::is_active()) { - ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, RID(), "Invalid pointer (object was freed)."); - }; + ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, ::RID(), "Invalid pointer (object was freed)."); + } #endif Callable::CallError ce; Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->get_rid, nullptr, 0, ce); - if (ce.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::_RID) { + if (ce.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::RID) { return ret; } - return RID(); + return ::RID(); } else { - return RID(); + return ::RID(); } } Variant::operator Object *() const { - - if (type == OBJECT) + if (type == OBJECT) { return _get_obj().obj; - else + } else { return nullptr; + } } Object *Variant::get_validated_object_with_check(bool &r_previously_freed) const { @@ -2252,59 +2083,59 @@ Object *Variant::get_validated_object_with_check(bool &r_previously_freed) const } Object *Variant::get_validated_object() const { - if (type == OBJECT) + if (type == OBJECT) { return ObjectDB::get_instance(_get_obj().id); - else + } else { return nullptr; + } } Variant::operator Node *() const { - - if (type == OBJECT) + if (type == OBJECT) { return Object::cast_to<Node>(_get_obj().obj); - else + } else { return nullptr; + } } -Variant::operator Control *() const { - if (type == OBJECT) +Variant::operator Control *() const { + if (type == OBJECT) { return Object::cast_to<Control>(_get_obj().obj); - else + } else { return nullptr; + } } Variant::operator Dictionary() const { - - if (type == DICTIONARY) + if (type == DICTIONARY) { return *reinterpret_cast<const Dictionary *>(_data._mem); - else + } else { return Dictionary(); + } } Variant::operator Callable() const { - - if (type == CALLABLE) + if (type == CALLABLE) { return *reinterpret_cast<const Callable *>(_data._mem); - else + } else { return Callable(); + } } Variant::operator Signal() const { - - if (type == SIGNAL) + if (type == SIGNAL) { return *reinterpret_cast<const Signal *>(_data._mem); - else + } else { return Signal(); + } } template <class DA, class SA> inline DA _convert_array(const SA &p_array) { - DA da; da.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - da.set(i, Variant(p_array.get(i))); } @@ -2313,9 +2144,7 @@ inline DA _convert_array(const SA &p_array) { template <class DA> inline DA _convert_array_from_variant(const Variant &p_variant) { - switch (p_variant.get_type()) { - case Variant::ARRAY: { return _convert_array<DA, Array>(p_variant.operator Array()); } @@ -2353,169 +2182,172 @@ inline DA _convert_array_from_variant(const Variant &p_variant) { } Variant::operator Array() const { - - if (type == ARRAY) + if (type == ARRAY) { return *reinterpret_cast<const Array *>(_data._mem); - else + } else { return _convert_array_from_variant<Array>(*this); + } } Variant::operator Vector<uint8_t>() const { - - if (type == PACKED_BYTE_ARRAY) + if (type == PACKED_BYTE_ARRAY) { return static_cast<PackedArrayRef<uint8_t> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<uint8_t>>(*this); + } } -Variant::operator Vector<int32_t>() const { - if (type == PACKED_INT32_ARRAY) +Variant::operator Vector<int32_t>() const { + if (type == PACKED_INT32_ARRAY) { return static_cast<PackedArrayRef<int32_t> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<int>>(*this); + } } -Variant::operator Vector<int64_t>() const { - if (type == PACKED_INT64_ARRAY) +Variant::operator Vector<int64_t>() const { + if (type == PACKED_INT64_ARRAY) { return static_cast<PackedArrayRef<int64_t> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<int64_t>>(*this); + } } Variant::operator Vector<float>() const { - - if (type == PACKED_FLOAT32_ARRAY) + if (type == PACKED_FLOAT32_ARRAY) { return static_cast<PackedArrayRef<float> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<float>>(*this); + } } Variant::operator Vector<double>() const { - - if (type == PACKED_FLOAT64_ARRAY) + if (type == PACKED_FLOAT64_ARRAY) { return static_cast<PackedArrayRef<double> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<double>>(*this); + } } Variant::operator Vector<String>() const { - - if (type == PACKED_STRING_ARRAY) + if (type == PACKED_STRING_ARRAY) { return static_cast<PackedArrayRef<String> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<String>>(*this); + } } -Variant::operator Vector<Vector3>() const { - if (type == PACKED_VECTOR3_ARRAY) +Variant::operator Vector<Vector3>() const { + if (type == PACKED_VECTOR3_ARRAY) { return static_cast<PackedArrayRef<Vector3> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<Vector3>>(*this); + } } -Variant::operator Vector<Vector2>() const { - if (type == PACKED_VECTOR2_ARRAY) +Variant::operator Vector<Vector2>() const { + if (type == PACKED_VECTOR2_ARRAY) { return static_cast<PackedArrayRef<Vector2> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<Vector2>>(*this); + } } Variant::operator Vector<Color>() const { - - if (type == PACKED_COLOR_ARRAY) + if (type == PACKED_COLOR_ARRAY) { return static_cast<PackedArrayRef<Color> *>(_data.packed_array)->array; - else + } else { return _convert_array_from_variant<Vector<Color>>(*this); + } } /* helpers */ -Variant::operator Vector<RID>() const { - +Variant::operator Vector<::RID>() const { Array va = operator Array(); - Vector<RID> rids; + Vector<::RID> rids; rids.resize(va.size()); - for (int i = 0; i < rids.size(); i++) + for (int i = 0; i < rids.size(); i++) { rids.write[i] = va[i]; + } return rids; } Variant::operator Vector<Plane>() const { - Array va = operator Array(); Vector<Plane> planes; int va_size = va.size(); - if (va_size == 0) + if (va_size == 0) { return planes; + } planes.resize(va_size); Plane *w = planes.ptrw(); - for (int i = 0; i < va_size; i++) + for (int i = 0; i < va_size; i++) { w[i] = va[i]; + } return planes; } Variant::operator Vector<Face3>() const { - Vector<Vector3> va = operator Vector<Vector3>(); Vector<Face3> faces; int va_size = va.size(); - if (va_size == 0) + if (va_size == 0) { return faces; + } faces.resize(va_size / 3); Face3 *w = faces.ptrw(); const Vector3 *r = va.ptr(); - for (int i = 0; i < va_size; i++) + for (int i = 0; i < va_size; i++) { w[i / 3].vertex[i % 3] = r[i]; + } return faces; } Variant::operator Vector<Variant>() const { - Array va = operator Array(); Vector<Variant> variants; int va_size = va.size(); - if (va_size == 0) + if (va_size == 0) { return variants; + } variants.resize(va_size); Variant *w = variants.ptrw(); - for (int i = 0; i < va_size; i++) + for (int i = 0; i < va_size; i++) { w[i] = va[i]; + } return variants; } -Variant::operator Vector<StringName>() const { +Variant::operator Vector<StringName>() const { Vector<String> from = operator Vector<String>(); Vector<StringName> to; int len = from.size(); to.resize(len); for (int i = 0; i < len; i++) { - to.write[i] = from[i]; } return to; } -Variant::operator Margin() const { - - return (Margin) operator int(); +Variant::operator Side() const { + return (Side) operator int(); } -Variant::operator Orientation() const { +Variant::operator Orientation() const { return (Orientation) operator int(); } Variant::operator IP_Address() const { - if (type == PACKED_FLOAT32_ARRAY || type == PACKED_INT32_ARRAY || type == PACKED_FLOAT64_ARRAY || type == PACKED_INT64_ARRAY || type == PACKED_BYTE_ARRAY) { - Vector<int> addr = operator Vector<int>(); if (addr.size() == 4) { return IP_Address(addr.get(0), addr.get(1), addr.get(2), addr.get(3)); @@ -2526,26 +2358,16 @@ Variant::operator IP_Address() const { } Variant::Variant(bool p_bool) { - type = BOOL; _data._bool = p_bool; } -/* -Variant::Variant(long unsigned int p_long) { - - type=INT; - _data._int=p_long; -}; -*/ - Variant::Variant(signed int p_int) { - type = INT; _data._int = p_int; } -Variant::Variant(unsigned int p_int) { +Variant::Variant(unsigned int p_int) { type = INT; _data._int = p_int; } @@ -2553,56 +2375,52 @@ Variant::Variant(unsigned int p_int) { #ifdef NEED_LONG_INT Variant::Variant(signed long p_int) { - type = INT; _data._int = p_int; } -Variant::Variant(unsigned long p_int) { +Variant::Variant(unsigned long p_int) { type = INT; _data._int = p_int; } #endif Variant::Variant(int64_t p_int) { - type = INT; _data._int = p_int; } Variant::Variant(uint64_t p_int) { - type = INT; _data._int = p_int; } Variant::Variant(signed short p_short) { - type = INT; _data._int = p_short; } -Variant::Variant(unsigned short p_short) { +Variant::Variant(unsigned short p_short) { type = INT; _data._int = p_short; } -Variant::Variant(signed char p_char) { +Variant::Variant(signed char p_char) { type = INT; _data._int = p_char; } -Variant::Variant(unsigned char p_char) { +Variant::Variant(unsigned char p_char) { type = INT; _data._int = p_char; } -Variant::Variant(float p_float) { +Variant::Variant(float p_float) { type = FLOAT; _data._float = p_float; } -Variant::Variant(double p_double) { +Variant::Variant(double p_double) { type = FLOAT; _data._float = p_double; } @@ -2613,121 +2431,106 @@ Variant::Variant(const ObjectID &p_id) { } Variant::Variant(const StringName &p_string) { - type = STRING_NAME; memnew_placement(_data._mem, StringName(p_string)); } -Variant::Variant(const String &p_string) { +Variant::Variant(const String &p_string) { type = STRING; memnew_placement(_data._mem, String(p_string)); } Variant::Variant(const char *const p_cstring) { - type = STRING; memnew_placement(_data._mem, String((const char *)p_cstring)); } -Variant::Variant(const CharType *p_wstring) { - +Variant::Variant(const char32_t *p_wstring) { type = STRING; memnew_placement(_data._mem, String(p_wstring)); } -Variant::Variant(const Vector3 &p_vector3) { +Variant::Variant(const Vector3 &p_vector3) { type = VECTOR3; memnew_placement(_data._mem, Vector3(p_vector3)); } -Variant::Variant(const Vector3i &p_vector3i) { +Variant::Variant(const Vector3i &p_vector3i) { type = VECTOR3I; memnew_placement(_data._mem, Vector3i(p_vector3i)); } Variant::Variant(const Vector2 &p_vector2) { - type = VECTOR2; memnew_placement(_data._mem, Vector2(p_vector2)); } Variant::Variant(const Vector2i &p_vector2i) { - type = VECTOR2I; memnew_placement(_data._mem, Vector2i(p_vector2i)); } Variant::Variant(const Rect2 &p_rect2) { - type = RECT2; memnew_placement(_data._mem, Rect2(p_rect2)); } Variant::Variant(const Rect2i &p_rect2i) { - type = RECT2I; memnew_placement(_data._mem, Rect2i(p_rect2i)); } Variant::Variant(const Plane &p_plane) { - type = PLANE; memnew_placement(_data._mem, Plane(p_plane)); } -Variant::Variant(const ::AABB &p_aabb) { +Variant::Variant(const ::AABB &p_aabb) { type = AABB; _data._aabb = memnew(::AABB(p_aabb)); } Variant::Variant(const Basis &p_matrix) { - type = BASIS; _data._basis = memnew(Basis(p_matrix)); } Variant::Variant(const Quat &p_quat) { - type = QUAT; memnew_placement(_data._mem, Quat(p_quat)); } -Variant::Variant(const Transform &p_transform) { +Variant::Variant(const Transform &p_transform) { type = TRANSFORM; _data._transform = memnew(Transform(p_transform)); } Variant::Variant(const Transform2D &p_transform) { - type = TRANSFORM2D; _data._transform2d = memnew(Transform2D(p_transform)); } -Variant::Variant(const Color &p_color) { +Variant::Variant(const Color &p_color) { type = COLOR; memnew_placement(_data._mem, Color(p_color)); } Variant::Variant(const NodePath &p_node_path) { - type = NODE_PATH; memnew_placement(_data._mem, NodePath(p_node_path)); } -Variant::Variant(const RID &p_rid) { - - type = _RID; - memnew_placement(_data._mem, RID(p_rid)); +Variant::Variant(const ::RID &p_rid) { + type = RID; + memnew_placement(_data._mem, ::RID(p_rid)); } Variant::Variant(const Object *p_object) { - type = OBJECT; memnew_placement(_data._mem, ObjData); if (p_object) { - if (p_object->is_reference()) { Reference *reference = const_cast<Reference *>(static_cast<const Reference *>(p_object)); if (!reference->init_ref()) { @@ -2746,30 +2549,26 @@ Variant::Variant(const Object *p_object) { } Variant::Variant(const Callable &p_callable) { - type = CALLABLE; memnew_placement(_data._mem, Callable(p_callable)); } -Variant::Variant(const Signal &p_callable) { +Variant::Variant(const Signal &p_callable) { type = SIGNAL; memnew_placement(_data._mem, Signal(p_callable)); } Variant::Variant(const Dictionary &p_dictionary) { - type = DICTIONARY; memnew_placement(_data._mem, Dictionary(p_dictionary)); } Variant::Variant(const Array &p_array) { - type = ARRAY; memnew_placement(_data._mem, Array(p_array)); } Variant::Variant(const Vector<Plane> &p_array) { - type = ARRAY; Array *plane_array = memnew_placement(_data._mem, Array); @@ -2777,13 +2576,11 @@ Variant::Variant(const Vector<Plane> &p_array) { plane_array->resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - plane_array->operator[](i) = Variant(p_array[i]); } } -Variant::Variant(const Vector<RID> &p_array) { - +Variant::Variant(const Vector<::RID> &p_array) { type = ARRAY; Array *rid_array = memnew_placement(_data._mem, Array); @@ -2791,65 +2588,57 @@ Variant::Variant(const Vector<RID> &p_array) { rid_array->resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - rid_array->set(i, Variant(p_array[i])); } } Variant::Variant(const Vector<uint8_t> &p_byte_array) { - type = PACKED_BYTE_ARRAY; _data.packed_array = PackedArrayRef<uint8_t>::create(p_byte_array); } -Variant::Variant(const Vector<int32_t> &p_int32_array) { +Variant::Variant(const Vector<int32_t> &p_int32_array) { type = PACKED_INT32_ARRAY; _data.packed_array = PackedArrayRef<int32_t>::create(p_int32_array); } Variant::Variant(const Vector<int64_t> &p_int64_array) { - type = PACKED_INT64_ARRAY; _data.packed_array = PackedArrayRef<int64_t>::create(p_int64_array); } Variant::Variant(const Vector<float> &p_float32_array) { - type = PACKED_FLOAT32_ARRAY; _data.packed_array = PackedArrayRef<float>::create(p_float32_array); } Variant::Variant(const Vector<double> &p_float64_array) { - type = PACKED_FLOAT64_ARRAY; _data.packed_array = PackedArrayRef<double>::create(p_float64_array); } Variant::Variant(const Vector<String> &p_string_array) { - type = PACKED_STRING_ARRAY; _data.packed_array = PackedArrayRef<String>::create(p_string_array); } -Variant::Variant(const Vector<Vector3> &p_vector3_array) { +Variant::Variant(const Vector<Vector3> &p_vector3_array) { type = PACKED_VECTOR3_ARRAY; _data.packed_array = PackedArrayRef<Vector3>::create(p_vector3_array); } Variant::Variant(const Vector<Vector2> &p_vector2_array) { - type = PACKED_VECTOR2_ARRAY; _data.packed_array = PackedArrayRef<Vector2>::create(p_vector2_array); } -Variant::Variant(const Vector<Color> &p_color_array) { +Variant::Variant(const Vector<Color> &p_color_array) { type = PACKED_COLOR_ARRAY; _data.packed_array = PackedArrayRef<Color>::create(p_color_array); } Variant::Variant(const Vector<Face3> &p_face_array) { - Vector<Vector3> vertices; int face_count = p_face_array.size(); vertices.resize(face_count * 3); @@ -2859,9 +2648,9 @@ Variant::Variant(const Vector<Face3> &p_face_array) { Vector3 *w = vertices.ptrw(); for (int i = 0; i < face_count; i++) { - - for (int j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) { w[i * 3 + j] = r[i].vertex[j]; + } } } @@ -2882,20 +2671,20 @@ Variant::Variant(const Vector<Variant> &p_array) { } Variant::Variant(const Vector<StringName> &p_array) { - type = NIL; Vector<String> v; int len = p_array.size(); v.resize(len); - for (int i = 0; i < len; i++) + for (int i = 0; i < len; i++) { v.set(i, p_array[i]); + } *this = v; } void Variant::operator=(const Variant &p_variant) { - - if (unlikely(this == &p_variant)) + if (unlikely(this == &p_variant)) { return; + } if (unlikely(type != p_variant.type)) { reference(p_variant); @@ -2904,90 +2693,70 @@ void Variant::operator=(const Variant &p_variant) { switch (p_variant.type) { case NIL: { - // none } break; // atomic types case BOOL: { - _data._bool = p_variant._data._bool; } break; case INT: { - _data._int = p_variant._data._int; } break; case FLOAT: { - _data._float = p_variant._data._float; } break; case STRING: { - *reinterpret_cast<String *>(_data._mem) = *reinterpret_cast<const String *>(p_variant._data._mem); } break; // math types case VECTOR2: { - *reinterpret_cast<Vector2 *>(_data._mem) = *reinterpret_cast<const Vector2 *>(p_variant._data._mem); } break; case VECTOR2I: { - *reinterpret_cast<Vector2i *>(_data._mem) = *reinterpret_cast<const Vector2i *>(p_variant._data._mem); } break; case RECT2: { - *reinterpret_cast<Rect2 *>(_data._mem) = *reinterpret_cast<const Rect2 *>(p_variant._data._mem); } break; case RECT2I: { - *reinterpret_cast<Rect2i *>(_data._mem) = *reinterpret_cast<const Rect2i *>(p_variant._data._mem); } break; case TRANSFORM2D: { - *_data._transform2d = *(p_variant._data._transform2d); } break; case VECTOR3: { - *reinterpret_cast<Vector3 *>(_data._mem) = *reinterpret_cast<const Vector3 *>(p_variant._data._mem); } break; case VECTOR3I: { - *reinterpret_cast<Vector3i *>(_data._mem) = *reinterpret_cast<const Vector3i *>(p_variant._data._mem); } break; case PLANE: { - *reinterpret_cast<Plane *>(_data._mem) = *reinterpret_cast<const Plane *>(p_variant._data._mem); } break; case AABB: { - *_data._aabb = *(p_variant._data._aabb); } break; case QUAT: { - *reinterpret_cast<Quat *>(_data._mem) = *reinterpret_cast<const Quat *>(p_variant._data._mem); } break; case BASIS: { - *_data._basis = *(p_variant._data._basis); } break; case TRANSFORM: { - *_data._transform = *(p_variant._data._transform); } break; // misc types case COLOR: { - *reinterpret_cast<Color *>(_data._mem) = *reinterpret_cast<const Color *>(p_variant._data._mem); } break; - case _RID: { - - *reinterpret_cast<RID *>(_data._mem) = *reinterpret_cast<const RID *>(p_variant._data._mem); + case RID: { + *reinterpret_cast<::RID *>(_data._mem) = *reinterpret_cast<const ::RID *>(p_variant._data._mem); } break; case OBJECT: { - if (_get_obj().id.is_reference()) { //we are safe that there is a reference here Reference *reference = static_cast<Reference *>(_get_obj().obj); @@ -3010,66 +2779,51 @@ void Variant::operator=(const Variant &p_variant) { } break; case CALLABLE: { - *reinterpret_cast<Callable *>(_data._mem) = *reinterpret_cast<const Callable *>(p_variant._data._mem); } break; case SIGNAL: { - *reinterpret_cast<Signal *>(_data._mem) = *reinterpret_cast<const Signal *>(p_variant._data._mem); } break; case STRING_NAME: { - *reinterpret_cast<StringName *>(_data._mem) = *reinterpret_cast<const StringName *>(p_variant._data._mem); } break; case NODE_PATH: { - *reinterpret_cast<NodePath *>(_data._mem) = *reinterpret_cast<const NodePath *>(p_variant._data._mem); } break; case DICTIONARY: { - *reinterpret_cast<Dictionary *>(_data._mem) = *reinterpret_cast<const Dictionary *>(p_variant._data._mem); } break; case ARRAY: { - *reinterpret_cast<Array *>(_data._mem) = *reinterpret_cast<const Array *>(p_variant._data._mem); } break; // arrays case PACKED_BYTE_ARRAY: { - _data.packed_array = PackedArrayRef<uint8_t>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; case PACKED_INT32_ARRAY: { - _data.packed_array = PackedArrayRef<int32_t>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; case PACKED_INT64_ARRAY: { - _data.packed_array = PackedArrayRef<int64_t>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; case PACKED_FLOAT32_ARRAY: { - _data.packed_array = PackedArrayRef<float>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; case PACKED_FLOAT64_ARRAY: { - _data.packed_array = PackedArrayRef<double>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; case PACKED_STRING_ARRAY: { - _data.packed_array = PackedArrayRef<String>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; case PACKED_VECTOR2_ARRAY: { - _data.packed_array = PackedArrayRef<Vector2>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; case PACKED_VECTOR3_ARRAY: { - _data.packed_array = PackedArrayRef<Vector3>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; case PACKED_COLOR_ARRAY: { - _data.packed_array = PackedArrayRef<Color>::reference_from(_data.packed_array, p_variant._data.packed_array); } break; default: { @@ -3078,77 +2832,56 @@ void Variant::operator=(const Variant &p_variant) { } Variant::Variant(const IP_Address &p_address) { - type = STRING; memnew_placement(_data._mem, String(p_address)); } Variant::Variant(const Variant &p_variant) { - - type = NIL; reference(p_variant); } -/* -Variant::~Variant() { - - clear(); -}*/ - uint32_t Variant::hash() const { - switch (type) { case NIL: { - return 0; } break; case BOOL: { - return _data._bool ? 1 : 0; } break; case INT: { - return _data._int; } break; case FLOAT: { - return hash_djb2_one_float(_data._float); } break; case STRING: { - return reinterpret_cast<const String *>(_data._mem)->hash(); } break; // math types case VECTOR2: { - uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Vector2 *>(_data._mem)->x); return hash_djb2_one_float(reinterpret_cast<const Vector2 *>(_data._mem)->y, hash); } break; case VECTOR2I: { - uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->x); return hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->y, hash); } break; case RECT2: { - uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->position.x); hash = hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->position.y, hash); hash = hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->size.x, hash); return hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->size.y, hash); } break; case RECT2I: { - uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.x); hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash); hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash); return hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash); } break; case TRANSFORM2D: { - uint32_t hash = 5831; for (int i = 0; i < 3; i++) { - for (int j = 0; j < 2; j++) { hash = hash_djb2_one_float(_data._transform2d->elements[i][j], hash); } @@ -3157,35 +2890,25 @@ uint32_t Variant::hash() const { return hash; } break; case VECTOR3: { - uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Vector3 *>(_data._mem)->x); hash = hash_djb2_one_float(reinterpret_cast<const Vector3 *>(_data._mem)->y, hash); return hash_djb2_one_float(reinterpret_cast<const Vector3 *>(_data._mem)->z, hash); } break; case VECTOR3I: { - uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->x); hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->y, hash); return hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->z, hash); } break; case PLANE: { - uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Plane *>(_data._mem)->normal.x); hash = hash_djb2_one_float(reinterpret_cast<const Plane *>(_data._mem)->normal.y, hash); hash = hash_djb2_one_float(reinterpret_cast<const Plane *>(_data._mem)->normal.z, hash); return hash_djb2_one_float(reinterpret_cast<const Plane *>(_data._mem)->d, hash); } break; - /* - case QUAT: { - - - } break;*/ case AABB: { - uint32_t hash = 5831; for (int i = 0; i < 3; i++) { - hash = hash_djb2_one_float(_data._aabb->position[i], hash); hash = hash_djb2_one_float(_data._aabb->size[i], hash); } @@ -3194,7 +2917,6 @@ uint32_t Variant::hash() const { } break; case QUAT: { - uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Quat *>(_data._mem)->x); hash = hash_djb2_one_float(reinterpret_cast<const Quat *>(_data._mem)->y, hash); hash = hash_djb2_one_float(reinterpret_cast<const Quat *>(_data._mem)->z, hash); @@ -3202,10 +2924,8 @@ uint32_t Variant::hash() const { } break; case BASIS: { - uint32_t hash = 5831; for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { hash = hash_djb2_one_float(_data._basis->elements[i][j], hash); } @@ -3215,10 +2935,8 @@ uint32_t Variant::hash() const { } break; case TRANSFORM: { - uint32_t hash = 5831; for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { hash = hash_djb2_one_float(_data._transform->basis.elements[i][j], hash); } @@ -3231,53 +2949,43 @@ uint32_t Variant::hash() const { // misc types case COLOR: { - uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Color *>(_data._mem)->r); hash = hash_djb2_one_float(reinterpret_cast<const Color *>(_data._mem)->g, hash); hash = hash_djb2_one_float(reinterpret_cast<const Color *>(_data._mem)->b, hash); return hash_djb2_one_float(reinterpret_cast<const Color *>(_data._mem)->a, hash); } break; - case _RID: { - - return hash_djb2_one_64(reinterpret_cast<const RID *>(_data._mem)->get_id()); + case RID: { + return hash_djb2_one_64(reinterpret_cast<const ::RID *>(_data._mem)->get_id()); } break; case OBJECT: { - return hash_djb2_one_64(make_uint64_t(_get_obj().obj)); } break; case STRING_NAME: { - return reinterpret_cast<const StringName *>(_data._mem)->hash(); } break; case NODE_PATH: { - return reinterpret_cast<const NodePath *>(_data._mem)->hash(); } break; case DICTIONARY: { - return reinterpret_cast<const Dictionary *>(_data._mem)->hash(); } break; case CALLABLE: { - return reinterpret_cast<const Callable *>(_data._mem)->hash(); } break; case SIGNAL: { - const Signal &s = *reinterpret_cast<const Signal *>(_data._mem); uint32_t hash = s.get_name().hash(); return hash_djb2_one_64(s.get_object_id(), hash); } break; case ARRAY: { - const Array &arr = *reinterpret_cast<const Array *>(_data._mem); return arr.hash(); } break; case PACKED_BYTE_ARRAY: { - const Vector<uint8_t> &arr = PackedArrayRef<uint8_t>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { @@ -3289,7 +2997,6 @@ uint32_t Variant::hash() const { } break; case PACKED_INT32_ARRAY: { - const Vector<int32_t> &arr = PackedArrayRef<int32_t>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { @@ -3301,7 +3008,6 @@ uint32_t Variant::hash() const { } break; case PACKED_INT64_ARRAY: { - const Vector<int64_t> &arr = PackedArrayRef<int64_t>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { @@ -3313,7 +3019,6 @@ uint32_t Variant::hash() const { } break; case PACKED_FLOAT32_ARRAY: { - const Vector<float> &arr = PackedArrayRef<float>::get_array(_data.packed_array); int len = arr.size(); @@ -3326,7 +3031,6 @@ uint32_t Variant::hash() const { } break; case PACKED_FLOAT64_ARRAY: { - const Vector<double> &arr = PackedArrayRef<double>::get_array(_data.packed_array); int len = arr.size(); @@ -3339,7 +3043,6 @@ uint32_t Variant::hash() const { } break; case PACKED_STRING_ARRAY: { - uint32_t hash = 5831; const Vector<String> &arr = PackedArrayRef<String>::get_array(_data.packed_array); int len = arr.size(); @@ -3355,7 +3058,6 @@ uint32_t Variant::hash() const { return hash; } break; case PACKED_VECTOR2_ARRAY: { - uint32_t hash = 5831; const Vector<Vector2> &arr = PackedArrayRef<Vector2>::get_array(_data.packed_array); int len = arr.size(); @@ -3372,7 +3074,6 @@ uint32_t Variant::hash() const { return hash; } break; case PACKED_VECTOR3_ARRAY: { - uint32_t hash = 5831; const Vector<Vector3> &arr = PackedArrayRef<Vector3>::get_array(_data.packed_array); int len = arr.size(); @@ -3390,7 +3091,6 @@ uint32_t Variant::hash() const { return hash; } break; case PACKED_COLOR_ARRAY: { - uint32_t hash = 5831; const Vector<Color> &arr = PackedArrayRef<Color>::get_array(_data.packed_array); int len = arr.size(); @@ -3457,8 +3157,9 @@ uint32_t Variant::hash() const { return true bool Variant::hash_compare(const Variant &p_variant) const { - if (type != p_variant.type) + if (type != p_variant.type) { return false; + } switch (type) { case FLOAT: { @@ -3496,8 +3197,9 @@ bool Variant::hash_compare(const Variant &p_variant) const { Transform2D *r = p_variant._data._transform2d; for (int i = 0; i < 3; i++) { - if (!(hash_compare_vector2(l->elements[i], r->elements[i]))) + if (!(hash_compare_vector2(l->elements[i], r->elements[i]))) { return false; + } } return true; @@ -3545,8 +3247,9 @@ bool Variant::hash_compare(const Variant &p_variant) const { const Basis *r = p_variant._data._basis; for (int i = 0; i < 3; i++) { - if (!(hash_compare_vector3(l->elements[i], r->elements[i]))) + if (!(hash_compare_vector3(l->elements[i], r->elements[i]))) { return false; + } } return true; @@ -3557,8 +3260,9 @@ bool Variant::hash_compare(const Variant &p_variant) const { const Transform *r = p_variant._data._transform; for (int i = 0; i < 3; i++) { - if (!(hash_compare_vector3(l->basis.elements[i], r->basis.elements[i]))) + if (!(hash_compare_vector3(l->basis.elements[i], r->basis.elements[i]))) { return false; + } } return hash_compare_vector3(l->origin, r->origin); @@ -3575,12 +3279,14 @@ bool Variant::hash_compare(const Variant &p_variant) const { const Array &l = *(reinterpret_cast<const Array *>(_data._mem)); const Array &r = *(reinterpret_cast<const Array *>(p_variant._data._mem)); - if (l.size() != r.size()) + if (l.size() != r.size()) { return false; + } for (int i = 0; i < l.size(); ++i) { - if (!l[i].hash_compare(r[i])) + if (!l[i].hash_compare(r[i])) { return false; + } } return true; @@ -3618,38 +3324,35 @@ bool Variant::hash_compare(const Variant &p_variant) const { } bool Variant::is_ref() const { - return type == OBJECT && _get_obj().id.is_reference(); } Vector<Variant> varray() { - return Vector<Variant>(); } Vector<Variant> varray(const Variant &p_arg1) { - Vector<Variant> v; v.push_back(p_arg1); return v; } -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2) { +Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2) { Vector<Variant> v; v.push_back(p_arg1); v.push_back(p_arg2); return v; } -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3) { +Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3) { Vector<Variant> v; v.push_back(p_arg1); v.push_back(p_arg2); v.push_back(p_arg3); return v; } -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4) { +Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4) { Vector<Variant> v; v.push_back(p_arg1); v.push_back(p_arg2); @@ -3659,7 +3362,6 @@ Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Varia } Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5) { - Vector<Variant> v; v.push_back(p_arg1); v.push_back(p_arg2); @@ -3673,12 +3375,13 @@ void Variant::static_assign(const Variant &p_variant) { } bool Variant::is_shared() const { - switch (type) { - - case OBJECT: return true; - case ARRAY: return true; - case DICTIONARY: return true; + case OBJECT: + return true; + case ARRAY: + return true; + case DICTIONARY: + return true; default: { } } @@ -3690,30 +3393,28 @@ Variant Variant::call(const StringName &p_method, VARIANT_ARG_DECLARE) { VARIANT_ARGPTRS; int argc = 0; for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) + if (argptr[i]->get_type() == Variant::NIL) { break; + } argc++; } Callable::CallError error; - Variant ret = call(p_method, argptr, argc, error); + Variant ret; + call(p_method, argptr, argc, ret, error); switch (error.error) { - case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { - String err = "Invalid type for argument #" + itos(error.argument) + ", expected '" + Variant::get_type_name(Variant::Type(error.expected)) + "'."; ERR_PRINT(err.utf8().get_data()); } break; case Callable::CallError::CALL_ERROR_INVALID_METHOD: { - String err = "Invalid method '" + p_method + "' for type '" + Variant::get_type_name(type) + "'."; ERR_PRINT(err.utf8().get_data()); } break; case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: { - String err = "Too many arguments for method '" + p_method + "'"; ERR_PRINT(err.utf8().get_data()); } break; @@ -3725,20 +3426,41 @@ Variant Variant::call(const StringName &p_method, VARIANT_ARG_DECLARE) { } void Variant::construct_from_string(const String &p_string, Variant &r_value, ObjectConstruct p_obj_construct, void *p_construct_ud) { - r_value = Variant(); } String Variant::get_construct_string() const { - String vars; VariantWriter::write_to_string(*this, vars); return vars; } -String Variant::get_call_error_text(Object *p_base, const StringName &p_method, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) { +String Variant::get_call_error_text(const StringName &p_method, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) { + String err_text; + if (ce.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) { + int errorarg = ce.argument; + if (p_argptrs) { + err_text = "Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(p_argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(ce.expected)) + "."; + } else { + err_text = "Cannot convert argument " + itos(errorarg + 1) + " from [missing argptr, type unknown] to " + Variant::get_type_name(Variant::Type(ce.expected)) + "."; + } + } else if (ce.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { + err_text = "Method expected " + itos(ce.argument) + " arguments, but called with " + itos(p_argcount) + "."; + } else if (ce.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { + err_text = "Method expected " + itos(ce.argument) + " arguments, but called with " + itos(p_argcount) + "."; + } else if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) { + err_text = "Method not found."; + } else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) { + err_text = "Instance is null"; + } else if (ce.error == Callable::CallError::CALL_OK) { + return "Call OK"; + } + return "'" + String(p_method) + "': " + err_text; +} + +String Variant::get_call_error_text(Object *p_base, const StringName &p_method, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) { String err_text; if (ce.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) { @@ -3763,14 +3485,12 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method, String class_name = p_base->get_class(); Ref<Script> script = p_base->get_script(); if (script.is_valid() && script->get_path().is_resource_file()) { - class_name += "(" + script->get_path().get_file() + ")"; } return "'" + class_name + "::" + String(p_method) + "': " + err_text; } String Variant::get_callable_error_text(const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) { - String err_text; if (ce.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) { @@ -3796,26 +3516,20 @@ String Variant::get_callable_error_text(const Callable &p_callable, const Varian } String vformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) { - Array args; if (p1.get_type() != Variant::NIL) { - args.push_back(p1); if (p2.get_type() != Variant::NIL) { - args.push_back(p2); if (p3.get_type() != Variant::NIL) { - args.push_back(p3); if (p4.get_type() != Variant::NIL) { - args.push_back(p4); if (p5.get_type() != Variant::NIL) { - args.push_back(p5); } } @@ -3830,3 +3544,18 @@ String vformat(const String &p_text, const Variant &p1, const Variant &p2, const return fmt; } + +void Variant::register_types() { + _register_variant_operators(); + _register_variant_methods(); + _register_variant_setters_getters(); + _register_variant_constructors(); + _register_variant_utility_functions(); +} +void Variant::unregister_types() { + _unregister_variant_operators(); + _unregister_variant_methods(); + _unregister_variant_setters_getters(); + _unregister_variant_constructors(); + _unregister_variant_utility_functions(); +} diff --git a/core/variant.h b/core/variant/variant.h index 1ae09ad82f..5050aa24ec 100644 --- a/core/variant.h +++ b/core/variant/variant.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,13 +31,10 @@ #ifndef VARIANT_H #define VARIANT_H -#include "core/array.h" -#include "core/callable.h" -#include "core/color.h" -#include "core/dictionary.h" #include "core/io/ip_address.h" #include "core/math/aabb.h" #include "core/math/basis.h" +#include "core/math/color.h" #include "core/math/face3.h" #include "core/math/plane.h" #include "core/math/quat.h" @@ -45,10 +42,13 @@ #include "core/math/transform_2d.h" #include "core/math/vector3.h" #include "core/math/vector3i.h" -#include "core/node_path.h" -#include "core/object_id.h" -#include "core/rid.h" -#include "core/ustring.h" +#include "core/object/object_id.h" +#include "core/string/node_path.h" +#include "core/string/ustring.h" +#include "core/templates/rid.h" +#include "core/variant/array.h" +#include "core/variant/callable.h" +#include "core/variant/dictionary.h" class Object; class Node; // helper @@ -97,7 +97,7 @@ public: COLOR, STRING_NAME, NODE_PATH, - _RID, + RID, OBJECT, CALLABLE, SIGNAL, @@ -120,15 +120,15 @@ public: private: friend struct _VariantCall; + friend class VariantInternal; // Variant takes 20 bytes when real_t is float, and 36 if double // it only allocates extra memory for aabb/matrix. - Type type; + Type type = NIL; struct ObjData { - ObjectID id; - Object *obj; + Object *obj = nullptr; }; /* array helpers */ @@ -207,7 +207,68 @@ private: } _data alignas(8); void reference(const Variant &p_variant); - void clear(); + + void _clear_internal(); + + _FORCE_INLINE_ void clear() { + static const bool needs_deinit[Variant::VARIANT_MAX] = { + false, //NIL, + false, //BOOL, + false, //INT, + false, //FLOAT, + true, //STRING, + false, //VECTOR2, + false, //VECTOR2I, + false, //RECT2, + false, //RECT2I, + false, //VECTOR3, + false, //VECTOR3I, + true, //TRANSFORM2D, + false, //PLANE, + false, //QUAT, + true, //AABB, + true, //BASIS, + true, //TRANSFORM, + + // misc types + false, //COLOR, + true, //STRING_NAME, + true, //NODE_PATH, + false, //RID, + true, //OBJECT, + true, //CALLABLE, + true, //SIGNAL, + true, //DICTIONARY, + true, //ARRAY, + + // typed arrays + true, //PACKED_BYTE_ARRAY, + true, //PACKED_INT32_ARRAY, + true, //PACKED_INT64_ARRAY, + true, //PACKED_FLOAT32_ARRAY, + true, //PACKED_FLOAT64_ARRAY, + true, //PACKED_STRING_ARRAY, + true, //PACKED_VECTOR2_ARRAY, + true, //PACKED_VECTOR3_ARRAY, + true, //PACKED_COLOR_ARRAY, + }; + + if (unlikely(needs_deinit[type])) { //make it fast for types that dont need deinit + _clear_internal(); + } + type = NIL; + } + + static void _register_variant_operators(); + static void _unregister_variant_operators(); + static void _register_variant_methods(); + static void _unregister_variant_methods(); + static void _register_variant_setters_getters(); + static void _unregister_variant_setters_getters(); + static void _register_variant_constructors(); + static void _unregister_variant_constructors(); + static void _register_variant_utility_functions(); + static void _unregister_variant_utility_functions(); public: _FORCE_INLINE_ Type get_type() const { @@ -220,10 +281,10 @@ public: bool is_ref() const; _FORCE_INLINE_ bool is_num() const { return type == INT || type == FLOAT; - }; + } _FORCE_INLINE_ bool is_array() const { return type >= ARRAY; - }; + } bool is_shared() const; bool is_zero() const; bool is_one() const; @@ -246,7 +307,7 @@ public: operator ObjectID() const; - operator CharType() const; + operator char32_t() const; operator float() const; operator double() const; operator String() const; @@ -266,7 +327,7 @@ public: operator Color() const; operator NodePath() const; - operator RID() const; + operator ::RID() const; operator Object *() const; operator Node *() const; @@ -291,11 +352,11 @@ public: operator Vector<Variant>() const; operator Vector<StringName>() const; - operator Vector<RID>() const; + operator Vector<::RID>() const; operator Vector<Vector2>() const; // some core type enums to convert to - operator Margin() const; + operator Side() const; operator Orientation() const; operator IP_Address() const; @@ -309,7 +370,6 @@ public: #ifdef NEED_LONG_INT Variant(signed long p_long); // real one Variant(unsigned long p_long); -//Variant(long unsigned int p_long); #endif Variant(signed short p_short); // real one Variant(unsigned short p_short); @@ -323,7 +383,7 @@ public: Variant(const String &p_string); Variant(const StringName &p_string); Variant(const char *const p_cstring); - Variant(const CharType *p_wstring); + Variant(const char32_t *p_wstring); Variant(const Vector2 &p_vector2); Variant(const Vector2i &p_vector2i); Variant(const Rect2 &p_rect2); @@ -338,7 +398,7 @@ public: Variant(const Transform &p_transform); Variant(const Color &p_color); Variant(const NodePath &p_node_path); - Variant(const RID &p_rid); + Variant(const ::RID &p_rid); Variant(const Object *p_object); Variant(const Callable &p_callable); Variant(const Signal &p_signal); @@ -358,14 +418,13 @@ public: Variant(const Vector<Variant> &p_array); Variant(const Vector<StringName> &p_array); - Variant(const Vector<RID> &p_array); // helper + Variant(const Vector<::RID> &p_array); // helper Variant(const Vector<Vector2> &p_array); // helper Variant(const IP_Address &p_address); // If this changes the table in variant_op must be updated enum Operator { - //comparison OP_EQUAL, OP_NOT_EQUAL, @@ -381,7 +440,6 @@ public: OP_NEGATE, OP_POSITIVE, OP_MODULE, - OP_STRING_CONCAT, //bitwise OP_SHIFT_LEFT, OP_SHIFT_RIGHT, @@ -403,37 +461,139 @@ public: static String get_operator_name(Operator p_op); static void evaluate(const Operator &p_op, const Variant &p_a, const Variant &p_b, Variant &r_ret, bool &r_valid); static _FORCE_INLINE_ Variant evaluate(const Operator &p_op, const Variant &p_a, const Variant &p_b) { - bool valid = true; Variant res; evaluate(p_op, p_a, p_b, res, valid); return res; } + static Variant::Type get_operator_return_type(Operator p_operator, Type p_type_a, Type p_type_b); + typedef void (*ValidatedOperatorEvaluator)(const Variant *left, const Variant *right, Variant *r_ret); + static ValidatedOperatorEvaluator get_validated_operator_evaluator(Operator p_operator, Type p_type_a, Type p_type_b); + typedef void (*PTROperatorEvaluator)(const void *left, const void *right, void *r_ret); + static PTROperatorEvaluator get_ptr_operator_evaluator(Operator p_operator, Type p_type_a, Type p_type_b); + void zero(); Variant duplicate(bool deep = false) const; static void blend(const Variant &a, const Variant &b, float c, Variant &r_dst); static void interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst); - void call_ptr(const StringName &p_method, const Variant **p_args, int p_argcount, Variant *r_ret, Callable::CallError &r_error); - Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + /* Built-In Methods */ + + typedef void (*ValidatedBuiltInMethod)(Variant *base, const Variant **p_args, int p_argcount, Variant *r_ret); + typedef void (*PTRBuiltInMethod)(void *p_base, const void **p_args, void *r_ret, int p_argcount); + + static bool has_builtin_method(Variant::Type p_type, const StringName &p_method); + + static ValidatedBuiltInMethod get_validated_builtin_method(Variant::Type p_type, const StringName &p_method); + static PTRBuiltInMethod get_ptr_builtin_method(Variant::Type p_type, const StringName &p_method); + + static int get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method); + static Variant::Type get_builtin_method_argument_type(Variant::Type p_type, const StringName &p_method, int p_argument); + static String get_builtin_method_argument_name(Variant::Type p_type, const StringName &p_method, int p_argument); + static Vector<Variant> get_builtin_method_default_arguments(Variant::Type p_type, const StringName &p_method); + static bool has_builtin_method_return_value(Variant::Type p_type, const StringName &p_method); + static Variant::Type get_builtin_method_return_type(Variant::Type p_type, const StringName &p_method); + static bool is_builtin_method_const(Variant::Type p_type, const StringName &p_method); + static bool is_builtin_method_vararg(Variant::Type p_type, const StringName &p_method); + static void get_builtin_method_list(Variant::Type p_type, List<StringName> *p_list); + static int get_builtin_method_count(Variant::Type p_type); + + void call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error); Variant call(const StringName &p_method, const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant()); + static String get_call_error_text(const StringName &p_method, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce); static String get_call_error_text(Object *p_base, const StringName &p_method, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce); static String get_callable_error_text(const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce); - static Variant construct(const Variant::Type, const Variant **p_args, int p_argcount, Callable::CallError &r_error, bool p_strict = true); - + //dynamic (includes Object) void get_method_list(List<MethodInfo> *p_list) const; bool has_method(const StringName &p_method) const; - static Vector<Variant::Type> get_method_argument_types(Variant::Type p_type, const StringName &p_method); - static Vector<Variant> get_method_default_arguments(Variant::Type p_type, const StringName &p_method); - static Variant::Type get_method_return_type(Variant::Type p_type, const StringName &p_method, bool *r_has_return = nullptr); - static Vector<StringName> get_method_argument_names(Variant::Type p_type, const StringName &p_method); - static bool is_method_const(Variant::Type p_type, const StringName &p_method); - void set_named(const StringName &p_index, const Variant &p_value, bool *r_valid = nullptr); - Variant get_named(const StringName &p_index, bool *r_valid = nullptr) const; + /* Constructors */ + + typedef void (*ValidatedConstructor)(Variant *r_base, const Variant **p_args); + typedef void (*PTRConstructor)(void *base, const void **p_args); + + static int get_constructor_count(Variant::Type p_type); + static ValidatedConstructor get_validated_constructor(Variant::Type p_type, int p_constructor); + static PTRConstructor get_ptr_constructor(Variant::Type p_type, int p_constructor); + static int get_constructor_argument_count(Variant::Type p_type, int p_constructor); + static Variant::Type get_constructor_argument_type(Variant::Type p_type, int p_constructor, int p_argument); + static String get_constructor_argument_name(Variant::Type p_type, int p_constructor, int p_argument); + static void construct(Variant::Type, Variant &base, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + + static void get_constructor_list(Type p_type, List<MethodInfo> *r_list); //convenience + + /* Properties */ + + void set_named(const StringName &p_member, const Variant &p_value, bool &r_valid); + Variant get_named(const StringName &p_member, bool &r_valid) const; + + typedef void (*ValidatedSetter)(Variant *base, const Variant *value); + typedef void (*ValidatedGetter)(const Variant *base, Variant *value); + + static bool has_member(Variant::Type p_type, const StringName &p_member); + static Variant::Type get_member_type(Variant::Type p_type, const StringName &p_member); + static void get_member_list(Type p_type, List<StringName> *r_members); + static int get_member_count(Type p_type); + + static ValidatedSetter get_member_validated_setter(Variant::Type p_type, const StringName &p_member); + static ValidatedGetter get_member_validated_getter(Variant::Type p_type, const StringName &p_member); + + typedef void (*PTRSetter)(void *base, const void *value); + typedef void (*PTRGetter)(const void *base, void *value); + + static PTRSetter get_member_ptr_setter(Variant::Type p_type, const StringName &p_member); + static PTRGetter get_member_ptr_getter(Variant::Type p_type, const StringName &p_member); + + /* Indexing */ + + static bool has_indexing(Variant::Type p_type); + static Variant::Type get_indexed_element_type(Variant::Type p_type); + + typedef void (*ValidatedIndexedSetter)(Variant *base, int64_t index, const Variant *value, bool *oob); + typedef void (*ValidatedIndexedGetter)(const Variant *base, int64_t index, Variant *value, bool *oob); + + static ValidatedIndexedSetter get_member_validated_indexed_setter(Variant::Type p_type); + static ValidatedIndexedGetter get_member_validated_indexed_getter(Variant::Type p_type); + + typedef void (*PTRIndexedSetter)(void *base, int64_t index, const void *value); + typedef void (*PTRIndexedGetter)(const void *base, int64_t index, void *value); + + static PTRIndexedSetter get_member_ptr_indexed_setter(Variant::Type p_type); + static PTRIndexedGetter get_member_ptr_indexed_getter(Variant::Type p_type); + + void set_indexed(int64_t p_index, const Variant &p_value, bool &r_valid, bool &r_oob); + Variant get_indexed(int64_t p_index, bool &r_valid, bool &r_oob) const; + + uint64_t get_indexed_size() const; + + /* Keying */ + + static bool is_keyed(Variant::Type p_type); + + typedef void (*ValidatedKeyedSetter)(Variant *base, const Variant *key, const Variant *value, bool *valid); + typedef void (*ValidatedKeyedGetter)(const Variant *base, const Variant *key, Variant *value, bool *valid); + typedef bool (*ValidatedKeyedChecker)(const Variant *base, const Variant *key, bool *valid); + + static ValidatedKeyedSetter get_member_validated_keyed_setter(Variant::Type p_type); + static ValidatedKeyedGetter get_member_validated_keyed_getter(Variant::Type p_type); + static ValidatedKeyedChecker get_member_validated_keyed_checker(Variant::Type p_type); + + typedef void (*PTRKeyedSetter)(void *base, const void *key, const void *value); + typedef void (*PTRKeyedGetter)(const void *base, const void *key, void *value); + typedef bool (*PTRKeyedChecker)(const void *base, const void *key); + + static PTRKeyedSetter get_member_ptr_keyed_setter(Variant::Type p_type); + static PTRKeyedGetter get_member_ptr_keyed_getter(Variant::Type p_type); + static PTRKeyedChecker get_member_ptr_keyed_checker(Variant::Type p_type); + + void set_keyed(const Variant &p_key, const Variant &p_value, bool &r_valid); + Variant get_keyed(const Variant &p_key, bool &r_valid) const; + bool has_key(const Variant &p_key, bool &r_valid) const; + + /* Generic */ void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr); Variant get(const Variant &p_index, bool *r_valid = nullptr) const; @@ -445,6 +605,33 @@ public: void get_property_list(List<PropertyInfo> *p_list) const; + static void call_utility_function(const StringName &p_name, Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static bool has_utility_function(const StringName &p_name); + + typedef void (*ValidatedUtilityFunction)(Variant *r_ret, const Variant **p_args, int p_argcount); + typedef void (*PTRUtilityFunction)(void *r_ret, const void **p_args, int p_argcount); + + static ValidatedUtilityFunction get_validated_utility_function(const StringName &p_name); + static PTRUtilityFunction get_ptr_utility_function(const StringName &p_name); + + enum UtilityFunctionType { + UTILITY_FUNC_TYPE_MATH, + UTILITY_FUNC_TYPE_RANDOM, + UTILITY_FUNC_TYPE_GENERAL, + }; + + static UtilityFunctionType get_utility_function_type(const StringName &p_name); + + static int get_utility_function_argument_count(const StringName &p_name); + static Variant::Type get_utility_function_argument_type(const StringName &p_name, int p_arg); + static String get_utility_function_argument_name(const StringName &p_name, int p_arg); + static bool has_utility_function_return_value(const StringName &p_name); + static Variant::Type get_utility_function_return_type(const StringName &p_name); + static bool is_utility_function_vararg(const StringName &p_name); + + static void get_utility_function_list(List<StringName> *r_functions); + static int get_utility_function_count(); + //argsVariant call() bool operator==(const Variant &p_variant) const; @@ -457,8 +644,8 @@ public: String stringify(List<const void *> &stack) const; void static_assign(const Variant &p_variant); - static void get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_list); static void get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); + static int get_constants_count_for_type(Variant::Type p_type); static bool has_constant(Variant::Type p_type, const StringName &p_value); static Variant get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = nullptr); @@ -469,12 +656,14 @@ public: static void construct_from_string(const String &p_string, Variant &r_value, ObjectConstruct p_obj_construct = nullptr, void *p_construct_ud = nullptr); void operator=(const Variant &p_variant); // only this is enough for all the other types + + static void register_types(); + static void unregister_types(); + Variant(const Variant &p_variant); - _FORCE_INLINE_ Variant() { - type = NIL; - } + _FORCE_INLINE_ Variant() {} _FORCE_INLINE_ ~Variant() { - if (type != Variant::NIL) clear(); + clear(); } }; @@ -489,22 +678,18 @@ Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Varia Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5); struct VariantHasher { - static _FORCE_INLINE_ uint32_t hash(const Variant &p_variant) { return p_variant.hash(); } }; struct VariantComparator { - static _FORCE_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) { return p_lhs.hash_compare(p_rhs); } }; Variant::ObjData &Variant::_get_obj() { - return *reinterpret_cast<ObjData *>(&_data._mem[0]); } const Variant::ObjData &Variant::_get_obj() const { - return *reinterpret_cast<const ObjData *>(&_data._mem[0]); } diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp new file mode 100644 index 0000000000..85e3b29279 --- /dev/null +++ b/core/variant/variant_call.cpp @@ -0,0 +1,1579 @@ +/*************************************************************************/ +/* variant_call.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "variant.h" + +#include "core/core_string_names.h" +#include "core/crypto/crypto_core.h" +#include "core/debugger/engine_debugger.h" +#include "core/io/compression.h" +#include "core/object/class_db.h" +#include "core/os/os.h" +#include "core/templates/local_vector.h" +#include "core/templates/oa_hash_map.h" + +typedef void (*VariantFunc)(Variant &r_ret, Variant &p_self, const Variant **p_args); +typedef void (*VariantConstructFunc)(Variant &r_ret, const Variant **p_args); + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { + call_with_variant_args_ret_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_ret, r_error, p_defvals); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { + call_with_variant_args_retc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_ret, r_error, p_defvals); +} + +template <class T, class... P> +static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { + call_with_variant_args_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals); +} + +template <class T, class... P> +static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { + call_with_variant_argsc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_validated_call(R (T::*method)(P...), Variant *base, const Variant **p_args, Variant *r_ret) { + call_with_validated_variant_args_ret(base, method, p_args, r_ret); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_validated_call(R (T::*method)(P...) const, Variant *base, const Variant **p_args, Variant *r_ret) { + call_with_validated_variant_args_retc(base, method, p_args, r_ret); +} +template <class T, class... P> +static _FORCE_INLINE_ void vc_validated_call(void (T::*method)(P...), Variant *base, const Variant **p_args, Variant *r_ret) { + call_with_validated_variant_args(base, method, p_args); +} + +template <class T, class... P> +static _FORCE_INLINE_ void vc_validated_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, Variant *r_ret) { + call_with_validated_variant_argsc(base, method, p_args); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_ptrcall(R (T::*method)(P...), void *p_base, const void **p_args, void *r_ret) { + call_with_ptr_args_ret(reinterpret_cast<T *>(p_base), method, p_args, r_ret); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_ptrcall(R (T::*method)(P...) const, void *p_base, const void **p_args, void *r_ret) { + call_with_ptr_args_retc(reinterpret_cast<T *>(p_base), method, p_args, r_ret); +} + +template <class T, class... P> +static _FORCE_INLINE_ void vc_ptrcall(void (T::*method)(P...), void *p_base, const void **p_args, void *r_ret) { + call_with_ptr_args(reinterpret_cast<T *>(p_base), method, p_args); +} + +template <class T, class... P> +static _FORCE_INLINE_ void vc_ptrcall(void (T::*method)(P...) const, void *p_base, const void **p_args, void *r_ret) { + call_with_ptr_argsc(reinterpret_cast<T *>(p_base), method, p_args); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_change_return_type(R (T::*method)(P...), Variant *v) { + VariantTypeAdjust<R>::adjust(v); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_change_return_type(R (T::*method)(P...) const, Variant *v) { + VariantTypeAdjust<R>::adjust(v); +} + +template <class T, class... P> +static _FORCE_INLINE_ void vc_change_return_type(void (T::*method)(P...), Variant *v) { + VariantInternal::clear(v); +} + +template <class T, class... P> +static _FORCE_INLINE_ void vc_change_return_type(void (T::*method)(P...) const, Variant *v) { + VariantInternal::clear(v); +} + +template <class R, class... P> +static _FORCE_INLINE_ void vc_change_return_type(R (*method)(P...), Variant *v) { + VariantTypeAdjust<R>::adjust(v); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ int vc_get_argument_count(R (T::*method)(P...)) { + return sizeof...(P); +} +template <class R, class T, class... P> +static _FORCE_INLINE_ int vc_get_argument_count(R (T::*method)(P...) const) { + return sizeof...(P); +} + +template <class T, class... P> +static _FORCE_INLINE_ int vc_get_argument_count(void (T::*method)(P...)) { + return sizeof...(P); +} + +template <class T, class... P> +static _FORCE_INLINE_ int vc_get_argument_count(void (T::*method)(P...) const) { + return sizeof...(P); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ int vc_get_argument_count(R (*method)(T *, P...)) { + return sizeof...(P); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_argument_type(R (T::*method)(P...), int p_arg) { + return call_get_argument_type<P...>(p_arg); +} +template <class R, class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_argument_type(R (T::*method)(P...) const, int p_arg) { + return call_get_argument_type<P...>(p_arg); +} + +template <class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_argument_type(void (T::*method)(P...), int p_arg) { + return call_get_argument_type<P...>(p_arg); +} + +template <class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_argument_type(void (T::*method)(P...) const, int p_arg) { + return call_get_argument_type<P...>(p_arg); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_argument_type(R (*method)(T *, P...), int p_arg) { + return call_get_argument_type<P...>(p_arg); +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_return_type(R (T::*method)(P...)) { + return GetTypeInfo<R>::VARIANT_TYPE; +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_return_type(R (T::*method)(P...) const) { + return GetTypeInfo<R>::VARIANT_TYPE; +} + +template <class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_return_type(void (T::*method)(P...)) { + return Variant::NIL; +} + +template <class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_return_type(void (T::*method)(P...) const) { + return Variant::NIL; +} + +template <class R, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_return_type(R (*method)(P...)) { + return GetTypeInfo<R>::VARIANT_TYPE; +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ bool vc_has_return_type(R (T::*method)(P...)) { + return true; +} +template <class R, class T, class... P> +static _FORCE_INLINE_ bool vc_has_return_type(R (T::*method)(P...) const) { + return true; +} + +template <class T, class... P> +static _FORCE_INLINE_ bool vc_has_return_type(void (T::*method)(P...)) { + return false; +} + +template <class T, class... P> +static _FORCE_INLINE_ bool vc_has_return_type(void (T::*method)(P...) const) { + return false; +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ bool vc_is_const(R (T::*method)(P...)) { + return false; +} +template <class R, class T, class... P> +static _FORCE_INLINE_ bool vc_is_const(R (T::*method)(P...) const) { + return true; +} + +template <class T, class... P> +static _FORCE_INLINE_ bool vc_is_const(void (T::*method)(P...)) { + return false; +} + +template <class T, class... P> +static _FORCE_INLINE_ bool vc_is_const(void (T::*method)(P...) const) { + return true; +} + +template <class R, class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_base_type(R (T::*method)(P...)) { + return GetTypeInfo<T>::VARIANT_TYPE; +} +template <class R, class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_base_type(R (T::*method)(P...) const) { + return GetTypeInfo<T>::VARIANT_TYPE; +} + +template <class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_base_type(void (T::*method)(P...)) { + return GetTypeInfo<T>::VARIANT_TYPE; +} + +template <class T, class... P> +static _FORCE_INLINE_ Variant::Type vc_get_base_type(void (T::*method)(P...) const) { + return GetTypeInfo<T>::VARIANT_TYPE; +} + +#define METHOD_CLASS(m_class, m_method_name, m_method_ptr) \ + struct Method_##m_class##_##m_method_name { \ + static void call(Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \ + vc_method_call(m_method_ptr, base, p_args, p_argcount, r_ret, p_defvals, r_error); \ + } \ + static void validated_call(Variant *base, const Variant **p_args, int p_argcount, Variant *r_ret) { \ + vc_change_return_type(m_method_ptr, r_ret); \ + vc_validated_call(m_method_ptr, base, p_args, r_ret); \ + } \ + static void ptrcall(void *p_base, const void **p_args, void *r_ret, int p_argcount) { \ + vc_ptrcall(m_method_ptr, p_base, p_args, r_ret); \ + } \ + static int get_argument_count() { \ + return vc_get_argument_count(m_method_ptr); \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return vc_get_argument_type(m_method_ptr, p_arg); \ + } \ + static Variant::Type get_return_type() { \ + return vc_get_return_type(m_method_ptr); \ + } \ + static bool has_return_type() { \ + return vc_has_return_type(m_method_ptr); \ + } \ + static bool is_const() { \ + return vc_is_const(m_method_ptr); \ + } \ + static bool is_vararg() { \ + return false; \ + } \ + static Variant::Type get_base_type() { \ + return vc_get_base_type(m_method_ptr); \ + } \ + static StringName get_name() { \ + return #m_method_name; \ + } \ + }; + +template <class R, class T, class... P> +static _FORCE_INLINE_ void vc_ptrcall(R (*method)(T *, P...), void *p_base, const void **p_args, void *r_ret) { + call_with_ptr_args_static_retc<T, R, P...>(reinterpret_cast<T *>(p_base), method, p_args, r_ret); +} + +#define FUNCTION_CLASS(m_class, m_method_name, m_method_ptr) \ + struct Method_##m_class##_##m_method_name { \ + static void call(Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \ + call_with_variant_args_retc_static_helper_dv(VariantGetInternalPtr<m_class>::get_ptr(base), m_method_ptr, p_args, p_argcount, r_ret, p_defvals, r_error); \ + } \ + static void validated_call(Variant *base, const Variant **p_args, int p_argcount, Variant *r_ret) { \ + vc_change_return_type(m_method_ptr, r_ret); \ + call_with_validated_variant_args_static_retc(base, m_method_ptr, p_args, r_ret); \ + } \ + static void ptrcall(void *p_base, const void **p_args, void *r_ret, int p_argcount) { \ + vc_ptrcall(m_method_ptr, p_base, p_args, r_ret); \ + } \ + static int get_argument_count() { \ + return vc_get_argument_count(m_method_ptr); \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return vc_get_argument_type(m_method_ptr, p_arg); \ + } \ + static Variant::Type get_return_type() { \ + return vc_get_return_type(m_method_ptr); \ + } \ + static bool has_return_type() { \ + return true; \ + } \ + static bool is_const() { \ + return true; \ + } \ + static bool is_vararg() { \ + return false; \ + } \ + static Variant::Type get_base_type() { \ + return GetTypeInfo<m_class>::VARIANT_TYPE; \ + } \ + static StringName get_name() { \ + return #m_method_name; \ + } \ + }; + +#define VARARG_CLASS(m_class, m_method_name, m_method_ptr, m_has_return, m_return_type) \ + struct Method_##m_class##_##m_method_name { \ + static void call(Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \ + m_method_ptr(base, p_args, p_argcount, r_ret, r_error); \ + } \ + static void validated_call(Variant *base, const Variant **p_args, int p_argcount, Variant *r_ret) { \ + Callable::CallError ce; \ + m_method_ptr(base, p_args, p_argcount, *r_ret, ce); \ + } \ + static void ptrcall(void *p_base, const void **p_args, void *r_ret, int p_argcount) { \ + LocalVector<Variant> vars; \ + vars.resize(p_argcount); \ + LocalVector<const Variant *> vars_ptrs; \ + vars_ptrs.resize(p_argcount); \ + for (int i = 0; i < p_argcount; i++) { \ + vars[i] = PtrToArg<Variant>::convert(p_args[i]); \ + vars_ptrs[i] = &vars[i]; \ + } \ + Variant base = PtrToArg<m_class>::convert(p_base); \ + Variant ret; \ + Callable::CallError ce; \ + m_method_ptr(&base, (const Variant **)&vars_ptrs[0], p_argcount, ret, ce); \ + if (m_has_return) { \ + m_return_type r = ret; \ + PtrToArg<m_return_type>::encode(ret, r_ret); \ + } \ + } \ + static int get_argument_count() { \ + return 0; \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return Variant::NIL; \ + } \ + static Variant::Type get_return_type() { \ + return GetTypeInfo<m_return_type>::VARIANT_TYPE; \ + } \ + static bool has_return_type() { \ + return m_has_return; \ + } \ + static bool is_const() { \ + return true; \ + } \ + static bool is_vararg() { \ + return true; \ + } \ + static Variant::Type get_base_type() { \ + return GetTypeInfo<m_class>::VARIANT_TYPE; \ + } \ + static StringName get_name() { \ + return #m_method_name; \ + } \ + }; + +struct _VariantCall { + static String func_PackedByteArray_get_string_from_ascii(PackedByteArray *p_instance) { + String s; + if (p_instance->size() > 0) { + const uint8_t *r = p_instance->ptr(); + CharString cs; + cs.resize(p_instance->size() + 1); + copymem(cs.ptrw(), r, p_instance->size()); + cs[p_instance->size()] = 0; + + s = cs.get_data(); + } + return s; + } + + static String func_PackedByteArray_get_string_from_utf8(PackedByteArray *p_instance) { + String s; + if (p_instance->size() > 0) { + const uint8_t *r = p_instance->ptr(); + s.parse_utf8((const char *)r, p_instance->size()); + } + return s; + } + + static String func_PackedByteArray_get_string_from_utf16(PackedByteArray *p_instance) { + String s; + if (p_instance->size() > 0) { + const uint8_t *r = p_instance->ptr(); + s.parse_utf16((const char16_t *)r, floor((double)p_instance->size() / (double)sizeof(char16_t))); + } + return s; + } + + static String func_PackedByteArray_get_string_from_utf32(PackedByteArray *p_instance) { + String s; + if (p_instance->size() > 0) { + const uint8_t *r = p_instance->ptr(); + s = String((const char32_t *)r, floor((double)p_instance->size() / (double)sizeof(char32_t))); + } + return s; + } + + static PackedByteArray func_PackedByteArray_compress(PackedByteArray *p_instance, int p_mode) { + PackedByteArray compressed; + + if (p_instance->size() > 0) { + Compression::Mode mode = (Compression::Mode)(p_mode); + compressed.resize(Compression::get_max_compressed_buffer_size(p_instance->size(), mode)); + int result = Compression::compress(compressed.ptrw(), p_instance->ptr(), p_instance->size(), mode); + + result = result >= 0 ? result : 0; + compressed.resize(result); + } + + return compressed; + } + + static PackedByteArray func_PackedByteArray_decompress(PackedByteArray *p_instance, int64_t p_buffer_size, int p_mode) { + PackedByteArray decompressed; + Compression::Mode mode = (Compression::Mode)(p_mode); + + int64_t buffer_size = p_buffer_size; + + if (buffer_size <= 0) { + ERR_FAIL_V_MSG(decompressed, "Decompression buffer size must be greater than zero."); + } + + decompressed.resize(buffer_size); + int result = Compression::decompress(decompressed.ptrw(), buffer_size, p_instance->ptr(), p_instance->size(), mode); + + result = result >= 0 ? result : 0; + decompressed.resize(result); + + return decompressed; + } + + static PackedByteArray func_PackedByteArray_decompress_dynamic(PackedByteArray *p_instance, int64_t p_buffer_size, int p_mode) { + PackedByteArray decompressed; + int64_t max_output_size = p_buffer_size; + Compression::Mode mode = (Compression::Mode)(p_mode); + + int result = Compression::decompress_dynamic(&decompressed, max_output_size, p_instance->ptr(), p_instance->size(), mode); + + if (result == OK) { + return decompressed; + } else { + decompressed.clear(); + ERR_FAIL_V_MSG(decompressed, "Decompression failed."); + } + } + + static String func_PackedByteArray_hex_encode(PackedByteArray *p_instance) { + if (p_instance->size() == 0) { + return String(); + } + const uint8_t *r = p_instance->ptr(); + String s = String::hex_encode_buffer(&r[0], p_instance->size()); + return s; + } + + static void func_Callable_call(Variant *v, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { + Callable *callable = VariantGetInternalPtr<Callable>::get_ptr(v); + callable->call(p_args, p_argcount, r_ret, r_error); + } + + static void func_Callable_call_deferred(Variant *v, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { + Callable *callable = VariantGetInternalPtr<Callable>::get_ptr(v); + callable->call_deferred(p_args, p_argcount); + } + + static void func_Callable_bind(Variant *v, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { + Callable *callable = VariantGetInternalPtr<Callable>::get_ptr(v); + r_ret = callable->bind(p_args, p_argcount); + } + + static void func_Signal_emit(Variant *v, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { + Signal *signal = VariantGetInternalPtr<Signal>::get_ptr(v); + signal->emit(p_args, p_argcount); + } + + struct ConstantData { + Map<StringName, int> value; +#ifdef DEBUG_ENABLED + List<StringName> value_ordered; +#endif + Map<StringName, Variant> variant_value; +#ifdef DEBUG_ENABLED + List<StringName> variant_value_ordered; +#endif + }; + + static ConstantData *constant_data; + + static void add_constant(int p_type, StringName p_constant_name, int p_constant_value) { + constant_data[p_type].value[p_constant_name] = p_constant_value; +#ifdef DEBUG_ENABLED + constant_data[p_type].value_ordered.push_back(p_constant_name); +#endif + } + + static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) { + constant_data[p_type].variant_value[p_constant_name] = p_constant_value; +#ifdef DEBUG_ENABLED + constant_data[p_type].variant_value_ordered.push_back(p_constant_name); +#endif + } +}; + +_VariantCall::ConstantData *_VariantCall::constant_data = nullptr; + +struct VariantBuiltInMethodInfo { + void (*call)(Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error); + Variant::ValidatedBuiltInMethod validated_call; + Variant::PTRBuiltInMethod ptrcall; + + Vector<Variant> default_arguments; + Vector<String> argument_names; + + bool is_const; + bool has_return_type; + bool is_vararg; + Variant::Type return_type; + int argument_count; + Variant::Type (*get_argument_type)(int p_arg); +}; + +typedef OAHashMap<StringName, VariantBuiltInMethodInfo> BuiltinMethodMap; +static BuiltinMethodMap *builtin_method_info; +static List<StringName> *builtin_method_names; + +template <class T> +static void register_builtin_method(const Vector<String> &p_argnames, const Vector<Variant> &p_def_args) { + StringName name = T::get_name(); + + ERR_FAIL_COND(builtin_method_info[T::get_base_type()].has(name)); + + VariantBuiltInMethodInfo imi; + + imi.call = T::call; + imi.validated_call = T::validated_call; + if (T::is_vararg()) { + imi.ptrcall = nullptr; + } else { + imi.ptrcall = T::ptrcall; + } + + imi.default_arguments = p_def_args; + imi.argument_names = p_argnames; + + imi.is_const = T::is_const(); + imi.is_vararg = T::is_vararg(); + imi.has_return_type = T::has_return_type(); + imi.return_type = T::get_return_type(); + imi.argument_count = T::get_argument_count(); + imi.get_argument_type = T::get_argument_type; +#ifdef DEBUG_METHODS_ENABLED + ERR_FAIL_COND(!imi.is_vararg && imi.argument_count != imi.argument_names.size()); +#endif + + builtin_method_info[T::get_base_type()].insert(name, imi); + builtin_method_names[T::get_base_type()].push_back(name); +} + +void Variant::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { + if (type == Variant::OBJECT) { + //call object + Object *obj = _get_obj().obj; + if (!obj) { + r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; + } +#ifdef DEBUG_ENABLED + if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; + } + +#endif + r_ret = _get_obj().obj->call(p_method, p_args, p_argcount, r_error); + + //else if (type==Variant::METHOD) { + } else { + r_error.error = Callable::CallError::CALL_OK; + + const VariantBuiltInMethodInfo *imf = builtin_method_info[type].lookup_ptr(p_method); + + if (!imf) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return; + } + + imf->call(this, p_args, p_argcount, r_ret, imf->default_arguments, r_error); + } +} + +bool Variant::has_method(const StringName &p_method) const { + if (type == OBJECT) { + Object *obj = get_validated_object(); + if (!obj) { + return false; + } + + return obj->has_method(p_method); + } + + return builtin_method_info[type].has(p_method); +} + +bool Variant::has_builtin_method(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); + return builtin_method_info[p_type].has(p_method); +} + +Variant::ValidatedBuiltInMethod Variant::get_validated_builtin_method(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, nullptr); + return method->validated_call; +} + +Variant::PTRBuiltInMethod Variant::get_ptr_builtin_method(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, nullptr); + return method->ptrcall; +} + +int Variant::get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, 0); + return method->argument_count; +} + +Variant::Type Variant::get_builtin_method_argument_type(Variant::Type p_type, const StringName &p_method, int p_argument) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::NIL); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, Variant::NIL); + ERR_FAIL_INDEX_V(p_argument, method->argument_count, Variant::NIL); + return method->get_argument_type(p_argument); +} + +String Variant::get_builtin_method_argument_name(Variant::Type p_type, const StringName &p_method, int p_argument) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, String()); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, String()); +#ifdef DEBUG_METHODS_ENABLED + ERR_FAIL_INDEX_V(p_argument, method->argument_count, String()); + return method->argument_names[p_argument]; +#else + return "arg" + itos(p_argument + 1); +#endif +} + +Vector<Variant> Variant::get_builtin_method_default_arguments(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Vector<Variant>()); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, Vector<Variant>()); + return method->default_arguments; +} + +bool Variant::has_builtin_method_return_value(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, false); + return method->has_return_type; +} + +void Variant::get_builtin_method_list(Variant::Type p_type, List<StringName> *p_list) { + ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); + for (List<StringName>::Element *E = builtin_method_names[p_type].front(); E; E = E->next()) { + p_list->push_back(E->get()); + } +} + +int Variant::get_builtin_method_count(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, -1); + return builtin_method_names[p_type].size(); +} + +Variant::Type Variant::get_builtin_method_return_type(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::NIL); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, Variant::NIL); + return method->return_type; +} + +bool Variant::is_builtin_method_const(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, false); + return method->is_const; +} + +bool Variant::is_builtin_method_vararg(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, false); + return method->is_vararg; +} + +void Variant::get_method_list(List<MethodInfo> *p_list) const { + if (type == OBJECT) { + Object *obj = get_validated_object(); + if (obj) { + obj->get_method_list(p_list); + } + } else { + for (List<StringName>::Element *E = builtin_method_names[type].front(); E; E = E->next()) { + const VariantBuiltInMethodInfo *method = builtin_method_info[type].lookup_ptr(E->get()); + ERR_CONTINUE(!method); + + MethodInfo mi; + mi.name = E->get(); + + //return type + if (method->has_return_type) { + mi.return_val.type = method->return_type; + if (mi.return_val.type == Variant::NIL) { + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + } + + if (method->is_const) { + mi.flags |= METHOD_FLAG_CONST; + } + if (method->is_vararg) { + mi.flags |= METHOD_FLAG_VARARG; + } + + for (int i = 0; i < method->argument_count; i++) { + PropertyInfo pi; +#ifdef DEBUG_METHODS_ENABLED + pi.name = method->argument_names[i]; +#else + pi.name = "arg" + itos(i + 1); +#endif + pi.type = method->get_argument_type(i); + if (pi.type == Variant::NIL) { + pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + mi.arguments.push_back(pi); + } + + mi.default_arguments = method->default_arguments; + p_list->push_back(mi); + } + } +} + +void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { + ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); + + _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; + +#ifdef DEBUG_ENABLED + for (List<StringName>::Element *E = cd.value_ordered.front(); E; E = E->next()) { + p_constants->push_back(E->get()); +#else + for (Map<StringName, int>::Element *E = cd.value.front(); E; E = E->next()) { + p_constants->push_back(E->key()); +#endif + } + +#ifdef DEBUG_ENABLED + for (List<StringName>::Element *E = cd.variant_value_ordered.front(); E; E = E->next()) { + p_constants->push_back(E->get()); +#else + for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) { + p_constants->push_back(E->key()); +#endif + } +} + +int Variant::get_constants_count_for_type(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, -1); + _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; + + return cd.value.size() + cd.variant_value.size(); +} + +bool Variant::has_constant(Variant::Type p_type, const StringName &p_value) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); + _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; + return cd.value.has(p_value) || cd.variant_value.has(p_value); +} + +Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { + if (r_valid) { + *r_valid = false; + } + + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0); + _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; + + Map<StringName, int>::Element *E = cd.value.find(p_value); + if (!E) { + Map<StringName, Variant>::Element *F = cd.variant_value.find(p_value); + if (F) { + if (r_valid) { + *r_valid = true; + } + return F->get(); + } else { + return -1; + } + } + if (r_valid) { + *r_valid = true; + } + + return E->get(); +} + +#ifdef DEBUG_METHODS_ENABLED +#define bind_method(m_type, m_method, m_arg_names, m_default_args) \ + METHOD_CLASS(m_type, m_method, &m_type::m_method); \ + register_builtin_method<Method_##m_type##_##m_method>(m_arg_names, m_default_args); +#else +#define bind_method(m_type, m_method, m_arg_names, m_default_args) \ + METHOD_CLASS(m_type, m_method, &m_type ::m_method); \ + register_builtin_method<Method_##m_type##_##m_method>(sarray(), m_default_args); +#endif + +#ifdef DEBUG_METHODS_ENABLED +#define bind_methodv(m_type, m_name, m_method, m_arg_names, m_default_args) \ + METHOD_CLASS(m_type, m_name, m_method); \ + register_builtin_method<Method_##m_type##_##m_name>(m_arg_names, m_default_args); +#else +#define bind_methodv(m_type, m_name, m_method, m_arg_names, m_default_args) \ + METHOD_CLASS(m_type, m_name, m_method); \ + register_builtin_method<Method_##m_type##_##m_name>(sarray(), m_default_args); +#endif + +#ifdef DEBUG_METHODS_ENABLED +#define bind_function(m_type, m_name, m_method, m_arg_names, m_default_args) \ + FUNCTION_CLASS(m_type, m_name, m_method); \ + register_builtin_method<Method_##m_type##_##m_name>(m_arg_names, m_default_args); +#else +#define bind_function(m_type, m_name, m_method, m_arg_names, m_default_args) \ + FUNCTION_CLASS(m_type, m_name, m_method); \ + register_builtin_method<Method_##m_type##_##m_name>(sarray(), m_default_args); +#endif + +#define bind_custom(m_type, m_name, m_method, m_has_return, m_ret_type) \ + VARARG_CLASS(m_type, m_name, m_method, m_has_return, m_ret_type) \ + register_builtin_method<Method_##m_type##_##m_name>(sarray(), Vector<Variant>()); + +static void _register_variant_builtin_methods() { + _VariantCall::constant_data = memnew_arr(_VariantCall::ConstantData, Variant::VARIANT_MAX); + builtin_method_info = memnew_arr(BuiltinMethodMap, Variant::VARIANT_MAX); + builtin_method_names = memnew_arr(List<StringName>, Variant::VARIANT_MAX); + + /* String */ + + bind_method(String, casecmp_to, sarray("to"), varray()); + bind_method(String, nocasecmp_to, sarray("to"), varray()); + bind_method(String, naturalnocasecmp_to, sarray("to"), varray()); + bind_method(String, length, sarray(), varray()); + bind_method(String, substr, sarray("from", "len"), varray(-1)); + bind_methodv(String, find, static_cast<int (String::*)(const String &, int) const>(&String::find), sarray("what", "from"), varray(0)); + bind_method(String, count, sarray("what", "from", "to"), varray(0, 0)); + bind_method(String, countn, sarray("what", "from", "to"), varray(0, 0)); + bind_method(String, findn, sarray("what", "from"), varray(0)); + bind_method(String, rfind, sarray("what", "from"), varray(-1)); + bind_method(String, rfindn, sarray("what", "from"), varray(-1)); + bind_method(String, match, sarray("expr"), varray()); + bind_method(String, matchn, sarray("expr"), varray()); + bind_methodv(String, begins_with, static_cast<bool (String::*)(const String &) const>(&String::begins_with), sarray("text"), varray()); + bind_method(String, ends_with, sarray("text"), varray()); + bind_method(String, is_subsequence_of, sarray("text"), varray()); + bind_method(String, is_subsequence_ofi, sarray("text"), varray()); + bind_method(String, bigrams, sarray(), varray()); + bind_method(String, similarity, sarray("text"), varray()); + + bind_method(String, format, sarray("values", "placeholder"), varray("{_}")); + bind_methodv(String, replace, static_cast<String (String::*)(const String &, const String &) const>(&String::replace), sarray("what", "forwhat"), varray()); + bind_method(String, replacen, sarray("what", "forwhat"), varray()); + bind_method(String, repeat, sarray("count"), varray()); + bind_method(String, insert, sarray("position", "what"), varray()); + bind_method(String, capitalize, sarray(), varray()); + bind_method(String, split, sarray("delimiter", "allow_empty", "maxsplit"), varray(true, 0)); + bind_method(String, rsplit, sarray("delimiter", "allow_empty", "maxsplit"), varray(true, 0)); + bind_method(String, split_floats, sarray("delimiter", "allow_empty"), varray(true)); + bind_method(String, join, sarray("parts"), varray()); + + bind_method(String, to_upper, sarray(), varray()); + bind_method(String, to_lower, sarray(), varray()); + + bind_method(String, left, sarray("position"), varray()); + bind_method(String, right, sarray("position"), varray()); + + bind_method(String, strip_edges, sarray("left", "right"), varray(true, true)); + bind_method(String, strip_escapes, sarray(), varray()); + bind_method(String, lstrip, sarray("chars"), varray()); + bind_method(String, rstrip, sarray("chars"), varray()); + bind_method(String, get_extension, sarray(), varray()); + bind_method(String, get_basename, sarray(), varray()); + bind_method(String, plus_file, sarray("file"), varray()); + bind_method(String, ord_at, sarray("at"), varray()); + bind_method(String, dedent, sarray(), varray()); + // FIXME: String needs to be immutable when binding + //bind_method(String, erase, sarray("position", "chars"), varray()); + bind_method(String, hash, sarray(), varray()); + bind_method(String, md5_text, sarray(), varray()); + bind_method(String, sha1_text, sarray(), varray()); + bind_method(String, sha256_text, sarray(), varray()); + bind_method(String, md5_buffer, sarray(), varray()); + bind_method(String, sha1_buffer, sarray(), varray()); + bind_method(String, sha256_buffer, sarray(), varray()); + bind_method(String, is_empty, sarray(), varray()); + // FIXME: Static function, not sure how to bind + //bind_method(String, humanize_size, sarray("size"), varray()); + + bind_method(String, is_abs_path, sarray(), varray()); + bind_method(String, is_rel_path, sarray(), varray()); + bind_method(String, get_base_dir, sarray(), varray()); + bind_method(String, get_file, sarray(), varray()); + bind_method(String, xml_escape, sarray("escape_quotes"), varray(false)); + bind_method(String, xml_unescape, sarray(), varray()); + bind_method(String, http_escape, sarray(), varray()); + bind_method(String, http_unescape, sarray(), varray()); + bind_method(String, c_escape, sarray(), varray()); + bind_method(String, c_unescape, sarray(), varray()); + bind_method(String, json_escape, sarray(), varray()); + bind_method(String, percent_encode, sarray(), varray()); + bind_method(String, percent_decode, sarray(), varray()); + + bind_method(String, is_valid_identifier, sarray(), varray()); + bind_method(String, is_valid_integer, sarray(), varray()); + bind_method(String, is_valid_float, sarray(), varray()); + bind_method(String, is_valid_hex_number, sarray("with_prefix"), varray(false)); + bind_method(String, is_valid_html_color, sarray(), varray()); + bind_method(String, is_valid_ip_address, sarray(), varray()); + bind_method(String, is_valid_filename, sarray(), varray()); + + bind_method(String, to_int, sarray(), varray()); + bind_method(String, to_float, sarray(), varray()); + bind_method(String, hex_to_int, sarray("with_prefix"), varray(true)); + bind_method(String, bin_to_int, sarray("with_prefix"), varray(true)); + + bind_method(String, lpad, sarray("min_length", "character"), varray(" ")); + bind_method(String, rpad, sarray("min_length", "character"), varray(" ")); + bind_method(String, pad_decimals, sarray("digits"), varray()); + bind_method(String, pad_zeros, sarray("digits"), varray()); + bind_method(String, trim_prefix, sarray("prefix"), varray()); + bind_method(String, trim_suffix, sarray("suffix"), varray()); + + bind_method(String, to_ascii_buffer, sarray(), varray()); + bind_method(String, to_utf8_buffer, sarray(), varray()); + bind_method(String, to_utf16_buffer, sarray(), varray()); + bind_method(String, to_utf32_buffer, sarray(), varray()); + + /* Vector2 */ + + bind_method(Vector2, angle, sarray(), varray()); + bind_method(Vector2, angle_to, sarray("to"), varray()); + bind_method(Vector2, angle_to_point, sarray("to"), varray()); + bind_method(Vector2, direction_to, sarray("b"), varray()); + bind_method(Vector2, distance_to, sarray("to"), varray()); + bind_method(Vector2, distance_squared_to, sarray("to"), varray()); + bind_method(Vector2, length, sarray(), varray()); + bind_method(Vector2, length_squared, sarray(), varray()); + bind_method(Vector2, normalized, sarray(), varray()); + bind_method(Vector2, is_normalized, sarray(), varray()); + bind_method(Vector2, is_equal_approx, sarray("to"), varray()); + bind_method(Vector2, posmod, sarray("mod"), varray()); + bind_method(Vector2, posmodv, sarray("modv"), varray()); + bind_method(Vector2, project, sarray("b"), varray()); + bind_method(Vector2, lerp, sarray("to", "weight"), varray()); + bind_method(Vector2, slerp, sarray("to", "weight"), varray()); + bind_method(Vector2, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray()); + bind_method(Vector2, move_toward, sarray("to", "delta"), varray()); + bind_method(Vector2, rotated, sarray("phi"), varray()); + bind_method(Vector2, orthogonal, sarray(), varray()); + bind_method(Vector2, floor, sarray(), varray()); + bind_method(Vector2, ceil, sarray(), varray()); + bind_method(Vector2, round, sarray(), varray()); + bind_method(Vector2, aspect, sarray(), varray()); + bind_method(Vector2, dot, sarray("with"), varray()); + bind_method(Vector2, slide, sarray("n"), varray()); + bind_method(Vector2, bounce, sarray("n"), varray()); + bind_method(Vector2, reflect, sarray("n"), varray()); + bind_method(Vector2, cross, sarray("with"), varray()); + bind_method(Vector2, abs, sarray(), varray()); + bind_method(Vector2, sign, sarray(), varray()); + bind_method(Vector2, snapped, sarray("step"), varray()); + bind_method(Vector2, clamped, sarray("length"), varray()); + + /* Vector2i */ + + bind_method(Vector2i, aspect, sarray(), varray()); + bind_method(Vector2i, sign, sarray(), varray()); + bind_method(Vector2i, abs, sarray(), varray()); + + /* Rect2 */ + + bind_method(Rect2, get_area, sarray(), varray()); + bind_method(Rect2, has_no_area, sarray(), varray()); + bind_method(Rect2, has_point, sarray("point"), varray()); + bind_method(Rect2, is_equal_approx, sarray("rect"), varray()); + bind_method(Rect2, intersects, sarray("b", "include_borders"), varray(false)); + bind_method(Rect2, encloses, sarray("b"), varray()); + bind_method(Rect2, intersection, sarray("b"), varray()); + bind_method(Rect2, merge, sarray("b"), varray()); + bind_method(Rect2, expand, sarray("to"), varray()); + bind_method(Rect2, grow, sarray("amount"), varray()); + bind_methodv(Rect2, grow_side, &Rect2::grow_side_bind, sarray("side", "amount"), varray()); + bind_method(Rect2, grow_individual, sarray("left", "top", "right", "bottom"), varray()); + bind_method(Rect2, abs, sarray(), varray()); + + /* Rect2i */ + + bind_method(Rect2i, get_area, sarray(), varray()); + bind_method(Rect2i, has_no_area, sarray(), varray()); + bind_method(Rect2i, has_point, sarray("point"), varray()); + bind_method(Rect2i, intersects, sarray("b"), varray()); + bind_method(Rect2i, encloses, sarray("b"), varray()); + bind_method(Rect2i, intersection, sarray("b"), varray()); + bind_method(Rect2i, merge, sarray("b"), varray()); + bind_method(Rect2i, expand, sarray("to"), varray()); + bind_method(Rect2i, grow, sarray("amount"), varray()); + bind_methodv(Rect2i, grow_side, &Rect2i::grow_side_bind, sarray("side", "amount"), varray()); + bind_method(Rect2i, grow_individual, sarray("left", "top", "right", "bottom"), varray()); + bind_method(Rect2i, abs, sarray(), varray()); + + /* Vector3 */ + + bind_method(Vector3, min_axis, sarray(), varray()); + bind_method(Vector3, max_axis, sarray(), varray()); + bind_method(Vector3, angle_to, sarray("to"), varray()); + bind_method(Vector3, direction_to, sarray("b"), varray()); + bind_method(Vector3, distance_to, sarray("b"), varray()); + bind_method(Vector3, distance_squared_to, sarray("b"), varray()); + bind_method(Vector3, length, sarray(), varray()); + bind_method(Vector3, length_squared, sarray(), varray()); + bind_method(Vector3, normalized, sarray(), varray()); + bind_method(Vector3, is_normalized, sarray(), varray()); + bind_method(Vector3, is_equal_approx, sarray("to"), varray()); + bind_method(Vector3, inverse, sarray(), varray()); + bind_method(Vector3, snapped, sarray("step"), varray()); + bind_method(Vector3, rotated, sarray("by_axis", "phi"), varray()); + bind_method(Vector3, lerp, sarray("to", "weight"), varray()); + bind_method(Vector3, slerp, sarray("to", "weight"), varray()); + bind_method(Vector3, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray()); + bind_method(Vector3, move_toward, sarray("to", "delta"), varray()); + bind_method(Vector3, dot, sarray("with"), varray()); + bind_method(Vector3, cross, sarray("with"), varray()); + bind_method(Vector3, outer, sarray("with"), varray()); + bind_method(Vector3, to_diagonal_matrix, sarray(), varray()); + bind_method(Vector3, abs, sarray(), varray()); + bind_method(Vector3, floor, sarray(), varray()); + bind_method(Vector3, ceil, sarray(), varray()); + bind_method(Vector3, round, sarray(), varray()); + bind_method(Vector3, posmod, sarray("mod"), varray()); + bind_method(Vector3, posmodv, sarray("modv"), varray()); + bind_method(Vector3, project, sarray("b"), varray()); + bind_method(Vector3, slide, sarray("n"), varray()); + bind_method(Vector3, bounce, sarray("n"), varray()); + bind_method(Vector3, reflect, sarray("n"), varray()); + bind_method(Vector3, sign, sarray(), varray()); + + /* Vector3i */ + + bind_method(Vector3i, min_axis, sarray(), varray()); + bind_method(Vector3i, max_axis, sarray(), varray()); + bind_method(Vector3i, sign, sarray(), varray()); + bind_method(Vector3i, abs, sarray(), varray()); + + /* Plane */ + + bind_method(Plane, normalized, sarray(), varray()); + bind_method(Plane, center, sarray(), varray()); + bind_method(Plane, is_equal_approx, sarray("to_plane"), varray()); + bind_method(Plane, is_point_over, sarray("plane"), varray()); + bind_method(Plane, distance_to, sarray("point"), varray()); + bind_method(Plane, has_point, sarray("point", "epsilon"), varray(CMP_EPSILON)); + bind_method(Plane, project, sarray("point"), varray()); + bind_methodv(Plane, intersect_3, &Plane::intersect_3_bind, sarray("b", "c"), varray()); + bind_methodv(Plane, intersects_ray, &Plane::intersects_ray_bind, sarray("from", "dir"), varray()); + bind_methodv(Plane, intersects_segment, &Plane::intersects_segment_bind, sarray("from", "to"), varray()); + + /* Quat */ + + bind_method(Quat, length, sarray(), varray()); + bind_method(Quat, length_squared, sarray(), varray()); + bind_method(Quat, normalized, sarray(), varray()); + bind_method(Quat, is_normalized, sarray(), varray()); + bind_method(Quat, is_equal_approx, sarray("to"), varray()); + bind_method(Quat, inverse, sarray(), varray()); + bind_method(Quat, dot, sarray("with"), varray()); + bind_method(Quat, slerp, sarray("to", "weight"), varray()); + bind_method(Quat, slerpni, sarray("to", "weight"), varray()); + bind_method(Quat, cubic_slerp, sarray("b", "pre_a", "post_b", "weight"), varray()); + bind_method(Quat, get_euler, sarray(), varray()); + + // FIXME: Quat is atomic, this should be done via construcror + //ADDFUNC1(QUAT, NIL, Quat, set_euler, VECTOR3, "euler", varray()); + //ADDFUNC2(QUAT, NIL, Quat, set_axis_angle, VECTOR3, "axis", FLOAT, "angle", varray()); + + /* Color */ + + bind_method(Color, to_argb32, sarray(), varray()); + bind_method(Color, to_abgr32, sarray(), varray()); + bind_method(Color, to_rgba32, sarray(), varray()); + bind_method(Color, to_argb64, sarray(), varray()); + bind_method(Color, to_abgr64, sarray(), varray()); + bind_method(Color, to_rgba64, sarray(), varray()); + + bind_method(Color, inverted, sarray(), varray()); + bind_method(Color, lerp, sarray("to", "weight"), varray()); + bind_method(Color, lightened, sarray("amount"), varray()); + bind_method(Color, darkened, sarray("amount"), varray()); + bind_method(Color, to_html, sarray("with_alpha"), varray(true)); + bind_method(Color, blend, sarray("over"), varray()); + + // FIXME: Color is immutable, need to probably find a way to do this via constructor + //ADDFUNC4R(COLOR, COLOR, Color, from_hsv, FLOAT, "h", FLOAT, "s", FLOAT, "v", FLOAT, "a", varray(1.0)); + bind_method(Color, is_equal_approx, sarray("to"), varray()); + + /* RID */ + + bind_method(RID, get_id, sarray(), varray()); + + /* NodePath */ + + bind_method(NodePath, is_absolute, sarray(), varray()); + bind_method(NodePath, get_name_count, sarray(), varray()); + bind_method(NodePath, get_name, sarray("idx"), varray()); + bind_method(NodePath, get_subname_count, sarray(), varray()); + bind_method(NodePath, get_subname, sarray("idx"), varray()); + bind_method(NodePath, get_concatenated_subnames, sarray(), varray()); + bind_method(NodePath, get_as_property_path, sarray(), varray()); + bind_method(NodePath, is_empty, sarray(), varray()); + + /* Callable */ + + bind_method(Callable, is_null, sarray(), varray()); + bind_method(Callable, is_custom, sarray(), varray()); + bind_method(Callable, is_standard, sarray(), varray()); + bind_method(Callable, get_object, sarray(), varray()); + bind_method(Callable, get_object_id, sarray(), varray()); + bind_method(Callable, get_method, sarray(), varray()); + bind_method(Callable, hash, sarray(), varray()); + bind_method(Callable, unbind, sarray("argcount"), varray()); + + bind_custom(Callable, call, _VariantCall::func_Callable_call, true, Variant); + bind_custom(Callable, call_deferred, _VariantCall::func_Callable_call_deferred, false, Variant); + bind_custom(Callable, bind, _VariantCall::func_Callable_bind, true, Callable); + + /* Signal */ + + bind_method(Signal, is_null, sarray(), varray()); + bind_method(Signal, get_object, sarray(), varray()); + bind_method(Signal, get_object_id, sarray(), varray()); + bind_method(Signal, get_name, sarray(), varray()); + + bind_method(Signal, connect, sarray("callable", "binds", "flags"), varray(Array(), 0)); + bind_method(Signal, disconnect, sarray("callable"), varray()); + bind_method(Signal, is_connected, sarray("callable"), varray()); + bind_method(Signal, get_connections, sarray(), varray()); + + bind_custom(Signal, emit, _VariantCall::func_Signal_emit, false, Variant); + + /* Transform2D */ + + bind_method(Transform2D, inverse, sarray(), varray()); + bind_method(Transform2D, affine_inverse, sarray(), varray()); + bind_method(Transform2D, get_rotation, sarray(), varray()); + bind_method(Transform2D, get_origin, sarray(), varray()); + bind_method(Transform2D, get_scale, sarray(), varray()); + bind_method(Transform2D, orthonormalized, sarray(), varray()); + bind_method(Transform2D, rotated, sarray("phi"), varray()); + bind_method(Transform2D, scaled, sarray("scale"), varray()); + bind_method(Transform2D, translated, sarray("offset"), varray()); + bind_method(Transform2D, basis_xform, sarray("v"), varray()); + bind_method(Transform2D, basis_xform_inv, sarray("v"), varray()); + bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray()); + bind_method(Transform2D, is_equal_approx, sarray("xform"), varray()); + + /* Basis */ + + bind_method(Basis, inverse, sarray(), varray()); + bind_method(Basis, transposed, sarray(), varray()); + bind_method(Basis, orthonormalized, sarray(), varray()); + bind_method(Basis, determinant, sarray(), varray()); + bind_methodv(Basis, rotated, static_cast<Basis (Basis::*)(const Vector3 &, float) const>(&Basis::rotated), sarray("axis", "phi"), varray()); + bind_method(Basis, scaled, sarray("scale"), varray()); + bind_method(Basis, get_scale, sarray(), varray()); + bind_method(Basis, get_euler, sarray(), varray()); + bind_method(Basis, tdotx, sarray("with"), varray()); + bind_method(Basis, tdoty, sarray("with"), varray()); + bind_method(Basis, tdotz, sarray("with"), varray()); + bind_method(Basis, get_orthogonal_index, sarray(), varray()); + bind_method(Basis, slerp, sarray("to", "weight"), varray()); + bind_method(Basis, is_equal_approx, sarray("b"), varray()); + bind_method(Basis, get_rotation_quat, sarray(), varray()); + + /* AABB */ + + bind_method(AABB, abs, sarray(), varray()); + bind_method(AABB, get_area, sarray(), varray()); + bind_method(AABB, has_no_area, sarray(), varray()); + bind_method(AABB, has_no_surface, sarray(), varray()); + bind_method(AABB, has_point, sarray("point"), varray()); + bind_method(AABB, is_equal_approx, sarray("aabb"), varray()); + bind_method(AABB, intersects, sarray("with"), varray()); + bind_method(AABB, encloses, sarray("with"), varray()); + bind_method(AABB, intersects_plane, sarray("plane"), varray()); + bind_method(AABB, intersection, sarray("with"), varray()); + bind_method(AABB, merge, sarray("with"), varray()); + bind_method(AABB, expand, sarray("to_point"), varray()); + bind_method(AABB, grow, sarray("by"), varray()); + bind_method(AABB, get_support, sarray("dir"), varray()); + bind_method(AABB, get_longest_axis, sarray(), varray()); + bind_method(AABB, get_longest_axis_index, sarray(), varray()); + bind_method(AABB, get_longest_axis_size, sarray(), varray()); + bind_method(AABB, get_shortest_axis, sarray(), varray()); + bind_method(AABB, get_shortest_axis_index, sarray(), varray()); + bind_method(AABB, get_shortest_axis_size, sarray(), varray()); + bind_method(AABB, get_endpoint, sarray("idx"), varray()); + bind_methodv(AABB, intersects_segment, &AABB::intersects_segment_bind, sarray("from", "to"), varray()); + bind_methodv(AABB, intersects_ray, &AABB::intersects_ray_bind, sarray("from", "dir"), varray()); + + /* Transform */ + + bind_method(Transform, inverse, sarray(), varray()); + bind_method(Transform, affine_inverse, sarray(), varray()); + bind_method(Transform, orthonormalized, sarray(), varray()); + bind_method(Transform, rotated, sarray("axis", "phi"), varray()); + bind_method(Transform, scaled, sarray("scale"), varray()); + bind_method(Transform, translated, sarray("offset"), varray()); + bind_method(Transform, looking_at, sarray("target", "up"), varray()); + bind_method(Transform, interpolate_with, sarray("xform", "weight"), varray()); + bind_method(Transform, is_equal_approx, sarray("xform"), varray()); + + /* Dictionary */ + + bind_method(Dictionary, size, sarray(), varray()); + bind_method(Dictionary, is_empty, sarray(), varray()); + bind_method(Dictionary, clear, sarray(), varray()); + bind_method(Dictionary, has, sarray("key"), varray()); + bind_method(Dictionary, has_all, sarray("keys"), varray()); + bind_method(Dictionary, erase, sarray("key"), varray()); + bind_method(Dictionary, hash, sarray(), varray()); + bind_method(Dictionary, keys, sarray(), varray()); + bind_method(Dictionary, values, sarray(), varray()); + bind_method(Dictionary, duplicate, sarray("deep"), varray(false)); + bind_method(Dictionary, get, sarray("key", "default"), varray(Variant())); + + /* Array */ + + bind_method(Array, size, sarray(), varray()); + bind_method(Array, is_empty, sarray(), varray()); + bind_method(Array, clear, sarray(), varray()); + bind_method(Array, hash, sarray(), varray()); + bind_method(Array, push_back, sarray("value"), varray()); + bind_method(Array, push_front, sarray("value"), varray()); + bind_method(Array, append, sarray("value"), varray()); + bind_method(Array, append_array, sarray("array"), varray()); + bind_method(Array, resize, sarray("size"), varray()); + bind_method(Array, insert, sarray("position", "value"), varray()); + bind_method(Array, remove, sarray("position"), varray()); + bind_method(Array, erase, sarray("value"), varray()); + bind_method(Array, front, sarray(), varray()); + bind_method(Array, back, sarray(), varray()); + bind_method(Array, find, sarray("what", "from"), varray(0)); + bind_method(Array, rfind, sarray("what", "from"), varray(-1)); + bind_method(Array, find_last, sarray("value"), varray()); + bind_method(Array, count, sarray("value"), varray()); + bind_method(Array, has, sarray("value"), varray()); + bind_method(Array, pop_back, sarray(), varray()); + bind_method(Array, pop_front, sarray(), varray()); + bind_method(Array, sort, sarray(), varray()); + bind_method(Array, sort_custom, sarray("obj", "func"), varray()); + bind_method(Array, shuffle, sarray(), varray()); + bind_method(Array, bsearch, sarray("value", "before"), varray(true)); + bind_method(Array, bsearch_custom, sarray("value", "obj", "func", "before"), varray(true)); + bind_method(Array, invert, sarray(), varray()); + bind_method(Array, duplicate, sarray("deep"), varray(false)); + bind_method(Array, slice, sarray("begin", "end", "step", "deep"), varray(1, false)); + bind_method(Array, max, sarray(), varray()); + bind_method(Array, min, sarray(), varray()); + + /* Byte Array */ + bind_method(PackedByteArray, size, sarray(), varray()); + bind_method(PackedByteArray, is_empty, sarray(), varray()); + bind_method(PackedByteArray, set, sarray("index", "value"), varray()); + bind_method(PackedByteArray, push_back, sarray("value"), varray()); + bind_method(PackedByteArray, append, sarray("value"), varray()); + bind_method(PackedByteArray, append_array, sarray("array"), varray()); + bind_method(PackedByteArray, remove, sarray("index"), varray()); + bind_method(PackedByteArray, insert, sarray("at_index", "value"), varray()); + bind_method(PackedByteArray, resize, sarray("new_size"), varray()); + bind_method(PackedByteArray, has, sarray("value"), varray()); + bind_method(PackedByteArray, invert, sarray(), varray()); + bind_method(PackedByteArray, subarray, sarray("from", "to"), varray()); + bind_method(PackedByteArray, sort, sarray(), varray()); + bind_method(PackedByteArray, duplicate, sarray(), varray()); + + bind_function(PackedByteArray, get_string_from_ascii, _VariantCall::func_PackedByteArray_get_string_from_ascii, sarray(), varray()); + bind_function(PackedByteArray, get_string_from_utf8, _VariantCall::func_PackedByteArray_get_string_from_utf8, sarray(), varray()); + bind_function(PackedByteArray, get_string_from_utf16, _VariantCall::func_PackedByteArray_get_string_from_utf16, sarray(), varray()); + bind_function(PackedByteArray, get_string_from_utf32, _VariantCall::func_PackedByteArray_get_string_from_utf32, sarray(), varray()); + bind_function(PackedByteArray, hex_encode, _VariantCall::func_PackedByteArray_hex_encode, sarray(), varray()); + bind_function(PackedByteArray, compress, _VariantCall::func_PackedByteArray_compress, sarray("compression_mode"), varray(0)); + bind_function(PackedByteArray, decompress, _VariantCall::func_PackedByteArray_decompress, sarray("buffer_size", "compression_mode"), varray(0)); + bind_function(PackedByteArray, decompress_dynamic, _VariantCall::func_PackedByteArray_decompress_dynamic, sarray("max_output_size", "compression_mode"), varray(0)); + + /* Int32 Array */ + + bind_method(PackedInt32Array, size, sarray(), varray()); + bind_method(PackedInt32Array, is_empty, sarray(), varray()); + bind_method(PackedInt32Array, set, sarray("index", "value"), varray()); + bind_method(PackedInt32Array, push_back, sarray("value"), varray()); + bind_method(PackedInt32Array, append, sarray("value"), varray()); + bind_method(PackedInt32Array, append_array, sarray("array"), varray()); + bind_method(PackedInt32Array, remove, sarray("index"), varray()); + bind_method(PackedInt32Array, insert, sarray("at_index", "value"), varray()); + bind_method(PackedInt32Array, resize, sarray("new_size"), varray()); + bind_method(PackedInt32Array, has, sarray("value"), varray()); + bind_method(PackedInt32Array, invert, sarray(), varray()); + bind_method(PackedInt32Array, subarray, sarray("from", "to"), varray()); + bind_method(PackedInt32Array, to_byte_array, sarray(), varray()); + bind_method(PackedInt32Array, sort, sarray(), varray()); + bind_method(PackedInt32Array, duplicate, sarray(), varray()); + + /* Int64 Array */ + + bind_method(PackedInt64Array, size, sarray(), varray()); + bind_method(PackedInt64Array, is_empty, sarray(), varray()); + bind_method(PackedInt64Array, set, sarray("index", "value"), varray()); + bind_method(PackedInt64Array, push_back, sarray("value"), varray()); + bind_method(PackedInt64Array, append, sarray("value"), varray()); + bind_method(PackedInt64Array, append_array, sarray("array"), varray()); + bind_method(PackedInt64Array, remove, sarray("index"), varray()); + bind_method(PackedInt64Array, insert, sarray("at_index", "value"), varray()); + bind_method(PackedInt64Array, resize, sarray("new_size"), varray()); + bind_method(PackedInt64Array, has, sarray("value"), varray()); + bind_method(PackedInt64Array, invert, sarray(), varray()); + bind_method(PackedInt64Array, subarray, sarray("from", "to"), varray()); + bind_method(PackedInt64Array, to_byte_array, sarray(), varray()); + bind_method(PackedInt64Array, sort, sarray(), varray()); + bind_method(PackedInt64Array, duplicate, sarray(), varray()); + + /* Float32 Array */ + + bind_method(PackedFloat32Array, size, sarray(), varray()); + bind_method(PackedFloat32Array, is_empty, sarray(), varray()); + bind_method(PackedFloat32Array, set, sarray("index", "value"), varray()); + bind_method(PackedFloat32Array, push_back, sarray("value"), varray()); + bind_method(PackedFloat32Array, append, sarray("value"), varray()); + bind_method(PackedFloat32Array, append_array, sarray("array"), varray()); + bind_method(PackedFloat32Array, remove, sarray("index"), varray()); + bind_method(PackedFloat32Array, insert, sarray("at_index", "value"), varray()); + bind_method(PackedFloat32Array, resize, sarray("new_size"), varray()); + bind_method(PackedFloat32Array, has, sarray("value"), varray()); + bind_method(PackedFloat32Array, invert, sarray(), varray()); + bind_method(PackedFloat32Array, subarray, sarray("from", "to"), varray()); + bind_method(PackedFloat32Array, to_byte_array, sarray(), varray()); + bind_method(PackedFloat32Array, sort, sarray(), varray()); + bind_method(PackedFloat32Array, duplicate, sarray(), varray()); + + /* Float64 Array */ + + bind_method(PackedFloat64Array, size, sarray(), varray()); + bind_method(PackedFloat64Array, is_empty, sarray(), varray()); + bind_method(PackedFloat64Array, set, sarray("index", "value"), varray()); + bind_method(PackedFloat64Array, push_back, sarray("value"), varray()); + bind_method(PackedFloat64Array, append, sarray("value"), varray()); + bind_method(PackedFloat64Array, append_array, sarray("array"), varray()); + bind_method(PackedFloat64Array, remove, sarray("index"), varray()); + bind_method(PackedFloat64Array, insert, sarray("at_index", "value"), varray()); + bind_method(PackedFloat64Array, resize, sarray("new_size"), varray()); + bind_method(PackedFloat64Array, has, sarray("value"), varray()); + bind_method(PackedFloat64Array, invert, sarray(), varray()); + bind_method(PackedFloat64Array, subarray, sarray("from", "to"), varray()); + bind_method(PackedFloat64Array, to_byte_array, sarray(), varray()); + bind_method(PackedFloat64Array, sort, sarray(), varray()); + bind_method(PackedFloat64Array, duplicate, sarray(), varray()); + + /* String Array */ + + bind_method(PackedStringArray, size, sarray(), varray()); + bind_method(PackedStringArray, is_empty, sarray(), varray()); + bind_method(PackedStringArray, set, sarray("index", "value"), varray()); + bind_method(PackedStringArray, push_back, sarray("value"), varray()); + bind_method(PackedStringArray, append, sarray("value"), varray()); + bind_method(PackedStringArray, append_array, sarray("array"), varray()); + bind_method(PackedStringArray, remove, sarray("index"), varray()); + bind_method(PackedStringArray, insert, sarray("at_index", "value"), varray()); + bind_method(PackedStringArray, resize, sarray("new_size"), varray()); + bind_method(PackedStringArray, has, sarray("value"), varray()); + bind_method(PackedStringArray, invert, sarray(), varray()); + bind_method(PackedStringArray, subarray, sarray("from", "to"), varray()); + bind_method(PackedStringArray, to_byte_array, sarray(), varray()); + bind_method(PackedStringArray, sort, sarray(), varray()); + bind_method(PackedStringArray, duplicate, sarray(), varray()); + + /* Vector2 Array */ + + bind_method(PackedVector2Array, size, sarray(), varray()); + bind_method(PackedVector2Array, is_empty, sarray(), varray()); + bind_method(PackedVector2Array, set, sarray("index", "value"), varray()); + bind_method(PackedVector2Array, push_back, sarray("value"), varray()); + bind_method(PackedVector2Array, append, sarray("value"), varray()); + bind_method(PackedVector2Array, append_array, sarray("array"), varray()); + bind_method(PackedVector2Array, remove, sarray("index"), varray()); + bind_method(PackedVector2Array, insert, sarray("at_index", "value"), varray()); + bind_method(PackedVector2Array, resize, sarray("new_size"), varray()); + bind_method(PackedVector2Array, has, sarray("value"), varray()); + bind_method(PackedVector2Array, invert, sarray(), varray()); + bind_method(PackedVector2Array, subarray, sarray("from", "to"), varray()); + bind_method(PackedVector2Array, to_byte_array, sarray(), varray()); + bind_method(PackedVector2Array, sort, sarray(), varray()); + bind_method(PackedVector2Array, duplicate, sarray(), varray()); + + /* Vector3 Array */ + + bind_method(PackedVector3Array, size, sarray(), varray()); + bind_method(PackedVector3Array, is_empty, sarray(), varray()); + bind_method(PackedVector3Array, set, sarray("index", "value"), varray()); + bind_method(PackedVector3Array, push_back, sarray("value"), varray()); + bind_method(PackedVector3Array, append, sarray("value"), varray()); + bind_method(PackedVector3Array, append_array, sarray("array"), varray()); + bind_method(PackedVector3Array, remove, sarray("index"), varray()); + bind_method(PackedVector3Array, insert, sarray("at_index", "value"), varray()); + bind_method(PackedVector3Array, resize, sarray("new_size"), varray()); + bind_method(PackedVector3Array, has, sarray("value"), varray()); + bind_method(PackedVector3Array, invert, sarray(), varray()); + bind_method(PackedVector3Array, subarray, sarray("from", "to"), varray()); + bind_method(PackedVector3Array, to_byte_array, sarray(), varray()); + bind_method(PackedVector3Array, sort, sarray(), varray()); + bind_method(PackedVector3Array, duplicate, sarray(), varray()); + + /* Color Array */ + + bind_method(PackedColorArray, size, sarray(), varray()); + bind_method(PackedColorArray, is_empty, sarray(), varray()); + bind_method(PackedColorArray, set, sarray("index", "value"), varray()); + bind_method(PackedColorArray, push_back, sarray("value"), varray()); + bind_method(PackedColorArray, append, sarray("value"), varray()); + bind_method(PackedColorArray, append_array, sarray("array"), varray()); + bind_method(PackedColorArray, remove, sarray("index"), varray()); + bind_method(PackedColorArray, insert, sarray("at_index", "value"), varray()); + bind_method(PackedColorArray, resize, sarray("new_size"), varray()); + bind_method(PackedColorArray, has, sarray("value"), varray()); + bind_method(PackedColorArray, invert, sarray(), varray()); + bind_method(PackedColorArray, subarray, sarray("from", "to"), varray()); + bind_method(PackedColorArray, to_byte_array, sarray(), varray()); + bind_method(PackedColorArray, sort, sarray(), varray()); + bind_method(PackedColorArray, duplicate, sarray(), varray()); + + /* Register constants */ + + int ncc = Color::get_named_color_count(); + for (int i = 0; i < ncc; i++) { + _VariantCall::add_variant_constant(Variant::COLOR, Color::get_named_color_name(i), Color::get_named_color(i)); + } + + _VariantCall::add_constant(Variant::VECTOR3, "AXIS_X", Vector3::AXIS_X); + _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Y", Vector3::AXIS_Y); + _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Z", Vector3::AXIS_Z); + + _VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "ONE", Vector3(1, 1, 1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "DOWN", Vector3(0, -1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); + + _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_X", Vector3i::AXIS_X); + _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3i::AXIS_Y); + _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3i::AXIS_Z); + + _VariantCall::add_variant_constant(Variant::VECTOR3I, "ZERO", Vector3i(0, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3I, "ONE", Vector3i(1, 1, 1)); + _VariantCall::add_variant_constant(Variant::VECTOR3I, "LEFT", Vector3i(-1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3I, "RIGHT", Vector3i(1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3I, "UP", Vector3i(0, 1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3I, "DOWN", Vector3i(0, -1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3I, "FORWARD", Vector3i(0, 0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR3I, "BACK", Vector3i(0, 0, 1)); + + _VariantCall::add_constant(Variant::VECTOR2, "AXIS_X", Vector2::AXIS_X); + _VariantCall::add_constant(Variant::VECTOR2, "AXIS_Y", Vector2::AXIS_Y); + + _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_X", Vector2i::AXIS_X); + _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_Y", Vector2i::AXIS_Y); + + _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "DOWN", Vector2(0, 1)); + + _VariantCall::add_variant_constant(Variant::VECTOR2I, "ZERO", Vector2i(0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2I, "ONE", Vector2i(1, 1)); + _VariantCall::add_variant_constant(Variant::VECTOR2I, "LEFT", Vector2i(-1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2I, "RIGHT", Vector2i(1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2I, "UP", Vector2i(0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR2I, "DOWN", Vector2i(0, 1)); + + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D()); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0)); + + Transform identity_transform = Transform(); + Transform flip_x_transform = Transform(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + Transform flip_y_transform = Transform(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); + Transform flip_z_transform = Transform(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", flip_x_transform); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", flip_y_transform); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", flip_z_transform); + + Basis identity_basis = Basis(); + Basis flip_x_basis = Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1); + Basis flip_y_basis = Basis(1, 0, 0, 0, -1, 0, 0, 0, 1); + Basis flip_z_basis = Basis(1, 0, 0, 0, 1, 0, 0, 0, -1); + _VariantCall::add_variant_constant(Variant::BASIS, "IDENTITY", identity_basis); + _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_X", flip_x_basis); + _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_Y", flip_y_basis); + _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_Z", flip_z_basis); + + _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_YZ", Plane(Vector3(1, 0, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XZ", Plane(Vector3(0, 1, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XY", Plane(Vector3(0, 0, 1), 0)); + + _VariantCall::add_variant_constant(Variant::QUAT, "IDENTITY", Quat(0, 0, 0, 1)); +} + +void Variant::_register_variant_methods() { + _register_variant_builtin_methods(); //needs to be out due to namespace +} + +void Variant::_unregister_variant_methods() { + //clear methods + memdelete_arr(builtin_method_names); + memdelete_arr(builtin_method_info); + memdelete_arr(_VariantCall::constant_data); +} diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp new file mode 100644 index 0000000000..9835734583 --- /dev/null +++ b/core/variant/variant_construct.cpp @@ -0,0 +1,871 @@ +/*************************************************************************/ +/* variant_construct.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "variant.h" + +#include "core/core_string_names.h" +#include "core/crypto/crypto_core.h" +#include "core/debugger/engine_debugger.h" +#include "core/io/compression.h" +#include "core/object/class_db.h" +#include "core/os/os.h" +#include "core/templates/local_vector.h" +#include "core/templates/oa_hash_map.h" + +template <class T> +struct PtrConstruct {}; + +#define MAKE_PTRCONSTRUCT(m_type) \ + template <> \ + struct PtrConstruct<m_type> { \ + _FORCE_INLINE_ static void construct(const m_type &p_value, void *p_ptr) { \ + memnew_placement(p_ptr, m_type(p_value)); \ + } \ + }; + +MAKE_PTRCONSTRUCT(bool); +MAKE_PTRCONSTRUCT(int64_t); +MAKE_PTRCONSTRUCT(double); +MAKE_PTRCONSTRUCT(String); +MAKE_PTRCONSTRUCT(Vector2); +MAKE_PTRCONSTRUCT(Vector2i); +MAKE_PTRCONSTRUCT(Rect2); +MAKE_PTRCONSTRUCT(Rect2i); +MAKE_PTRCONSTRUCT(Vector3); +MAKE_PTRCONSTRUCT(Vector3i); +MAKE_PTRCONSTRUCT(Transform2D); +MAKE_PTRCONSTRUCT(Plane); +MAKE_PTRCONSTRUCT(Quat); +MAKE_PTRCONSTRUCT(AABB); +MAKE_PTRCONSTRUCT(Basis); +MAKE_PTRCONSTRUCT(Transform); +MAKE_PTRCONSTRUCT(Color); +MAKE_PTRCONSTRUCT(StringName); +MAKE_PTRCONSTRUCT(NodePath); +MAKE_PTRCONSTRUCT(RID); + +template <> +struct PtrConstruct<Object *> { + _FORCE_INLINE_ static void construct(Object *p_value, void *p_ptr) { + *((Object **)p_ptr) = p_value; + } +}; + +MAKE_PTRCONSTRUCT(Callable); +MAKE_PTRCONSTRUCT(Signal); +MAKE_PTRCONSTRUCT(Dictionary); +MAKE_PTRCONSTRUCT(Array); +MAKE_PTRCONSTRUCT(PackedByteArray); +MAKE_PTRCONSTRUCT(PackedInt32Array); +MAKE_PTRCONSTRUCT(PackedInt64Array); +MAKE_PTRCONSTRUCT(PackedFloat32Array); +MAKE_PTRCONSTRUCT(PackedFloat64Array); +MAKE_PTRCONSTRUCT(PackedStringArray); +MAKE_PTRCONSTRUCT(PackedVector2Array); +MAKE_PTRCONSTRUCT(PackedVector3Array); +MAKE_PTRCONSTRUCT(PackedColorArray); +MAKE_PTRCONSTRUCT(Variant); + +template <class T, class... P> +class VariantConstructor { + template <size_t... Is> + static _FORCE_INLINE_ void construct_helper(T &base, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { + r_error.error = Callable::CallError::CALL_OK; + +#ifdef DEBUG_METHODS_ENABLED + base = T(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...); +#else + base = T(VariantCaster<P>::cast(*p_args[Is])...); +#endif + } + + template <size_t... Is> + static _FORCE_INLINE_ void validated_construct_helper(T &base, const Variant **p_args, IndexSequence<Is...>) { + base = T((*VariantGetInternalPtr<P>::get_ptr(p_args[Is]))...); + } + + template <size_t... Is> + static _FORCE_INLINE_ void ptr_construct_helper(void *base, const void **p_args, IndexSequence<Is...>) { + PtrConstruct<T>::construct(T(PtrToArg<P>::convert(p_args[Is])...), base); + } + +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + VariantTypeChanger<T>::change(&r_ret); + construct_helper(*VariantGetInternalPtr<T>::get_ptr(&r_ret), p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantTypeChanger<T>::change(r_ret); + validated_construct_helper(*VariantGetInternalPtr<T>::get_ptr(r_ret), p_args, BuildIndexSequence<sizeof...(P)>{}); + } + static void ptr_construct(void *base, const void **p_args) { + ptr_construct_helper(base, p_args, BuildIndexSequence<sizeof...(P)>{}); + } + + static int get_argument_count() { + return sizeof...(P); + } + + static Variant::Type get_argument_type(int p_arg) { + return call_get_argument_type<P...>(p_arg); + } + + static Variant::Type get_base_type() { + return GetTypeInfo<T>::VARIANT_TYPE; + } +}; + +class VariantConstructorObject { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + VariantInternal::clear(&r_ret); + if (p_args[0]->get_type() == Variant::NIL) { + VariantInternal::object_assign_null(&r_ret); + r_error.error = Callable::CallError::CALL_OK; + } else if (p_args[0]->get_type() == Variant::OBJECT) { + VariantInternal::object_assign(&r_ret, p_args[0]); + r_error.error = Callable::CallError::CALL_OK; + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + } + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantInternal::clear(r_ret); + VariantInternal::object_assign(r_ret, p_args[0]); + } + static void ptr_construct(void *base, const void **p_args) { + PtrConstruct<Object *>::construct(PtrToArg<Object *>::convert(p_args[0]), base); + } + + static int get_argument_count() { + return 1; + } + + static Variant::Type get_argument_type(int p_arg) { + return Variant::OBJECT; + } + + static Variant::Type get_base_type() { + return Variant::OBJECT; + } +}; + +class VariantConstructorNilObject { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + if (p_args[0]->get_type() != Variant::NIL) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + } + + VariantInternal::clear(&r_ret); + VariantInternal::object_assign_null(&r_ret); + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantInternal::clear(r_ret); + VariantInternal::object_assign_null(r_ret); + } + static void ptr_construct(void *base, const void **p_args) { + PtrConstruct<Object *>::construct(nullptr, base); + } + + static int get_argument_count() { + return 1; + } + + static Variant::Type get_argument_type(int p_arg) { + return Variant::NIL; + } + + static Variant::Type get_base_type() { + return Variant::OBJECT; + } +}; + +class VariantConstructorCallableArgs { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + ObjectID object_id; + StringName method; + + if (p_args[0]->get_type() == Variant::NIL) { + // leave as is + } else if (p_args[0]->get_type() == Variant::OBJECT) { + object_id = VariantInternal::get_object_id(p_args[0]); + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return; + } + + if (p_args[1]->get_type() == Variant::STRING_NAME) { + method = *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]); + } else if (p_args[1]->get_type() == Variant::STRING) { + method = *VariantGetInternalPtr<String>::get_ptr(p_args[1]); + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING_NAME; + return; + } + + VariantTypeChanger<Callable>::change(&r_ret); + *VariantGetInternalPtr<Callable>::get_ptr(&r_ret) = Callable(object_id, method); + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantTypeChanger<Callable>::change(r_ret); + *VariantGetInternalPtr<Callable>::get_ptr(r_ret) = Callable(VariantInternal::get_object_id(p_args[0]), *VariantGetInternalPtr<StringName>::get_ptr(p_args[1])); + } + static void ptr_construct(void *base, const void **p_args) { + PtrConstruct<Callable>::construct(Callable(PtrToArg<Object *>::convert(p_args[0]), PtrToArg<StringName>::convert(p_args[1])), base); + } + + static int get_argument_count() { + return 2; + } + + static Variant::Type get_argument_type(int p_arg) { + if (p_arg == 0) { + return Variant::OBJECT; + } else { + return Variant::STRING_NAME; + } + } + + static Variant::Type get_base_type() { + return Variant::CALLABLE; + } +}; + +class VariantConstructorSignalArgs { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + ObjectID object_id; + StringName method; + + if (p_args[0]->get_type() == Variant::NIL) { + // leave as is + } else if (p_args[0]->get_type() == Variant::OBJECT) { + object_id = VariantInternal::get_object_id(p_args[0]); + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return; + } + + if (p_args[1]->get_type() == Variant::STRING_NAME) { + method = *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]); + } else if (p_args[1]->get_type() == Variant::STRING) { + method = *VariantGetInternalPtr<String>::get_ptr(p_args[1]); + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING_NAME; + return; + } + + VariantTypeChanger<Signal>::change(&r_ret); + *VariantGetInternalPtr<Signal>::get_ptr(&r_ret) = Signal(object_id, method); + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantTypeChanger<Signal>::change(r_ret); + *VariantGetInternalPtr<Signal>::get_ptr(r_ret) = Signal(VariantInternal::get_object_id(p_args[0]), *VariantGetInternalPtr<StringName>::get_ptr(p_args[1])); + } + static void ptr_construct(void *base, const void **p_args) { + PtrConstruct<Signal>::construct(Signal(PtrToArg<Object *>::convert(p_args[0]), PtrToArg<StringName>::convert(p_args[1])), base); + } + + static int get_argument_count() { + return 2; + } + + static Variant::Type get_argument_type(int p_arg) { + if (p_arg == 0) { + return Variant::OBJECT; + } else { + return Variant::STRING_NAME; + } + } + + static Variant::Type get_base_type() { + return Variant::SIGNAL; + } +}; + +template <class T> +class VariantConstructorToArray { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + if (p_args[0]->get_type() != GetTypeInfo<T>::VARIANT_TYPE) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = GetTypeInfo<T>::VARIANT_TYPE; + return; + } + + VariantTypeChanger<Array>::change(&r_ret); + Array &dst_arr = *VariantGetInternalPtr<Array>::get_ptr(&r_ret); + const T &src_arr = *VariantGetInternalPtr<T>::get_ptr(p_args[0]); + + int size = src_arr.size(); + dst_arr.resize(size); + for (int i = 0; i < size; i++) { + dst_arr[i] = src_arr[i]; + } + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantTypeChanger<Array>::change(r_ret); + Array &dst_arr = *VariantGetInternalPtr<Array>::get_ptr(r_ret); + const T &src_arr = *VariantGetInternalPtr<T>::get_ptr(p_args[0]); + + int size = src_arr.size(); + dst_arr.resize(size); + for (int i = 0; i < size; i++) { + dst_arr[i] = src_arr[i]; + } + } + static void ptr_construct(void *base, const void **p_args) { + Array dst_arr; + T src_arr = PtrToArg<T>::convert(p_args[0]); + + int size = src_arr.size(); + dst_arr.resize(size); + for (int i = 0; i < size; i++) { + dst_arr[i] = src_arr[i]; + } + + PtrConstruct<Array>::construct(dst_arr, base); + } + + static int get_argument_count() { + return 1; + } + + static Variant::Type get_argument_type(int p_arg) { + return GetTypeInfo<T>::VARIANT_TYPE; + } + + static Variant::Type get_base_type() { + return Variant::ARRAY; + } +}; + +template <class T> +class VariantConstructorFromArray { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + if (p_args[0]->get_type() != Variant::ARRAY) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::ARRAY; + return; + } + + VariantTypeChanger<T>::change(&r_ret); + const Array &src_arr = *VariantGetInternalPtr<Array>::get_ptr(p_args[0]); + T &dst_arr = *VariantGetInternalPtr<T>::get_ptr(&r_ret); + + int size = src_arr.size(); + dst_arr.resize(size); + for (int i = 0; i < size; i++) { + dst_arr.write[i] = src_arr[i]; + } + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantTypeChanger<T>::change(r_ret); + const Array &src_arr = *VariantGetInternalPtr<Array>::get_ptr(p_args[0]); + T &dst_arr = *VariantGetInternalPtr<T>::get_ptr(r_ret); + + int size = src_arr.size(); + dst_arr.resize(size); + for (int i = 0; i < size; i++) { + dst_arr.write[i] = src_arr[i]; + } + } + static void ptr_construct(void *base, const void **p_args) { + Array src_arr = PtrToArg<Array>::convert(p_args[0]); + T dst_arr; + + int size = src_arr.size(); + dst_arr.resize(size); + for (int i = 0; i < size; i++) { + dst_arr.write[i] = src_arr[i]; + } + + PtrConstruct<T>::construct(dst_arr, base); + } + + static int get_argument_count() { + return 1; + } + + static Variant::Type get_argument_type(int p_arg) { + return Variant::ARRAY; + } + + static Variant::Type get_base_type() { + return GetTypeInfo<T>::VARIANT_TYPE; + } +}; + +class VariantConstructorNil { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + if (p_args[0]->get_type() != Variant::NIL) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + return; + } + + r_error.error = Callable::CallError::CALL_OK; + VariantInternal::clear(&r_ret); + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantInternal::clear(r_ret); + } + static void ptr_construct(void *base, const void **p_args) { + PtrConstruct<Variant>::construct(Variant(), base); + } + + static int get_argument_count() { + return 1; + } + + static Variant::Type get_argument_type(int p_arg) { + return Variant::NIL; + } + + static Variant::Type get_base_type() { + return Variant::NIL; + } +}; + +template <class T> +class VariantConstructNoArgs { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + VariantTypeChanger<T>::change_and_reset(&r_ret); + r_error.error = Callable::CallError::CALL_OK; + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantTypeChanger<T>::change_and_reset(r_ret); + } + static void ptr_construct(void *base, const void **p_args) { + PtrConstruct<T>::construct(T(), base); + } + + static int get_argument_count() { + return 0; + } + + static Variant::Type get_argument_type(int p_arg) { + return Variant::NIL; + } + + static Variant::Type get_base_type() { + return GetTypeInfo<T>::VARIANT_TYPE; + } +}; + +class VariantConstructNoArgsNil { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + VariantInternal::clear(&r_ret); + r_error.error = Callable::CallError::CALL_OK; + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantInternal::clear(r_ret); + } + static void ptr_construct(void *base, const void **p_args) { + ERR_FAIL_MSG("can't ptrcall nil constructor"); + } + + static int get_argument_count() { + return 0; + } + + static Variant::Type get_argument_type(int p_arg) { + return Variant::NIL; + } + + static Variant::Type get_base_type() { + return Variant::NIL; + } +}; + +class VariantConstructNoArgsObject { +public: + static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { + VariantInternal::clear(&r_ret); + VariantInternal::object_assign_null(&r_ret); + r_error.error = Callable::CallError::CALL_OK; + } + + static void validated_construct(Variant *r_ret, const Variant **p_args) { + VariantInternal::clear(r_ret); + VariantInternal::object_assign_null(r_ret); + } + static void ptr_construct(void *base, const void **p_args) { + PtrConstruct<Object *>::construct(nullptr, base); + } + + static int get_argument_count() { + return 0; + } + + static Variant::Type get_argument_type(int p_arg) { + return Variant::NIL; + } + + static Variant::Type get_base_type() { + return Variant::OBJECT; + } +}; + +struct VariantConstructData { + void (*construct)(Variant &r_base, const Variant **p_args, Callable::CallError &r_error); + Variant::ValidatedConstructor validated_construct; + Variant::PTRConstructor ptr_construct; + Variant::Type (*get_argument_type)(int); + int argument_count; + Vector<String> arg_names; +}; + +static LocalVector<VariantConstructData> construct_data[Variant::VARIANT_MAX]; + +template <class T> +static void add_constructor(const Vector<String> &arg_names) { + ERR_FAIL_COND_MSG(arg_names.size() != T::get_argument_count(), "Argument names size mismatch for " + Variant::get_type_name(T::get_base_type()) + "."); + + VariantConstructData cd; + cd.construct = T::construct; + cd.validated_construct = T::validated_construct; + cd.ptr_construct = T::ptr_construct; + cd.get_argument_type = T::get_argument_type; + cd.argument_count = T::get_argument_count(); + cd.arg_names = arg_names; + construct_data[T::get_base_type()].push_back(cd); +} + +void Variant::_register_variant_constructors() { + add_constructor<VariantConstructNoArgsNil>(sarray()); + add_constructor<VariantConstructorNil>(sarray("from")); + + add_constructor<VariantConstructNoArgs<bool>>(sarray()); + add_constructor<VariantConstructor<bool, bool>>(sarray("from")); + add_constructor<VariantConstructor<bool, int64_t>>(sarray("from")); + add_constructor<VariantConstructor<bool, double>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<int64_t>>(sarray()); + add_constructor<VariantConstructor<int64_t, int64_t>>(sarray("from")); + add_constructor<VariantConstructor<int64_t, double>>(sarray("from")); + add_constructor<VariantConstructor<int64_t, bool>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<double>>(sarray()); + add_constructor<VariantConstructor<double, double>>(sarray("from")); + add_constructor<VariantConstructor<double, int64_t>>(sarray("from")); + add_constructor<VariantConstructor<double, bool>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<String>>(sarray()); + add_constructor<VariantConstructor<String, String>>(sarray("from")); + add_constructor<VariantConstructor<String, StringName>>(sarray("from")); + add_constructor<VariantConstructor<String, NodePath>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<Vector2>>(sarray()); + add_constructor<VariantConstructor<Vector2, Vector2>>(sarray("from")); + add_constructor<VariantConstructor<Vector2, Vector2i>>(sarray("from")); + add_constructor<VariantConstructor<Vector2, double, double>>(sarray("x", "y")); + + add_constructor<VariantConstructNoArgs<Vector2i>>(sarray()); + add_constructor<VariantConstructor<Vector2i, Vector2i>>(sarray("from")); + add_constructor<VariantConstructor<Vector2i, Vector2>>(sarray("from")); + add_constructor<VariantConstructor<Vector2i, int64_t, int64_t>>(sarray("x", "y")); + + add_constructor<VariantConstructNoArgs<Rect2>>(sarray()); + add_constructor<VariantConstructor<Rect2, Rect2>>(sarray("from")); + add_constructor<VariantConstructor<Rect2, Rect2i>>(sarray("from")); + add_constructor<VariantConstructor<Rect2, Vector2, Vector2>>(sarray("position", "size")); + add_constructor<VariantConstructor<Rect2, double, double, double, double>>(sarray("x", "y", "width", "height")); + + add_constructor<VariantConstructNoArgs<Rect2i>>(sarray()); + add_constructor<VariantConstructor<Rect2i, Rect2i>>(sarray("from")); + add_constructor<VariantConstructor<Rect2i, Rect2>>(sarray("from")); + add_constructor<VariantConstructor<Rect2i, Vector2i, Vector2i>>(sarray("position", "size")); + add_constructor<VariantConstructor<Rect2i, int64_t, int64_t, int64_t, int64_t>>(sarray("x", "y", "width", "height")); + + add_constructor<VariantConstructNoArgs<Vector3>>(sarray()); + add_constructor<VariantConstructor<Vector3, Vector3>>(sarray("from")); + add_constructor<VariantConstructor<Vector3, Vector3i>>(sarray("from")); + add_constructor<VariantConstructor<Vector3, double, double, double>>(sarray("x", "y", "z")); + + add_constructor<VariantConstructNoArgs<Vector3i>>(sarray()); + add_constructor<VariantConstructor<Vector3i, Vector3i>>(sarray("from")); + add_constructor<VariantConstructor<Vector3i, Vector3>>(sarray("from")); + add_constructor<VariantConstructor<Vector3i, int64_t, int64_t, int64_t>>(sarray("x", "y", "z")); + + add_constructor<VariantConstructNoArgs<Transform2D>>(sarray()); + add_constructor<VariantConstructor<Transform2D, Transform2D>>(sarray("from")); + add_constructor<VariantConstructor<Transform2D, float, Vector2>>(sarray("rotation", "position")); + add_constructor<VariantConstructor<Transform2D, Vector2, Vector2, Vector2>>(sarray("x_axis", "y_axis", "origin")); + + add_constructor<VariantConstructNoArgs<Plane>>(sarray()); + add_constructor<VariantConstructor<Plane, Plane>>(sarray("from")); + add_constructor<VariantConstructor<Plane, Vector3, double>>(sarray("normal", "d")); + add_constructor<VariantConstructor<Plane, Vector3, Vector3>>(sarray("point", "normal")); + add_constructor<VariantConstructor<Plane, Vector3, Vector3, Vector3>>(sarray("point1", "point2", "point3")); + add_constructor<VariantConstructor<Plane, double, double, double, double>>(sarray("a", "b", "c", "d")); + + add_constructor<VariantConstructNoArgs<Quat>>(sarray()); + add_constructor<VariantConstructor<Quat, Quat>>(sarray("from")); + add_constructor<VariantConstructor<Quat, Basis>>(sarray("from")); + add_constructor<VariantConstructor<Quat, Vector3>>(sarray("euler")); + add_constructor<VariantConstructor<Quat, Vector3, double>>(sarray("axis", "angle")); + add_constructor<VariantConstructor<Quat, Vector3, Vector3>>(sarray("arc_from", "arc_to")); + add_constructor<VariantConstructor<Quat, double, double, double, double>>(sarray("x", "y", "z", "w")); + + add_constructor<VariantConstructNoArgs<::AABB>>(sarray()); + add_constructor<VariantConstructor<::AABB, ::AABB>>(sarray("from")); + add_constructor<VariantConstructor<::AABB, Vector3, Vector3>>(sarray("position", "size")); + + add_constructor<VariantConstructNoArgs<Basis>>(sarray()); + add_constructor<VariantConstructor<Basis, Basis>>(sarray("from")); + add_constructor<VariantConstructor<Basis, Quat>>(sarray("from")); + add_constructor<VariantConstructor<Basis, Vector3>>(sarray("euler")); + add_constructor<VariantConstructor<Basis, Vector3, double>>(sarray("axis", "phi")); + add_constructor<VariantConstructor<Basis, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis")); + + add_constructor<VariantConstructNoArgs<Transform>>(sarray()); + add_constructor<VariantConstructor<Transform, Transform>>(sarray("from")); + add_constructor<VariantConstructor<Transform, Basis, Vector3>>(sarray("basis", "origin")); + add_constructor<VariantConstructor<Transform, Vector3, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis", "origin")); + + add_constructor<VariantConstructNoArgs<Color>>(sarray()); + add_constructor<VariantConstructor<Color, Color>>(sarray("from")); + add_constructor<VariantConstructor<Color, Color, double>>(sarray("from", "alpha")); + add_constructor<VariantConstructor<Color, double, double, double>>(sarray("r", "g", "b")); + add_constructor<VariantConstructor<Color, double, double, double, double>>(sarray("r", "g", "b", "a")); + + add_constructor<VariantConstructNoArgs<StringName>>(sarray()); + add_constructor<VariantConstructor<StringName, StringName>>(sarray("from")); + add_constructor<VariantConstructor<StringName, String>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<NodePath>>(sarray()); + add_constructor<VariantConstructor<NodePath, NodePath>>(sarray("from")); + add_constructor<VariantConstructor<NodePath, String>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<::RID>>(sarray()); + add_constructor<VariantConstructor<::RID, ::RID>>(sarray("from")); + + add_constructor<VariantConstructNoArgsObject>(sarray()); + add_constructor<VariantConstructorObject>(sarray("from")); + add_constructor<VariantConstructorNilObject>(sarray("from")); + + add_constructor<VariantConstructNoArgs<Callable>>(sarray()); + add_constructor<VariantConstructor<Callable, Callable>>(sarray("from")); + add_constructor<VariantConstructorCallableArgs>(sarray("object", "method")); + + add_constructor<VariantConstructNoArgs<Signal>>(sarray()); + add_constructor<VariantConstructor<Signal, Signal>>(sarray("from")); + add_constructor<VariantConstructorSignalArgs>(sarray("object", "signal")); + + add_constructor<VariantConstructNoArgs<Dictionary>>(sarray()); + add_constructor<VariantConstructor<Dictionary, Dictionary>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<Array>>(sarray()); + add_constructor<VariantConstructor<Array, Array>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedByteArray>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedInt32Array>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedInt64Array>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedFloat32Array>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedFloat64Array>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedStringArray>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedVector2Array>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedVector3Array>>(sarray("from")); + add_constructor<VariantConstructorToArray<PackedColorArray>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedByteArray>>(sarray()); + add_constructor<VariantConstructor<PackedByteArray, PackedByteArray>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedByteArray>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedInt32Array>>(sarray()); + add_constructor<VariantConstructor<PackedInt32Array, PackedInt32Array>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedInt32Array>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedInt64Array>>(sarray()); + add_constructor<VariantConstructor<PackedInt64Array, PackedInt64Array>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedInt64Array>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedFloat32Array>>(sarray()); + add_constructor<VariantConstructor<PackedFloat32Array, PackedFloat32Array>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedFloat32Array>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedFloat64Array>>(sarray()); + add_constructor<VariantConstructor<PackedFloat64Array, PackedFloat64Array>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedFloat64Array>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedStringArray>>(sarray()); + add_constructor<VariantConstructor<PackedStringArray, PackedStringArray>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedStringArray>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedVector2Array>>(sarray()); + add_constructor<VariantConstructor<PackedVector2Array, PackedVector2Array>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedVector2Array>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedVector3Array>>(sarray()); + add_constructor<VariantConstructor<PackedVector3Array, PackedVector3Array>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedVector3Array>>(sarray("from")); + + add_constructor<VariantConstructNoArgs<PackedColorArray>>(sarray()); + add_constructor<VariantConstructor<PackedColorArray, PackedColorArray>>(sarray("from")); + add_constructor<VariantConstructorFromArray<PackedColorArray>>(sarray("from")); +} + +void Variant::_unregister_variant_constructors() { + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + construct_data[i].clear(); + } +} + +void Variant::construct(Variant::Type p_type, Variant &base, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + uint32_t s = construct_data[p_type].size(); + for (uint32_t i = 0; i < s; i++) { + int argc = construct_data[p_type][i].argument_count; + if (argc != p_argcount) { + continue; + } + bool args_match = true; + for (int j = 0; j < argc; j++) { + if (!Variant::can_convert_strict(p_args[j]->get_type(), construct_data[p_type][i].get_argument_type(j))) { + args_match = false; + break; + } + } + + if (!args_match) { + continue; + } + + construct_data[p_type][i].construct(base, p_args, r_error); + return; + } + + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; +} + +int Variant::get_constructor_count(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, -1); + return construct_data[p_type].size(); +} + +Variant::ValidatedConstructor Variant::get_validated_constructor(Variant::Type p_type, int p_constructor) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + ERR_FAIL_INDEX_V(p_constructor, (int)construct_data[p_type].size(), nullptr); + return construct_data[p_type][p_constructor].validated_construct; +} + +Variant::PTRConstructor Variant::get_ptr_constructor(Variant::Type p_type, int p_constructor) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + ERR_FAIL_INDEX_V(p_constructor, (int)construct_data[p_type].size(), nullptr); + return construct_data[p_type][p_constructor].ptr_construct; +} + +int Variant::get_constructor_argument_count(Variant::Type p_type, int p_constructor) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, -1); + ERR_FAIL_INDEX_V(p_constructor, (int)construct_data[p_type].size(), -1); + return construct_data[p_type][p_constructor].argument_count; +} + +Variant::Type Variant::get_constructor_argument_type(Variant::Type p_type, int p_constructor, int p_argument) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::VARIANT_MAX); + ERR_FAIL_INDEX_V(p_constructor, (int)construct_data[p_type].size(), Variant::VARIANT_MAX); + return construct_data[p_type][p_constructor].get_argument_type(p_argument); +} + +String Variant::get_constructor_argument_name(Variant::Type p_type, int p_constructor, int p_argument) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, String()); + ERR_FAIL_INDEX_V(p_constructor, (int)construct_data[p_type].size(), String()); + return construct_data[p_type][p_constructor].arg_names[p_argument]; +} + +void VariantInternal::object_assign(Variant *v, const Object *o) { + if (o) { + if (o->is_reference()) { + Reference *reference = const_cast<Reference *>(static_cast<const Reference *>(o)); + if (!reference->init_ref()) { + v->_get_obj().obj = nullptr; + v->_get_obj().id = ObjectID(); + return; + } + } + + v->_get_obj().obj = const_cast<Object *>(o); + v->_get_obj().id = o->get_instance_id(); + } else { + v->_get_obj().obj = nullptr; + v->_get_obj().id = ObjectID(); + } +} + +void Variant::get_constructor_list(Type p_type, List<MethodInfo> *r_list) { + ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); + + MethodInfo mi; + mi.return_val.type = p_type; + mi.name = get_type_name(p_type); + + for (int i = 0; i < get_constructor_count(p_type); i++) { + int ac = get_constructor_argument_count(p_type, i); + mi.arguments.clear(); + for (int j = 0; j < ac; j++) { + PropertyInfo arg; + arg.name = get_constructor_argument_name(p_type, i, j); + arg.type = get_constructor_argument_type(p_type, i, j); + mi.arguments.push_back(arg); + } + r_list->push_back(mi); + } +} diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h new file mode 100644 index 0000000000..7d33d85cd6 --- /dev/null +++ b/core/variant/variant_internal.h @@ -0,0 +1,1381 @@ +/*************************************************************************/ +/* variant_internal.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 VARIANT_INTERNAL_H +#define VARIANT_INTERNAL_H + +#include "variant.h" + +// For use when you want to access the internal pointer of a Variant directly. +// Use with caution. You need to be sure that the type is correct. +class VariantInternal { +public: + // Set type. + _FORCE_INLINE_ static void initialize(Variant *v, Variant::Type p_type) { + v->clear(); + v->type = p_type; + + switch (p_type) { + case Variant::AABB: + init_aabb(v); + break; + case Variant::TRANSFORM2D: + init_transform2d(v); + break; + case Variant::TRANSFORM: + init_transform(v); + break; + case Variant::STRING: + init_string(v); + break; + case Variant::STRING_NAME: + init_string_name(v); + break; + case Variant::NODE_PATH: + init_node_path(v); + break; + case Variant::CALLABLE: + init_callable(v); + break; + case Variant::SIGNAL: + init_signal(v); + break; + case Variant::DICTIONARY: + init_dictionary(v); + break; + case Variant::ARRAY: + init_array(v); + break; + case Variant::PACKED_BYTE_ARRAY: + init_byte_array(v); + break; + case Variant::PACKED_INT32_ARRAY: + init_int32_array(v); + break; + case Variant::PACKED_INT64_ARRAY: + init_int64_array(v); + break; + case Variant::PACKED_FLOAT32_ARRAY: + init_float32_array(v); + break; + case Variant::PACKED_FLOAT64_ARRAY: + init_float64_array(v); + break; + case Variant::PACKED_STRING_ARRAY: + init_string_array(v); + break; + case Variant::PACKED_VECTOR2_ARRAY: + init_vector2_array(v); + break; + case Variant::PACKED_VECTOR3_ARRAY: + init_vector3_array(v); + break; + case Variant::PACKED_COLOR_ARRAY: + init_color_array(v); + break; + case Variant::OBJECT: + object_assign_null(v); + break; + default: + break; + } + } + + // Atomic types. + _FORCE_INLINE_ static bool *get_bool(Variant *v) { return &v->_data._bool; } + _FORCE_INLINE_ static const bool *get_bool(const Variant *v) { return &v->_data._bool; } + _FORCE_INLINE_ static int64_t *get_int(Variant *v) { return &v->_data._int; } + _FORCE_INLINE_ static const int64_t *get_int(const Variant *v) { return &v->_data._int; } + _FORCE_INLINE_ static double *get_float(Variant *v) { return &v->_data._float; } + _FORCE_INLINE_ static const double *get_float(const Variant *v) { return &v->_data._float; } + _FORCE_INLINE_ static String *get_string(Variant *v) { return reinterpret_cast<String *>(v->_data._mem); } + _FORCE_INLINE_ static const String *get_string(const Variant *v) { return reinterpret_cast<const String *>(v->_data._mem); } + + // Math types. + _FORCE_INLINE_ static Vector2 *get_vector2(Variant *v) { return reinterpret_cast<Vector2 *>(v->_data._mem); } + _FORCE_INLINE_ static const Vector2 *get_vector2(const Variant *v) { return reinterpret_cast<const Vector2 *>(v->_data._mem); } + _FORCE_INLINE_ static Vector2i *get_vector2i(Variant *v) { return reinterpret_cast<Vector2i *>(v->_data._mem); } + _FORCE_INLINE_ static const Vector2i *get_vector2i(const Variant *v) { return reinterpret_cast<const Vector2i *>(v->_data._mem); } + _FORCE_INLINE_ static Rect2 *get_rect2(Variant *v) { return reinterpret_cast<Rect2 *>(v->_data._mem); } + _FORCE_INLINE_ static const Rect2 *get_rect2(const Variant *v) { return reinterpret_cast<const Rect2 *>(v->_data._mem); } + _FORCE_INLINE_ static Rect2i *get_rect2i(Variant *v) { return reinterpret_cast<Rect2i *>(v->_data._mem); } + _FORCE_INLINE_ static const Rect2i *get_rect2i(const Variant *v) { return reinterpret_cast<const Rect2i *>(v->_data._mem); } + _FORCE_INLINE_ static Vector3 *get_vector3(Variant *v) { return reinterpret_cast<Vector3 *>(v->_data._mem); } + _FORCE_INLINE_ static const Vector3 *get_vector3(const Variant *v) { return reinterpret_cast<const Vector3 *>(v->_data._mem); } + _FORCE_INLINE_ static Vector3i *get_vector3i(Variant *v) { return reinterpret_cast<Vector3i *>(v->_data._mem); } + _FORCE_INLINE_ static const Vector3i *get_vector3i(const Variant *v) { return reinterpret_cast<const Vector3i *>(v->_data._mem); } + _FORCE_INLINE_ static Transform2D *get_transform2d(Variant *v) { return v->_data._transform2d; } + _FORCE_INLINE_ static const Transform2D *get_transform2d(const Variant *v) { return v->_data._transform2d; } + _FORCE_INLINE_ static Plane *get_plane(Variant *v) { return reinterpret_cast<Plane *>(v->_data._mem); } + _FORCE_INLINE_ static const Plane *get_plane(const Variant *v) { return reinterpret_cast<const Plane *>(v->_data._mem); } + _FORCE_INLINE_ static Quat *get_quat(Variant *v) { return reinterpret_cast<Quat *>(v->_data._mem); } + _FORCE_INLINE_ static const Quat *get_quat(const Variant *v) { return reinterpret_cast<const Quat *>(v->_data._mem); } + _FORCE_INLINE_ static ::AABB *get_aabb(Variant *v) { return v->_data._aabb; } + _FORCE_INLINE_ static const ::AABB *get_aabb(const Variant *v) { return v->_data._aabb; } + _FORCE_INLINE_ static Basis *get_basis(Variant *v) { return v->_data._basis; } + _FORCE_INLINE_ static const Basis *get_basis(const Variant *v) { return v->_data._basis; } + _FORCE_INLINE_ static Transform *get_transform(Variant *v) { return v->_data._transform; } + _FORCE_INLINE_ static const Transform *get_transform(const Variant *v) { return v->_data._transform; } + + // Misc types. + _FORCE_INLINE_ static Color *get_color(Variant *v) { return reinterpret_cast<Color *>(v->_data._mem); } + _FORCE_INLINE_ static const Color *get_color(const Variant *v) { return reinterpret_cast<const Color *>(v->_data._mem); } + _FORCE_INLINE_ static StringName *get_string_name(Variant *v) { return reinterpret_cast<StringName *>(v->_data._mem); } + _FORCE_INLINE_ static const StringName *get_string_name(const Variant *v) { return reinterpret_cast<const StringName *>(v->_data._mem); } + _FORCE_INLINE_ static NodePath *get_node_path(Variant *v) { return reinterpret_cast<NodePath *>(v->_data._mem); } + _FORCE_INLINE_ static const NodePath *get_node_path(const Variant *v) { return reinterpret_cast<const NodePath *>(v->_data._mem); } + _FORCE_INLINE_ static ::RID *get_rid(Variant *v) { return reinterpret_cast<::RID *>(v->_data._mem); } + _FORCE_INLINE_ static const ::RID *get_rid(const Variant *v) { return reinterpret_cast<const ::RID *>(v->_data._mem); } + _FORCE_INLINE_ static Callable *get_callable(Variant *v) { return reinterpret_cast<Callable *>(v->_data._mem); } + _FORCE_INLINE_ static const Callable *get_callable(const Variant *v) { return reinterpret_cast<const Callable *>(v->_data._mem); } + _FORCE_INLINE_ static Signal *get_signal(Variant *v) { return reinterpret_cast<Signal *>(v->_data._mem); } + _FORCE_INLINE_ static const Signal *get_signal(const Variant *v) { return reinterpret_cast<const Signal *>(v->_data._mem); } + _FORCE_INLINE_ static Dictionary *get_dictionary(Variant *v) { return reinterpret_cast<Dictionary *>(v->_data._mem); } + _FORCE_INLINE_ static const Dictionary *get_dictionary(const Variant *v) { return reinterpret_cast<const Dictionary *>(v->_data._mem); } + _FORCE_INLINE_ static Array *get_array(Variant *v) { return reinterpret_cast<Array *>(v->_data._mem); } + _FORCE_INLINE_ static const Array *get_array(const Variant *v) { return reinterpret_cast<const Array *>(v->_data._mem); } + + // Typed arrays. + _FORCE_INLINE_ static PackedByteArray *get_byte_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<uint8_t> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedByteArray *get_byte_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<uint8_t> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static PackedInt32Array *get_int32_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<int32_t> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedInt32Array *get_int32_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<int32_t> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static PackedInt64Array *get_int64_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<int64_t> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedInt64Array *get_int64_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<int64_t> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static PackedFloat32Array *get_float32_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<float> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedFloat32Array *get_float32_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<float> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static PackedFloat64Array *get_float64_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<double> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedFloat64Array *get_float64_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<double> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static PackedStringArray *get_string_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<String> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedStringArray *get_string_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<String> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static PackedVector2Array *get_vector2_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<Vector2> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedVector2Array *get_vector2_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Vector2> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static PackedVector3Array *get_vector3_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<Vector3> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedVector3Array *get_vector3_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Vector3> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static PackedColorArray *get_color_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<Color> *>(v->_data.packed_array)->array; } + _FORCE_INLINE_ static const PackedColorArray *get_color_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Color> *>(v->_data.packed_array)->array; } + + _FORCE_INLINE_ static Object **get_object(Variant *v) { return (Object **)&v->_get_obj().obj; } + _FORCE_INLINE_ static const Object **get_object(const Variant *v) { return (const Object **)&v->_get_obj().obj; } + + _FORCE_INLINE_ static const ObjectID get_object_id(const Variant *v) { return v->_get_obj().id; } + + template <class T> + _FORCE_INLINE_ static void init_generic(Variant *v) { + v->type = GetTypeInfo<T>::VARIANT_TYPE; + } + + _FORCE_INLINE_ static void init_string(Variant *v) { + memnew_placement(v->_data._mem, String); + v->type = Variant::STRING; + } + + _FORCE_INLINE_ static void init_transform2d(Variant *v) { + v->_data._transform2d = memnew(Transform2D); + v->type = Variant::TRANSFORM2D; + } + _FORCE_INLINE_ static void init_aabb(Variant *v) { + v->_data._aabb = memnew(AABB); + v->type = Variant::AABB; + } + _FORCE_INLINE_ static void init_basis(Variant *v) { + v->_data._basis = memnew(Basis); + v->type = Variant::BASIS; + } + _FORCE_INLINE_ static void init_transform(Variant *v) { + v->_data._transform = memnew(Transform); + v->type = Variant::TRANSFORM; + } + _FORCE_INLINE_ static void init_string_name(Variant *v) { + memnew_placement(v->_data._mem, StringName); + v->type = Variant::STRING_NAME; + } + _FORCE_INLINE_ static void init_node_path(Variant *v) { + memnew_placement(v->_data._mem, NodePath); + v->type = Variant::NODE_PATH; + } + _FORCE_INLINE_ static void init_callable(Variant *v) { + memnew_placement(v->_data._mem, Callable); + v->type = Variant::CALLABLE; + } + _FORCE_INLINE_ static void init_signal(Variant *v) { + memnew_placement(v->_data._mem, Signal); + v->type = Variant::SIGNAL; + } + _FORCE_INLINE_ static void init_dictionary(Variant *v) { + memnew_placement(v->_data._mem, Dictionary); + v->type = Variant::DICTIONARY; + } + _FORCE_INLINE_ static void init_array(Variant *v) { + memnew_placement(v->_data._mem, Array); + v->type = Variant::ARRAY; + } + _FORCE_INLINE_ static void init_byte_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<uint8_t>::create(Vector<uint8_t>()); + v->type = Variant::PACKED_BYTE_ARRAY; + } + _FORCE_INLINE_ static void init_int32_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<int32_t>::create(Vector<int32_t>()); + v->type = Variant::PACKED_INT32_ARRAY; + } + _FORCE_INLINE_ static void init_int64_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<int64_t>::create(Vector<int64_t>()); + v->type = Variant::PACKED_INT64_ARRAY; + } + _FORCE_INLINE_ static void init_float32_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<float>::create(Vector<float>()); + v->type = Variant::PACKED_FLOAT32_ARRAY; + } + _FORCE_INLINE_ static void init_float64_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<double>::create(Vector<double>()); + v->type = Variant::PACKED_FLOAT64_ARRAY; + } + _FORCE_INLINE_ static void init_string_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<String>::create(Vector<String>()); + v->type = Variant::PACKED_STRING_ARRAY; + } + _FORCE_INLINE_ static void init_vector2_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<Vector2>::create(Vector<Vector2>()); + v->type = Variant::PACKED_VECTOR2_ARRAY; + } + _FORCE_INLINE_ static void init_vector3_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<Vector3>::create(Vector<Vector3>()); + v->type = Variant::PACKED_VECTOR3_ARRAY; + } + _FORCE_INLINE_ static void init_color_array(Variant *v) { + v->_data.packed_array = Variant::PackedArrayRef<Color>::create(Vector<Color>()); + v->type = Variant::PACKED_COLOR_ARRAY; + } + + _FORCE_INLINE_ static void clear(Variant *v) { + v->clear(); + } + + static void object_assign(Variant *v, const Object *o); // Needs Reference, so it's implemented elsewhere. + + _FORCE_INLINE_ static void object_assign(Variant *v, const Variant *o) { + object_assign(v, o->_get_obj().obj); + } + + _FORCE_INLINE_ static void object_assign_null(Variant *v) { + v->_get_obj().obj = nullptr; + v->_get_obj().id = ObjectID(); + } + + _FORCE_INLINE_ static void *get_opaque_pointer(Variant *v) { + switch (v->type) { + case Variant::NIL: + return nullptr; + case Variant::BOOL: + return get_bool(v); + case Variant::INT: + return get_int(v); + case Variant::FLOAT: + return get_float(v); + case Variant::STRING: + return get_string(v); + case Variant::VECTOR2: + return get_vector2(v); + case Variant::VECTOR2I: + return get_vector2i(v); + case Variant::VECTOR3: + return get_vector3(v); + case Variant::VECTOR3I: + return get_vector3i(v); + case Variant::RECT2: + return get_rect2(v); + case Variant::RECT2I: + return get_rect2i(v); + case Variant::TRANSFORM: + return get_transform(v); + case Variant::TRANSFORM2D: + return get_transform2d(v); + case Variant::QUAT: + return get_quat(v); + case Variant::PLANE: + return get_plane(v); + case Variant::BASIS: + return get_basis(v); + case Variant::AABB: + return get_aabb(v); + case Variant::COLOR: + return get_color(v); + case Variant::STRING_NAME: + return get_string_name(v); + case Variant::NODE_PATH: + return get_node_path(v); + case Variant::RID: + return get_rid(v); + case Variant::CALLABLE: + return get_callable(v); + case Variant::SIGNAL: + return get_signal(v); + case Variant::DICTIONARY: + return get_dictionary(v); + case Variant::ARRAY: + return get_array(v); + case Variant::PACKED_BYTE_ARRAY: + return get_byte_array(v); + case Variant::PACKED_INT32_ARRAY: + return get_int32_array(v); + case Variant::PACKED_INT64_ARRAY: + return get_int64_array(v); + case Variant::PACKED_FLOAT32_ARRAY: + return get_float32_array(v); + case Variant::PACKED_FLOAT64_ARRAY: + return get_float64_array(v); + case Variant::PACKED_STRING_ARRAY: + return get_string_array(v); + case Variant::PACKED_VECTOR2_ARRAY: + return get_vector2_array(v); + case Variant::PACKED_VECTOR3_ARRAY: + return get_vector3_array(v); + case Variant::PACKED_COLOR_ARRAY: + return get_color_array(v); + case Variant::OBJECT: + return v->_get_obj().obj; + case Variant::VARIANT_MAX: + ERR_FAIL_V(nullptr); + } + ERR_FAIL_V(nullptr); + } + + _FORCE_INLINE_ static const void *get_opaque_pointer(const Variant *v) { + switch (v->type) { + case Variant::NIL: + return nullptr; + case Variant::BOOL: + return get_bool(v); + case Variant::INT: + return get_int(v); + case Variant::FLOAT: + return get_float(v); + case Variant::STRING: + return get_string(v); + case Variant::VECTOR2: + return get_vector2(v); + case Variant::VECTOR2I: + return get_vector2i(v); + case Variant::VECTOR3: + return get_vector3(v); + case Variant::VECTOR3I: + return get_vector3i(v); + case Variant::RECT2: + return get_rect2(v); + case Variant::RECT2I: + return get_rect2i(v); + case Variant::TRANSFORM: + return get_transform(v); + case Variant::TRANSFORM2D: + return get_transform2d(v); + case Variant::QUAT: + return get_quat(v); + case Variant::PLANE: + return get_plane(v); + case Variant::BASIS: + return get_basis(v); + case Variant::AABB: + return get_aabb(v); + case Variant::COLOR: + return get_color(v); + case Variant::STRING_NAME: + return get_string_name(v); + case Variant::NODE_PATH: + return get_node_path(v); + case Variant::RID: + return get_rid(v); + case Variant::CALLABLE: + return get_callable(v); + case Variant::SIGNAL: + return get_signal(v); + case Variant::DICTIONARY: + return get_dictionary(v); + case Variant::ARRAY: + return get_array(v); + case Variant::PACKED_BYTE_ARRAY: + return get_byte_array(v); + case Variant::PACKED_INT32_ARRAY: + return get_int32_array(v); + case Variant::PACKED_INT64_ARRAY: + return get_int64_array(v); + case Variant::PACKED_FLOAT32_ARRAY: + return get_float32_array(v); + case Variant::PACKED_FLOAT64_ARRAY: + return get_float64_array(v); + case Variant::PACKED_STRING_ARRAY: + return get_string_array(v); + case Variant::PACKED_VECTOR2_ARRAY: + return get_vector2_array(v); + case Variant::PACKED_VECTOR3_ARRAY: + return get_vector3_array(v); + case Variant::PACKED_COLOR_ARRAY: + return get_color_array(v); + case Variant::OBJECT: + return v->_get_obj().obj; + case Variant::VARIANT_MAX: + ERR_FAIL_V(nullptr); + } + ERR_FAIL_V(nullptr); + } +}; + +template <class T> +struct VariantGetInternalPtr { +}; + +template <> +struct VariantGetInternalPtr<bool> { + static bool *get_ptr(Variant *v) { return VariantInternal::get_bool(v); } + static const bool *get_ptr(const Variant *v) { return VariantInternal::get_bool(v); } +}; + +template <> +struct VariantGetInternalPtr<int8_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<uint8_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<int16_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<uint16_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<int32_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<uint32_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<int64_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<uint64_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<char32_t> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<ObjectID> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<Error> { + static int64_t *get_ptr(Variant *v) { return VariantInternal::get_int(v); } + static const int64_t *get_ptr(const Variant *v) { return VariantInternal::get_int(v); } +}; + +template <> +struct VariantGetInternalPtr<float> { + static double *get_ptr(Variant *v) { return VariantInternal::get_float(v); } + static const double *get_ptr(const Variant *v) { return VariantInternal::get_float(v); } +}; + +template <> +struct VariantGetInternalPtr<double> { + static double *get_ptr(Variant *v) { return VariantInternal::get_float(v); } + static const double *get_ptr(const Variant *v) { return VariantInternal::get_float(v); } +}; + +template <> +struct VariantGetInternalPtr<String> { + static String *get_ptr(Variant *v) { return VariantInternal::get_string(v); } + static const String *get_ptr(const Variant *v) { return VariantInternal::get_string(v); } +}; + +template <> +struct VariantGetInternalPtr<Vector2> { + static Vector2 *get_ptr(Variant *v) { return VariantInternal::get_vector2(v); } + static const Vector2 *get_ptr(const Variant *v) { return VariantInternal::get_vector2(v); } +}; + +template <> +struct VariantGetInternalPtr<Vector2i> { + static Vector2i *get_ptr(Variant *v) { return VariantInternal::get_vector2i(v); } + static const Vector2i *get_ptr(const Variant *v) { return VariantInternal::get_vector2i(v); } +}; + +template <> +struct VariantGetInternalPtr<Rect2> { + static Rect2 *get_ptr(Variant *v) { return VariantInternal::get_rect2(v); } + static const Rect2 *get_ptr(const Variant *v) { return VariantInternal::get_rect2(v); } +}; + +template <> +struct VariantGetInternalPtr<Rect2i> { + static Rect2i *get_ptr(Variant *v) { return VariantInternal::get_rect2i(v); } + static const Rect2i *get_ptr(const Variant *v) { return VariantInternal::get_rect2i(v); } +}; + +template <> +struct VariantGetInternalPtr<Vector3> { + static Vector3 *get_ptr(Variant *v) { return VariantInternal::get_vector3(v); } + static const Vector3 *get_ptr(const Variant *v) { return VariantInternal::get_vector3(v); } +}; + +template <> +struct VariantGetInternalPtr<Vector3i> { + static Vector3i *get_ptr(Variant *v) { return VariantInternal::get_vector3i(v); } + static const Vector3i *get_ptr(const Variant *v) { return VariantInternal::get_vector3i(v); } +}; + +template <> +struct VariantGetInternalPtr<Transform2D> { + static Transform2D *get_ptr(Variant *v) { return VariantInternal::get_transform2d(v); } + static const Transform2D *get_ptr(const Variant *v) { return VariantInternal::get_transform2d(v); } +}; + +template <> +struct VariantGetInternalPtr<Transform> { + static Transform *get_ptr(Variant *v) { return VariantInternal::get_transform(v); } + static const Transform *get_ptr(const Variant *v) { return VariantInternal::get_transform(v); } +}; + +template <> +struct VariantGetInternalPtr<Plane> { + static Plane *get_ptr(Variant *v) { return VariantInternal::get_plane(v); } + static const Plane *get_ptr(const Variant *v) { return VariantInternal::get_plane(v); } +}; + +template <> +struct VariantGetInternalPtr<Quat> { + static Quat *get_ptr(Variant *v) { return VariantInternal::get_quat(v); } + static const Quat *get_ptr(const Variant *v) { return VariantInternal::get_quat(v); } +}; + +template <> +struct VariantGetInternalPtr<::AABB> { + static ::AABB *get_ptr(Variant *v) { return VariantInternal::get_aabb(v); } + static const ::AABB *get_ptr(const Variant *v) { return VariantInternal::get_aabb(v); } +}; + +template <> +struct VariantGetInternalPtr<Basis> { + static Basis *get_ptr(Variant *v) { return VariantInternal::get_basis(v); } + static const Basis *get_ptr(const Variant *v) { return VariantInternal::get_basis(v); } +}; + +// + +template <> +struct VariantGetInternalPtr<Color> { + static Color *get_ptr(Variant *v) { return VariantInternal::get_color(v); } + static const Color *get_ptr(const Variant *v) { return VariantInternal::get_color(v); } +}; + +template <> +struct VariantGetInternalPtr<StringName> { + static StringName *get_ptr(Variant *v) { return VariantInternal::get_string_name(v); } + static const StringName *get_ptr(const Variant *v) { return VariantInternal::get_string_name(v); } +}; + +template <> +struct VariantGetInternalPtr<NodePath> { + static NodePath *get_ptr(Variant *v) { return VariantInternal::get_node_path(v); } + static const NodePath *get_ptr(const Variant *v) { return VariantInternal::get_node_path(v); } +}; + +template <> +struct VariantGetInternalPtr<::RID> { + static ::RID *get_ptr(Variant *v) { return VariantInternal::get_rid(v); } + static const ::RID *get_ptr(const Variant *v) { return VariantInternal::get_rid(v); } +}; + +template <> +struct VariantGetInternalPtr<Callable> { + static Callable *get_ptr(Variant *v) { return VariantInternal::get_callable(v); } + static const Callable *get_ptr(const Variant *v) { return VariantInternal::get_callable(v); } +}; + +template <> +struct VariantGetInternalPtr<Signal> { + static Signal *get_ptr(Variant *v) { return VariantInternal::get_signal(v); } + static const Signal *get_ptr(const Variant *v) { return VariantInternal::get_signal(v); } +}; + +template <> +struct VariantGetInternalPtr<Dictionary> { + static Dictionary *get_ptr(Variant *v) { return VariantInternal::get_dictionary(v); } + static const Dictionary *get_ptr(const Variant *v) { return VariantInternal::get_dictionary(v); } +}; + +template <> +struct VariantGetInternalPtr<Array> { + static Array *get_ptr(Variant *v) { return VariantInternal::get_array(v); } + static const Array *get_ptr(const Variant *v) { return VariantInternal::get_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedByteArray> { + static PackedByteArray *get_ptr(Variant *v) { return VariantInternal::get_byte_array(v); } + static const PackedByteArray *get_ptr(const Variant *v) { return VariantInternal::get_byte_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedInt32Array> { + static PackedInt32Array *get_ptr(Variant *v) { return VariantInternal::get_int32_array(v); } + static const PackedInt32Array *get_ptr(const Variant *v) { return VariantInternal::get_int32_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedInt64Array> { + static PackedInt64Array *get_ptr(Variant *v) { return VariantInternal::get_int64_array(v); } + static const PackedInt64Array *get_ptr(const Variant *v) { return VariantInternal::get_int64_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedFloat32Array> { + static PackedFloat32Array *get_ptr(Variant *v) { return VariantInternal::get_float32_array(v); } + static const PackedFloat32Array *get_ptr(const Variant *v) { return VariantInternal::get_float32_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedFloat64Array> { + static PackedFloat64Array *get_ptr(Variant *v) { return VariantInternal::get_float64_array(v); } + static const PackedFloat64Array *get_ptr(const Variant *v) { return VariantInternal::get_float64_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedStringArray> { + static PackedStringArray *get_ptr(Variant *v) { return VariantInternal::get_string_array(v); } + static const PackedStringArray *get_ptr(const Variant *v) { return VariantInternal::get_string_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedVector2Array> { + static PackedVector2Array *get_ptr(Variant *v) { return VariantInternal::get_vector2_array(v); } + static const PackedVector2Array *get_ptr(const Variant *v) { return VariantInternal::get_vector2_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedVector3Array> { + static PackedVector3Array *get_ptr(Variant *v) { return VariantInternal::get_vector3_array(v); } + static const PackedVector3Array *get_ptr(const Variant *v) { return VariantInternal::get_vector3_array(v); } +}; + +template <> +struct VariantGetInternalPtr<PackedColorArray> { + static PackedColorArray *get_ptr(Variant *v) { return VariantInternal::get_color_array(v); } + static const PackedColorArray *get_ptr(const Variant *v) { return VariantInternal::get_color_array(v); } +}; + +template <class T> +struct VariantInternalAccessor { +}; + +template <> +struct VariantInternalAccessor<bool> { + static _FORCE_INLINE_ bool get(const Variant *v) { return *VariantInternal::get_bool(v); } + static _FORCE_INLINE_ void set(Variant *v, bool p_value) { *VariantInternal::get_bool(v) = p_value; } +}; + +#define VARIANT_ACCESSOR_NUMBER(m_type) \ + template <> \ + struct VariantInternalAccessor<m_type> { \ + static _FORCE_INLINE_ m_type get(const Variant *v) { return (m_type)*VariantInternal::get_int(v); } \ + static _FORCE_INLINE_ void set(Variant *v, m_type p_value) { *VariantInternal::get_int(v) = p_value; } \ + }; + +VARIANT_ACCESSOR_NUMBER(int8_t) +VARIANT_ACCESSOR_NUMBER(uint8_t) +VARIANT_ACCESSOR_NUMBER(int16_t) +VARIANT_ACCESSOR_NUMBER(uint16_t) +VARIANT_ACCESSOR_NUMBER(int32_t) +VARIANT_ACCESSOR_NUMBER(uint32_t) +VARIANT_ACCESSOR_NUMBER(int64_t) +VARIANT_ACCESSOR_NUMBER(uint64_t) +VARIANT_ACCESSOR_NUMBER(char32_t) +VARIANT_ACCESSOR_NUMBER(Error) +VARIANT_ACCESSOR_NUMBER(Side) + +template <> +struct VariantInternalAccessor<ObjectID> { + static _FORCE_INLINE_ ObjectID get(const Variant *v) { return ObjectID(*VariantInternal::get_int(v)); } + static _FORCE_INLINE_ void set(Variant *v, ObjectID p_value) { *VariantInternal::get_int(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<float> { + static _FORCE_INLINE_ float get(const Variant *v) { return *VariantInternal::get_float(v); } + static _FORCE_INLINE_ void set(Variant *v, float p_value) { *VariantInternal::get_float(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<double> { + static _FORCE_INLINE_ double get(const Variant *v) { return *VariantInternal::get_float(v); } + static _FORCE_INLINE_ void set(Variant *v, double p_value) { *VariantInternal::get_float(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<String> { + static _FORCE_INLINE_ const String &get(const Variant *v) { return *VariantInternal::get_string(v); } + static _FORCE_INLINE_ void set(Variant *v, const String &p_value) { *VariantInternal::get_string(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Vector2> { + static _FORCE_INLINE_ const Vector2 &get(const Variant *v) { return *VariantInternal::get_vector2(v); } + static _FORCE_INLINE_ void set(Variant *v, const Vector2 &p_value) { *VariantInternal::get_vector2(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Vector2i> { + static _FORCE_INLINE_ const Vector2i &get(const Variant *v) { return *VariantInternal::get_vector2i(v); } + static _FORCE_INLINE_ void set(Variant *v, const Vector2i &p_value) { *VariantInternal::get_vector2i(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Rect2> { + static _FORCE_INLINE_ const Rect2 &get(const Variant *v) { return *VariantInternal::get_rect2(v); } + static _FORCE_INLINE_ void set(Variant *v, const Rect2 &p_value) { *VariantInternal::get_rect2(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Rect2i> { + static _FORCE_INLINE_ const Rect2i &get(const Variant *v) { return *VariantInternal::get_rect2i(v); } + static _FORCE_INLINE_ void set(Variant *v, const Rect2i &p_value) { *VariantInternal::get_rect2i(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Vector3> { + static _FORCE_INLINE_ const Vector3 &get(const Variant *v) { return *VariantInternal::get_vector3(v); } + static _FORCE_INLINE_ void set(Variant *v, const Vector3 &p_value) { *VariantInternal::get_vector3(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Vector3i> { + static _FORCE_INLINE_ const Vector3i &get(const Variant *v) { return *VariantInternal::get_vector3i(v); } + static _FORCE_INLINE_ void set(Variant *v, const Vector3i &p_value) { *VariantInternal::get_vector3i(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Transform2D> { + static _FORCE_INLINE_ const Transform2D &get(const Variant *v) { return *VariantInternal::get_transform2d(v); } + static _FORCE_INLINE_ void set(Variant *v, const Transform2D &p_value) { *VariantInternal::get_transform2d(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Transform> { + static _FORCE_INLINE_ const Transform &get(const Variant *v) { return *VariantInternal::get_transform(v); } + static _FORCE_INLINE_ void set(Variant *v, const Transform &p_value) { *VariantInternal::get_transform(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Plane> { + static _FORCE_INLINE_ const Plane &get(const Variant *v) { return *VariantInternal::get_plane(v); } + static _FORCE_INLINE_ void set(Variant *v, const Plane &p_value) { *VariantInternal::get_plane(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Quat> { + static _FORCE_INLINE_ const Quat &get(const Variant *v) { return *VariantInternal::get_quat(v); } + static _FORCE_INLINE_ void set(Variant *v, const Quat &p_value) { *VariantInternal::get_quat(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<AABB> { + static _FORCE_INLINE_ const AABB &get(const Variant *v) { return *VariantInternal::get_aabb(v); } + static _FORCE_INLINE_ void set(Variant *v, const AABB &p_value) { *VariantInternal::get_aabb(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Basis> { + static _FORCE_INLINE_ const Basis &get(const Variant *v) { return *VariantInternal::get_basis(v); } + static _FORCE_INLINE_ void set(Variant *v, const Basis &p_value) { *VariantInternal::get_basis(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Color> { + static _FORCE_INLINE_ const Color &get(const Variant *v) { return *VariantInternal::get_color(v); } + static _FORCE_INLINE_ void set(Variant *v, const Color &p_value) { *VariantInternal::get_color(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<StringName> { + static _FORCE_INLINE_ const StringName &get(const Variant *v) { return *VariantInternal::get_string_name(v); } + static _FORCE_INLINE_ void set(Variant *v, const StringName &p_value) { *VariantInternal::get_string_name(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<NodePath> { + static _FORCE_INLINE_ const NodePath &get(const Variant *v) { return *VariantInternal::get_node_path(v); } + static _FORCE_INLINE_ void set(Variant *v, const NodePath &p_value) { *VariantInternal::get_node_path(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<::RID> { + static _FORCE_INLINE_ const ::RID &get(const Variant *v) { return *VariantInternal::get_rid(v); } + static _FORCE_INLINE_ void set(Variant *v, const ::RID &p_value) { *VariantInternal::get_rid(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Callable> { + static _FORCE_INLINE_ const Callable &get(const Variant *v) { return *VariantInternal::get_callable(v); } + static _FORCE_INLINE_ void set(Variant *v, const Callable &p_value) { *VariantInternal::get_callable(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Signal> { + static _FORCE_INLINE_ const Signal &get(const Variant *v) { return *VariantInternal::get_signal(v); } + static _FORCE_INLINE_ void set(Variant *v, const Signal &p_value) { *VariantInternal::get_signal(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Dictionary> { + static _FORCE_INLINE_ const Dictionary &get(const Variant *v) { return *VariantInternal::get_dictionary(v); } + static _FORCE_INLINE_ void set(Variant *v, const Dictionary &p_value) { *VariantInternal::get_dictionary(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Array> { + static _FORCE_INLINE_ const Array &get(const Variant *v) { return *VariantInternal::get_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const Array &p_value) { *VariantInternal::get_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedByteArray> { + static _FORCE_INLINE_ const PackedByteArray &get(const Variant *v) { return *VariantInternal::get_byte_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedByteArray &p_value) { *VariantInternal::get_byte_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedInt32Array> { + static _FORCE_INLINE_ const PackedInt32Array &get(const Variant *v) { return *VariantInternal::get_int32_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedInt32Array &p_value) { *VariantInternal::get_int32_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedInt64Array> { + static _FORCE_INLINE_ const PackedInt64Array &get(const Variant *v) { return *VariantInternal::get_int64_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedInt64Array &p_value) { *VariantInternal::get_int64_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedFloat32Array> { + static _FORCE_INLINE_ const PackedFloat32Array &get(const Variant *v) { return *VariantInternal::get_float32_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedFloat32Array &p_value) { *VariantInternal::get_float32_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedFloat64Array> { + static _FORCE_INLINE_ const PackedFloat64Array &get(const Variant *v) { return *VariantInternal::get_float64_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedFloat64Array &p_value) { *VariantInternal::get_float64_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedStringArray> { + static _FORCE_INLINE_ const PackedStringArray &get(const Variant *v) { return *VariantInternal::get_string_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedStringArray &p_value) { *VariantInternal::get_string_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedVector2Array> { + static _FORCE_INLINE_ const PackedVector2Array &get(const Variant *v) { return *VariantInternal::get_vector2_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedVector2Array &p_value) { *VariantInternal::get_vector2_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedVector3Array> { + static _FORCE_INLINE_ const PackedVector3Array &get(const Variant *v) { return *VariantInternal::get_vector3_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedVector3Array &p_value) { *VariantInternal::get_vector3_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<PackedColorArray> { + static _FORCE_INLINE_ const PackedColorArray &get(const Variant *v) { return *VariantInternal::get_color_array(v); } + static _FORCE_INLINE_ void set(Variant *v, const PackedColorArray &p_value) { *VariantInternal::get_color_array(v) = p_value; } +}; + +template <> +struct VariantInternalAccessor<Object *> { + static _FORCE_INLINE_ Object *get(const Variant *v) { return const_cast<Object *>(*VariantInternal::get_object(v)); } + static _FORCE_INLINE_ void set(Variant *v, const Object *p_value) { *VariantInternal::get_object(v) = const_cast<Object *>(p_value); } +}; + +template <> +struct VariantInternalAccessor<Variant> { + static _FORCE_INLINE_ Variant &get(Variant *v) { return *v; } + static _FORCE_INLINE_ const Variant &get(const Variant *v) { return *v; } + static _FORCE_INLINE_ void set(Variant *v, const Variant &p_value) { *v = p_value; } +}; + +template <> +struct VariantInternalAccessor<Vector<Variant>> { + static _FORCE_INLINE_ Vector<Variant> get(const Variant *v) { + Vector<Variant> ret; + int s = VariantInternal::get_array(v)->size(); + ret.resize(s); + for (int i = 0; i < s; i++) { + ret.write[i] = VariantInternal::get_array(v)->get(i); + } + + return ret; + } + static _FORCE_INLINE_ void set(Variant *v, const Vector<Variant> &p_value) { + int s = p_value.size(); + VariantInternal::get_array(v)->resize(s); + for (int i = 0; i < s; i++) { + VariantInternal::get_array(v)->set(i, p_value[i]); + } + } +}; + +template <class T> +struct VariantInitializer { +}; + +template <> +struct VariantInitializer<bool> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<bool>(v); } +}; + +#define INITIALIZER_INT(m_type) \ + template <> \ + struct VariantInitializer<m_type> { \ + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<int64_t>(v); } \ + }; + +INITIALIZER_INT(uint8_t) +INITIALIZER_INT(int8_t) +INITIALIZER_INT(uint16_t) +INITIALIZER_INT(int16_t) +INITIALIZER_INT(uint32_t) +INITIALIZER_INT(int32_t) +INITIALIZER_INT(uint64_t) +INITIALIZER_INT(int64_t) +INITIALIZER_INT(char32_t) +INITIALIZER_INT(Error) +INITIALIZER_INT(ObjectID) + +template <> +struct VariantInitializer<double> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<double>(v); } +}; + +template <> +struct VariantInitializer<float> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<double>(v); } +}; + +template <> +struct VariantInitializer<String> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_string(v); } +}; + +template <> +struct VariantInitializer<Vector2> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Vector2>(v); } +}; + +template <> +struct VariantInitializer<Vector2i> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Vector2i>(v); } +}; + +template <> +struct VariantInitializer<Rect2> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Rect2>(v); } +}; + +template <> +struct VariantInitializer<Rect2i> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Rect2i>(v); } +}; + +template <> +struct VariantInitializer<Vector3> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Vector3>(v); } +}; + +template <> +struct VariantInitializer<Vector3i> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Vector3i>(v); } +}; + +template <> +struct VariantInitializer<Transform2D> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform2d(v); } +}; + +template <> +struct VariantInitializer<Plane> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Plane>(v); } +}; + +template <> +struct VariantInitializer<Quat> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Quat>(v); } +}; + +template <> +struct VariantInitializer<AABB> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_aabb(v); } +}; + +template <> +struct VariantInitializer<Basis> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_basis(v); } +}; + +template <> +struct VariantInitializer<Transform> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform(v); } +}; + +template <> +struct VariantInitializer<Color> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Color>(v); } +}; + +template <> +struct VariantInitializer<StringName> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_string_name(v); } +}; + +template <> +struct VariantInitializer<NodePath> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_node_path(v); } +}; + +template <> +struct VariantInitializer<::RID> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<::RID>(v); } +}; + +template <> +struct VariantInitializer<Callable> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_callable(v); } +}; + +template <> +struct VariantInitializer<Signal> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_signal(v); } +}; + +template <> +struct VariantInitializer<Dictionary> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_dictionary(v); } +}; + +template <> +struct VariantInitializer<Array> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_array(v); } +}; + +template <> +struct VariantInitializer<PackedByteArray> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_byte_array(v); } +}; + +template <> +struct VariantInitializer<PackedInt32Array> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_int32_array(v); } +}; + +template <> +struct VariantInitializer<PackedInt64Array> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_int64_array(v); } +}; + +template <> +struct VariantInitializer<PackedFloat32Array> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_float32_array(v); } +}; + +template <> +struct VariantInitializer<PackedFloat64Array> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_float64_array(v); } +}; + +template <> +struct VariantInitializer<PackedStringArray> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_string_array(v); } +}; + +template <> +struct VariantInitializer<PackedVector2Array> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_vector2_array(v); } +}; + +template <> +struct VariantInitializer<PackedVector3Array> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_vector3_array(v); } +}; + +template <> +struct VariantInitializer<PackedColorArray> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_color_array(v); } +}; + +template <class T> +struct VariantZeroAssigner { +}; + +template <> +struct VariantZeroAssigner<bool> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_bool(v) = false; } +}; + +template <> +struct VariantZeroAssigner<int64_t> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int(v) = 0; } +}; + +template <> +struct VariantZeroAssigner<double> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; } +}; + +template <> +struct VariantZeroAssigner<float> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; } +}; + +template <> +struct VariantZeroAssigner<String> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<Vector2> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); } +}; + +template <> +struct VariantZeroAssigner<Vector2i> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); } +}; + +template <> +struct VariantZeroAssigner<Rect2> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); } +}; + +template <> +struct VariantZeroAssigner<Rect2i> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); } +}; + +template <> +struct VariantZeroAssigner<Vector3> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); } +}; + +template <> +struct VariantZeroAssigner<Vector3i> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); } +}; + +template <> +struct VariantZeroAssigner<Transform2D> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); } +}; + +template <> +struct VariantZeroAssigner<Plane> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_plane(v) = Plane(); } +}; + +template <> +struct VariantZeroAssigner<Quat> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_quat(v) = Quat(); } +}; + +template <> +struct VariantZeroAssigner<AABB> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); } +}; + +template <> +struct VariantZeroAssigner<Basis> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_basis(v) = Basis(); } +}; + +template <> +struct VariantZeroAssigner<Transform> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform(v) = Transform(); } +}; + +template <> +struct VariantZeroAssigner<Color> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color(v) = Color(); } +}; + +template <> +struct VariantZeroAssigner<StringName> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<NodePath> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<::RID> { + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rid(v) = RID(); } +}; + +template <> +struct VariantZeroAssigner<Callable> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<Signal> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<Dictionary> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<Array> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedByteArray> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedInt32Array> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedInt64Array> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedFloat32Array> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedFloat64Array> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedStringArray> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedVector2Array> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedVector3Array> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <> +struct VariantZeroAssigner<PackedColorArray> { + static _FORCE_INLINE_ void zero(Variant *v) {} +}; + +template <class T> +struct VariantTypeChanger { + static _FORCE_INLINE_ void change(Variant *v) { + if (v->get_type() != GetTypeInfo<T>::VARIANT_TYPE || GetTypeInfo<T>::VARIANT_TYPE >= Variant::PACKED_BYTE_ARRAY) { //second condition removed by optimizer + VariantInternal::clear(v); + VariantInitializer<T>::init(v); + } + } + static _FORCE_INLINE_ void change_and_reset(Variant *v) { + if (v->get_type() != GetTypeInfo<T>::VARIANT_TYPE || GetTypeInfo<T>::VARIANT_TYPE >= Variant::PACKED_BYTE_ARRAY) { //second condition removed by optimizer + VariantInternal::clear(v); + VariantInitializer<T>::init(v); + } + + VariantZeroAssigner<T>::zero(v); + } +}; + +template <class T> +struct VariantTypeAdjust { + _FORCE_INLINE_ static void adjust(Variant *r_ret) { + VariantTypeChanger<typename GetSimpleTypeT<T>::type_t>::change(r_ret); + } +}; + +template <> +struct VariantTypeAdjust<Variant> { + _FORCE_INLINE_ static void adjust(Variant *r_ret) { + // Do nothing for variant. + } +}; + +template <> +struct VariantTypeAdjust<Object *> { + _FORCE_INLINE_ static void adjust(Variant *r_ret) { + VariantInternal::clear(r_ret); + *r_ret = (Object *)nullptr; + } +}; + +#endif // VARIANT_INTERNAL_H diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp new file mode 100644 index 0000000000..e0a3cf4215 --- /dev/null +++ b/core/variant/variant_op.cpp @@ -0,0 +1,2018 @@ +/*************************************************************************/ +/* variant_op.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "variant.h" + +#include "core/core_string_names.h" +#include "core/debugger/engine_debugger.h" +#include "core/object/class_db.h" + +template <class R, class A, class B> +class OperatorEvaluatorAdd { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a + b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) + *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) + PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorSub { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a - b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) - *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) - PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorMul { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a * b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) * *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) * PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorXForm { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a.xform(b); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = VariantGetInternalPtr<A>::get_ptr(left)->xform(*VariantGetInternalPtr<B>::get_ptr(right)); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left).xform(PtrToArg<B>::convert(right)), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorXFormInv { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = b.xform_inv(a); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = VariantGetInternalPtr<B>::get_ptr(right)->xform_inv(*VariantGetInternalPtr<A>::get_ptr(left)); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<B>::convert(right).xform_inv(PtrToArg<A>::convert(left)), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorDiv { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a / b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) / *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) / PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorDivNZ { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + if (b == 0) { + r_valid = false; + *r_ret = "Division by zero error"; + return; + } + *r_ret = a / b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) / *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) / PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorMod { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a % b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) % *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) % PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorModNZ { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + if (b == 0) { + r_valid = false; + *r_ret = "Module by zero error"; + return; + } + *r_ret = a % b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) % *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) % PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A> +class OperatorEvaluatorNeg { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + *r_ret = -a; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = -*VariantGetInternalPtr<A>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(-PtrToArg<A>::convert(left), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A> +class OperatorEvaluatorPos { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + *r_ret = a; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorShiftLeft { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a << b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) << *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) << PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorShiftRight { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a >> b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) >> *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) >> PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorBitOr { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a | b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) | *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) | PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorBitAnd { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a & b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) & *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) & PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A, class B> +class OperatorEvaluatorBitXor { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a ^ b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) ^ *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(PtrToArg<A>::convert(left) ^ PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class R, class A> +class OperatorEvaluatorBitNeg { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + *r_ret = ~a; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<R>::change(r_ret); + *VariantGetInternalPtr<R>::get_ptr(r_ret) = ~*VariantGetInternalPtr<A>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<R>::encode(~PtrToArg<A>::convert(left), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } +}; + +template <class A, class B> +class OperatorEvaluatorEqual { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a == b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) == *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<A>::convert(left) == PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorEqualObject { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Object *a = p_left.get_validated_object(); + const Object *b = p_right.get_validated_object(); + *r_ret = a == b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Object *a = left->get_validated_object(); + const Object *b = right->get_validated_object(); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == PtrToArg<Object *>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorEqualObjectNil { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Object *a = p_left.get_validated_object(); + *r_ret = a == nullptr; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Object *a = left->get_validated_object(); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == nullptr, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorEqualNilObject { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Object *b = p_right.get_validated_object(); + *r_ret = nullptr == b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Object *b = right->get_validated_object(); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(nullptr == PtrToArg<Object *>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A, class B> +class OperatorEvaluatorNotEqual { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a != b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) != *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<A>::convert(left) != PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorNotEqualObject { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + Object *a = p_left.get_validated_object(); + Object *b = p_right.get_validated_object(); + *r_ret = a != b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + Object *a = left->get_validated_object(); + Object *b = right->get_validated_object(); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) != PtrToArg<Object *>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorNotEqualObjectNil { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + Object *a = p_left.get_validated_object(); + *r_ret = a != nullptr; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + Object *a = left->get_validated_object(); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) != nullptr, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorNotEqualNilObject { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + Object *b = p_right.get_validated_object(); + *r_ret = nullptr != b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + Object *b = right->get_validated_object(); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(nullptr != PtrToArg<Object *>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A, class B> +class OperatorEvaluatorLess { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a < b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) < *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<A>::convert(left) < PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A, class B> +class OperatorEvaluatorLessEqual { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a <= b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) <= *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<A>::convert(left) <= PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A, class B> +class OperatorEvaluatorGreater { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a > b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) > *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<A>::convert(left) > PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A, class B> +class OperatorEvaluatorGreaterEqual { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a >= b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) >= *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<A>::convert(left) >= PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A, class B> +class OperatorEvaluatorAnd { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a && b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) && *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<A>::convert(left) && PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A, class B> +class OperatorEvaluatorOr { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = a || b; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) || *VariantGetInternalPtr<B>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<A>::convert(left) || PtrToArg<B>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +#define XOR_OP(m_a, m_b) (((m_a) || (m_b)) && !((m_a) && (m_b))) +template <class A, class B> +class OperatorEvaluatorXor { +public: + _FORCE_INLINE_ static bool xor_op(const A &a, const B &b) { + return ((a) || (b)) && !((a) && (b)); + } + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + *r_ret = xor_op(a, b); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = xor_op(*VariantGetInternalPtr<A>::get_ptr(left), *VariantGetInternalPtr<B>::get_ptr(right)); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(xor_op(PtrToArg<A>::convert(left), PtrToArg<B>::convert(right)), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A> +class OperatorEvaluatorNot { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + *r_ret = !a; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<A>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(!PtrToArg<A>::convert(left)); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +//// CUSTOM //// + +class OperatorEvaluatorAddArray { +public: + _FORCE_INLINE_ static void _add_arrays(Array &sum, const Array &array_a, const Array &array_b) { + int asize = array_a.size(); + int bsize = array_b.size(); + sum.resize(asize + bsize); + for (int i = 0; i < asize; i++) { + sum[i] = array_a[i]; + } + for (int i = 0; i < bsize; i++) { + sum[i + asize] = array_b[i]; + } + } + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Array &array_a = *VariantGetInternalPtr<Array>::get_ptr(&p_left); + const Array &array_b = *VariantGetInternalPtr<Array>::get_ptr(&p_right); + Array sum; + _add_arrays(sum, array_a, array_b); + *r_ret = sum; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<Array>::change(r_ret); + _add_arrays(*VariantGetInternalPtr<Array>::get_ptr(r_ret), *VariantGetInternalPtr<Array>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right)); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + Array ret; + _add_arrays(ret, PtrToArg<Array>::convert(left), PtrToArg<Array>::convert(right)); + PtrToArg<Array>::encode(ret, r_ret); + } + static Variant::Type get_return_type() { return Variant::ARRAY; } +}; + +template <class T> +class OperatorEvaluatorAppendArray { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Vector<T> &array_a = *VariantGetInternalPtr<Vector<T>>::get_ptr(&p_left); + const Vector<T> &array_b = *VariantGetInternalPtr<Vector<T>>::get_ptr(&p_right); + Vector<T> sum = array_a; + sum.append_array(array_b); + *r_ret = sum; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<Vector<T>>::change(r_ret); + *VariantGetInternalPtr<Vector<T>>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector<T>>::get_ptr(left); + VariantGetInternalPtr<Vector<T>>::get_ptr(r_ret)->append_array(*VariantGetInternalPtr<Vector<T>>::get_ptr(right)); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + Vector<T> sum = PtrToArg<Vector<T>>::convert(left); + sum.append_array(PtrToArg<Vector<T>>::convert(right)); + PtrToArg<Vector<T>>::encode(sum, r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<Vector<T>>::VARIANT_TYPE; } +}; + +class OperatorEvaluatorStringModNil { +public: + _FORCE_INLINE_ static String do_mod(const String &s, bool *r_valid) { + Array values; + values.push_back(Variant()); + + String a = s.sprintf(values, r_valid); + if (r_valid) { + *r_valid = !*r_valid; + } + return a; + } + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left); + *r_ret = do_mod(a, &r_valid); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<String>::change(r_ret); + *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), nullptr); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), nullptr), r_ret); + } + static Variant::Type get_return_type() { return Variant::STRING; } +}; + +class OperatorEvaluatorStringModArray { +public: + _FORCE_INLINE_ static String do_mod(const String &s, const Array &p_values, bool *r_valid) { + String a = s.sprintf(p_values, r_valid); + if (r_valid) { + *r_valid = !*r_valid; + } + return a; + } + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left); + *r_ret = do_mod(a, *VariantGetInternalPtr<Array>::get_ptr(&p_right), &r_valid); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<String>::change(r_ret); + *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right), nullptr); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<Array>::convert(right), nullptr), r_ret); + } + static Variant::Type get_return_type() { return Variant::STRING; } +}; + +class OperatorEvaluatorStringModObject { +public: + _FORCE_INLINE_ static String do_mod(const String &s, const Object *p_object, bool *r_valid) { + Array values; + values.push_back(p_object); + String a = s.sprintf(values, r_valid); + if (r_valid) { + *r_valid = !*r_valid; + } + + return a; + } + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left); + *r_ret = do_mod(a, p_right.get_validated_object(), &r_valid); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<String>::change(r_ret); + *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), right->get_validated_object(), nullptr); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<Object *>::convert(right), nullptr), r_ret); + } + static Variant::Type get_return_type() { return Variant::STRING; } +}; + +template <class T> +class OperatorEvaluatorStringModT { +public: + _FORCE_INLINE_ static String do_mod(const String &s, const T &p_value, bool *r_valid) { + Array values; + values.push_back(p_value); + String a = s.sprintf(values, r_valid); + if (r_valid) { + *r_valid = !*r_valid; + } + return a; + } + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left); + *r_ret = do_mod(a, *VariantGetInternalPtr<T>::get_ptr(&p_right), &r_valid); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<String>::change(r_ret); + *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), *VariantGetInternalPtr<T>::get_ptr(right), nullptr); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<T>::convert(right), nullptr), r_ret); + } + static Variant::Type get_return_type() { return Variant::STRING; } +}; + +template <Variant::Operator op, Variant::Type type_left, Variant::Type type_right> +class OperatorEvaluatorAlwaysTrue { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + *r_ret = true; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = true; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(true, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <Variant::Operator op, Variant::Type type_left, Variant::Type type_right> +class OperatorEvaluatorAlwaysFalse { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + *r_ret = false; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = false; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(false, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +///// OR /////// + +_FORCE_INLINE_ static bool _operate_or(bool p_left, bool p_right) { + return p_left || p_right; +} + +_FORCE_INLINE_ static bool _operate_and(bool p_left, bool p_right) { + return p_left && p_right; +} + +_FORCE_INLINE_ static bool _operate_xor(bool p_left, bool p_right) { + return (p_left || p_right) && !(p_left && p_right); +} + +_FORCE_INLINE_ static bool _operate_get_nil(const Variant *p_ptr) { + return p_ptr->get_validated_object() != nullptr; +} + +_FORCE_INLINE_ static bool _operate_get_bool(const Variant *p_ptr) { + return *VariantGetInternalPtr<bool>::get_ptr(p_ptr); +} + +_FORCE_INLINE_ static bool _operate_get_int(const Variant *p_ptr) { + return *VariantGetInternalPtr<int64_t>::get_ptr(p_ptr) != 0; +} + +_FORCE_INLINE_ static bool _operate_get_float(const Variant *p_ptr) { + return *VariantGetInternalPtr<double>::get_ptr(p_ptr) != 0.0; +} + +_FORCE_INLINE_ static bool _operate_get_object(const Variant *p_ptr) { + return p_ptr->get_validated_object() != nullptr; +} + +_FORCE_INLINE_ static bool _operate_get_ptr_nil(const void *p_ptr) { + return false; +} + +_FORCE_INLINE_ static bool _operate_get_ptr_bool(const void *p_ptr) { + return PtrToArg<bool>::convert(p_ptr); +} + +_FORCE_INLINE_ static bool _operate_get_ptr_int(const void *p_ptr) { + return PtrToArg<int64_t>::convert(p_ptr) != 0; +} + +_FORCE_INLINE_ static bool _operate_get_ptr_float(const void *p_ptr) { + return PtrToArg<double>::convert(p_ptr) != 0.0; +} + +_FORCE_INLINE_ static bool _operate_get_ptr_object(const void *p_ptr) { + return PtrToArg<Object *>::convert(p_ptr) != nullptr; +} + +#define OP_EVALUATOR(m_class_name, m_left, m_right, m_op) \ + class m_class_name { \ + public: \ + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { \ + *r_ret = m_op(_operate_get_##m_left(&p_left), _operate_get_##m_right(&p_right)); \ + r_valid = true; \ + } \ + \ + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { \ + VariantTypeChanger<bool>::change(r_ret); \ + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = m_op(_operate_get_##m_left(left), _operate_get_##m_right(right)); \ + } \ + \ + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { \ + PtrToArg<bool>::encode(m_op(_operate_get_ptr_##m_left(left), _operate_get_ptr_##m_right(right)), r_ret); \ + } \ + \ + static Variant::Type get_return_type() { \ + return Variant::BOOL; \ + } \ + }; + +// OR + +// nil +OP_EVALUATOR(OperatorEvaluatorNilXBoolOr, nil, bool, _operate_or) +OP_EVALUATOR(OperatorEvaluatorBoolXNilOr, bool, nil, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorNilXIntOr, nil, int, _operate_or) +OP_EVALUATOR(OperatorEvaluatorIntXNilOr, int, nil, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorNilXFloatOr, nil, float, _operate_or) +OP_EVALUATOR(OperatorEvaluatorFloatXNilOr, float, nil, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorObjectXNilOr, object, nil, _operate_or) +OP_EVALUATOR(OperatorEvaluatorNilXObjectOr, nil, object, _operate_or) + +// bool +OP_EVALUATOR(OperatorEvaluatorBoolXBoolOr, bool, bool, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorBoolXIntOr, bool, int, _operate_or) +OP_EVALUATOR(OperatorEvaluatorIntXBoolOr, int, bool, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorBoolXFloatOr, bool, float, _operate_or) +OP_EVALUATOR(OperatorEvaluatorFloatXBoolOr, float, bool, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorBoolXObjectOr, bool, object, _operate_or) +OP_EVALUATOR(OperatorEvaluatorObjectXBoolOr, object, bool, _operate_or) + +// int +OP_EVALUATOR(OperatorEvaluatorIntXIntOr, int, int, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorIntXFloatOr, int, float, _operate_or) +OP_EVALUATOR(OperatorEvaluatorFloatXIntOr, float, int, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorIntXObjectOr, int, object, _operate_or) +OP_EVALUATOR(OperatorEvaluatorObjectXIntOr, object, int, _operate_or) + +// float +OP_EVALUATOR(OperatorEvaluatorFloatXFloatOr, float, float, _operate_or) + +OP_EVALUATOR(OperatorEvaluatorFloatXObjectOr, float, object, _operate_or) +OP_EVALUATOR(OperatorEvaluatorObjectXFloatOr, object, float, _operate_or) + +// object +OP_EVALUATOR(OperatorEvaluatorObjectXObjectOr, object, object, _operate_or) + +// AND + +// nil +OP_EVALUATOR(OperatorEvaluatorNilXBoolAnd, nil, bool, _operate_and) +OP_EVALUATOR(OperatorEvaluatorBoolXNilAnd, bool, nil, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorNilXIntAnd, nil, int, _operate_and) +OP_EVALUATOR(OperatorEvaluatorIntXNilAnd, int, nil, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorNilXFloatAnd, nil, float, _operate_and) +OP_EVALUATOR(OperatorEvaluatorFloatXNilAnd, float, nil, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorObjectXNilAnd, object, nil, _operate_and) +OP_EVALUATOR(OperatorEvaluatorNilXObjectAnd, nil, object, _operate_and) + +// bool +OP_EVALUATOR(OperatorEvaluatorBoolXBoolAnd, bool, bool, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorBoolXIntAnd, bool, int, _operate_and) +OP_EVALUATOR(OperatorEvaluatorIntXBoolAnd, int, bool, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorBoolXFloatAnd, bool, float, _operate_and) +OP_EVALUATOR(OperatorEvaluatorFloatXBoolAnd, float, bool, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorBoolXObjectAnd, bool, object, _operate_and) +OP_EVALUATOR(OperatorEvaluatorObjectXBoolAnd, object, bool, _operate_and) + +// int +OP_EVALUATOR(OperatorEvaluatorIntXIntAnd, int, int, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorIntXFloatAnd, int, float, _operate_and) +OP_EVALUATOR(OperatorEvaluatorFloatXIntAnd, float, int, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorIntXObjectAnd, int, object, _operate_and) +OP_EVALUATOR(OperatorEvaluatorObjectXIntAnd, object, int, _operate_and) + +// float +OP_EVALUATOR(OperatorEvaluatorFloatXFloatAnd, float, float, _operate_and) + +OP_EVALUATOR(OperatorEvaluatorFloatXObjectAnd, float, object, _operate_and) +OP_EVALUATOR(OperatorEvaluatorObjectXFloatAnd, object, float, _operate_and) + +// object +OP_EVALUATOR(OperatorEvaluatorObjectXObjectAnd, object, object, _operate_and) + +// XOR + +// nil +OP_EVALUATOR(OperatorEvaluatorNilXBoolXor, nil, bool, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorBoolXNilXor, bool, nil, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorNilXIntXor, nil, int, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorIntXNilXor, int, nil, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorNilXFloatXor, nil, float, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorFloatXNilXor, float, nil, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorObjectXNilXor, object, nil, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorNilXObjectXor, nil, object, _operate_xor) + +// bool +OP_EVALUATOR(OperatorEvaluatorBoolXBoolXor, bool, bool, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorBoolXIntXor, bool, int, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorIntXBoolXor, int, bool, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorBoolXFloatXor, bool, float, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorFloatXBoolXor, float, bool, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorBoolXObjectXor, bool, object, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorObjectXBoolXor, object, bool, _operate_xor) + +// int +OP_EVALUATOR(OperatorEvaluatorIntXIntXor, int, int, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorIntXFloatXor, int, float, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorFloatXIntXor, float, int, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorIntXObjectXor, int, object, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorObjectXIntXor, object, int, _operate_xor) + +// float +OP_EVALUATOR(OperatorEvaluatorFloatXFloatXor, float, float, _operate_xor) + +OP_EVALUATOR(OperatorEvaluatorFloatXObjectXor, float, object, _operate_xor) +OP_EVALUATOR(OperatorEvaluatorObjectXFloatXor, object, float, _operate_xor) + +// object +OP_EVALUATOR(OperatorEvaluatorObjectXObjectXor, object, object, _operate_xor) + +class OperatorEvaluatorNotBool { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + *r_ret = !*VariantGetInternalPtr<bool>::get_ptr(&p_left); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<bool>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(!PtrToArg<bool>::convert(left), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorNotInt { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + *r_ret = !*VariantGetInternalPtr<int64_t>::get_ptr(&p_left); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<int64_t>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(!PtrToArg<int64_t>::convert(left), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorNotFloat { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + *r_ret = !*VariantGetInternalPtr<double>::get_ptr(&p_left); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<double>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(!PtrToArg<double>::convert(left), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorNotObject { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + *r_ret = p_left.get_validated_object() == nullptr; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = left->get_validated_object() == nullptr; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == nullptr, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +//// + +class OperatorEvaluatorInStringFind { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const String &str_a = *VariantGetInternalPtr<String>::get_ptr(&p_left); + const String &str_b = *VariantGetInternalPtr<String>::get_ptr(&p_right); + + *r_ret = str_b.find(str_a) != -1; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const String &str_a = *VariantGetInternalPtr<String>::get_ptr(left); + const String &str_b = *VariantGetInternalPtr<String>::get_ptr(right); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = str_b.find(str_a) != -1; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<String>::convert(right).find(PtrToArg<String>::convert(left)) != -1, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A, class B> +class OperatorEvaluatorInArrayFind { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); + + *r_ret = b.find(a) != -1; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const A &a = *VariantGetInternalPtr<A>::get_ptr(left); + const B &b = *VariantGetInternalPtr<B>::get_ptr(right); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(a) != -1; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<B>::convert(right).find(PtrToArg<A>::convert(left)) != -1, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorInArrayFindNil { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Array &b = *VariantGetInternalPtr<Array>::get_ptr(&p_right); + *r_ret = b.find(Variant()) != -1; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Array &b = *VariantGetInternalPtr<Array>::get_ptr(right); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(Variant()) != -1; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Array>::convert(right).find(Variant()) != -1, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorInArrayFindObject { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Array &b = *VariantGetInternalPtr<Array>::get_ptr(&p_right); + *r_ret = b.find(p_left) != -1; + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Array &b = *VariantGetInternalPtr<Array>::get_ptr(right); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(*left) != -1; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Array>::convert(right).find(PtrToArg<Object *>::convert(left)) != -1, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class A> +class OperatorEvaluatorInDictionaryHas { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right); + const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left); + + *r_ret = b.has(a); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right); + const A &a = *VariantGetInternalPtr<A>::get_ptr(left); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(a); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(PtrToArg<A>::convert(left)), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorInDictionaryHasNil { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right); + + *r_ret = b.has(Variant()); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(Variant()); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(Variant()), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorInDictionaryHasObject { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right); + + *r_ret = b.has(p_left); + r_valid = true; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(*left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(PtrToArg<Object *>::convert(left)), r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorObjectHasPropertyString { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + Object *b = p_right.get_validated_object(); + if (!b) { + *r_ret = "Invalid base object for 'in'"; + r_valid = false; + return; + } + + const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left); + + b->get(a, &r_valid); + *r_ret = r_valid; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + Object *l = right->get_validated_object(); + ERR_FAIL_COND(l == nullptr); + const String &a = *VariantGetInternalPtr<String>::get_ptr(left); + + bool valid; + l->get(a, &valid); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = valid; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + bool valid; + PtrToArg<Object *>::convert(right)->get(PtrToArg<String>::convert(left), &valid); + PtrToArg<bool>::encode(valid, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +class OperatorEvaluatorObjectHasPropertyStringName { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + Object *b = p_right.get_validated_object(); + if (!b) { + *r_ret = "Invalid base object for 'in'"; + r_valid = false; + return; + } + + const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(&p_left); + + b->get(a, &r_valid); + *r_ret = r_valid; + } + static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + Object *l = right->get_validated_object(); + ERR_FAIL_COND(l == nullptr); + const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(left); + + bool valid; + l->get(a, &valid); + VariantTypeChanger<bool>::change(r_ret); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = valid; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + bool valid; + PtrToArg<Object *>::convert(right)->get(PtrToArg<StringName>::convert(left), &valid); + PtrToArg<bool>::encode(valid, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +typedef void (*VariantEvaluatorFunction)(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid); + +static Variant::Type operator_return_type_table[Variant::OP_MAX][Variant::VARIANT_MAX][Variant::VARIANT_MAX]; +static VariantEvaluatorFunction operator_evaluator_table[Variant::OP_MAX][Variant::VARIANT_MAX][Variant::VARIANT_MAX]; +static Variant::ValidatedOperatorEvaluator validated_operator_evaluator_table[Variant::OP_MAX][Variant::VARIANT_MAX][Variant::VARIANT_MAX]; +static Variant::PTROperatorEvaluator ptr_operator_evaluator_table[Variant::OP_MAX][Variant::VARIANT_MAX][Variant::VARIANT_MAX]; + +template <class T> +void register_op(Variant::Operator p_op, Variant::Type p_type_a, Variant::Type p_type_b) { + operator_return_type_table[p_op][p_type_a][p_type_b] = T::get_return_type(); + operator_evaluator_table[p_op][p_type_a][p_type_b] = T::evaluate; + validated_operator_evaluator_table[p_op][p_type_a][p_type_b] = T::validated_evaluate; + ptr_operator_evaluator_table[p_op][p_type_a][p_type_b] = T::ptr_evaluate; +} + +void Variant::_register_variant_operators() { + zeromem(operator_return_type_table, sizeof(operator_return_type_table)); + zeromem(operator_evaluator_table, sizeof(operator_evaluator_table)); + zeromem(validated_operator_evaluator_table, sizeof(validated_operator_evaluator_table)); + zeromem(ptr_operator_evaluator_table, sizeof(ptr_operator_evaluator_table)); + + register_op<OperatorEvaluatorAdd<int64_t, int64_t, int64_t>>(Variant::OP_ADD, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorAdd<double, int64_t, double>>(Variant::OP_ADD, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorAdd<double, double, int64_t>>(Variant::OP_ADD, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorAdd<double, double, double>>(Variant::OP_ADD, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorAdd<String, String, String>>(Variant::OP_ADD, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorAdd<Vector2, Vector2, Vector2>>(Variant::OP_ADD, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorAdd<Vector2i, Vector2i, Vector2i>>(Variant::OP_ADD, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorAdd<Vector3, Vector3, Vector3>>(Variant::OP_ADD, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorAdd<Vector3i, Vector3i, Vector3i>>(Variant::OP_ADD, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorAdd<Quat, Quat, Quat>>(Variant::OP_ADD, Variant::QUAT, Variant::QUAT); + register_op<OperatorEvaluatorAdd<Color, Color, Color>>(Variant::OP_ADD, Variant::COLOR, Variant::COLOR); + register_op<OperatorEvaluatorAddArray>(Variant::OP_ADD, Variant::ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorAppendArray<uint8_t>>(Variant::OP_ADD, Variant::PACKED_BYTE_ARRAY, Variant::PACKED_BYTE_ARRAY); + register_op<OperatorEvaluatorAppendArray<int32_t>>(Variant::OP_ADD, Variant::PACKED_INT32_ARRAY, Variant::PACKED_INT32_ARRAY); + register_op<OperatorEvaluatorAppendArray<int64_t>>(Variant::OP_ADD, Variant::PACKED_INT64_ARRAY, Variant::PACKED_INT64_ARRAY); + register_op<OperatorEvaluatorAppendArray<float>>(Variant::OP_ADD, Variant::PACKED_FLOAT32_ARRAY, Variant::PACKED_FLOAT32_ARRAY); + register_op<OperatorEvaluatorAppendArray<double>>(Variant::OP_ADD, Variant::PACKED_FLOAT64_ARRAY, Variant::PACKED_FLOAT64_ARRAY); + register_op<OperatorEvaluatorAppendArray<String>>(Variant::OP_ADD, Variant::PACKED_STRING_ARRAY, Variant::PACKED_STRING_ARRAY); + register_op<OperatorEvaluatorAppendArray<Vector2>>(Variant::OP_ADD, Variant::PACKED_VECTOR2_ARRAY, Variant::PACKED_VECTOR2_ARRAY); + register_op<OperatorEvaluatorAppendArray<Vector3>>(Variant::OP_ADD, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY); + register_op<OperatorEvaluatorAppendArray<Color>>(Variant::OP_ADD, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY); + + register_op<OperatorEvaluatorSub<int64_t, int64_t, int64_t>>(Variant::OP_SUBTRACT, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorSub<double, int64_t, double>>(Variant::OP_SUBTRACT, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorSub<double, double, int64_t>>(Variant::OP_SUBTRACT, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorSub<double, double, double>>(Variant::OP_SUBTRACT, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorSub<Vector2, Vector2, Vector2>>(Variant::OP_SUBTRACT, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorSub<Vector2i, Vector2i, Vector2i>>(Variant::OP_SUBTRACT, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorSub<Vector3, Vector3, Vector3>>(Variant::OP_SUBTRACT, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorSub<Vector3i, Vector3i, Vector3i>>(Variant::OP_SUBTRACT, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorSub<Quat, Quat, Quat>>(Variant::OP_SUBTRACT, Variant::QUAT, Variant::QUAT); + register_op<OperatorEvaluatorSub<Color, Color, Color>>(Variant::OP_SUBTRACT, Variant::COLOR, Variant::COLOR); + + register_op<OperatorEvaluatorMul<int64_t, int64_t, int64_t>>(Variant::OP_MULTIPLY, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorMul<double, int64_t, double>>(Variant::OP_MULTIPLY, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorMul<Vector2, int64_t, Vector2>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR2); + register_op<OperatorEvaluatorMul<Vector2i, int64_t, Vector2i>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR2I); + register_op<OperatorEvaluatorMul<Vector3, int64_t, Vector3>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR3); + register_op<OperatorEvaluatorMul<Vector3i, int64_t, Vector3i>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR3I); + + register_op<OperatorEvaluatorMul<double, double, double>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorMul<double, double, int64_t>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorMul<Vector2, double, Vector2>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2); + register_op<OperatorEvaluatorMul<Vector2i, double, Vector2i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2I); + register_op<OperatorEvaluatorMul<Vector3, double, Vector3>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3); + register_op<OperatorEvaluatorMul<Vector3i, double, Vector3i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3I); + + register_op<OperatorEvaluatorMul<Vector2, Vector2, Vector2>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorMul<Vector2, Vector2, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::INT); + register_op<OperatorEvaluatorMul<Vector2, Vector2, double>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::FLOAT); + + register_op<OperatorEvaluatorMul<Vector2i, Vector2i, Vector2i>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorMul<Vector2i, Vector2i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::INT); + register_op<OperatorEvaluatorMul<Vector2i, Vector2i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::FLOAT); + + register_op<OperatorEvaluatorMul<Vector3, Vector3, Vector3>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorMul<Vector3, Vector3, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::INT); + register_op<OperatorEvaluatorMul<Vector3, Vector3, double>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::FLOAT); + + register_op<OperatorEvaluatorMul<Vector3i, Vector3i, Vector3i>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorMul<Vector3i, Vector3i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::INT); + register_op<OperatorEvaluatorMul<Vector3i, Vector3i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::FLOAT); + + register_op<OperatorEvaluatorMul<Quat, Quat, Quat>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::QUAT); + register_op<OperatorEvaluatorMul<Quat, Quat, int64_t>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::INT); + register_op<OperatorEvaluatorMul<Quat, Quat, double>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::FLOAT); + + register_op<OperatorEvaluatorMul<Color, Color, Color>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::COLOR); + register_op<OperatorEvaluatorMul<Color, Color, int64_t>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::INT); + register_op<OperatorEvaluatorMul<Color, Color, double>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::FLOAT); + + register_op<OperatorEvaluatorMul<Transform2D, Transform2D, Transform2D>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::TRANSFORM2D); + register_op<OperatorEvaluatorXForm<Vector2, Transform2D, Vector2>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::VECTOR2); + register_op<OperatorEvaluatorXFormInv<Vector2, Vector2, Transform2D>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::TRANSFORM2D); + register_op<OperatorEvaluatorXForm<Rect2, Transform2D, Rect2>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::RECT2); + register_op<OperatorEvaluatorXFormInv<Rect2, Rect2, Transform2D>>(Variant::OP_MULTIPLY, Variant::RECT2, Variant::TRANSFORM2D); + register_op<OperatorEvaluatorXForm<Vector<Vector2>, Transform2D, Vector<Vector2>>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::PACKED_VECTOR2_ARRAY); + register_op<OperatorEvaluatorXFormInv<Vector<Vector2>, Vector<Vector2>, Transform2D>>(Variant::OP_MULTIPLY, Variant::PACKED_VECTOR2_ARRAY, Variant::TRANSFORM2D); + + register_op<OperatorEvaluatorMul<Transform, Transform, Transform>>(Variant::OP_MULTIPLY, Variant::TRANSFORM, Variant::TRANSFORM); + register_op<OperatorEvaluatorXForm<Vector3, Transform, Vector3>>(Variant::OP_MULTIPLY, Variant::TRANSFORM, Variant::VECTOR3); + register_op<OperatorEvaluatorXFormInv<Vector3, Vector3, Transform>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::TRANSFORM); + register_op<OperatorEvaluatorXForm<::AABB, Transform, ::AABB>>(Variant::OP_MULTIPLY, Variant::TRANSFORM, Variant::AABB); + register_op<OperatorEvaluatorXFormInv<::AABB, ::AABB, Transform>>(Variant::OP_MULTIPLY, Variant::AABB, Variant::TRANSFORM); + register_op<OperatorEvaluatorXForm<Vector<Vector3>, Transform, Vector<Vector3>>>(Variant::OP_MULTIPLY, Variant::TRANSFORM, Variant::PACKED_VECTOR3_ARRAY); + register_op<OperatorEvaluatorXFormInv<Vector<Vector3>, Vector<Vector3>, Transform>>(Variant::OP_MULTIPLY, Variant::PACKED_VECTOR3_ARRAY, Variant::TRANSFORM); + + register_op<OperatorEvaluatorMul<Basis, Basis, Basis>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::BASIS); + register_op<OperatorEvaluatorXForm<Vector3, Basis, Vector3>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::VECTOR3); + register_op<OperatorEvaluatorXFormInv<Vector3, Vector3, Basis>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::BASIS); + + register_op<OperatorEvaluatorMul<Quat, Quat, Quat>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::QUAT); + register_op<OperatorEvaluatorMul<Quat, Quat, int64_t>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::INT); + register_op<OperatorEvaluatorMul<Quat, int64_t, Quat>>(Variant::OP_MULTIPLY, Variant::INT, Variant::QUAT); + register_op<OperatorEvaluatorMul<Quat, Quat, double>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::FLOAT); + register_op<OperatorEvaluatorMul<Quat, double, Quat>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::QUAT); + register_op<OperatorEvaluatorXForm<Vector3, Quat, Vector3>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::VECTOR3); + register_op<OperatorEvaluatorXFormInv<Vector3, Vector3, Quat>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::QUAT); + + register_op<OperatorEvaluatorMul<Color, Color, Color>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::COLOR); + register_op<OperatorEvaluatorMul<Color, Color, int64_t>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::INT); + register_op<OperatorEvaluatorMul<Color, int64_t, Color>>(Variant::OP_MULTIPLY, Variant::INT, Variant::COLOR); + register_op<OperatorEvaluatorMul<Color, Color, double>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::FLOAT); + register_op<OperatorEvaluatorMul<Color, double, Color>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::COLOR); + + register_op<OperatorEvaluatorDivNZ<int64_t, int64_t, int64_t>>(Variant::OP_DIVIDE, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorDiv<double, double, int64_t>>(Variant::OP_DIVIDE, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorDiv<double, int64_t, double>>(Variant::OP_DIVIDE, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorDiv<double, double, double>>(Variant::OP_DIVIDE, Variant::FLOAT, Variant::FLOAT); + + register_op<OperatorEvaluatorDiv<Vector2, Vector2, Vector2>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorDiv<Vector2, Vector2, double>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::FLOAT); + register_op<OperatorEvaluatorDiv<Vector2, Vector2, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::INT); + + register_op<OperatorEvaluatorDiv<Vector2i, Vector2i, Vector2i>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, double>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::FLOAT); + register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::INT); + + register_op<OperatorEvaluatorDiv<Vector2, Vector2, Vector2>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorDiv<Vector2, Vector2, double>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::FLOAT); + register_op<OperatorEvaluatorDiv<Vector2, Vector2, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::INT); + + register_op<OperatorEvaluatorDiv<Vector3, Vector3, Vector3>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorDiv<Vector3, Vector3, double>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::FLOAT); + register_op<OperatorEvaluatorDiv<Vector3, Vector3, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::INT); + + register_op<OperatorEvaluatorDiv<Vector3i, Vector3i, Vector3i>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, double>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::FLOAT); + register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::INT); + + register_op<OperatorEvaluatorDiv<Quat, Quat, double>>(Variant::OP_DIVIDE, Variant::QUAT, Variant::FLOAT); + register_op<OperatorEvaluatorDiv<Quat, Quat, int64_t>>(Variant::OP_DIVIDE, Variant::QUAT, Variant::INT); + + register_op<OperatorEvaluatorDiv<Color, Color, Color>>(Variant::OP_DIVIDE, Variant::COLOR, Variant::COLOR); + register_op<OperatorEvaluatorDiv<Color, Color, double>>(Variant::OP_DIVIDE, Variant::COLOR, Variant::FLOAT); + register_op<OperatorEvaluatorDiv<Color, Color, int64_t>>(Variant::OP_DIVIDE, Variant::COLOR, Variant::INT); + + register_op<OperatorEvaluatorModNZ<int64_t, int64_t, int64_t>>(Variant::OP_MODULE, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorMod<Vector2i, Vector2i, Vector2i>>(Variant::OP_MODULE, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorModNZ<Vector2i, Vector2i, int64_t>>(Variant::OP_MODULE, Variant::VECTOR2I, Variant::INT); + + register_op<OperatorEvaluatorMod<Vector3i, Vector3i, Vector3i>>(Variant::OP_MODULE, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorModNZ<Vector3i, Vector3i, int64_t>>(Variant::OP_MODULE, Variant::VECTOR3I, Variant::INT); + + register_op<OperatorEvaluatorStringModNil>(Variant::OP_MODULE, Variant::STRING, Variant::NIL); + + register_op<OperatorEvaluatorStringModT<bool>>(Variant::OP_MODULE, Variant::STRING, Variant::BOOL); + register_op<OperatorEvaluatorStringModT<int64_t>>(Variant::OP_MODULE, Variant::STRING, Variant::INT); + register_op<OperatorEvaluatorStringModT<double>>(Variant::OP_MODULE, Variant::STRING, Variant::FLOAT); + register_op<OperatorEvaluatorStringModT<String>>(Variant::OP_MODULE, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorStringModT<Vector2>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR2); + register_op<OperatorEvaluatorStringModT<Vector2i>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR2I); + register_op<OperatorEvaluatorStringModT<Rect2>>(Variant::OP_MODULE, Variant::STRING, Variant::RECT2); + register_op<OperatorEvaluatorStringModT<Rect2i>>(Variant::OP_MODULE, Variant::STRING, Variant::RECT2I); + register_op<OperatorEvaluatorStringModT<Vector3>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR3); + register_op<OperatorEvaluatorStringModT<Vector3i>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR3I); + register_op<OperatorEvaluatorStringModT<Transform2D>>(Variant::OP_MODULE, Variant::STRING, Variant::TRANSFORM2D); + register_op<OperatorEvaluatorStringModT<Plane>>(Variant::OP_MODULE, Variant::STRING, Variant::PLANE); + register_op<OperatorEvaluatorStringModT<Quat>>(Variant::OP_MODULE, Variant::STRING, Variant::QUAT); + register_op<OperatorEvaluatorStringModT<::AABB>>(Variant::OP_MODULE, Variant::STRING, Variant::AABB); + register_op<OperatorEvaluatorStringModT<Basis>>(Variant::OP_MODULE, Variant::STRING, Variant::BASIS); + register_op<OperatorEvaluatorStringModT<Transform>>(Variant::OP_MODULE, Variant::STRING, Variant::TRANSFORM); + + register_op<OperatorEvaluatorStringModT<Color>>(Variant::OP_MODULE, Variant::STRING, Variant::COLOR); + register_op<OperatorEvaluatorStringModT<StringName>>(Variant::OP_MODULE, Variant::STRING, Variant::STRING_NAME); + register_op<OperatorEvaluatorStringModT<NodePath>>(Variant::OP_MODULE, Variant::STRING, Variant::NODE_PATH); + register_op<OperatorEvaluatorStringModObject>(Variant::OP_MODULE, Variant::STRING, Variant::OBJECT); + register_op<OperatorEvaluatorStringModT<Callable>>(Variant::OP_MODULE, Variant::STRING, Variant::CALLABLE); + register_op<OperatorEvaluatorStringModT<Signal>>(Variant::OP_MODULE, Variant::STRING, Variant::SIGNAL); + register_op<OperatorEvaluatorStringModT<Dictionary>>(Variant::OP_MODULE, Variant::STRING, Variant::DICTIONARY); + register_op<OperatorEvaluatorStringModArray>(Variant::OP_MODULE, Variant::STRING, Variant::ARRAY); + + register_op<OperatorEvaluatorStringModT<PackedByteArray>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_BYTE_ARRAY); + register_op<OperatorEvaluatorStringModT<PackedInt32Array>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_INT32_ARRAY); + register_op<OperatorEvaluatorStringModT<PackedInt64Array>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_INT64_ARRAY); + register_op<OperatorEvaluatorStringModT<PackedFloat32Array>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_FLOAT32_ARRAY); + register_op<OperatorEvaluatorStringModT<PackedFloat64Array>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_FLOAT64_ARRAY); + register_op<OperatorEvaluatorStringModT<PackedStringArray>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_STRING_ARRAY); + register_op<OperatorEvaluatorStringModT<PackedVector2Array>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_VECTOR2_ARRAY); + register_op<OperatorEvaluatorStringModT<PackedVector3Array>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_VECTOR3_ARRAY); + register_op<OperatorEvaluatorStringModT<PackedColorArray>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_COLOR_ARRAY); + + register_op<OperatorEvaluatorNeg<int64_t, int64_t>>(Variant::OP_NEGATE, Variant::INT, Variant::NIL); + register_op<OperatorEvaluatorNeg<double, double>>(Variant::OP_NEGATE, Variant::FLOAT, Variant::NIL); + register_op<OperatorEvaluatorNeg<Vector2, Vector2>>(Variant::OP_NEGATE, Variant::VECTOR2, Variant::NIL); + register_op<OperatorEvaluatorNeg<Vector2i, Vector2i>>(Variant::OP_NEGATE, Variant::VECTOR2I, Variant::NIL); + register_op<OperatorEvaluatorNeg<Vector3, Vector3>>(Variant::OP_NEGATE, Variant::VECTOR3, Variant::NIL); + register_op<OperatorEvaluatorNeg<Vector3i, Vector3i>>(Variant::OP_NEGATE, Variant::VECTOR3I, Variant::NIL); + register_op<OperatorEvaluatorNeg<Quat, Quat>>(Variant::OP_NEGATE, Variant::QUAT, Variant::NIL); + register_op<OperatorEvaluatorNeg<Plane, Plane>>(Variant::OP_NEGATE, Variant::PLANE, Variant::NIL); + register_op<OperatorEvaluatorNeg<Color, Color>>(Variant::OP_NEGATE, Variant::COLOR, Variant::NIL); + + register_op<OperatorEvaluatorPos<int64_t, int64_t>>(Variant::OP_POSITIVE, Variant::INT, Variant::NIL); + register_op<OperatorEvaluatorPos<double, double>>(Variant::OP_POSITIVE, Variant::FLOAT, Variant::NIL); + register_op<OperatorEvaluatorPos<Vector2, Vector2>>(Variant::OP_POSITIVE, Variant::VECTOR2, Variant::NIL); + register_op<OperatorEvaluatorPos<Vector2i, Vector2i>>(Variant::OP_POSITIVE, Variant::VECTOR2I, Variant::NIL); + register_op<OperatorEvaluatorPos<Vector3, Vector3>>(Variant::OP_POSITIVE, Variant::VECTOR3, Variant::NIL); + register_op<OperatorEvaluatorPos<Vector3i, Vector3i>>(Variant::OP_POSITIVE, Variant::VECTOR3I, Variant::NIL); + register_op<OperatorEvaluatorPos<Quat, Quat>>(Variant::OP_POSITIVE, Variant::QUAT, Variant::NIL); + register_op<OperatorEvaluatorPos<Plane, Plane>>(Variant::OP_POSITIVE, Variant::PLANE, Variant::NIL); + register_op<OperatorEvaluatorPos<Color, Color>>(Variant::OP_POSITIVE, Variant::COLOR, Variant::NIL); + + register_op<OperatorEvaluatorShiftLeft<int64_t, int64_t, int64_t>>(Variant::OP_SHIFT_LEFT, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorShiftRight<int64_t, int64_t, int64_t>>(Variant::OP_SHIFT_RIGHT, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorBitOr<int64_t, int64_t, int64_t>>(Variant::OP_BIT_OR, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorBitAnd<int64_t, int64_t, int64_t>>(Variant::OP_BIT_AND, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorBitXor<int64_t, int64_t, int64_t>>(Variant::OP_BIT_XOR, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorBitNeg<int64_t, int64_t>>(Variant::OP_BIT_NEGATE, Variant::INT, Variant::NIL); + + register_op<OperatorEvaluatorBitNeg<int64_t, int64_t>>(Variant::OP_BIT_NEGATE, Variant::INT, Variant::NIL); + + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_EQUAL, Variant::NIL, Variant::NIL>>(Variant::OP_EQUAL, Variant::NIL, Variant::NIL); + register_op<OperatorEvaluatorEqual<bool, bool>>(Variant::OP_EQUAL, Variant::BOOL, Variant::BOOL); + register_op<OperatorEvaluatorEqual<int64_t, int64_t>>(Variant::OP_EQUAL, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorEqual<int64_t, double>>(Variant::OP_EQUAL, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorEqual<double, int64_t>>(Variant::OP_EQUAL, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorEqual<double, double>>(Variant::OP_EQUAL, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorEqual<String, String>>(Variant::OP_EQUAL, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorEqual<Vector2, Vector2>>(Variant::OP_EQUAL, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorEqual<Vector2i, Vector2i>>(Variant::OP_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorEqual<Rect2, Rect2>>(Variant::OP_EQUAL, Variant::RECT2, Variant::RECT2); + register_op<OperatorEvaluatorEqual<Rect2i, Rect2i>>(Variant::OP_EQUAL, Variant::RECT2I, Variant::RECT2I); + register_op<OperatorEvaluatorEqual<Vector3, Vector3>>(Variant::OP_EQUAL, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorEqual<Vector3i, Vector3i>>(Variant::OP_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorEqual<Transform2D, Transform2D>>(Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::TRANSFORM2D); + register_op<OperatorEvaluatorEqual<Plane, Plane>>(Variant::OP_EQUAL, Variant::PLANE, Variant::PLANE); + register_op<OperatorEvaluatorEqual<Quat, Quat>>(Variant::OP_EQUAL, Variant::QUAT, Variant::QUAT); + register_op<OperatorEvaluatorEqual<::AABB, ::AABB>>(Variant::OP_EQUAL, Variant::AABB, Variant::AABB); + register_op<OperatorEvaluatorEqual<Basis, Basis>>(Variant::OP_EQUAL, Variant::BASIS, Variant::BASIS); + register_op<OperatorEvaluatorEqual<Transform, Transform>>(Variant::OP_EQUAL, Variant::TRANSFORM, Variant::TRANSFORM); + register_op<OperatorEvaluatorEqual<Color, Color>>(Variant::OP_EQUAL, Variant::COLOR, Variant::COLOR); + + register_op<OperatorEvaluatorEqual<StringName, String>>(Variant::OP_EQUAL, Variant::STRING_NAME, Variant::STRING); + register_op<OperatorEvaluatorEqual<String, StringName>>(Variant::OP_EQUAL, Variant::STRING, Variant::STRING_NAME); + register_op<OperatorEvaluatorEqual<StringName, StringName>>(Variant::OP_EQUAL, Variant::STRING_NAME, Variant::STRING_NAME); + + register_op<OperatorEvaluatorEqual<NodePath, NodePath>>(Variant::OP_EQUAL, Variant::NODE_PATH, Variant::NODE_PATH); + register_op<OperatorEvaluatorEqual<::RID, ::RID>>(Variant::OP_EQUAL, Variant::RID, Variant::RID); + + register_op<OperatorEvaluatorEqualObject>(Variant::OP_EQUAL, Variant::OBJECT, Variant::OBJECT); + register_op<OperatorEvaluatorEqualObjectNil>(Variant::OP_EQUAL, Variant::OBJECT, Variant::NIL); + register_op<OperatorEvaluatorEqualNilObject>(Variant::OP_EQUAL, Variant::NIL, Variant::OBJECT); + + register_op<OperatorEvaluatorEqual<Callable, Callable>>(Variant::OP_EQUAL, Variant::CALLABLE, Variant::CALLABLE); + register_op<OperatorEvaluatorEqual<Signal, Signal>>(Variant::OP_EQUAL, Variant::SIGNAL, Variant::SIGNAL); + register_op<OperatorEvaluatorEqual<Dictionary, Dictionary>>(Variant::OP_EQUAL, Variant::DICTIONARY, Variant::DICTIONARY); + register_op<OperatorEvaluatorEqual<Array, Array>>(Variant::OP_EQUAL, Variant::ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorEqual<PackedByteArray, PackedByteArray>>(Variant::OP_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::PACKED_BYTE_ARRAY); + register_op<OperatorEvaluatorEqual<PackedInt32Array, PackedInt32Array>>(Variant::OP_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::PACKED_INT32_ARRAY); + register_op<OperatorEvaluatorEqual<PackedInt64Array, PackedInt64Array>>(Variant::OP_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::PACKED_INT64_ARRAY); + register_op<OperatorEvaluatorEqual<PackedFloat32Array, PackedFloat32Array>>(Variant::OP_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::PACKED_FLOAT32_ARRAY); + register_op<OperatorEvaluatorEqual<PackedFloat64Array, PackedFloat64Array>>(Variant::OP_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::PACKED_FLOAT64_ARRAY); + register_op<OperatorEvaluatorEqual<PackedStringArray, PackedStringArray>>(Variant::OP_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::PACKED_STRING_ARRAY); + register_op<OperatorEvaluatorEqual<PackedVector2Array, PackedVector2Array>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::PACKED_VECTOR2_ARRAY); + register_op<OperatorEvaluatorEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY); + register_op<OperatorEvaluatorEqual<PackedColorArray, PackedColorArray>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY); + + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL); + register_op<OperatorEvaluatorNotEqual<bool, bool>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::BOOL); + register_op<OperatorEvaluatorNotEqual<int64_t, int64_t>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorNotEqual<int64_t, double>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorNotEqual<double, int64_t>>(Variant::OP_NOT_EQUAL, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorNotEqual<double, double>>(Variant::OP_NOT_EQUAL, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorNotEqual<String, String>>(Variant::OP_NOT_EQUAL, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorNotEqual<Vector2, Vector2>>(Variant::OP_NOT_EQUAL, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorNotEqual<Vector2i, Vector2i>>(Variant::OP_NOT_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorNotEqual<Rect2, Rect2>>(Variant::OP_NOT_EQUAL, Variant::RECT2, Variant::RECT2); + register_op<OperatorEvaluatorNotEqual<Rect2i, Rect2i>>(Variant::OP_NOT_EQUAL, Variant::RECT2I, Variant::RECT2I); + register_op<OperatorEvaluatorNotEqual<Vector3, Vector3>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorNotEqual<Vector3i, Vector3i>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorNotEqual<Transform2D, Transform2D>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::TRANSFORM2D); + register_op<OperatorEvaluatorNotEqual<Plane, Plane>>(Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::PLANE); + register_op<OperatorEvaluatorNotEqual<Quat, Quat>>(Variant::OP_NOT_EQUAL, Variant::QUAT, Variant::QUAT); + register_op<OperatorEvaluatorNotEqual<::AABB, ::AABB>>(Variant::OP_NOT_EQUAL, Variant::AABB, Variant::AABB); + register_op<OperatorEvaluatorNotEqual<Basis, Basis>>(Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::BASIS); + register_op<OperatorEvaluatorNotEqual<Transform, Transform>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM, Variant::TRANSFORM); + register_op<OperatorEvaluatorNotEqual<Color, Color>>(Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::COLOR); + + register_op<OperatorEvaluatorNotEqual<StringName, String>>(Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::STRING); + register_op<OperatorEvaluatorNotEqual<String, StringName>>(Variant::OP_NOT_EQUAL, Variant::STRING, Variant::STRING_NAME); + register_op<OperatorEvaluatorNotEqual<StringName, StringName>>(Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::STRING_NAME); + + register_op<OperatorEvaluatorNotEqual<NodePath, NodePath>>(Variant::OP_NOT_EQUAL, Variant::NODE_PATH, Variant::NODE_PATH); + register_op<OperatorEvaluatorNotEqual<::RID, ::RID>>(Variant::OP_NOT_EQUAL, Variant::RID, Variant::RID); + + register_op<OperatorEvaluatorNotEqualObject>(Variant::OP_NOT_EQUAL, Variant::OBJECT, Variant::OBJECT); + register_op<OperatorEvaluatorNotEqualObjectNil>(Variant::OP_NOT_EQUAL, Variant::OBJECT, Variant::NIL); + register_op<OperatorEvaluatorNotEqualNilObject>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::OBJECT); + + register_op<OperatorEvaluatorNotEqual<Callable, Callable>>(Variant::OP_NOT_EQUAL, Variant::CALLABLE, Variant::CALLABLE); + register_op<OperatorEvaluatorNotEqual<Signal, Signal>>(Variant::OP_NOT_EQUAL, Variant::SIGNAL, Variant::SIGNAL); + register_op<OperatorEvaluatorNotEqual<Dictionary, Dictionary>>(Variant::OP_NOT_EQUAL, Variant::DICTIONARY, Variant::DICTIONARY); + register_op<OperatorEvaluatorNotEqual<Array, Array>>(Variant::OP_NOT_EQUAL, Variant::ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedByteArray, PackedByteArray>>(Variant::OP_NOT_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::PACKED_BYTE_ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedInt32Array, PackedInt32Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::PACKED_INT32_ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedInt64Array, PackedInt64Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::PACKED_INT64_ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedFloat32Array, PackedFloat32Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::PACKED_FLOAT32_ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedFloat64Array, PackedFloat64Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::PACKED_FLOAT64_ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedStringArray, PackedStringArray>>(Variant::OP_NOT_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::PACKED_STRING_ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedVector2Array, PackedVector2Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::PACKED_VECTOR2_ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY); + register_op<OperatorEvaluatorNotEqual<PackedColorArray, PackedColorArray>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY); + + register_op<OperatorEvaluatorLess<bool, bool>>(Variant::OP_LESS, Variant::BOOL, Variant::BOOL); + register_op<OperatorEvaluatorLess<int64_t, int64_t>>(Variant::OP_LESS, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorLess<int64_t, double>>(Variant::OP_LESS, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorLess<double, int64_t>>(Variant::OP_LESS, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorLess<double, double>>(Variant::OP_LESS, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorLess<String, String>>(Variant::OP_LESS, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorLess<Vector2, Vector2>>(Variant::OP_LESS, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorLess<Vector2i, Vector2i>>(Variant::OP_LESS, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorLess<Vector3, Vector3>>(Variant::OP_LESS, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorLess<Vector3i, Vector3i>>(Variant::OP_LESS, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorLess<::RID, ::RID>>(Variant::OP_LESS, Variant::RID, Variant::RID); + register_op<OperatorEvaluatorLess<Array, Array>>(Variant::OP_LESS, Variant::ARRAY, Variant::ARRAY); + + register_op<OperatorEvaluatorLessEqual<int64_t, int64_t>>(Variant::OP_LESS_EQUAL, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorLessEqual<int64_t, double>>(Variant::OP_LESS_EQUAL, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorLessEqual<double, int64_t>>(Variant::OP_LESS_EQUAL, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorLessEqual<double, double>>(Variant::OP_LESS_EQUAL, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorLessEqual<String, String>>(Variant::OP_LESS_EQUAL, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorLessEqual<Vector2, Vector2>>(Variant::OP_LESS_EQUAL, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorLessEqual<Vector2i, Vector2i>>(Variant::OP_LESS_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorLessEqual<Vector3, Vector3>>(Variant::OP_LESS_EQUAL, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorLessEqual<Vector3i, Vector3i>>(Variant::OP_LESS_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorLessEqual<::RID, ::RID>>(Variant::OP_LESS_EQUAL, Variant::RID, Variant::RID); + register_op<OperatorEvaluatorLessEqual<Array, Array>>(Variant::OP_LESS_EQUAL, Variant::ARRAY, Variant::ARRAY); + + register_op<OperatorEvaluatorGreater<bool, bool>>(Variant::OP_GREATER, Variant::BOOL, Variant::BOOL); + register_op<OperatorEvaluatorGreater<int64_t, int64_t>>(Variant::OP_GREATER, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorGreater<int64_t, double>>(Variant::OP_GREATER, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorGreater<double, int64_t>>(Variant::OP_GREATER, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorGreater<double, double>>(Variant::OP_GREATER, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorGreater<String, String>>(Variant::OP_GREATER, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorGreater<Vector2, Vector2>>(Variant::OP_GREATER, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorGreater<Vector2i, Vector2i>>(Variant::OP_GREATER, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorGreater<Vector3, Vector3>>(Variant::OP_GREATER, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorGreater<Vector3i, Vector3i>>(Variant::OP_GREATER, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorGreater<::RID, ::RID>>(Variant::OP_GREATER, Variant::RID, Variant::RID); + register_op<OperatorEvaluatorGreater<Array, Array>>(Variant::OP_GREATER, Variant::ARRAY, Variant::ARRAY); + + register_op<OperatorEvaluatorGreaterEqual<int64_t, int64_t>>(Variant::OP_GREATER_EQUAL, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorGreaterEqual<int64_t, double>>(Variant::OP_GREATER_EQUAL, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorGreaterEqual<double, int64_t>>(Variant::OP_GREATER_EQUAL, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorGreaterEqual<double, double>>(Variant::OP_GREATER_EQUAL, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorGreaterEqual<String, String>>(Variant::OP_GREATER_EQUAL, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorGreaterEqual<Vector2, Vector2>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR2, Variant::VECTOR2); + register_op<OperatorEvaluatorGreaterEqual<Vector2i, Vector2i>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I); + register_op<OperatorEvaluatorGreaterEqual<Vector3, Vector3>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR3, Variant::VECTOR3); + register_op<OperatorEvaluatorGreaterEqual<Vector3i, Vector3i>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I); + register_op<OperatorEvaluatorGreaterEqual<::RID, ::RID>>(Variant::OP_GREATER_EQUAL, Variant::RID, Variant::RID); + register_op<OperatorEvaluatorGreaterEqual<Array, Array>>(Variant::OP_GREATER_EQUAL, Variant::ARRAY, Variant::ARRAY); + + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_OR, Variant::NIL, Variant::NIL>>(Variant::OP_OR, Variant::NIL, Variant::NIL); + + // OR + register_op<OperatorEvaluatorNilXBoolOr>(Variant::OP_OR, Variant::NIL, Variant::BOOL); + register_op<OperatorEvaluatorBoolXNilOr>(Variant::OP_OR, Variant::BOOL, Variant::NIL); + register_op<OperatorEvaluatorNilXIntOr>(Variant::OP_OR, Variant::NIL, Variant::INT); + register_op<OperatorEvaluatorIntXNilOr>(Variant::OP_OR, Variant::INT, Variant::NIL); + register_op<OperatorEvaluatorNilXFloatOr>(Variant::OP_OR, Variant::NIL, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXNilOr>(Variant::OP_OR, Variant::FLOAT, Variant::NIL); + register_op<OperatorEvaluatorNilXObjectOr>(Variant::OP_OR, Variant::NIL, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXNilOr>(Variant::OP_OR, Variant::OBJECT, Variant::NIL); + + register_op<OperatorEvaluatorBoolXBoolOr>(Variant::OP_OR, Variant::BOOL, Variant::BOOL); + register_op<OperatorEvaluatorBoolXIntOr>(Variant::OP_OR, Variant::BOOL, Variant::INT); + register_op<OperatorEvaluatorIntXBoolOr>(Variant::OP_OR, Variant::INT, Variant::BOOL); + register_op<OperatorEvaluatorBoolXFloatOr>(Variant::OP_OR, Variant::BOOL, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXBoolOr>(Variant::OP_OR, Variant::FLOAT, Variant::BOOL); + register_op<OperatorEvaluatorBoolXObjectOr>(Variant::OP_OR, Variant::BOOL, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXBoolOr>(Variant::OP_OR, Variant::OBJECT, Variant::BOOL); + + register_op<OperatorEvaluatorIntXIntOr>(Variant::OP_OR, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorIntXFloatOr>(Variant::OP_OR, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXIntOr>(Variant::OP_OR, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorIntXObjectOr>(Variant::OP_OR, Variant::INT, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXIntOr>(Variant::OP_OR, Variant::OBJECT, Variant::INT); + + register_op<OperatorEvaluatorFloatXFloatOr>(Variant::OP_OR, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXObjectOr>(Variant::OP_OR, Variant::FLOAT, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXFloatOr>(Variant::OP_OR, Variant::OBJECT, Variant::FLOAT); + register_op<OperatorEvaluatorObjectXObjectOr>(Variant::OP_OR, Variant::OBJECT, Variant::OBJECT); + + // AND + register_op<OperatorEvaluatorNilXBoolAnd>(Variant::OP_AND, Variant::NIL, Variant::BOOL); + register_op<OperatorEvaluatorBoolXNilAnd>(Variant::OP_AND, Variant::BOOL, Variant::NIL); + register_op<OperatorEvaluatorNilXIntAnd>(Variant::OP_AND, Variant::NIL, Variant::INT); + register_op<OperatorEvaluatorIntXNilAnd>(Variant::OP_AND, Variant::INT, Variant::NIL); + register_op<OperatorEvaluatorNilXFloatAnd>(Variant::OP_AND, Variant::NIL, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXNilAnd>(Variant::OP_AND, Variant::FLOAT, Variant::NIL); + register_op<OperatorEvaluatorNilXObjectAnd>(Variant::OP_AND, Variant::NIL, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXNilAnd>(Variant::OP_AND, Variant::OBJECT, Variant::NIL); + + register_op<OperatorEvaluatorBoolXBoolAnd>(Variant::OP_AND, Variant::BOOL, Variant::BOOL); + register_op<OperatorEvaluatorBoolXIntAnd>(Variant::OP_AND, Variant::BOOL, Variant::INT); + register_op<OperatorEvaluatorIntXBoolAnd>(Variant::OP_AND, Variant::INT, Variant::BOOL); + register_op<OperatorEvaluatorBoolXFloatAnd>(Variant::OP_AND, Variant::BOOL, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXBoolAnd>(Variant::OP_AND, Variant::FLOAT, Variant::BOOL); + register_op<OperatorEvaluatorBoolXObjectAnd>(Variant::OP_AND, Variant::BOOL, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXBoolAnd>(Variant::OP_AND, Variant::OBJECT, Variant::BOOL); + + register_op<OperatorEvaluatorIntXIntAnd>(Variant::OP_AND, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorIntXFloatAnd>(Variant::OP_AND, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXIntAnd>(Variant::OP_AND, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorIntXObjectAnd>(Variant::OP_AND, Variant::INT, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXIntAnd>(Variant::OP_AND, Variant::OBJECT, Variant::INT); + + register_op<OperatorEvaluatorFloatXFloatAnd>(Variant::OP_AND, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXObjectAnd>(Variant::OP_AND, Variant::FLOAT, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXFloatAnd>(Variant::OP_AND, Variant::OBJECT, Variant::FLOAT); + register_op<OperatorEvaluatorObjectXObjectAnd>(Variant::OP_AND, Variant::OBJECT, Variant::OBJECT); + + // XOR + register_op<OperatorEvaluatorNilXBoolXor>(Variant::OP_XOR, Variant::NIL, Variant::BOOL); + register_op<OperatorEvaluatorBoolXNilXor>(Variant::OP_XOR, Variant::BOOL, Variant::NIL); + register_op<OperatorEvaluatorNilXIntXor>(Variant::OP_XOR, Variant::NIL, Variant::INT); + register_op<OperatorEvaluatorIntXNilXor>(Variant::OP_XOR, Variant::INT, Variant::NIL); + register_op<OperatorEvaluatorNilXFloatXor>(Variant::OP_XOR, Variant::NIL, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXNilXor>(Variant::OP_XOR, Variant::FLOAT, Variant::NIL); + register_op<OperatorEvaluatorNilXObjectXor>(Variant::OP_XOR, Variant::NIL, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXNilXor>(Variant::OP_XOR, Variant::OBJECT, Variant::NIL); + + register_op<OperatorEvaluatorBoolXBoolXor>(Variant::OP_XOR, Variant::BOOL, Variant::BOOL); + register_op<OperatorEvaluatorBoolXIntXor>(Variant::OP_XOR, Variant::BOOL, Variant::INT); + register_op<OperatorEvaluatorIntXBoolXor>(Variant::OP_XOR, Variant::INT, Variant::BOOL); + register_op<OperatorEvaluatorBoolXFloatXor>(Variant::OP_XOR, Variant::BOOL, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXBoolXor>(Variant::OP_XOR, Variant::FLOAT, Variant::BOOL); + register_op<OperatorEvaluatorBoolXObjectXor>(Variant::OP_XOR, Variant::BOOL, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXBoolXor>(Variant::OP_XOR, Variant::OBJECT, Variant::BOOL); + + register_op<OperatorEvaluatorIntXIntXor>(Variant::OP_XOR, Variant::INT, Variant::INT); + register_op<OperatorEvaluatorIntXFloatXor>(Variant::OP_XOR, Variant::INT, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXIntXor>(Variant::OP_XOR, Variant::FLOAT, Variant::INT); + register_op<OperatorEvaluatorIntXObjectXor>(Variant::OP_XOR, Variant::INT, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXIntXor>(Variant::OP_XOR, Variant::OBJECT, Variant::INT); + + register_op<OperatorEvaluatorFloatXFloatXor>(Variant::OP_XOR, Variant::FLOAT, Variant::FLOAT); + register_op<OperatorEvaluatorFloatXObjectXor>(Variant::OP_XOR, Variant::FLOAT, Variant::OBJECT); + register_op<OperatorEvaluatorObjectXFloatXor>(Variant::OP_XOR, Variant::OBJECT, Variant::FLOAT); + register_op<OperatorEvaluatorObjectXObjectXor>(Variant::OP_XOR, Variant::OBJECT, Variant::OBJECT); + + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT, Variant::NIL, Variant::NIL>>(Variant::OP_NOT, Variant::NIL, Variant::NIL); + register_op<OperatorEvaluatorNotBool>(Variant::OP_NOT, Variant::BOOL, Variant::NIL); + register_op<OperatorEvaluatorNotInt>(Variant::OP_NOT, Variant::INT, Variant::NIL); + register_op<OperatorEvaluatorNotFloat>(Variant::OP_NOT, Variant::FLOAT, Variant::NIL); + register_op<OperatorEvaluatorNotObject>(Variant::OP_NOT, Variant::OBJECT, Variant::NIL); + + register_op<OperatorEvaluatorInStringFind>(Variant::OP_IN, Variant::STRING, Variant::STRING); + + register_op<OperatorEvaluatorInDictionaryHasNil>(Variant::OP_IN, Variant::NIL, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<bool>>(Variant::OP_IN, Variant::BOOL, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<int64_t>>(Variant::OP_IN, Variant::INT, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<double>>(Variant::OP_IN, Variant::FLOAT, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<String>>(Variant::OP_IN, Variant::STRING, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Vector2>>(Variant::OP_IN, Variant::VECTOR2, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Vector2i>>(Variant::OP_IN, Variant::VECTOR2I, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Rect2>>(Variant::OP_IN, Variant::RECT2, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Rect2i>>(Variant::OP_IN, Variant::RECT2I, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Vector3>>(Variant::OP_IN, Variant::VECTOR3, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Vector3i>>(Variant::OP_IN, Variant::VECTOR3I, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Transform2D>>(Variant::OP_IN, Variant::TRANSFORM2D, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Plane>>(Variant::OP_IN, Variant::PLANE, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Quat>>(Variant::OP_IN, Variant::QUAT, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<::AABB>>(Variant::OP_IN, Variant::AABB, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Basis>>(Variant::OP_IN, Variant::BASIS, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Transform>>(Variant::OP_IN, Variant::TRANSFORM, Variant::DICTIONARY); + + register_op<OperatorEvaluatorInDictionaryHas<Color>>(Variant::OP_IN, Variant::COLOR, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<NodePath>>(Variant::OP_IN, Variant::NODE_PATH, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHasObject>(Variant::OP_IN, Variant::OBJECT, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Callable>>(Variant::OP_IN, Variant::CALLABLE, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Signal>>(Variant::OP_IN, Variant::SIGNAL, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Dictionary>>(Variant::OP_IN, Variant::DICTIONARY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<Array>>(Variant::OP_IN, Variant::ARRAY, Variant::DICTIONARY); + + register_op<OperatorEvaluatorInDictionaryHas<PackedByteArray>>(Variant::OP_IN, Variant::PACKED_BYTE_ARRAY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<PackedInt32Array>>(Variant::OP_IN, Variant::PACKED_INT32_ARRAY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<PackedInt64Array>>(Variant::OP_IN, Variant::PACKED_INT64_ARRAY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<PackedFloat32Array>>(Variant::OP_IN, Variant::PACKED_FLOAT32_ARRAY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<PackedFloat64Array>>(Variant::OP_IN, Variant::PACKED_FLOAT64_ARRAY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<PackedStringArray>>(Variant::OP_IN, Variant::PACKED_STRING_ARRAY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<PackedVector2Array>>(Variant::OP_IN, Variant::PACKED_VECTOR2_ARRAY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<PackedVector3Array>>(Variant::OP_IN, Variant::PACKED_VECTOR3_ARRAY, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<PackedColorArray>>(Variant::OP_IN, Variant::PACKED_COLOR_ARRAY, Variant::DICTIONARY); + + register_op<OperatorEvaluatorInArrayFindNil>(Variant::OP_IN, Variant::NIL, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<bool, Array>>(Variant::OP_IN, Variant::BOOL, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<int64_t, Array>>(Variant::OP_IN, Variant::INT, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<double, Array>>(Variant::OP_IN, Variant::FLOAT, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<String, Array>>(Variant::OP_IN, Variant::STRING, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Vector2, Array>>(Variant::OP_IN, Variant::VECTOR2, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Vector2i, Array>>(Variant::OP_IN, Variant::VECTOR2I, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Rect2, Array>>(Variant::OP_IN, Variant::RECT2, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Rect2i, Array>>(Variant::OP_IN, Variant::RECT2I, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Vector3, Array>>(Variant::OP_IN, Variant::VECTOR3, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Vector3i, Array>>(Variant::OP_IN, Variant::VECTOR3I, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Transform2D, Array>>(Variant::OP_IN, Variant::TRANSFORM2D, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Plane, Array>>(Variant::OP_IN, Variant::PLANE, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Quat, Array>>(Variant::OP_IN, Variant::QUAT, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<::AABB, Array>>(Variant::OP_IN, Variant::AABB, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Basis, Array>>(Variant::OP_IN, Variant::BASIS, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Transform, Array>>(Variant::OP_IN, Variant::TRANSFORM, Variant::ARRAY); + + register_op<OperatorEvaluatorInArrayFind<Color, Array>>(Variant::OP_IN, Variant::COLOR, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<StringName, Array>>(Variant::OP_IN, Variant::STRING_NAME, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<NodePath, Array>>(Variant::OP_IN, Variant::NODE_PATH, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFindObject>(Variant::OP_IN, Variant::OBJECT, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Callable, Array>>(Variant::OP_IN, Variant::CALLABLE, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Signal, Array>>(Variant::OP_IN, Variant::SIGNAL, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Dictionary, Array>>(Variant::OP_IN, Variant::DICTIONARY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<Array, Array>>(Variant::OP_IN, Variant::ARRAY, Variant::ARRAY); + + register_op<OperatorEvaluatorInArrayFind<PackedByteArray, Array>>(Variant::OP_IN, Variant::PACKED_BYTE_ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<PackedInt32Array, Array>>(Variant::OP_IN, Variant::PACKED_INT32_ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<PackedInt64Array, Array>>(Variant::OP_IN, Variant::PACKED_INT64_ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<PackedFloat32Array, Array>>(Variant::OP_IN, Variant::PACKED_FLOAT32_ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<PackedFloat64Array, Array>>(Variant::OP_IN, Variant::PACKED_FLOAT64_ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<PackedStringArray, Array>>(Variant::OP_IN, Variant::PACKED_STRING_ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<PackedVector2Array, Array>>(Variant::OP_IN, Variant::PACKED_VECTOR2_ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<PackedVector3Array, Array>>(Variant::OP_IN, Variant::PACKED_VECTOR3_ARRAY, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<PackedColorArray, Array>>(Variant::OP_IN, Variant::PACKED_COLOR_ARRAY, Variant::ARRAY); + + register_op<OperatorEvaluatorInArrayFind<int, PackedByteArray>>(Variant::OP_IN, Variant::INT, Variant::PACKED_BYTE_ARRAY); + register_op<OperatorEvaluatorInArrayFind<float, PackedByteArray>>(Variant::OP_IN, Variant::FLOAT, Variant::PACKED_BYTE_ARRAY); + + register_op<OperatorEvaluatorInArrayFind<int, PackedInt32Array>>(Variant::OP_IN, Variant::INT, Variant::PACKED_INT32_ARRAY); + register_op<OperatorEvaluatorInArrayFind<float, PackedInt32Array>>(Variant::OP_IN, Variant::FLOAT, Variant::PACKED_INT32_ARRAY); + + register_op<OperatorEvaluatorInArrayFind<int, PackedInt64Array>>(Variant::OP_IN, Variant::INT, Variant::PACKED_INT64_ARRAY); + register_op<OperatorEvaluatorInArrayFind<float, PackedInt64Array>>(Variant::OP_IN, Variant::FLOAT, Variant::PACKED_INT64_ARRAY); + + register_op<OperatorEvaluatorInArrayFind<int, PackedFloat32Array>>(Variant::OP_IN, Variant::INT, Variant::PACKED_FLOAT32_ARRAY); + register_op<OperatorEvaluatorInArrayFind<float, PackedFloat32Array>>(Variant::OP_IN, Variant::FLOAT, Variant::PACKED_FLOAT32_ARRAY); + + register_op<OperatorEvaluatorInArrayFind<int, PackedFloat64Array>>(Variant::OP_IN, Variant::INT, Variant::PACKED_FLOAT64_ARRAY); + register_op<OperatorEvaluatorInArrayFind<float, PackedFloat64Array>>(Variant::OP_IN, Variant::FLOAT, Variant::PACKED_FLOAT64_ARRAY); + + register_op<OperatorEvaluatorInArrayFind<String, PackedStringArray>>(Variant::OP_IN, Variant::STRING, Variant::PACKED_STRING_ARRAY); + + register_op<OperatorEvaluatorInArrayFind<Vector2, PackedVector2Array>>(Variant::OP_IN, Variant::VECTOR2, Variant::PACKED_VECTOR2_ARRAY); + register_op<OperatorEvaluatorInArrayFind<Vector3, PackedVector3Array>>(Variant::OP_IN, Variant::VECTOR3, Variant::PACKED_VECTOR3_ARRAY); + + register_op<OperatorEvaluatorInArrayFind<Color, PackedColorArray>>(Variant::OP_IN, Variant::COLOR, Variant::PACKED_COLOR_ARRAY); + + register_op<OperatorEvaluatorObjectHasPropertyString>(Variant::OP_IN, Variant::STRING, Variant::OBJECT); + register_op<OperatorEvaluatorObjectHasPropertyStringName>(Variant::OP_IN, Variant::STRING_NAME, Variant::OBJECT); +} + +void Variant::_unregister_variant_operators() { +} + +void Variant::evaluate(const Operator &p_op, const Variant &p_a, + const Variant &p_b, Variant &r_ret, bool &r_valid) { + ERR_FAIL_INDEX(p_op, Variant::OP_MAX); + Variant::Type type_a = p_a.get_type(); + Variant::Type type_b = p_b.get_type(); + ERR_FAIL_INDEX(type_a, Variant::VARIANT_MAX); + ERR_FAIL_INDEX(type_b, Variant::VARIANT_MAX); + + VariantEvaluatorFunction ev = operator_evaluator_table[p_op][type_a][type_b]; + if (unlikely(!ev)) { + r_valid = false; + r_ret = Variant(); + return; + } + + ev(p_a, p_b, &r_ret, r_valid); +} + +Variant::Type Variant::get_operator_return_type(Operator p_operator, Type p_type_a, Type p_type_b) { + ERR_FAIL_INDEX_V(p_operator, Variant::OP_MAX, Variant::NIL); + ERR_FAIL_INDEX_V(p_type_a, Variant::VARIANT_MAX, Variant::NIL); + ERR_FAIL_INDEX_V(p_type_b, Variant::VARIANT_MAX, Variant::NIL); + + return operator_return_type_table[p_operator][p_type_a][p_type_b]; +} + +Variant::ValidatedOperatorEvaluator Variant::get_validated_operator_evaluator(Operator p_operator, Type p_type_a, Type p_type_b) { + ERR_FAIL_INDEX_V(p_operator, Variant::OP_MAX, nullptr); + ERR_FAIL_INDEX_V(p_type_a, Variant::VARIANT_MAX, nullptr); + ERR_FAIL_INDEX_V(p_type_b, Variant::VARIANT_MAX, nullptr); + return validated_operator_evaluator_table[p_operator][p_type_a][p_type_b]; +} + +Variant::PTROperatorEvaluator Variant::get_ptr_operator_evaluator(Operator p_operator, Type p_type_a, Type p_type_b) { + ERR_FAIL_INDEX_V(p_operator, Variant::OP_MAX, nullptr); + ERR_FAIL_INDEX_V(p_type_a, Variant::VARIANT_MAX, nullptr); + ERR_FAIL_INDEX_V(p_type_b, Variant::VARIANT_MAX, nullptr); + return ptr_operator_evaluator_table[p_operator][p_type_a][p_type_b]; +} + +static const char *_op_names[Variant::OP_MAX] = { + "==", + "!=", + "<", + "<=", + ">", + ">=", + "+", + "-", + "*", + "/", + "-", + "+", + "%", + "<<", + ">>", + "&", + "|", + "^", + "~", + "and", + "or", + "xor", + "not", + "in" +}; + +String Variant::get_operator_name(Operator p_op) { + ERR_FAIL_INDEX_V(p_op, OP_MAX, ""); + return _op_names[p_op]; +} + +Variant::operator bool() const { + return booleanize(); +} + +// We consider all uninitialized or empty types to be false based on the type's +// zeroiness. +bool Variant::booleanize() const { + return !is_zero(); +} + +bool Variant::in(const Variant &p_index, bool *r_valid) const { + bool valid; + Variant ret; + evaluate(OP_IN, p_index, *this, ret, valid); + if (r_valid) { + *r_valid = valid; + return false; + } + ERR_FAIL_COND_V(ret.type != BOOL, false); + return *VariantGetInternalPtr<bool>::get_ptr(&ret); +} diff --git a/core/variant_parser.cpp b/core/variant/variant_parser.cpp index 0a578faf78..ed936c626b 100644 --- a/core/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,24 +33,21 @@ #include "core/input/input_event.h" #include "core/io/resource_loader.h" #include "core/os/keyboard.h" -#include "core/string_buffer.h" - -CharType VariantParser::StreamFile::get_char() { +#include "core/string/string_buffer.h" +char32_t VariantParser::StreamFile::get_char() { return f->get_8(); } bool VariantParser::StreamFile::is_utf8() const { - return true; } -bool VariantParser::StreamFile::is_eof() const { +bool VariantParser::StreamFile::is_eof() const { return f->eof_reached(); } -CharType VariantParser::StreamString::get_char() { - +char32_t VariantParser::StreamString::get_char() { if (pos > s.length()) { return 0; } else if (pos == s.length()) { @@ -66,6 +63,7 @@ CharType VariantParser::StreamString::get_char() { bool VariantParser::StreamString::is_utf8() const { return false; } + bool VariantParser::StreamString::is_eof() const { return pos > s.length(); } @@ -93,12 +91,10 @@ const char *VariantParser::tk_name[TK_MAX] = { }; Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, String &r_err_str) { - bool string_name = false; while (true) { - - CharType cchar; + char32_t cchar; if (p_stream->saved) { cchar = p_stream->saved; p_stream->saved = 0; @@ -111,86 +107,73 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } switch (cchar) { - case '\n': { - line++; break; - }; + } case 0: { r_token.type = TK_EOF; return OK; } break; case '{': { - r_token.type = TK_CURLY_BRACKET_OPEN; return OK; - }; + } case '}': { - r_token.type = TK_CURLY_BRACKET_CLOSE; return OK; - }; + } case '[': { - r_token.type = TK_BRACKET_OPEN; return OK; - }; + } case ']': { - r_token.type = TK_BRACKET_CLOSE; return OK; - }; + } case '(': { - r_token.type = TK_PARENTHESIS_OPEN; return OK; - }; + } case ')': { - r_token.type = TK_PARENTHESIS_CLOSE; return OK; - }; + } case ':': { - r_token.type = TK_COLON; return OK; - }; + } case ';': { - while (true) { - CharType ch = p_stream->get_char(); + char32_t ch = p_stream->get_char(); if (p_stream->is_eof()) { r_token.type = TK_EOF; return OK; } - if (ch == '\n') + if (ch == '\n') { break; + } } break; - }; + } case ',': { - r_token.type = TK_COMMA; return OK; - }; + } case '.': { - r_token.type = TK_PERIOD; return OK; - }; + } case '=': { - r_token.type = TK_EQUAL; return OK; - }; + } case '#': { - StringBuffer<> color_str; color_str += '#'; while (true) { - CharType ch = p_stream->get_char(); + char32_t ch = p_stream->get_char(); if (p_stream->is_eof()) { r_token.type = TK_EOF; return OK; @@ -206,7 +189,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri r_token.value = Color::html(color_str.as_string()); r_token.type = TK_COLOR; return OK; - }; + } case '@': { cchar = p_stream->get_char(); if (cchar != '"') { @@ -219,11 +202,9 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri [[fallthrough]]; } case '"': { - String str; while (true) { - - CharType ch = p_stream->get_char(); + char32_t ch = p_stream->get_char(); if (ch == 0) { r_err_str = "Unterminated String"; @@ -233,37 +214,45 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri break; } else if (ch == '\\') { //escaped characters... - CharType next = p_stream->get_char(); + char32_t next = p_stream->get_char(); if (next == 0) { r_err_str = "Unterminated String"; r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { - - case 'b': res = 8; break; - case 't': res = 9; break; - case 'n': res = 10; break; - case 'f': res = 12; break; - case 'r': res = 13; break; + case 'b': + res = 8; + break; + case 't': + res = 9; + break; + case 'n': + res = 10; + break; + case 'f': + res = 12; + break; + case 'r': + res = 13; + break; case 'u': { //hex number for (int j = 0; j < 4; j++) { - CharType c = p_stream->get_char(); + char32_t c = p_stream->get_char(); if (c == 0) { r_err_str = "Unterminated String"; r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { - r_err_str = "Malformed hex constant in string"; r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -290,8 +279,9 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri str += res; } else { - if (ch == '\n') + if (ch == '\n') { line++; + } str += ch; } } @@ -311,7 +301,6 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; default: { - if (cchar <= 32) { break; } @@ -332,16 +321,14 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri cchar = p_stream->get_char(); } - CharType c = cchar; + char32_t c = cchar; bool exp_sign = false; bool exp_beg = false; bool is_float = false; while (true) { - switch (reading) { case READING_INT: { - if (c >= '0' && c <= '9') { //pass } else if (c == '.') { @@ -356,9 +343,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; case READING_DEC: { - if (c >= '0' && c <= '9') { - } else if (c == 'e') { reading = READING_EXP; } else { @@ -367,7 +352,6 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; case READING_EXP: { - if (c >= '0' && c <= '9') { exp_beg = true; @@ -380,8 +364,9 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; } - if (reading == READING_DONE) + if (reading == READING_DONE) { break; + } num += c; c = p_stream->get_char(); } @@ -390,19 +375,18 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri r_token.type = TK_NUMBER; - if (is_float) + if (is_float) { r_token.value = num.as_double(); - else + } else { r_token.value = num.as_int(); + } return OK; } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { - StringBuffer<> id; bool first = true; while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { - id += cchar; cchar = p_stream->get_char(); first = false; @@ -427,7 +411,6 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } Error VariantParser::_parse_enginecfg(Stream *p_stream, Vector<String> &strings, int &line, String &r_err_str) { - Token token; get_token(p_stream, token, line, r_err_str); if (token.type != TK_PARENTHESIS_OPEN) { @@ -438,8 +421,7 @@ Error VariantParser::_parse_enginecfg(Stream *p_stream, Vector<String> &strings, String accum; while (true) { - - CharType c = p_stream->get_char(); + char32_t c = p_stream->get_char(); if (p_stream->is_eof()) { r_err_str = "Unexpected EOF while parsing old-style project.godot construct"; @@ -460,7 +442,6 @@ Error VariantParser::_parse_enginecfg(Stream *p_stream, Vector<String> &strings, template <class T> Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct, int &line, String &r_err_str) { - Token token; get_token(p_stream, token, line, r_err_str); if (token.type != TK_PARENTHESIS_OPEN) { @@ -470,7 +451,6 @@ Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct, bool first = true; while (true) { - if (!first) { get_token(p_stream, token, line, r_err_str); if (token.type == TK_COMMA) { @@ -499,221 +479,208 @@ 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); - if (err) + if (err) { return err; + } value = d; return OK; } else if (token.type == TK_BRACKET_OPEN) { - Array a; Error err = _parse_array(a, p_stream, line, r_err_str, p_res_parser); - if (err) + if (err) { return err; + } value = a; return OK; - } else if (token.type == TK_IDENTIFIER) { - String id = token.value; - if (id == "true") + if (id == "true") { value = true; - else if (id == "false") + } else if (id == "false") { value = false; - else if (id == "null" || id == "nil") + } else if (id == "null" || id == "nil") { value = Variant(); - else if (id == "inf") + } else if (id == "inf") { value = Math_INF; - else if (id == "nan") + } else if (id == "nan") { value = Math_NAN; - else if (id == "Vector2") { - + } else if (id == "Vector2") { Vector<float> args; Error err = _parse_construct<float>(p_stream, args, line, r_err_str); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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) { r_err_str = "Expected '('"; @@ -733,9 +700,7 @@ 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) { r_err_str = "Expected '('"; @@ -755,10 +720,7 @@ 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) { r_err_str = "Expected '('"; @@ -781,6 +743,8 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, return ERR_PARSE_ERROR; } + REF ref = REF(Object::cast_to<Reference>(obj)); + get_token(p_stream, token, line, r_err_str); if (token.type != TK_COMMA) { r_err_str = "Expected ',' after object type"; @@ -793,32 +757,24 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, bool need_comma = false; while (true) { - if (p_stream->is_eof()) { r_err_str = "Unexpected End of File while parsing Object()"; return ERR_FILE_CORRUPT; } if (at_key) { - Error err = get_token(p_stream, token2, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token2.type == TK_PARENTHESIS_CLOSE) { - Reference *reference = Object::cast_to<Reference>(obj); - if (reference) { - value = REF(reference); - } else { - value = obj; - } + value = ref.is_valid() ? Variant(ref) : Variant(obj); return OK; } if (need_comma) { - if (token2.type != TK_COMMA) { - r_err_str = "Expected '}' or ','"; return ERR_PARSE_ERROR; } else { @@ -836,32 +792,31 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, err = get_token(p_stream, token2, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token2.type != TK_COLON) { - r_err_str = "Expected ':'"; return ERR_PARSE_ERROR; } at_key = false; } else { - Error err = get_token(p_stream, token2, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } Variant v; err = parse_value(token2, v, p_stream, line, r_err_str, p_res_parser); - if (err) + if (err) { return err; + } obj->set(key, v); need_comma = true; 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) { r_err_str = "Expected '('"; @@ -869,37 +824,30 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } if (p_res_parser && id == "Resource" && p_res_parser->func) { - RES res; Error err = p_res_parser->func(p_res_parser->userdata, p_stream, res, line, r_err_str); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } 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); - if (err) + if (err) { return err; + } value = res; - - return OK; } else { - get_token(p_stream, token, line, r_err_str); if (token.type == TK_STRING) { String path = token.value; @@ -916,20 +864,17 @@ 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); - if (err) + if (err) { return err; + } Vector<uint8_t> arr; { @@ -942,15 +887,12 @@ 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); - if (err) + if (err) { return err; + } Vector<int32_t> arr; { @@ -963,15 +905,12 @@ 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); - if (err) + if (err) { return err; + } Vector<int64_t> arr; { @@ -984,15 +923,12 @@ 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); - if (err) + if (err) { return err; + } Vector<float> arr; { @@ -1005,14 +941,12 @@ 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); - if (err) + if (err) { return err; + } Vector<double> arr; { @@ -1025,10 +959,7 @@ 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) { r_err_str = "Expected '('"; @@ -1039,7 +970,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, bool first = true; while (true) { - if (!first) { get_token(p_stream, token, line, r_err_str); if (token.type == TK_COMMA) { @@ -1075,15 +1005,12 @@ 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); - if (err) + if (err) { return err; + } Vector<Vector2> arr; { @@ -1096,15 +1023,12 @@ 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); - if (err) + if (err) { return err; + } Vector<Vector3> arr; { @@ -1117,15 +1041,12 @@ 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); - if (err) + if (err) { return err; + } Vector<Color> arr; { @@ -1138,29 +1059,23 @@ 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; } else if (token.type == TK_STRING) { - value = token.value; return OK; } else if (token.type == TK_STRING_NAME) { - value = token.value; return OK; } else if (token.type == TK_COLOR) { - value = token.value; return OK; } else { @@ -1170,30 +1085,26 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } Error VariantParser::_parse_array(Array &array, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser) { - Token token; bool need_comma = false; while (true) { - if (p_stream->is_eof()) { r_err_str = "Unexpected End of File while parsing array"; return ERR_FILE_CORRUPT; } Error err = get_token(p_stream, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type == TK_BRACKET_CLOSE) { - return OK; } if (need_comma) { - if (token.type != TK_COMMA) { - r_err_str = "Expected ','"; return ERR_PARSE_ERROR; } else { @@ -1204,8 +1115,9 @@ Error VariantParser::_parse_array(Array &array, Stream *p_stream, int &line, Str Variant v; err = parse_value(token, v, p_stream, line, r_err_str, p_res_parser); - if (err) + if (err) { return err; + } array.push_back(v); need_comma = true; @@ -1213,34 +1125,29 @@ Error VariantParser::_parse_array(Array &array, Stream *p_stream, int &line, Str } Error VariantParser::_parse_dictionary(Dictionary &object, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser) { - bool at_key = true; Variant key; Token token; bool need_comma = false; while (true) { - if (p_stream->is_eof()) { r_err_str = "Unexpected End of File while parsing dictionary"; return ERR_FILE_CORRUPT; } if (at_key) { - Error err = get_token(p_stream, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type == TK_CURLY_BRACKET_CLOSE) { - return OK; } if (need_comma) { - if (token.type != TK_COMMA) { - r_err_str = "Expected '}' or ','"; return ERR_PARSE_ERROR; } else { @@ -1251,29 +1158,31 @@ Error VariantParser::_parse_dictionary(Dictionary &object, Stream *p_stream, int err = parse_value(token, key, p_stream, line, r_err_str, p_res_parser); - if (err) + if (err) { return err; + } err = get_token(p_stream, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type != TK_COLON) { - r_err_str = "Expected ':'"; return ERR_PARSE_ERROR; } at_key = false; } else { - Error err = get_token(p_stream, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } Variant v; err = parse_value(token, v, p_stream, line, r_err_str, p_res_parser); - if (err) + if (err) { return err; + } object[key] = v; need_comma = true; at_key = true; @@ -1282,7 +1191,6 @@ Error VariantParser::_parse_dictionary(Dictionary &object, Stream *p_stream, int } Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, String &r_err_str, Tag &r_tag, ResourceParser *p_res_parser, bool p_simple_tag) { - r_tag.fields.clear(); if (token.type != TK_BRACKET_OPEN) { @@ -1291,19 +1199,18 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin } if (p_simple_tag) { - r_tag.name = ""; r_tag.fields.clear(); while (true) { - - CharType c = p_stream->get_char(); + char32_t c = p_stream->get_char(); if (p_stream->is_eof()) { r_err_str = "Unexpected EOF while parsing simple tag"; return ERR_PARSE_ERROR; } - if (c == ']') + if (c == ']') { break; + } r_tag.name += String::chr(c); } @@ -1323,15 +1230,15 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin bool parsing_tag = true; while (true) { - if (p_stream->is_eof()) { r_err_str = "Unexpected End of File while parsing tag: " + r_tag.name; return ERR_FILE_CORRUPT; } get_token(p_stream, token, line, r_err_str); - if (token.type == TK_BRACKET_CLOSE) + if (token.type == TK_BRACKET_CLOSE) { break; + } if (parsing_tag && token.type == TK_PERIOD) { r_tag.name += "."; //support tags such as [someprop.Android] for specific platforms @@ -1363,8 +1270,9 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin get_token(p_stream, token, line, r_err_str); Variant value; Error err = parse_value(token, value, p_stream, line, r_err_str, p_res_parser); - if (err) + if (err) { return err; + } r_tag.fields[id] = value; } @@ -1373,7 +1281,6 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin } Error VariantParser::parse_tag(Stream *p_stream, int &line, String &r_err_str, Tag &r_tag, ResourceParser *p_res_parser, bool p_simple_tag) { - Token token; get_token(p_stream, token, line, r_err_str); @@ -1390,14 +1297,12 @@ Error VariantParser::parse_tag(Stream *p_stream, int &line, String &r_err_str, T } Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r_err_str, Tag &r_tag, String &r_assign, Variant &r_value, ResourceParser *p_res_parser, bool p_simple_tag) { - //assign.. r_assign = ""; String what; while (true) { - - CharType c; + char32_t c; if (p_stream->saved) { c = p_stream->saved; p_stream->saved = 0; @@ -1406,17 +1311,19 @@ Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r c = p_stream->get_char(); } - if (p_stream->is_eof()) + if (p_stream->is_eof()) { return ERR_FILE_EOF; + } if (c == ';') { //comment while (true) { - CharType ch = p_stream->get_char(); + char32_t ch = p_stream->get_char(); if (p_stream->is_eof()) { return ERR_FILE_EOF; } - if (ch == '\n') + if (ch == '\n') { break; + } } continue; } @@ -1435,8 +1342,9 @@ Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r p_stream->saved = '"'; Token tk; Error err = get_token(p_stream, tk, line, r_err_str); - if (err) + if (err) { return err; + } if (tk.type != TK_STRING) { r_err_str = "Error reading quoted string"; return ERR_INVALID_DATA; @@ -1460,11 +1368,11 @@ Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r } Error VariantParser::parse(Stream *p_stream, Variant &r_ret, String &r_err_str, int &r_err_line, ResourceParser *p_res_parser) { - Token token; Error err = get_token(p_stream, token, r_err_line, r_err_str); - if (err) + if (err) { return err; + } if (token.type == TK_EOF) { return ERR_FILE_EOF; @@ -1478,103 +1386,88 @@ Error VariantParser::parse(Stream *p_stream, Variant &r_ret, String &r_err_str, //////////////////////////////////////////////////////////////////////////////// static String rtosfix(double p_value) { - - if (p_value == 0.0) + if (p_value == 0.0) { return "0"; //avoid negative zero (-0) being written, which may annoy git, svn, etc. for changes when they don't exist. - else + } else { return rtoss(p_value); + } } Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud) { - switch (p_variant.get_type()) { - case Variant::NIL: { p_store_string_func(p_store_string_ud, "null"); } break; case Variant::BOOL: { - p_store_string_func(p_store_string_ud, p_variant.operator bool() ? "true" : "false"); } break; case Variant::INT: { - p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t())); } break; case Variant::FLOAT: { - String s = rtosfix(p_variant.operator real_t()); if (s != "inf" && s != "nan") { - if (s.find(".") == -1 && s.find("e") == -1) + if (s.find(".") == -1 && s.find("e") == -1) { s += ".0"; + } } p_store_string_func(p_store_string_ud, s); } break; case Variant::STRING: { - String str = p_variant; str = "\"" + str.c_escape_multiline() + "\""; p_store_string_func(p_store_string_ud, str); } break; case Variant::VECTOR2: { - Vector2 v = p_variant; p_store_string_func(p_store_string_ud, "Vector2( " + rtosfix(v.x) + ", " + rtosfix(v.y) + " )"); } break; case Variant::VECTOR2I: { - Vector2i v = p_variant; p_store_string_func(p_store_string_ud, "Vector2i( " + itos(v.x) + ", " + itos(v.y) + " )"); } break; case Variant::RECT2: { - Rect2 aabb = p_variant; p_store_string_func(p_store_string_ud, "Rect2( " + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + " )"); } break; case Variant::RECT2I: { - Rect2i aabb = p_variant; p_store_string_func(p_store_string_ud, "Rect2i( " + itos(aabb.position.x) + ", " + itos(aabb.position.y) + ", " + itos(aabb.size.x) + ", " + itos(aabb.size.y) + " )"); } break; case Variant::VECTOR3: { - Vector3 v = p_variant; p_store_string_func(p_store_string_ud, "Vector3( " + rtosfix(v.x) + ", " + rtosfix(v.y) + ", " + rtosfix(v.z) + " )"); } break; case Variant::VECTOR3I: { - Vector3i v = p_variant; p_store_string_func(p_store_string_ud, "Vector3i( " + itos(v.x) + ", " + itos(v.y) + ", " + itos(v.z) + " )"); } break; case Variant::PLANE: { - Plane p = p_variant; p_store_string_func(p_store_string_ud, "Plane( " + rtosfix(p.normal.x) + ", " + rtosfix(p.normal.y) + ", " + rtosfix(p.normal.z) + ", " + rtosfix(p.d) + " )"); } break; case Variant::AABB: { - AABB aabb = p_variant; p_store_string_func(p_store_string_ud, "AABB( " + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.position.z) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ", " + rtosfix(aabb.size.z) + " )"); } break; case Variant::QUAT: { - Quat quat = p_variant; p_store_string_func(p_store_string_ud, "Quat( " + rtosfix(quat.x) + ", " + rtosfix(quat.y) + ", " + rtosfix(quat.z) + ", " + rtosfix(quat.w) + " )"); } break; case Variant::TRANSFORM2D: { - String s = "Transform2D( "; Transform2D m3 = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtosfix(m3.elements[i][j]); } } @@ -1583,14 +1476,13 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::BASIS: { - String s = "Basis( "; Basis m3 = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtosfix(m3.elements[i][j]); } } @@ -1599,15 +1491,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::TRANSFORM: { - String s = "Transform( "; Transform t = p_variant; Basis &m3 = t.basis; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtosfix(m3.elements[i][j]); } } @@ -1619,13 +1510,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str // misc types case Variant::COLOR: { - Color c = p_variant; p_store_string_func(p_store_string_ud, "Color( " + rtosfix(c.r) + ", " + rtosfix(c.g) + ", " + rtosfix(c.b) + ", " + rtosfix(c.a) + " )"); } break; case Variant::STRING_NAME: { - String str = p_variant; str = "@\"" + str.c_escape() + "\""; @@ -1633,7 +1522,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::NODE_PATH: { - String str = p_variant; str = "NodePath(\"" + str.c_escape() + "\")"; @@ -1642,7 +1530,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::OBJECT: { - Object *obj = p_variant; if (!obj) { @@ -1657,13 +1544,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str //try external function if (p_encode_res_func) { - res_text = p_encode_res_func(p_encode_res_ud, res); } //try path because it's a file if (res_text == String() && res->get_path().is_resource_file()) { - //external resource String path = res->get_path(); res_text = "Resource( \"" + path + "\")"; @@ -1684,7 +1569,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str obj->get_property_list(&props); bool first = true; for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE || E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE) { //must be serialized @@ -1704,7 +1588,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::DICTIONARY: { - Dictionary dict = p_variant; List<Variant> keys; @@ -1713,7 +1596,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, "{\n"); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - /* if (!_check_type(dict[E->get()])) continue; @@ -1721,22 +1603,24 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); p_store_string_func(p_store_string_ud, ": "); write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); - if (E->next()) + if (E->next()) { p_store_string_func(p_store_string_ud, ",\n"); + } else { + p_store_string_func(p_store_string_ud, "\n"); + } } - p_store_string_func(p_store_string_ud, "\n}"); + p_store_string_func(p_store_string_ud, "}"); } break; case Variant::ARRAY: { - p_store_string_func(p_store_string_ud, "[ "); Array array = p_variant; int len = array.size(); for (int i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); } p_store_string_func(p_store_string_ud, " ]"); @@ -1744,7 +1628,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_BYTE_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedByteArray( "); String s; Vector<uint8_t> data = p_variant; @@ -1752,9 +1635,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str const uint8_t *ptr = data.ptr(); for (int i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } p_store_string_func(p_store_string_ud, itos(ptr[i])); } @@ -1763,16 +1646,15 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_INT32_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedInt32Array( "); Vector<int32_t> data = p_variant; int32_t len = data.size(); const int32_t *ptr = data.ptr(); for (int32_t i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } p_store_string_func(p_store_string_ud, itos(ptr[i])); } @@ -1781,16 +1663,15 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_INT64_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedInt64Array( "); Vector<int64_t> data = p_variant; int64_t len = data.size(); const int64_t *ptr = data.ptr(); for (int64_t i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } p_store_string_func(p_store_string_ud, itos(ptr[i])); } @@ -1799,16 +1680,15 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_FLOAT32_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedFloat32Array( "); Vector<float> data = p_variant; int len = data.size(); const float *ptr = data.ptr(); for (int i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } p_store_string_func(p_store_string_ud, rtosfix(ptr[i])); } @@ -1816,16 +1696,15 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_FLOAT64_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedFloat64Array( "); Vector<double> data = p_variant; int len = data.size(); const double *ptr = data.ptr(); for (int i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } p_store_string_func(p_store_string_ud, rtosfix(ptr[i])); } @@ -1833,7 +1712,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_STRING_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedStringArray( "); Vector<String> data = p_variant; int len = data.size(); @@ -1843,9 +1721,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str //write_string("\n"); for (int i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } String str = ptr[i]; p_store_string_func(p_store_string_ud, "\"" + str.c_escape() + "\""); } @@ -1854,16 +1732,15 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_VECTOR2_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedVector2Array( "); Vector<Vector2> data = p_variant; int len = data.size(); const Vector2 *ptr = data.ptr(); for (int i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y)); } @@ -1871,16 +1748,15 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_VECTOR3_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedVector3Array( "); Vector<Vector3> data = p_variant; int len = data.size(); const Vector3 *ptr = data.ptr(); for (int i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y) + ", " + rtosfix(ptr[i].z)); } @@ -1888,7 +1764,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PACKED_COLOR_ARRAY: { - p_store_string_func(p_store_string_ud, "PackedColorArray( "); Vector<Color> data = p_variant; @@ -1896,9 +1771,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str const Color *ptr = data.ptr(); for (int i = 0; i < len; i++) { - - if (i > 0) + if (i > 0) { p_store_string_func(p_store_string_ud, ", "); + } p_store_string_func(p_store_string_ud, rtosfix(ptr[i].r) + ", " + rtosfix(ptr[i].g) + ", " + rtosfix(ptr[i].b) + ", " + rtosfix(ptr[i].a)); } @@ -1913,14 +1788,12 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } static Error _write_to_str(void *ud, const String &p_string) { - String *str = (String *)ud; (*str) += p_string; return OK; } Error VariantWriter::write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud) { - r_string = String(); return write(p_variant, _write_to_str, &r_string, p_encode_res_func, p_encode_res_ud); diff --git a/core/variant_parser.h b/core/variant/variant_parser.h index 63ed51bcc9..5703f0200c 100644 --- a/core/variant_parser.h +++ b/core/variant/variant_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,52 +31,47 @@ #ifndef VARIANT_PARSER_H #define VARIANT_PARSER_H +#include "core/io/resource.h" #include "core/os/file_access.h" -#include "core/resource.h" -#include "core/variant.h" +#include "core/variant/variant.h" class VariantParser { public: struct Stream { - - virtual CharType get_char() = 0; + virtual char32_t get_char() = 0; virtual bool is_utf8() const = 0; virtual bool is_eof() const = 0; - CharType saved; + char32_t saved = 0; - Stream() : - saved(0) {} + Stream() {} virtual ~Stream() {} }; struct StreamFile : public Stream { + FileAccess *f = nullptr; - FileAccess *f; - - virtual CharType get_char(); + virtual char32_t get_char(); virtual bool is_utf8() const; virtual bool is_eof() const; - StreamFile() { f = nullptr; } + StreamFile() {} }; struct StreamString : public Stream { - String s; - int pos; + int pos = 0; - virtual CharType get_char(); + virtual char32_t get_char(); virtual bool is_utf8() const; virtual bool is_eof() const; - StreamString() { pos = 0; } + StreamString() {} }; typedef Error (*ParseResourceFunc)(void *p_self, Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str); struct ResourceParser { - void *userdata = nullptr; ParseResourceFunc func; ParseResourceFunc ext_func; @@ -105,7 +100,6 @@ public: }; enum Expecting { - EXPECT_OBJECT, EXPECT_OBJECT_KEY, EXPECT_COLON, @@ -113,13 +107,11 @@ public: }; struct Token { - TokenType type; Variant value; }; struct Tag { - String name; Map<String, Variant> fields; }; diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp new file mode 100644 index 0000000000..ea8263402a --- /dev/null +++ b/core/variant/variant_setget.cpp @@ -0,0 +1,2524 @@ +/*************************************************************************/ +/* variant_setget.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "variant.h" + +#include "core/core_string_names.h" +#include "core/debugger/engine_debugger.h" +#include "core/object/class_db.h" +#include "core/templates/local_vector.h" +#include "core/variant/variant_internal.h" + +/**** NAMED SETTERS AND GETTERS ****/ + +#define SETGET_STRUCT(m_base_type, m_member_type, m_member) \ + struct VariantSetGet_##m_base_type##_##m_member { \ + static void get(const Variant *base, Variant *member) { \ + VariantTypeAdjust<m_member_type>::adjust(member); \ + *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member; \ + } \ + static void ptr_get(const void *base, void *member) { \ + PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_member, member); \ + } \ + static void set(Variant *base, const Variant *value, bool &valid) { \ + if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \ + valid = true; \ + } else { \ + valid = false; \ + } \ + } \ + static void validated_set(Variant *base, const Variant *value) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \ + } \ + static void ptr_set(void *base, const void *member) { \ + m_base_type b = PtrToArg<m_base_type>::convert(base); \ + b.m_member = PtrToArg<m_member_type>::convert(member); \ + PtrToArg<m_base_type>::encode(b, base); \ + } \ + static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \ + }; + +#define SETGET_NUMBER_STRUCT(m_base_type, m_member_type, m_member) \ + struct VariantSetGet_##m_base_type##_##m_member { \ + static void get(const Variant *base, Variant *member) { \ + VariantTypeAdjust<m_member_type>::adjust(member); \ + *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member; \ + } \ + static void ptr_get(const void *base, void *member) { \ + PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_member, member); \ + } \ + static void set(Variant *base, const Variant *value, bool &valid) { \ + if (value->get_type() == Variant::FLOAT) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<double>::get_ptr(value); \ + valid = true; \ + } else if (value->get_type() == Variant::INT) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<int64_t>::get_ptr(value); \ + valid = true; \ + } else { \ + valid = false; \ + } \ + } \ + static void validated_set(Variant *base, const Variant *value) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \ + } \ + static void ptr_set(void *base, const void *member) { \ + m_base_type b = PtrToArg<m_base_type>::convert(base); \ + b.m_member = PtrToArg<m_member_type>::convert(member); \ + PtrToArg<m_base_type>::encode(b, base); \ + } \ + static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \ + }; + +#define SETGET_STRUCT_CUSTOM(m_base_type, m_member_type, m_member, m_custom) \ + struct VariantSetGet_##m_base_type##_##m_member { \ + static void get(const Variant *base, Variant *member) { \ + VariantTypeAdjust<m_member_type>::adjust(member); \ + *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom; \ + } \ + static void ptr_get(const void *base, void *member) { \ + PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_custom, member); \ + } \ + static void set(Variant *base, const Variant *value, bool &valid) { \ + if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \ + valid = true; \ + } else { \ + valid = false; \ + } \ + } \ + static void validated_set(Variant *base, const Variant *value) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \ + } \ + static void ptr_set(void *base, const void *member) { \ + m_base_type b = PtrToArg<m_base_type>::convert(base); \ + b.m_custom = PtrToArg<m_member_type>::convert(member); \ + PtrToArg<m_base_type>::encode(b, base); \ + } \ + static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \ + }; + +#define SETGET_NUMBER_STRUCT_CUSTOM(m_base_type, m_member_type, m_member, m_custom) \ + struct VariantSetGet_##m_base_type##_##m_member { \ + static void get(const Variant *base, Variant *member) { \ + VariantTypeAdjust<m_member_type>::adjust(member); \ + *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom; \ + } \ + static void ptr_get(const void *base, void *member) { \ + PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_custom, member); \ + } \ + static void set(Variant *base, const Variant *value, bool &valid) { \ + if (value->get_type() == Variant::FLOAT) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<double>::get_ptr(value); \ + valid = true; \ + } else if (value->get_type() == Variant::INT) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<int64_t>::get_ptr(value); \ + valid = true; \ + } else { \ + valid = false; \ + } \ + } \ + static void validated_set(Variant *base, const Variant *value) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \ + } \ + static void ptr_set(void *base, const void *member) { \ + m_base_type b = PtrToArg<m_base_type>::convert(base); \ + b.m_custom = PtrToArg<m_member_type>::convert(member); \ + PtrToArg<m_base_type>::encode(b, base); \ + } \ + static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \ + }; + +#define SETGET_STRUCT_FUNC(m_base_type, m_member_type, m_member, m_setter, m_getter) \ + struct VariantSetGet_##m_base_type##_##m_member { \ + static void get(const Variant *base, Variant *member) { \ + VariantTypeAdjust<m_member_type>::adjust(member); \ + *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(); \ + } \ + static void ptr_get(const void *base, void *member) { \ + PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(), member); \ + } \ + static void set(Variant *base, const Variant *value, bool &valid) { \ + if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \ + valid = true; \ + } else { \ + valid = false; \ + } \ + } \ + static void validated_set(Variant *base, const Variant *value) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \ + } \ + static void ptr_set(void *base, const void *member) { \ + m_base_type b = PtrToArg<m_base_type>::convert(base); \ + b.m_setter(PtrToArg<m_member_type>::convert(member)); \ + PtrToArg<m_base_type>::encode(b, base); \ + } \ + static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \ + }; + +#define SETGET_NUMBER_STRUCT_FUNC(m_base_type, m_member_type, m_member, m_setter, m_getter) \ + struct VariantSetGet_##m_base_type##_##m_member { \ + static void get(const Variant *base, Variant *member) { \ + VariantTypeAdjust<m_member_type>::adjust(member); \ + *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(); \ + } \ + static void ptr_get(const void *base, void *member) { \ + PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(), member); \ + } \ + static void set(Variant *base, const Variant *value, bool &valid) { \ + if (value->get_type() == Variant::FLOAT) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<double>::get_ptr(value)); \ + valid = true; \ + } else if (value->get_type() == Variant::INT) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<int64_t>::get_ptr(value)); \ + valid = true; \ + } else { \ + valid = false; \ + } \ + } \ + static void validated_set(Variant *base, const Variant *value) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \ + } \ + static void ptr_set(void *base, const void *member) { \ + m_base_type b = PtrToArg<m_base_type>::convert(base); \ + b.m_setter(PtrToArg<m_member_type>::convert(member)); \ + PtrToArg<m_base_type>::encode(b, base); \ + } \ + static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \ + }; + +#define SETGET_STRUCT_FUNC_INDEX(m_base_type, m_member_type, m_member, m_setter, m_getter, m_index) \ + struct VariantSetGet_##m_base_type##_##m_member { \ + static void get(const Variant *base, Variant *member) { \ + VariantTypeAdjust<m_member_type>::adjust(member); \ + *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(m_index); \ + } \ + static void ptr_get(const void *base, void *member) { \ + PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(m_index), member); \ + } \ + static void set(Variant *base, const Variant *value, bool &valid) { \ + if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(m_index, *VariantGetInternalPtr<m_member_type>::get_ptr(value)); \ + valid = true; \ + } else { \ + valid = false; \ + } \ + } \ + static void validated_set(Variant *base, const Variant *value) { \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(m_index, *VariantGetInternalPtr<m_member_type>::get_ptr(value)); \ + } \ + static void ptr_set(void *base, const void *member) { \ + m_base_type b = PtrToArg<m_base_type>::convert(base); \ + b.m_setter(m_index, PtrToArg<m_member_type>::convert(member)); \ + PtrToArg<m_base_type>::encode(b, base); \ + } \ + static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \ + }; + +SETGET_NUMBER_STRUCT(Vector2, double, x) +SETGET_NUMBER_STRUCT(Vector2, double, y) + +SETGET_NUMBER_STRUCT(Vector2i, int64_t, x) +SETGET_NUMBER_STRUCT(Vector2i, int64_t, y) + +SETGET_NUMBER_STRUCT(Vector3, double, x) +SETGET_NUMBER_STRUCT(Vector3, double, y) +SETGET_NUMBER_STRUCT(Vector3, double, z) + +SETGET_NUMBER_STRUCT(Vector3i, int64_t, x) +SETGET_NUMBER_STRUCT(Vector3i, int64_t, y) +SETGET_NUMBER_STRUCT(Vector3i, int64_t, z) + +SETGET_STRUCT(Rect2, Vector2, position) +SETGET_STRUCT(Rect2, Vector2, size) +SETGET_STRUCT_FUNC(Rect2, Vector2, end, set_end, get_end) + +SETGET_STRUCT(Rect2i, Vector2i, position) +SETGET_STRUCT(Rect2i, Vector2i, size) +SETGET_STRUCT_FUNC(Rect2i, Vector2i, end, set_end, get_end) + +SETGET_STRUCT(AABB, Vector3, position) +SETGET_STRUCT(AABB, Vector3, size) +SETGET_STRUCT_FUNC(AABB, Vector3, end, set_end, get_end) + +SETGET_STRUCT_CUSTOM(Transform2D, Vector2, x, elements[0]) +SETGET_STRUCT_CUSTOM(Transform2D, Vector2, y, elements[1]) +SETGET_STRUCT_CUSTOM(Transform2D, Vector2, origin, elements[2]) + +SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, x, normal.x) +SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, y, normal.y) +SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, z, normal.z) +SETGET_STRUCT(Plane, Vector3, normal) +SETGET_NUMBER_STRUCT(Plane, double, d) + +SETGET_NUMBER_STRUCT(Quat, double, x) +SETGET_NUMBER_STRUCT(Quat, double, y) +SETGET_NUMBER_STRUCT(Quat, double, z) +SETGET_NUMBER_STRUCT(Quat, double, w) + +SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, x, set_axis, get_axis, 0) +SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, y, set_axis, get_axis, 1) +SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, z, set_axis, get_axis, 2) + +SETGET_STRUCT(Transform, Basis, basis) +SETGET_STRUCT(Transform, Vector3, origin) + +SETGET_NUMBER_STRUCT(Color, double, r) +SETGET_NUMBER_STRUCT(Color, double, g) +SETGET_NUMBER_STRUCT(Color, double, b) +SETGET_NUMBER_STRUCT(Color, double, a) + +SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, r8, set_r8, get_r8) +SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, g8, set_g8, get_g8) +SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, b8, set_b8, get_b8) +SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, a8, set_a8, get_a8) + +SETGET_NUMBER_STRUCT_FUNC(Color, double, h, set_h, get_h) +SETGET_NUMBER_STRUCT_FUNC(Color, double, s, set_s, get_s) +SETGET_NUMBER_STRUCT_FUNC(Color, double, v, set_v, get_v) + +struct VariantSetterGetterInfo { + void (*setter)(Variant *base, const Variant *value, bool &valid); + void (*getter)(const Variant *base, Variant *value); + Variant::ValidatedSetter validated_setter; + Variant::ValidatedGetter validated_getter; + Variant::PTRSetter ptr_setter; + Variant::PTRGetter ptr_getter; + Variant::Type member_type; +}; + +static LocalVector<VariantSetterGetterInfo> variant_setters_getters[Variant::VARIANT_MAX]; +static LocalVector<StringName> variant_setters_getters_names[Variant::VARIANT_MAX]; //one next to another to make it cache friendly + +template <class T> +static void register_member(Variant::Type p_type, const StringName &p_member) { + VariantSetterGetterInfo sgi; + sgi.setter = T::set; + sgi.validated_setter = T::validated_set; + sgi.ptr_setter = T::ptr_set; + + sgi.getter = T::get; + sgi.validated_getter = T::get; + sgi.ptr_getter = T::ptr_get; + + sgi.member_type = T::get_type(); + + variant_setters_getters[p_type].push_back(sgi); + variant_setters_getters_names[p_type].push_back(p_member); +} + +void register_named_setters_getters() { +#define REGISTER_MEMBER(m_base_type, m_member) register_member<VariantSetGet_##m_base_type##_##m_member>(GetTypeInfo<m_base_type>::VARIANT_TYPE, #m_member) + + REGISTER_MEMBER(Vector2, x); + REGISTER_MEMBER(Vector2, y); + + REGISTER_MEMBER(Vector2i, x); + REGISTER_MEMBER(Vector2i, y); + + REGISTER_MEMBER(Vector3, x); + REGISTER_MEMBER(Vector3, y); + REGISTER_MEMBER(Vector3, z); + + REGISTER_MEMBER(Vector3i, x); + REGISTER_MEMBER(Vector3i, y); + REGISTER_MEMBER(Vector3i, z); + + REGISTER_MEMBER(Rect2, position); + REGISTER_MEMBER(Rect2, size); + REGISTER_MEMBER(Rect2, end); + + REGISTER_MEMBER(Rect2i, position); + REGISTER_MEMBER(Rect2i, size); + REGISTER_MEMBER(Rect2i, end); + + REGISTER_MEMBER(AABB, position); + REGISTER_MEMBER(AABB, size); + REGISTER_MEMBER(AABB, end); + + REGISTER_MEMBER(Transform2D, x); + REGISTER_MEMBER(Transform2D, y); + REGISTER_MEMBER(Transform2D, origin); + + REGISTER_MEMBER(Plane, x); + REGISTER_MEMBER(Plane, y); + REGISTER_MEMBER(Plane, z); + REGISTER_MEMBER(Plane, d); + REGISTER_MEMBER(Plane, normal); + + REGISTER_MEMBER(Quat, x); + REGISTER_MEMBER(Quat, y); + REGISTER_MEMBER(Quat, z); + REGISTER_MEMBER(Quat, w); + + REGISTER_MEMBER(Basis, x); + REGISTER_MEMBER(Basis, y); + REGISTER_MEMBER(Basis, z); + + REGISTER_MEMBER(Transform, basis); + REGISTER_MEMBER(Transform, origin); + + REGISTER_MEMBER(Color, r); + REGISTER_MEMBER(Color, g); + REGISTER_MEMBER(Color, b); + REGISTER_MEMBER(Color, a); + + REGISTER_MEMBER(Color, r8); + REGISTER_MEMBER(Color, g8); + REGISTER_MEMBER(Color, b8); + REGISTER_MEMBER(Color, a8); + + REGISTER_MEMBER(Color, h); + REGISTER_MEMBER(Color, s); + REGISTER_MEMBER(Color, v); +} + +void unregister_named_setters_getters() { + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + variant_setters_getters[i].clear(); + variant_setters_getters_names[i].clear(); + } +} + +bool Variant::has_member(Variant::Type p_type, const StringName &p_member) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); + + for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { + if (variant_setters_getters_names[p_type][i] == p_member) { + return true; + } + } + return false; +} + +Variant::Type Variant::get_member_type(Variant::Type p_type, const StringName &p_member) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::VARIANT_MAX); + + for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { + if (variant_setters_getters_names[p_type][i] == p_member) { + return variant_setters_getters[p_type][i].member_type; + } + } + + return Variant::NIL; +} + +void Variant::get_member_list(Variant::Type p_type, List<StringName> *r_members) { + for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { + r_members->push_back(variant_setters_getters_names[p_type][i]); + } +} + +int Variant::get_member_count(Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, -1); + return variant_setters_getters_names[p_type].size(); +} + +Variant::ValidatedSetter Variant::get_member_validated_setter(Variant::Type p_type, const StringName &p_member) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + + for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { + if (variant_setters_getters_names[p_type][i] == p_member) { + return variant_setters_getters[p_type][i].validated_setter; + } + } + + return nullptr; +} +Variant::ValidatedGetter Variant::get_member_validated_getter(Variant::Type p_type, const StringName &p_member) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + + for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { + if (variant_setters_getters_names[p_type][i] == p_member) { + return variant_setters_getters[p_type][i].validated_getter; + } + } + + return nullptr; +} + +Variant::PTRSetter Variant::get_member_ptr_setter(Variant::Type p_type, const StringName &p_member) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + + for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { + if (variant_setters_getters_names[p_type][i] == p_member) { + return variant_setters_getters[p_type][i].ptr_setter; + } + } + + return nullptr; +} + +Variant::PTRGetter Variant::get_member_ptr_getter(Variant::Type p_type, const StringName &p_member) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + + for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { + if (variant_setters_getters_names[p_type][i] == p_member) { + return variant_setters_getters[p_type][i].ptr_getter; + } + } + + return nullptr; +} + +void Variant::set_named(const StringName &p_member, const Variant &p_value, bool &r_valid) { + uint32_t s = variant_setters_getters[type].size(); + if (s) { + for (uint32_t i = 0; i < s; i++) { + if (variant_setters_getters_names[type][i] == p_member) { + variant_setters_getters[type][i].setter(this, &p_value, r_valid); + return; + } + } + r_valid = false; + + } else if (type == Variant::OBJECT) { + Object *obj = get_validated_object(); + if (!obj) { + r_valid = false; + } else { + obj->set(p_member, p_value, &r_valid); + return; + } + } else if (type == Variant::DICTIONARY) { + Variant *v = VariantGetInternalPtr<Dictionary>::get_ptr(this)->getptr(p_member); + if (v) { + *v = p_value; + r_valid = true; + } else { + r_valid = false; + } + + } else { + r_valid = false; + } +} + +Variant Variant::get_named(const StringName &p_member, bool &r_valid) const { + Variant ret; + uint32_t s = variant_setters_getters[type].size(); + if (s) { + for (uint32_t i = 0; i < s; i++) { + if (variant_setters_getters_names[type][i] == p_member) { + variant_setters_getters[type][i].getter(this, &ret); + r_valid = true; + return ret; + } + } + + r_valid = false; + + } else if (type == Variant::OBJECT) { + Object *obj = get_validated_object(); + if (!obj) { + r_valid = false; + return "Instance base is null."; + } else { + return obj->get(p_member, &r_valid); + } + } else if (type == Variant::DICTIONARY) { + const Variant *v = VariantGetInternalPtr<Dictionary>::get_ptr(this)->getptr(p_member); + if (v) { + r_valid = true; + + return *v; + } else { + r_valid = false; + } + + } else { + r_valid = false; + } + + return ret; +} + +/**** INDEXED SETTERS AND GETTERS ****/ + +#ifdef DEBUG_ENABLED + +#define OOB_TEST(m_idx, m_v) \ + ERR_FAIL_INDEX(m_idx, m_v) + +#else + +#define OOB_TEST(m_idx, m_v) + +#endif + +#ifdef DEBUG_ENABLED + +#define NULL_TEST(m_key) \ + ERR_FAIL_COND(!m_key) + +#else + +#define NULL_TEST(m_key) + +#endif + +#define INDEXED_SETGET_STRUCT_TYPED(m_base_type, m_elem_type) \ + struct VariantIndexedSetGet_##m_base_type { \ + static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + return; \ + } \ + VariantTypeAdjust<m_elem_type>::adjust(value); \ + *VariantGetInternalPtr<m_elem_type>::get_ptr(value) = (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index]; \ + *oob = false; \ + } \ + static void ptr_get(const void *base, int64_t index, void *member) { \ + /* avoid ptrconvert for performance*/ \ + const m_base_type &v = *reinterpret_cast<const m_base_type *>(base); \ + if (index < 0) \ + index += v.size(); \ + OOB_TEST(index, v.size()); \ + PtrToArg<m_elem_type>::encode(v[index], member); \ + } \ + static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ + if (value->get_type() != GetTypeInfo<m_elem_type>::VARIANT_TYPE) { \ + *oob = false; \ + *valid = false; \ + return; \ + } \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + *valid = false; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base)).write[index] = *VariantGetInternalPtr<m_elem_type>::get_ptr(value); \ + *oob = false; \ + *valid = true; \ + } \ + static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base)).write[index] = *VariantGetInternalPtr<m_elem_type>::get_ptr(value); \ + *oob = false; \ + } \ + static void ptr_set(void *base, int64_t index, const void *member) { \ + /* avoid ptrconvert for performance*/ \ + m_base_type &v = *reinterpret_cast<m_base_type *>(base); \ + if (index < 0) \ + index += v.size(); \ + OOB_TEST(index, v.size()); \ + v.write[index] = PtrToArg<m_elem_type>::convert(member); \ + } \ + static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \ + static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); } \ + }; + +#define INDEXED_SETGET_STRUCT_TYPED_NUMERIC(m_base_type, m_elem_type, m_assign_type) \ + struct VariantIndexedSetGet_##m_base_type { \ + static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + return; \ + } \ + VariantTypeAdjust<m_elem_type>::adjust(value); \ + *VariantGetInternalPtr<m_elem_type>::get_ptr(value) = (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index]; \ + *oob = false; \ + } \ + static void ptr_get(const void *base, int64_t index, void *member) { \ + /* avoid ptrconvert for performance*/ \ + const m_base_type &v = *reinterpret_cast<const m_base_type *>(base); \ + if (index < 0) \ + index += v.size(); \ + OOB_TEST(index, v.size()); \ + PtrToArg<m_elem_type>::encode(v[index], member); \ + } \ + static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + *valid = false; \ + return; \ + } \ + m_assign_type num; \ + if (value->get_type() == Variant::INT) { \ + num = (m_assign_type)*VariantGetInternalPtr<int64_t>::get_ptr(value); \ + } else if (value->get_type() == Variant::FLOAT) { \ + num = (m_assign_type)*VariantGetInternalPtr<double>::get_ptr(value); \ + } else { \ + *oob = false; \ + *valid = false; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base)).write[index] = num; \ + *oob = false; \ + *valid = true; \ + } \ + static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base)).write[index] = *VariantGetInternalPtr<m_elem_type>::get_ptr(value); \ + *oob = false; \ + } \ + static void ptr_set(void *base, int64_t index, const void *member) { \ + /* avoid ptrconvert for performance*/ \ + m_base_type &v = *reinterpret_cast<m_base_type *>(base); \ + if (index < 0) \ + index += v.size(); \ + OOB_TEST(index, v.size()); \ + v.write[index] = PtrToArg<m_elem_type>::convert(member); \ + } \ + static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \ + static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); } \ + }; + +#define INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(m_base_type, m_elem_type, m_assign_type, m_max) \ + struct VariantIndexedSetGet_##m_base_type { \ + static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + return; \ + } \ + VariantTypeAdjust<m_elem_type>::adjust(value); \ + *VariantGetInternalPtr<m_elem_type>::get_ptr(value) = (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index]; \ + *oob = false; \ + } \ + static void ptr_get(const void *base, int64_t index, void *member) { \ + /* avoid ptrconvert for performance*/ \ + const m_base_type &v = *reinterpret_cast<const m_base_type *>(base); \ + OOB_TEST(index, m_max); \ + PtrToArg<m_elem_type>::encode(v[index], member); \ + } \ + static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + *valid = false; \ + return; \ + } \ + m_assign_type num; \ + if (value->get_type() == Variant::INT) { \ + num = (m_assign_type)*VariantGetInternalPtr<int64_t>::get_ptr(value); \ + } else if (value->get_type() == Variant::FLOAT) { \ + num = (m_assign_type)*VariantGetInternalPtr<double>::get_ptr(value); \ + } else { \ + *oob = false; \ + *valid = false; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = num; \ + *oob = false; \ + *valid = true; \ + } \ + static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *VariantGetInternalPtr<m_elem_type>::get_ptr(value); \ + *oob = false; \ + } \ + static void ptr_set(void *base, int64_t index, const void *member) { \ + /* avoid ptrconvert for performance*/ \ + m_base_type &v = *reinterpret_cast<m_base_type *>(base); \ + OOB_TEST(index, m_max); \ + v[index] = PtrToArg<m_elem_type>::convert(member); \ + } \ + static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \ + static uint64_t get_indexed_size(const Variant *base) { return m_max; } \ + }; + +#define INDEXED_SETGET_STRUCT_BULTIN_ACCESSOR(m_base_type, m_elem_type, m_accessor, m_max) \ + struct VariantIndexedSetGet_##m_base_type { \ + static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + return; \ + } \ + VariantTypeAdjust<m_elem_type>::adjust(value); \ + *VariantGetInternalPtr<m_elem_type>::get_ptr(value) = (*VariantGetInternalPtr<m_base_type>::get_ptr(base))m_accessor[index]; \ + *oob = false; \ + } \ + static void ptr_get(const void *base, int64_t index, void *member) { \ + /* avoid ptrconvert for performance*/ \ + const m_base_type &v = *reinterpret_cast<const m_base_type *>(base); \ + OOB_TEST(index, m_max); \ + PtrToArg<m_elem_type>::encode(v m_accessor[index], member); \ + } \ + static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ + if (value->get_type() != GetTypeInfo<m_elem_type>::VARIANT_TYPE) { \ + *oob = false; \ + *valid = false; \ + } \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + *valid = false; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base)) m_accessor[index] = *VariantGetInternalPtr<m_elem_type>::get_ptr(value); \ + *oob = false; \ + *valid = true; \ + } \ + static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base)) m_accessor[index] = *VariantGetInternalPtr<m_elem_type>::get_ptr(value); \ + *oob = false; \ + } \ + static void ptr_set(void *base, int64_t index, const void *member) { \ + /* avoid ptrconvert for performance*/ \ + m_base_type &v = *reinterpret_cast<m_base_type *>(base); \ + OOB_TEST(index, m_max); \ + v m_accessor[index] = PtrToArg<m_elem_type>::convert(member); \ + } \ + static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \ + static uint64_t get_indexed_size(const Variant *base) { return m_max; } \ + }; + +#define INDEXED_SETGET_STRUCT_BULTIN_FUNC(m_base_type, m_elem_type, m_set, m_get, m_max) \ + struct VariantIndexedSetGet_##m_base_type { \ + static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + return; \ + } \ + VariantTypeAdjust<m_elem_type>::adjust(value); \ + *VariantGetInternalPtr<m_elem_type>::get_ptr(value) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_get(index); \ + *oob = false; \ + } \ + static void ptr_get(const void *base, int64_t index, void *member) { \ + /* avoid ptrconvert for performance*/ \ + const m_base_type &v = *reinterpret_cast<const m_base_type *>(base); \ + OOB_TEST(index, m_max); \ + PtrToArg<m_elem_type>::encode(v.m_get(index), member); \ + } \ + static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ + if (value->get_type() != GetTypeInfo<m_elem_type>::VARIANT_TYPE) { \ + *oob = false; \ + *valid = false; \ + } \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + *valid = false; \ + return; \ + } \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_set(index, *VariantGetInternalPtr<m_elem_type>::get_ptr(value)); \ + *oob = false; \ + *valid = true; \ + } \ + static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ + if (index < 0 || index >= m_max) { \ + *oob = true; \ + return; \ + } \ + VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_set(index, *VariantGetInternalPtr<m_elem_type>::get_ptr(value)); \ + *oob = false; \ + } \ + static void ptr_set(void *base, int64_t index, const void *member) { \ + /* avoid ptrconvert for performance*/ \ + m_base_type &v = *reinterpret_cast<m_base_type *>(base); \ + OOB_TEST(index, m_max); \ + v.m_set(index, PtrToArg<m_elem_type>::convert(member)); \ + } \ + static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \ + static uint64_t get_indexed_size(const Variant *base) { return m_max; } \ + }; + +#define INDEXED_SETGET_STRUCT_VARIANT(m_base_type) \ + struct VariantIndexedSetGet_##m_base_type { \ + static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + return; \ + } \ + *value = (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index]; \ + *oob = false; \ + } \ + static void ptr_get(const void *base, int64_t index, void *member) { \ + /* avoid ptrconvert for performance*/ \ + const m_base_type &v = *reinterpret_cast<const m_base_type *>(base); \ + if (index < 0) \ + index += v.size(); \ + OOB_TEST(index, v.size()); \ + PtrToArg<Variant>::encode(v[index], member); \ + } \ + static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + *valid = false; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value; \ + *oob = false; \ + *valid = true; \ + } \ + static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ + int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \ + if (index < 0) { \ + index += size; \ + } \ + if (index < 0 || index >= size) { \ + *oob = true; \ + return; \ + } \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value; \ + *oob = false; \ + } \ + static void ptr_set(void *base, int64_t index, const void *member) { \ + /* avoid ptrconvert for performance*/ \ + m_base_type &v = *reinterpret_cast<m_base_type *>(base); \ + if (index < 0) \ + index += v.size(); \ + OOB_TEST(index, v.size()); \ + v[index] = PtrToArg<Variant>::convert(member); \ + } \ + static Variant::Type get_index_type() { return Variant::NIL; } \ + static uint64_t get_indexed_size(const Variant *base) { return 0; } \ + }; + +#define INDEXED_SETGET_STRUCT_DICT(m_base_type) \ + struct VariantIndexedSetGet_##m_base_type { \ + static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ + const Variant *ptr = VariantGetInternalPtr<m_base_type>::get_ptr(base)->getptr(index); \ + if (!ptr) { \ + *oob = true; \ + return; \ + } \ + *value = *ptr; \ + *oob = false; \ + } \ + static void ptr_get(const void *base, int64_t index, void *member) { \ + /* avoid ptrconvert for performance*/ \ + const m_base_type &v = *reinterpret_cast<const m_base_type *>(base); \ + const Variant *ptr = v.getptr(index); \ + NULL_TEST(ptr); \ + PtrToArg<Variant>::encode(*ptr, member); \ + } \ + static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value; \ + *oob = false; \ + *valid = true; \ + } \ + static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ + (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value; \ + *oob = false; \ + } \ + static void ptr_set(void *base, int64_t index, const void *member) { \ + m_base_type &v = *reinterpret_cast<m_base_type *>(base); \ + v[index] = PtrToArg<Variant>::convert(member); \ + } \ + static Variant::Type get_index_type() { return Variant::NIL; } \ + static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); } \ + }; + +INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector2, double, real_t, 2) +INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector2i, int64_t, int32_t, 2) +INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector3, double, real_t, 3) +INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector3i, int64_t, int32_t, 3) +INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Quat, double, real_t, 4) +INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Color, double, float, 4) + +INDEXED_SETGET_STRUCT_BULTIN_ACCESSOR(Transform2D, Vector2, .elements, 3) +INDEXED_SETGET_STRUCT_BULTIN_FUNC(Basis, Vector3, set_axis, get_axis, 3) + +INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedByteArray, int64_t, uint8_t) +INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedInt32Array, int64_t, int32_t) +INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedInt64Array, int64_t, int64_t) +INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedFloat32Array, double, float) +INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedFloat64Array, double, double) +INDEXED_SETGET_STRUCT_TYPED(PackedVector2Array, Vector2) +INDEXED_SETGET_STRUCT_TYPED(PackedVector3Array, Vector3) +INDEXED_SETGET_STRUCT_TYPED(PackedStringArray, String) +INDEXED_SETGET_STRUCT_TYPED(PackedColorArray, Color) + +INDEXED_SETGET_STRUCT_VARIANT(Array) +INDEXED_SETGET_STRUCT_DICT(Dictionary) + +struct VariantIndexedSetterGetterInfo { + void (*setter)(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob); + void (*getter)(const Variant *base, int64_t index, Variant *value, bool *oob); + + Variant::ValidatedIndexedSetter validated_setter; + Variant::ValidatedIndexedGetter validated_getter; + + Variant::PTRIndexedSetter ptr_setter; + Variant::PTRIndexedGetter ptr_getter; + + uint64_t (*get_indexed_size)(const Variant *base); + + Variant::Type index_type; + + bool valid = false; +}; + +static VariantIndexedSetterGetterInfo variant_indexed_setters_getters[Variant::VARIANT_MAX]; + +template <class T> +static void register_indexed_member(Variant::Type p_type) { + VariantIndexedSetterGetterInfo &sgi = variant_indexed_setters_getters[p_type]; + + sgi.setter = T::set; + sgi.validated_setter = T::validated_set; + sgi.ptr_setter = T::ptr_set; + + sgi.getter = T::get; + sgi.validated_getter = T::get; + sgi.ptr_getter = T::ptr_get; + + sgi.index_type = T::get_index_type(); + sgi.get_indexed_size = T::get_indexed_size; + + sgi.valid = true; +} + +void register_indexed_setters_getters() { +#define REGISTER_INDEXED_MEMBER(m_base_type) register_indexed_member<VariantIndexedSetGet_##m_base_type>(GetTypeInfo<m_base_type>::VARIANT_TYPE) + + REGISTER_INDEXED_MEMBER(Vector2); + REGISTER_INDEXED_MEMBER(Vector2i); + REGISTER_INDEXED_MEMBER(Vector3); + REGISTER_INDEXED_MEMBER(Vector3i); + REGISTER_INDEXED_MEMBER(Quat); + REGISTER_INDEXED_MEMBER(Color); + REGISTER_INDEXED_MEMBER(Transform2D); + REGISTER_INDEXED_MEMBER(Basis); + + REGISTER_INDEXED_MEMBER(PackedByteArray); + REGISTER_INDEXED_MEMBER(PackedInt32Array); + REGISTER_INDEXED_MEMBER(PackedInt64Array); + REGISTER_INDEXED_MEMBER(PackedFloat64Array); + REGISTER_INDEXED_MEMBER(PackedVector2Array); + REGISTER_INDEXED_MEMBER(PackedVector3Array); + REGISTER_INDEXED_MEMBER(PackedStringArray); + REGISTER_INDEXED_MEMBER(PackedColorArray); + + REGISTER_INDEXED_MEMBER(Array); + REGISTER_INDEXED_MEMBER(Dictionary); +} + +static void unregister_indexed_setters_getters() { +} + +bool Variant::has_indexing(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); + return variant_indexed_setters_getters[p_type].valid; +} + +Variant::Type Variant::get_indexed_element_type(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::VARIANT_MAX); + return variant_indexed_setters_getters[p_type].index_type; +} + +Variant::ValidatedIndexedSetter Variant::get_member_validated_indexed_setter(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + return variant_indexed_setters_getters[p_type].validated_setter; +} +Variant::ValidatedIndexedGetter Variant::get_member_validated_indexed_getter(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + return variant_indexed_setters_getters[p_type].validated_getter; +} + +Variant::PTRIndexedSetter Variant::get_member_ptr_indexed_setter(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + return variant_indexed_setters_getters[p_type].ptr_setter; +} +Variant::PTRIndexedGetter Variant::get_member_ptr_indexed_getter(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + return variant_indexed_setters_getters[p_type].ptr_getter; +} + +void Variant::set_indexed(int64_t p_index, const Variant &p_value, bool &r_valid, bool &r_oob) { + if (likely(variant_indexed_setters_getters[type].valid)) { + variant_indexed_setters_getters[type].setter(this, p_index, &p_value, &r_valid, &r_oob); + } else { + r_valid = false; + r_oob = false; + } +} +Variant Variant::get_indexed(int64_t p_index, bool &r_valid, bool &r_oob) const { + if (likely(variant_indexed_setters_getters[type].valid)) { + Variant ret; + variant_indexed_setters_getters[type].getter(this, p_index, &ret, &r_oob); + r_valid = !r_oob; + return ret; + } else { + r_valid = false; + r_oob = false; + return Variant(); + } +} + +uint64_t Variant::get_indexed_size() const { + if (likely(variant_indexed_setters_getters[type].valid && variant_indexed_setters_getters[type].get_indexed_size)) { + return variant_indexed_setters_getters[type].get_indexed_size(this); + } else { + return 0; + } +} + +struct VariantKeyedSetGetDictionary { + static void get(const Variant *base, const Variant *key, Variant *value, bool *r_valid) { + const Variant *ptr = VariantGetInternalPtr<Dictionary>::get_ptr(base)->getptr(*key); + if (!ptr) { + *r_valid = false; + return; + } + *value = *ptr; + *r_valid = true; + } + static void ptr_get(const void *base, const void *key, void *value) { + /* avoid ptrconvert for performance*/ + const Dictionary &v = *reinterpret_cast<const Dictionary *>(base); + const Variant *ptr = v.getptr(PtrToArg<Variant>::convert(key)); + NULL_TEST(ptr); + PtrToArg<Variant>::encode(*ptr, value); + } + static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) { + (*VariantGetInternalPtr<Dictionary>::get_ptr(base))[*key] = *value; + *r_valid = true; + } + static void ptr_set(void *base, const void *key, const void *value) { + Dictionary &v = *reinterpret_cast<Dictionary *>(base); + v[PtrToArg<Variant>::convert(key)] = PtrToArg<Variant>::convert(value); + } + + static bool has(const Variant *base, const Variant *key, bool *r_valid) { + *r_valid = true; + return VariantGetInternalPtr<Dictionary>::get_ptr(base)->has(*key); + } + static bool ptr_has(const void *base, const void *key) { + /* avoid ptrconvert for performance*/ + const Dictionary &v = *reinterpret_cast<const Dictionary *>(base); + return v.has(PtrToArg<Variant>::convert(key)); + } +}; + +struct VariantKeyedSetGetObject { + static void get(const Variant *base, const Variant *key, Variant *value, bool *r_valid) { + Object *obj = base->get_validated_object(); + + if (!obj) { + *r_valid = false; + *value = Variant(); + return; + } + *value = obj->getvar(*key, r_valid); + } + static void ptr_get(const void *base, const void *key, void *value) { + const Object *obj = PtrToArg<Object *>::convert(base); + NULL_TEST(obj); + Variant v = obj->getvar(PtrToArg<Variant>::convert(key)); + PtrToArg<Variant>::encode(v, value); + } + static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) { + Object *obj = base->get_validated_object(); + + if (!obj) { + *r_valid = false; + return; + } + obj->setvar(*key, *value, r_valid); + } + static void ptr_set(void *base, const void *key, const void *value) { + Object *obj = PtrToArg<Object *>::convert(base); + NULL_TEST(obj); + obj->setvar(PtrToArg<Variant>::convert(key), PtrToArg<Variant>::convert(value)); + } + + static bool has(const Variant *base, const Variant *key, bool *r_valid) { + Object *obj = base->get_validated_object(); + if (!obj) { + *r_valid = false; + return false; + } + *r_valid = true; + bool exists; + obj->getvar(*key, &exists); + return exists; + } + static bool ptr_has(const void *base, const void *key) { + const Object *obj = PtrToArg<Object *>::convert(base); + ERR_FAIL_COND_V(!obj, false); + bool valid; + obj->getvar(PtrToArg<Variant>::convert(key), &valid); + return valid; + } +}; + +struct VariantKeyedSetterGetterInfo { + Variant::ValidatedKeyedSetter validated_setter; + Variant::ValidatedKeyedGetter validated_getter; + Variant::ValidatedKeyedChecker validated_checker; + + Variant::PTRKeyedSetter ptr_setter; + Variant::PTRKeyedGetter ptr_getter; + Variant::PTRKeyedChecker ptr_checker; + + bool valid = false; +}; + +static VariantKeyedSetterGetterInfo variant_keyed_setters_getters[Variant::VARIANT_MAX]; + +template <class T> +static void register_keyed_member(Variant::Type p_type) { + VariantKeyedSetterGetterInfo &sgi = variant_keyed_setters_getters[p_type]; + + sgi.validated_setter = T::set; + sgi.ptr_setter = T::ptr_set; + + sgi.validated_getter = T::get; + sgi.ptr_getter = T::ptr_get; + + sgi.validated_checker = T::has; + sgi.ptr_checker = T::ptr_has; + + sgi.valid = true; +} + +static void register_keyed_setters_getters() { + register_keyed_member<VariantKeyedSetGetDictionary>(Variant::DICTIONARY); + register_keyed_member<VariantKeyedSetGetObject>(Variant::OBJECT); +} +bool Variant::is_keyed(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, false); + return variant_keyed_setters_getters[p_type].valid; +} + +Variant::ValidatedKeyedSetter Variant::get_member_validated_keyed_setter(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); + return variant_keyed_setters_getters[p_type].validated_setter; +} +Variant::ValidatedKeyedGetter Variant::get_member_validated_keyed_getter(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); + return variant_keyed_setters_getters[p_type].validated_getter; +} +Variant::ValidatedKeyedChecker Variant::get_member_validated_keyed_checker(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); + return variant_keyed_setters_getters[p_type].validated_checker; +} + +Variant::PTRKeyedSetter Variant::get_member_ptr_keyed_setter(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); + return variant_keyed_setters_getters[p_type].ptr_setter; +} +Variant::PTRKeyedGetter Variant::get_member_ptr_keyed_getter(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); + return variant_keyed_setters_getters[p_type].ptr_getter; +} +Variant::PTRKeyedChecker Variant::get_member_ptr_keyed_checker(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); + return variant_keyed_setters_getters[p_type].ptr_checker; +} + +void Variant::set_keyed(const Variant &p_key, const Variant &p_value, bool &r_valid) { + if (likely(variant_keyed_setters_getters[type].valid)) { + variant_keyed_setters_getters[type].validated_setter(this, &p_key, &p_value, &r_valid); + } else { + r_valid = false; + } +} +Variant Variant::get_keyed(const Variant &p_key, bool &r_valid) const { + if (likely(variant_keyed_setters_getters[type].valid)) { + Variant ret; + variant_keyed_setters_getters[type].validated_getter(this, &p_key, &ret, &r_valid); + return ret; + } else { + r_valid = false; + return Variant(); + } +} +bool Variant::has_key(const Variant &p_key, bool &r_valid) const { + if (likely(variant_keyed_setters_getters[type].valid)) { + return variant_keyed_setters_getters[type].validated_checker(this, &p_key, &r_valid); + } else { + r_valid = false; + return false; + } +} + +void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) { + if (type == DICTIONARY || type == OBJECT) { + bool valid; + set_keyed(p_index, p_value, valid); + if (r_valid) { + *r_valid = valid; + } + } else { + bool valid = false; + if (p_index.get_type() == STRING_NAME) { + set_named(*VariantGetInternalPtr<StringName>::get_ptr(&p_index), p_value, valid); + } else if (p_index.get_type() == INT) { + bool obb; + set_indexed(*VariantGetInternalPtr<int64_t>::get_ptr(&p_index), p_value, valid, obb); + if (obb) { + valid = false; + } + } else if (p_index.get_type() == STRING) { // less efficient version of named + set_named(*VariantGetInternalPtr<String>::get_ptr(&p_index), p_value, valid); + } else if (p_index.get_type() == FLOAT) { // less efficient version of indexed + bool obb; + set_indexed(*VariantGetInternalPtr<double>::get_ptr(&p_index), p_value, valid, obb); + if (obb) { + valid = false; + } + } + if (r_valid) { + *r_valid = valid; + } + } +} + +Variant Variant::get(const Variant &p_index, bool *r_valid) const { + Variant ret; + if (type == DICTIONARY || type == OBJECT) { + bool valid; + ret = get_keyed(p_index, valid); + if (r_valid) { + *r_valid = valid; + } + } else { + bool valid = false; + if (p_index.get_type() == STRING_NAME) { + ret = get_named(*VariantGetInternalPtr<StringName>::get_ptr(&p_index), valid); + } else if (p_index.get_type() == INT) { + bool obb; + ret = get_indexed(*VariantGetInternalPtr<int64_t>::get_ptr(&p_index), valid, obb); + if (obb) { + valid = false; + } + } else if (p_index.get_type() == STRING) { // less efficient version of named + ret = get_named(*VariantGetInternalPtr<String>::get_ptr(&p_index), valid); + } else if (p_index.get_type() == FLOAT) { // less efficient version of indexed + bool obb; + ret = get_indexed(*VariantGetInternalPtr<double>::get_ptr(&p_index), valid, obb); + if (obb) { + valid = false; + } + } + if (r_valid) { + *r_valid = valid; + } + } + + return ret; +} + +void Variant::get_property_list(List<PropertyInfo> *p_list) const { + if (type == DICTIONARY) { + const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); + List<Variant> keys; + dic->get_key_list(&keys); + for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + if (E->get().get_type() == Variant::STRING) { + p_list->push_back(PropertyInfo(Variant::STRING, E->get())); + } + } + } else if (type == OBJECT) { + Object *obj = get_validated_object(); + ERR_FAIL_COND(!obj); + obj->get_property_list(p_list); + + } else { + List<StringName> members; + get_member_list(type, &members); + for (List<StringName>::Element *E = members.front(); E; E = E->next()) { + PropertyInfo pi; + pi.name = E->get(); + pi.type = get_member_type(type, E->get()); + p_list->push_back(pi); + } + } +} + +bool Variant::iter_init(Variant &r_iter, bool &valid) const { + valid = true; + switch (type) { + case INT: { + r_iter = 0; + return _data._int > 0; + } break; + case FLOAT: { + r_iter = 0; + return _data._float > 0.0; + } break; + case VECTOR2: { + double from = reinterpret_cast<const Vector2 *>(_data._mem)->x; + double to = reinterpret_cast<const Vector2 *>(_data._mem)->y; + + r_iter = from; + + return from < to; + } break; + case VECTOR2I: { + int64_t from = reinterpret_cast<const Vector2i *>(_data._mem)->x; + int64_t to = reinterpret_cast<const Vector2i *>(_data._mem)->y; + + r_iter = from; + + return from < to; + } break; + case VECTOR3: { + double from = reinterpret_cast<const Vector3 *>(_data._mem)->x; + double to = reinterpret_cast<const Vector3 *>(_data._mem)->y; + double step = reinterpret_cast<const Vector3 *>(_data._mem)->z; + + r_iter = from; + + if (from == to) { + return false; + } else if (from < to) { + return step > 0; + } + return step < 0; + } break; + case VECTOR3I: { + int64_t from = reinterpret_cast<const Vector3i *>(_data._mem)->x; + int64_t to = reinterpret_cast<const Vector3i *>(_data._mem)->y; + int64_t step = reinterpret_cast<const Vector3i *>(_data._mem)->z; + + r_iter = from; + + if (from == to) { + return false; + } else if (from < to) { + return step > 0; + } + return step < 0; + } break; + case OBJECT: { + if (!_get_obj().obj) { + valid = false; + return false; + } + +#ifdef DEBUG_ENABLED + + if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + valid = false; + return false; + } + +#endif + Callable::CallError ce; + ce.error = Callable::CallError::CALL_OK; + Array ref; + ref.push_back(r_iter); + Variant vref = ref; + const Variant *refp[] = { &vref }; + Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_init, refp, 1, ce); + + if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { + valid = false; + return false; + } + + r_iter = ref[0]; + return ret; + } break; + + case STRING: { + const String *str = reinterpret_cast<const String *>(_data._mem); + if (str->is_empty()) { + return false; + } + r_iter = 0; + return true; + } break; + case DICTIONARY: { + const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); + if (dic->is_empty()) { + return false; + } + + const Variant *next = dic->next(nullptr); + r_iter = *next; + return true; + + } break; + case ARRAY: { + const Array *arr = reinterpret_cast<const Array *>(_data._mem); + if (arr->is_empty()) { + return false; + } + r_iter = 0; + return true; + } break; + case PACKED_BYTE_ARRAY: { + const Vector<uint8_t> *arr = &PackedArrayRef<uint8_t>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + case PACKED_INT32_ARRAY: { + const Vector<int32_t> *arr = &PackedArrayRef<int32_t>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + case PACKED_INT64_ARRAY: { + const Vector<int64_t> *arr = &PackedArrayRef<int64_t>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + case PACKED_FLOAT32_ARRAY: { + const Vector<float> *arr = &PackedArrayRef<float>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + case PACKED_FLOAT64_ARRAY: { + const Vector<double> *arr = &PackedArrayRef<double>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + case PACKED_STRING_ARRAY: { + const Vector<String> *arr = &PackedArrayRef<String>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case PACKED_VECTOR2_ARRAY: { + const Vector<Vector2> *arr = &PackedArrayRef<Vector2>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case PACKED_VECTOR3_ARRAY: { + const Vector<Vector3> *arr = &PackedArrayRef<Vector3>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case PACKED_COLOR_ARRAY: { + const Vector<Color> *arr = &PackedArrayRef<Color>::get_array(_data.packed_array); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + default: { + } + } + + valid = false; + return false; +} + +bool Variant::iter_next(Variant &r_iter, bool &valid) const { + valid = true; + switch (type) { + case INT: { + int64_t idx = r_iter; + idx++; + if (idx >= _data._int) { + return false; + } + r_iter = idx; + return true; + } break; + case FLOAT: { + int64_t idx = r_iter; + idx++; + if (idx >= _data._float) { + return false; + } + r_iter = idx; + return true; + } break; + case VECTOR2: { + double to = reinterpret_cast<const Vector2 *>(_data._mem)->y; + + double idx = r_iter; + idx++; + + if (idx >= to) { + return false; + } + + r_iter = idx; + return true; + } break; + case VECTOR2I: { + int64_t to = reinterpret_cast<const Vector2i *>(_data._mem)->y; + + int64_t idx = r_iter; + idx++; + + if (idx >= to) { + return false; + } + + r_iter = idx; + return true; + } break; + case VECTOR3: { + double to = reinterpret_cast<const Vector3 *>(_data._mem)->y; + double step = reinterpret_cast<const Vector3 *>(_data._mem)->z; + + double idx = r_iter; + idx += step; + + if (step < 0 && idx <= to) { + return false; + } + + if (step > 0 && idx >= to) { + return false; + } + + r_iter = idx; + return true; + } break; + case VECTOR3I: { + int64_t to = reinterpret_cast<const Vector3i *>(_data._mem)->y; + int64_t step = reinterpret_cast<const Vector3i *>(_data._mem)->z; + + int64_t idx = r_iter; + idx += step; + + if (step < 0 && idx <= to) { + return false; + } + + if (step > 0 && idx >= to) { + return false; + } + + r_iter = idx; + return true; + } break; + case OBJECT: { + if (!_get_obj().obj) { + valid = false; + return false; + } + +#ifdef DEBUG_ENABLED + + if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + valid = false; + return false; + } + +#endif + Callable::CallError ce; + ce.error = Callable::CallError::CALL_OK; + Array ref; + ref.push_back(r_iter); + Variant vref = ref; + const Variant *refp[] = { &vref }; + Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_next, refp, 1, ce); + + if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { + valid = false; + return false; + } + + r_iter = ref[0]; + + return ret; + } break; + + case STRING: { + const String *str = reinterpret_cast<const String *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= str->length()) { + return false; + } + r_iter = idx; + return true; + } break; + case DICTIONARY: { + const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); + const Variant *next = dic->next(&r_iter); + if (!next) { + return false; + } + + r_iter = *next; + return true; + + } break; + case ARRAY: { + const Array *arr = reinterpret_cast<const Array *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case PACKED_BYTE_ARRAY: { + const Vector<uint8_t> *arr = &PackedArrayRef<uint8_t>::get_array(_data.packed_array); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + + } break; + case PACKED_INT32_ARRAY: { + const Vector<int32_t> *arr = &PackedArrayRef<int32_t>::get_array(_data.packed_array); + int32_t idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + + } break; + case PACKED_INT64_ARRAY: { + const Vector<int64_t> *arr = &PackedArrayRef<int64_t>::get_array(_data.packed_array); + int64_t idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + + } break; + case PACKED_FLOAT32_ARRAY: { + const Vector<float> *arr = &PackedArrayRef<float>::get_array(_data.packed_array); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + + } break; + case PACKED_FLOAT64_ARRAY: { + const Vector<double> *arr = &PackedArrayRef<double>::get_array(_data.packed_array); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + + } break; + case PACKED_STRING_ARRAY: { + const Vector<String> *arr = &PackedArrayRef<String>::get_array(_data.packed_array); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case PACKED_VECTOR2_ARRAY: { + const Vector<Vector2> *arr = &PackedArrayRef<Vector2>::get_array(_data.packed_array); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case PACKED_VECTOR3_ARRAY: { + const Vector<Vector3> *arr = &PackedArrayRef<Vector3>::get_array(_data.packed_array); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case PACKED_COLOR_ARRAY: { + const Vector<Color> *arr = &PackedArrayRef<Color>::get_array(_data.packed_array); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + default: { + } + } + + valid = false; + return false; +} + +Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const { + r_valid = true; + switch (type) { + case INT: { + return r_iter; + } break; + case FLOAT: { + return r_iter; + } break; + case VECTOR2: { + return r_iter; + } break; + case VECTOR2I: { + return r_iter; + } break; + case VECTOR3: { + return r_iter; + } break; + case VECTOR3I: { + return r_iter; + } break; + case OBJECT: { + if (!_get_obj().obj) { + r_valid = false; + return Variant(); + } +#ifdef DEBUG_ENABLED + if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + r_valid = false; + return Variant(); + } + +#endif + Callable::CallError ce; + ce.error = Callable::CallError::CALL_OK; + const Variant *refp[] = { &r_iter }; + Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_get, refp, 1, ce); + + if (ce.error != Callable::CallError::CALL_OK) { + r_valid = false; + return Variant(); + } + + //r_iter=ref[0]; + + return ret; + } break; + + case STRING: { + const String *str = reinterpret_cast<const String *>(_data._mem); + return str->substr(r_iter, 1); + } break; + case DICTIONARY: { + return r_iter; //iterator is the same as the key + + } break; + case ARRAY: { + const Array *arr = reinterpret_cast<const Array *>(_data._mem); + int idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_BYTE_ARRAY: { + const Vector<uint8_t> *arr = &PackedArrayRef<uint8_t>::get_array(_data.packed_array); + int idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_INT32_ARRAY: { + const Vector<int32_t> *arr = &PackedArrayRef<int32_t>::get_array(_data.packed_array); + int32_t idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_INT64_ARRAY: { + const Vector<int64_t> *arr = &PackedArrayRef<int64_t>::get_array(_data.packed_array); + int64_t idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_FLOAT32_ARRAY: { + const Vector<float> *arr = &PackedArrayRef<float>::get_array(_data.packed_array); + int idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_FLOAT64_ARRAY: { + const Vector<double> *arr = &PackedArrayRef<double>::get_array(_data.packed_array); + int idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_STRING_ARRAY: { + const Vector<String> *arr = &PackedArrayRef<String>::get_array(_data.packed_array); + int idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_VECTOR2_ARRAY: { + const Vector<Vector2> *arr = &PackedArrayRef<Vector2>::get_array(_data.packed_array); + int idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_VECTOR3_ARRAY: { + const Vector<Vector3> *arr = &PackedArrayRef<Vector3>::get_array(_data.packed_array); + int idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + case PACKED_COLOR_ARRAY: { + const Vector<Color> *arr = &PackedArrayRef<Color>::get_array(_data.packed_array); + int idx = r_iter; +#ifdef DEBUG_ENABLED + if (idx < 0 || idx >= arr->size()) { + r_valid = false; + return Variant(); + } +#endif + return arr->get(idx); + } break; + default: { + } + } + + r_valid = false; + return Variant(); +} + +Variant Variant::duplicate(bool deep) const { + switch (type) { + case OBJECT: { + /* breaks stuff :( + if (deep && !_get_obj().ref.is_null()) { + Ref<Resource> resource = _get_obj().ref; + if (resource.is_valid()) { + return resource->duplicate(true); + } + } + */ + return *this; + } break; + case DICTIONARY: + return operator Dictionary().duplicate(deep); + case ARRAY: + return operator Array().duplicate(deep); + case PACKED_BYTE_ARRAY: + return operator Vector<uint8_t>().duplicate(); + case PACKED_INT32_ARRAY: + return operator Vector<int32_t>().duplicate(); + case PACKED_INT64_ARRAY: + return operator Vector<int64_t>().duplicate(); + case PACKED_FLOAT32_ARRAY: + return operator Vector<float>().duplicate(); + case PACKED_FLOAT64_ARRAY: + return operator Vector<double>().duplicate(); + case PACKED_STRING_ARRAY: + return operator Vector<String>().duplicate(); + case PACKED_VECTOR2_ARRAY: + return operator Vector<Vector2>().duplicate(); + case PACKED_VECTOR3_ARRAY: + return operator Vector<Vector3>().duplicate(); + case PACKED_COLOR_ARRAY: + return operator Vector<Color>().duplicate(); + default: + return *this; + } +} + +void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst) { + if (a.type != b.type) { + if (a.is_num() && b.is_num()) { + real_t va = a; + real_t vb = b; + r_dst = va + vb * c; + } else { + r_dst = a; + } + return; + } + + switch (a.type) { + case NIL: { + r_dst = Variant(); + } + return; + case INT: { + int64_t va = a._data._int; + int64_t vb = b._data._int; + r_dst = int(va + vb * c + 0.5); + } + return; + case FLOAT: { + double ra = a._data._float; + double rb = b._data._float; + r_dst = ra + rb * c; + } + return; + case VECTOR2: { + r_dst = *reinterpret_cast<const Vector2 *>(a._data._mem) + *reinterpret_cast<const Vector2 *>(b._data._mem) * c; + } + return; + case VECTOR2I: { + int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; + int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; + int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; + int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; + r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); + } + return; + case RECT2: { + const Rect2 *ra = reinterpret_cast<const Rect2 *>(a._data._mem); + const Rect2 *rb = reinterpret_cast<const Rect2 *>(b._data._mem); + r_dst = Rect2(ra->position + rb->position * c, ra->size + rb->size * c); + } + return; + case RECT2I: { + const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); + const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); + + int32_t vax = ra->position.x; + int32_t vay = ra->position.y; + int32_t vbx = ra->size.x; + int32_t vby = ra->size.y; + int32_t vcx = rb->position.x; + int32_t vcy = rb->position.y; + int32_t vdx = rb->size.x; + int32_t vdy = rb->size.y; + + r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); + } + return; + case VECTOR3: { + r_dst = *reinterpret_cast<const Vector3 *>(a._data._mem) + *reinterpret_cast<const Vector3 *>(b._data._mem) * c; + } + return; + case VECTOR3I: { + int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; + int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; + int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; + int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; + int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; + int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; + r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); + } + return; + case AABB: { + const ::AABB *ra = reinterpret_cast<const ::AABB *>(a._data._mem); + const ::AABB *rb = reinterpret_cast<const ::AABB *>(b._data._mem); + r_dst = ::AABB(ra->position + rb->position * c, ra->size + rb->size * c); + } + return; + case QUAT: { + Quat empty_rot; + const Quat *qa = reinterpret_cast<const Quat *>(a._data._mem); + const Quat *qb = reinterpret_cast<const Quat *>(b._data._mem); + r_dst = *qa * empty_rot.slerp(*qb, c); + } + return; + case COLOR: { + const Color *ca = reinterpret_cast<const Color *>(a._data._mem); + const Color *cb = reinterpret_cast<const Color *>(b._data._mem); + float new_r = ca->r + cb->r * c; + float new_g = ca->g + cb->g * c; + float new_b = ca->b + cb->b * c; + float new_a = ca->a + cb->a * c; + new_r = new_r > 1.0 ? 1.0 : new_r; + new_g = new_g > 1.0 ? 1.0 : new_g; + new_b = new_b > 1.0 ? 1.0 : new_b; + new_a = new_a > 1.0 ? 1.0 : new_a; + r_dst = Color(new_r, new_g, new_b, new_a); + } + return; + default: { + r_dst = c < 0.5 ? a : b; + } + return; + } +} + +void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst) { + if (a.type != b.type) { + if (a.is_num() && b.is_num()) { + //not as efficient but.. + real_t va = a; + real_t vb = b; + r_dst = va + (vb - va) * c; + + } else { + r_dst = a; + } + return; + } + + switch (a.type) { + case NIL: { + r_dst = Variant(); + } + return; + case BOOL: { + r_dst = a; + } + return; + case INT: { + int64_t va = a._data._int; + int64_t vb = b._data._int; + r_dst = int(va + (vb - va) * c); + } + return; + case FLOAT: { + real_t va = a._data._float; + real_t vb = b._data._float; + r_dst = va + (vb - va) * c; + } + return; + case STRING: { + //this is pretty funny and bizarre, but artists like to use it for typewritter effects + String sa = *reinterpret_cast<const String *>(a._data._mem); + String sb = *reinterpret_cast<const String *>(b._data._mem); + String dst; + int sa_len = sa.length(); + int sb_len = sb.length(); + int csize = sa_len + (sb_len - sa_len) * c; + if (csize == 0) { + r_dst = ""; + return; + } + dst.resize(csize + 1); + dst[csize] = 0; + int split = csize / 2; + + for (int i = 0; i < csize; i++) { + char32_t chr = ' '; + + if (i < split) { + if (i < sa.length()) { + chr = sa[i]; + } else if (i < sb.length()) { + chr = sb[i]; + } + + } else { + if (i < sb.length()) { + chr = sb[i]; + } else if (i < sa.length()) { + chr = sa[i]; + } + } + + dst[i] = chr; + } + + r_dst = dst; + } + return; + case VECTOR2: { + r_dst = reinterpret_cast<const Vector2 *>(a._data._mem)->lerp(*reinterpret_cast<const Vector2 *>(b._data._mem), c); + } + return; + case VECTOR2I: { + int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; + int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; + int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; + int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; + r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); + } + return; + + case RECT2: { + r_dst = Rect2(reinterpret_cast<const Rect2 *>(a._data._mem)->position.lerp(reinterpret_cast<const Rect2 *>(b._data._mem)->position, c), reinterpret_cast<const Rect2 *>(a._data._mem)->size.lerp(reinterpret_cast<const Rect2 *>(b._data._mem)->size, c)); + } + return; + case RECT2I: { + const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); + const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); + + int32_t vax = ra->position.x; + int32_t vay = ra->position.y; + int32_t vbx = ra->size.x; + int32_t vby = ra->size.y; + int32_t vcx = rb->position.x; + int32_t vcy = rb->position.y; + int32_t vdx = rb->size.x; + int32_t vdy = rb->size.y; + + r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); + } + return; + + case VECTOR3: { + r_dst = reinterpret_cast<const Vector3 *>(a._data._mem)->lerp(*reinterpret_cast<const Vector3 *>(b._data._mem), c); + } + return; + case VECTOR3I: { + int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; + int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; + int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; + int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; + int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; + int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; + r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); + } + return; + + case TRANSFORM2D: { + r_dst = a._data._transform2d->interpolate_with(*b._data._transform2d, c); + } + return; + case PLANE: { + r_dst = a; + } + return; + case QUAT: { + r_dst = reinterpret_cast<const Quat *>(a._data._mem)->slerp(*reinterpret_cast<const Quat *>(b._data._mem), c); + } + return; + case AABB: { + r_dst = ::AABB(a._data._aabb->position.lerp(b._data._aabb->position, c), a._data._aabb->size.lerp(b._data._aabb->size, c)); + } + return; + case BASIS: { + r_dst = Transform(*a._data._basis).interpolate_with(Transform(*b._data._basis), c).basis; + } + return; + case TRANSFORM: { + r_dst = a._data._transform->interpolate_with(*b._data._transform, c); + } + return; + case COLOR: { + r_dst = reinterpret_cast<const Color *>(a._data._mem)->lerp(*reinterpret_cast<const Color *>(b._data._mem), c); + } + return; + case STRING_NAME: { + r_dst = a; + } + return; + case NODE_PATH: { + r_dst = a; + } + return; + case RID: { + r_dst = a; + } + return; + case OBJECT: { + r_dst = a; + } + return; + case DICTIONARY: { + } + return; + case ARRAY: { + r_dst = a; + } + return; + case PACKED_BYTE_ARRAY: { + r_dst = a; + } + return; + case PACKED_INT32_ARRAY: { + const Vector<int32_t> *arr_a = &PackedArrayRef<int32_t>::get_array(a._data.packed_array); + const Vector<int32_t> *arr_b = &PackedArrayRef<int32_t>::get_array(b._data.packed_array); + int32_t sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + Vector<int32_t> v; + v.resize(sz); + { + int32_t *vw = v.ptrw(); + const int32_t *ar = arr_a->ptr(); + const int32_t *br = arr_b->ptr(); + + Variant va; + for (int32_t i = 0; i < sz; i++) { + Variant::interpolate(ar[i], br[i], c, va); + vw[i] = va; + } + } + r_dst = v; + } + } + return; + case PACKED_INT64_ARRAY: { + const Vector<int64_t> *arr_a = &PackedArrayRef<int64_t>::get_array(a._data.packed_array); + const Vector<int64_t> *arr_b = &PackedArrayRef<int64_t>::get_array(b._data.packed_array); + int64_t sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + Vector<int64_t> v; + v.resize(sz); + { + int64_t *vw = v.ptrw(); + const int64_t *ar = arr_a->ptr(); + const int64_t *br = arr_b->ptr(); + + Variant va; + for (int64_t i = 0; i < sz; i++) { + Variant::interpolate(ar[i], br[i], c, va); + vw[i] = va; + } + } + r_dst = v; + } + } + return; + case PACKED_FLOAT32_ARRAY: { + const Vector<float> *arr_a = &PackedArrayRef<float>::get_array(a._data.packed_array); + const Vector<float> *arr_b = &PackedArrayRef<float>::get_array(b._data.packed_array); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + Vector<float> v; + v.resize(sz); + { + float *vw = v.ptrw(); + const float *ar = arr_a->ptr(); + const float *br = arr_b->ptr(); + + Variant va; + for (int i = 0; i < sz; i++) { + Variant::interpolate(ar[i], br[i], c, va); + vw[i] = va; + } + } + r_dst = v; + } + } + return; + case PACKED_FLOAT64_ARRAY: { + const Vector<double> *arr_a = &PackedArrayRef<double>::get_array(a._data.packed_array); + const Vector<double> *arr_b = &PackedArrayRef<double>::get_array(b._data.packed_array); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + Vector<double> v; + v.resize(sz); + { + double *vw = v.ptrw(); + const double *ar = arr_a->ptr(); + const double *br = arr_b->ptr(); + + Variant va; + for (int i = 0; i < sz; i++) { + Variant::interpolate(ar[i], br[i], c, va); + vw[i] = va; + } + } + r_dst = v; + } + } + return; + case PACKED_STRING_ARRAY: { + r_dst = a; + } + return; + case PACKED_VECTOR2_ARRAY: { + const Vector<Vector2> *arr_a = &PackedArrayRef<Vector2>::get_array(a._data.packed_array); + const Vector<Vector2> *arr_b = &PackedArrayRef<Vector2>::get_array(b._data.packed_array); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + Vector<Vector2> v; + v.resize(sz); + { + Vector2 *vw = v.ptrw(); + const Vector2 *ar = arr_a->ptr(); + const Vector2 *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + r_dst = v; + } + } + return; + case PACKED_VECTOR3_ARRAY: { + const Vector<Vector3> *arr_a = &PackedArrayRef<Vector3>::get_array(a._data.packed_array); + const Vector<Vector3> *arr_b = &PackedArrayRef<Vector3>::get_array(b._data.packed_array); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + Vector<Vector3> v; + v.resize(sz); + { + Vector3 *vw = v.ptrw(); + const Vector3 *ar = arr_a->ptr(); + const Vector3 *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + r_dst = v; + } + } + return; + case PACKED_COLOR_ARRAY: { + const Vector<Color> *arr_a = &PackedArrayRef<Color>::get_array(a._data.packed_array); + const Vector<Color> *arr_b = &PackedArrayRef<Color>::get_array(b._data.packed_array); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + Vector<Color> v; + v.resize(sz); + { + Color *vw = v.ptrw(); + const Color *ar = arr_a->ptr(); + const Color *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + r_dst = v; + } + } + return; + default: { + r_dst = a; + } + } +} + +void Variant::_register_variant_setters_getters() { + register_named_setters_getters(); + register_indexed_setters_getters(); + register_keyed_setters_getters(); +} +void Variant::_unregister_variant_setters_getters() { + unregister_named_setters_getters(); + unregister_indexed_setters_getters(); +} diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp new file mode 100644 index 0000000000..f154ab1ed6 --- /dev/null +++ b/core/variant/variant_utility.cpp @@ -0,0 +1,1385 @@ +/*************************************************************************/ +/* variant_utility.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "variant.h" + +#include "core/core_string_names.h" +#include "core/io/marshalls.h" +#include "core/object/reference.h" +#include "core/os/os.h" +#include "core/templates/oa_hash_map.h" +#include "core/variant/binder_common.h" +#include "core/variant/variant_parser.h" + +struct VariantUtilityFunctions { + // Math + + static inline double sin(double arg) { + return Math::sin(arg); + } + + static inline double cos(double arg) { + return Math::cos(arg); + } + + static inline double tan(double arg) { + return Math::tan(arg); + } + + static inline double sinh(double arg) { + return Math::sinh(arg); + } + + static inline double cosh(double arg) { + return Math::cosh(arg); + } + + static inline double tanh(double arg) { + return Math::tanh(arg); + } + + static inline double asin(double arg) { + return Math::asin(arg); + } + + static inline double acos(double arg) { + return Math::acos(arg); + } + + static inline double atan(double arg) { + return Math::atan(arg); + } + + static inline double atan2(double y, double x) { + return Math::atan2(y, x); + } + + static inline double sqrt(double x) { + return Math::sqrt(x); + } + + static inline double fmod(double b, double r) { + return Math::fmod(b, r); + } + + static inline double fposmod(double b, double r) { + return Math::fposmod(b, r); + } + + static inline double floor(double x) { + return Math::floor(x); + } + + static inline double ceil(double x) { + return Math::ceil(x); + } + + static inline double round(double x) { + return Math::round(x); + } + + static inline Variant abs(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return ABS(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return Math::absd(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).abs(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).abs(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).abs(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).abs(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + } + } + + static inline double absf(double x) { + return Math::absd(x); + } + + static inline int64_t absi(int64_t x) { + return ABS(x); + } + + static inline Variant sign(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return SGN(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return SGN(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).sign(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).sign(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).sign(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).sign(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + } + } + + static inline double signf(double x) { + return SGN(x); + } + + static inline int64_t signi(int64_t x) { + return SGN(x); + } + + static inline double pow(double x, double y) { + return Math::pow(x, y); + } + + static inline double log(double x) { + return Math::log(x); + } + + static inline double exp(double x) { + return Math::exp(x); + } + + static inline bool is_nan(double x) { + return Math::is_nan(x); + } + + static inline bool is_inf(double x) { + return Math::is_inf(x); + } + + static inline bool is_equal_approx(double x, double y) { + return Math::is_equal_approx(x, y); + } + + static inline bool is_zero_approx(double x) { + return Math::is_zero_approx(x); + } + + static inline double ease(float x, float curve) { + return Math::ease(x, curve); + } + + static inline int step_decimals(float step) { + return Math::step_decimals(step); + } + + static inline int range_step_decimals(float step) { + return Math::range_step_decimals(step); + } + + static inline double snapped(double value, double step) { + return Math::snapped(value, step); + } + + static inline double lerp(double from, double to, double weight) { + return Math::lerp(from, to, weight); + } + + static inline double lerp_angle(double from, double to, double weight) { + return Math::lerp_angle(from, to, weight); + } + + static inline double inverse_lerp(double from, double to, double weight) { + return Math::inverse_lerp(from, to, weight); + } + + static inline double range_lerp(double value, double istart, double istop, double ostart, double ostop) { + return Math::range_lerp(value, istart, istop, ostart, ostop); + } + + static inline double smoothstep(double from, double to, double val) { + return Math::smoothstep(from, to, val); + } + + static inline double move_toward(double from, double to, double delta) { + return Math::move_toward(from, to, delta); + } + + static inline double dectime(double value, double amount, double step) { + return Math::dectime(value, amount, step); + } + + static inline double deg2rad(double angle_deg) { + return Math::deg2rad(angle_deg); + } + + static inline double rad2deg(double angle_rad) { + return Math::rad2deg(angle_rad); + } + + static inline double linear2db(double linear) { + return Math::linear2db(linear); + } + + static inline double db2linear(double db) { + return Math::db2linear(db); + } + + static inline Vector2 polar2cartesian(double r, double th) { + return Vector2(r * Math::cos(th), r * Math::sin(th)); + } + + static inline Vector2 cartesian2polar(double x, double y) { + return Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); + } + + static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { + return Math::wrapi(value, min, max); + } + + static inline double wrapf(double value, double min, double max) { + return Math::wrapf(value, min, max); + } + + static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); + } + Variant base = *p_args[0]; + Variant ret; + for (int i = 1; i < p_argcount; i++) { + bool valid; + Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = base.get_type(); + r_error.argument = i; + return Variant(); + } + if (ret.booleanize()) { + base = *p_args[i]; + } + } + r_error.error = Callable::CallError::CALL_OK; + return base; + } + + static inline double maxf(double x, double y) { + return MAX(x, y); + } + + static inline int64_t maxi(int64_t x, int64_t y) { + return MAX(x, y); + } + + static inline Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); + } + Variant base = *p_args[0]; + Variant ret; + for (int i = 1; i < p_argcount; i++) { + bool valid; + Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = base.get_type(); + r_error.argument = i; + return Variant(); + } + if (ret.booleanize()) { + base = *p_args[i]; + } + } + r_error.error = Callable::CallError::CALL_OK; + return base; + } + + static inline double minf(double x, double y) { + return MIN(x, y); + } + + static inline int64_t mini(int64_t x, int64_t y) { + return MIN(x, y); + } + + static inline Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { + Variant value = x; + + Variant ret; + + bool valid; + Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 1; + return Variant(); + } + if (ret.booleanize()) { + value = min; + } + Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 2; + return Variant(); + } + if (ret.booleanize()) { + value = max; + } + + r_error.error = Callable::CallError::CALL_OK; + + return value; + } + + static inline double clampf(double x, double min, double max) { + return CLAMP(x, min, max); + } + + static inline int64_t clampi(int64_t x, int64_t min, int64_t max) { + return CLAMP(x, min, max); + } + + static inline int64_t nearest_po2(int64_t x) { + return nearest_power_of_2_templated(uint64_t(x)); + } + + // Random + + static inline void randomize() { + Math::randomize(); + } + + static inline int64_t randi() { + return Math::rand(); + } + + static inline double randf() { + return Math::randf(); + } + + static inline int64_t randi_range(int64_t from, int64_t to) { + return Math::random((int32_t)from, (int32_t)to); + } + + static inline double randf_range(double from, double to) { + return Math::random(from, to); + } + + static inline void seed(int64_t s) { + return Math::seed(s); + } + + static inline PackedInt64Array rand_from_seed(int64_t seed) { + uint64_t s = seed; + PackedInt64Array arr; + arr.resize(2); + arr.write[0] = Math::rand_from_seed(&s); + arr.write[1] = s; + return arr; + } + + // Utility + + static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) { + if (obj.get_type() == Variant::OBJECT) { + r_error.error = Callable::CallError::CALL_OK; + if (obj.is_ref()) { + Ref<WeakRef> wref = memnew(WeakRef); + REF r = obj; + if (r.is_valid()) { + wref->set_ref(r); + } + return wref; + } else { + Ref<WeakRef> wref = memnew(WeakRef); + Object *o = obj.get_validated_object(); + if (o) { + wref->set_obj(o); + } + return wref; + } + } else if (obj.get_type() == Variant::NIL) { + r_error.error = Callable::CallError::CALL_OK; + Ref<WeakRef> wref = memnew(WeakRef); + return wref; + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return Variant(); + } + } + + static inline int64_t _typeof(const Variant &obj) { + return obj.get_type(); + } + + static inline String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + return String(); + } + String str; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } + + r_error.error = Callable::CallError::CALL_OK; + + return str; + } + + static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + } + String str; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } + + print_line(str); + r_error.error = Callable::CallError::CALL_OK; + } + + static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + } + String str; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } + + print_error(str); + r_error.error = Callable::CallError::CALL_OK; + } + + static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + } + String str; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + str += "\t"; + } + str += p_args[i]->operator String(); + } + + print_line(str); + r_error.error = Callable::CallError::CALL_OK; + } + + static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + } + String str; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + str += " "; + } + str += p_args[i]->operator String(); + } + + print_line(str); + r_error.error = Callable::CallError::CALL_OK; + } + + static inline void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + } + String str; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } + + OS::get_singleton()->print("%s", str.utf8().get_data()); + r_error.error = Callable::CallError::CALL_OK; + } + + static inline void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + } + String str; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } + + ERR_PRINT(str); + r_error.error = Callable::CallError::CALL_OK; + } + + static inline void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + } + String str; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } + + WARN_PRINT(str); + r_error.error = Callable::CallError::CALL_OK; + } + + static inline String var2str(const Variant &p_var) { + String vars; + VariantWriter::write_to_string(p_var, vars); + return vars; + } + + static inline Variant str2var(const String &p_var) { + VariantParser::StreamString ss; + ss.s = p_var; + + String errs; + int line; + Variant ret; + (void)VariantParser::parse(&ss, ret, errs, line); + + return ret; + } + + static inline PackedByteArray var2bytes(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, false); + if (err != OK) { + return PackedByteArray(); + } + + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, false); + if (err != OK) { + return PackedByteArray(); + } + } + + return barr; + } + + static inline PackedByteArray var2bytes_with_objects(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, true); + if (err != OK) { + return PackedByteArray(); + } + + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, true); + if (err != OK) { + return PackedByteArray(); + } + } + + return barr; + } + + static inline Variant bytes2var(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); + if (err != OK) { + return Variant(); + } + } + return ret; + } + + static inline Variant bytes2var_with_objects(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); + if (err != OK) { + return Variant(); + } + } + return ret; + } + + static inline int64_t hash(const Variant &p_arr) { + return p_arr.hash(); + } + + static inline Object *instance_from_id(int64_t p_id) { + ObjectID id = ObjectID((uint64_t)p_id); + Object *ret = ObjectDB::get_instance(id); + return ret; + } + + static inline bool is_instance_id_valid(int64_t p_id) { + return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; + } + + static inline bool is_instance_valid(const Variant &p_instance) { + if (p_instance.get_type() != Variant::OBJECT) { + return false; + } + return p_instance.get_validated_object() != nullptr; + } +}; + +#ifdef DEBUG_METHODS_ENABLED +#define VCALLR *ret = p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...) +#define VCALL p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...) +#else +#define VCALLR *ret = p_func(VariantCaster<P>::cast(*p_args[Is])...) +#define VCALL p_func(VariantCaster<P>::cast(*p_args[Is])...) +#endif + +template <class R, class... P, size_t... Is> +static _FORCE_INLINE_ void call_helperpr(R (*p_func)(P...), Variant *ret, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { + r_error.error = Callable::CallError::CALL_OK; + VCALLR; + (void)p_args; // avoid gcc warning + (void)r_error; +} + +template <class R, class... P, size_t... Is> +static _FORCE_INLINE_ void validated_call_helperpr(R (*p_func)(P...), Variant *ret, const Variant **p_args, IndexSequence<Is...>) { + *ret = p_func(VariantCaster<P>::cast(*p_args[Is])...); + (void)p_args; +} + +template <class R, class... P, size_t... Is> +static _FORCE_INLINE_ void ptr_call_helperpr(R (*p_func)(P...), void *ret, const void **p_args, IndexSequence<Is...>) { + PtrToArg<R>::encode(p_func(PtrToArg<P>::convert(p_args[Is])...), ret); + (void)p_args; +} + +template <class R, class... P> +static _FORCE_INLINE_ void call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args, Callable::CallError &r_error) { + call_helperpr(p_func, ret, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class R, class... P> +static _FORCE_INLINE_ void validated_call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args) { + validated_call_helperpr(p_func, ret, p_args, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class R, class... P> +static _FORCE_INLINE_ void ptr_call_helperr(R (*p_func)(P...), void *ret, const void **p_args) { + ptr_call_helperpr(p_func, ret, p_args, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class R, class... P> +static _FORCE_INLINE_ int get_arg_count_helperr(R (*p_func)(P...)) { + return sizeof...(P); +} + +template <class R, class... P> +static _FORCE_INLINE_ Variant::Type get_arg_type_helperr(R (*p_func)(P...), int p_arg) { + return call_get_argument_type<P...>(p_arg); +} + +template <class R, class... P> +static _FORCE_INLINE_ Variant::Type get_ret_type_helperr(R (*p_func)(P...)) { + return GetTypeInfo<R>::VARIANT_TYPE; +} + +// WITHOUT RET + +template <class... P, size_t... Is> +static _FORCE_INLINE_ void call_helperp(void (*p_func)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { + r_error.error = Callable::CallError::CALL_OK; + VCALL; + (void)p_args; + (void)r_error; +} + +template <class... P, size_t... Is> +static _FORCE_INLINE_ void validated_call_helperp(void (*p_func)(P...), const Variant **p_args, IndexSequence<Is...>) { + p_func(VariantCaster<P>::cast(*p_args[Is])...); + (void)p_args; +} + +template <class... P, size_t... Is> +static _FORCE_INLINE_ void ptr_call_helperp(void (*p_func)(P...), const void **p_args, IndexSequence<Is...>) { + p_func(PtrToArg<P>::convert(p_args[Is])...); + (void)p_args; +} + +template <class... P> +static _FORCE_INLINE_ void call_helper(void (*p_func)(P...), const Variant **p_args, Callable::CallError &r_error) { + call_helperp(p_func, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class... P> +static _FORCE_INLINE_ void validated_call_helper(void (*p_func)(P...), const Variant **p_args) { + validated_call_helperp(p_func, p_args, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class... P> +static _FORCE_INLINE_ void ptr_call_helper(void (*p_func)(P...), const void **p_args) { + ptr_call_helperp(p_func, p_args, BuildIndexSequence<sizeof...(P)>{}); +} + +template <class... P> +static _FORCE_INLINE_ int get_arg_count_helper(void (*p_func)(P...)) { + return sizeof...(P); +} + +template <class... P> +static _FORCE_INLINE_ Variant::Type get_arg_type_helper(void (*p_func)(P...), int p_arg) { + return call_get_argument_type<P...>(p_arg); +} + +template <class... P> +static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) { + return Variant::NIL; +} + +#define FUNCBINDR(m_func, m_args, m_category) \ + class Func_##m_func { \ + public: \ + static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ + call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args, r_error); \ + } \ + static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ + validated_call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args); \ + } \ + static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ + ptr_call_helperr(VariantUtilityFunctions::m_func, ret, p_args); \ + } \ + static int get_argument_count() { \ + return get_arg_count_helperr(VariantUtilityFunctions::m_func); \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return get_arg_type_helperr(VariantUtilityFunctions::m_func, p_arg); \ + } \ + static Variant::Type get_return_type() { \ + return get_ret_type_helperr(VariantUtilityFunctions::m_func); \ + } \ + static bool has_return_type() { \ + return true; \ + } \ + static bool is_vararg() { return false; } \ + static Variant::UtilityFunctionType get_type() { return m_category; } \ + }; \ + register_utility_function<Func_##m_func>(#m_func, m_args) + +#define FUNCBINDVR(m_func, m_args, m_category) \ + class Func_##m_func { \ + public: \ + static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ + r_error.error = Callable::CallError::CALL_OK; \ + *r_ret = VariantUtilityFunctions::m_func(*p_args[0], r_error); \ + } \ + static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ + Callable::CallError ce; \ + *r_ret = VariantUtilityFunctions::m_func(*p_args[0], ce); \ + } \ + static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ + Callable::CallError ce; \ + PtrToArg<Variant>::encode(VariantUtilityFunctions::m_func(PtrToArg<Variant>::convert(p_args[0]), ce), ret); \ + } \ + static int get_argument_count() { \ + return 1; \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return Variant::NIL; \ + } \ + static Variant::Type get_return_type() { \ + return Variant::NIL; \ + } \ + static bool has_return_type() { \ + return true; \ + } \ + static bool is_vararg() { return false; } \ + static Variant::UtilityFunctionType get_type() { return m_category; } \ + }; \ + register_utility_function<Func_##m_func>(#m_func, m_args) + +#define FUNCBINDVR3(m_func, m_args, m_category) \ + class Func_##m_func { \ + public: \ + static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ + r_error.error = Callable::CallError::CALL_OK; \ + *r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], *p_args[2], r_error); \ + } \ + static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ + Callable::CallError ce; \ + *r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], *p_args[2], ce); \ + } \ + static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ + Callable::CallError ce; \ + Variant r; \ + r = VariantUtilityFunctions::m_func(PtrToArg<Variant>::convert(p_args[0]), PtrToArg<Variant>::convert(p_args[1]), PtrToArg<Variant>::convert(p_args[2]), ce); \ + PtrToArg<Variant>::encode(r, ret); \ + } \ + static int get_argument_count() { \ + return 3; \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return Variant::NIL; \ + } \ + static Variant::Type get_return_type() { \ + return Variant::NIL; \ + } \ + static bool has_return_type() { \ + return true; \ + } \ + static bool is_vararg() { return false; } \ + static Variant::UtilityFunctionType get_type() { return m_category; } \ + }; \ + register_utility_function<Func_##m_func>(#m_func, m_args) + +#define FUNCBINDVARARG(m_func, m_args, m_category) \ + class Func_##m_func { \ + public: \ + static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ + r_error.error = Callable::CallError::CALL_OK; \ + *r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \ + } \ + static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ + Callable::CallError c; \ + *r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, c); \ + } \ + static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ + Vector<Variant> args; \ + for (int i = 0; i < p_argcount; i++) { \ + args.push_back(PtrToArg<Variant>::convert(p_args[i])); \ + } \ + Vector<const Variant *> argsp; \ + for (int i = 0; i < p_argcount; i++) { \ + argsp.push_back(&args[i]); \ + } \ + Variant r; \ + validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \ + PtrToArg<Variant>::encode(r, ret); \ + } \ + static int get_argument_count() { \ + return 2; \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return Variant::NIL; \ + } \ + static Variant::Type get_return_type() { \ + return Variant::NIL; \ + } \ + static bool has_return_type() { \ + return true; \ + } \ + static bool is_vararg() { \ + return true; \ + } \ + static Variant::UtilityFunctionType get_type() { \ + return m_category; \ + } \ + }; \ + register_utility_function<Func_##m_func>(#m_func, m_args) + +#define FUNCBINDVARARGS(m_func, m_args, m_category) \ + class Func_##m_func { \ + public: \ + static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ + r_error.error = Callable::CallError::CALL_OK; \ + *r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \ + } \ + static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ + Callable::CallError c; \ + *r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, c); \ + } \ + static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ + Vector<Variant> args; \ + for (int i = 0; i < p_argcount; i++) { \ + args.push_back(PtrToArg<Variant>::convert(p_args[i])); \ + } \ + Vector<const Variant *> argsp; \ + for (int i = 0; i < p_argcount; i++) { \ + argsp.push_back(&args[i]); \ + } \ + Variant r; \ + validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \ + PtrToArg<String>::encode(r.operator String(), ret); \ + } \ + static int get_argument_count() { \ + return 1; \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return Variant::NIL; \ + } \ + static Variant::Type get_return_type() { \ + return Variant::STRING; \ + } \ + static bool has_return_type() { \ + return true; \ + } \ + static bool is_vararg() { \ + return true; \ + } \ + static Variant::UtilityFunctionType get_type() { \ + return m_category; \ + } \ + }; \ + register_utility_function<Func_##m_func>(#m_func, m_args) + +#define FUNCBINDVARARGV(m_func, m_args, m_category) \ + class Func_##m_func { \ + public: \ + static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ + r_error.error = Callable::CallError::CALL_OK; \ + VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \ + } \ + static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ + Callable::CallError c; \ + VariantUtilityFunctions::m_func(p_args, p_argcount, c); \ + } \ + static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ + Vector<Variant> args; \ + for (int i = 0; i < p_argcount; i++) { \ + args.push_back(PtrToArg<Variant>::convert(p_args[i])); \ + } \ + Vector<const Variant *> argsp; \ + for (int i = 0; i < p_argcount; i++) { \ + argsp.push_back(&args[i]); \ + } \ + Variant r; \ + validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \ + } \ + static int get_argument_count() { \ + return 1; \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return Variant::NIL; \ + } \ + static Variant::Type get_return_type() { \ + return Variant::NIL; \ + } \ + static bool has_return_type() { \ + return false; \ + } \ + static bool is_vararg() { \ + return true; \ + } \ + static Variant::UtilityFunctionType get_type() { \ + return m_category; \ + } \ + }; \ + register_utility_function<Func_##m_func>(#m_func, m_args) + +#define FUNCBIND(m_func, m_args, m_category) \ + class Func_##m_func { \ + public: \ + static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ + call_helper(VariantUtilityFunctions::m_func, p_args, r_error); \ + } \ + static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ + validated_call_helper(VariantUtilityFunctions::m_func, p_args); \ + } \ + static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ + ptr_call_helper(VariantUtilityFunctions::m_func, p_args); \ + } \ + static int get_argument_count() { \ + return get_arg_count_helper(VariantUtilityFunctions::m_func); \ + } \ + static Variant::Type get_argument_type(int p_arg) { \ + return get_arg_type_helper(VariantUtilityFunctions::m_func, p_arg); \ + } \ + static Variant::Type get_return_type() { \ + return get_ret_type_helper(VariantUtilityFunctions::m_func); \ + } \ + static bool has_return_type() { \ + return false; \ + } \ + static bool is_vararg() { return false; } \ + static Variant::UtilityFunctionType get_type() { return m_category; } \ + }; \ + register_utility_function<Func_##m_func>(#m_func, m_args) + +struct VariantUtilityFunctionInfo { + void (*call_utility)(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + Variant::ValidatedUtilityFunction validated_call_utility; + Variant::PTRUtilityFunction ptr_call_utility; + Vector<String> argnames; + bool is_vararg; + bool returns_value; + int argcount; + Variant::Type (*get_arg_type)(int); + Variant::Type return_type; + Variant::UtilityFunctionType type; +}; + +static OAHashMap<StringName, VariantUtilityFunctionInfo> utility_function_table; +static List<StringName> utility_function_name_table; + +template <class T> +static void register_utility_function(const String &p_name, const Vector<String> &argnames) { + String name = p_name; + if (name.begins_with("_")) { + name = name.substr(1, name.length() - 1); + } + StringName sname = name; + ERR_FAIL_COND(utility_function_table.has(sname)); + + VariantUtilityFunctionInfo bfi; + bfi.call_utility = T::call; + bfi.validated_call_utility = T::validated_call; + bfi.ptr_call_utility = T::ptrcall; + bfi.is_vararg = T::is_vararg(); + bfi.argnames = argnames; + bfi.argcount = T::get_argument_count(); + if (!bfi.is_vararg) { + ERR_FAIL_COND_MSG(argnames.size() != bfi.argcount, "wrong number of arguments binding utility function: " + name); + } + bfi.get_arg_type = T::get_argument_type; + bfi.return_type = T::get_return_type(); + bfi.type = T::get_type(); + bfi.returns_value = T::has_return_type(); + + utility_function_table.insert(sname, bfi); + utility_function_name_table.push_back(sname); +} + +void Variant::_register_variant_utility_functions() { + // Math + + FUNCBINDR(sin, sarray("angle_rad"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(cos, sarray("angle_rad"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(tan, sarray("angle_rad"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(sinh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(cosh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(tanh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(asin, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(acos, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(atan, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(atan2, sarray("y", "x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(sqrt, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(fmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(fposmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(floor, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(ceil, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(round, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDVR(abs, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(absf, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(absi, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDVR(sign, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(signf, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(signi, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(pow, sarray("base", "exp"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(log, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(exp, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(is_nan, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(is_inf, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(is_equal_approx, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(is_zero_approx, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(ease, sarray("x", "curve"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(step_decimals, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(range_step_decimals, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(snapped, sarray("x", "step"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(lerp_angle, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(inverse_lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(range_lerp, sarray("value", "istart", "istop", "ostart", "ostop"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(smoothstep, sarray("from", "to", "x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(move_toward, sarray("from", "to", "delta"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(dectime, sarray("value", "amount", "step"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(deg2rad, sarray("deg"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(rad2deg, sarray("rad"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(linear2db, sarray("lin"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(db2linear, sarray("db"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(polar2cartesian, sarray("r", "th"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(cartesian2polar, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(wrapi, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(wrapf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDVARARG(max, sarray(), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(maxi, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(maxf, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDVARARG(min, sarray(), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(mini, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(minf, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDVR3(clamp, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(clampi, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(clampf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); + + FUNCBINDR(nearest_po2, sarray("value"), Variant::UTILITY_FUNC_TYPE_MATH); + + // Random + + FUNCBIND(randomize, sarray(), Variant::UTILITY_FUNC_TYPE_RANDOM); + FUNCBINDR(randi, sarray(), Variant::UTILITY_FUNC_TYPE_RANDOM); + FUNCBINDR(randf, sarray(), Variant::UTILITY_FUNC_TYPE_RANDOM); + FUNCBINDR(randi_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM); + FUNCBINDR(randf_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM); + FUNCBIND(seed, sarray("base"), Variant::UTILITY_FUNC_TYPE_RANDOM); + FUNCBINDR(rand_from_seed, sarray("seed"), Variant::UTILITY_FUNC_TYPE_RANDOM); + + // Utility + + FUNCBINDVR(weakref, sarray("obj"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(_typeof, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(printerr, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(printt, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(prints, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(printraw, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(push_error, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(push_warning, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + + FUNCBINDR(var2str, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(str2var, sarray("string"), Variant::UTILITY_FUNC_TYPE_GENERAL); + + FUNCBINDR(var2bytes, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(bytes2var, sarray("bytes"), Variant::UTILITY_FUNC_TYPE_GENERAL); + + FUNCBINDR(var2bytes_with_objects, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(bytes2var_with_objects, sarray("bytes"), Variant::UTILITY_FUNC_TYPE_GENERAL); + + FUNCBINDR(hash, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); + + FUNCBINDR(instance_from_id, sarray("instance_id"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(is_instance_id_valid, sarray("id"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(is_instance_valid, sarray("instance"), Variant::UTILITY_FUNC_TYPE_GENERAL); +} + +void Variant::_unregister_variant_utility_functions() { + utility_function_table.clear(); + utility_function_name_table.clear(); +} + +void Variant::call_utility_function(const StringName &p_name, Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + r_error.argument = 0; + r_error.expected = 0; + return; + } + + if (unlikely(!bfi->is_vararg && p_argcount < bfi->argcount)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 0; + r_error.expected = bfi->argcount; + return; + } + + if (unlikely(!bfi->is_vararg && p_argcount > bfi->argcount)) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 0; + r_error.expected = bfi->argcount; + return; + } + + bfi->call_utility(r_ret, p_args, p_argcount, r_error); +} + +bool Variant::has_utility_function(const StringName &p_name) { + return utility_function_table.has(p_name); +} + +Variant::ValidatedUtilityFunction Variant::get_validated_utility_function(const StringName &p_name) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return nullptr; + } + + return bfi->validated_call_utility; +} + +Variant::PTRUtilityFunction Variant::get_ptr_utility_function(const StringName &p_name) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return nullptr; + } + + return bfi->ptr_call_utility; +} + +Variant::UtilityFunctionType Variant::get_utility_function_type(const StringName &p_name) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return Variant::UTILITY_FUNC_TYPE_MATH; + } + + return bfi->type; +} + +int Variant::get_utility_function_argument_count(const StringName &p_name) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return 0; + } + + return bfi->argcount; +} + +Variant::Type Variant::get_utility_function_argument_type(const StringName &p_name, int p_arg) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return Variant::NIL; + } + + return bfi->get_arg_type(p_arg); +} + +String Variant::get_utility_function_argument_name(const StringName &p_name, int p_arg) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return String(); + } + ERR_FAIL_COND_V(bfi->is_vararg, String()); + ERR_FAIL_INDEX_V(p_arg, bfi->argnames.size(), String()); + return bfi->argnames[p_arg]; +} + +bool Variant::has_utility_function_return_value(const StringName &p_name) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return false; + } + return bfi->returns_value; +} + +Variant::Type Variant::get_utility_function_return_type(const StringName &p_name) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return Variant::NIL; + } + + return bfi->return_type; +} + +bool Variant::is_utility_function_vararg(const StringName &p_name) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (!bfi) { + return false; + } + + return bfi->is_vararg; +} + +void Variant::get_utility_function_list(List<StringName> *r_functions) { + for (List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) { + r_functions->push_back(E->get()); + } +} + +int Variant::get_utility_function_count() { + return utility_function_name_table.size(); +} diff --git a/core/variant_call.cpp b/core/variant_call.cpp deleted file mode 100644 index 9fffb42ff6..0000000000 --- a/core/variant_call.cpp +++ /dev/null @@ -1,2322 +0,0 @@ -/*************************************************************************/ -/* variant_call.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 "variant.h" - -#include "core/color_names.inc" -#include "core/core_string_names.h" -#include "core/crypto/crypto_core.h" -#include "core/debugger/engine_debugger.h" -#include "core/io/compression.h" -#include "core/object.h" -#include "core/os/os.h" - -typedef void (*VariantFunc)(Variant &r_ret, Variant &p_self, const Variant **p_args); -typedef void (*VariantConstructFunc)(Variant &r_ret, const Variant **p_args); - -struct _VariantCall { - - static void Vector3_dot(Variant &r_ret, Variant &p_self, const Variant **p_args) { - - r_ret = reinterpret_cast<Vector3 *>(p_self._data._mem)->dot(*reinterpret_cast<const Vector3 *>(p_args[0]->_data._mem)); - } - - struct FuncData { - - int arg_count; - Vector<Variant> default_args; - Vector<Variant::Type> arg_types; - Vector<StringName> arg_names; - Variant::Type return_type; - - bool _const; - bool returns; - - VariantFunc func; - - _FORCE_INLINE_ bool verify_arguments(const Variant **p_args, Callable::CallError &r_error) { - - if (arg_count == 0) - return true; - - const Variant::Type *tptr = &arg_types[0]; - - for (int i = 0; i < arg_count; i++) { - - if (tptr[i] == Variant::NIL || tptr[i] == p_args[i]->type) - continue; // all good - if (!Variant::can_convert(p_args[i]->type, tptr[i])) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = i; - r_error.expected = tptr[i]; - return false; - } - } - return true; - } - - _FORCE_INLINE_ void call(Variant &r_ret, Variant &p_self, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { -#ifdef DEBUG_ENABLED - if (p_argcount > arg_count) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = arg_count; - return; - } else -#endif - if (p_argcount < arg_count) { - int def_argcount = default_args.size(); -#ifdef DEBUG_ENABLED - if (p_argcount < (arg_count - def_argcount)) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = arg_count - def_argcount; - return; - } - -#endif - ERR_FAIL_COND(p_argcount > VARIANT_ARG_MAX); - const Variant *newargs[VARIANT_ARG_MAX]; - for (int i = 0; i < p_argcount; i++) - newargs[i] = p_args[i]; - // fill in any remaining parameters with defaults - int first_default_arg = arg_count - def_argcount; - for (int i = p_argcount; i < arg_count; i++) - newargs[i] = &default_args[i - first_default_arg]; -#ifdef DEBUG_ENABLED - if (!verify_arguments(newargs, r_error)) - return; -#endif - func(r_ret, p_self, newargs); - } else { -#ifdef DEBUG_ENABLED - if (!verify_arguments(p_args, r_error)) - return; -#endif - func(r_ret, p_self, p_args); - } - } - }; - - struct TypeFunc { - - Map<StringName, FuncData> functions; - }; - - static TypeFunc *type_funcs; - - struct Arg { - StringName name; - Variant::Type type; - Arg() { type = Variant::NIL; } - Arg(Variant::Type p_type, const StringName &p_name) : - name(p_name), - type(p_type) { - } - }; - - //void addfunc(Variant::Type p_type, const StringName& p_name,VariantFunc p_func); - - static void make_func_return_variant(Variant::Type p_type, const StringName &p_name) { - -#ifdef DEBUG_ENABLED - type_funcs[p_type].functions[p_name].returns = true; -#endif - } - - static void addfunc(bool p_const, Variant::Type p_type, Variant::Type p_return, bool p_has_return, const StringName &p_name, VariantFunc p_func, const Vector<Variant> &p_defaultarg, const Arg &p_argtype1 = Arg(), const Arg &p_argtype2 = Arg(), const Arg &p_argtype3 = Arg(), const Arg &p_argtype4 = Arg(), const Arg &p_argtype5 = Arg()) { - - FuncData funcdata; - funcdata.func = p_func; - funcdata.default_args = p_defaultarg; - funcdata._const = p_const; - funcdata.returns = p_has_return; - funcdata.return_type = p_return; - - if (p_argtype1.name) { - funcdata.arg_types.push_back(p_argtype1.type); -#ifdef DEBUG_ENABLED - funcdata.arg_names.push_back(p_argtype1.name); -#endif - - } else - goto end; - - if (p_argtype2.name) { - funcdata.arg_types.push_back(p_argtype2.type); -#ifdef DEBUG_ENABLED - funcdata.arg_names.push_back(p_argtype2.name); -#endif - - } else - goto end; - - if (p_argtype3.name) { - funcdata.arg_types.push_back(p_argtype3.type); -#ifdef DEBUG_ENABLED - funcdata.arg_names.push_back(p_argtype3.name); -#endif - - } else - goto end; - - if (p_argtype4.name) { - funcdata.arg_types.push_back(p_argtype4.type); -#ifdef DEBUG_ENABLED - funcdata.arg_names.push_back(p_argtype4.name); -#endif - } else - goto end; - - if (p_argtype5.name) { - funcdata.arg_types.push_back(p_argtype5.type); -#ifdef DEBUG_ENABLED - funcdata.arg_names.push_back(p_argtype5.name); -#endif - } else - goto end; - - end: - - funcdata.arg_count = funcdata.arg_types.size(); - type_funcs[p_type].functions[p_name] = funcdata; - } - -#define VCALL_LOCALMEM0(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._mem)->m_method(); } -#define VCALL_LOCALMEM0R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._mem)->m_method(); } -#define VCALL_LOCALMEM1(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0]); } -#define VCALL_LOCALMEM1R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0]); } -#define VCALL_LOCALMEM2(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0], *p_args[1]); } -#define VCALL_LOCALMEM2R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0], *p_args[1]); } -#define VCALL_LOCALMEM3(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0], *p_args[1], *p_args[2]); } -#define VCALL_LOCALMEM3R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0], *p_args[1], *p_args[2]); } -#define VCALL_LOCALMEM4(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); } -#define VCALL_LOCALMEM4R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); } -#define VCALL_LOCALMEM5(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3], *p_args[4]); } -#define VCALL_LOCALMEM5R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._mem)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3], *p_args[4]); } - - // built-in functions of localmem based types - - VCALL_LOCALMEM1R(String, casecmp_to); - VCALL_LOCALMEM1R(String, nocasecmp_to); - VCALL_LOCALMEM0R(String, length); - VCALL_LOCALMEM3R(String, count); - VCALL_LOCALMEM3R(String, countn); - VCALL_LOCALMEM2R(String, substr); - VCALL_LOCALMEM2R(String, find); - VCALL_LOCALMEM1R(String, find_last); - VCALL_LOCALMEM2R(String, findn); - VCALL_LOCALMEM2R(String, rfind); - VCALL_LOCALMEM2R(String, rfindn); - VCALL_LOCALMEM1R(String, match); - VCALL_LOCALMEM1R(String, matchn); - VCALL_LOCALMEM1R(String, begins_with); - VCALL_LOCALMEM1R(String, ends_with); - VCALL_LOCALMEM1R(String, is_subsequence_of); - VCALL_LOCALMEM1R(String, is_subsequence_ofi); - VCALL_LOCALMEM0R(String, bigrams); - VCALL_LOCALMEM1R(String, similarity); - VCALL_LOCALMEM2R(String, format); - VCALL_LOCALMEM2R(String, replace); - VCALL_LOCALMEM2R(String, replacen); - VCALL_LOCALMEM1R(String, repeat); - VCALL_LOCALMEM2R(String, insert); - VCALL_LOCALMEM0R(String, capitalize); - VCALL_LOCALMEM3R(String, split); - VCALL_LOCALMEM3R(String, rsplit); - VCALL_LOCALMEM2R(String, split_floats); - VCALL_LOCALMEM0R(String, to_upper); - VCALL_LOCALMEM0R(String, to_lower); - VCALL_LOCALMEM1R(String, left); - VCALL_LOCALMEM1R(String, right); - VCALL_LOCALMEM0R(String, dedent); - VCALL_LOCALMEM2R(String, strip_edges); - VCALL_LOCALMEM0R(String, strip_escapes); - VCALL_LOCALMEM1R(String, lstrip); - VCALL_LOCALMEM1R(String, rstrip); - VCALL_LOCALMEM0R(String, get_extension); - VCALL_LOCALMEM0R(String, get_basename); - VCALL_LOCALMEM1R(String, plus_file); - VCALL_LOCALMEM1R(String, ord_at); - VCALL_LOCALMEM2(String, erase); - VCALL_LOCALMEM0R(String, hash); - VCALL_LOCALMEM0R(String, md5_text); - VCALL_LOCALMEM0R(String, sha1_text); - VCALL_LOCALMEM0R(String, sha256_text); - VCALL_LOCALMEM0R(String, md5_buffer); - VCALL_LOCALMEM0R(String, sha1_buffer); - VCALL_LOCALMEM0R(String, sha256_buffer); - VCALL_LOCALMEM0R(String, empty); - VCALL_LOCALMEM1R(String, humanize_size); - VCALL_LOCALMEM0R(String, is_abs_path); - VCALL_LOCALMEM0R(String, is_rel_path); - VCALL_LOCALMEM0R(String, get_base_dir); - VCALL_LOCALMEM0R(String, get_file); - VCALL_LOCALMEM0R(String, xml_escape); - VCALL_LOCALMEM0R(String, xml_unescape); - VCALL_LOCALMEM0R(String, http_escape); - VCALL_LOCALMEM0R(String, http_unescape); - VCALL_LOCALMEM0R(String, c_escape); - VCALL_LOCALMEM0R(String, c_unescape); - VCALL_LOCALMEM0R(String, json_escape); - VCALL_LOCALMEM0R(String, percent_encode); - VCALL_LOCALMEM0R(String, percent_decode); - VCALL_LOCALMEM0R(String, is_valid_identifier); - VCALL_LOCALMEM0R(String, is_valid_integer); - VCALL_LOCALMEM0R(String, is_valid_float); - VCALL_LOCALMEM1R(String, is_valid_hex_number); - VCALL_LOCALMEM0R(String, is_valid_html_color); - VCALL_LOCALMEM0R(String, is_valid_ip_address); - VCALL_LOCALMEM0R(String, is_valid_filename); - VCALL_LOCALMEM0R(String, to_int); - VCALL_LOCALMEM0R(String, to_float); - VCALL_LOCALMEM0R(String, hex_to_int); - VCALL_LOCALMEM1R(String, pad_decimals); - VCALL_LOCALMEM1R(String, pad_zeros); - VCALL_LOCALMEM1R(String, trim_prefix); - VCALL_LOCALMEM1R(String, trim_suffix); - - static void _call_String_to_ascii(Variant &r_ret, Variant &p_self, const Variant **p_args) { - - String *s = reinterpret_cast<String *>(p_self._data._mem); - if (s->empty()) { - r_ret = PackedByteArray(); - return; - } - CharString charstr = s->ascii(); - - PackedByteArray retval; - size_t len = charstr.length(); - retval.resize(len); - uint8_t *w = retval.ptrw(); - copymem(w, charstr.ptr(), len); - - r_ret = retval; - } - - static void _call_String_to_utf8(Variant &r_ret, Variant &p_self, const Variant **p_args) { - - String *s = reinterpret_cast<String *>(p_self._data._mem); - if (s->empty()) { - r_ret = PackedByteArray(); - return; - } - CharString charstr = s->utf8(); - - PackedByteArray retval; - size_t len = charstr.length(); - retval.resize(len); - uint8_t *w = retval.ptrw(); - copymem(w, charstr.ptr(), len); - - r_ret = retval; - } - - VCALL_LOCALMEM1R(Vector2, distance_to); - VCALL_LOCALMEM1R(Vector2, distance_squared_to); - VCALL_LOCALMEM0R(Vector2, length); - VCALL_LOCALMEM0R(Vector2, length_squared); - VCALL_LOCALMEM0R(Vector2, normalized); - VCALL_LOCALMEM0R(Vector2, is_normalized); - VCALL_LOCALMEM1R(Vector2, is_equal_approx); - VCALL_LOCALMEM1R(Vector2, posmod); - VCALL_LOCALMEM1R(Vector2, posmodv); - VCALL_LOCALMEM1R(Vector2, project); - VCALL_LOCALMEM1R(Vector2, angle_to); - VCALL_LOCALMEM1R(Vector2, angle_to_point); - VCALL_LOCALMEM1R(Vector2, direction_to); - VCALL_LOCALMEM2R(Vector2, lerp); - VCALL_LOCALMEM2R(Vector2, slerp); - VCALL_LOCALMEM4R(Vector2, cubic_interpolate); - VCALL_LOCALMEM2R(Vector2, move_toward); - VCALL_LOCALMEM1R(Vector2, rotated); - VCALL_LOCALMEM0R(Vector2, tangent); - VCALL_LOCALMEM0R(Vector2, floor); - VCALL_LOCALMEM0R(Vector2, ceil); - VCALL_LOCALMEM0R(Vector2, round); - VCALL_LOCALMEM1R(Vector2, snapped); - VCALL_LOCALMEM0R(Vector2, aspect); - VCALL_LOCALMEM1R(Vector2, dot); - VCALL_LOCALMEM1R(Vector2, slide); - VCALL_LOCALMEM1R(Vector2, bounce); - VCALL_LOCALMEM1R(Vector2, reflect); - VCALL_LOCALMEM0R(Vector2, angle); - VCALL_LOCALMEM1R(Vector2, cross); - VCALL_LOCALMEM0R(Vector2, abs); - VCALL_LOCALMEM1R(Vector2, clamped); - VCALL_LOCALMEM0R(Vector2, sign); - - VCALL_LOCALMEM0R(Vector2i, aspect); - VCALL_LOCALMEM0R(Vector2i, sign); - VCALL_LOCALMEM0R(Vector2i, abs); - - VCALL_LOCALMEM0R(Rect2, get_area); - VCALL_LOCALMEM0R(Rect2, has_no_area); - VCALL_LOCALMEM1R(Rect2, has_point); - VCALL_LOCALMEM1R(Rect2, is_equal_approx); - VCALL_LOCALMEM2R(Rect2, intersects); - VCALL_LOCALMEM1R(Rect2, encloses); - VCALL_LOCALMEM1R(Rect2, clip); - VCALL_LOCALMEM1R(Rect2, merge); - VCALL_LOCALMEM1R(Rect2, expand); - VCALL_LOCALMEM1R(Rect2, grow); - VCALL_LOCALMEM2R(Rect2, grow_margin); - VCALL_LOCALMEM4R(Rect2, grow_individual); - VCALL_LOCALMEM0R(Rect2, abs); - - VCALL_LOCALMEM0R(Rect2i, get_area); - VCALL_LOCALMEM0R(Rect2i, has_no_area); - VCALL_LOCALMEM1R(Rect2i, has_point); - VCALL_LOCALMEM1R(Rect2i, intersects); - VCALL_LOCALMEM1R(Rect2i, encloses); - VCALL_LOCALMEM1R(Rect2i, clip); - VCALL_LOCALMEM1R(Rect2i, merge); - VCALL_LOCALMEM1R(Rect2i, expand); - VCALL_LOCALMEM1R(Rect2i, grow); - VCALL_LOCALMEM2R(Rect2i, grow_margin); - VCALL_LOCALMEM4R(Rect2i, grow_individual); - VCALL_LOCALMEM0R(Rect2i, abs); - - VCALL_LOCALMEM0R(Vector3, min_axis); - VCALL_LOCALMEM0R(Vector3, max_axis); - VCALL_LOCALMEM1R(Vector3, distance_to); - VCALL_LOCALMEM1R(Vector3, distance_squared_to); - VCALL_LOCALMEM0R(Vector3, length); - VCALL_LOCALMEM0R(Vector3, length_squared); - VCALL_LOCALMEM0R(Vector3, normalized); - VCALL_LOCALMEM0R(Vector3, is_normalized); - VCALL_LOCALMEM1R(Vector3, is_equal_approx); - VCALL_LOCALMEM0R(Vector3, inverse); - VCALL_LOCALMEM1R(Vector3, snapped); - VCALL_LOCALMEM2R(Vector3, rotated); - VCALL_LOCALMEM2R(Vector3, lerp); - VCALL_LOCALMEM2R(Vector3, slerp); - VCALL_LOCALMEM4R(Vector3, cubic_interpolate); - VCALL_LOCALMEM2R(Vector3, move_toward); - VCALL_LOCALMEM1R(Vector3, dot); - VCALL_LOCALMEM1R(Vector3, cross); - VCALL_LOCALMEM1R(Vector3, outer); - VCALL_LOCALMEM0R(Vector3, to_diagonal_matrix); - VCALL_LOCALMEM0R(Vector3, abs); - VCALL_LOCALMEM0R(Vector3, floor); - VCALL_LOCALMEM0R(Vector3, ceil); - VCALL_LOCALMEM0R(Vector3, round); - VCALL_LOCALMEM1R(Vector3, posmod); - VCALL_LOCALMEM1R(Vector3, posmodv); - VCALL_LOCALMEM1R(Vector3, project); - VCALL_LOCALMEM1R(Vector3, angle_to); - VCALL_LOCALMEM1R(Vector3, direction_to); - VCALL_LOCALMEM1R(Vector3, slide); - VCALL_LOCALMEM1R(Vector3, bounce); - VCALL_LOCALMEM1R(Vector3, reflect); - VCALL_LOCALMEM0R(Vector3, sign); - - VCALL_LOCALMEM0R(Vector3i, min_axis); - VCALL_LOCALMEM0R(Vector3i, max_axis); - VCALL_LOCALMEM0R(Vector3i, sign); - - VCALL_LOCALMEM0R(Plane, normalized); - VCALL_LOCALMEM0R(Plane, center); - VCALL_LOCALMEM0R(Plane, get_any_point); - VCALL_LOCALMEM1R(Plane, is_equal_approx); - VCALL_LOCALMEM1R(Plane, is_point_over); - VCALL_LOCALMEM1R(Plane, distance_to); - VCALL_LOCALMEM2R(Plane, has_point); - VCALL_LOCALMEM1R(Plane, project); - - //return vector3 if intersected, nil if not - static void _call_Plane_intersect_3(Variant &r_ret, Variant &p_self, const Variant **p_args) { - Vector3 result; - if (reinterpret_cast<Plane *>(p_self._data._mem)->intersect_3(*p_args[0], *p_args[1], &result)) - r_ret = result; - else - r_ret = Variant(); - } - - static void _call_Plane_intersects_ray(Variant &r_ret, Variant &p_self, const Variant **p_args) { - Vector3 result; - if (reinterpret_cast<Plane *>(p_self._data._mem)->intersects_ray(*p_args[0], *p_args[1], &result)) - r_ret = result; - else - r_ret = Variant(); - } - - static void _call_Plane_intersects_segment(Variant &r_ret, Variant &p_self, const Variant **p_args) { - Vector3 result; - if (reinterpret_cast<Plane *>(p_self._data._mem)->intersects_segment(*p_args[0], *p_args[1], &result)) - r_ret = result; - else - r_ret = Variant(); - } - - VCALL_LOCALMEM0R(Quat, length); - VCALL_LOCALMEM0R(Quat, length_squared); - VCALL_LOCALMEM0R(Quat, normalized); - VCALL_LOCALMEM0R(Quat, is_normalized); - VCALL_LOCALMEM1R(Quat, is_equal_approx); - VCALL_LOCALMEM0R(Quat, inverse); - VCALL_LOCALMEM1R(Quat, dot); - VCALL_LOCALMEM1R(Quat, xform); - VCALL_LOCALMEM2R(Quat, slerp); - VCALL_LOCALMEM2R(Quat, slerpni); - VCALL_LOCALMEM4R(Quat, cubic_slerp); - VCALL_LOCALMEM0R(Quat, get_euler); - VCALL_LOCALMEM1(Quat, set_euler); - VCALL_LOCALMEM2(Quat, set_axis_angle); - - VCALL_LOCALMEM0R(Color, to_argb32); - VCALL_LOCALMEM0R(Color, to_abgr32); - VCALL_LOCALMEM0R(Color, to_rgba32); - VCALL_LOCALMEM0R(Color, to_argb64); - VCALL_LOCALMEM0R(Color, to_abgr64); - VCALL_LOCALMEM0R(Color, to_rgba64); - VCALL_LOCALMEM0R(Color, inverted); - VCALL_LOCALMEM0R(Color, contrasted); - VCALL_LOCALMEM2R(Color, lerp); - VCALL_LOCALMEM1R(Color, blend); - VCALL_LOCALMEM1R(Color, lightened); - VCALL_LOCALMEM1R(Color, darkened); - VCALL_LOCALMEM1R(Color, to_html); - VCALL_LOCALMEM4R(Color, from_hsv); - VCALL_LOCALMEM1R(Color, is_equal_approx); - - VCALL_LOCALMEM0R(RID, get_id); - - VCALL_LOCALMEM0R(NodePath, is_absolute); - VCALL_LOCALMEM0R(NodePath, get_name_count); - VCALL_LOCALMEM1R(NodePath, get_name); - VCALL_LOCALMEM0R(NodePath, get_subname_count); - VCALL_LOCALMEM1R(NodePath, get_subname); - VCALL_LOCALMEM0R(NodePath, get_concatenated_subnames); - VCALL_LOCALMEM0R(NodePath, get_as_property_path); - VCALL_LOCALMEM0R(NodePath, is_empty); - - VCALL_LOCALMEM0R(Dictionary, size); - VCALL_LOCALMEM0R(Dictionary, empty); - VCALL_LOCALMEM0(Dictionary, clear); - VCALL_LOCALMEM1R(Dictionary, has); - VCALL_LOCALMEM1R(Dictionary, has_all); - VCALL_LOCALMEM1R(Dictionary, erase); - VCALL_LOCALMEM0R(Dictionary, hash); - VCALL_LOCALMEM0R(Dictionary, keys); - VCALL_LOCALMEM0R(Dictionary, values); - VCALL_LOCALMEM1R(Dictionary, duplicate); - VCALL_LOCALMEM2R(Dictionary, get); - - VCALL_LOCALMEM0R(Callable, is_null); - VCALL_LOCALMEM0R(Callable, is_custom); - VCALL_LOCALMEM0(Callable, is_standard); - VCALL_LOCALMEM0(Callable, get_object); - VCALL_LOCALMEM0(Callable, get_object_id); - VCALL_LOCALMEM0(Callable, get_method); - VCALL_LOCALMEM0(Callable, hash); - - VCALL_LOCALMEM0R(Signal, is_null); - VCALL_LOCALMEM0R(Signal, get_object); - VCALL_LOCALMEM0R(Signal, get_object_id); - VCALL_LOCALMEM0R(Signal, get_name); - VCALL_LOCALMEM3R(Signal, connect); - VCALL_LOCALMEM1(Signal, disconnect); - VCALL_LOCALMEM1R(Signal, is_connected); - VCALL_LOCALMEM0R(Signal, get_connections); - - VCALL_LOCALMEM2(Array, set); - VCALL_LOCALMEM1R(Array, get); - VCALL_LOCALMEM0R(Array, size); - VCALL_LOCALMEM0R(Array, empty); - VCALL_LOCALMEM0(Array, clear); - VCALL_LOCALMEM0R(Array, hash); - VCALL_LOCALMEM1(Array, push_back); - VCALL_LOCALMEM1(Array, push_front); - VCALL_LOCALMEM0R(Array, pop_back); - VCALL_LOCALMEM0R(Array, pop_front); - VCALL_LOCALMEM1(Array, append); - VCALL_LOCALMEM1(Array, resize); - VCALL_LOCALMEM2(Array, insert); - VCALL_LOCALMEM1(Array, remove); - VCALL_LOCALMEM0R(Array, front); - VCALL_LOCALMEM0R(Array, back); - VCALL_LOCALMEM2R(Array, find); - VCALL_LOCALMEM2R(Array, rfind); - VCALL_LOCALMEM1R(Array, find_last); - VCALL_LOCALMEM1R(Array, count); - VCALL_LOCALMEM1R(Array, has); - VCALL_LOCALMEM1(Array, erase); - VCALL_LOCALMEM0(Array, sort); - VCALL_LOCALMEM2(Array, sort_custom); - VCALL_LOCALMEM0(Array, shuffle); - VCALL_LOCALMEM2R(Array, bsearch); - VCALL_LOCALMEM4R(Array, bsearch_custom); - VCALL_LOCALMEM1R(Array, duplicate); - VCALL_LOCALMEM4R(Array, slice); - VCALL_LOCALMEM0(Array, invert); - VCALL_LOCALMEM0R(Array, max); - VCALL_LOCALMEM0R(Array, min); - - static void _call_PackedByteArray_get_string_from_ascii(Variant &r_ret, Variant &p_self, const Variant **p_args) { - - PackedByteArray *ba = reinterpret_cast<PackedByteArray *>(p_self._data._mem); - String s; - if (ba->size() > 0) { - const uint8_t *r = ba->ptr(); - CharString cs; - cs.resize(ba->size() + 1); - copymem(cs.ptrw(), r, ba->size()); - cs[ba->size()] = 0; - - s = cs.get_data(); - } - r_ret = s; - } - - static void _call_PackedByteArray_get_string_from_utf8(Variant &r_ret, Variant &p_self, const Variant **p_args) { - - PackedByteArray *ba = reinterpret_cast<PackedByteArray *>(p_self._data._mem); - String s; - if (ba->size() > 0) { - const uint8_t *r = ba->ptr(); - s.parse_utf8((const char *)r, ba->size()); - } - r_ret = s; - } - - static void _call_PackedByteArray_compress(Variant &r_ret, Variant &p_self, const Variant **p_args) { - - PackedByteArray *ba = reinterpret_cast<PackedByteArray *>(p_self._data._mem); - PackedByteArray compressed; - if (ba->size() > 0) { - Compression::Mode mode = (Compression::Mode)(int)(*p_args[0]); - - compressed.resize(Compression::get_max_compressed_buffer_size(ba->size(), mode)); - int result = Compression::compress(compressed.ptrw(), ba->ptr(), ba->size(), mode); - - result = result >= 0 ? result : 0; - compressed.resize(result); - } - r_ret = compressed; - } - - static void _call_PackedByteArray_decompress(Variant &r_ret, Variant &p_self, const Variant **p_args) { - - PackedByteArray *ba = reinterpret_cast<PackedByteArray *>(p_self._data._mem); - PackedByteArray decompressed; - Compression::Mode mode = (Compression::Mode)(int)(*p_args[1]); - - int buffer_size = (int)(*p_args[0]); - - if (buffer_size <= 0) { - r_ret = decompressed; - ERR_FAIL_MSG("Decompression buffer size must be greater than zero."); - } - - decompressed.resize(buffer_size); - int result = Compression::decompress(decompressed.ptrw(), buffer_size, ba->ptr(), ba->size(), mode); - - result = result >= 0 ? result : 0; - decompressed.resize(result); - - r_ret = decompressed; - } - - static void _call_PackedByteArray_hex_encode(Variant &r_ret, Variant &p_self, const Variant **p_args) { - PackedByteArray *ba = reinterpret_cast<PackedByteArray *>(p_self._data._mem); - if (ba->size() == 0) { - r_ret = String(); - return; - } - const uint8_t *r = ba->ptr(); - String s = String::hex_encode_buffer(&r[0], ba->size()); - r_ret = s; - } - -#define VCALL_PARRMEM0(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(); } -#define VCALL_PARRMEM0R(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(); } -#define VCALL_PARRMEM1(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0]); } -#define VCALL_PARRMEM1R(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0]); } -#define VCALL_PARRMEM2(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0], *p_args[1]); } -#define VCALL_PARRMEM2R(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0], *p_args[1]); } -#define VCALL_PARRMEM3(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0], *p_args[1], *p_args[2]); } -#define VCALL_PARRMEM3R(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0], *p_args[1], *p_args[2]); } -#define VCALL_PARRMEM4(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); } -#define VCALL_PARRMEM4R(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); } -#define VCALL_PARRMEM5(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3], *p_args[4]); } -#define VCALL_PARRMEM5R(m_type, m_elemtype, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = Variant::PackedArrayRef<m_elemtype>::get_array_ptr(p_self._data.packed_array)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3], *p_args[4]); } - - VCALL_PARRMEM0R(PackedByteArray, uint8_t, size); - VCALL_PARRMEM0R(PackedByteArray, uint8_t, empty); - VCALL_PARRMEM2(PackedByteArray, uint8_t, set); - VCALL_PARRMEM1R(PackedByteArray, uint8_t, get); - VCALL_PARRMEM1(PackedByteArray, uint8_t, push_back); - VCALL_PARRMEM1(PackedByteArray, uint8_t, resize); - VCALL_PARRMEM2R(PackedByteArray, uint8_t, insert); - VCALL_PARRMEM1(PackedByteArray, uint8_t, remove); - VCALL_PARRMEM1(PackedByteArray, uint8_t, append); - VCALL_PARRMEM1(PackedByteArray, uint8_t, append_array); - VCALL_PARRMEM0(PackedByteArray, uint8_t, invert); - VCALL_PARRMEM2R(PackedByteArray, uint8_t, subarray); - - VCALL_PARRMEM0R(PackedInt32Array, int32_t, size); - VCALL_PARRMEM0R(PackedInt32Array, int32_t, empty); - VCALL_PARRMEM2(PackedInt32Array, int32_t, set); - VCALL_PARRMEM1R(PackedInt32Array, int32_t, get); - VCALL_PARRMEM1(PackedInt32Array, int32_t, push_back); - VCALL_PARRMEM1(PackedInt32Array, int32_t, resize); - VCALL_PARRMEM2R(PackedInt32Array, int32_t, insert); - VCALL_PARRMEM1(PackedInt32Array, int32_t, remove); - VCALL_PARRMEM1(PackedInt32Array, int32_t, append); - VCALL_PARRMEM1(PackedInt32Array, int32_t, append_array); - VCALL_PARRMEM0(PackedInt32Array, int32_t, invert); - - VCALL_PARRMEM0R(PackedInt64Array, int64_t, size); - VCALL_PARRMEM0R(PackedInt64Array, int64_t, empty); - VCALL_PARRMEM2(PackedInt64Array, int64_t, set); - VCALL_PARRMEM1R(PackedInt64Array, int64_t, get); - VCALL_PARRMEM1(PackedInt64Array, int64_t, push_back); - VCALL_PARRMEM1(PackedInt64Array, int64_t, resize); - VCALL_PARRMEM2R(PackedInt64Array, int64_t, insert); - VCALL_PARRMEM1(PackedInt64Array, int64_t, remove); - VCALL_PARRMEM1(PackedInt64Array, int64_t, append); - VCALL_PARRMEM1(PackedInt64Array, int64_t, append_array); - VCALL_PARRMEM0(PackedInt64Array, int64_t, invert); - - VCALL_PARRMEM0R(PackedFloat32Array, float, size); - VCALL_PARRMEM0R(PackedFloat32Array, float, empty); - VCALL_PARRMEM2(PackedFloat32Array, float, set); - VCALL_PARRMEM1R(PackedFloat32Array, float, get); - VCALL_PARRMEM1(PackedFloat32Array, float, push_back); - VCALL_PARRMEM1(PackedFloat32Array, float, resize); - VCALL_PARRMEM2R(PackedFloat32Array, float, insert); - VCALL_PARRMEM1(PackedFloat32Array, float, remove); - VCALL_PARRMEM1(PackedFloat32Array, float, append); - VCALL_PARRMEM1(PackedFloat32Array, float, append_array); - VCALL_PARRMEM0(PackedFloat32Array, float, invert); - - VCALL_PARRMEM0R(PackedFloat64Array, double, size); - VCALL_PARRMEM0R(PackedFloat64Array, double, empty); - VCALL_PARRMEM2(PackedFloat64Array, double, set); - VCALL_PARRMEM1R(PackedFloat64Array, double, get); - VCALL_PARRMEM1(PackedFloat64Array, double, push_back); - VCALL_PARRMEM1(PackedFloat64Array, double, resize); - VCALL_PARRMEM2R(PackedFloat64Array, double, insert); - VCALL_PARRMEM1(PackedFloat64Array, double, remove); - VCALL_PARRMEM1(PackedFloat64Array, double, append); - VCALL_PARRMEM1(PackedFloat64Array, double, append_array); - VCALL_PARRMEM0(PackedFloat64Array, double, invert); - - VCALL_PARRMEM0R(PackedStringArray, String, size); - VCALL_PARRMEM0R(PackedStringArray, String, empty); - VCALL_PARRMEM2(PackedStringArray, String, set); - VCALL_PARRMEM1R(PackedStringArray, String, get); - VCALL_PARRMEM1(PackedStringArray, String, push_back); - VCALL_PARRMEM1(PackedStringArray, String, resize); - VCALL_PARRMEM2R(PackedStringArray, String, insert); - VCALL_PARRMEM1(PackedStringArray, String, remove); - VCALL_PARRMEM1(PackedStringArray, String, append); - VCALL_PARRMEM1(PackedStringArray, String, append_array); - VCALL_PARRMEM0(PackedStringArray, String, invert); - - VCALL_PARRMEM0R(PackedVector2Array, Vector2, size); - VCALL_PARRMEM0R(PackedVector2Array, Vector2, empty); - VCALL_PARRMEM2(PackedVector2Array, Vector2, set); - VCALL_PARRMEM1R(PackedVector2Array, Vector2, get); - VCALL_PARRMEM1(PackedVector2Array, Vector2, push_back); - VCALL_PARRMEM1(PackedVector2Array, Vector2, resize); - VCALL_PARRMEM2R(PackedVector2Array, Vector2, insert); - VCALL_PARRMEM1(PackedVector2Array, Vector2, remove); - VCALL_PARRMEM1(PackedVector2Array, Vector2, append); - VCALL_PARRMEM1(PackedVector2Array, Vector2, append_array); - VCALL_PARRMEM0(PackedVector2Array, Vector2, invert); - - VCALL_PARRMEM0R(PackedVector3Array, Vector3, size); - VCALL_PARRMEM0R(PackedVector3Array, Vector3, empty); - VCALL_PARRMEM2(PackedVector3Array, Vector3, set); - VCALL_PARRMEM1R(PackedVector3Array, Vector3, get); - VCALL_PARRMEM1(PackedVector3Array, Vector3, push_back); - VCALL_PARRMEM1(PackedVector3Array, Vector3, resize); - VCALL_PARRMEM2R(PackedVector3Array, Vector3, insert); - VCALL_PARRMEM1(PackedVector3Array, Vector3, remove); - VCALL_PARRMEM1(PackedVector3Array, Vector3, append); - VCALL_PARRMEM1(PackedVector3Array, Vector3, append_array); - VCALL_PARRMEM0(PackedVector3Array, Vector3, invert); - - VCALL_PARRMEM0R(PackedColorArray, Color, size); - VCALL_PARRMEM0R(PackedColorArray, Color, empty); - VCALL_PARRMEM2(PackedColorArray, Color, set); - VCALL_PARRMEM1R(PackedColorArray, Color, get); - VCALL_PARRMEM1(PackedColorArray, Color, push_back); - VCALL_PARRMEM1(PackedColorArray, Color, resize); - VCALL_PARRMEM2R(PackedColorArray, Color, insert); - VCALL_PARRMEM1(PackedColorArray, Color, remove); - VCALL_PARRMEM1(PackedColorArray, Color, append); - VCALL_PARRMEM1(PackedColorArray, Color, append_array); - VCALL_PARRMEM0(PackedColorArray, Color, invert); - -#define VCALL_PTR0(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(); } -#define VCALL_PTR0R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(); } -#define VCALL_PTR1(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0]); } -#define VCALL_PTR1R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0]); } -#define VCALL_PTR2(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1]); } -#define VCALL_PTR2R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1]); } -#define VCALL_PTR3(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1], *p_args[2]); } -#define VCALL_PTR3R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1], *p_args[2]); } -#define VCALL_PTR4(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); } -#define VCALL_PTR4R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); } -#define VCALL_PTR5(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3], *p_args[4]); } -#define VCALL_PTR5R(m_type, m_method) \ - static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3], *p_args[4]); } - - VCALL_PTR0R(AABB, get_area); - VCALL_PTR0R(AABB, has_no_area); - VCALL_PTR0R(AABB, has_no_surface); - VCALL_PTR1R(AABB, has_point); - VCALL_PTR1R(AABB, is_equal_approx); - VCALL_PTR1R(AABB, intersects); - VCALL_PTR1R(AABB, encloses); - VCALL_PTR1R(AABB, intersects_plane); - VCALL_PTR2R(AABB, intersects_segment); - VCALL_PTR1R(AABB, intersection); - VCALL_PTR1R(AABB, merge); - VCALL_PTR1R(AABB, expand); - VCALL_PTR1R(AABB, grow); - VCALL_PTR1R(AABB, get_support); - VCALL_PTR0R(AABB, get_longest_axis); - VCALL_PTR0R(AABB, get_longest_axis_index); - VCALL_PTR0R(AABB, get_longest_axis_size); - VCALL_PTR0R(AABB, get_shortest_axis); - VCALL_PTR0R(AABB, get_shortest_axis_index); - VCALL_PTR0R(AABB, get_shortest_axis_size); - VCALL_PTR1R(AABB, get_endpoint); - - VCALL_PTR0R(Transform2D, inverse); - VCALL_PTR0R(Transform2D, affine_inverse); - VCALL_PTR0R(Transform2D, get_rotation); - VCALL_PTR0R(Transform2D, get_origin); - VCALL_PTR0R(Transform2D, get_scale); - VCALL_PTR0R(Transform2D, orthonormalized); - VCALL_PTR1R(Transform2D, rotated); - VCALL_PTR1R(Transform2D, scaled); - VCALL_PTR1R(Transform2D, translated); - VCALL_PTR2R(Transform2D, interpolate_with); - VCALL_PTR1R(Transform2D, is_equal_approx); - - static void _call_Transform2D_xform(Variant &r_ret, Variant &p_self, const Variant **p_args) { - switch (p_args[0]->type) { - case Variant::VECTOR2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform(p_args[0]->operator Vector2()); return; - case Variant::RECT2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform(p_args[0]->operator Rect2()); return; - case Variant::PACKED_VECTOR2_ARRAY: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform(p_args[0]->operator PackedVector2Array()); return; - default: - r_ret = Variant(); - ERR_PRINT("Invalid type in function 'xform' in base 'Transform2D'. Valid types are Vector2, Rect2, and PackedVector2Array."); - } - } - - static void _call_Transform2D_xform_inv(Variant &r_ret, Variant &p_self, const Variant **p_args) { - switch (p_args[0]->type) { - case Variant::VECTOR2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform_inv(p_args[0]->operator Vector2()); return; - case Variant::RECT2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform_inv(p_args[0]->operator Rect2()); return; - case Variant::PACKED_VECTOR2_ARRAY: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform_inv(p_args[0]->operator PackedVector2Array()); return; - default: - r_ret = Variant(); - ERR_PRINT("Invalid type in function 'xform_inv' in base 'Transform2D'. Valid types are Vector2, Rect2, and PackedVector2Array."); - } - } - - static void _call_Transform2D_basis_xform(Variant &r_ret, Variant &p_self, const Variant **p_args) { - switch (p_args[0]->type) { - case Variant::VECTOR2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->basis_xform(p_args[0]->operator Vector2()); return; - default: - r_ret = Variant(); - ERR_PRINT("Invalid type in function 'basis_xform' in base 'Transform2D'. Only Vector2 is valid."); - } - } - - static void _call_Transform2D_basis_xform_inv(Variant &r_ret, Variant &p_self, const Variant **p_args) { - switch (p_args[0]->type) { - case Variant::VECTOR2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->basis_xform_inv(p_args[0]->operator Vector2()); return; - default: - r_ret = Variant(); - ERR_PRINT("Invalid type in function 'basis_xform_inv' in base 'Transform2D'. Only Vector2 is valid."); - } - } - - VCALL_PTR0R(Basis, inverse); - VCALL_PTR0R(Basis, transposed); - VCALL_PTR0R(Basis, determinant); - VCALL_PTR2R(Basis, rotated); - VCALL_PTR1R(Basis, scaled); - VCALL_PTR0R(Basis, get_scale); - VCALL_PTR0R(Basis, get_euler); - VCALL_PTR1R(Basis, tdotx); - VCALL_PTR1R(Basis, tdoty); - VCALL_PTR1R(Basis, tdotz); - VCALL_PTR1R(Basis, xform); - VCALL_PTR1R(Basis, xform_inv); - VCALL_PTR0R(Basis, get_orthogonal_index); - VCALL_PTR0R(Basis, orthonormalized); - VCALL_PTR2R(Basis, slerp); - VCALL_PTR2R(Basis, is_equal_approx); // TODO: Break compatibility in 4.0 to change this to an instance method (a.is_equal_approx(b) as VCALL_PTR1R) for consistency. - VCALL_PTR0R(Basis, get_rotation_quat); - - VCALL_PTR0R(Transform, inverse); - VCALL_PTR0R(Transform, affine_inverse); - VCALL_PTR2R(Transform, rotated); - VCALL_PTR1R(Transform, scaled); - VCALL_PTR1R(Transform, translated); - VCALL_PTR0R(Transform, orthonormalized); - VCALL_PTR2R(Transform, looking_at); - VCALL_PTR2R(Transform, interpolate_with); - VCALL_PTR1R(Transform, is_equal_approx); - - static void _call_Transform_xform(Variant &r_ret, Variant &p_self, const Variant **p_args) { - switch (p_args[0]->type) { - case Variant::VECTOR3: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform(p_args[0]->operator Vector3()); return; - case Variant::PLANE: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform(p_args[0]->operator Plane()); return; - case Variant::AABB: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform(p_args[0]->operator ::AABB()); return; - case Variant::PACKED_VECTOR3_ARRAY: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform(p_args[0]->operator ::PackedVector3Array()); return; - default: - r_ret = Variant(); - ERR_PRINT("Invalid type in function 'xform' in base 'Transform'. Valid types are Vector3, Plane, AABB, and PackedVector3Array."); - } - } - - static void _call_Transform_xform_inv(Variant &r_ret, Variant &p_self, const Variant **p_args) { - switch (p_args[0]->type) { - case Variant::VECTOR3: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform_inv(p_args[0]->operator Vector3()); return; - case Variant::PLANE: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform_inv(p_args[0]->operator Plane()); return; - case Variant::AABB: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform_inv(p_args[0]->operator ::AABB()); return; - case Variant::PACKED_VECTOR3_ARRAY: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform_inv(p_args[0]->operator ::PackedVector3Array()); return; - default: - r_ret = Variant(); - ERR_PRINT("Invalid type in function 'xform_inv' in base 'Transform'. Valid types are Vector3, Plane, AABB, and PackedVector3Array."); - } - } - - struct ConstructData { - - int arg_count; - Vector<Variant::Type> arg_types; - Vector<String> arg_names; - VariantConstructFunc func; - }; - - struct ConstructFunc { - - List<ConstructData> constructors; - }; - - static ConstructFunc *construct_funcs; - - static void Vector2_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Vector2(*p_args[0], *p_args[1]); - } - - static void Vector2i_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Vector2i(*p_args[0], *p_args[1]); - } - - static void Rect2_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Rect2(*p_args[0], *p_args[1]); - } - - static void Rect2_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Rect2(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); - } - - static void Rect2i_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Rect2i(*p_args[0], *p_args[1]); - } - - static void Rect2i_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Rect2i(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); - } - - static void Transform2D_init2(Variant &r_ret, const Variant **p_args) { - - Transform2D m(*p_args[0], *p_args[1]); - r_ret = m; - } - - static void Transform2D_init3(Variant &r_ret, const Variant **p_args) { - - Transform2D m; - m[0] = *p_args[0]; - m[1] = *p_args[1]; - m[2] = *p_args[2]; - r_ret = m; - } - - static void Vector3_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Vector3(*p_args[0], *p_args[1], *p_args[2]); - } - - static void Vector3i_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Vector3i(*p_args[0], *p_args[1], *p_args[2]); - } - - static void Plane_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Plane(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); - } - - static void Plane_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Plane(*p_args[0], *p_args[1], *p_args[2]); - } - - static void Plane_init3(Variant &r_ret, const Variant **p_args) { - - r_ret = Plane(p_args[0]->operator Vector3(), p_args[1]->operator real_t()); - } - static void Plane_init4(Variant &r_ret, const Variant **p_args) { - - r_ret = Plane(p_args[0]->operator Vector3(), p_args[1]->operator Vector3()); - } - - static void Quat_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Quat(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); - } - - static void Quat_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Quat(((Vector3)(*p_args[0])), ((real_t)(*p_args[1]))); - } - - static void Quat_init3(Variant &r_ret, const Variant **p_args) { - - r_ret = Quat(((Vector3)(*p_args[0]))); - } - - static void Color_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = Color(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); - } - - static void Color_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Color(*p_args[0], *p_args[1], *p_args[2]); - } - - static void Color_init3(Variant &r_ret, const Variant **p_args) { - - r_ret = Color::html(*p_args[0]); - } - - static void Color_init4(Variant &r_ret, const Variant **p_args) { - - r_ret = Color::hex(*p_args[0]); - } - - static void Color_init5(Variant &r_ret, const Variant **p_args) { - - r_ret = Color(((Color)(*p_args[0])), *p_args[1]); - } - - static void AABB_init1(Variant &r_ret, const Variant **p_args) { - - r_ret = ::AABB(*p_args[0], *p_args[1]); - } - - static void Basis_init1(Variant &r_ret, const Variant **p_args) { - - Basis m; - m.set_axis(0, *p_args[0]); - m.set_axis(1, *p_args[1]); - m.set_axis(2, *p_args[2]); - r_ret = m; - } - - static void Basis_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Basis(p_args[0]->operator Vector3(), p_args[1]->operator real_t()); - } - - static void Transform_init1(Variant &r_ret, const Variant **p_args) { - - Transform t; - t.basis.set_axis(0, *p_args[0]); - t.basis.set_axis(1, *p_args[1]); - t.basis.set_axis(2, *p_args[2]); - t.origin = *p_args[3]; - r_ret = t; - } - - static void Transform_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Transform(p_args[0]->operator Basis(), p_args[1]->operator Vector3()); - } - - static void Callable_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Callable(p_args[0]->operator ObjectID(), p_args[1]->operator String()); - } - - static void Signal_init2(Variant &r_ret, const Variant **p_args) { - - r_ret = Signal(p_args[0]->operator ObjectID(), p_args[1]->operator String()); - } - - static void add_constructor(VariantConstructFunc p_func, const Variant::Type p_type, - const String &p_name1 = "", const Variant::Type p_type1 = Variant::NIL, - const String &p_name2 = "", const Variant::Type p_type2 = Variant::NIL, - const String &p_name3 = "", const Variant::Type p_type3 = Variant::NIL, - const String &p_name4 = "", const Variant::Type p_type4 = Variant::NIL) { - - ConstructData cd; - cd.func = p_func; - cd.arg_count = 0; - - if (p_name1 == "") - goto end; - cd.arg_count++; - cd.arg_names.push_back(p_name1); - cd.arg_types.push_back(p_type1); - - if (p_name2 == "") - goto end; - cd.arg_count++; - cd.arg_names.push_back(p_name2); - cd.arg_types.push_back(p_type2); - - if (p_name3 == "") - goto end; - cd.arg_count++; - cd.arg_names.push_back(p_name3); - cd.arg_types.push_back(p_type3); - - if (p_name4 == "") - goto end; - cd.arg_count++; - cd.arg_names.push_back(p_name4); - cd.arg_types.push_back(p_type4); - - end: - - construct_funcs[p_type].constructors.push_back(cd); - } - - struct ConstantData { - - Map<StringName, int> value; -#ifdef DEBUG_ENABLED - List<StringName> value_ordered; -#endif - Map<StringName, Variant> variant_value; - }; - - static ConstantData *constant_data; - - static void add_constant(int p_type, StringName p_constant_name, int p_constant_value) { - - constant_data[p_type].value[p_constant_name] = p_constant_value; -#ifdef DEBUG_ENABLED - constant_data[p_type].value_ordered.push_back(p_constant_name); -#endif - } - - static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) { - - constant_data[p_type].variant_value[p_constant_name] = p_constant_value; - } -}; - -_VariantCall::TypeFunc *_VariantCall::type_funcs = nullptr; -_VariantCall::ConstructFunc *_VariantCall::construct_funcs = nullptr; -_VariantCall::ConstantData *_VariantCall::constant_data = nullptr; - -Variant Variant::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - - Variant ret; - call_ptr(p_method, p_args, p_argcount, &ret, r_error); - return ret; -} - -void Variant::call_ptr(const StringName &p_method, const Variant **p_args, int p_argcount, Variant *r_ret, Callable::CallError &r_error) { - Variant ret; - - if (type == Variant::OBJECT) { - //call object - Object *obj = _get_obj().obj; - if (!obj) { - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - return; - } -#ifdef DEBUG_ENABLED - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - return; - } - -#endif - ret = _get_obj().obj->call(p_method, p_args, p_argcount, r_error); - - //else if (type==Variant::METHOD) { - - } else { - - r_error.error = Callable::CallError::CALL_OK; - - Map<StringName, _VariantCall::FuncData>::Element *E = _VariantCall::type_funcs[type].functions.find(p_method); - - if (E) { - - _VariantCall::FuncData &funcdata = E->get(); - funcdata.call(ret, *this, p_args, p_argcount, r_error); - - } else { - //handle vararg functions manually - bool valid = false; - if (type == CALLABLE) { - if (p_method == CoreStringNames::get_singleton()->call) { - - reinterpret_cast<const Callable *>(_data._mem)->call(p_args, p_argcount, ret, r_error); - valid = true; - } - if (p_method == CoreStringNames::get_singleton()->call_deferred) { - reinterpret_cast<const Callable *>(_data._mem)->call_deferred(p_args, p_argcount); - valid = true; - } - } else if (type == SIGNAL) { - if (p_method == CoreStringNames::get_singleton()->emit) { - if (r_ret) { - *r_ret = Variant(); - } - reinterpret_cast<const Signal *>(_data._mem)->emit(p_args, p_argcount); - valid = true; - } - } - if (!valid) { - //ok fail because not found - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return; - } - } - } - - if (r_error.error == Callable::CallError::CALL_OK && r_ret) - *r_ret = ret; -} - -#define VCALL(m_type, m_method) _VariantCall::_call_##m_type##_##m_method - -Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, int p_argcount, Callable::CallError &r_error, bool p_strict) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, Variant()); - - r_error.error = Callable::CallError::CALL_OK; - if (p_argcount == 0) { //generic construct - - switch (p_type) { - case NIL: - return Variant(); - - // atomic types - case BOOL: return Variant(false); - case INT: return 0; - case FLOAT: return 0.0f; - case STRING: - return String(); - - // math types - case VECTOR2: - return Vector2(); - case RECT2: return Rect2(); - case VECTOR3: return Vector3(); - case TRANSFORM2D: return Transform2D(); - case PLANE: return Plane(); - case QUAT: return Quat(); - case AABB: - return ::AABB(); - case BASIS: return Basis(); - case TRANSFORM: - return Transform(); - - // misc types - case COLOR: return Color(); - case STRING_NAME: - return StringName(); - case NODE_PATH: - return NodePath(); - case _RID: return RID(); - case OBJECT: return (Object *)nullptr; - case CALLABLE: return Callable(); - case SIGNAL: return Signal(); - case DICTIONARY: return Dictionary(); - case ARRAY: - return Array(); - case PACKED_BYTE_ARRAY: return PackedByteArray(); - case PACKED_INT32_ARRAY: return PackedInt32Array(); - case PACKED_INT64_ARRAY: return PackedInt64Array(); - case PACKED_FLOAT32_ARRAY: return PackedFloat32Array(); - case PACKED_FLOAT64_ARRAY: return PackedFloat64Array(); - case PACKED_STRING_ARRAY: return PackedStringArray(); - case PACKED_VECTOR2_ARRAY: - return PackedVector2Array(); - case PACKED_VECTOR3_ARRAY: return PackedVector3Array(); - case PACKED_COLOR_ARRAY: return PackedColorArray(); - default: return Variant(); - } - - } else if (p_argcount == 1 && p_args[0]->type == p_type) { - return *p_args[0]; //copy construct - } else if (p_argcount == 1 && (!p_strict || Variant::can_convert(p_args[0]->type, p_type))) { - //near match construct - - switch (p_type) { - case NIL: { - - return Variant(); - } break; - case BOOL: { - return Variant(bool(*p_args[0])); - } - case INT: { - return (int64_t(*p_args[0])); - } - case FLOAT: { - return real_t(*p_args[0]); - } - case STRING: { - return String(*p_args[0]); - } - case VECTOR2: { - return Vector2(*p_args[0]); - } - case VECTOR2I: { - return Vector2i(*p_args[0]); - } - case RECT2: return (Rect2(*p_args[0])); - case RECT2I: return (Rect2i(*p_args[0])); - case VECTOR3: return (Vector3(*p_args[0])); - case VECTOR3I: return (Vector3i(*p_args[0])); - case TRANSFORM2D: - return (Transform2D(p_args[0]->operator Transform2D())); - case PLANE: return (Plane(*p_args[0])); - case QUAT: return (p_args[0]->operator Quat()); - case AABB: - return (::AABB(*p_args[0])); - case BASIS: return (Basis(p_args[0]->operator Basis())); - case TRANSFORM: - return (Transform(p_args[0]->operator Transform())); - - // misc types - case COLOR: return p_args[0]->type == Variant::STRING ? Color::html(*p_args[0]) : Color::hex(*p_args[0]); - case STRING_NAME: - return (StringName(p_args[0]->operator StringName())); - case NODE_PATH: - return (NodePath(p_args[0]->operator NodePath())); - case _RID: return (RID(*p_args[0])); - case OBJECT: return ((Object *)(p_args[0]->operator Object *())); - case CALLABLE: return ((Callable)(p_args[0]->operator Callable())); - case SIGNAL: return ((Signal)(p_args[0]->operator Signal())); - case DICTIONARY: return p_args[0]->operator Dictionary(); - case ARRAY: - return p_args[0]->operator Array(); - - // arrays - case PACKED_BYTE_ARRAY: return (PackedByteArray(*p_args[0])); - case PACKED_INT32_ARRAY: return (PackedInt32Array(*p_args[0])); - case PACKED_INT64_ARRAY: return (PackedInt64Array(*p_args[0])); - case PACKED_FLOAT32_ARRAY: return (PackedFloat32Array(*p_args[0])); - case PACKED_FLOAT64_ARRAY: return (PackedFloat64Array(*p_args[0])); - case PACKED_STRING_ARRAY: return (PackedStringArray(*p_args[0])); - case PACKED_VECTOR2_ARRAY: - return (PackedVector2Array(*p_args[0])); - case PACKED_VECTOR3_ARRAY: return (PackedVector3Array(*p_args[0])); - case PACKED_COLOR_ARRAY: return (PackedColorArray(*p_args[0])); - default: return Variant(); - } - } else if (p_argcount >= 1) { - - _VariantCall::ConstructFunc &c = _VariantCall::construct_funcs[p_type]; - - for (List<_VariantCall::ConstructData>::Element *E = c.constructors.front(); E; E = E->next()) { - const _VariantCall::ConstructData &cd = E->get(); - - if (cd.arg_count != p_argcount) - continue; - - //validate parameters - for (int i = 0; i < cd.arg_count; i++) { - if (!Variant::can_convert(p_args[i]->type, cd.arg_types[i])) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; //no such constructor - r_error.argument = i; - r_error.expected = cd.arg_types[i]; - return Variant(); - } - } - - Variant v; - cd.func(v, p_args); - return v; - } - } - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; //no such constructor - return Variant(); -} - -bool Variant::has_method(const StringName &p_method) const { - - if (type == OBJECT) { - Object *obj = get_validated_object(); - if (!obj) - return false; - - return obj->has_method(p_method); - } - - const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[type]; - return tf.functions.has(p_method); -} - -Vector<Variant::Type> Variant::get_method_argument_types(Variant::Type p_type, const StringName &p_method) { - - const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type]; - - const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method); - if (!E) - return Vector<Variant::Type>(); - - return E->get().arg_types; -} - -bool Variant::is_method_const(Variant::Type p_type, const StringName &p_method) { - - const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type]; - - const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method); - if (!E) - return false; - - return E->get()._const; -} - -Vector<StringName> Variant::get_method_argument_names(Variant::Type p_type, const StringName &p_method) { - - const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type]; - - const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method); - if (!E) - return Vector<StringName>(); - - return E->get().arg_names; -} - -Variant::Type Variant::get_method_return_type(Variant::Type p_type, const StringName &p_method, bool *r_has_return) { - - const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type]; - - const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method); - if (!E) - return Variant::NIL; - - if (r_has_return) - *r_has_return = E->get().returns; - - return E->get().return_type; -} - -Vector<Variant> Variant::get_method_default_arguments(Variant::Type p_type, const StringName &p_method) { - - const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type]; - - const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method); - if (!E) - return Vector<Variant>(); - - return E->get().default_args; -} - -void Variant::get_method_list(List<MethodInfo> *p_list) const { - - const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[type]; - - for (const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.front(); E; E = E->next()) { - - const _VariantCall::FuncData &fd = E->get(); - - MethodInfo mi; - mi.name = E->key(); - - if (fd._const) { - mi.flags |= METHOD_FLAG_CONST; - } - - for (int i = 0; i < fd.arg_types.size(); i++) { - - PropertyInfo pi; - pi.type = fd.arg_types[i]; -#ifdef DEBUG_ENABLED - pi.name = fd.arg_names[i]; -#endif - mi.arguments.push_back(pi); - } - - mi.default_arguments = fd.default_args; - PropertyInfo ret; -#ifdef DEBUG_ENABLED - ret.type = fd.return_type; - if (fd.returns) { - ret.name = "ret"; - if (fd.return_type == Variant::NIL) - ret.usage = PROPERTY_USAGE_NIL_IS_VARIANT; - } - mi.return_val = ret; -#endif - - p_list->push_back(mi); - } - - if (type == CALLABLE) { - - MethodInfo mi; - mi.name = "call"; - mi.return_val.usage = PROPERTY_USAGE_NIL_IS_VARIANT; - mi.flags |= METHOD_FLAG_VARARG; - - p_list->push_back(mi); - - mi.name = "call_deferred"; - mi.return_val.usage = 0; - - p_list->push_back(mi); - } - - if (type == SIGNAL) { - - MethodInfo mi; - mi.name = "emit"; - mi.flags |= METHOD_FLAG_VARARG; - - p_list->push_back(mi); - } -} - -void Variant::get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_list) { - - ERR_FAIL_INDEX(p_type, VARIANT_MAX); - - //custom constructors - for (const List<_VariantCall::ConstructData>::Element *E = _VariantCall::construct_funcs[p_type].constructors.front(); E; E = E->next()) { - - const _VariantCall::ConstructData &cd = E->get(); - MethodInfo mi; - mi.name = Variant::get_type_name(p_type); - mi.return_val.type = p_type; - for (int i = 0; i < cd.arg_count; i++) { - - PropertyInfo pi; - pi.name = cd.arg_names[i]; - pi.type = cd.arg_types[i]; - mi.arguments.push_back(pi); - } - p_list->push_back(mi); - } - //default constructors - for (int i = 0; i < VARIANT_MAX; i++) { - if (i == p_type) - continue; - if (!Variant::can_convert(Variant::Type(i), p_type)) - continue; - - MethodInfo mi; - mi.name = Variant::get_type_name(p_type); - PropertyInfo pi; - pi.name = "from"; - pi.type = Variant::Type(i); - mi.arguments.push_back(pi); - mi.return_val.type = p_type; - p_list->push_back(mi); - } -} - -void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { - - ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); - - _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; - -#ifdef DEBUG_ENABLED - for (List<StringName>::Element *E = cd.value_ordered.front(); E; E = E->next()) { - - p_constants->push_back(E->get()); -#else - for (Map<StringName, int>::Element *E = cd.value.front(); E; E = E->next()) { - - p_constants->push_back(E->key()); -#endif - } - - for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) { - - p_constants->push_back(E->key()); - } -} - -bool Variant::has_constant(Variant::Type p_type, const StringName &p_value) { - - ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); - _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; - return cd.value.has(p_value) || cd.variant_value.has(p_value); -} - -Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { - - if (r_valid) - *r_valid = false; - - ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0); - _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; - - Map<StringName, int>::Element *E = cd.value.find(p_value); - if (!E) { - Map<StringName, Variant>::Element *F = cd.variant_value.find(p_value); - if (F) { - if (r_valid) - *r_valid = true; - return F->get(); - } else { - return -1; - } - } - if (r_valid) - *r_valid = true; - - return E->get(); -} - -void register_variant_methods() { - - _VariantCall::type_funcs = memnew_arr(_VariantCall::TypeFunc, Variant::VARIANT_MAX); - - _VariantCall::construct_funcs = memnew_arr(_VariantCall::ConstructFunc, Variant::VARIANT_MAX); - _VariantCall::constant_data = memnew_arr(_VariantCall::ConstantData, Variant::VARIANT_MAX); - -#define ADDFUNC0R(m_vtype, m_ret, m_class, m_method, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg); -#define ADDFUNC1R(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1))); -#define ADDFUNC2R(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2))); -#define ADDFUNC3R(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_arg3, m_argname3, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2)), _VariantCall::Arg(Variant::m_arg3, _scs_create(m_argname3))); -#define ADDFUNC4R(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_arg3, m_argname3, m_arg4, m_argname4, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2)), _VariantCall::Arg(Variant::m_arg3, _scs_create(m_argname3)), _VariantCall::Arg(Variant::m_arg4, _scs_create(m_argname4))); - -#define ADDFUNC0RNC(m_vtype, m_ret, m_class, m_method, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg); -#define ADDFUNC1RNC(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1))); -#define ADDFUNC2RNC(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2))); -#define ADDFUNC3RNC(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_arg3, m_argname3, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2)), _VariantCall::Arg(Variant::m_arg3, _scs_create(m_argname3))); -#define ADDFUNC4RNC(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_arg3, m_argname3, m_arg4, m_argname4, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, true, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2)), _VariantCall::Arg(Variant::m_arg3, _scs_create(m_argname3)), _VariantCall::Arg(Variant::m_arg4, _scs_create(m_argname4))); - -#define ADDFUNC0(m_vtype, m_ret, m_class, m_method, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg); -#define ADDFUNC1(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1))); -#define ADDFUNC2(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2))); -#define ADDFUNC3(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_arg3, m_argname3, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2)), _VariantCall::Arg(Variant::m_arg3, _scs_create(m_argname3))); -#define ADDFUNC4(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_arg3, m_argname3, m_arg4, m_argname4, m_defarg) \ - _VariantCall::addfunc(true, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2)), _VariantCall::Arg(Variant::m_arg3, _scs_create(m_argname3)), _VariantCall::Arg(Variant::m_arg4, _scs_create(m_argname4))); - -#define ADDFUNC0NC(m_vtype, m_ret, m_class, m_method, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg); -#define ADDFUNC1NC(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1))); -#define ADDFUNC2NC(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2))); -#define ADDFUNC3NC(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_arg3, m_argname3, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2)), _VariantCall::Arg(Variant::m_arg3, _scs_create(m_argname3))); -#define ADDFUNC4NC(m_vtype, m_ret, m_class, m_method, m_arg1, m_argname1, m_arg2, m_argname2, m_arg3, m_argname3, m_arg4, m_argname4, m_defarg) \ - _VariantCall::addfunc(false, Variant::m_vtype, Variant::m_ret, false, _scs_create(#m_method), VCALL(m_class, m_method), m_defarg, _VariantCall::Arg(Variant::m_arg1, _scs_create(m_argname1)), _VariantCall::Arg(Variant::m_arg2, _scs_create(m_argname2)), _VariantCall::Arg(Variant::m_arg3, _scs_create(m_argname3)), _VariantCall::Arg(Variant::m_arg4, _scs_create(m_argname4))); - - /* STRING */ - ADDFUNC1R(STRING, INT, String, casecmp_to, STRING, "to", varray()); - ADDFUNC1R(STRING, INT, String, nocasecmp_to, STRING, "to", varray()); - ADDFUNC0R(STRING, INT, String, length, varray()); - ADDFUNC2R(STRING, STRING, String, substr, INT, "from", INT, "len", varray(-1)); - - ADDFUNC2R(STRING, INT, String, find, STRING, "what", INT, "from", varray(0)); - - ADDFUNC3R(STRING, INT, String, count, STRING, "what", INT, "from", INT, "to", varray(0, 0)); - ADDFUNC3R(STRING, INT, String, countn, STRING, "what", INT, "from", INT, "to", varray(0, 0)); - - ADDFUNC1R(STRING, INT, String, find_last, STRING, "what", varray()); - ADDFUNC2R(STRING, INT, String, findn, STRING, "what", INT, "from", varray(0)); - ADDFUNC2R(STRING, INT, String, rfind, STRING, "what", INT, "from", varray(-1)); - ADDFUNC2R(STRING, INT, String, rfindn, STRING, "what", INT, "from", varray(-1)); - ADDFUNC1R(STRING, BOOL, String, match, STRING, "expr", varray()); - ADDFUNC1R(STRING, BOOL, String, matchn, STRING, "expr", varray()); - ADDFUNC1R(STRING, BOOL, String, begins_with, STRING, "text", varray()); - ADDFUNC1R(STRING, BOOL, String, ends_with, STRING, "text", varray()); - ADDFUNC1R(STRING, BOOL, String, is_subsequence_of, STRING, "text", varray()); - ADDFUNC1R(STRING, BOOL, String, is_subsequence_ofi, STRING, "text", varray()); - ADDFUNC0R(STRING, PACKED_STRING_ARRAY, String, bigrams, varray()); - ADDFUNC1R(STRING, FLOAT, String, similarity, STRING, "text", varray()); - - ADDFUNC2R(STRING, STRING, String, format, NIL, "values", STRING, "placeholder", varray("{_}")); - ADDFUNC2R(STRING, STRING, String, replace, STRING, "what", STRING, "forwhat", varray()); - ADDFUNC2R(STRING, STRING, String, replacen, STRING, "what", STRING, "forwhat", varray()); - ADDFUNC1R(STRING, STRING, String, repeat, INT, "count", varray()); - ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray()); - ADDFUNC0R(STRING, STRING, String, capitalize, varray()); - ADDFUNC3R(STRING, PACKED_STRING_ARRAY, String, split, STRING, "delimiter", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); - ADDFUNC3R(STRING, PACKED_STRING_ARRAY, String, rsplit, STRING, "delimiter", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); - ADDFUNC2R(STRING, PACKED_FLOAT32_ARRAY, String, split_floats, STRING, "delimiter", BOOL, "allow_empty", varray(true)); - - ADDFUNC0R(STRING, STRING, String, to_upper, varray()); - ADDFUNC0R(STRING, STRING, String, to_lower, varray()); - - ADDFUNC1R(STRING, STRING, String, left, INT, "position", varray()); - ADDFUNC1R(STRING, STRING, String, right, INT, "position", varray()); - ADDFUNC2R(STRING, STRING, String, strip_edges, BOOL, "left", BOOL, "right", varray(true, true)); - ADDFUNC0R(STRING, STRING, String, strip_escapes, varray()); - ADDFUNC1R(STRING, STRING, String, lstrip, STRING, "chars", varray()); - ADDFUNC1R(STRING, STRING, String, rstrip, STRING, "chars", varray()); - ADDFUNC0R(STRING, STRING, String, get_extension, varray()); - ADDFUNC0R(STRING, STRING, String, get_basename, varray()); - ADDFUNC1R(STRING, STRING, String, plus_file, STRING, "file", varray()); - ADDFUNC1R(STRING, INT, String, ord_at, INT, "at", varray()); - ADDFUNC0R(STRING, STRING, String, dedent, varray()); - ADDFUNC2(STRING, NIL, String, erase, INT, "position", INT, "chars", varray()); - ADDFUNC0R(STRING, INT, String, hash, varray()); - ADDFUNC0R(STRING, STRING, String, md5_text, varray()); - ADDFUNC0R(STRING, STRING, String, sha1_text, varray()); - ADDFUNC0R(STRING, STRING, String, sha256_text, varray()); - ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, md5_buffer, varray()); - ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, sha1_buffer, varray()); - ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, sha256_buffer, varray()); - ADDFUNC0R(STRING, BOOL, String, empty, varray()); - ADDFUNC1R(STRING, STRING, String, humanize_size, INT, "size", varray()); - ADDFUNC0R(STRING, BOOL, String, is_abs_path, varray()); - ADDFUNC0R(STRING, BOOL, String, is_rel_path, varray()); - ADDFUNC0R(STRING, STRING, String, get_base_dir, varray()); - ADDFUNC0R(STRING, STRING, String, get_file, varray()); - ADDFUNC0R(STRING, STRING, String, xml_escape, varray()); - ADDFUNC0R(STRING, STRING, String, xml_unescape, varray()); - ADDFUNC0R(STRING, STRING, String, http_escape, varray()); - ADDFUNC0R(STRING, STRING, String, http_unescape, varray()); - ADDFUNC0R(STRING, STRING, String, c_escape, varray()); - ADDFUNC0R(STRING, STRING, String, c_unescape, varray()); - ADDFUNC0R(STRING, STRING, String, json_escape, varray()); - ADDFUNC0R(STRING, STRING, String, percent_encode, varray()); - ADDFUNC0R(STRING, STRING, String, percent_decode, varray()); - ADDFUNC0R(STRING, BOOL, String, is_valid_identifier, varray()); - ADDFUNC0R(STRING, BOOL, String, is_valid_integer, varray()); - ADDFUNC0R(STRING, BOOL, String, is_valid_float, varray()); - ADDFUNC1R(STRING, BOOL, String, is_valid_hex_number, BOOL, "with_prefix", varray(false)); - ADDFUNC0R(STRING, BOOL, String, is_valid_html_color, varray()); - ADDFUNC0R(STRING, BOOL, String, is_valid_ip_address, varray()); - ADDFUNC0R(STRING, BOOL, String, is_valid_filename, varray()); - ADDFUNC0R(STRING, INT, String, to_int, varray()); - ADDFUNC0R(STRING, FLOAT, String, to_float, varray()); - ADDFUNC0R(STRING, INT, String, hex_to_int, varray()); - ADDFUNC1R(STRING, STRING, String, pad_decimals, INT, "digits", varray()); - ADDFUNC1R(STRING, STRING, String, pad_zeros, INT, "digits", varray()); - ADDFUNC1R(STRING, STRING, String, trim_prefix, STRING, "prefix", varray()); - ADDFUNC1R(STRING, STRING, String, trim_suffix, STRING, "suffix", varray()); - - ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, to_ascii, varray()); - ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, to_utf8, varray()); - - ADDFUNC0R(VECTOR2, FLOAT, Vector2, angle, varray()); - ADDFUNC1R(VECTOR2, FLOAT, Vector2, angle_to, VECTOR2, "to", varray()); - ADDFUNC1R(VECTOR2, FLOAT, Vector2, angle_to_point, VECTOR2, "to", varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, direction_to, VECTOR2, "b", varray()); - ADDFUNC1R(VECTOR2, FLOAT, Vector2, distance_to, VECTOR2, "to", varray()); - ADDFUNC1R(VECTOR2, FLOAT, Vector2, distance_squared_to, VECTOR2, "to", varray()); - ADDFUNC0R(VECTOR2, FLOAT, Vector2, length, varray()); - ADDFUNC0R(VECTOR2, FLOAT, Vector2, length_squared, varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, normalized, varray()); - ADDFUNC0R(VECTOR2, BOOL, Vector2, is_normalized, varray()); - ADDFUNC1R(VECTOR2, BOOL, Vector2, is_equal_approx, VECTOR2, "v", varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, posmod, FLOAT, "mod", varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, posmodv, VECTOR2, "modv", varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, project, VECTOR2, "b", varray()); - ADDFUNC2R(VECTOR2, VECTOR2, Vector2, lerp, VECTOR2, "b", FLOAT, "t", varray()); - ADDFUNC2R(VECTOR2, VECTOR2, Vector2, slerp, VECTOR2, "b", FLOAT, "t", varray()); - ADDFUNC4R(VECTOR2, VECTOR2, Vector2, cubic_interpolate, VECTOR2, "b", VECTOR2, "pre_a", VECTOR2, "post_b", FLOAT, "t", varray()); - ADDFUNC2R(VECTOR2, VECTOR2, Vector2, move_toward, VECTOR2, "to", FLOAT, "delta", varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, rotated, FLOAT, "phi", varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, tangent, varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, floor, varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, ceil, varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, round, varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, snapped, VECTOR2, "by", varray()); - ADDFUNC0R(VECTOR2, FLOAT, Vector2, aspect, varray()); - ADDFUNC1R(VECTOR2, FLOAT, Vector2, dot, VECTOR2, "with", varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, slide, VECTOR2, "n", varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, bounce, VECTOR2, "n", varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, reflect, VECTOR2, "n", varray()); - ADDFUNC1R(VECTOR2, FLOAT, Vector2, cross, VECTOR2, "with", varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, abs, varray()); - ADDFUNC1R(VECTOR2, VECTOR2, Vector2, clamped, FLOAT, "length", varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, sign, varray()); - - ADDFUNC0R(VECTOR2I, FLOAT, Vector2i, aspect, varray()); - ADDFUNC0R(VECTOR2I, VECTOR2I, Vector2i, sign, varray()); - ADDFUNC0R(VECTOR2I, VECTOR2I, Vector2i, abs, varray()); - - ADDFUNC0R(RECT2, FLOAT, Rect2, get_area, varray()); - ADDFUNC0R(RECT2, BOOL, Rect2, has_no_area, varray()); - ADDFUNC1R(RECT2, BOOL, Rect2, has_point, VECTOR2, "point", varray()); - ADDFUNC1R(RECT2, BOOL, Rect2, is_equal_approx, RECT2, "rect", varray()); - ADDFUNC2R(RECT2, BOOL, Rect2, intersects, RECT2, "b", BOOL, "include_borders", varray(false)); - ADDFUNC1R(RECT2, BOOL, Rect2, encloses, RECT2, "b", varray()); - ADDFUNC1R(RECT2, RECT2, Rect2, clip, RECT2, "b", varray()); - ADDFUNC1R(RECT2, RECT2, Rect2, merge, RECT2, "b", varray()); - ADDFUNC1R(RECT2, RECT2, Rect2, expand, VECTOR2, "to", varray()); - ADDFUNC1R(RECT2, RECT2, Rect2, grow, FLOAT, "by", varray()); - ADDFUNC2R(RECT2, RECT2, Rect2, grow_margin, INT, "margin", FLOAT, "by", varray()); - ADDFUNC4R(RECT2, RECT2, Rect2, grow_individual, FLOAT, "left", FLOAT, "top", FLOAT, "right", FLOAT, " bottom", varray()); - ADDFUNC0R(RECT2, RECT2, Rect2, abs, varray()); - - ADDFUNC0R(RECT2I, INT, Rect2i, get_area, varray()); - ADDFUNC0R(RECT2I, BOOL, Rect2i, has_no_area, varray()); - ADDFUNC1R(RECT2I, BOOL, Rect2i, has_point, VECTOR2I, "point", varray()); - ADDFUNC1R(RECT2I, BOOL, Rect2i, intersects, RECT2I, "b", varray()); - ADDFUNC1R(RECT2I, BOOL, Rect2i, encloses, RECT2I, "b", varray()); - ADDFUNC1R(RECT2I, RECT2I, Rect2i, clip, RECT2I, "b", varray()); - ADDFUNC1R(RECT2I, RECT2I, Rect2i, merge, RECT2I, "b", varray()); - ADDFUNC1R(RECT2I, RECT2I, Rect2i, expand, VECTOR2I, "to", varray()); - ADDFUNC1R(RECT2I, RECT2I, Rect2i, grow, INT, "by", varray()); - ADDFUNC2R(RECT2I, RECT2I, Rect2i, grow_margin, INT, "margin", INT, "by", varray()); - ADDFUNC4R(RECT2I, RECT2I, Rect2i, grow_individual, INT, "left", INT, "top", INT, "right", INT, " bottom", varray()); - ADDFUNC0R(RECT2I, RECT2I, Rect2i, abs, varray()); - - ADDFUNC0R(VECTOR3, INT, Vector3, min_axis, varray()); - ADDFUNC0R(VECTOR3, INT, Vector3, max_axis, varray()); - ADDFUNC1R(VECTOR3, FLOAT, Vector3, angle_to, VECTOR3, "to", varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, direction_to, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, FLOAT, Vector3, distance_to, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, FLOAT, Vector3, distance_squared_to, VECTOR3, "b", varray()); - ADDFUNC0R(VECTOR3, FLOAT, Vector3, length, varray()); - ADDFUNC0R(VECTOR3, FLOAT, Vector3, length_squared, varray()); - ADDFUNC0R(VECTOR3, VECTOR3, Vector3, normalized, varray()); - ADDFUNC0R(VECTOR3, BOOL, Vector3, is_normalized, varray()); - ADDFUNC1R(VECTOR3, BOOL, Vector3, is_equal_approx, VECTOR3, "v", varray()); - ADDFUNC0R(VECTOR3, VECTOR3, Vector3, inverse, varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, snapped, VECTOR3, "by", varray()); - ADDFUNC2R(VECTOR3, VECTOR3, Vector3, rotated, VECTOR3, "axis", FLOAT, "phi", varray()); - ADDFUNC2R(VECTOR3, VECTOR3, Vector3, lerp, VECTOR3, "b", FLOAT, "t", varray()); - ADDFUNC2R(VECTOR3, VECTOR3, Vector3, slerp, VECTOR3, "b", FLOAT, "t", varray()); - ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", FLOAT, "t", varray()); - ADDFUNC2R(VECTOR3, VECTOR3, Vector3, move_toward, VECTOR3, "to", FLOAT, "delta", varray()); - ADDFUNC1R(VECTOR3, FLOAT, Vector3, dot, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, cross, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, BASIS, Vector3, outer, VECTOR3, "b", varray()); - ADDFUNC0R(VECTOR3, BASIS, Vector3, to_diagonal_matrix, varray()); - ADDFUNC0R(VECTOR3, VECTOR3, Vector3, abs, varray()); - ADDFUNC0R(VECTOR3, VECTOR3, Vector3, floor, varray()); - ADDFUNC0R(VECTOR3, VECTOR3, Vector3, ceil, varray()); - ADDFUNC0R(VECTOR3, VECTOR3, Vector3, round, varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, posmod, FLOAT, "mod", varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, posmodv, VECTOR3, "modv", varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, project, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, slide, VECTOR3, "n", varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, bounce, VECTOR3, "n", varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, reflect, VECTOR3, "n", varray()); - ADDFUNC0R(VECTOR3, VECTOR3, Vector3, sign, varray()); - - ADDFUNC0R(VECTOR3I, INT, Vector3i, min_axis, varray()); - ADDFUNC0R(VECTOR3I, INT, Vector3i, max_axis, varray()); - ADDFUNC0R(VECTOR3I, VECTOR3I, Vector3i, sign, varray()); - - ADDFUNC0R(PLANE, PLANE, Plane, normalized, varray()); - ADDFUNC0R(PLANE, VECTOR3, Plane, center, varray()); - ADDFUNC0R(PLANE, VECTOR3, Plane, get_any_point, varray()); - ADDFUNC1R(PLANE, BOOL, Plane, is_equal_approx, PLANE, "plane", varray()); - ADDFUNC1R(PLANE, BOOL, Plane, is_point_over, VECTOR3, "point", varray()); - ADDFUNC1R(PLANE, FLOAT, Plane, distance_to, VECTOR3, "point", varray()); - ADDFUNC2R(PLANE, BOOL, Plane, has_point, VECTOR3, "point", FLOAT, "epsilon", varray(CMP_EPSILON)); - ADDFUNC1R(PLANE, VECTOR3, Plane, project, VECTOR3, "point", varray()); - ADDFUNC2R(PLANE, VECTOR3, Plane, intersect_3, PLANE, "b", PLANE, "c", varray()); - ADDFUNC2R(PLANE, VECTOR3, Plane, intersects_ray, VECTOR3, "from", VECTOR3, "dir", varray()); - ADDFUNC2R(PLANE, VECTOR3, Plane, intersects_segment, VECTOR3, "begin", VECTOR3, "end", varray()); - - ADDFUNC0R(QUAT, FLOAT, Quat, length, varray()); - ADDFUNC0R(QUAT, FLOAT, Quat, length_squared, varray()); - ADDFUNC0R(QUAT, QUAT, Quat, normalized, varray()); - ADDFUNC0R(QUAT, BOOL, Quat, is_normalized, varray()); - ADDFUNC1R(QUAT, BOOL, Quat, is_equal_approx, QUAT, "quat", varray()); - ADDFUNC0R(QUAT, QUAT, Quat, inverse, varray()); - ADDFUNC1R(QUAT, FLOAT, Quat, dot, QUAT, "b", varray()); - ADDFUNC1R(QUAT, VECTOR3, Quat, xform, VECTOR3, "v", varray()); - ADDFUNC2R(QUAT, QUAT, Quat, slerp, QUAT, "b", FLOAT, "t", varray()); - ADDFUNC2R(QUAT, QUAT, Quat, slerpni, QUAT, "b", FLOAT, "t", varray()); - ADDFUNC4R(QUAT, QUAT, Quat, cubic_slerp, QUAT, "b", QUAT, "pre_a", QUAT, "post_b", FLOAT, "t", varray()); - ADDFUNC0R(QUAT, VECTOR3, Quat, get_euler, varray()); - ADDFUNC1(QUAT, NIL, Quat, set_euler, VECTOR3, "euler", varray()); - ADDFUNC2(QUAT, NIL, Quat, set_axis_angle, VECTOR3, "axis", FLOAT, "angle", varray()); - - ADDFUNC0R(COLOR, INT, Color, to_argb32, varray()); - ADDFUNC0R(COLOR, INT, Color, to_abgr32, varray()); - ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray()); - ADDFUNC0R(COLOR, INT, Color, to_argb64, varray()); - ADDFUNC0R(COLOR, INT, Color, to_abgr64, varray()); - ADDFUNC0R(COLOR, INT, Color, to_rgba64, varray()); - ADDFUNC0R(COLOR, COLOR, Color, inverted, varray()); - ADDFUNC0R(COLOR, COLOR, Color, contrasted, varray()); - ADDFUNC2R(COLOR, COLOR, Color, lerp, COLOR, "b", FLOAT, "t", varray()); - ADDFUNC1R(COLOR, COLOR, Color, blend, COLOR, "over", varray()); - ADDFUNC1R(COLOR, COLOR, Color, lightened, FLOAT, "amount", varray()); - ADDFUNC1R(COLOR, COLOR, Color, darkened, FLOAT, "amount", varray()); - ADDFUNC1R(COLOR, STRING, Color, to_html, BOOL, "with_alpha", varray(true)); - ADDFUNC4R(COLOR, COLOR, Color, from_hsv, FLOAT, "h", FLOAT, "s", FLOAT, "v", FLOAT, "a", varray(1.0)); - ADDFUNC1R(COLOR, BOOL, Color, is_equal_approx, COLOR, "color", varray()); - - ADDFUNC0R(_RID, INT, RID, get_id, varray()); - - ADDFUNC0R(NODE_PATH, BOOL, NodePath, is_absolute, varray()); - ADDFUNC0R(NODE_PATH, INT, NodePath, get_name_count, varray()); - ADDFUNC1R(NODE_PATH, STRING, NodePath, get_name, INT, "idx", varray()); - ADDFUNC0R(NODE_PATH, INT, NodePath, get_subname_count, varray()); - ADDFUNC1R(NODE_PATH, STRING, NodePath, get_subname, INT, "idx", varray()); - ADDFUNC0R(NODE_PATH, STRING, NodePath, get_concatenated_subnames, varray()); - ADDFUNC0R(NODE_PATH, NODE_PATH, NodePath, get_as_property_path, varray()); - ADDFUNC0R(NODE_PATH, BOOL, NodePath, is_empty, varray()); - - ADDFUNC0R(DICTIONARY, INT, Dictionary, size, varray()); - ADDFUNC0R(DICTIONARY, BOOL, Dictionary, empty, varray()); - ADDFUNC0NC(DICTIONARY, NIL, Dictionary, clear, varray()); - ADDFUNC1R(DICTIONARY, BOOL, Dictionary, has, NIL, "key", varray()); - ADDFUNC1R(DICTIONARY, BOOL, Dictionary, has_all, ARRAY, "keys", varray()); - ADDFUNC1RNC(DICTIONARY, BOOL, Dictionary, erase, NIL, "key", varray()); - ADDFUNC0R(DICTIONARY, INT, Dictionary, hash, varray()); - ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, keys, varray()); - ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, values, varray()); - ADDFUNC1R(DICTIONARY, DICTIONARY, Dictionary, duplicate, BOOL, "deep", varray(false)); - ADDFUNC2R(DICTIONARY, NIL, Dictionary, get, NIL, "key", NIL, "default", varray(Variant())); - - ADDFUNC0R(CALLABLE, BOOL, Callable, is_null, varray()); - ADDFUNC0R(CALLABLE, BOOL, Callable, is_custom, varray()); - ADDFUNC0R(CALLABLE, BOOL, Callable, is_standard, varray()); - ADDFUNC0R(CALLABLE, OBJECT, Callable, get_object, varray()); - ADDFUNC0R(CALLABLE, INT, Callable, get_object_id, varray()); - ADDFUNC0R(CALLABLE, STRING_NAME, Callable, get_method, varray()); - ADDFUNC0R(CALLABLE, INT, Callable, hash, varray()); - - ADDFUNC0R(SIGNAL, BOOL, Signal, is_null, varray()); - ADDFUNC0R(SIGNAL, OBJECT, Signal, get_object, varray()); - ADDFUNC0R(SIGNAL, INT, Signal, get_object_id, varray()); - ADDFUNC0R(SIGNAL, STRING_NAME, Signal, get_name, varray()); - - ADDFUNC3R(SIGNAL, INT, Signal, connect, CALLABLE, "callable", ARRAY, "binds", INT, "flags", varray(Array(), 0)); - - ADDFUNC1R(SIGNAL, NIL, Signal, disconnect, CALLABLE, "callable", varray()); - ADDFUNC1R(SIGNAL, BOOL, Signal, is_connected, CALLABLE, "callable", varray()); - ADDFUNC0R(SIGNAL, ARRAY, Signal, get_connections, varray()); - - ADDFUNC0R(ARRAY, INT, Array, size, varray()); - ADDFUNC0R(ARRAY, BOOL, Array, empty, varray()); - ADDFUNC0NC(ARRAY, NIL, Array, clear, varray()); - ADDFUNC0R(ARRAY, INT, Array, hash, varray()); - ADDFUNC1NC(ARRAY, NIL, Array, push_back, NIL, "value", varray()); - ADDFUNC1NC(ARRAY, NIL, Array, push_front, NIL, "value", varray()); - ADDFUNC1NC(ARRAY, NIL, Array, append, NIL, "value", varray()); - ADDFUNC1NC(ARRAY, NIL, Array, resize, INT, "size", varray()); - ADDFUNC2NC(ARRAY, NIL, Array, insert, INT, "position", NIL, "value", varray()); - ADDFUNC1NC(ARRAY, NIL, Array, remove, INT, "position", varray()); - ADDFUNC1NC(ARRAY, NIL, Array, erase, NIL, "value", varray()); - ADDFUNC0R(ARRAY, NIL, Array, front, varray()); - ADDFUNC0R(ARRAY, NIL, Array, back, varray()); - ADDFUNC2R(ARRAY, INT, Array, find, NIL, "what", INT, "from", varray(0)); - ADDFUNC2R(ARRAY, INT, Array, rfind, NIL, "what", INT, "from", varray(-1)); - ADDFUNC1R(ARRAY, INT, Array, find_last, NIL, "value", varray()); - ADDFUNC1R(ARRAY, INT, Array, count, NIL, "value", varray()); - ADDFUNC1R(ARRAY, BOOL, Array, has, NIL, "value", varray()); - ADDFUNC0RNC(ARRAY, NIL, Array, pop_back, varray()); - ADDFUNC0RNC(ARRAY, NIL, Array, pop_front, varray()); - ADDFUNC0NC(ARRAY, NIL, Array, sort, varray()); - ADDFUNC2NC(ARRAY, NIL, Array, sort_custom, OBJECT, "obj", STRING, "func", varray()); - ADDFUNC0NC(ARRAY, NIL, Array, shuffle, varray()); - ADDFUNC2R(ARRAY, INT, Array, bsearch, NIL, "value", BOOL, "before", varray(true)); - ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true)); - ADDFUNC0NC(ARRAY, NIL, Array, invert, varray()); - ADDFUNC1R(ARRAY, ARRAY, Array, duplicate, BOOL, "deep", varray(false)); - ADDFUNC4R(ARRAY, ARRAY, Array, slice, INT, "begin", INT, "end", INT, "step", BOOL, "deep", varray(1, false)); - ADDFUNC0R(ARRAY, NIL, Array, max, varray()); - ADDFUNC0R(ARRAY, NIL, Array, min, varray()); - - ADDFUNC0R(PACKED_BYTE_ARRAY, INT, PackedByteArray, size, varray()); - ADDFUNC0R(PACKED_BYTE_ARRAY, BOOL, PackedByteArray, empty, varray()); - ADDFUNC2(PACKED_BYTE_ARRAY, NIL, PackedByteArray, set, INT, "idx", INT, "byte", varray()); - ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, push_back, INT, "byte", varray()); - ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, append, INT, "byte", varray()); - ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, append_array, PACKED_BYTE_ARRAY, "array", varray()); - ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_BYTE_ARRAY, INT, PackedByteArray, insert, INT, "idx", INT, "byte", varray()); - ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_BYTE_ARRAY, NIL, PackedByteArray, invert, varray()); - ADDFUNC2R(PACKED_BYTE_ARRAY, PACKED_BYTE_ARRAY, PackedByteArray, subarray, INT, "from", INT, "to", varray()); - - ADDFUNC0R(PACKED_BYTE_ARRAY, STRING, PackedByteArray, get_string_from_ascii, varray()); - ADDFUNC0R(PACKED_BYTE_ARRAY, STRING, PackedByteArray, get_string_from_utf8, varray()); - ADDFUNC0R(PACKED_BYTE_ARRAY, STRING, PackedByteArray, hex_encode, varray()); - ADDFUNC1R(PACKED_BYTE_ARRAY, PACKED_BYTE_ARRAY, PackedByteArray, compress, INT, "compression_mode", varray(0)); - ADDFUNC2R(PACKED_BYTE_ARRAY, PACKED_BYTE_ARRAY, PackedByteArray, decompress, INT, "buffer_size", INT, "compression_mode", varray(0)); - - ADDFUNC0R(PACKED_INT32_ARRAY, INT, PackedInt32Array, size, varray()); - ADDFUNC0R(PACKED_INT32_ARRAY, BOOL, PackedInt32Array, empty, varray()); - ADDFUNC2(PACKED_INT32_ARRAY, NIL, PackedInt32Array, set, INT, "idx", INT, "integer", varray()); - ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, push_back, INT, "integer", varray()); - ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, append, INT, "integer", varray()); - ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, append_array, PACKED_INT32_ARRAY, "array", varray()); - ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_INT32_ARRAY, INT, PackedInt32Array, insert, INT, "idx", INT, "integer", varray()); - ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_INT32_ARRAY, NIL, PackedInt32Array, invert, varray()); - - ADDFUNC0R(PACKED_INT64_ARRAY, INT, PackedInt64Array, size, varray()); - ADDFUNC0R(PACKED_INT64_ARRAY, BOOL, PackedInt64Array, empty, varray()); - ADDFUNC2(PACKED_INT64_ARRAY, NIL, PackedInt64Array, set, INT, "idx", INT, "integer", varray()); - ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, push_back, INT, "integer", varray()); - ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, append, INT, "integer", varray()); - ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, append_array, PACKED_INT64_ARRAY, "array", varray()); - ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_INT64_ARRAY, INT, PackedInt64Array, insert, INT, "idx", INT, "integer", varray()); - ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_INT64_ARRAY, NIL, PackedInt64Array, invert, varray()); - - ADDFUNC0R(PACKED_FLOAT32_ARRAY, INT, PackedFloat32Array, size, varray()); - ADDFUNC0R(PACKED_FLOAT32_ARRAY, BOOL, PackedFloat32Array, empty, varray()); - ADDFUNC2(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, set, INT, "idx", FLOAT, "value", varray()); - ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, push_back, FLOAT, "value", varray()); - ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, append, FLOAT, "value", varray()); - ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, append_array, PACKED_FLOAT32_ARRAY, "array", varray()); - ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_FLOAT32_ARRAY, INT, PackedFloat32Array, insert, INT, "idx", FLOAT, "value", varray()); - ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, invert, varray()); - - ADDFUNC0R(PACKED_FLOAT64_ARRAY, INT, PackedFloat64Array, size, varray()); - ADDFUNC0R(PACKED_FLOAT64_ARRAY, BOOL, PackedFloat64Array, empty, varray()); - ADDFUNC2(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, set, INT, "idx", FLOAT, "value", varray()); - ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, push_back, FLOAT, "value", varray()); - ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, append, FLOAT, "value", varray()); - ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, append_array, PACKED_FLOAT64_ARRAY, "array", varray()); - ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_FLOAT64_ARRAY, INT, PackedFloat64Array, insert, INT, "idx", FLOAT, "value", varray()); - ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, invert, varray()); - - ADDFUNC0R(PACKED_STRING_ARRAY, INT, PackedStringArray, size, varray()); - ADDFUNC0R(PACKED_STRING_ARRAY, BOOL, PackedStringArray, empty, varray()); - ADDFUNC2(PACKED_STRING_ARRAY, NIL, PackedStringArray, set, INT, "idx", STRING, "string", varray()); - ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, push_back, STRING, "string", varray()); - ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, append, STRING, "string", varray()); - ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, append_array, PACKED_STRING_ARRAY, "array", varray()); - ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_STRING_ARRAY, INT, PackedStringArray, insert, INT, "idx", STRING, "string", varray()); - ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_STRING_ARRAY, NIL, PackedStringArray, invert, varray()); - - ADDFUNC0R(PACKED_VECTOR2_ARRAY, INT, PackedVector2Array, size, varray()); - ADDFUNC0R(PACKED_VECTOR2_ARRAY, BOOL, PackedVector2Array, empty, varray()); - ADDFUNC2(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, set, INT, "idx", VECTOR2, "vector2", varray()); - ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, push_back, VECTOR2, "vector2", varray()); - ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, append, VECTOR2, "vector2", varray()); - ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, append_array, PACKED_VECTOR2_ARRAY, "array", varray()); - ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_VECTOR2_ARRAY, INT, PackedVector2Array, insert, INT, "idx", VECTOR2, "vector2", varray()); - ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, invert, varray()); - - ADDFUNC0R(PACKED_VECTOR3_ARRAY, INT, PackedVector3Array, size, varray()); - ADDFUNC0R(PACKED_VECTOR3_ARRAY, BOOL, PackedVector3Array, empty, varray()); - ADDFUNC2(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, set, INT, "idx", VECTOR3, "vector3", varray()); - ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, push_back, VECTOR3, "vector3", varray()); - ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, append, VECTOR3, "vector3", varray()); - ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, append_array, PACKED_VECTOR3_ARRAY, "array", varray()); - ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_VECTOR3_ARRAY, INT, PackedVector3Array, insert, INT, "idx", VECTOR3, "vector3", varray()); - ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, invert, varray()); - - ADDFUNC0R(PACKED_COLOR_ARRAY, INT, PackedColorArray, size, varray()); - ADDFUNC0R(PACKED_COLOR_ARRAY, BOOL, PackedColorArray, empty, varray()); - ADDFUNC2(PACKED_COLOR_ARRAY, NIL, PackedColorArray, set, INT, "idx", COLOR, "color", varray()); - ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, push_back, COLOR, "color", varray()); - ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, append, COLOR, "color", varray()); - ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, append_array, PACKED_COLOR_ARRAY, "array", varray()); - ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, remove, INT, "idx", varray()); - ADDFUNC2R(PACKED_COLOR_ARRAY, INT, PackedColorArray, insert, INT, "idx", COLOR, "color", varray()); - ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, resize, INT, "idx", varray()); - ADDFUNC0(PACKED_COLOR_ARRAY, NIL, PackedColorArray, invert, varray()); - - //pointerbased - - ADDFUNC0R(AABB, FLOAT, AABB, get_area, varray()); - ADDFUNC0R(AABB, BOOL, AABB, has_no_area, varray()); - ADDFUNC0R(AABB, BOOL, AABB, has_no_surface, varray()); - ADDFUNC1R(AABB, BOOL, AABB, has_point, VECTOR3, "point", varray()); - ADDFUNC1R(AABB, BOOL, AABB, is_equal_approx, AABB, "aabb", varray()); - ADDFUNC1R(AABB, BOOL, AABB, intersects, AABB, "with", varray()); - ADDFUNC1R(AABB, BOOL, AABB, encloses, AABB, "with", varray()); - ADDFUNC1R(AABB, BOOL, AABB, intersects_plane, PLANE, "plane", varray()); - ADDFUNC2R(AABB, BOOL, AABB, intersects_segment, VECTOR3, "from", VECTOR3, "to", varray()); - ADDFUNC1R(AABB, AABB, AABB, intersection, AABB, "with", varray()); - ADDFUNC1R(AABB, AABB, AABB, merge, AABB, "with", varray()); - ADDFUNC1R(AABB, AABB, AABB, expand, VECTOR3, "to_point", varray()); - ADDFUNC1R(AABB, AABB, AABB, grow, FLOAT, "by", varray()); - ADDFUNC1R(AABB, VECTOR3, AABB, get_support, VECTOR3, "dir", varray()); - ADDFUNC0R(AABB, VECTOR3, AABB, get_longest_axis, varray()); - ADDFUNC0R(AABB, INT, AABB, get_longest_axis_index, varray()); - ADDFUNC0R(AABB, FLOAT, AABB, get_longest_axis_size, varray()); - ADDFUNC0R(AABB, VECTOR3, AABB, get_shortest_axis, varray()); - ADDFUNC0R(AABB, INT, AABB, get_shortest_axis_index, varray()); - ADDFUNC0R(AABB, FLOAT, AABB, get_shortest_axis_size, varray()); - ADDFUNC1R(AABB, VECTOR3, AABB, get_endpoint, INT, "idx", varray()); - - ADDFUNC0R(TRANSFORM2D, TRANSFORM2D, Transform2D, inverse, varray()); - ADDFUNC0R(TRANSFORM2D, TRANSFORM2D, Transform2D, affine_inverse, varray()); - ADDFUNC0R(TRANSFORM2D, FLOAT, Transform2D, get_rotation, varray()); - ADDFUNC0R(TRANSFORM2D, VECTOR2, Transform2D, get_origin, varray()); - ADDFUNC0R(TRANSFORM2D, VECTOR2, Transform2D, get_scale, varray()); - ADDFUNC0R(TRANSFORM2D, TRANSFORM2D, Transform2D, orthonormalized, varray()); - ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, rotated, FLOAT, "phi", varray()); - ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, scaled, VECTOR2, "scale", varray()); - ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, translated, VECTOR2, "offset", varray()); - ADDFUNC1R(TRANSFORM2D, NIL, Transform2D, xform, NIL, "v", varray()); - ADDFUNC1R(TRANSFORM2D, NIL, Transform2D, xform_inv, NIL, "v", varray()); - ADDFUNC1R(TRANSFORM2D, VECTOR2, Transform2D, basis_xform, VECTOR2, "v", varray()); - ADDFUNC1R(TRANSFORM2D, VECTOR2, Transform2D, basis_xform_inv, VECTOR2, "v", varray()); - ADDFUNC2R(TRANSFORM2D, TRANSFORM2D, Transform2D, interpolate_with, TRANSFORM2D, "transform", FLOAT, "weight", varray()); - ADDFUNC1R(TRANSFORM2D, BOOL, Transform2D, is_equal_approx, TRANSFORM2D, "transform", varray()); - - ADDFUNC0R(BASIS, BASIS, Basis, inverse, varray()); - ADDFUNC0R(BASIS, BASIS, Basis, transposed, varray()); - ADDFUNC0R(BASIS, BASIS, Basis, orthonormalized, varray()); - ADDFUNC0R(BASIS, FLOAT, Basis, determinant, varray()); - ADDFUNC2R(BASIS, BASIS, Basis, rotated, VECTOR3, "axis", FLOAT, "phi", varray()); - ADDFUNC1R(BASIS, BASIS, Basis, scaled, VECTOR3, "scale", varray()); - ADDFUNC0R(BASIS, VECTOR3, Basis, get_scale, varray()); - ADDFUNC0R(BASIS, VECTOR3, Basis, get_euler, varray()); - ADDFUNC1R(BASIS, FLOAT, Basis, tdotx, VECTOR3, "with", varray()); - ADDFUNC1R(BASIS, FLOAT, Basis, tdoty, VECTOR3, "with", varray()); - ADDFUNC1R(BASIS, FLOAT, Basis, tdotz, VECTOR3, "with", varray()); - ADDFUNC1R(BASIS, VECTOR3, Basis, xform, VECTOR3, "v", varray()); - ADDFUNC1R(BASIS, VECTOR3, Basis, xform_inv, VECTOR3, "v", varray()); - ADDFUNC0R(BASIS, INT, Basis, get_orthogonal_index, varray()); - ADDFUNC2R(BASIS, BASIS, Basis, slerp, BASIS, "b", FLOAT, "t", varray()); - ADDFUNC2R(BASIS, BOOL, Basis, is_equal_approx, BASIS, "b", FLOAT, "epsilon", varray(CMP_EPSILON)); // TODO: Replace in 4.0, see other TODO. - ADDFUNC0R(BASIS, QUAT, Basis, get_rotation_quat, varray()); - - ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, inverse, varray()); - ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, affine_inverse, varray()); - ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, orthonormalized, varray()); - ADDFUNC2R(TRANSFORM, TRANSFORM, Transform, rotated, VECTOR3, "axis", FLOAT, "phi", varray()); - ADDFUNC1R(TRANSFORM, TRANSFORM, Transform, scaled, VECTOR3, "scale", varray()); - ADDFUNC1R(TRANSFORM, TRANSFORM, Transform, translated, VECTOR3, "offset", varray()); - ADDFUNC2R(TRANSFORM, TRANSFORM, Transform, looking_at, VECTOR3, "target", VECTOR3, "up", varray()); - ADDFUNC2R(TRANSFORM, TRANSFORM, Transform, interpolate_with, TRANSFORM, "transform", FLOAT, "weight", varray()); - ADDFUNC1R(TRANSFORM, BOOL, Transform, is_equal_approx, TRANSFORM, "transform", varray()); - ADDFUNC1R(TRANSFORM, NIL, Transform, xform, NIL, "v", varray()); - ADDFUNC1R(TRANSFORM, NIL, Transform, xform_inv, NIL, "v", varray()); - - /* REGISTER CONSTRUCTORS */ - - _VariantCall::add_constructor(_VariantCall::Vector2_init1, Variant::VECTOR2, "x", Variant::FLOAT, "y", Variant::FLOAT); - _VariantCall::add_constructor(_VariantCall::Vector2i_init1, Variant::VECTOR2I, "x", Variant::INT, "y", Variant::INT); - - _VariantCall::add_constructor(_VariantCall::Rect2_init1, Variant::RECT2, "position", Variant::VECTOR2, "size", Variant::VECTOR2); - _VariantCall::add_constructor(_VariantCall::Rect2_init2, Variant::RECT2, "x", Variant::FLOAT, "y", Variant::FLOAT, "width", Variant::FLOAT, "height", Variant::FLOAT); - - _VariantCall::add_constructor(_VariantCall::Rect2i_init1, Variant::RECT2I, "position", Variant::VECTOR2, "size", Variant::VECTOR2); - _VariantCall::add_constructor(_VariantCall::Rect2i_init2, Variant::RECT2I, "x", Variant::INT, "y", Variant::INT, "width", Variant::INT, "height", Variant::INT); - - _VariantCall::add_constructor(_VariantCall::Transform2D_init2, Variant::TRANSFORM2D, "rotation", Variant::FLOAT, "position", Variant::VECTOR2); - _VariantCall::add_constructor(_VariantCall::Transform2D_init3, Variant::TRANSFORM2D, "x_axis", Variant::VECTOR2, "y_axis", Variant::VECTOR2, "origin", Variant::VECTOR2); - - _VariantCall::add_constructor(_VariantCall::Vector3_init1, Variant::VECTOR3, "x", Variant::FLOAT, "y", Variant::FLOAT, "z", Variant::FLOAT); - _VariantCall::add_constructor(_VariantCall::Vector3i_init1, Variant::VECTOR3I, "x", Variant::INT, "y", Variant::INT, "z", Variant::INT); - - _VariantCall::add_constructor(_VariantCall::Plane_init1, Variant::PLANE, "a", Variant::FLOAT, "b", Variant::FLOAT, "c", Variant::FLOAT, "d", Variant::FLOAT); - _VariantCall::add_constructor(_VariantCall::Plane_init2, Variant::PLANE, "v1", Variant::VECTOR3, "v2", Variant::VECTOR3, "v3", Variant::VECTOR3); - _VariantCall::add_constructor(_VariantCall::Plane_init3, Variant::PLANE, "normal", Variant::VECTOR3, "d", Variant::FLOAT); - - _VariantCall::add_constructor(_VariantCall::Quat_init1, Variant::QUAT, "x", Variant::FLOAT, "y", Variant::FLOAT, "z", Variant::FLOAT, "w", Variant::FLOAT); - _VariantCall::add_constructor(_VariantCall::Quat_init2, Variant::QUAT, "axis", Variant::VECTOR3, "angle", Variant::FLOAT); - _VariantCall::add_constructor(_VariantCall::Quat_init3, Variant::QUAT, "euler", Variant::VECTOR3); - - _VariantCall::add_constructor(_VariantCall::Color_init1, Variant::COLOR, "r", Variant::FLOAT, "g", Variant::FLOAT, "b", Variant::FLOAT, "a", Variant::FLOAT); - _VariantCall::add_constructor(_VariantCall::Color_init2, Variant::COLOR, "r", Variant::FLOAT, "g", Variant::FLOAT, "b", Variant::FLOAT); - // init3 and init4 are the constructors for HTML hex strings and integers respectively which don't need binding here, so we skip to init5. - _VariantCall::add_constructor(_VariantCall::Color_init5, Variant::COLOR, "c", Variant::COLOR, "a", Variant::FLOAT); - - _VariantCall::add_constructor(_VariantCall::AABB_init1, Variant::AABB, "position", Variant::VECTOR3, "size", Variant::VECTOR3); - - _VariantCall::add_constructor(_VariantCall::Basis_init1, Variant::BASIS, "x_axis", Variant::VECTOR3, "y_axis", Variant::VECTOR3, "z_axis", Variant::VECTOR3); - _VariantCall::add_constructor(_VariantCall::Basis_init2, Variant::BASIS, "axis", Variant::VECTOR3, "phi", Variant::FLOAT); - - _VariantCall::add_constructor(_VariantCall::Transform_init1, Variant::TRANSFORM, "x_axis", Variant::VECTOR3, "y_axis", Variant::VECTOR3, "z_axis", Variant::VECTOR3, "origin", Variant::VECTOR3); - _VariantCall::add_constructor(_VariantCall::Transform_init2, Variant::TRANSFORM, "basis", Variant::BASIS, "origin", Variant::VECTOR3); - - _VariantCall::add_constructor(_VariantCall::Callable_init2, Variant::CALLABLE, "object", Variant::OBJECT, "method_name", Variant::STRING_NAME); - _VariantCall::add_constructor(_VariantCall::Signal_init2, Variant::SIGNAL, "object", Variant::OBJECT, "signal_name", Variant::STRING_NAME); - - /* REGISTER CONSTANTS */ - - _populate_named_colors(); - for (Map<String, Color>::Element *color = _named_colors.front(); color; color = color->next()) { - _VariantCall::add_variant_constant(Variant::COLOR, color->key(), color->value()); - } - - _VariantCall::add_constant(Variant::VECTOR3, "AXIS_X", Vector3::AXIS_X); - _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Y", Vector3::AXIS_Y); - _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Z", Vector3::AXIS_Z); - - _VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "ONE", Vector3(1, 1, 1)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "DOWN", Vector3(0, -1, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); - - _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_X", Vector3::AXIS_X); - _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3::AXIS_Y); - _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3::AXIS_Z); - - _VariantCall::add_variant_constant(Variant::VECTOR3I, "ZERO", Vector3i(0, 0, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3I, "ONE", Vector3i(1, 1, 1)); - _VariantCall::add_variant_constant(Variant::VECTOR3I, "LEFT", Vector3i(-1, 0, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3I, "RIGHT", Vector3i(1, 0, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3I, "UP", Vector3i(0, 1, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3I, "DOWN", Vector3i(0, -1, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR3I, "FORWARD", Vector3i(0, 0, -1)); - _VariantCall::add_variant_constant(Variant::VECTOR3I, "BACK", Vector3i(0, 0, 1)); - - _VariantCall::add_constant(Variant::VECTOR2, "AXIS_X", Vector2::AXIS_X); - _VariantCall::add_constant(Variant::VECTOR2, "AXIS_Y", Vector2::AXIS_Y); - - _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_X", Vector2::AXIS_X); - _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_Y", Vector2::AXIS_Y); - - _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1)); - _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); - _VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); - _VariantCall::add_variant_constant(Variant::VECTOR2, "DOWN", Vector2(0, 1)); - - _VariantCall::add_variant_constant(Variant::VECTOR2I, "ZERO", Vector2i(0, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR2I, "ONE", Vector2i(1, 1)); - _VariantCall::add_variant_constant(Variant::VECTOR2I, "LEFT", Vector2i(-1, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR2I, "RIGHT", Vector2i(1, 0)); - _VariantCall::add_variant_constant(Variant::VECTOR2I, "UP", Vector2i(0, -1)); - _VariantCall::add_variant_constant(Variant::VECTOR2I, "DOWN", Vector2i(0, 1)); - - _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D()); - _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0)); - _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0)); - - Transform identity_transform = Transform(); - Transform flip_x_transform = Transform(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); - Transform flip_y_transform = Transform(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); - Transform flip_z_transform = Transform(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); - _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform); - _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", flip_x_transform); - _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", flip_y_transform); - _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", flip_z_transform); - - Basis identity_basis = Basis(); - Basis flip_x_basis = Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1); - Basis flip_y_basis = Basis(1, 0, 0, 0, -1, 0, 0, 0, 1); - Basis flip_z_basis = Basis(1, 0, 0, 0, 1, 0, 0, 0, -1); - _VariantCall::add_variant_constant(Variant::BASIS, "IDENTITY", identity_basis); - _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_X", flip_x_basis); - _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_Y", flip_y_basis); - _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_Z", flip_z_basis); - - _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_YZ", Plane(Vector3(1, 0, 0), 0)); - _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XZ", Plane(Vector3(0, 1, 0), 0)); - _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XY", Plane(Vector3(0, 0, 1), 0)); - - _VariantCall::add_variant_constant(Variant::QUAT, "IDENTITY", Quat(0, 0, 0, 1)); -} - -void unregister_variant_methods() { - - memdelete_arr(_VariantCall::type_funcs); - memdelete_arr(_VariantCall::construct_funcs); - memdelete_arr(_VariantCall::constant_data); -} diff --git a/core/variant_op.cpp b/core/variant_op.cpp deleted file mode 100644 index 27624b81ee..0000000000 --- a/core/variant_op.cpp +++ /dev/null @@ -1,4547 +0,0 @@ -/*************************************************************************/ -/* variant_op.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 "variant.h" - -#include "core/core_string_names.h" -#include "core/debugger/engine_debugger.h" -#include "core/object.h" - -#define CASE_TYPE_ALL(PREFIX, OP) \ - CASE_TYPE(PREFIX, OP, INT) \ - CASE_TYPE_ALL_BUT_INT(PREFIX, OP) - -#define CASE_TYPE_ALL_BUT_INT(PREFIX, OP) \ - CASE_TYPE(PREFIX, OP, NIL) \ - CASE_TYPE(PREFIX, OP, BOOL) \ - CASE_TYPE(PREFIX, OP, FLOAT) \ - CASE_TYPE(PREFIX, OP, STRING) \ - CASE_TYPE(PREFIX, OP, VECTOR2) \ - CASE_TYPE(PREFIX, OP, VECTOR2I) \ - CASE_TYPE(PREFIX, OP, RECT2) \ - CASE_TYPE(PREFIX, OP, RECT2I) \ - CASE_TYPE(PREFIX, OP, VECTOR3) \ - CASE_TYPE(PREFIX, OP, VECTOR3I) \ - CASE_TYPE(PREFIX, OP, TRANSFORM2D) \ - CASE_TYPE(PREFIX, OP, PLANE) \ - CASE_TYPE(PREFIX, OP, QUAT) \ - CASE_TYPE(PREFIX, OP, AABB) \ - CASE_TYPE(PREFIX, OP, BASIS) \ - CASE_TYPE(PREFIX, OP, TRANSFORM) \ - CASE_TYPE(PREFIX, OP, COLOR) \ - CASE_TYPE(PREFIX, OP, STRING_NAME) \ - CASE_TYPE(PREFIX, OP, NODE_PATH) \ - CASE_TYPE(PREFIX, OP, _RID) \ - CASE_TYPE(PREFIX, OP, OBJECT) \ - CASE_TYPE(PREFIX, OP, CALLABLE) \ - CASE_TYPE(PREFIX, OP, SIGNAL) \ - CASE_TYPE(PREFIX, OP, DICTIONARY) \ - CASE_TYPE(PREFIX, OP, ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_BYTE_ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_INT32_ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_INT64_ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_FLOAT32_ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_FLOAT64_ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_STRING_ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_VECTOR2_ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_VECTOR3_ARRAY) \ - CASE_TYPE(PREFIX, OP, PACKED_COLOR_ARRAY) - -#ifdef __GNUC__ -#define TYPE(PREFIX, OP, TYPE) &&PREFIX##_##OP##_##TYPE - -/* clang-format off */ -#define TYPES(PREFIX, OP) { \ - TYPE(PREFIX, OP, NIL), \ - TYPE(PREFIX, OP, BOOL), \ - TYPE(PREFIX, OP, INT), \ - TYPE(PREFIX, OP, FLOAT), \ - TYPE(PREFIX, OP, STRING), \ - TYPE(PREFIX, OP, VECTOR2), \ - TYPE(PREFIX, OP, VECTOR2I), \ - TYPE(PREFIX, OP, RECT2), \ - TYPE(PREFIX, OP, RECT2I), \ - TYPE(PREFIX, OP, VECTOR3), \ - TYPE(PREFIX, OP, VECTOR3I), \ - TYPE(PREFIX, OP, TRANSFORM2D), \ - TYPE(PREFIX, OP, PLANE), \ - TYPE(PREFIX, OP, QUAT), \ - TYPE(PREFIX, OP, AABB), \ - TYPE(PREFIX, OP, BASIS), \ - TYPE(PREFIX, OP, TRANSFORM), \ - TYPE(PREFIX, OP, COLOR), \ - TYPE(PREFIX, OP, STRING_NAME), \ - TYPE(PREFIX, OP, NODE_PATH), \ - TYPE(PREFIX, OP, _RID), \ - TYPE(PREFIX, OP, OBJECT), \ - TYPE(PREFIX, OP, CALLABLE), \ - TYPE(PREFIX, OP, SIGNAL), \ - TYPE(PREFIX, OP, DICTIONARY), \ - TYPE(PREFIX, OP, ARRAY), \ - TYPE(PREFIX, OP, PACKED_BYTE_ARRAY), \ - TYPE(PREFIX, OP, PACKED_INT32_ARRAY), \ - TYPE(PREFIX, OP, PACKED_INT64_ARRAY), \ - TYPE(PREFIX, OP, PACKED_FLOAT32_ARRAY), \ - TYPE(PREFIX, OP, PACKED_FLOAT64_ARRAY), \ - TYPE(PREFIX, OP, PACKED_STRING_ARRAY), \ - TYPE(PREFIX, OP, PACKED_VECTOR2_ARRAY), \ - TYPE(PREFIX, OP, PACKED_VECTOR3_ARRAY), \ - TYPE(PREFIX, OP, PACKED_COLOR_ARRAY), \ -} -/* clang-format on */ - -#define CASES(PREFIX) static const void *switch_table_##PREFIX[25][Variant::VARIANT_MAX] = { \ - TYPES(PREFIX, OP_EQUAL), \ - TYPES(PREFIX, OP_NOT_EQUAL), \ - TYPES(PREFIX, OP_LESS), \ - TYPES(PREFIX, OP_LESS_EQUAL), \ - TYPES(PREFIX, OP_GREATER), \ - TYPES(PREFIX, OP_GREATER_EQUAL), \ - TYPES(PREFIX, OP_ADD), \ - TYPES(PREFIX, OP_SUBTRACT), \ - TYPES(PREFIX, OP_MULTIPLY), \ - TYPES(PREFIX, OP_DIVIDE), \ - TYPES(PREFIX, OP_NEGATE), \ - TYPES(PREFIX, OP_POSITIVE), \ - TYPES(PREFIX, OP_MODULE), \ - TYPES(PREFIX, OP_STRING_CONCAT), \ - TYPES(PREFIX, OP_SHIFT_LEFT), \ - TYPES(PREFIX, OP_SHIFT_RIGHT), \ - TYPES(PREFIX, OP_BIT_AND), \ - TYPES(PREFIX, OP_BIT_OR), \ - TYPES(PREFIX, OP_BIT_XOR), \ - TYPES(PREFIX, OP_BIT_NEGATE), \ - TYPES(PREFIX, OP_AND), \ - TYPES(PREFIX, OP_OR), \ - TYPES(PREFIX, OP_XOR), \ - TYPES(PREFIX, OP_NOT), \ - TYPES(PREFIX, OP_IN), \ -} - -#define SWITCH(PREFIX, op, val) goto *switch_table_##PREFIX[op][val]; -#define SWITCH_OP(PREFIX, OP, val) -#define CASE_TYPE(PREFIX, OP, TYPE) PREFIX##_##OP##_##TYPE: - -#else -#define CASES(PREFIX) -#define SWITCH(PREFIX, op, val) switch (op) -#define SWITCH_OP(PREFIX, OP, val) \ - case OP: \ - switch (val) -#define CASE_TYPE(PREFIX, OP, TYPE) case TYPE: -#endif - -Variant::operator bool() const { - - return booleanize(); -} - -// We consider all uninitialized or empty types to be false based on the type's -// zeroiness. -bool Variant::booleanize() const { - return !is_zero(); -} - -#define _RETURN(m_what) \ - { \ - r_ret = m_what; \ - return; \ - } - -#define _RETURN_FAIL \ - { \ - r_valid = false; \ - return; \ - } - -#define DEFAULT_OP_NUM(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == INT) _RETURN(p_a._data.m_type m_op p_b._data._int); \ - if (p_b.type == FLOAT) _RETURN(p_a._data.m_type m_op p_b._data._float); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_NUM_NULL(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == INT) _RETURN(p_a._data.m_type m_op p_b._data._int); \ - if (p_b.type == FLOAT) _RETURN(p_a._data.m_type m_op p_b._data._float); \ - if (p_b.type == NIL) _RETURN(!(p_b.type m_op NIL)); \ - \ - _RETURN_FAIL \ - }; - -#ifdef DEBUG_ENABLED -#define DEFAULT_OP_NUM_DIV(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == INT) { \ - if (p_b._data._int == 0) { \ - r_valid = false; \ - _RETURN("Division By Zero"); \ - } \ - _RETURN(p_a._data.m_type / p_b._data._int); \ - } \ - if (p_b.type == FLOAT) { \ - if (p_b._data._float == 0) { \ - r_valid = false; \ - _RETURN("Division By Zero"); \ - } \ - _RETURN(p_a._data.m_type / p_b._data._float); \ - } \ - \ - _RETURN_FAIL \ - }; -#else -#define DEFAULT_OP_NUM_DIV(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == INT) _RETURN(p_a._data.m_type / p_b._data._int); \ - if (p_b.type == FLOAT) _RETURN(p_a._data.m_type / p_b._data._float); \ - \ - _RETURN_FAIL \ - }; -#endif - -#define DEFAULT_OP_NUM_NEG(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - _RETURN(-p_a._data.m_type); \ - }; - -#define DEFAULT_OP_NUM_POS(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - _RETURN(p_a._data.m_type); \ - }; - -#define DEFAULT_OP_NUM_VEC(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == INT) _RETURN(p_a._data.m_type m_op p_b._data._int); \ - if (p_b.type == FLOAT) _RETURN(p_a._data.m_type m_op p_b._data._float); \ - if (p_b.type == VECTOR2) _RETURN(p_a._data.m_type m_op *reinterpret_cast<const Vector2 *>(p_b._data._mem)); \ - if (p_b.type == VECTOR3) _RETURN(p_a._data.m_type m_op *reinterpret_cast<const Vector3 *>(p_b._data._mem)); \ - if (p_b.type == VECTOR2I) _RETURN(p_a._data.m_type m_op *reinterpret_cast<const Vector2 *>(p_b._data._mem)); \ - if (p_b.type == VECTOR3I) _RETURN(p_a._data.m_type m_op *reinterpret_cast<const Vector3 *>(p_b._data._mem)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_STR_REV(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const String *>(p_a._data._mem)); \ - if (p_b.type == STRING_NAME) _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const StringName *>(p_a._data._mem)); \ - if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const NodePath *>(p_a._data._mem)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_STR(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \ - if (p_b.type == STRING_NAME) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const StringName *>(p_b._data._mem)); \ - if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const NodePath *>(p_b._data._mem)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_STR_NULL(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \ - if (p_b.type == STRING_NAME) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const StringName *>(p_b._data._mem)); \ - if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const NodePath *>(p_b._data._mem)); \ - if (p_b.type == NIL) _RETURN(!(p_b.type m_op NIL)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_STR_NULL_NP(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \ - if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const NodePath *>(p_b._data._mem)); \ - if (p_b.type == NIL) _RETURN(!(p_b.type m_op NIL)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_STR_NULL_SN(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \ - if (p_b.type == STRING_NAME) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const StringName *>(p_b._data._mem)); \ - if (p_b.type == NIL) _RETURN(!(p_b.type m_op NIL)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_LOCALMEM_REV(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == m_name) \ - _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const m_type *>(p_a._data._mem)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_LOCALMEM(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == m_name) \ - _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const m_type *>(p_b._data._mem)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_LOCALMEM_NULL(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == m_name) \ - _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const m_type *>(p_b._data._mem)); \ - if (p_b.type == NIL) \ - _RETURN(!(p_b.type m_op NIL)); \ - \ - _RETURN_FAIL \ - }; - -#define DEFAULT_OP_LOCALMEM_NEG(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - _RETURN(-*reinterpret_cast<const m_type *>(p_a._data._mem)); \ - } - -#define DEFAULT_OP_LOCALMEM_POS(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem)); \ - } - -#define DEFAULT_OP_LOCALMEM_NUM(m_prefix, m_op_name, m_name, m_op, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == m_name) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const m_type *>(p_b._data._mem)); \ - if (p_b.type == INT) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op p_b._data._int); \ - if (p_b.type == FLOAT) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op p_b._data._float); \ - \ - _RETURN_FAIL \ - } - -#define DEFAULT_OP_PTR(m_op, m_name, m_sub) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == m_name) \ - _RETURN(p_a._data.m_sub m_op p_b._data.m_sub); \ - \ - _RETURN_FAIL \ - } - -#define DEFAULT_OP_PTRREF(m_prefix, m_op_name, m_name, m_op, m_sub) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == m_name) \ - _RETURN(*p_a._data.m_sub m_op *p_b._data.m_sub); \ - \ - _RETURN_FAIL \ - } - -#define DEFAULT_OP_PTRREF_NULL(m_prefix, m_op_name, m_name, m_op, m_sub) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == m_name) \ - _RETURN(*p_a._data.m_sub m_op *p_b._data.m_sub); \ - if (p_b.type == NIL) \ - _RETURN(!(p_b.type m_op NIL)); \ - \ - _RETURN_FAIL \ - } - -#define DEFAULT_OP_ARRAY_EQ(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == NIL) \ - _RETURN(false) \ - DEFAULT_OP_ARRAY_OP_BODY(m_prefix, m_op_name, m_name, m_type, !=, !=, true, false, false) \ - } - -#define DEFAULT_OP_ARRAY_NEQ(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_b.type == NIL) \ - _RETURN(true) \ - DEFAULT_OP_ARRAY_OP_BODY(m_prefix, m_op_name, m_name, m_type, !=, !=, false, true, true) \ - } - -#define DEFAULT_OP_ARRAY_LT(m_prefix, m_op_name, m_name, m_type) \ - DEFAULT_OP_ARRAY_OP(m_prefix, m_op_name, m_name, m_type, <, !=, false, a_len < array_b.size(), true) - -#define DEFAULT_OP_ARRAY_GT(m_prefix, m_op_name, m_name, m_type) \ - DEFAULT_OP_ARRAY_OP(m_prefix, m_op_name, m_name, m_type, >, !=, false, a_len < array_b.size(), true) - -#define DEFAULT_OP_ARRAY_OP(m_prefix, m_op_name, m_name, m_type, m_opa, m_opb, m_ret_def, m_ret_s, m_ret_f) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - DEFAULT_OP_ARRAY_OP_BODY(m_prefix, m_op_name, m_name, m_type, m_opa, m_opb, m_ret_def, m_ret_s, m_ret_f) \ - } - -#define DEFAULT_OP_ARRAY_OP_BODY(m_prefix, m_op_name, m_name, m_type, m_opa, m_opb, m_ret_def, m_ret_s, m_ret_f) \ - if (p_a.type != p_b.type) \ - _RETURN_FAIL \ - \ - const Vector<m_type> &array_a = PackedArrayRef<m_type>::get_array(p_a._data.packed_array); \ - const Vector<m_type> &array_b = PackedArrayRef<m_type>::get_array(p_b._data.packed_array); \ - \ - int a_len = array_a.size(); \ - if (a_len m_opa array_b.size()) { \ - _RETURN(m_ret_s); \ - } else { \ - \ - const m_type *ra = array_a.ptr(); \ - const m_type *rb = array_b.ptr(); \ - \ - for (int i = 0; i < a_len; i++) { \ - if (ra[i] m_opb rb[i]) \ - _RETURN(m_ret_f); \ - } \ - \ - _RETURN(m_ret_def); \ - } - -#define DEFAULT_OP_ARRAY_ADD(m_prefix, m_op_name, m_name, m_type) \ - CASE_TYPE(m_prefix, m_op_name, m_name) { \ - if (p_a.type != p_b.type) \ - _RETURN_FAIL; \ - \ - const Vector<m_type> &array_a = PackedArrayRef<m_type>::get_array(p_a._data.packed_array); \ - const Vector<m_type> &array_b = PackedArrayRef<m_type>::get_array(p_b._data.packed_array); \ - Vector<m_type> sum = array_a; \ - sum.append_array(array_b); \ - _RETURN(sum); \ - } - -void Variant::evaluate(const Operator &p_op, const Variant &p_a, - const Variant &p_b, Variant &r_ret, bool &r_valid) { - - CASES(math); - r_valid = true; - - SWITCH(math, p_op, p_a.type) { - SWITCH_OP(math, OP_EQUAL, p_a.type) { - CASE_TYPE(math, OP_EQUAL, NIL) { - if (p_b.type == NIL) _RETURN(true); - if (p_b.type == OBJECT) - _RETURN(p_b._get_obj().obj == nullptr); - - _RETURN(false); - } - - CASE_TYPE(math, OP_EQUAL, BOOL) { - if (p_b.type != BOOL) { - if (p_b.type == NIL) - _RETURN(false); - _RETURN_FAIL; - } - - _RETURN(p_a._data._bool == p_b._data._bool); - } - - CASE_TYPE(math, OP_EQUAL, OBJECT) { - if (p_b.type == OBJECT) - _RETURN((p_a._get_obj().obj == p_b._get_obj().obj)); - if (p_b.type == NIL) - _RETURN(p_a._get_obj().obj == nullptr); - - _RETURN_FAIL; - } - - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, CALLABLE, ==, Callable); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, SIGNAL, ==, Signal); - - CASE_TYPE(math, OP_EQUAL, DICTIONARY) { - if (p_b.type != DICTIONARY) { - if (p_b.type == NIL) - _RETURN(false); - _RETURN_FAIL; - } - - const Dictionary *arr_a = reinterpret_cast<const Dictionary *>(p_a._data._mem); - const Dictionary *arr_b = reinterpret_cast<const Dictionary *>(p_b._data._mem); - - _RETURN(*arr_a == *arr_b); - } - - CASE_TYPE(math, OP_EQUAL, ARRAY) { - if (p_b.type != ARRAY) { - if (p_b.type == NIL) - _RETURN(false); - _RETURN_FAIL; - } - const Array *arr_a = reinterpret_cast<const Array *>(p_a._data._mem); - const Array *arr_b = reinterpret_cast<const Array *>(p_b._data._mem); - - int l = arr_a->size(); - if (arr_b->size() != l) - _RETURN(false); - for (int i = 0; i < l; i++) { - if (!((*arr_a)[i] == (*arr_b)[i])) { - _RETURN(false); - } - } - - _RETURN(true); - } - - DEFAULT_OP_NUM_NULL(math, OP_EQUAL, INT, ==, _int); - DEFAULT_OP_NUM_NULL(math, OP_EQUAL, FLOAT, ==, _float); - DEFAULT_OP_STR_NULL(math, OP_EQUAL, STRING, ==, String); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR2, ==, Vector2); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR2I, ==, Vector2i); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, RECT2, ==, Rect2); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, RECT2I, ==, Rect2i); - DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, TRANSFORM2D, ==, _transform2d); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR3, ==, Vector3); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR3I, ==, Vector3i); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, PLANE, ==, Plane); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, QUAT, ==, Quat); - DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, AABB, ==, _aabb); - DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, BASIS, ==, _basis); - DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, TRANSFORM, ==, _transform); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, COLOR, ==, Color); - DEFAULT_OP_STR_NULL_SN(math, OP_EQUAL, STRING_NAME, ==, StringName); - DEFAULT_OP_STR_NULL_NP(math, OP_EQUAL, NODE_PATH, ==, NodePath); - DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, _RID, ==, RID); - - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_BYTE_ARRAY, uint8_t); - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_INT32_ARRAY, int32_t); - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_INT64_ARRAY, int64_t); - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_FLOAT32_ARRAY, float); - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_FLOAT64_ARRAY, double); - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_STRING_ARRAY, String); - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_VECTOR2_ARRAY, Vector2); - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_VECTOR3_ARRAY, Vector3); - DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_COLOR_ARRAY, Color); - } - - SWITCH_OP(math, OP_NOT_EQUAL, p_a.type) { - CASE_TYPE(math, OP_NOT_EQUAL, NIL) { - if (p_b.type == NIL) _RETURN(false); - if (p_b.type == OBJECT) - _RETURN(p_b._get_obj().obj != nullptr); - - _RETURN(true); - } - - CASE_TYPE(math, OP_NOT_EQUAL, BOOL) { - if (p_b.type != BOOL) { - if (p_b.type == NIL) - _RETURN(true); - - _RETURN_FAIL; - } - - _RETURN(p_a._data._bool != p_b._data._bool); - } - - CASE_TYPE(math, OP_NOT_EQUAL, OBJECT) { - if (p_b.type == OBJECT) - _RETURN((p_a._get_obj().obj != p_b._get_obj().obj)); - if (p_b.type == NIL) - _RETURN(p_a._get_obj().obj != nullptr); - - _RETURN_FAIL; - } - - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, CALLABLE, !=, Callable); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, SIGNAL, !=, Signal); - - CASE_TYPE(math, OP_NOT_EQUAL, DICTIONARY) { - if (p_b.type != DICTIONARY) { - if (p_b.type == NIL) - _RETURN(true); - _RETURN_FAIL; - } - - const Dictionary *arr_a = reinterpret_cast<const Dictionary *>(p_a._data._mem); - const Dictionary *arr_b = reinterpret_cast<const Dictionary *>(p_b._data._mem); - - _RETURN(*arr_a != *arr_b); - } - - CASE_TYPE(math, OP_NOT_EQUAL, ARRAY) { - if (p_b.type != ARRAY) { - if (p_b.type == NIL) - _RETURN(true); - - _RETURN_FAIL; - } - - const Array *arr_a = reinterpret_cast<const Array *>(p_a._data._mem); - const Array *arr_b = reinterpret_cast<const Array *>(p_b._data._mem); - - int l = arr_a->size(); - if (arr_b->size() != l) - _RETURN(true); - for (int i = 0; i < l; i++) { - if (((*arr_a)[i] != (*arr_b)[i])) { - _RETURN(true); - } - } - - _RETURN(false); - } - - DEFAULT_OP_NUM_NULL(math, OP_NOT_EQUAL, INT, !=, _int); - DEFAULT_OP_NUM_NULL(math, OP_NOT_EQUAL, FLOAT, !=, _float); - DEFAULT_OP_STR_NULL(math, OP_NOT_EQUAL, STRING, !=, String); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR2, !=, Vector2); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR2I, !=, Vector2i); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, RECT2, !=, Rect2); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, RECT2I, !=, Rect2i); - DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, TRANSFORM2D, !=, _transform2d); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR3, !=, Vector3); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR3I, !=, Vector3i); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, PLANE, !=, Plane); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, QUAT, !=, Quat); - DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, AABB, !=, _aabb); - DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, BASIS, !=, _basis); - DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, TRANSFORM, !=, _transform); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, COLOR, !=, Color); - DEFAULT_OP_STR_NULL_SN(math, OP_NOT_EQUAL, STRING_NAME, !=, StringName); - DEFAULT_OP_STR_NULL_NP(math, OP_NOT_EQUAL, NODE_PATH, !=, NodePath); - DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, _RID, !=, RID); - - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_BYTE_ARRAY, uint8_t); - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_INT32_ARRAY, int32_t); - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_INT64_ARRAY, int64_t); - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_FLOAT32_ARRAY, float); - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_FLOAT64_ARRAY, double); - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_STRING_ARRAY, String); - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_VECTOR2_ARRAY, Vector2); - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_VECTOR3_ARRAY, Vector3); - DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_COLOR_ARRAY, Color); - } - - SWITCH_OP(math, OP_LESS, p_a.type) { - CASE_TYPE(math, OP_LESS, BOOL) { - if (p_b.type != BOOL) - _RETURN_FAIL; - - if (p_a._data._bool == p_b._data._bool) - _RETURN(false); - - if (p_a._data._bool && !p_b._data._bool) - _RETURN(false); - - _RETURN(true); - } - - CASE_TYPE(math, OP_LESS, OBJECT) { - if (p_b.type != OBJECT) - _RETURN_FAIL; - _RETURN((p_a._get_obj().obj < p_b._get_obj().obj)); - } - - DEFAULT_OP_LOCALMEM_NULL(math, OP_LESS, CALLABLE, <, Callable); - DEFAULT_OP_LOCALMEM_NULL(math, OP_LESS, SIGNAL, <, Signal); - - CASE_TYPE(math, OP_LESS, ARRAY) { - if (p_b.type != ARRAY) - _RETURN_FAIL; - - const Array *arr_a = reinterpret_cast<const Array *>(p_a._data._mem); - const Array *arr_b = reinterpret_cast<const Array *>(p_b._data._mem); - - int l = arr_a->size(); - if (arr_b->size() < l) - _RETURN(false); - for (int i = 0; i < l; i++) { - if (!((*arr_a)[i] < (*arr_b)[i])) { - _RETURN(true); - } - } - - _RETURN(false); - } - - DEFAULT_OP_NUM(math, OP_LESS, INT, <, _int); - DEFAULT_OP_NUM(math, OP_LESS, FLOAT, <, _float); - DEFAULT_OP_STR(math, OP_LESS, STRING, <, String); - DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR2, <, Vector2); - DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR2I, <, Vector2i); - DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR3, <, Vector3); - DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR3I, <, Vector3i); - DEFAULT_OP_LOCALMEM(math, OP_LESS, _RID, <, RID); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_BYTE_ARRAY, uint8_t); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_INT32_ARRAY, int32_t); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_INT64_ARRAY, int64_t); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_FLOAT32_ARRAY, float); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_FLOAT64_ARRAY, double); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_STRING_ARRAY, String); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_VECTOR2_ARRAY, Vector3); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_VECTOR3_ARRAY, Vector3); - DEFAULT_OP_ARRAY_LT(math, OP_LESS, PACKED_COLOR_ARRAY, Color); - - CASE_TYPE(math, OP_LESS, NIL) - CASE_TYPE(math, OP_LESS, RECT2) - CASE_TYPE(math, OP_LESS, RECT2I) - CASE_TYPE(math, OP_LESS, TRANSFORM2D) - CASE_TYPE(math, OP_LESS, PLANE) - CASE_TYPE(math, OP_LESS, QUAT) - CASE_TYPE(math, OP_LESS, AABB) - CASE_TYPE(math, OP_LESS, BASIS) - CASE_TYPE(math, OP_LESS, TRANSFORM) - CASE_TYPE(math, OP_LESS, COLOR) - CASE_TYPE(math, OP_LESS, STRING_NAME) - CASE_TYPE(math, OP_LESS, NODE_PATH) - CASE_TYPE(math, OP_LESS, DICTIONARY) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_LESS_EQUAL, p_a.type) { - CASE_TYPE(math, OP_LESS_EQUAL, OBJECT) { - if (p_b.type != OBJECT) - _RETURN_FAIL; - _RETURN((p_a._get_obj().obj <= p_b._get_obj().obj)); - } - - DEFAULT_OP_NUM(math, OP_LESS_EQUAL, INT, <=, _int); - DEFAULT_OP_NUM(math, OP_LESS_EQUAL, FLOAT, <=, _float); - DEFAULT_OP_STR(math, OP_LESS_EQUAL, STRING, <=, String); - DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR2, <=, Vector2); - DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR2I, <=, Vector2i); - DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR3, <=, Vector3); - DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR3I, <=, Vector3i); - DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, _RID, <=, RID); - - CASE_TYPE(math, OP_LESS_EQUAL, NIL) - CASE_TYPE(math, OP_LESS_EQUAL, BOOL) - CASE_TYPE(math, OP_LESS_EQUAL, RECT2) - CASE_TYPE(math, OP_LESS_EQUAL, RECT2I) - CASE_TYPE(math, OP_LESS_EQUAL, TRANSFORM2D) - CASE_TYPE(math, OP_LESS_EQUAL, PLANE) - CASE_TYPE(math, OP_LESS_EQUAL, QUAT) - CASE_TYPE(math, OP_LESS_EQUAL, AABB) - CASE_TYPE(math, OP_LESS_EQUAL, BASIS) - CASE_TYPE(math, OP_LESS_EQUAL, TRANSFORM) - CASE_TYPE(math, OP_LESS_EQUAL, COLOR) - CASE_TYPE(math, OP_LESS_EQUAL, STRING_NAME) - CASE_TYPE(math, OP_LESS_EQUAL, NODE_PATH) - CASE_TYPE(math, OP_LESS_EQUAL, CALLABLE) - CASE_TYPE(math, OP_LESS_EQUAL, SIGNAL) - - CASE_TYPE(math, OP_LESS_EQUAL, DICTIONARY) - CASE_TYPE(math, OP_LESS_EQUAL, ARRAY) - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_BYTE_ARRAY); - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_INT32_ARRAY); - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_INT64_ARRAY); - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_FLOAT32_ARRAY); - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_FLOAT64_ARRAY); - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_STRING_ARRAY); - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_VECTOR2_ARRAY); - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_VECTOR3_ARRAY); - CASE_TYPE(math, OP_LESS_EQUAL, PACKED_COLOR_ARRAY); - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_GREATER, p_a.type) { - CASE_TYPE(math, OP_GREATER, BOOL) { - if (p_b.type != BOOL) - _RETURN_FAIL; - - if (p_a._data._bool == p_b._data._bool) - _RETURN(false); - - if (!p_a._data._bool && p_b._data._bool) - _RETURN(false); - - _RETURN(true); - } - - CASE_TYPE(math, OP_GREATER, OBJECT) { - if (p_b.type != OBJECT) - _RETURN_FAIL; - _RETURN((p_a._get_obj().obj > p_b._get_obj().obj)); - } - - CASE_TYPE(math, OP_GREATER, ARRAY) { - if (p_b.type != ARRAY) - _RETURN_FAIL; - - const Array *arr_a = reinterpret_cast<const Array *>(p_a._data._mem); - const Array *arr_b = reinterpret_cast<const Array *>(p_b._data._mem); - - int l = arr_a->size(); - if (arr_b->size() > l) - _RETURN(false); - for (int i = 0; i < l; i++) { - if (((*arr_a)[i] < (*arr_b)[i])) { - _RETURN(false); - } - } - - _RETURN(true); - } - - DEFAULT_OP_NUM(math, OP_GREATER, INT, >, _int); - DEFAULT_OP_NUM(math, OP_GREATER, FLOAT, >, _float); - DEFAULT_OP_STR_REV(math, OP_GREATER, STRING, <, String); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR2, <, Vector2); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR2I, <, Vector2i); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR3, <, Vector3); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR3I, <, Vector3i); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, _RID, <, RID); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_BYTE_ARRAY, uint8_t); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_INT32_ARRAY, int32_t); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_INT64_ARRAY, int64_t); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_FLOAT32_ARRAY, float); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_FLOAT64_ARRAY, double); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_STRING_ARRAY, String); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_VECTOR2_ARRAY, Vector3); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_VECTOR3_ARRAY, Vector3); - DEFAULT_OP_ARRAY_GT(math, OP_GREATER, PACKED_COLOR_ARRAY, Color); - - CASE_TYPE(math, OP_GREATER, NIL) - CASE_TYPE(math, OP_GREATER, RECT2) - CASE_TYPE(math, OP_GREATER, RECT2I) - CASE_TYPE(math, OP_GREATER, TRANSFORM2D) - CASE_TYPE(math, OP_GREATER, PLANE) - CASE_TYPE(math, OP_GREATER, QUAT) - CASE_TYPE(math, OP_GREATER, AABB) - CASE_TYPE(math, OP_GREATER, BASIS) - CASE_TYPE(math, OP_GREATER, TRANSFORM) - CASE_TYPE(math, OP_GREATER, COLOR) - CASE_TYPE(math, OP_GREATER, STRING_NAME) - CASE_TYPE(math, OP_GREATER, NODE_PATH) - CASE_TYPE(math, OP_GREATER, DICTIONARY) - CASE_TYPE(math, OP_GREATER, CALLABLE) - CASE_TYPE(math, OP_GREATER, SIGNAL) - - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_GREATER_EQUAL, p_a.type) { - CASE_TYPE(math, OP_GREATER_EQUAL, OBJECT) { - if (p_b.type != OBJECT) - _RETURN_FAIL; - _RETURN((p_a._get_obj().obj >= p_b._get_obj().obj)); - } - - DEFAULT_OP_NUM(math, OP_GREATER_EQUAL, INT, >=, _int); - DEFAULT_OP_NUM(math, OP_GREATER_EQUAL, FLOAT, >=, _float); - DEFAULT_OP_STR_REV(math, OP_GREATER_EQUAL, STRING, <=, String); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR2, <=, Vector2); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR2I, <=, Vector2i); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR3, <=, Vector3); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR3I, <=, Vector3i); - DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, _RID, <=, RID); - - CASE_TYPE(math, OP_GREATER_EQUAL, NIL) - CASE_TYPE(math, OP_GREATER_EQUAL, BOOL) - CASE_TYPE(math, OP_GREATER_EQUAL, RECT2) - CASE_TYPE(math, OP_GREATER_EQUAL, RECT2I) - CASE_TYPE(math, OP_GREATER_EQUAL, TRANSFORM2D) - CASE_TYPE(math, OP_GREATER_EQUAL, PLANE) - CASE_TYPE(math, OP_GREATER_EQUAL, QUAT) - CASE_TYPE(math, OP_GREATER_EQUAL, AABB) - CASE_TYPE(math, OP_GREATER_EQUAL, BASIS) - CASE_TYPE(math, OP_GREATER_EQUAL, TRANSFORM) - CASE_TYPE(math, OP_GREATER_EQUAL, COLOR) - CASE_TYPE(math, OP_GREATER_EQUAL, STRING_NAME) - CASE_TYPE(math, OP_GREATER_EQUAL, NODE_PATH) - CASE_TYPE(math, OP_GREATER_EQUAL, CALLABLE) - CASE_TYPE(math, OP_GREATER_EQUAL, SIGNAL) - - CASE_TYPE(math, OP_GREATER_EQUAL, DICTIONARY) - CASE_TYPE(math, OP_GREATER_EQUAL, ARRAY) - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_BYTE_ARRAY); - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_INT32_ARRAY); - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_INT64_ARRAY); - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_FLOAT32_ARRAY); - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_FLOAT64_ARRAY); - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_STRING_ARRAY); - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_VECTOR2_ARRAY); - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_VECTOR3_ARRAY); - CASE_TYPE(math, OP_GREATER_EQUAL, PACKED_COLOR_ARRAY); - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_ADD, p_a.type) { - CASE_TYPE(math, OP_ADD, ARRAY) { - if (p_a.type != p_b.type) - _RETURN_FAIL; - - const Array &array_a = *reinterpret_cast<const Array *>(p_a._data._mem); - const Array &array_b = *reinterpret_cast<const Array *>(p_b._data._mem); - Array sum; - int asize = array_a.size(); - int bsize = array_b.size(); - sum.resize(asize + bsize); - for (int i = 0; i < asize; i++) - sum[i] = array_a[i]; - for (int i = 0; i < bsize; i++) - sum[i + asize] = array_b[i]; - _RETURN(sum); - } - - DEFAULT_OP_NUM(math, OP_ADD, INT, +, _int); - DEFAULT_OP_NUM(math, OP_ADD, FLOAT, +, _float); - DEFAULT_OP_STR(math, OP_ADD, STRING, +, String); - DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR2, +, Vector2); - DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR2I, +, Vector2i); - DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR3, +, Vector3); - DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR3I, +, Vector3i); - DEFAULT_OP_LOCALMEM(math, OP_ADD, QUAT, +, Quat); - DEFAULT_OP_LOCALMEM(math, OP_ADD, COLOR, +, Color); - - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_BYTE_ARRAY, uint8_t); - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_INT32_ARRAY, int32_t); - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_INT64_ARRAY, int64_t); - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_FLOAT32_ARRAY, float); - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_FLOAT64_ARRAY, double); - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_STRING_ARRAY, String); - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_VECTOR2_ARRAY, Vector2); - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_VECTOR3_ARRAY, Vector3); - DEFAULT_OP_ARRAY_ADD(math, OP_ADD, PACKED_COLOR_ARRAY, Color); - - CASE_TYPE(math, OP_ADD, NIL) - CASE_TYPE(math, OP_ADD, BOOL) - CASE_TYPE(math, OP_ADD, RECT2) - CASE_TYPE(math, OP_ADD, RECT2I) - CASE_TYPE(math, OP_ADD, TRANSFORM2D) - CASE_TYPE(math, OP_ADD, PLANE) - CASE_TYPE(math, OP_ADD, AABB) - CASE_TYPE(math, OP_ADD, BASIS) - CASE_TYPE(math, OP_ADD, TRANSFORM) - CASE_TYPE(math, OP_ADD, STRING_NAME) - CASE_TYPE(math, OP_ADD, NODE_PATH) - CASE_TYPE(math, OP_ADD, _RID) - CASE_TYPE(math, OP_ADD, OBJECT) - CASE_TYPE(math, OP_ADD, CALLABLE) - CASE_TYPE(math, OP_ADD, SIGNAL) - - CASE_TYPE(math, OP_ADD, DICTIONARY) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_SUBTRACT, p_a.type) { - DEFAULT_OP_NUM(math, OP_SUBTRACT, INT, -, _int); - DEFAULT_OP_NUM(math, OP_SUBTRACT, FLOAT, -, _float); - DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR2, -, Vector2); - DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR2I, -, Vector2i); - DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR3, -, Vector3); - DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR3I, -, Vector3i); - DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, QUAT, -, Quat); - DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, COLOR, -, Color); - - CASE_TYPE(math, OP_SUBTRACT, NIL) - CASE_TYPE(math, OP_SUBTRACT, BOOL) - CASE_TYPE(math, OP_SUBTRACT, STRING) - CASE_TYPE(math, OP_SUBTRACT, RECT2) - CASE_TYPE(math, OP_SUBTRACT, RECT2I) - CASE_TYPE(math, OP_SUBTRACT, TRANSFORM2D) - CASE_TYPE(math, OP_SUBTRACT, PLANE) - CASE_TYPE(math, OP_SUBTRACT, AABB) - CASE_TYPE(math, OP_SUBTRACT, BASIS) - CASE_TYPE(math, OP_SUBTRACT, TRANSFORM) - CASE_TYPE(math, OP_SUBTRACT, STRING_NAME) - CASE_TYPE(math, OP_SUBTRACT, NODE_PATH) - CASE_TYPE(math, OP_SUBTRACT, _RID) - CASE_TYPE(math, OP_SUBTRACT, OBJECT) - CASE_TYPE(math, OP_SUBTRACT, CALLABLE) - CASE_TYPE(math, OP_SUBTRACT, SIGNAL) - - CASE_TYPE(math, OP_SUBTRACT, DICTIONARY) - CASE_TYPE(math, OP_SUBTRACT, ARRAY) - CASE_TYPE(math, OP_SUBTRACT, PACKED_BYTE_ARRAY); - CASE_TYPE(math, OP_SUBTRACT, PACKED_INT32_ARRAY); - CASE_TYPE(math, OP_SUBTRACT, PACKED_INT64_ARRAY); - CASE_TYPE(math, OP_SUBTRACT, PACKED_FLOAT32_ARRAY); - CASE_TYPE(math, OP_SUBTRACT, PACKED_FLOAT64_ARRAY); - CASE_TYPE(math, OP_SUBTRACT, PACKED_STRING_ARRAY); - CASE_TYPE(math, OP_SUBTRACT, PACKED_VECTOR2_ARRAY); - CASE_TYPE(math, OP_SUBTRACT, PACKED_VECTOR3_ARRAY); - CASE_TYPE(math, OP_SUBTRACT, PACKED_COLOR_ARRAY); - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_MULTIPLY, p_a.type) { - CASE_TYPE(math, OP_MULTIPLY, TRANSFORM2D) { - switch (p_b.type) { - case TRANSFORM2D: { - _RETURN(*p_a._data._transform2d * *p_b._data._transform2d); - } - case VECTOR2: { - _RETURN(p_a._data._transform2d->xform(*(const Vector2 *)p_b._data._mem)); - } - default: _RETURN_FAIL; - } - } - - CASE_TYPE(math, OP_MULTIPLY, QUAT) { - switch (p_b.type) { - case VECTOR3: { - _RETURN(reinterpret_cast<const Quat *>(p_a._data._mem)->xform(*(const Vector3 *)p_b._data._mem)); - } - case QUAT: { - _RETURN(*reinterpret_cast<const Quat *>(p_a._data._mem) * *reinterpret_cast<const Quat *>(p_b._data._mem)); - } - case FLOAT: { - _RETURN(*reinterpret_cast<const Quat *>(p_a._data._mem) * p_b._data._float); - } - default: _RETURN_FAIL; - } - } - - CASE_TYPE(math, OP_MULTIPLY, BASIS) { - switch (p_b.type) { - case VECTOR3: { - _RETURN(p_a._data._basis->xform(*(const Vector3 *)p_b._data._mem)); - } - case BASIS: { - _RETURN(*p_a._data._basis * *p_b._data._basis); - } - default: _RETURN_FAIL; - } - } - - CASE_TYPE(math, OP_MULTIPLY, TRANSFORM) { - switch (p_b.type) { - case VECTOR3: { - _RETURN(p_a._data._transform->xform(*(const Vector3 *)p_b._data._mem)); - } - case TRANSFORM: { - _RETURN(*p_a._data._transform * *p_b._data._transform); - } - default: _RETURN_FAIL; - } - } - - DEFAULT_OP_NUM_VEC(math, OP_MULTIPLY, INT, *, _int); - DEFAULT_OP_NUM_VEC(math, OP_MULTIPLY, FLOAT, *, _float); - DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR2, *, Vector2); - DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR2I, *, Vector2i); - DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR3, *, Vector3); - DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR3I, *, Vector3i); - DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, COLOR, *, Color); - - CASE_TYPE(math, OP_MULTIPLY, NIL) - CASE_TYPE(math, OP_MULTIPLY, BOOL) - CASE_TYPE(math, OP_MULTIPLY, STRING) - CASE_TYPE(math, OP_MULTIPLY, RECT2) - CASE_TYPE(math, OP_MULTIPLY, RECT2I) - CASE_TYPE(math, OP_MULTIPLY, PLANE) - CASE_TYPE(math, OP_MULTIPLY, AABB) - CASE_TYPE(math, OP_MULTIPLY, STRING_NAME) - CASE_TYPE(math, OP_MULTIPLY, NODE_PATH) - CASE_TYPE(math, OP_MULTIPLY, _RID) - CASE_TYPE(math, OP_MULTIPLY, OBJECT) - CASE_TYPE(math, OP_MULTIPLY, CALLABLE) - CASE_TYPE(math, OP_MULTIPLY, SIGNAL) - - CASE_TYPE(math, OP_MULTIPLY, DICTIONARY) - CASE_TYPE(math, OP_MULTIPLY, ARRAY) - CASE_TYPE(math, OP_MULTIPLY, PACKED_BYTE_ARRAY); - CASE_TYPE(math, OP_MULTIPLY, PACKED_INT32_ARRAY); - CASE_TYPE(math, OP_MULTIPLY, PACKED_INT64_ARRAY); - CASE_TYPE(math, OP_MULTIPLY, PACKED_FLOAT32_ARRAY); - CASE_TYPE(math, OP_MULTIPLY, PACKED_FLOAT64_ARRAY); - CASE_TYPE(math, OP_MULTIPLY, PACKED_STRING_ARRAY); - CASE_TYPE(math, OP_MULTIPLY, PACKED_VECTOR2_ARRAY); - CASE_TYPE(math, OP_MULTIPLY, PACKED_VECTOR3_ARRAY); - CASE_TYPE(math, OP_MULTIPLY, PACKED_COLOR_ARRAY); - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_DIVIDE, p_a.type) { - CASE_TYPE(math, OP_DIVIDE, QUAT) { - if (p_b.type != FLOAT) - _RETURN_FAIL; -#ifdef DEBUG_ENABLED - if (p_b._data._float == 0) { - r_valid = false; - _RETURN("Division By Zero"); - } -#endif - _RETURN(*reinterpret_cast<const Quat *>(p_a._data._mem) / p_b._data._float); - } - - DEFAULT_OP_NUM_DIV(math, OP_DIVIDE, INT, _int); - DEFAULT_OP_NUM_DIV(math, OP_DIVIDE, FLOAT, _float); - DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR2, /, Vector2); - DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR2I, /, Vector2i); - DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR3, /, Vector3); - DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR3I, /, Vector3i); - DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, COLOR, /, Color); - - CASE_TYPE(math, OP_DIVIDE, NIL) - CASE_TYPE(math, OP_DIVIDE, BOOL) - CASE_TYPE(math, OP_DIVIDE, STRING) - CASE_TYPE(math, OP_DIVIDE, RECT2) - CASE_TYPE(math, OP_DIVIDE, RECT2I) - CASE_TYPE(math, OP_DIVIDE, TRANSFORM2D) - CASE_TYPE(math, OP_DIVIDE, PLANE) - CASE_TYPE(math, OP_DIVIDE, AABB) - CASE_TYPE(math, OP_DIVIDE, BASIS) - CASE_TYPE(math, OP_DIVIDE, TRANSFORM) - CASE_TYPE(math, OP_DIVIDE, STRING_NAME) - CASE_TYPE(math, OP_DIVIDE, NODE_PATH) - CASE_TYPE(math, OP_DIVIDE, _RID) - CASE_TYPE(math, OP_DIVIDE, OBJECT) - CASE_TYPE(math, OP_DIVIDE, CALLABLE) - CASE_TYPE(math, OP_DIVIDE, SIGNAL) - - CASE_TYPE(math, OP_DIVIDE, DICTIONARY) - CASE_TYPE(math, OP_DIVIDE, ARRAY) - CASE_TYPE(math, OP_DIVIDE, PACKED_BYTE_ARRAY); - CASE_TYPE(math, OP_DIVIDE, PACKED_INT32_ARRAY); - CASE_TYPE(math, OP_DIVIDE, PACKED_INT64_ARRAY); - CASE_TYPE(math, OP_DIVIDE, PACKED_FLOAT32_ARRAY); - CASE_TYPE(math, OP_DIVIDE, PACKED_FLOAT64_ARRAY); - CASE_TYPE(math, OP_DIVIDE, PACKED_STRING_ARRAY); - CASE_TYPE(math, OP_DIVIDE, PACKED_VECTOR2_ARRAY); - CASE_TYPE(math, OP_DIVIDE, PACKED_VECTOR3_ARRAY); - CASE_TYPE(math, OP_DIVIDE, PACKED_COLOR_ARRAY); - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_POSITIVE, p_a.type) { - DEFAULT_OP_NUM_POS(math, OP_POSITIVE, INT, _int); - DEFAULT_OP_NUM_POS(math, OP_POSITIVE, FLOAT, _float); - DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR3, Vector3); - DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR3I, Vector3i); - DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, PLANE, Plane); - DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, QUAT, Quat); - DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR2, Vector2); - DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR2I, Vector2i); - - CASE_TYPE(math, OP_POSITIVE, NIL) - CASE_TYPE(math, OP_POSITIVE, BOOL) - CASE_TYPE(math, OP_POSITIVE, STRING) - CASE_TYPE(math, OP_POSITIVE, RECT2) - CASE_TYPE(math, OP_POSITIVE, RECT2I) - CASE_TYPE(math, OP_POSITIVE, TRANSFORM2D) - CASE_TYPE(math, OP_POSITIVE, AABB) - CASE_TYPE(math, OP_POSITIVE, BASIS) - CASE_TYPE(math, OP_POSITIVE, TRANSFORM) - CASE_TYPE(math, OP_POSITIVE, COLOR) - CASE_TYPE(math, OP_POSITIVE, STRING_NAME) - CASE_TYPE(math, OP_POSITIVE, NODE_PATH) - CASE_TYPE(math, OP_POSITIVE, _RID) - CASE_TYPE(math, OP_POSITIVE, OBJECT) - CASE_TYPE(math, OP_POSITIVE, CALLABLE) - CASE_TYPE(math, OP_POSITIVE, SIGNAL) - - CASE_TYPE(math, OP_POSITIVE, DICTIONARY) - CASE_TYPE(math, OP_POSITIVE, ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_BYTE_ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_INT32_ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_INT64_ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_FLOAT32_ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_FLOAT64_ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_STRING_ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_VECTOR2_ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_VECTOR3_ARRAY) - CASE_TYPE(math, OP_POSITIVE, PACKED_COLOR_ARRAY) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_NEGATE, p_a.type) { - DEFAULT_OP_NUM_NEG(math, OP_NEGATE, INT, _int); - DEFAULT_OP_NUM_NEG(math, OP_NEGATE, FLOAT, _float); - - DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR2, Vector2); - DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR2I, Vector2i); - DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR3, Vector3); - DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR3I, Vector3i); - DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, PLANE, Plane); - DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, QUAT, Quat); - DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, COLOR, Color); - - CASE_TYPE(math, OP_NEGATE, NIL) - CASE_TYPE(math, OP_NEGATE, BOOL) - CASE_TYPE(math, OP_NEGATE, STRING) - CASE_TYPE(math, OP_NEGATE, RECT2) - CASE_TYPE(math, OP_NEGATE, RECT2I) - CASE_TYPE(math, OP_NEGATE, TRANSFORM2D) - CASE_TYPE(math, OP_NEGATE, AABB) - CASE_TYPE(math, OP_NEGATE, BASIS) - CASE_TYPE(math, OP_NEGATE, TRANSFORM) - CASE_TYPE(math, OP_NEGATE, STRING_NAME) - CASE_TYPE(math, OP_NEGATE, NODE_PATH) - CASE_TYPE(math, OP_NEGATE, _RID) - CASE_TYPE(math, OP_NEGATE, OBJECT) - CASE_TYPE(math, OP_NEGATE, CALLABLE) - CASE_TYPE(math, OP_NEGATE, SIGNAL) - - CASE_TYPE(math, OP_NEGATE, DICTIONARY) - CASE_TYPE(math, OP_NEGATE, ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_BYTE_ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_INT32_ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_INT64_ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_FLOAT32_ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_FLOAT64_ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_STRING_ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_VECTOR2_ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_VECTOR3_ARRAY) - CASE_TYPE(math, OP_NEGATE, PACKED_COLOR_ARRAY) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_MODULE, p_a.type) { - CASE_TYPE(math, OP_MODULE, INT) { - if (p_b.type != INT) - _RETURN_FAIL; -#ifdef DEBUG_ENABLED - if (p_b._data._int == 0) { - r_valid = false; - _RETURN("Division By Zero"); - } -#endif - _RETURN(p_a._data._int % p_b._data._int); - } - - CASE_TYPE(math, OP_MODULE, STRING) { - const String *format = reinterpret_cast<const String *>(p_a._data._mem); - - String result; - bool error; - if (p_b.type == ARRAY) { - // e.g. "frog %s %d" % ["fish", 12] - const Array *args = reinterpret_cast<const Array *>(p_b._data._mem); - result = format->sprintf(*args, &error); - } else { - // e.g. "frog %d" % 12 - Array args; - args.push_back(p_b); - result = format->sprintf(args, &error); - } - r_valid = !error; - _RETURN(result); - } - - CASE_TYPE(math, OP_MODULE, NIL) - CASE_TYPE(math, OP_MODULE, BOOL) - CASE_TYPE(math, OP_MODULE, FLOAT) - CASE_TYPE(math, OP_MODULE, VECTOR2) - CASE_TYPE(math, OP_MODULE, VECTOR2I) - CASE_TYPE(math, OP_MODULE, RECT2) - CASE_TYPE(math, OP_MODULE, RECT2I) - CASE_TYPE(math, OP_MODULE, VECTOR3) - CASE_TYPE(math, OP_MODULE, VECTOR3I) - CASE_TYPE(math, OP_MODULE, TRANSFORM2D) - CASE_TYPE(math, OP_MODULE, PLANE) - CASE_TYPE(math, OP_MODULE, QUAT) - CASE_TYPE(math, OP_MODULE, AABB) - CASE_TYPE(math, OP_MODULE, BASIS) - CASE_TYPE(math, OP_MODULE, TRANSFORM) - CASE_TYPE(math, OP_MODULE, COLOR) - CASE_TYPE(math, OP_MODULE, STRING_NAME) - CASE_TYPE(math, OP_MODULE, NODE_PATH) - CASE_TYPE(math, OP_MODULE, _RID) - CASE_TYPE(math, OP_MODULE, OBJECT) - CASE_TYPE(math, OP_MODULE, CALLABLE) - CASE_TYPE(math, OP_MODULE, SIGNAL) - - CASE_TYPE(math, OP_MODULE, DICTIONARY) - CASE_TYPE(math, OP_MODULE, ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_BYTE_ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_INT32_ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_INT64_ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_FLOAT32_ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_FLOAT64_ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_STRING_ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_VECTOR2_ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_VECTOR3_ARRAY) - CASE_TYPE(math, OP_MODULE, PACKED_COLOR_ARRAY) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_STRING_CONCAT, p_a.type) { - CASE_TYPE_ALL(math, OP_STRING_CONCAT) - - _RETURN(p_a.operator String() + p_b.operator String()); - } - - SWITCH_OP(math, OP_SHIFT_LEFT, p_a.type) { - CASE_TYPE(math, OP_SHIFT_LEFT, INT) { - if (p_b.type != INT) - _RETURN_FAIL; - if (p_b._data._int < 0 || p_b._data._int >= 64) - _RETURN_FAIL; - _RETURN(p_a._data._int << p_b._data._int); - } - - CASE_TYPE_ALL_BUT_INT(math, OP_SHIFT_LEFT) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_SHIFT_RIGHT, p_a.type) { - CASE_TYPE(math, OP_SHIFT_RIGHT, INT) { - if (p_b.type != INT) - _RETURN_FAIL; - if (p_b._data._int < 0 || p_b._data._int >= 64) - _RETURN_FAIL; - _RETURN(p_a._data._int >> p_b._data._int); - } - - CASE_TYPE_ALL_BUT_INT(math, OP_SHIFT_RIGHT) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_BIT_AND, p_a.type) { - CASE_TYPE(math, OP_BIT_AND, INT) { - if (p_b.type != INT) - _RETURN_FAIL; - _RETURN(p_a._data._int & p_b._data._int); - } - - CASE_TYPE_ALL_BUT_INT(math, OP_BIT_AND) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_BIT_OR, p_a.type) { - CASE_TYPE(math, OP_BIT_OR, INT) { - if (p_b.type != INT) - _RETURN_FAIL; - _RETURN(p_a._data._int | p_b._data._int); - } - - CASE_TYPE_ALL_BUT_INT(math, OP_BIT_OR) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_BIT_XOR, p_a.type) { - CASE_TYPE(math, OP_BIT_XOR, INT) { - if (p_b.type != INT) - _RETURN_FAIL; - _RETURN(p_a._data._int ^ p_b._data._int); - } - - CASE_TYPE_ALL_BUT_INT(math, OP_BIT_XOR) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_BIT_NEGATE, p_a.type) { - CASE_TYPE(math, OP_BIT_NEGATE, INT) { - _RETURN(~p_a._data._int); - } - - CASE_TYPE_ALL_BUT_INT(math, OP_BIT_NEGATE) - _RETURN_FAIL; - } - - SWITCH_OP(math, OP_AND, p_a.type) { - CASE_TYPE_ALL(math, OP_AND) { - bool l = p_a.booleanize(); - bool r = p_b.booleanize(); - - _RETURN(l && r); - } - } - - SWITCH_OP(math, OP_OR, p_a.type) { - CASE_TYPE_ALL(math, OP_OR) { - bool l = p_a.booleanize(); - bool r = p_b.booleanize(); - - _RETURN(l || r); - } - } - - SWITCH_OP(math, OP_XOR, p_a.type) { - CASE_TYPE_ALL(math, OP_XOR) { - bool l = p_a.booleanize(); - bool r = p_b.booleanize(); - - _RETURN((l || r) && !(l && r)); - } - } - - SWITCH_OP(math, OP_NOT, p_a.type) { - CASE_TYPE_ALL(math, OP_NOT) { - bool l = p_a.booleanize(); - _RETURN(!l); - } - } - - SWITCH_OP(math, OP_IN, p_a.type) { - CASE_TYPE_ALL(math, OP_IN) - _RETURN(p_b.in(p_a, &r_valid)); - } - } -} - -void Variant::set_named(const StringName &p_index, const Variant &p_value, bool *r_valid) { - - bool valid = false; - switch (type) { - case VECTOR2: { - if (p_value.type == Variant::INT) { - Vector2 *v = reinterpret_cast<Vector2 *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._int; - valid = true; - } - } else if (p_value.type == Variant::FLOAT) { - Vector2 *v = reinterpret_cast<Vector2 *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._float; - valid = true; - } - } - - } break; - case VECTOR2I: { - if (p_value.type == Variant::INT) { - Vector2i *v = reinterpret_cast<Vector2i *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._int; - valid = true; - } - } else if (p_value.type == Variant::FLOAT) { - Vector2i *v = reinterpret_cast<Vector2i *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._float; - valid = true; - } - } - - } break; - case RECT2: { - - if (p_value.type == Variant::VECTOR2) { - Rect2 *v = reinterpret_cast<Rect2 *>(_data._mem); - //scalar name - if (p_index == CoreStringNames::singleton->position) { - v->position = *reinterpret_cast<const Vector2 *>(p_value._data._mem); - valid = true; - } else if (p_index == CoreStringNames::singleton->size) { - v->size = *reinterpret_cast<const Vector2 *>(p_value._data._mem); - valid = true; - } else if (p_index == CoreStringNames::singleton->end) { - v->size = *reinterpret_cast<const Vector2 *>(p_value._data._mem) - v->position; - valid = true; - } - } - } break; - case RECT2I: { - - if (p_value.type == Variant::VECTOR2I) { - Rect2i *v = reinterpret_cast<Rect2i *>(_data._mem); - //scalar name - if (p_index == CoreStringNames::singleton->position) { - v->position = *reinterpret_cast<const Vector2i *>(p_value._data._mem); - valid = true; - } else if (p_index == CoreStringNames::singleton->size) { - v->size = *reinterpret_cast<const Vector2i *>(p_value._data._mem); - valid = true; - } else if (p_index == CoreStringNames::singleton->end) { - v->size = *reinterpret_cast<const Vector2i *>(p_value._data._mem) - v->position; - valid = true; - } - } - } break; - case TRANSFORM2D: { - - if (p_value.type == Variant::VECTOR2) { - Transform2D *v = _data._transform2d; - if (p_index == CoreStringNames::singleton->x) { - v->elements[0] = *reinterpret_cast<const Vector2 *>(p_value._data._mem); - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->elements[1] = *reinterpret_cast<const Vector2 *>(p_value._data._mem); - valid = true; - } else if (p_index == CoreStringNames::singleton->origin) { - v->elements[2] = *reinterpret_cast<const Vector2 *>(p_value._data._mem); - valid = true; - } - } - - } break; - case VECTOR3: { - - if (p_value.type == Variant::INT) { - Vector3 *v = reinterpret_cast<Vector3 *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->z = p_value._data._int; - valid = true; - } - } else if (p_value.type == Variant::FLOAT) { - Vector3 *v = reinterpret_cast<Vector3 *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->z = p_value._data._float; - valid = true; - } - } - - } break; - case VECTOR3I: { - - if (p_value.type == Variant::INT) { - Vector3i *v = reinterpret_cast<Vector3i *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->z = p_value._data._int; - valid = true; - } - } else if (p_value.type == Variant::FLOAT) { - Vector3i *v = reinterpret_cast<Vector3i *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->z = p_value._data._float; - valid = true; - } - } - - } break; - case PLANE: { - - if (p_value.type == Variant::INT) { - Plane *v = reinterpret_cast<Plane *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->normal.x = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->normal.y = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->normal.z = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->d) { - v->d = p_value._data._int; - valid = true; - } - } else if (p_value.type == Variant::FLOAT) { - Plane *v = reinterpret_cast<Plane *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->normal.x = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->normal.y = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->normal.z = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->d) { - v->d = p_value._data._float; - valid = true; - } - - } else if (p_value.type == Variant::VECTOR3) { - Plane *v = reinterpret_cast<Plane *>(_data._mem); - if (p_index == CoreStringNames::singleton->normal) { - v->normal = *reinterpret_cast<const Vector3 *>(p_value._data._mem); - valid = true; - } - } - - } break; - case QUAT: { - - if (p_value.type == Variant::INT) { - Quat *v = reinterpret_cast<Quat *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->z = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->w) { - v->w = p_value._data._int; - valid = true; - } - } else if (p_value.type == Variant::FLOAT) { - Quat *v = reinterpret_cast<Quat *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - v->x = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->y = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->z = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->w) { - v->w = p_value._data._float; - valid = true; - } - } - - } break; - case AABB: { - - if (p_value.type == Variant::VECTOR3) { - ::AABB *v = _data._aabb; - //scalar name - if (p_index == CoreStringNames::singleton->position) { - v->position = *reinterpret_cast<const Vector3 *>(p_value._data._mem); - valid = true; - } else if (p_index == CoreStringNames::singleton->size) { - v->size = *reinterpret_cast<const Vector3 *>(p_value._data._mem); - valid = true; - } else if (p_index == CoreStringNames::singleton->end) { - v->size = *reinterpret_cast<const Vector3 *>(p_value._data._mem) - v->position; - valid = true; - } - } - } break; - case BASIS: { - - if (p_value.type == Variant::VECTOR3) { - Basis *v = _data._basis; - //scalar name - if (p_index == CoreStringNames::singleton->x) { - v->set_axis(0, *reinterpret_cast<const Vector3 *>(p_value._data._mem)); - valid = true; - } else if (p_index == CoreStringNames::singleton->y) { - v->set_axis(1, *reinterpret_cast<const Vector3 *>(p_value._data._mem)); - valid = true; - } else if (p_index == CoreStringNames::singleton->z) { - v->set_axis(2, *reinterpret_cast<const Vector3 *>(p_value._data._mem)); - valid = true; - } - } - } break; - case TRANSFORM: { - - if (p_value.type == Variant::BASIS && p_index == CoreStringNames::singleton->basis) { - _data._transform->basis = *p_value._data._basis; - valid = true; - } else if (p_value.type == Variant::VECTOR3 && p_index == CoreStringNames::singleton->origin) { - _data._transform->origin = *reinterpret_cast<const Vector3 *>(p_value._data._mem); - valid = true; - } - - } break; - case COLOR: { - - if (p_value.type == Variant::INT) { - Color *v = reinterpret_cast<Color *>(_data._mem); - if (p_index == CoreStringNames::singleton->r) { - v->r = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->g) { - v->g = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->b) { - v->b = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->a) { - v->a = p_value._data._int; - valid = true; - } else if (p_index == CoreStringNames::singleton->r8) { - v->r = p_value._data._int / 255.0; - valid = true; - } else if (p_index == CoreStringNames::singleton->g8) { - v->g = p_value._data._int / 255.0; - valid = true; - } else if (p_index == CoreStringNames::singleton->b8) { - v->b = p_value._data._int / 255.0; - valid = true; - } else if (p_index == CoreStringNames::singleton->a8) { - v->a = p_value._data._int / 255.0; - valid = true; - } else if (p_index == CoreStringNames::singleton->h) { - v->set_hsv(p_value._data._int, v->get_s(), v->get_v(), v->a); - valid = true; - } else if (p_index == CoreStringNames::singleton->s) { - v->set_hsv(v->get_h(), p_value._data._int, v->get_v(), v->a); - valid = true; - } else if (p_index == CoreStringNames::singleton->v) { - v->set_hsv(v->get_h(), v->get_v(), p_value._data._int, v->a); - valid = true; - } - } else if (p_value.type == Variant::FLOAT) { - Color *v = reinterpret_cast<Color *>(_data._mem); - if (p_index == CoreStringNames::singleton->r) { - v->r = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->g) { - v->g = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->b) { - v->b = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->a) { - v->a = p_value._data._float; - valid = true; - } else if (p_index == CoreStringNames::singleton->r8) { - v->r = p_value._data._float / 255.0; - valid = true; - } else if (p_index == CoreStringNames::singleton->g8) { - v->g = p_value._data._float / 255.0; - valid = true; - } else if (p_index == CoreStringNames::singleton->b8) { - v->b = p_value._data._float / 255.0; - valid = true; - } else if (p_index == CoreStringNames::singleton->a8) { - v->a = p_value._data._float / 255.0; - valid = true; - } else if (p_index == CoreStringNames::singleton->h) { - v->set_hsv(p_value._data._float, v->get_s(), v->get_v(), v->a); - valid = true; - } else if (p_index == CoreStringNames::singleton->s) { - v->set_hsv(v->get_h(), p_value._data._float, v->get_v(), v->a); - valid = true; - } else if (p_index == CoreStringNames::singleton->v) { - v->set_hsv(v->get_h(), v->get_s(), p_value._data._float, v->a); - valid = true; - } - } - } break; - case OBJECT: { - -#ifdef DEBUG_ENABLED - if (!_get_obj().obj) { - break; - } else if (EngineDebugger::is_active() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - break; - } - -#endif - _get_obj().obj->set(p_index, p_value, &valid); - - } break; - default: { - set(p_index.operator String(), p_value, &valid); - } break; - } - - if (r_valid) { - *r_valid = valid; - } -} - -Variant Variant::get_named(const StringName &p_index, bool *r_valid) const { - - if (r_valid) { - *r_valid = true; - } - switch (type) { - case VECTOR2: { - const Vector2 *v = reinterpret_cast<const Vector2 *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - return v->x; - } else if (p_index == CoreStringNames::singleton->y) { - return v->y; - } - - } break; - case VECTOR2I: { - const Vector2i *v = reinterpret_cast<const Vector2i *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - return v->x; - } else if (p_index == CoreStringNames::singleton->y) { - return v->y; - } - - } break; - case RECT2: { - - const Rect2 *v = reinterpret_cast<const Rect2 *>(_data._mem); - //scalar name - if (p_index == CoreStringNames::singleton->position) { - return v->position; - } else if (p_index == CoreStringNames::singleton->size) { - return v->size; - } else if (p_index == CoreStringNames::singleton->end) { - return v->size + v->position; - } - } break; - case RECT2I: { - - const Rect2i *v = reinterpret_cast<const Rect2i *>(_data._mem); - //scalar name - if (p_index == CoreStringNames::singleton->position) { - return v->position; - } else if (p_index == CoreStringNames::singleton->size) { - return v->size; - } else if (p_index == CoreStringNames::singleton->end) { - return v->size + v->position; - } - } break; - case TRANSFORM2D: { - - const Transform2D *v = _data._transform2d; - if (p_index == CoreStringNames::singleton->x) { - return v->elements[0]; - } else if (p_index == CoreStringNames::singleton->y) { - return v->elements[1]; - } else if (p_index == CoreStringNames::singleton->origin) { - return v->elements[2]; - } - - } break; - case VECTOR3: { - - const Vector3 *v = reinterpret_cast<const Vector3 *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - return v->x; - } else if (p_index == CoreStringNames::singleton->y) { - return v->y; - } else if (p_index == CoreStringNames::singleton->z) { - return v->z; - } - - } break; - case VECTOR3I: { - - const Vector3i *v = reinterpret_cast<const Vector3i *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - return v->x; - } else if (p_index == CoreStringNames::singleton->y) { - return v->y; - } else if (p_index == CoreStringNames::singleton->z) { - return v->z; - } - - } break; - case PLANE: { - - const Plane *v = reinterpret_cast<const Plane *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - return v->normal.x; - } else if (p_index == CoreStringNames::singleton->y) { - return v->normal.y; - } else if (p_index == CoreStringNames::singleton->z) { - return v->normal.z; - } else if (p_index == CoreStringNames::singleton->d) { - return v->d; - } else if (p_index == CoreStringNames::singleton->normal) { - return v->normal; - } - - } break; - case QUAT: { - - const Quat *v = reinterpret_cast<const Quat *>(_data._mem); - if (p_index == CoreStringNames::singleton->x) { - return v->x; - } else if (p_index == CoreStringNames::singleton->y) { - return v->y; - } else if (p_index == CoreStringNames::singleton->z) { - return v->z; - } else if (p_index == CoreStringNames::singleton->w) { - return v->w; - } - - } break; - case AABB: { - - const ::AABB *v = _data._aabb; - //scalar name - if (p_index == CoreStringNames::singleton->position) { - return v->position; - } else if (p_index == CoreStringNames::singleton->size) { - return v->size; - } else if (p_index == CoreStringNames::singleton->end) { - return v->size + v->position; - } - } break; - case BASIS: { - - const Basis *v = _data._basis; - //scalar name - if (p_index == CoreStringNames::singleton->x) { - return v->get_axis(0); - } else if (p_index == CoreStringNames::singleton->y) { - return v->get_axis(1); - } else if (p_index == CoreStringNames::singleton->z) { - return v->get_axis(2); - } - - } break; - case TRANSFORM: { - - if (p_index == CoreStringNames::singleton->basis) { - return _data._transform->basis; - } else if (p_index == CoreStringNames::singleton->origin) { - return _data._transform->origin; - } - - } break; - case COLOR: { - - const Color *v = reinterpret_cast<const Color *>(_data._mem); - if (p_index == CoreStringNames::singleton->r) { - return v->r; - } else if (p_index == CoreStringNames::singleton->g) { - return v->g; - } else if (p_index == CoreStringNames::singleton->b) { - return v->b; - } else if (p_index == CoreStringNames::singleton->a) { - return v->a; - } else if (p_index == CoreStringNames::singleton->r8) { - return int(Math::round(v->r * 255.0)); - } else if (p_index == CoreStringNames::singleton->g8) { - return int(Math::round(v->g * 255.0)); - } else if (p_index == CoreStringNames::singleton->b8) { - return int(Math::round(v->b * 255.0)); - } else if (p_index == CoreStringNames::singleton->a8) { - return int(Math::round(v->a * 255.0)); - } else if (p_index == CoreStringNames::singleton->h) { - return v->get_h(); - } else if (p_index == CoreStringNames::singleton->s) { - return v->get_s(); - } else if (p_index == CoreStringNames::singleton->v) { - return v->get_v(); - } - } break; - case OBJECT: { - -#ifdef DEBUG_ENABLED - if (!_get_obj().obj) { - if (r_valid) - *r_valid = false; - return "Instance base is null."; - } else { - - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - if (r_valid) - *r_valid = false; - return "Attempted use of stray pointer object."; - } - } - -#endif - - return _get_obj().obj->get(p_index, r_valid); - - } break; - default: { - return get(p_index.operator String(), r_valid); - } - } - - if (r_valid) { - *r_valid = false; - } - return Variant(); -} - -#define DEFAULT_OP_ARRAY_CMD(m_name, m_type, skip_test, cmd) \ - case m_name: { \ - skip_test; \ - \ - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { \ - int index = p_index; \ - m_type *arr = reinterpret_cast<m_type *>(_data._mem); \ - \ - if (index < 0) \ - index += arr->size(); \ - if (index >= 0 && index < arr->size()) { \ - valid = true; \ - cmd; \ - } \ - } \ - } break; - -#define DEFAULT_OP_DVECTOR_SET(m_name, m_type, skip_cond) \ - case m_name: { \ - if (skip_cond) return; \ - \ - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { \ - int index = p_index; \ - Vector<m_type> *arr = PackedArrayRef<m_type>::get_array_ptr(_data.packed_array); \ - \ - if (index < 0) \ - index += arr->size(); \ - if (index >= 0 && index < arr->size()) { \ - valid = true; \ - arr->set(index, p_value); \ - } \ - } \ - } break; - -#define DEFAULT_OP_DVECTOR_GET(m_name, m_type) \ - case m_name: { \ - \ - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { \ - int index = p_index; \ - const Vector<m_type> *arr = &PackedArrayRef<m_type>::get_array(_data.packed_array); \ - \ - if (index < 0) \ - index += arr->size(); \ - if (index >= 0 && index < arr->size()) { \ - valid = true; \ - return arr->get(index); \ - } \ - } \ - } break; - -void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) { - - static bool _dummy = false; - - bool &valid = r_valid ? *r_valid : _dummy; - valid = false; - - switch (type) { - case NIL: { - return; - } break; - case BOOL: { - return; - } break; - case INT: { - return; - } break; - case FLOAT: { - return; - } break; - case STRING: { - - if (p_index.type != Variant::INT && p_index.type != Variant::FLOAT) - return; - - int idx = p_index; - String *str = reinterpret_cast<String *>(_data._mem); - int len = str->length(); - if (idx < 0) - idx += len; - if (idx < 0 || idx >= len) - return; - - String chr; - if (p_value.type == Variant::INT || p_value.type == Variant::FLOAT) { - - chr = String::chr(p_value); - } else if (p_value.type == Variant::STRING) { - - chr = p_value; - } else { - return; - } - - *str = str->substr(0, idx) + chr + str->substr(idx + 1, len); - valid = true; - return; - - } break; - case VECTOR2: { - - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - // scalar index - int idx = p_index; - - if (idx < 0) - idx += 2; - if (idx >= 0 && idx < 2) { - - Vector2 *v = reinterpret_cast<Vector2 *>(_data._mem); - valid = true; - (*v)[idx] = p_value; - return; - } - } else if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Vector2 *v = reinterpret_cast<Vector2 *>(_data._mem); - if (*str == "x") { - valid = true; - v->x = p_value; - return; - } else if (*str == "y") { - valid = true; - v->y = p_value; - return; - } - } - - } break; - case VECTOR2I: { - - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - // scalar index - int idx = p_index; - - if (idx < 0) - idx += 2; - if (idx >= 0 && idx < 2) { - - Vector2i *v = reinterpret_cast<Vector2i *>(_data._mem); - valid = true; - (*v)[idx] = p_value; - return; - } - } else if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Vector2i *v = reinterpret_cast<Vector2i *>(_data._mem); - if (*str == "x") { - valid = true; - v->x = p_value; - return; - } else if (*str == "y") { - valid = true; - v->y = p_value; - return; - } - } - - } break; - case RECT2: { - - if (p_value.type != Variant::VECTOR2) - return; - - if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Rect2 *v = reinterpret_cast<Rect2 *>(_data._mem); - if (*str == "position") { - valid = true; - v->position = p_value; - return; - } else if (*str == "size") { - valid = true; - v->size = p_value; - return; - } else if (*str == "end") { - valid = true; - v->size = Vector2(p_value) - v->position; - return; - } - } - } break; - case RECT2I: { - - if (p_value.type != Variant::VECTOR2I) - return; - - if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Rect2i *v = reinterpret_cast<Rect2i *>(_data._mem); - if (*str == "position") { - valid = true; - v->position = p_value; - return; - } else if (*str == "size") { - valid = true; - v->size = p_value; - return; - } else if (*str == "end") { - valid = true; - v->size = Vector2i(p_value) - v->position; - return; - } - } - } break; - case TRANSFORM2D: { - - if (p_value.type != Variant::VECTOR2) - return; - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - int index = p_index; - - if (index < 0) - index += 3; - if (index >= 0 && index < 3) { - Transform2D *v = _data._transform2d; - - valid = true; - v->elements[index] = p_value; - return; - } - } else if (p_index.get_type() == Variant::STRING && p_value.get_type() == Variant::VECTOR2) { - - //scalar name - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Transform2D *v = _data._transform2d; - if (*str == "x") { - valid = true; - v->elements[0] = p_value; - return; - } else if (*str == "y") { - valid = true; - v->elements[1] = p_value; - return; - } else if (*str == "origin") { - valid = true; - v->elements[2] = p_value; - return; - } - } - - } break; - case VECTOR3: { - - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - //scalar index - int idx = p_index; - if (idx < 0) - idx += 3; - if (idx >= 0 && idx < 3) { - - Vector3 *v = reinterpret_cast<Vector3 *>(_data._mem); - valid = true; - (*v)[idx] = p_value; - return; - } - } else if (p_index.get_type() == Variant::STRING) { - - //scalar name - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Vector3 *v = reinterpret_cast<Vector3 *>(_data._mem); - if (*str == "x") { - valid = true; - v->x = p_value; - return; - } else if (*str == "y") { - valid = true; - v->y = p_value; - return; - } else if (*str == "z") { - valid = true; - v->z = p_value; - return; - } - } - - } break; - case VECTOR3I: { - - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - //scalar index - int idx = p_index; - if (idx < 0) - idx += 3; - if (idx >= 0 && idx < 3) { - - Vector3i *v = reinterpret_cast<Vector3i *>(_data._mem); - valid = true; - (*v)[idx] = p_value; - return; - } - } else if (p_index.get_type() == Variant::STRING) { - - //scalar name - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Vector3i *v = reinterpret_cast<Vector3i *>(_data._mem); - if (*str == "x") { - valid = true; - v->x = p_value; - return; - } else if (*str == "y") { - valid = true; - v->y = p_value; - return; - } else if (*str == "z") { - valid = true; - v->z = p_value; - return; - } - } - - } break; - case PLANE: { - - if (p_index.get_type() == Variant::STRING) { - //scalar name - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Plane *v = reinterpret_cast<Plane *>(_data._mem); - if (*str == "x") { - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - valid = true; - v->normal.x = p_value; - return; - } else if (*str == "y") { - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - valid = true; - v->normal.y = p_value; - return; - } else if (*str == "z") { - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - valid = true; - v->normal.z = p_value; - return; - } else if (*str == "normal") { - if (p_value.type != Variant::VECTOR3) - return; - - valid = true; - v->normal = p_value; - return; - } else if (*str == "d") { - valid = true; - v->d = p_value; - return; - } - } - - } break; - case QUAT: { - - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - if (p_index.get_type() == Variant::STRING) { - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Quat *v = reinterpret_cast<Quat *>(_data._mem); - if (*str == "x") { - valid = true; - v->x = p_value; - return; - } else if (*str == "y") { - valid = true; - v->y = p_value; - return; - } else if (*str == "z") { - valid = true; - v->z = p_value; - return; - } else if (*str == "w") { - valid = true; - v->w = p_value; - return; - } - } - - } break; - case AABB: { - - if (p_value.type != Variant::VECTOR3) - return; - - if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - ::AABB *v = _data._aabb; - if (*str == "position") { - valid = true; - v->position = p_value; - return; - } else if (*str == "size") { - valid = true; - v->size = p_value; - return; - } else if (*str == "end") { - valid = true; - v->size = Vector3(p_value) - v->position; - return; - } - } - } break; - case BASIS: { - - if (p_value.type != Variant::VECTOR3) - return; - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - int index = p_index; - - if (index < 0) - index += 3; - if (index >= 0 && index < 3) { - Basis *v = _data._basis; - - valid = true; - v->set_axis(index, p_value); - return; - } - } else if (p_index.get_type() == Variant::STRING) { - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Basis *v = _data._basis; - - if (*str == "x") { - valid = true; - v->set_axis(0, p_value); - return; - } else if (*str == "y") { - valid = true; - v->set_axis(1, p_value); - return; - } else if (*str == "z") { - valid = true; - v->set_axis(2, p_value); - return; - } - } - - } break; - case TRANSFORM: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - if (p_value.type != Variant::VECTOR3) - return; - - int index = p_index; - - if (index < 0) - index += 4; - if (index >= 0 && index < 4) { - Transform *v = _data._transform; - valid = true; - if (index == 3) - v->origin = p_value; - else - v->basis.set_axis(index, p_value); - return; - } - } else if (p_index.get_type() == Variant::STRING) { - - Transform *v = _data._transform; - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - - if (*str == "basis") { - - if (p_value.type != Variant::BASIS) - return; - valid = true; - v->basis = p_value; - return; - } - if (*str == "origin") { - if (p_value.type != Variant::VECTOR3) - return; - valid = true; - v->origin = p_value; - return; - } - } - - } break; - case COLOR: { - - if (p_value.type != Variant::INT && p_value.type != Variant::FLOAT) - return; - - if (p_index.get_type() == Variant::STRING) { - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - Color *v = reinterpret_cast<Color *>(_data._mem); - if (*str == "r") { - valid = true; - v->r = p_value; - return; - } else if (*str == "g") { - valid = true; - v->g = p_value; - return; - } else if (*str == "b") { - valid = true; - v->b = p_value; - return; - } else if (*str == "a") { - valid = true; - v->a = p_value; - return; - } else if (*str == "h") { - valid = true; - v->set_hsv(p_value, v->get_s(), v->get_v(), v->a); - return; - } else if (*str == "s") { - valid = true; - v->set_hsv(v->get_h(), p_value, v->get_v(), v->a); - return; - } else if (*str == "v") { - valid = true; - v->set_hsv(v->get_h(), v->get_s(), p_value, v->a); - return; - } else if (*str == "r8") { - valid = true; - v->r = float(p_value) / 255.0; - return; - } else if (*str == "g8") { - valid = true; - v->g = float(p_value) / 255.0; - return; - } else if (*str == "b8") { - valid = true; - v->b = float(p_value) / 255.0; - return; - } else if (*str == "a8") { - valid = true; - v->a = float(p_value) / 255.0; - return; - } - } else if (p_index.get_type() == Variant::INT) { - - int idx = p_index; - if (idx < 0) - idx += 4; - if (idx >= 0 && idx < 4) { - Color *v = reinterpret_cast<Color *>(_data._mem); - (*v)[idx] = p_value; - valid = true; - } - } - - } break; - case STRING_NAME: { - } break; - case NODE_PATH: { - } break; - case _RID: { - } break; - case OBJECT: { - - Object *obj = _get_obj().obj; - //only if debugging! - - if (obj) { -#ifdef DEBUG_ENABLED - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - - WARN_PRINT("Attempted use of previously freed pointer object."); - valid = false; - return; - } -#endif - - if (p_index.get_type() != Variant::STRING_NAME && p_index.get_type() != Variant::STRING) { - obj->setvar(p_index, p_value, r_valid); - return; - } - - obj->set(p_index, p_value, r_valid); - return; - } - } break; - case DICTIONARY: { - - Dictionary *dic = reinterpret_cast<Dictionary *>(_data._mem); - dic->operator[](p_index) = p_value; - valid = true; //always valid, i guess? should this really be ok? - return; - } break; - DEFAULT_OP_ARRAY_CMD(ARRAY, Array, ;, (*arr)[index] = p_value; return ) - DEFAULT_OP_DVECTOR_SET(PACKED_BYTE_ARRAY, uint8_t, p_value.type != Variant::FLOAT && p_value.type != Variant::INT) - DEFAULT_OP_DVECTOR_SET(PACKED_INT32_ARRAY, int32_t, p_value.type != Variant::FLOAT && p_value.type != Variant::INT) - DEFAULT_OP_DVECTOR_SET(PACKED_INT64_ARRAY, int64_t, p_value.type != Variant::FLOAT && p_value.type != Variant::INT) - DEFAULT_OP_DVECTOR_SET(PACKED_FLOAT32_ARRAY, float, p_value.type != Variant::FLOAT && p_value.type != Variant::INT) - DEFAULT_OP_DVECTOR_SET(PACKED_FLOAT64_ARRAY, double, p_value.type != Variant::FLOAT && p_value.type != Variant::INT) - DEFAULT_OP_DVECTOR_SET(PACKED_STRING_ARRAY, String, p_value.type != Variant::STRING) - DEFAULT_OP_DVECTOR_SET(PACKED_VECTOR2_ARRAY, Vector2, p_value.type != Variant::VECTOR2) - DEFAULT_OP_DVECTOR_SET(PACKED_VECTOR3_ARRAY, Vector3, p_value.type != Variant::VECTOR3) - DEFAULT_OP_DVECTOR_SET(PACKED_COLOR_ARRAY, Color, p_value.type != Variant::COLOR) - default: - return; - } -} - -Variant Variant::get(const Variant &p_index, bool *r_valid) const { - - static bool _dummy = false; - - bool &valid = r_valid ? *r_valid : _dummy; - - valid = false; - - switch (type) { - case NIL: { - return Variant(); - } break; - case BOOL: { - return Variant(); - } break; - case INT: { - return Variant(); - } break; - case FLOAT: { - return Variant(); - } break; - case STRING: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - //string index - - int idx = p_index; - const String *str = reinterpret_cast<const String *>(_data._mem); - if (idx < 0) - idx += str->length(); - if (idx >= 0 && idx < str->length()) { - - valid = true; - return str->substr(idx, 1); - } - } - - } break; - case VECTOR2: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - // scalar index - int idx = p_index; - if (idx < 0) - idx += 2; - if (idx >= 0 && idx < 2) { - - const Vector2 *v = reinterpret_cast<const Vector2 *>(_data._mem); - valid = true; - return (*v)[idx]; - } - } else if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Vector2 *v = reinterpret_cast<const Vector2 *>(_data._mem); - if (*str == "x") { - valid = true; - return v->x; - } else if (*str == "y") { - valid = true; - return v->y; - } - } - - } break; - case VECTOR2I: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - // scalar index - int idx = p_index; - if (idx < 0) - idx += 2; - if (idx >= 0 && idx < 2) { - - const Vector2i *v = reinterpret_cast<const Vector2i *>(_data._mem); - valid = true; - return (*v)[idx]; - } - } else if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Vector2i *v = reinterpret_cast<const Vector2i *>(_data._mem); - if (*str == "x") { - valid = true; - return v->x; - } else if (*str == "y") { - valid = true; - return v->y; - } - } - - } break; - case RECT2: { - - if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Rect2 *v = reinterpret_cast<const Rect2 *>(_data._mem); - if (*str == "position") { - valid = true; - return v->position; - } else if (*str == "size") { - valid = true; - return v->size; - } else if (*str == "end") { - valid = true; - return v->size + v->position; - } - } - } break; - case RECT2I: { - - if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Rect2i *v = reinterpret_cast<const Rect2i *>(_data._mem); - if (*str == "position") { - valid = true; - return v->position; - } else if (*str == "size") { - valid = true; - return v->size; - } else if (*str == "end") { - valid = true; - return v->size + v->position; - } - } - } break; - case VECTOR3: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - //scalar index - int idx = p_index; - if (idx < 0) - idx += 3; - if (idx >= 0 && idx < 3) { - - const Vector3 *v = reinterpret_cast<const Vector3 *>(_data._mem); - valid = true; - return (*v)[idx]; - } - } else if (p_index.get_type() == Variant::STRING) { - - //scalar name - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Vector3 *v = reinterpret_cast<const Vector3 *>(_data._mem); - if (*str == "x") { - valid = true; - return v->x; - } else if (*str == "y") { - valid = true; - return v->y; - } else if (*str == "z") { - valid = true; - return v->z; - } - } - - } break; - case VECTOR3I: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - //scalar index - int idx = p_index; - if (idx < 0) - idx += 3; - if (idx >= 0 && idx < 3) { - - const Vector3i *v = reinterpret_cast<const Vector3i *>(_data._mem); - valid = true; - return (*v)[idx]; - } - } else if (p_index.get_type() == Variant::STRING) { - - //scalar name - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Vector3i *v = reinterpret_cast<const Vector3i *>(_data._mem); - if (*str == "x") { - valid = true; - return v->x; - } else if (*str == "y") { - valid = true; - return v->y; - } else if (*str == "z") { - valid = true; - return v->z; - } - } - - } break; - case TRANSFORM2D: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - int index = p_index; - - if (index < 0) - index += 3; - if (index >= 0 && index < 3) { - const Transform2D *v = _data._transform2d; - - valid = true; - return v->elements[index]; - } - } else if (p_index.get_type() == Variant::STRING) { - - //scalar name - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Transform2D *v = _data._transform2d; - if (*str == "x") { - valid = true; - return v->elements[0]; - } else if (*str == "y") { - valid = true; - return v->elements[1]; - } else if (*str == "origin") { - valid = true; - return v->elements[2]; - } - } - - } break; - case PLANE: { - - if (p_index.get_type() == Variant::STRING) { - //scalar name - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Plane *v = reinterpret_cast<const Plane *>(_data._mem); - if (*str == "x") { - valid = true; - return v->normal.x; - } else if (*str == "y") { - valid = true; - return v->normal.y; - } else if (*str == "z") { - valid = true; - return v->normal.z; - } else if (*str == "normal") { - valid = true; - return v->normal; - } else if (*str == "d") { - valid = true; - return v->d; - } - } - - } break; - case QUAT: { - - if (p_index.get_type() == Variant::STRING) { - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Quat *v = reinterpret_cast<const Quat *>(_data._mem); - if (*str == "x") { - valid = true; - return v->x; - } else if (*str == "y") { - valid = true; - return v->y; - } else if (*str == "z") { - valid = true; - return v->z; - } else if (*str == "w") { - valid = true; - return v->w; - } - } - - } break; - case AABB: { - - if (p_index.get_type() == Variant::STRING) { - //scalar name - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const ::AABB *v = _data._aabb; - if (*str == "position") { - valid = true; - return v->position; - } else if (*str == "size") { - valid = true; - return v->size; - } else if (*str == "end") { - valid = true; - return v->size + v->position; - } - } - } break; - case BASIS: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - int index = p_index; - if (index < 0) - index += 3; - if (index >= 0 && index < 3) { - const Basis *v = _data._basis; - - valid = true; - return v->get_axis(index); - } - } else if (p_index.get_type() == Variant::STRING) { - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Basis *v = _data._basis; - - if (*str == "x") { - valid = true; - return v->get_axis(0); - } else if (*str == "y") { - valid = true; - return v->get_axis(1); - } else if (*str == "z") { - valid = true; - return v->get_axis(2); - } - } - - } break; - case TRANSFORM: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - int index = p_index; - if (index < 0) - index += 4; - if (index >= 0 && index < 4) { - const Transform *v = _data._transform; - valid = true; - return index == 3 ? v->origin : v->basis.get_axis(index); - } - } else if (p_index.get_type() == Variant::STRING) { - - const Transform *v = _data._transform; - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - - if (*str == "basis") { - valid = true; - return v->basis; - } - if (*str == "origin") { - valid = true; - return v->origin; - } - } - - } break; - case COLOR: { - - if (p_index.get_type() == Variant::STRING) { - - const String *str = reinterpret_cast<const String *>(p_index._data._mem); - const Color *v = reinterpret_cast<const Color *>(_data._mem); - if (*str == "r") { - valid = true; - return v->r; - } else if (*str == "g") { - valid = true; - return v->g; - } else if (*str == "b") { - valid = true; - return v->b; - } else if (*str == "a") { - valid = true; - return v->a; - } else if (*str == "h") { - valid = true; - return v->get_h(); - } else if (*str == "s") { - valid = true; - return v->get_s(); - } else if (*str == "v") { - valid = true; - return v->get_v(); - } else if (*str == "r8") { - valid = true; - return (int)Math::round(v->r * 255.0); - } else if (*str == "g8") { - valid = true; - return (int)Math::round(v->g * 255.0); - } else if (*str == "b8") { - valid = true; - return (int)Math::round(v->b * 255.0); - } else if (*str == "a8") { - valid = true; - return (int)Math::round(v->a * 255.0); - } - } else if (p_index.get_type() == Variant::INT) { - - int idx = p_index; - if (idx < 0) - idx += 4; - if (idx >= 0 && idx < 4) { - const Color *v = reinterpret_cast<const Color *>(_data._mem); - valid = true; - return (*v)[idx]; - } - } - - } break; - case STRING_NAME: { - } break; - case NODE_PATH: { - } break; - case _RID: { - } break; - case OBJECT: { - Object *obj = _get_obj().obj; - if (obj) { - -#ifdef DEBUG_ENABLED - - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - valid = false; - return "Attempted get on previously freed instance."; - } -#endif - - if (p_index.get_type() != Variant::STRING) { - return obj->getvar(p_index, r_valid); - } - - return obj->get(p_index, r_valid); - } - - } break; - case DICTIONARY: { - - const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); - const Variant *res = dic->getptr(p_index); - if (res) { - valid = true; - return *res; - } - } break; - DEFAULT_OP_ARRAY_CMD(ARRAY, const Array, ;, return (*arr)[index]) - DEFAULT_OP_DVECTOR_GET(PACKED_BYTE_ARRAY, uint8_t) - DEFAULT_OP_DVECTOR_GET(PACKED_INT32_ARRAY, int32_t) - DEFAULT_OP_DVECTOR_GET(PACKED_INT64_ARRAY, int64_t) - DEFAULT_OP_DVECTOR_GET(PACKED_FLOAT32_ARRAY, float) - DEFAULT_OP_DVECTOR_GET(PACKED_FLOAT64_ARRAY, double) - DEFAULT_OP_DVECTOR_GET(PACKED_STRING_ARRAY, String) - DEFAULT_OP_DVECTOR_GET(PACKED_VECTOR2_ARRAY, Vector2) - DEFAULT_OP_DVECTOR_GET(PACKED_VECTOR3_ARRAY, Vector3) - DEFAULT_OP_DVECTOR_GET(PACKED_COLOR_ARRAY, Color) - default: - return Variant(); - } - - return Variant(); -} - -bool Variant::in(const Variant &p_index, bool *r_valid) const { - - if (r_valid) - *r_valid = true; - - switch (type) { - - case STRING: { - - if (p_index.get_type() == Variant::STRING) { - //string index - String idx = p_index; - const String *str = reinterpret_cast<const String *>(_data._mem); - - return str->find(idx) != -1; - } - - } break; - case OBJECT: { - Object *obj = _get_obj().obj; - if (obj) { - - bool valid = false; -#ifdef DEBUG_ENABLED - - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - if (r_valid) { - *r_valid = false; - } - return true; // Attempted get on stray pointer. - } - -#endif - - if (p_index.get_type() != Variant::STRING) { - obj->getvar(p_index, &valid); - } else { - obj->get(p_index, &valid); - } - - return valid; - } else { - if (r_valid) - *r_valid = false; - } - return false; - } break; - case DICTIONARY: { - - const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); - return dic->has(p_index); - - } break; - case ARRAY: { - - const Array *arr = reinterpret_cast<const Array *>(_data._mem); - int l = arr->size(); - if (l) { - for (int i = 0; i < l; i++) { - - if (evaluate(OP_EQUAL, (*arr)[i], p_index)) - return true; - } - } - - return false; - - } break; - case PACKED_BYTE_ARRAY: { - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - int index = p_index; - const Vector<uint8_t> *arr = &PackedArrayRef<uint8_t>::get_array(_data.packed_array); - int l = arr->size(); - if (l) { - const uint8_t *r = arr->ptr(); - for (int i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - - } break; - case PACKED_INT32_ARRAY: { - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - int32_t index = p_index; - const Vector<int32_t> *arr = &PackedArrayRef<int32_t>::get_array(_data.packed_array); - int32_t l = arr->size(); - if (l) { - const int32_t *r = arr->ptr(); - for (int32_t i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - } break; - case PACKED_INT64_ARRAY: { - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - int64_t index = p_index; - const Vector<int64_t> *arr = &PackedArrayRef<int64_t>::get_array(_data.packed_array); - int64_t l = arr->size(); - if (l) { - const int64_t *r = arr->ptr(); - for (int64_t i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - } break; - case PACKED_FLOAT32_ARRAY: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - real_t index = p_index; - const Vector<float> *arr = &PackedArrayRef<float>::get_array(_data.packed_array); - int l = arr->size(); - if (l) { - const float *r = arr->ptr(); - for (int i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - - } break; - case PACKED_FLOAT64_ARRAY: { - - if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::FLOAT) { - - real_t index = p_index; - const Vector<double> *arr = &PackedArrayRef<double>::get_array(_data.packed_array); - int l = arr->size(); - if (l) { - const double *r = arr->ptr(); - for (int i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - - } break; - case PACKED_STRING_ARRAY: { - if (p_index.get_type() == Variant::STRING) { - - String index = p_index; - const Vector<String> *arr = &PackedArrayRef<String>::get_array(_data.packed_array); - - int l = arr->size(); - if (l) { - const String *r = arr->ptr(); - for (int i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - - } break; //25 - case PACKED_VECTOR2_ARRAY: { - if (p_index.get_type() == Variant::VECTOR2) { - - Vector2 index = p_index; - const Vector<Vector2> *arr = &PackedArrayRef<Vector2>::get_array(_data.packed_array); - - int l = arr->size(); - if (l) { - const Vector2 *r = arr->ptr(); - for (int i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - - } break; - case PACKED_VECTOR3_ARRAY: { - if (p_index.get_type() == Variant::VECTOR3) { - - Vector3 index = p_index; - const Vector<Vector3> *arr = &PackedArrayRef<Vector3>::get_array(_data.packed_array); - - int l = arr->size(); - if (l) { - const Vector3 *r = arr->ptr(); - for (int i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - - } break; - case PACKED_COLOR_ARRAY: { - - if (p_index.get_type() == Variant::COLOR) { - - Color index = p_index; - const Vector<Color> *arr = &PackedArrayRef<Color>::get_array(_data.packed_array); - - int l = arr->size(); - if (l) { - const Color *r = arr->ptr(); - for (int i = 0; i < l; i++) { - if (r[i] == index) - return true; - } - } - - return false; - } - } break; - default: { - } - } - - if (r_valid) - *r_valid = false; - return false; -} - -void Variant::get_property_list(List<PropertyInfo> *p_list) const { - - switch (type) { - case VECTOR2: { - - p_list->push_back(PropertyInfo(Variant::FLOAT, "x")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "y")); - - } break; - case VECTOR2I: { - - p_list->push_back(PropertyInfo(Variant::INT, "x")); - p_list->push_back(PropertyInfo(Variant::INT, "y")); - - } break; - case RECT2: { - - p_list->push_back(PropertyInfo(Variant::VECTOR2, "position")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "size")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "end")); - - } break; - case RECT2I: { - - p_list->push_back(PropertyInfo(Variant::VECTOR2I, "position")); - p_list->push_back(PropertyInfo(Variant::VECTOR2I, "size")); - p_list->push_back(PropertyInfo(Variant::VECTOR2I, "end")); - - } break; - case VECTOR3: { - - p_list->push_back(PropertyInfo(Variant::FLOAT, "x")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "y")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "z")); - - } break; - case VECTOR3I: { - - p_list->push_back(PropertyInfo(Variant::INT, "x")); - p_list->push_back(PropertyInfo(Variant::INT, "y")); - p_list->push_back(PropertyInfo(Variant::INT, "z")); - - } break; - case TRANSFORM2D: { - - p_list->push_back(PropertyInfo(Variant::VECTOR2, "x")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "y")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "origin")); - - } break; - case PLANE: { - - p_list->push_back(PropertyInfo(Variant::VECTOR3, "normal")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "x")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "y")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "z")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "d")); - - } break; - case QUAT: { - - p_list->push_back(PropertyInfo(Variant::FLOAT, "x")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "y")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "z")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "w")); - - } break; - case AABB: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); - p_list->push_back(PropertyInfo(Variant::VECTOR3, "size")); - p_list->push_back(PropertyInfo(Variant::VECTOR3, "end")); - } break; - case BASIS: { - - p_list->push_back(PropertyInfo(Variant::VECTOR3, "x")); - p_list->push_back(PropertyInfo(Variant::VECTOR3, "y")); - p_list->push_back(PropertyInfo(Variant::VECTOR3, "z")); - - } break; - case TRANSFORM: { - - p_list->push_back(PropertyInfo(Variant::BASIS, "basis")); - p_list->push_back(PropertyInfo(Variant::VECTOR3, "origin")); - - } break; - case COLOR: { - p_list->push_back(PropertyInfo(Variant::FLOAT, "r")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "g")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "b")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "a")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "h")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "s")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "v")); - p_list->push_back(PropertyInfo(Variant::INT, "r8")); - p_list->push_back(PropertyInfo(Variant::INT, "g8")); - p_list->push_back(PropertyInfo(Variant::INT, "b8")); - p_list->push_back(PropertyInfo(Variant::INT, "a8")); - - } break; - case STRING_NAME: { - } break; - case NODE_PATH: { - } break; - case _RID: { - } break; - case OBJECT: { - - Object *obj = _get_obj().obj; - if (obj) { -#ifdef DEBUG_ENABLED - - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - WARN_PRINT("Attempted get_property list on previously freed instance."); - return; - } - -#endif - - obj->get_property_list(p_list); - } - - } break; - case DICTIONARY: { - - const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); - List<Variant> keys; - dic->get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - if (E->get().get_type() == Variant::STRING) { - p_list->push_back(PropertyInfo(Variant::STRING, E->get())); - } - } - } break; - case ARRAY: - case PACKED_BYTE_ARRAY: - case PACKED_INT32_ARRAY: - case PACKED_INT64_ARRAY: - case PACKED_FLOAT32_ARRAY: - case PACKED_FLOAT64_ARRAY: - case PACKED_STRING_ARRAY: - case PACKED_VECTOR2_ARRAY: - case PACKED_VECTOR3_ARRAY: - case PACKED_COLOR_ARRAY: { - - //nothing - } break; - default: { - } - } -} - -bool Variant::iter_init(Variant &r_iter, bool &valid) const { - - valid = true; - switch (type) { - case INT: { - r_iter = 0; - return _data._int > 0; - } break; - case FLOAT: { - r_iter = 0; - return _data._float > 0.0; - } break; - case VECTOR2: { - int64_t from = reinterpret_cast<const Vector2 *>(_data._mem)->x; - int64_t to = reinterpret_cast<const Vector2 *>(_data._mem)->y; - - r_iter = from; - - return from < to; - } break; - case VECTOR3: { - int64_t from = reinterpret_cast<const Vector3 *>(_data._mem)->x; - int64_t to = reinterpret_cast<const Vector3 *>(_data._mem)->y; - int64_t step = reinterpret_cast<const Vector3 *>(_data._mem)->z; - - r_iter = from; - - if (from == to) { - return false; - } else if (from < to) { - return step > 0; - } else { - return step < 0; - } - //return true; - } break; - case OBJECT: { - - if (!_get_obj().obj) { - valid = false; - return false; - } - -#ifdef DEBUG_ENABLED - - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - valid = false; - return false; - } - -#endif - Callable::CallError ce; - ce.error = Callable::CallError::CALL_OK; - Array ref; - ref.push_back(r_iter); - Variant vref = ref; - const Variant *refp[] = { &vref }; - Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_init, refp, 1, ce); - - if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { - valid = false; - return false; - } - - r_iter = ref[0]; - return ret; - } break; - - case STRING: { - - const String *str = reinterpret_cast<const String *>(_data._mem); - if (str->empty()) - return false; - r_iter = 0; - return true; - } break; - case DICTIONARY: { - - const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); - if (dic->empty()) - return false; - - const Variant *next = dic->next(nullptr); - r_iter = *next; - return true; - - } break; - case ARRAY: { - - const Array *arr = reinterpret_cast<const Array *>(_data._mem); - if (arr->empty()) - return false; - r_iter = 0; - return true; - } break; - case PACKED_BYTE_ARRAY: { - const Vector<uint8_t> *arr = &PackedArrayRef<uint8_t>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - - } break; - case PACKED_INT32_ARRAY: { - const Vector<int32_t> *arr = &PackedArrayRef<int32_t>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - - } break; - case PACKED_INT64_ARRAY: { - const Vector<int64_t> *arr = &PackedArrayRef<int64_t>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - - } break; - case PACKED_FLOAT32_ARRAY: { - const Vector<float> *arr = &PackedArrayRef<float>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - - } break; - case PACKED_FLOAT64_ARRAY: { - const Vector<double> *arr = &PackedArrayRef<double>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - - } break; - case PACKED_STRING_ARRAY: { - const Vector<String> *arr = &PackedArrayRef<String>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - } break; - case PACKED_VECTOR2_ARRAY: { - - const Vector<Vector2> *arr = &PackedArrayRef<Vector2>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - } break; - case PACKED_VECTOR3_ARRAY: { - - const Vector<Vector3> *arr = &PackedArrayRef<Vector3>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - } break; - case PACKED_COLOR_ARRAY: { - - const Vector<Color> *arr = &PackedArrayRef<Color>::get_array(_data.packed_array); - if (arr->size() == 0) - return false; - r_iter = 0; - return true; - - } break; - default: { - } - } - - valid = false; - return false; -} -bool Variant::iter_next(Variant &r_iter, bool &valid) const { - - valid = true; - switch (type) { - case INT: { - int64_t idx = r_iter; - idx++; - if (idx >= _data._int) - return false; - r_iter = idx; - return true; - } break; - case FLOAT: { - int64_t idx = r_iter; - idx++; - if (idx >= _data._float) - return false; - r_iter = idx; - return true; - } break; - case VECTOR2: { - int64_t to = reinterpret_cast<const Vector2 *>(_data._mem)->y; - - int64_t idx = r_iter; - idx++; - - if (idx >= to) - return false; - - r_iter = idx; - return true; - } break; - case VECTOR3: { - int64_t to = reinterpret_cast<const Vector3 *>(_data._mem)->y; - int64_t step = reinterpret_cast<const Vector3 *>(_data._mem)->z; - - int64_t idx = r_iter; - idx += step; - - if (step < 0 && idx <= to) - return false; - - if (step > 0 && idx >= to) - return false; - - r_iter = idx; - return true; - } break; - case OBJECT: { - - if (!_get_obj().obj) { - valid = false; - return false; - } - -#ifdef DEBUG_ENABLED - - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - valid = false; - return false; - } - -#endif - Callable::CallError ce; - ce.error = Callable::CallError::CALL_OK; - Array ref; - ref.push_back(r_iter); - Variant vref = ref; - const Variant *refp[] = { &vref }; - Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_next, refp, 1, ce); - - if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { - valid = false; - return false; - } - - r_iter = ref[0]; - - return ret; - } break; - - case STRING: { - - const String *str = reinterpret_cast<const String *>(_data._mem); - int idx = r_iter; - idx++; - if (idx >= str->length()) - return false; - r_iter = idx; - return true; - } break; - case DICTIONARY: { - - const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); - const Variant *next = dic->next(&r_iter); - if (!next) - return false; - - r_iter = *next; - return true; - - } break; - case ARRAY: { - - const Array *arr = reinterpret_cast<const Array *>(_data._mem); - int idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - } break; - case PACKED_BYTE_ARRAY: { - const Vector<uint8_t> *arr = &PackedArrayRef<uint8_t>::get_array(_data.packed_array); - int idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - - } break; - case PACKED_INT32_ARRAY: { - const Vector<int32_t> *arr = &PackedArrayRef<int32_t>::get_array(_data.packed_array); - int32_t idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - - } break; - case PACKED_INT64_ARRAY: { - const Vector<int64_t> *arr = &PackedArrayRef<int64_t>::get_array(_data.packed_array); - int64_t idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - - } break; - case PACKED_FLOAT32_ARRAY: { - const Vector<float> *arr = &PackedArrayRef<float>::get_array(_data.packed_array); - int idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - - } break; - case PACKED_FLOAT64_ARRAY: { - const Vector<double> *arr = &PackedArrayRef<double>::get_array(_data.packed_array); - int idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - - } break; - case PACKED_STRING_ARRAY: { - const Vector<String> *arr = &PackedArrayRef<String>::get_array(_data.packed_array); - int idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - } break; - case PACKED_VECTOR2_ARRAY: { - - const Vector<Vector2> *arr = &PackedArrayRef<Vector2>::get_array(_data.packed_array); - int idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - } break; - case PACKED_VECTOR3_ARRAY: { - - const Vector<Vector3> *arr = &PackedArrayRef<Vector3>::get_array(_data.packed_array); - int idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - } break; - case PACKED_COLOR_ARRAY: { - - const Vector<Color> *arr = &PackedArrayRef<Color>::get_array(_data.packed_array); - int idx = r_iter; - idx++; - if (idx >= arr->size()) - return false; - r_iter = idx; - return true; - } break; - default: { - } - } - - valid = false; - return false; -} - -Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const { - - r_valid = true; - switch (type) { - case INT: { - - return r_iter; - } break; - case FLOAT: { - - return r_iter; - } break; - case VECTOR2: { - - return r_iter; - } break; - case VECTOR3: { - - return r_iter; - } break; - case OBJECT: { - - if (!_get_obj().obj) { - r_valid = false; - return Variant(); - } -#ifdef DEBUG_ENABLED - if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - r_valid = false; - return Variant(); - } - -#endif - Callable::CallError ce; - ce.error = Callable::CallError::CALL_OK; - const Variant *refp[] = { &r_iter }; - Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_get, refp, 1, ce); - - if (ce.error != Callable::CallError::CALL_OK) { - r_valid = false; - return Variant(); - } - - //r_iter=ref[0]; - - return ret; - } break; - - case STRING: { - - const String *str = reinterpret_cast<const String *>(_data._mem); - return str->substr(r_iter, 1); - } break; - case DICTIONARY: { - - return r_iter; //iterator is the same as the key - - } break; - case ARRAY: { - - const Array *arr = reinterpret_cast<const Array *>(_data._mem); - int idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_BYTE_ARRAY: { - const Vector<uint8_t> *arr = &PackedArrayRef<uint8_t>::get_array(_data.packed_array); - int idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_INT32_ARRAY: { - const Vector<int32_t> *arr = &PackedArrayRef<int32_t>::get_array(_data.packed_array); - int32_t idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_INT64_ARRAY: { - const Vector<int64_t> *arr = &PackedArrayRef<int64_t>::get_array(_data.packed_array); - int64_t idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_FLOAT32_ARRAY: { - const Vector<float> *arr = &PackedArrayRef<float>::get_array(_data.packed_array); - int idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_FLOAT64_ARRAY: { - const Vector<double> *arr = &PackedArrayRef<double>::get_array(_data.packed_array); - int idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_STRING_ARRAY: { - const Vector<String> *arr = &PackedArrayRef<String>::get_array(_data.packed_array); - int idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_VECTOR2_ARRAY: { - - const Vector<Vector2> *arr = &PackedArrayRef<Vector2>::get_array(_data.packed_array); - int idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_VECTOR3_ARRAY: { - - const Vector<Vector3> *arr = &PackedArrayRef<Vector3>::get_array(_data.packed_array); - int idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - case PACKED_COLOR_ARRAY: { - - const Vector<Color> *arr = &PackedArrayRef<Color>::get_array(_data.packed_array); - int idx = r_iter; -#ifdef DEBUG_ENABLED - if (idx < 0 || idx >= arr->size()) { - r_valid = false; - return Variant(); - } -#endif - return arr->get(idx); - } break; - default: { - } - } - - r_valid = false; - return Variant(); -} - -Variant Variant::duplicate(bool deep) const { - switch (type) { - case OBJECT: { - /* breaks stuff :( - if (deep && !_get_obj().ref.is_null()) { - Ref<Resource> resource = _get_obj().ref; - if (resource.is_valid()) { - return resource->duplicate(true); - } - } - */ - return *this; - } break; - case DICTIONARY: - return operator Dictionary().duplicate(deep); - case ARRAY: - return operator Array().duplicate(deep); - default: - return *this; - } -} - -void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst) { - if (a.type != b.type) { - if (a.is_num() && b.is_num()) { - real_t va = a; - real_t vb = b; - r_dst = va + vb * c; - } else { - r_dst = a; - } - return; - } - - switch (a.type) { - case NIL: { - r_dst = Variant(); - } - return; - case INT: { - int64_t va = a._data._int; - int64_t vb = b._data._int; - r_dst = int(va + vb * c + 0.5); - } - return; - case FLOAT: { - double ra = a._data._float; - double rb = b._data._float; - r_dst = ra + rb * c; - } - return; - case VECTOR2: { - r_dst = *reinterpret_cast<const Vector2 *>(a._data._mem) + *reinterpret_cast<const Vector2 *>(b._data._mem) * c; - } - return; - case VECTOR2I: { - int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; - r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); - } - return; - case RECT2: { - const Rect2 *ra = reinterpret_cast<const Rect2 *>(a._data._mem); - const Rect2 *rb = reinterpret_cast<const Rect2 *>(b._data._mem); - r_dst = Rect2(ra->position + rb->position * c, ra->size + rb->size * c); - } - return; - case RECT2I: { - const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); - const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); - - int32_t vax = ra->position.x; - int32_t vay = ra->position.y; - int32_t vbx = ra->size.x; - int32_t vby = ra->size.y; - int32_t vcx = rb->position.x; - int32_t vcy = rb->position.y; - int32_t vdx = rb->size.x; - int32_t vdy = rb->size.y; - - r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); - } - return; - case VECTOR3: { - r_dst = *reinterpret_cast<const Vector3 *>(a._data._mem) + *reinterpret_cast<const Vector3 *>(b._data._mem) * c; - } - return; - case VECTOR3I: { - int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; - int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; - int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; - r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); - } - return; - case AABB: { - const ::AABB *ra = reinterpret_cast<const ::AABB *>(a._data._mem); - const ::AABB *rb = reinterpret_cast<const ::AABB *>(b._data._mem); - r_dst = ::AABB(ra->position + rb->position * c, ra->size + rb->size * c); - } - return; - case QUAT: { - Quat empty_rot; - const Quat *qa = reinterpret_cast<const Quat *>(a._data._mem); - const Quat *qb = reinterpret_cast<const Quat *>(b._data._mem); - r_dst = *qa * empty_rot.slerp(*qb, c); - } - return; - case COLOR: { - const Color *ca = reinterpret_cast<const Color *>(a._data._mem); - const Color *cb = reinterpret_cast<const Color *>(b._data._mem); - float new_r = ca->r + cb->r * c; - float new_g = ca->g + cb->g * c; - float new_b = ca->b + cb->b * c; - float new_a = ca->a + cb->a * c; - new_r = new_r > 1.0 ? 1.0 : new_r; - new_g = new_g > 1.0 ? 1.0 : new_g; - new_b = new_b > 1.0 ? 1.0 : new_b; - new_a = new_a > 1.0 ? 1.0 : new_a; - r_dst = Color(new_r, new_g, new_b, new_a); - } - return; - default: { - r_dst = c < 0.5 ? a : b; - } - return; - } -} - -void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst) { - - if (a.type != b.type) { - if (a.is_num() && b.is_num()) { - //not as efficient but.. - real_t va = a; - real_t vb = b; - r_dst = va + (vb - va) * c; - - } else { - r_dst = a; - } - return; - } - - switch (a.type) { - - case NIL: { - r_dst = Variant(); - } - return; - case BOOL: { - r_dst = a; - } - return; - case INT: { - int64_t va = a._data._int; - int64_t vb = b._data._int; - r_dst = int(va + (vb - va) * c); - } - return; - case FLOAT: { - real_t va = a._data._float; - real_t vb = b._data._float; - r_dst = va + (vb - va) * c; - } - return; - case STRING: { - //this is pretty funny and bizarre, but artists like to use it for typewritter effects - String sa = *reinterpret_cast<const String *>(a._data._mem); - String sb = *reinterpret_cast<const String *>(b._data._mem); - String dst; - int sa_len = sa.length(); - int sb_len = sb.length(); - int csize = sa_len + (sb_len - sa_len) * c; - if (csize == 0) { - r_dst = ""; - return; - } - dst.resize(csize + 1); - dst[csize] = 0; - int split = csize / 2; - - for (int i = 0; i < csize; i++) { - - CharType chr = ' '; - - if (i < split) { - - if (i < sa.length()) - chr = sa[i]; - else if (i < sb.length()) - chr = sb[i]; - - } else { - - if (i < sb.length()) - chr = sb[i]; - else if (i < sa.length()) - chr = sa[i]; - } - - dst[i] = chr; - } - - r_dst = dst; - } - return; - case VECTOR2: { - r_dst = reinterpret_cast<const Vector2 *>(a._data._mem)->lerp(*reinterpret_cast<const Vector2 *>(b._data._mem), c); - } - return; - case VECTOR2I: { - int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; - r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); - } - return; - - case RECT2: { - r_dst = Rect2(reinterpret_cast<const Rect2 *>(a._data._mem)->position.lerp(reinterpret_cast<const Rect2 *>(b._data._mem)->position, c), reinterpret_cast<const Rect2 *>(a._data._mem)->size.lerp(reinterpret_cast<const Rect2 *>(b._data._mem)->size, c)); - } - return; - case RECT2I: { - const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); - const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); - - int32_t vax = ra->position.x; - int32_t vay = ra->position.y; - int32_t vbx = ra->size.x; - int32_t vby = ra->size.y; - int32_t vcx = rb->position.x; - int32_t vcy = rb->position.y; - int32_t vdx = rb->size.x; - int32_t vdy = rb->size.y; - - r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); - } - return; - - case VECTOR3: { - r_dst = reinterpret_cast<const Vector3 *>(a._data._mem)->lerp(*reinterpret_cast<const Vector3 *>(b._data._mem), c); - } - return; - case VECTOR3I: { - int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; - int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; - int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; - r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); - } - return; - - case TRANSFORM2D: { - r_dst = a._data._transform2d->interpolate_with(*b._data._transform2d, c); - } - return; - case PLANE: { - r_dst = a; - } - return; - case QUAT: { - r_dst = reinterpret_cast<const Quat *>(a._data._mem)->slerp(*reinterpret_cast<const Quat *>(b._data._mem), c); - } - return; - case AABB: { - r_dst = ::AABB(a._data._aabb->position.lerp(b._data._aabb->position, c), a._data._aabb->size.lerp(b._data._aabb->size, c)); - } - return; - case BASIS: { - r_dst = Transform(*a._data._basis).interpolate_with(Transform(*b._data._basis), c).basis; - } - return; - case TRANSFORM: { - r_dst = a._data._transform->interpolate_with(*b._data._transform, c); - } - return; - case COLOR: { - r_dst = reinterpret_cast<const Color *>(a._data._mem)->lerp(*reinterpret_cast<const Color *>(b._data._mem), c); - } - return; - case STRING_NAME: { - r_dst = a; - } - return; - case NODE_PATH: { - r_dst = a; - } - return; - case _RID: { - r_dst = a; - } - return; - case OBJECT: { - r_dst = a; - } - return; - case DICTIONARY: { - } - return; - case ARRAY: { - r_dst = a; - } - return; - case PACKED_BYTE_ARRAY: { - r_dst = a; - } - return; - case PACKED_INT32_ARRAY: { - const Vector<int32_t> *arr_a = &PackedArrayRef<int32_t>::get_array(a._data.packed_array); - const Vector<int32_t> *arr_b = &PackedArrayRef<int32_t>::get_array(b._data.packed_array); - int32_t sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - - r_dst = a; - } else { - - Vector<int32_t> v; - v.resize(sz); - { - int32_t *vw = v.ptrw(); - const int32_t *ar = arr_a->ptr(); - const int32_t *br = arr_b->ptr(); - - Variant va; - for (int32_t i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_INT64_ARRAY: { - const Vector<int64_t> *arr_a = &PackedArrayRef<int64_t>::get_array(a._data.packed_array); - const Vector<int64_t> *arr_b = &PackedArrayRef<int64_t>::get_array(b._data.packed_array); - int64_t sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - - r_dst = a; - } else { - - Vector<int64_t> v; - v.resize(sz); - { - int64_t *vw = v.ptrw(); - const int64_t *ar = arr_a->ptr(); - const int64_t *br = arr_b->ptr(); - - Variant va; - for (int64_t i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_FLOAT32_ARRAY: { - const Vector<float> *arr_a = &PackedArrayRef<float>::get_array(a._data.packed_array); - const Vector<float> *arr_b = &PackedArrayRef<float>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - - r_dst = a; - } else { - - Vector<float> v; - v.resize(sz); - { - float *vw = v.ptrw(); - const float *ar = arr_a->ptr(); - const float *br = arr_b->ptr(); - - Variant va; - for (int i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_FLOAT64_ARRAY: { - const Vector<double> *arr_a = &PackedArrayRef<double>::get_array(a._data.packed_array); - const Vector<double> *arr_b = &PackedArrayRef<double>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - - r_dst = a; - } else { - - Vector<double> v; - v.resize(sz); - { - double *vw = v.ptrw(); - const double *ar = arr_a->ptr(); - const double *br = arr_b->ptr(); - - Variant va; - for (int i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_STRING_ARRAY: { - r_dst = a; - } - return; - case PACKED_VECTOR2_ARRAY: { - const Vector<Vector2> *arr_a = &PackedArrayRef<Vector2>::get_array(a._data.packed_array); - const Vector<Vector2> *arr_b = &PackedArrayRef<Vector2>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - - r_dst = a; - } else { - - Vector<Vector2> v; - v.resize(sz); - { - Vector2 *vw = v.ptrw(); - const Vector2 *ar = arr_a->ptr(); - const Vector2 *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - case PACKED_VECTOR3_ARRAY: { - - const Vector<Vector3> *arr_a = &PackedArrayRef<Vector3>::get_array(a._data.packed_array); - const Vector<Vector3> *arr_b = &PackedArrayRef<Vector3>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - - r_dst = a; - } else { - - Vector<Vector3> v; - v.resize(sz); - { - Vector3 *vw = v.ptrw(); - const Vector3 *ar = arr_a->ptr(); - const Vector3 *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - case PACKED_COLOR_ARRAY: { - const Vector<Color> *arr_a = &PackedArrayRef<Color>::get_array(a._data.packed_array); - const Vector<Color> *arr_b = &PackedArrayRef<Color>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - - r_dst = a; - } else { - - Vector<Color> v; - v.resize(sz); - { - Color *vw = v.ptrw(); - const Color *ar = arr_a->ptr(); - const Color *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - default: { - - r_dst = a; - } - } -} - -static const char *_op_names[Variant::OP_MAX] = { - "==", - "!=", - "<", - "<=", - ">", - ">=", - "+", - "-", - "*", - "/", - "- (negation)", - "+ (positive)", - "%", - "+ (concatenation)", - "<<", - ">>", - "&", - "|", - "^", - "~", - "and", - "or", - "xor", - "not", - "in" - -}; - -String Variant::get_operator_name(Operator p_op) { - - ERR_FAIL_INDEX_V(p_op, OP_MAX, ""); - return _op_names[p_op]; -} diff --git a/core/version.h b/core/version.h index 1198f62db4..2a4fa9cfd4 100644 --- a/core/version.h +++ b/core/version.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ |