diff options
Diffstat (limited to 'core')
65 files changed, 1370 insertions, 626 deletions
diff --git a/core/SCsub b/core/SCsub index 1379e9df9b..df3e7a547a 100644 --- a/core/SCsub +++ b/core/SCsub @@ -129,6 +129,9 @@ if env["builtin_zstd"]: "decompress/zstd_decompress_block.c", "decompress/zstd_decompress.c", ] + if env["platform"] in ["android", "iphone", "linuxbsd", "osx"]: + # Match platforms with ZSTD_ASM_SUPPORTED in common/portability_macros.h + thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S") thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources] env_thirdparty.Prepend(CPPPATH=[thirdparty_zstd_dir, thirdparty_zstd_dir + "common"]) diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 28fbc78501..7145e628c1 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -41,7 +41,6 @@ #include "core/os/keyboard.h" #include "core/variant/variant_parser.h" #include "core/version.h" - #include "modules/modules_enabled.gen.h" // For mono. const String ProjectSettings::PROJECT_DATA_DIR_NAME_SUFFIX = "godot"; @@ -1250,9 +1249,6 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray()); GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray()); GLOBAL_DEF_INTERNAL("internationalization/locale/translations", PackedStringArray()); - - GLOBAL_DEF("rendering/textures/vram_compression/minimum_size", 512); - custom_prop_info["rendering/textures/vram_compression/minimum_size"] = PropertyInfo(Variant::INT, "rendering/textures/vram_compression/minimum_size", PROPERTY_HINT_RANGE, "16,16384,1"); } ProjectSettings::~ProjectSettings() { diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 8fafc459e3..24b27d2692 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -83,6 +83,14 @@ Vector<String> ResourceLoader::get_recognized_extensions_for_type(const String & return ret; } +void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front) { + ::ResourceLoader::add_resource_format_loader(p_format_loader, p_at_front); +} + +void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader) { + ::ResourceLoader::remove_resource_format_loader(p_format_loader); +} + void ResourceLoader::set_abort_on_missing_resources(bool p_abort) { ::ResourceLoader::set_abort_on_missing_resources(p_abort); } @@ -119,6 +127,8 @@ void ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE)); ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &ResourceLoader::get_recognized_extensions_for_type); + ClassDB::bind_method(D_METHOD("add_resource_format_loader", "format_loader", "at_front"), &ResourceLoader::add_resource_format_loader, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("remove_resource_format_loader", "format_loader"), &ResourceLoader::remove_resource_format_loader); ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &ResourceLoader::set_abort_on_missing_resources); ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &ResourceLoader::get_dependencies); ClassDB::bind_method(D_METHOD("has_cached", "path"), &ResourceLoader::has_cached); @@ -153,11 +163,21 @@ Vector<String> ResourceSaver::get_recognized_extensions(const Ref<Resource> &p_r return ret; } +void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front) { + ::ResourceSaver::add_resource_format_saver(p_format_saver, p_at_front); +} + +void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver) { + ::ResourceSaver::remove_resource_format_saver(p_format_saver); +} + ResourceSaver *ResourceSaver::singleton = nullptr; void ResourceSaver::_bind_methods() { ClassDB::bind_method(D_METHOD("save", "path", "resource", "flags"), &ResourceSaver::save, DEFVAL((uint32_t)FLAG_NONE)); ClassDB::bind_method(D_METHOD("get_recognized_extensions", "type"), &ResourceSaver::get_recognized_extensions); + ClassDB::bind_method(D_METHOD("add_resource_format_saver", "format_saver", "at_front"), &ResourceSaver::add_resource_format_saver, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("remove_resource_format_saver", "format_saver"), &ResourceSaver::remove_resource_format_saver); BIND_ENUM_CONSTANT(FLAG_NONE); BIND_ENUM_CONSTANT(FLAG_RELATIVE_PATHS); @@ -1820,60 +1840,26 @@ void Thread::_start_func(void *ud) { ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id())); } - Callable::CallError ce; - const Variant *arg[1] = { &t->userdata }; - int argc = 0; - if (arg[0]->get_type() != Variant::NIL) { - // Just pass to the target function whatever came as user data - argc = 1; - } else { - // There are two cases of null user data: - // a) The target function has zero parameters and the caller is just honoring that. - // b) The target function has at least one parameter with no default and the caller is - // leveraging the fact that user data defaults to null in Thread.start(). - // We care about the case of more than one parameter because, even if a thread - // function can have one at most, out mindset here is to do our best with the - // only/first one and let the call handle any other error conditions, like too - // much arguments. - // We must check if we are in case b). - int target_param_count = 0; - int target_default_arg_count = 0; - Ref<Script> script = target_instance->get_script(); - if (script.is_valid()) { - MethodInfo mi = script->get_method_info(t->target_callable.get_method()); - target_param_count = mi.arguments.size(); - target_default_arg_count = mi.default_arguments.size(); - } else { - MethodBind *method = ClassDB::get_method(target_instance->get_class_name(), t->target_callable.get_method()); - if (method) { - target_param_count = method->get_argument_count(); - target_default_arg_count = method->get_default_argument_count(); - } - } - if (target_param_count >= 1 && target_default_arg_count < target_param_count) { - argc = 1; - } - } + String func_name = t->target_callable.is_custom() ? t->target_callable.get_custom()->get_as_text() : String(t->target_callable.get_method()); + ::Thread::set_name(func_name); - ::Thread::set_name(t->target_callable.get_method()); - - t->target_callable.call(arg, argc, t->ret, ce); + Callable::CallError ce; + t->target_callable.call(nullptr, 0, t->ret, ce); if (ce.error != Callable::CallError::CALL_OK) { t->running.clear(); - ERR_FAIL_MSG("Could not call function '" + t->target_callable.get_method().operator String() + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, arg, argc, ce) + "."); + ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + "."); } t->running.clear(); } -Error Thread::start(const Callable &p_callable, const Variant &p_userdata, Priority p_priority) { +Error Thread::start(const Callable &p_callable, Priority p_priority) { ERR_FAIL_COND_V_MSG(is_started(), ERR_ALREADY_IN_USE, "Thread already started."); ERR_FAIL_COND_V(!p_callable.is_valid(), ERR_INVALID_PARAMETER); ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER); ret = Variant(); target_callable = p_callable; - userdata = p_userdata; running.set(); Ref<Thread> *ud = memnew(Ref<Thread>(this)); @@ -1902,13 +1888,12 @@ Variant Thread::wait_to_finish() { thread.wait_to_finish(); Variant r = ret; target_callable = Callable(); - userdata = Variant(); return r; } void Thread::_bind_methods() { - ClassDB::bind_method(D_METHOD("start", "callable", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); + ClassDB::bind_method(D_METHOD("start", "callable", "priority"), &Thread::start, DEFVAL(PRIORITY_NORMAL)); ClassDB::bind_method(D_METHOD("get_id"), &Thread::get_id); ClassDB::bind_method(D_METHOD("is_started"), &Thread::is_started); ClassDB::bind_method(D_METHOD("is_alive"), &Thread::is_alive); diff --git a/core/core_bind.h b/core/core_bind.h index c2098b1b59..99e14a75f5 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -77,6 +77,8 @@ public: Ref<Resource> load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE); Vector<String> get_recognized_extensions_for_type(const String &p_type); + void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front); + void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader); void set_abort_on_missing_resources(bool p_abort); PackedStringArray get_dependencies(const String &p_path); bool has_cached(const String &p_path); @@ -109,6 +111,8 @@ public: Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags); Vector<String> get_recognized_extensions(const Ref<Resource> &p_resource); + void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front); + void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver); ResourceSaver() { singleton = this; } }; @@ -551,7 +555,6 @@ class Thread : public RefCounted { protected: Variant ret; - Variant userdata; SafeFlag running; Callable target_callable; ::Thread thread; @@ -566,7 +569,7 @@ public: PRIORITY_MAX }; - Error start(const Callable &p_callable, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL); + Error start(const Callable &p_callable, Priority p_priority = PRIORITY_NORMAL); String get_id() const; bool is_started() const; bool is_alive() const; diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 4e0845e625..24d8b0af6e 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -588,6 +588,7 @@ void register_global_constants() { 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_EXPRESSION); 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); @@ -607,11 +608,13 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_TOO_BIG); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_SAVE_FILE); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALE_ID); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALIZABLE_STRING); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_TYPE); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NONE); diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp index 06e08081e9..58d239ccb9 100644 --- a/core/debugger/local_debugger.cpp +++ b/core/debugger/local_debugger.cpp @@ -31,7 +31,7 @@ #include "local_debugger.h" #include "core/debugger/script_debugger.h" -#include "scene/main/scene_tree.h" +#include "core/os/os.h" struct LocalDebugger::ScriptsProfiler { struct ProfileInfoSort { @@ -273,7 +273,10 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { script_debugger->set_depth(-1); script_debugger->set_lines_left(-1); - SceneTree::get_singleton()->quit(); + MainLoop *main_loop = OS::get_singleton()->get_main_loop(); + if (main_loop->get_class() == "SceneTree") { + main_loop->call("quit"); + } break; } else if (line.begins_with("delete")) { if (line.get_slice_count(" ") <= 1) { diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 5ee4e2c368..c73e2eb3fb 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -208,7 +208,7 @@ void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char * rd->script_debugger->send_error(String::utf8(p_func), String::utf8(p_file), p_line, String::utf8(p_err), String::utf8(p_descr), p_editor_notify, p_type, si); } -void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) { +void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) { RemoteDebugger *rd = static_cast<RemoteDebugger *>(p_this); if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) { // Can't handle recursive prints during flush. @@ -237,7 +237,13 @@ void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p OutputString output_string; output_string.message = s; - output_string.type = p_error ? MESSAGE_TYPE_ERROR : MESSAGE_TYPE_LOG; + if (p_error) { + output_string.type = MESSAGE_TYPE_ERROR; + } else if (p_rich) { + output_string.type = MESSAGE_TYPE_LOG_RICH; + } else { + output_string.type = MESSAGE_TYPE_LOG; + } rd->output_strings.push_back(output_string); if (overflowed) { @@ -291,6 +297,14 @@ void RemoteDebugger::flush_output() { } strings.push_back(output_string.message); types.push_back(MESSAGE_TYPE_ERROR); + } else if (output_string.type == MESSAGE_TYPE_LOG_RICH) { + if (!joined_log_strings.is_empty()) { + strings.push_back(String("\n").join(joined_log_strings)); + types.push_back(MESSAGE_TYPE_LOG_RICH); + joined_log_strings.clear(); + } + strings.push_back(output_string.message); + types.push_back(MESSAGE_TYPE_LOG_RICH); } else { joined_log_strings.push_back(output_string.message); } diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index fdb312ae68..fe4bbe86ea 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -44,6 +44,7 @@ public: enum MessageType { MESSAGE_TYPE_LOG, MESSAGE_TYPE_ERROR, + MESSAGE_TYPE_LOG_RICH, }; private: @@ -82,7 +83,7 @@ private: Thread::ID flush_thread = 0; PrintHandlerList phl; - static void _print_handler(void *p_this, const String &p_string, bool p_error); + static void _print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich); ErrorHandlerList eh; static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type); diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index 8add4b9a3a..f71a642b23 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -83,7 +83,13 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co // Main error printing function. void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { - OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, p_editor_notify, (Logger::ErrorType)p_type); + if (OS::get_singleton()) { + OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, p_editor_notify, (Logger::ErrorType)p_type); + } else { + // Fallback if errors happen before OS init or after it's destroyed. + const char *err_details = (p_message && *p_message) ? p_message : p_error; + fprintf(stderr, "ERROR: %s\n at: %s (%s:%i)\n", err_details, p_function, p_file, p_line); + } _global_lock(); ErrorHandlerList *l = error_handler_list; diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index edd48cf9cd..9d846f87c2 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -474,6 +474,38 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { } } { + //enums + Array enums; + + List<StringName> enum_names; + Variant::get_enums_for_type(type, &enum_names); + for (const StringName &enum_name : enum_names) { + Dictionary enum_dict; + enum_dict["name"] = String(enum_name); + + List<StringName> enumeration_names; + Variant::get_enumerations_for_enum(type, enum_name, &enumeration_names); + + Array values; + + for (const StringName &enumeration : enumeration_names) { + Dictionary values_dict; + values_dict["name"] = String(enumeration); + values_dict["value"] = Variant::get_enum_value(type, enum_name, enumeration); + values.push_back(values_dict); + } + + if (values.size()) { + enum_dict["values"] = values; + } + enums.push_back(enum_dict); + } + + if (enums.size()) { + d["enums"] = enums; + } + } + { //operators Array operators; diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index 095c7983ee..ccd6fb0f7e 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -153,7 +153,7 @@ typedef enum { GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */ GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */ GDNATIVE_CALL_ERROR_INSTANCE_IS_NULL, - + GDNATIVE_CALL_ERROR_METHOD_NOT_CONST, /* used for const call */ } GDNativeCallErrorType; typedef struct { diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index ac9d2ca8a6..262e28b442 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -55,14 +55,7 @@ protected: virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { GDNativePropertyInfo pinfo; get_argument_info_func(method_userdata, p_arg, &pinfo); - PropertyInfo ret; - ret.type = Variant::Type(pinfo.type); - ret.name = pinfo.name; - ret.class_name = pinfo.class_name; - ret.hint = PropertyHint(pinfo.hint); - ret.usage = pinfo.usage; - ret.class_name = pinfo.class_name; - return ret; + return PropertyInfo(pinfo); } public: @@ -204,16 +197,11 @@ void NativeExtension::_register_extension_class_property(const GDNativeExtension NativeExtension *self = static_cast<NativeExtension *>(p_library); StringName class_name = p_class_name; - ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + String(p_info->name) + "' for unexisting class '" + class_name + "'."); + String property_name = p_info->name; + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + property_name + "' for unexisting class '" + class_name + "'."); //Extension *extension = &self->extension_classes[class_name]; - PropertyInfo pinfo; - pinfo.type = Variant::Type(p_info->type); - pinfo.name = p_info->name; - pinfo.class_name = p_info->class_name; - pinfo.hint = PropertyHint(p_info->hint); - pinfo.hint_string = p_info->hint_string; - pinfo.usage = p_info->usage; + PropertyInfo pinfo(*p_info); ClassDB::add_property(class_name, pinfo, p_setter, p_getter); } @@ -245,13 +233,7 @@ void NativeExtension::_register_extension_class_signal(const GDNativeExtensionCl MethodInfo s; s.name = p_signal_name; for (int i = 0; i < p_argument_count; i++) { - PropertyInfo arg; - arg.type = Variant::Type(p_argument_info[i].type); - arg.name = p_argument_info[i].name; - arg.class_name = p_argument_info[i].class_name; - arg.hint = PropertyHint(p_argument_info[i].hint); - arg.hint_string = p_argument_info[i].hint_string; - arg.usage = p_argument_info[i].usage; + PropertyInfo arg(p_argument_info[i]); s.arguments.push_back(arg); } ClassDB::add_signal(class_name, s); @@ -281,6 +263,7 @@ void NativeExtension::_get_library_path(const GDNativeExtensionClassLibraryPtr p Error NativeExtension::open_library(const String &p_path, const String &p_entry_symbol) { Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true, &library_path); if (err != OK) { + ERR_PRINT("GDExtension dynamic library not found: " + p_path); return err; } @@ -289,6 +272,7 @@ Error NativeExtension::open_library(const String &p_path, const String &p_entry_ err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false); if (err != OK) { + ERR_PRINT("GDExtension entry point '" + p_entry_symbol + "' not found in library " + p_path); OS::get_singleton()->close_dynamic_library(library); return err; } @@ -299,6 +283,7 @@ Error NativeExtension::open_library(const String &p_path, const String &p_entry_ level_initialized = -1; return OK; } else { + ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error."); return FAILED; } } @@ -387,6 +372,7 @@ Ref<Resource> NativeExtensionResourceLoader::load(const String &p_path, const St } if (err != OK) { + ERR_PRINT("Error loading GDExtension config file: " + p_path); return Ref<Resource>(); } @@ -394,6 +380,7 @@ Ref<Resource> NativeExtensionResourceLoader::load(const String &p_path, const St if (r_error) { *r_error = ERR_INVALID_DATA; } + ERR_PRINT("GDExtension config file must contain 'configuration.entry_symbol' key: " + p_path); return Ref<Resource>(); } @@ -426,6 +413,7 @@ Ref<Resource> NativeExtensionResourceLoader::load(const String &p_path, const St if (r_error) { *r_error = ERR_FILE_NOT_FOUND; } + ERR_PRINT("No GDExtension library found for current architecture; in config file " + p_path); return Ref<Resource>(); } @@ -443,6 +431,7 @@ Ref<Resource> NativeExtensionResourceLoader::load(const String &p_path, const St } if (err != OK) { + // Errors already logged in open_library() return Ref<Resource>(); } diff --git a/core/input/input.cpp b/core/input/input.cpp index b3a68bb98c..da0c6cb62a 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -325,6 +325,7 @@ float Input::get_action_strength(const StringName &p_action, bool p_exact) const } float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const { + ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); if (!E) { return 0.0f; diff --git a/core/input/input.h b/core/input/input.h index f02f2abae5..3ad8c91ddf 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -110,7 +110,7 @@ private: bool emulate_touch_from_mouse = false; bool emulate_mouse_from_touch = false; bool use_input_buffering = false; - bool use_accumulated_input = false; + bool use_accumulated_input = true; int mouse_from_touch_index = -1; diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 19a0cce796..e656f6b885 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -84,8 +84,8 @@ private: return (a == p_val.a) && (b == p_val.b); } static uint32_t hash(const PathMD5 &p_val) { - uint32_t h = hash_djb2_one_32(p_val.a); - return hash_djb2_one_32(p_val.b, h); + uint32_t h = hash_murmur3_one_32(p_val.a); + return hash_fmix32(hash_murmur3_one_32(p_val.b, h)); } PathMD5() {} diff --git a/core/io/image.cpp b/core/io/image.cpp index dfba45c4e9..f065dac212 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -81,9 +81,15 @@ const char *Image::format_names[Image::FORMAT_MAX] = { }; SavePNGFunc Image::save_png_func = nullptr; +SaveJPGFunc Image::save_jpg_func = nullptr; SaveEXRFunc Image::save_exr_func = nullptr; SavePNGBufferFunc Image::save_png_buffer_func = nullptr; +SaveEXRBufferFunc Image::save_exr_buffer_func = nullptr; +SaveJPGBufferFunc Image::save_jpg_buffer_func = nullptr; + +SaveWebPFunc Image::save_webp_func = nullptr; +SaveWebPBufferFunc Image::save_webp_buffer_func = nullptr; void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixel_size, uint8_t *p_data, const uint8_t *p_pixel) { uint32_t ofs = (p_y * width + p_x) * p_pixel_size; @@ -2286,6 +2292,14 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref<Image>((Image *)this)); } +Error Image::save_jpg(const String &p_path, float p_quality) const { + if (save_jpg_func == nullptr) { + return ERR_UNAVAILABLE; + } + + return save_jpg_func(p_path, Ref<Image>((Image *)this), p_quality); +} + Vector<uint8_t> Image::save_png_to_buffer() const { if (save_png_buffer_func == nullptr) { return Vector<uint8_t>(); @@ -2294,6 +2308,14 @@ Vector<uint8_t> Image::save_png_to_buffer() const { return save_png_buffer_func(Ref<Image>((Image *)this)); } +Vector<uint8_t> Image::save_jpg_to_buffer(float p_quality) const { + if (save_jpg_buffer_func == nullptr) { + return Vector<uint8_t>(); + } + + return save_jpg_buffer_func(Ref<Image>((Image *)this), p_quality); +} + Error Image::save_exr(const String &p_path, bool p_grayscale) const { if (save_exr_func == nullptr) { return ERR_UNAVAILABLE; @@ -2302,6 +2324,31 @@ Error Image::save_exr(const String &p_path, bool p_grayscale) const { return save_exr_func(p_path, Ref<Image>((Image *)this), p_grayscale); } +Vector<uint8_t> Image::save_exr_to_buffer(bool p_grayscale) const { + if (save_exr_buffer_func == nullptr) { + return Vector<uint8_t>(); + } + return save_exr_buffer_func(Ref<Image>((Image *)this), p_grayscale); +} + +Error Image::save_webp(const String &p_path, const bool p_lossy, const float p_quality) const { + if (save_webp_func == nullptr) { + return ERR_UNAVAILABLE; + } + ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), ERR_INVALID_PARAMETER, "The WebP lossy quality was set to " + rtos(p_quality) + ", which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive)."); + + return save_webp_func(p_path, Ref<Image>((Image *)this), p_lossy, p_quality); +} + +Vector<uint8_t> Image::save_webp_to_buffer(const bool p_lossy, const float p_quality) const { + if (save_webp_buffer_func == nullptr) { + return Vector<uint8_t>(); + } + ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), Vector<uint8_t>(), "The WebP lossy quality was set to " + rtos(p_quality) + ", which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive)."); + + return save_webp_buffer_func(Ref<Image>((Image *)this), p_lossy, p_quality); +} + 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); @@ -3138,7 +3185,12 @@ void Image::_bind_methods() { 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_jpg", "path", "quality"), &Image::save_jpg, DEFVAL(0.75)); + ClassDB::bind_method(D_METHOD("save_jpg_to_buffer", "quality"), &Image::save_jpg_to_buffer, DEFVAL(0.75)); ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("save_exr_to_buffer", "grayscale"), &Image::save_exr_to_buffer, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("save_webp", "path", "lossy", "quality"), &Image::save_webp, DEFVAL(false), DEFVAL(0.75f)); + ClassDB::bind_method(D_METHOD("save_webp_to_buffer", "lossy", "quality"), &Image::save_webp_to_buffer, DEFVAL(false), DEFVAL(0.75f)); ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha); ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible); diff --git a/core/io/image.h b/core/io/image.h index 1025554d51..2cad26f3e9 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -45,17 +45,27 @@ class Image; typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img); typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img); +typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality); +typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality); typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size); +typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality); +typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality); typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale); +typedef Vector<uint8_t> (*SaveEXRBufferFunc)(const Ref<Image> &p_img, bool p_grayscale); class Image : public Resource { GDCLASS(Image, Resource); public: static SavePNGFunc save_png_func; + static SaveJPGFunc save_jpg_func; static SaveEXRFunc save_exr_func; static SavePNGBufferFunc save_png_buffer_func; + static SaveEXRBufferFunc save_exr_buffer_func; + static SaveJPGBufferFunc save_jpg_buffer_func; + static SaveWebPFunc save_webp_func; + static SaveWebPBufferFunc save_webp_buffer_func; enum { MAX_WIDTH = (1 << 24), // force a limit somehow @@ -281,8 +291,13 @@ public: Error load(const String &p_path); Error save_png(const String &p_path) const; + Error save_jpg(const String &p_path, float p_quality = 0.75) const; Vector<uint8_t> save_png_to_buffer() const; + Vector<uint8_t> save_jpg_to_buffer(float p_quality = 0.75) const; + Vector<uint8_t> save_exr_to_buffer(bool p_grayscale) const; Error save_exr(const String &p_path, bool p_grayscale) const; + Error save_webp(const String &p_path, const bool p_lossy = false, const float p_quality = 0.75f) const; + Vector<uint8_t> save_webp_to_buffer(const bool p_lossy = false, const float p_quality = 0.75f) const; void create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { create(p_width, p_height, p_use_mipmaps, p_format); diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index bb9606c94b..f71ea5c39e 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -532,7 +532,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::RID: { - r_variant = RID(); + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); + uint64_t id = decode_uint64(buf); + if (r_len) { + (*r_len) += 8; + } + + r_variant = RID::from_uint64(id); } break; case Variant::OBJECT: { if (type & ENCODE_FLAG_OBJECT_AS_ID) { @@ -614,9 +620,20 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = Callable(); } break; case Variant::SIGNAL: { - r_variant = Signal(); - } break; + String name; + Error err = _decode_string(buf, len, r_len, name); + if (err) { + return err; + } + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); + ObjectID id = ObjectID(decode_uint64(buf)); + if (r_len) { + (*r_len) += 8; + } + + r_variant = Signal(id, StringName(name)); + } break; case Variant::DICTIONARY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); @@ -1352,10 +1369,12 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::RID: { - } break; - case Variant::CALLABLE: { - } break; - case Variant::SIGNAL: { + RID rid = p_variant; + + if (buf) { + encode_uint64(rid.get_id(), buf); + } + r_len += 8; } break; case Variant::OBJECT: { if (p_full_objects) { @@ -1419,6 +1438,18 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } } break; + case Variant::CALLABLE: { + } break; + case Variant::SIGNAL: { + Signal signal = p_variant; + + _encode_string(signal.get_name(), buf, r_len); + + if (buf) { + encode_uint64(signal.get_object_id(), buf); + } + r_len += 8; + } break; case Variant::DICTIONARY: { Dictionary d = p_variant; diff --git a/core/io/resource.cpp b/core/io/resource.cpp index ad01eb1083..fec5ca5c7b 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -52,41 +52,36 @@ void Resource::set_path(const String &p_path, bool p_take_over) { return; } + if (p_path.is_empty()) { + p_take_over = false; // Can't take over an empty path + } + + ResourceCache::lock.lock(); + if (!path_cache.is_empty()) { - ResourceCache::lock.write_lock(); ResourceCache::resources.erase(path_cache); - ResourceCache::lock.write_unlock(); } path_cache = ""; - ResourceCache::lock.read_lock(); - bool has_path = ResourceCache::resources.has(p_path); - ResourceCache::lock.read_unlock(); + Ref<Resource> existing = ResourceCache::get_ref(p_path); - if (has_path) { + if (existing.is_valid()) { if (p_take_over) { - ResourceCache::lock.write_lock(); - Resource **res = ResourceCache::resources.getptr(p_path); - if (res) { - (*res)->set_name(""); - } - ResourceCache::lock.write_unlock(); + existing->path_cache = String(); + ResourceCache::resources.erase(p_path); } else { - ResourceCache::lock.read_lock(); - bool exists = ResourceCache::resources.has(p_path); - ResourceCache::lock.read_unlock(); - - ERR_FAIL_COND_MSG(exists, "Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion)."); + ResourceCache::lock.unlock(); + ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion)."); } } + path_cache = p_path; if (!path_cache.is_empty()) { - ResourceCache::lock.write_lock(); ResourceCache::resources[path_cache] = this; - ResourceCache::lock.write_unlock(); } + ResourceCache::lock.unlock(); _resource_path_changed(); } @@ -100,14 +95,14 @@ String Resource::generate_scene_unique_id() { // If it's not unique it does not matter because the saver will try again. OS::Date date = OS::get_singleton()->get_date(); OS::Time time = OS::get_singleton()->get_time(); - uint32_t hash = hash_djb2_one_32(OS::get_singleton()->get_ticks_usec()); - hash = hash_djb2_one_32(date.year, hash); - hash = hash_djb2_one_32(date.month, hash); - hash = hash_djb2_one_32(date.day, hash); - hash = hash_djb2_one_32(time.hour, hash); - hash = hash_djb2_one_32(time.minute, hash); - hash = hash_djb2_one_32(time.second, hash); - hash = hash_djb2_one_32(Math::rand(), hash); + uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec()); + hash = hash_murmur3_one_32(date.year, hash); + hash = hash_murmur3_one_32(date.month, hash); + hash = hash_murmur3_one_32(date.day, hash); + hash = hash_murmur3_one_32(time.hour, hash); + hash = hash_murmur3_one_32(time.minute, hash); + hash = hash_murmur3_one_32(time.second, hash); + hash = hash_murmur3_one_32(Math::rand(), hash); static constexpr uint32_t characters = 5; static constexpr uint32_t char_count = ('z' - 'a'); @@ -328,7 +323,7 @@ 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()); + uint32_t hash = hash_murmur3_one_32(get_edited_version()); List<PropertyInfo> plist; get_property_list(&plist); @@ -337,7 +332,7 @@ uint32_t Resource::hash_edited_version() const { if (E.usage & PROPERTY_USAGE_STORAGE && E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) { Ref<Resource> res = get(E.name); if (res.is_valid()) { - hash = hash_djb2_one_32(res->hash_edited_version(), hash); + hash = hash_murmur3_one_32(res->hash_edited_version(), hash); } } } @@ -380,7 +375,7 @@ void Resource::set_as_translation_remapped(bool p_remapped) { return; } - ResourceCache::lock.write_lock(); + ResourceCache::lock.lock(); if (p_remapped) { ResourceLoader::remapped_list.add(&remapped_list); @@ -388,7 +383,7 @@ void Resource::set_as_translation_remapped(bool p_remapped) { ResourceLoader::remapped_list.remove(&remapped_list); } - ResourceCache::lock.write_unlock(); + ResourceCache::lock.unlock(); } bool Resource::is_translation_remapped() const { @@ -455,9 +450,9 @@ Resource::Resource() : Resource::~Resource() { if (!path_cache.is_empty()) { - ResourceCache::lock.write_lock(); + ResourceCache::lock.lock(); ResourceCache::resources.erase(path_cache); - ResourceCache::lock.write_unlock(); + ResourceCache::lock.unlock(); } if (owners.size()) { WARN_PRINT("Resource is still owned."); @@ -469,7 +464,7 @@ HashMap<String, Resource *> ResourceCache::resources; HashMap<String, HashMap<String, String>> ResourceCache::resource_path_cache; #endif -RWLock ResourceCache::lock; +Mutex ResourceCache::lock; #ifdef TOOLS_ENABLED RWLock ResourceCache::path_cache_lock; #endif @@ -491,46 +486,67 @@ void ResourceCache::reload_externals() { } bool ResourceCache::has(const String &p_path) { - lock.read_lock(); - bool b = resources.has(p_path); - lock.read_unlock(); + lock.lock(); + + Resource **res = resources.getptr(p_path); - return b; + if (res && (*res)->reference_get_count() == 0) { + // This resource is in the process of being deleted, ignore its existence. + (*res)->path_cache = String(); + resources.erase(p_path); + res = nullptr; + } + + lock.unlock(); + + if (!res) { + return false; + } + + return true; } -Resource *ResourceCache::get(const String &p_path) { - lock.read_lock(); +Ref<Resource> ResourceCache::get_ref(const String &p_path) { + Ref<Resource> ref; + lock.lock(); Resource **res = resources.getptr(p_path); - lock.read_unlock(); + if (res) { + ref = Ref<Resource>(*res); + } - if (!res) { - return nullptr; + if (res && !ref.is_valid()) { + // This resource is in the process of being deleted, ignore its existence + (*res)->path_cache = String(); + resources.erase(p_path); + res = nullptr; } - return *res; + lock.unlock(); + + return ref; } void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) { - lock.read_lock(); + lock.lock(); for (KeyValue<String, Resource *> &E : resources) { p_resources->push_back(Ref<Resource>(E.value)); } - lock.read_unlock(); + lock.unlock(); } int ResourceCache::get_cached_resource_count() { - lock.read_lock(); + lock.lock(); int rc = resources.size(); - lock.read_unlock(); + lock.unlock(); return rc; } void ResourceCache::dump(const char *p_file, bool p_short) { #ifdef DEBUG_ENABLED - lock.read_lock(); + lock.lock(); HashMap<String, int> type_count; @@ -562,7 +578,7 @@ void ResourceCache::dump(const char *p_file, bool p_short) { } } - lock.read_unlock(); + lock.unlock(); #else WARN_PRINT("ResourceCache::dump only with in debug builds."); #endif diff --git a/core/io/resource.h b/core/io/resource.h index a45bc6e1e4..a2cde87990 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -153,7 +153,7 @@ public: class ResourceCache { friend class Resource; friend class ResourceLoader; //need the lock - static RWLock lock; + static Mutex lock; static HashMap<String, Resource *> resources; #ifdef TOOLS_ENABLED static HashMap<String, HashMap<String, String>> resource_path_cache; // Each tscn has a set of resource paths and IDs. @@ -166,7 +166,7 @@ class ResourceCache { public: static void reload_externals(); static bool has(const String &p_path); - static Resource *get(const String &p_path); + static Ref<Resource> get_ref(const String &p_path); static void dump(const char *p_file = nullptr, bool p_short = false); static void get_cached_resources(List<Ref<Resource>> *p_resources); static int get_cached_resource_count(); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 2469e1a4be..b1c50e829c 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -693,7 +693,7 @@ Error ResourceLoaderBinary::load() { } if (cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE && ResourceCache::has(path)) { - Ref<Resource> cached = ResourceCache::get(path); + Ref<Resource> cached = ResourceCache::get_ref(path); if (cached.is_valid()) { //already loaded, don't do anything error = OK; @@ -717,10 +717,10 @@ Error ResourceLoaderBinary::load() { if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(path)) { //use the existing one - Resource *r = ResourceCache::get(path); - if (r->get_class() == t) { - r->reset_state(); - res = Ref<Resource>(r); + Ref<Resource> cached = ResourceCache::get_ref(path); + if (cached->get_class() == t) { + cached->reset_state(); + res = cached; } } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index fb21db1a19..2cd455475c 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -335,23 +335,15 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String & thread_load_mutex->unlock(); ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Attempted to load a resource already being loaded from this thread, cyclic reference?"); } - //lock first if possible - ResourceCache::lock.read_lock(); - - //get ptr - Resource **rptr = ResourceCache::resources.getptr(local_path); - - if (rptr) { - Ref<Resource> res(*rptr); - //it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached - if (res.is_valid()) { - //referencing is fine - load_task.resource = res; - load_task.status = THREAD_LOAD_LOADED; - load_task.progress = 1.0; - } + + Ref<Resource> existing = ResourceCache::get_ref(local_path); + + if (existing.is_valid()) { + //referencing is fine + load_task.resource = existing; + load_task.status = THREAD_LOAD_LOADED; + load_task.progress = 1.0; } - ResourceCache::lock.read_unlock(); } if (!p_source_resource.is_empty()) { @@ -530,27 +522,18 @@ Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hi } //Is it cached? - ResourceCache::lock.read_lock(); - - Resource **rptr = ResourceCache::resources.getptr(local_path); - if (rptr) { - Ref<Resource> res(*rptr); + Ref<Resource> existing = ResourceCache::get_ref(local_path); - //it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached - if (res.is_valid()) { - ResourceCache::lock.read_unlock(); - thread_load_mutex->unlock(); - - if (r_error) { - *r_error = OK; - } + if (existing.is_valid()) { + thread_load_mutex->unlock(); - return res; //use cached + if (r_error) { + *r_error = OK; } - } - ResourceCache::lock.read_unlock(); + return existing; //use cached + } //load using task (but this thread) ThreadLoadTask load_task; @@ -867,7 +850,7 @@ String ResourceLoader::path_remap(const String &p_path) { } void ResourceLoader::reload_translation_remaps() { - ResourceCache::lock.read_lock(); + ResourceCache::lock.lock(); List<Resource *> to_reload; SelfList<Resource> *E = remapped_list.first(); @@ -877,7 +860,7 @@ void ResourceLoader::reload_translation_remaps() { E = E->next(); } - ResourceCache::lock.read_unlock(); + ResourceCache::lock.unlock(); //now just make sure to not delete any of these resources while changing locale.. while (to_reload.front()) { diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 65353d8118..ce5e9aa9b3 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -365,12 +365,12 @@ Basis Basis::rotated_local(const Vector3 &p_axis, real_t p_angle) const { return (*this) * Basis(p_axis, p_angle); } -Basis Basis::rotated(const Vector3 &p_euler) const { - return Basis(p_euler) * (*this); +Basis Basis::rotated(const Vector3 &p_euler, EulerOrder p_order) const { + return Basis::from_euler(p_euler, p_order) * (*this); } -void Basis::rotate(const Vector3 &p_euler) { - *this = rotated(p_euler); +void Basis::rotate(const Vector3 &p_euler, EulerOrder p_order) { + *this = rotated(p_euler, p_order); } Basis Basis::rotated(const Quaternion &p_quaternion) const { @@ -935,9 +935,9 @@ void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_angle, const Ve rotate(p_axis, p_angle); } -void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) { +void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale, EulerOrder p_order) { _set_diagonal(p_scale); - rotate(p_euler); + rotate(p_euler, p_order); } void Basis::set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale) { diff --git a/core/math/basis.h b/core/math/basis.h index 9cce22510b..4be325cdd2 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -56,6 +56,15 @@ struct _NO_DISCARD_ Basis { _FORCE_INLINE_ real_t determinant() const; + enum EulerOrder { + EULER_ORDER_XYZ, + EULER_ORDER_XZY, + EULER_ORDER_YXZ, + EULER_ORDER_YZX, + EULER_ORDER_ZXY, + EULER_ORDER_ZYX + }; + void from_z(const Vector3 &p_z); void rotate(const Vector3 &p_axis, real_t p_angle); @@ -64,21 +73,12 @@ struct _NO_DISCARD_ Basis { void rotate_local(const Vector3 &p_axis, real_t p_angle); Basis rotated_local(const Vector3 &p_axis, real_t p_angle) const; - void rotate(const Vector3 &p_euler); - Basis rotated(const Vector3 &p_euler) const; + void rotate(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ); + Basis rotated(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ) const; void rotate(const Quaternion &p_quaternion); Basis rotated(const Quaternion &p_quaternion) const; - enum EulerOrder { - EULER_ORDER_XYZ, - EULER_ORDER_XZY, - EULER_ORDER_YXZ, - EULER_ORDER_YZX, - EULER_ORDER_ZXY, - EULER_ORDER_ZYX - }; - Vector3 get_euler_normalized(EulerOrder p_order = EULER_ORDER_YXZ) const; void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const; void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const; @@ -119,7 +119,7 @@ struct _NO_DISCARD_ Basis { Vector3 get_scale_local() const; void set_axis_angle_scale(const Vector3 &p_axis, real_t p_angle, const Vector3 &p_scale); - void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale); + void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale, EulerOrder p_order = EULER_ORDER_YXZ); void set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale); // transposed dot products diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h index f8a10ec87e..4ab00e1f34 100644 --- a/core/math/delaunay_3d.h +++ b/core/math/delaunay_3d.h @@ -101,7 +101,7 @@ class Delaunay3D { _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); + return hash_fmix32(hash_djb2_one_32(p_triangle.triangle[2], h)); } }; diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 5a90f68b66..e230b69dc9 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -891,7 +891,7 @@ Expression::ENode *Expression::_parse_expression() { case TK_PERIOD: { //named indexing or function call _get_token(tk); - if (tk.type != TK_IDENTIFIER) { + if (tk.type != TK_IDENTIFIER && tk.type != TK_BUILTIN_FUNC) { _set_error("Expected identifier after '.'"); return nullptr; } @@ -1240,7 +1240,7 @@ bool Expression::_compile_expression() { return false; } -bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) { +bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, bool p_const_calls_only, String &r_error_str) { switch (p_node->type) { case Expression::ENode::TYPE_INPUT: { const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node); @@ -1266,7 +1266,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: 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); + bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, p_const_calls_only, r_error_str); if (ret) { return true; } @@ -1274,7 +1274,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: Variant b; if (op->nodes[1]) { - ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str); + ret = _execute(p_inputs, p_instance, op->nodes[1], b, p_const_calls_only, r_error_str); if (ret) { return true; } @@ -1292,14 +1292,14 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: 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); + bool ret = _execute(p_inputs, p_instance, index->base, base, p_const_calls_only, r_error_str); if (ret) { return true; } Variant idx; - ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str); + ret = _execute(p_inputs, p_instance, index->index, idx, p_const_calls_only, r_error_str); if (ret) { return true; } @@ -1316,7 +1316,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: 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); + bool ret = _execute(p_inputs, p_instance, index->base, base, p_const_calls_only, r_error_str); if (ret) { return true; } @@ -1336,7 +1336,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: 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); + bool ret = _execute(p_inputs, p_instance, array->array[i], value, p_const_calls_only, r_error_str); if (ret) { return true; @@ -1353,14 +1353,14 @@ 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); + bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, p_const_calls_only, r_error_str); if (ret) { return true; } Variant value; - ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str); + ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, p_const_calls_only, r_error_str); if (ret) { return true; } @@ -1380,7 +1380,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: 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); + bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, p_const_calls_only, r_error_str); if (ret) { return true; @@ -1408,7 +1408,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: 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); + bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, p_const_calls_only, r_error_str); if (ret) { return true; } @@ -1429,7 +1429,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: 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); + bool ret = _execute(p_inputs, p_instance, call->base, base, p_const_calls_only, r_error_str); if (ret) { return true; @@ -1442,7 +1442,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: for (int i = 0; i < call->arguments.size(); i++) { Variant value; - ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str); + ret = _execute(p_inputs, p_instance, call->arguments[i], value, p_const_calls_only, r_error_str); if (ret) { return true; @@ -1452,7 +1452,11 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression: } Callable::CallError ce; - base.callp(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce); + if (p_const_calls_only) { + base.call_const(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce); + } else { + base.callp(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)); @@ -1491,13 +1495,13 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu return OK; } -Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { +Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error, bool p_const_calls_only) { ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + "."); execution_error = false; Variant output; String error_txt; - bool err = _execute(p_inputs, p_base, root, output, error_txt); + bool err = _execute(p_inputs, p_base, root, output, p_const_calls_only, error_txt); if (err) { execution_error = true; error_str = error_txt; @@ -1517,7 +1521,7 @@ 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("execute", "inputs", "base_instance", "show_error", "const_calls_only"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed); ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text); } diff --git a/core/math/expression.h b/core/math/expression.h index 6ea3c1611f..2d58915996 100644 --- a/core/math/expression.h +++ b/core/math/expression.h @@ -257,14 +257,14 @@ private: Vector<String> input_names; bool execution_error = false; - bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str); + bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, bool p_const_calls_only, String &r_error_str); protected: static void _bind_methods(); public: Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>()); - Variant execute(Array p_inputs = Array(), 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 p_const_calls_only = false); bool has_execute_failed() const; String get_error_text() const; diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index c8a55341aa..53deb9bd42 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -253,6 +253,27 @@ public: (-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight)); } + static _ALWAYS_INLINE_ double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + /* Formula from Wikipedia article on Bezier curves. */ + double omt = (1.0 - p_t); + double omt2 = omt * omt; + double omt3 = omt2 * omt; + double t2 = p_t * p_t; + double t3 = t2 * p_t; + + return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; + } + static _ALWAYS_INLINE_ float bezier_interpolate(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) { + /* Formula from Wikipedia article on Bezier curves. */ + float omt = (1.0f - p_t); + float omt2 = omt * omt; + float omt3 = omt2 * omt; + float t2 = p_t * p_t; + float t3 = t2 * p_t; + + return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0f + p_control_2 * omt * t2 * 3.0f + p_end * t3; + } + static _ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) { double difference = fmod(p_to - p_from, Math_TAU); double distance = fmod(2.0 * difference, Math_TAU) - difference; diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index 54461bf70f..4433559e6d 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -104,9 +104,11 @@ void TriangleMesh::get_indices(Vector<int> *r_triangles_indices) const { } } -void TriangleMesh::create(const Vector<Vector3> &p_faces) { +void TriangleMesh::create(const Vector<Vector3> &p_faces, const Vector<int32_t> &p_surface_indices) { valid = false; + ERR_FAIL_COND(p_surface_indices.size() && p_surface_indices.size() != p_faces.size()); + int fc = p_faces.size(); ERR_FAIL_COND(!fc || ((fc % 3) != 0)); fc /= 3; @@ -121,6 +123,7 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) { //goes in-place. const Vector3 *r = p_faces.ptr(); + const int32_t *si = p_surface_indices.ptr(); Triangle *w = triangles.ptrw(); HashMap<Vector3, int> db; @@ -148,6 +151,7 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) { } f.normal = Face3(r[i * 3 + 0], r[i * 3 + 1], r[i * 3 + 2]).get_plane().get_normal(); + f.surface_index = si ? si[i] : 0; bw[i].left = -1; bw[i].right = -1; @@ -264,7 +268,7 @@ Vector3 TriangleMesh::get_area_normal(const AABB &p_aabb) const { return n; } -bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const { +bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index) const { uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); enum { @@ -317,6 +321,9 @@ bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_en d = nd; r_point = res; r_normal = f3.get_plane().get_normal(); + if (r_surf_index) { + *r_surf_index = s.surface_index; + } inters = true; } } @@ -366,7 +373,7 @@ bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_en return inters; } -bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const { +bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index) const { uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); enum { @@ -417,6 +424,9 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V d = nd; r_point = res; r_normal = f3.get_plane().get_normal(); + if (r_surf_index) { + *r_surf_index = s.surface_index; + } inters = true; } } diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index 1b99945698..166b4adb7a 100644 --- a/core/math/triangle_mesh.h +++ b/core/math/triangle_mesh.h @@ -41,6 +41,7 @@ public: struct Triangle { Vector3 normal; int indices[3]; + int32_t surface_index; }; private: @@ -81,8 +82,8 @@ private: public: bool is_valid() const; - bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const; - bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const; + bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index = nullptr) const; + bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index = nullptr) const; bool intersect_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const; bool inside_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, Vector3 p_scale = Vector3(1, 1, 1)) const; Vector3 get_area_normal(const AABB &p_aabb) const; @@ -92,7 +93,7 @@ public: const Vector<Vector3> &get_vertices() const { return vertices; } void get_indices(Vector<int> *r_triangles_indices) const; - void create(const Vector<Vector3> &p_faces); + void create(const Vector<Vector3> &p_faces, const Vector<int32_t> &p_surface_indices = Vector<int32_t>()); TriangleMesh(); }; diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index a27227905c..d9b5d55454 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -152,13 +152,6 @@ Vector2 Vector2::limit_length(const 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, const real_t p_weight) const { - Vector2 res = *this; - res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); - res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); - return res; -} - Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const { Vector2 v = *this; Vector2 vd = p_to - v; diff --git a/core/math/vector2.h b/core/math/vector2.h index bd67299f33..91d3d3a56b 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -113,7 +113,9 @@ struct _NO_DISCARD_ Vector2 { _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, const real_t p_weight) const; _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, const real_t p_weight) const; - Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const; + _FORCE_INLINE_ Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const; + _FORCE_INLINE_ Vector2 bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const; + Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const; Vector2 slide(const Vector2 &p_normal) const; @@ -261,6 +263,26 @@ Vector2 Vector2::slerp(const Vector2 &p_to, const real_t p_weight) const { return rotated(angle * p_weight) * (result_length / start_length); } +Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const { + Vector2 res = *this; + res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); + res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); + return res; +} + +Vector2 Vector2::bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const { + Vector2 res = *this; + + /* Formula from Wikipedia article on Bezier curves. */ + real_t omt = (1.0 - p_t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = p_t * p_t; + real_t t3 = t2 * p_t; + + return res * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; +} + Vector2 Vector2::direction_to(const Vector2 &p_to) const { Vector2 ret(p_to.x - x, p_to.y - y); ret.normalize(); diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index f94f39b7f2..d71d365053 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -85,14 +85,6 @@ Vector3 Vector3::limit_length(const real_t p_len) const { return v; } -Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const { - Vector3 res = *this; - res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); - res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); - res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight); - return res; -} - Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const { Vector3 v = *this; Vector3 vd = p_to - v; diff --git a/core/math/vector3.h b/core/math/vector3.h index 8891532f42..970416234d 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -104,7 +104,9 @@ struct _NO_DISCARD_ Vector3 { _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, const real_t p_weight) const; _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, const real_t p_weight) const; - Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const; + _FORCE_INLINE_ Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const; + _FORCE_INLINE_ Vector3 bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const; + Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const; Vector2 octahedron_encode() const; @@ -227,6 +229,27 @@ Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const { return rotated(cross(p_to).normalized(), angle * p_weight) * (result_length / start_length); } +Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const { + Vector3 res = *this; + res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); + res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); + res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight); + return res; +} + +Vector3 Vector3::bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const { + Vector3 res = *this; + + /* Formula from Wikipedia article on Bezier curves. */ + real_t omt = (1.0 - p_t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = p_t * p_t; + real_t t3 = t2 * p_t; + + return res * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; +} + real_t Vector3::distance_to(const Vector3 &p_to) const { return (p_to - *this).length(); } diff --git a/core/multiplayer/multiplayer_peer.cpp b/core/multiplayer/multiplayer_peer.cpp index ae3b139bcc..b262903ce8 100644 --- a/core/multiplayer/multiplayer_peer.cpp +++ b/core/multiplayer/multiplayer_peer.cpp @@ -36,17 +36,18 @@ uint32_t MultiplayerPeer::generate_unique_id() const { uint32_t hash = 0; while (hash == 0 || hash == 1) { - hash = hash_djb2_one_32( + hash = hash_murmur3_one_32( (uint32_t)OS::get_singleton()->get_ticks_usec()); - hash = hash_djb2_one_32( + hash = hash_murmur3_one_32( (uint32_t)OS::get_singleton()->get_unix_time(), hash); - hash = hash_djb2_one_32( + hash = hash_murmur3_one_32( (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash); - hash = hash_djb2_one_32( + hash = hash_murmur3_one_32( (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap - hash = hash_djb2_one_32( + hash = hash_murmur3_one_32( (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack + hash = hash_fmix32(hash); hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion } diff --git a/core/object/callable_method_pointer.cpp b/core/object/callable_method_pointer.cpp index 1bf926cafc..81f8ab6be2 100644 --- a/core/object/callable_method_pointer.cpp +++ b/core/object/callable_method_pointer.cpp @@ -85,9 +85,9 @@ void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_pt // Precompute hash. for (uint32_t i = 0; i < comp_size; i++) { if (i == 0) { - h = hash_djb2_one_32(comp_ptr[i]); + h = hash_murmur3_one_32(comp_ptr[i]); } else { - h = hash_djb2_one_32(comp_ptr[i], h); + h = hash_murmur3_one_32(comp_ptr[i], h); } } } diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 533cb6e789..3c9f373d12 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -164,7 +164,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { OBJTYPE_RLOCK; #ifdef DEBUG_METHODS_ENABLED - uint64_t hash = hash_djb2_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG)); + uint64_t hash = hash_murmur3_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG)); List<StringName> class_list; ClassDB::get_class_list(&class_list); @@ -177,8 +177,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { if (t->api != p_api || !t->exposed) { continue; } - hash = hash_djb2_one_64(t->name.hash(), hash); - hash = hash_djb2_one_64(t->inherits.hash(), hash); + hash = hash_murmur3_one_64(t->name.hash(), hash); + hash = hash_murmur3_one_64(t->inherits.hash(), hash); { //methods @@ -200,27 +200,27 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { for (const StringName &F : snames) { MethodBind *mb = t->method_map[F]; - hash = hash_djb2_one_64(mb->get_name().hash(), hash); - hash = hash_djb2_one_64(mb->get_argument_count(), hash); - hash = hash_djb2_one_64(mb->get_argument_type(-1), hash); //return + hash = hash_murmur3_one_64(mb->get_name().hash(), hash); + hash = hash_murmur3_one_64(mb->get_argument_count(), hash); + hash = hash_murmur3_one_64(mb->get_argument_type(-1), hash); //return for (int i = 0; i < mb->get_argument_count(); i++) { const PropertyInfo info = mb->get_argument_info(i); - hash = hash_djb2_one_64(info.type, hash); - hash = hash_djb2_one_64(info.name.hash(), hash); - hash = hash_djb2_one_64(info.hint, hash); - hash = hash_djb2_one_64(info.hint_string.hash(), hash); + hash = hash_murmur3_one_64(info.type, hash); + hash = hash_murmur3_one_64(info.name.hash(), hash); + hash = hash_murmur3_one_64(info.hint, hash); + hash = hash_murmur3_one_64(info.hint_string.hash(), hash); } - hash = hash_djb2_one_64(mb->get_default_argument_count(), hash); + hash = hash_murmur3_one_64(mb->get_default_argument_count(), hash); for (int i = 0; i < mb->get_default_argument_count(); i++) { //hash should not change, i hope for tis Variant da = mb->get_default_argument(i); - hash = hash_djb2_one_64(da.hash(), hash); + hash = hash_murmur3_one_64(da.hash(), hash); } - hash = hash_djb2_one_64(mb->get_hint_flags(), hash); + hash = hash_murmur3_one_64(mb->get_hint_flags(), hash); } } @@ -235,8 +235,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); for (const StringName &F : snames) { - hash = hash_djb2_one_64(F.hash(), hash); - hash = hash_djb2_one_64(t->constant_map[F], hash); + hash = hash_murmur3_one_64(F.hash(), hash); + hash = hash_murmur3_one_64(t->constant_map[F], hash); } } @@ -252,9 +252,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { for (const StringName &F : snames) { MethodInfo &mi = t->signal_map[F]; - hash = hash_djb2_one_64(F.hash(), hash); + hash = hash_murmur3_one_64(F.hash(), hash); for (int i = 0; i < mi.arguments.size(); i++) { - hash = hash_djb2_one_64(mi.arguments[i].type, hash); + hash = hash_murmur3_one_64(mi.arguments[i].type, hash); } } } @@ -273,23 +273,23 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { PropertySetGet *psg = t->property_setget.getptr(F); ERR_FAIL_COND_V(!psg, 0); - hash = hash_djb2_one_64(F.hash(), hash); - hash = hash_djb2_one_64(psg->setter.hash(), hash); - hash = hash_djb2_one_64(psg->getter.hash(), hash); + hash = hash_murmur3_one_64(F.hash(), hash); + hash = hash_murmur3_one_64(psg->setter.hash(), hash); + hash = hash_murmur3_one_64(psg->getter.hash(), hash); } } //property list for (const PropertyInfo &F : t->property_list) { - hash = hash_djb2_one_64(F.name.hash(), hash); - hash = hash_djb2_one_64(F.type, hash); - hash = hash_djb2_one_64(F.hint, hash); - hash = hash_djb2_one_64(F.hint_string.hash(), hash); - hash = hash_djb2_one_64(F.usage, hash); + hash = hash_murmur3_one_64(F.name.hash(), hash); + hash = hash_murmur3_one_64(F.type, hash); + hash = hash_murmur3_one_64(F.hint, hash); + hash = hash_murmur3_one_64(F.hint_string.hash(), hash); + hash = hash_murmur3_one_64(F.usage, hash); } } - return hash; + return hash_fmix32(hash); #else return 0; #endif diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp index a208c1a2b2..a4474ea53b 100644 --- a/core/object/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -35,32 +35,27 @@ #include "method_bind.h" uint32_t MethodBind::get_hash() const { - uint32_t hash = hash_djb2_one_32(has_return() ? 1 : 0); - hash = hash_djb2_one_32(get_argument_count(), hash); - -#ifndef _MSC_VER -#warning This needs proper class name and argument type for hashing -#endif -#if 0 + uint32_t hash = hash_murmur3_one_32(has_return() ? 1 : 0); + hash = hash_murmur3_one_32(get_argument_count(), hash); for (int i = (has_return() ? -1 : 0); i < get_argument_count(); i++) { PropertyInfo pi = i == -1 ? get_return_info() : get_argument_info(i); - hash = hash_djb2_one_32(get_argument_type(i), hash); + hash = hash_murmur3_one_32(get_argument_type(i), hash); if (pi.class_name != StringName()) { - hash = hash_djb2_one_32(pi.class_name.operator String().hash(), hash); + hash = hash_murmur3_one_32(pi.class_name.operator String().hash(), hash); } } -#endif - hash = hash_djb2_one_32(get_default_argument_count(), hash); + + hash = hash_murmur3_one_32(get_default_argument_count(), hash); for (int i = 0; i < get_default_argument_count(); i++) { Variant v = get_default_argument(i); - hash = hash_djb2_one_32(v.hash(), hash); + hash = hash_murmur3_one_32(v.hash(), hash); } - hash = hash_djb2_one_32(is_const(), hash); - hash = hash_djb2_one_32(is_vararg(), hash); + hash = hash_murmur3_one_32(is_const(), hash); + hash = hash_murmur3_one_32(is_vararg(), hash); - return hash; + return hash_fmix32(hash); } PropertyInfo MethodBind::get_argument_info(int p_argument) const { diff --git a/core/object/method_bind.h b/core/object/method_bind.h index 2870195911..d60550c899 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -33,20 +33,6 @@ #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_FLAG_STATIC = 256, - METHOD_FLAG_OBJECT_CORE = 512, - METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, -}; - VARIANT_ENUM_CAST(MethodFlags) // some helpers diff --git a/core/object/object.cpp b/core/object/object.cpp index 9dec417b11..5f2287c9d3 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -161,161 +161,6 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) { return mi; } -MethodInfo::MethodInfo() : - flags(METHOD_FLAG_NORMAL) {} - -MethodInfo::MethodInfo(const String &p_name) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { -} - -MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1) : - name(p_name), - 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) { - 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) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); -} - -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) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); -} - -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) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); - arguments.push_back(p_param5); -} - -MethodInfo::MethodInfo(Variant::Type ret) : - flags(METHOD_FLAG_NORMAL) { - return_val.type = ret; -} - -MethodInfo::MethodInfo(Variant::Type ret, const String &p_name) : - name(p_name), - 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) { - 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) { - return_val.type = ret; - arguments.push_back(p_param1); - arguments.push_back(p_param2); -} - -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) { - return_val.type = ret; - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); -} - -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) { - return_val.type = ret; - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); -} - -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) { - return_val.type = ret; - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); - arguments.push_back(p_param5); -} - -MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name) : - name(p_name), - return_val(p_ret), - 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) { - 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) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); -} - -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) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); -} - -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) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); -} - -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) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); - arguments.push_back(p_param5); -} - Object::Connection::operator Variant() const { Dictionary d; d["signal"] = signal; @@ -648,7 +493,7 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons uint32_t pcount; const GDNativePropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount); for (uint32_t i = 0; i < pcount; i++) { - p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name)); + p_list->push_back(PropertyInfo(pinfo[i])); } if (_extension->free_property_list) { _extension->free_property_list(_extension_instance, pinfo); @@ -824,6 +669,51 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: + case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST: + return ret; + case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: { + } + } + } + + //extension does not need this, because all methods are registered in MethodBind + + MethodBind *method = ClassDB::get_method(get_class_name(), p_method); + + if (method) { + ret = method->call(this, p_args, p_argcount, r_error); + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + } + + return ret; +} + +Variant Object::call_const(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) { + // Free is not const, so fail. + r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST; + return Variant(); + } + + Variant ret; + OBJ_DEBUG_LOCK + + if (script_instance) { + ret = script_instance->call_const(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: + break; + case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST: + break; + case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: + case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: + case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: return ret; case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: { } @@ -835,6 +725,10 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_ MethodBind *method = ClassDB::get_method(get_class_name(), p_method); if (method) { + if (!method->is_const()) { + r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST; + return ret; + } ret = method->call(this, p_args, p_argcount, r_error); } else { r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; diff --git a/core/object/object.h b/core/object/object.h index 7cbedd29d9..1f6386e6b4 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -47,7 +47,7 @@ enum PropertyHint { PROPERTY_HINT_NONE, ///< no hint provided. - PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,noslider][,radians][,degrees][,exp][,suffix:<keyword>] range. + PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,no_slider][,radians][,degrees][,exp][,suffix:<keyword>] range. PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout") @@ -67,6 +67,7 @@ enum PropertyHint { PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines + PROPERTY_HINT_EXPRESSION, ///< used for string properties that can contain multiple lines PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color PROPERTY_HINT_IMAGE_COMPRESS_LOSSY, @@ -85,11 +86,13 @@ enum PropertyHint { PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog + PROPERTY_HINT_GLOBAL_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog PROPERTY_HINT_INT_IS_OBJECTID, PROPERTY_HINT_ARRAY_TYPE, PROPERTY_HINT_INT_IS_POINTER, PROPERTY_HINT_LOCALE_ID, PROPERTY_HINT_LOCALIZABLE_STRING, + PROPERTY_HINT_NODE_TYPE, ///< a node object type PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; @@ -187,6 +190,14 @@ struct PropertyInfo { type(Variant::OBJECT), class_name(p_class_name) {} + explicit PropertyInfo(const GDNativePropertyInfo &pinfo) : + type((Variant::Type)pinfo.type), + name(pinfo.name), + class_name(pinfo.class_name), // can be null + hint((PropertyHint)pinfo.hint), + hint_string(pinfo.hint_string), // can be null + usage(pinfo.usage) {} + bool operator==(const PropertyInfo &p_info) const { return ((type == p_info.type) && (name == p_info.name) && @@ -203,10 +214,24 @@ struct PropertyInfo { Array convert_property_list(const List<PropertyInfo> *p_list); +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_FLAG_STATIC = 256, + METHOD_FLAG_OBJECT_CORE = 512, + METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, +}; + struct MethodInfo { String name; PropertyInfo return_val; - uint32_t flags; // NOLINT - prevent clang-tidy to assign method_bind.h constant here, it should stay in .cpp. + uint32_t flags = METHOD_FLAGS_DEFAULT; int id = 0; List<PropertyInfo> arguments; Vector<Variant> default_arguments; @@ -218,26 +243,50 @@ struct MethodInfo { static MethodInfo from_dict(const Dictionary &p_dict); - MethodInfo(); - MethodInfo(const String &p_name); - MethodInfo(const String &p_name, const PropertyInfo &p_param1); - MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2); - MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3); - MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4); - 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); - MethodInfo(Variant::Type ret); - MethodInfo(Variant::Type ret, const String &p_name); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4); - 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); - MethodInfo(const PropertyInfo &p_ret, const String &p_name); - MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1); - MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2); - MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3); - 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); - 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); + MethodInfo() {} + + void _push_params(const PropertyInfo &p_param) { + arguments.push_back(p_param); + } + + template <typename... VarArgs> + void _push_params(const PropertyInfo &p_param, VarArgs... p_params) { + arguments.push_back(p_param); + _push_params(p_params...); + } + + MethodInfo(const String &p_name) { name = p_name; } + + template <typename... VarArgs> + MethodInfo(const String &p_name, VarArgs... p_params) { + name = p_name; + _push_params(p_params...); + } + + MethodInfo(Variant::Type ret) { return_val.type = ret; } + MethodInfo(Variant::Type ret, const String &p_name) { + return_val.type = ret; + name = p_name; + } + + template <typename... VarArgs> + MethodInfo(Variant::Type ret, const String &p_name, VarArgs... p_params) { + name = p_name; + return_val.type = ret; + _push_params(p_params...); + } + + MethodInfo(const PropertyInfo &p_ret, const String &p_name) { + return_val = p_ret; + name = p_name; + } + + template <typename... VarArgs> + MethodInfo(const PropertyInfo &p_ret, const String &p_name, VarArgs... p_params) { + return_val = p_ret; + name = p_name; + _push_params(p_params...); + } }; // API used to extend in GDNative and other C compatible compiled languages. @@ -719,6 +768,7 @@ public: void get_method_list(List<MethodInfo> *p_list) const; Variant callv(const StringName &p_method, const Array &p_args); virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); template <typename... VarArgs> Variant call(const StringName &p_method, VarArgs... p_args) { diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 66c9a80193..4623d0e525 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -295,6 +295,11 @@ void ScriptServer::save_global_classes() { } //////////////////// + +Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + return callp(p_method, p_args, p_argcount, r_error); +} + void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) { List<PropertyInfo> pinfo; get_property_list(&pinfo); diff --git a/core/object/script_language.h b/core/object/script_language.h index 0a8e79a24e..776a9bfaab 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -190,6 +190,7 @@ public: return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr); } + virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions virtual void notification(int p_notification) = 0; virtual String to_string(bool *r_valid) { if (r_valid) { diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 406a431a11..7eea48370e 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -671,7 +671,7 @@ public: uint32_t pcount; const GDNativePropertyInfo *pinfo = native_info->get_property_list_func(instance, &pcount); for (uint32_t i = 0; i < pcount; i++) { - p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name)); + p_list->push_back(PropertyInfo(pinfo[i])); } if (native_info->free_property_list_func) { native_info->free_property_list_func(instance, pinfo); @@ -716,9 +716,9 @@ public: m.name = minfo[i].name; m.flags = minfo[i].flags; m.id = minfo[i].id; - m.return_val = PropertyInfo(Variant::Type(minfo[i].return_value.type), minfo[i].return_value.class_name, PropertyHint(minfo[i].return_value.hint), minfo[i].return_value.hint_string, minfo[i].return_value.usage, minfo[i].return_value.class_name); + m.return_val = PropertyInfo(minfo[i].return_value); for (uint32_t j = 0; j < minfo[i].argument_count; j++) { - m.arguments.push_back(PropertyInfo(Variant::Type(minfo[i].arguments[j].type), minfo[i].arguments[j].class_name, PropertyHint(minfo[i].arguments[j].hint), minfo[i].arguments[j].hint_string, minfo[i].arguments[j].usage, minfo[i].arguments[j].class_name)); + m.arguments.push_back(PropertyInfo(minfo[i].arguments[j])); } const Variant *def_values = (const Variant *)minfo[i].default_arguments; for (uint32_t j = 0; j < minfo[i].default_argument_count; j++) { diff --git a/core/os/os.cpp b/core/os/os.cpp index 327f1c95f2..b9daf6fa53 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -100,6 +100,21 @@ void OS::print(const char *p_format, ...) { va_end(argp); } +void OS::print_rich(const char *p_format, ...) { + if (!_stdout_enabled) { + return; + } + + va_list argp; + va_start(argp, p_format); + + if (_logger) { + _logger->logv(p_format, argp, false); + } + + va_end(argp); +} + void OS::printerr(const char *p_format, ...) { if (!_stderr_enabled) { return; @@ -388,6 +403,10 @@ bool OS::has_feature(const String &p_feature) { return true; } + if (p_feature == "movie") { + return _writing_movie; + } + #ifdef DEBUG_ENABLED if (p_feature == "debug") { return true; diff --git a/core/os/os.h b/core/os/os.h index 157b8ab992..af6c38cbe0 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -58,6 +58,7 @@ class OS { bool _allow_layered = false; bool _stdout_enabled = true; bool _stderr_enabled = true; + bool _writing_movie = false; CompositeLogger *_logger = nullptr; @@ -119,6 +120,7 @@ public: void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, Logger::ErrorType p_type = Logger::ERR_ERROR); void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; + void print_rich(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; virtual String get_stdin_string(bool p_block = true) = 0; diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index 238897c2b1..30fa434fad 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -199,6 +199,21 @@ Vector<StringName> NodePath::get_subnames() const { return Vector<StringName>(); } +StringName NodePath::get_concatenated_names() const { + ERR_FAIL_COND_V(!data, StringName()); + + if (!data->concatenated_path) { + int pc = data->path.size(); + String concatenated; + const StringName *sn = data->path.ptr(); + for (int i = 0; i < pc; i++) { + concatenated += i == 0 ? sn[i].operator String() : "/" + sn[i]; + } + data->concatenated_path = concatenated; + } + return data->concatenated_path; +} + StringName NodePath::get_concatenated_subnames() const { ERR_FAIL_COND_V(!data, StringName()); diff --git a/core/string/node_path.h b/core/string/node_path.h index 53976bd524..2bce33e21e 100644 --- a/core/string/node_path.h +++ b/core/string/node_path.h @@ -39,6 +39,7 @@ class NodePath { SafeRefCount refcount; Vector<StringName> path; Vector<StringName> subpath; + StringName concatenated_path; StringName concatenated_subpath; bool absolute; bool has_slashes; @@ -59,6 +60,7 @@ public: StringName get_subname(int p_idx) const; Vector<StringName> get_names() const; Vector<StringName> get_subnames() const; + StringName get_concatenated_names() const; StringName get_concatenated_subnames() const; NodePath rel_path_to(const NodePath &p_np) const; diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp index 919c9e08e3..f58486e0a5 100644 --- a/core/string/print_string.cpp +++ b/core/string/print_string.cpp @@ -79,7 +79,98 @@ void __print_line(String p_string) { _global_lock(); PrintHandlerList *l = print_handler_list; while (l) { - l->printfunc(l->userdata, p_string, false); + l->printfunc(l->userdata, p_string, false, false); + l = l->next; + } + + _global_unlock(); +} + +void __print_line_rich(String p_string) { + if (!_print_line_enabled) { + return; + } + + // Convert a subset of BBCode tags to ANSI escape codes for correct display in the terminal. + // Support of those ANSI escape codes varies across terminal emulators, + // especially for italic and strikethrough. + String p_string_ansi = p_string; + + p_string_ansi = p_string_ansi.replace("[b]", "\u001b[1m"); + p_string_ansi = p_string_ansi.replace("[/b]", "\u001b[22m"); + p_string_ansi = p_string_ansi.replace("[i]", "\u001b[3m"); + p_string_ansi = p_string_ansi.replace("[/i]", "\u001b[23m"); + p_string_ansi = p_string_ansi.replace("[u]", "\u001b[4m"); + p_string_ansi = p_string_ansi.replace("[/u]", "\u001b[24m"); + p_string_ansi = p_string_ansi.replace("[s]", "\u001b[9m"); + p_string_ansi = p_string_ansi.replace("[/s]", "\u001b[29m"); + + p_string_ansi = p_string_ansi.replace("[indent]", " "); + p_string_ansi = p_string_ansi.replace("[/indent]", ""); + p_string_ansi = p_string_ansi.replace("[code]", "\u001b[2m"); + p_string_ansi = p_string_ansi.replace("[/code]", "\u001b[22m"); + p_string_ansi = p_string_ansi.replace("[url]", ""); + p_string_ansi = p_string_ansi.replace("[/url]", ""); + p_string_ansi = p_string_ansi.replace("[center]", "\n\t\t\t"); + p_string_ansi = p_string_ansi.replace("[/center]", ""); + p_string_ansi = p_string_ansi.replace("[right]", "\n\t\t\t\t\t\t"); + p_string_ansi = p_string_ansi.replace("[/right]", ""); + + if (p_string_ansi.contains("[color")) { + p_string_ansi = p_string_ansi.replace("[color=black]", "\u001b[30m"); + p_string_ansi = p_string_ansi.replace("[color=red]", "\u001b[91m"); + p_string_ansi = p_string_ansi.replace("[color=green]", "\u001b[92m"); + p_string_ansi = p_string_ansi.replace("[color=lime]", "\u001b[92m"); + p_string_ansi = p_string_ansi.replace("[color=yellow]", "\u001b[93m"); + p_string_ansi = p_string_ansi.replace("[color=blue]", "\u001b[94m"); + p_string_ansi = p_string_ansi.replace("[color=magenta]", "\u001b[95m"); + p_string_ansi = p_string_ansi.replace("[color=pink]", "\u001b[38;5;218m"); + p_string_ansi = p_string_ansi.replace("[color=purple]", "\u001b[38;5;98m"); + p_string_ansi = p_string_ansi.replace("[color=cyan]", "\u001b[96m"); + p_string_ansi = p_string_ansi.replace("[color=white]", "\u001b[97m"); + p_string_ansi = p_string_ansi.replace("[color=orange]", "\u001b[38;5;208m"); + p_string_ansi = p_string_ansi.replace("[color=gray]", "\u001b[90m"); + p_string_ansi = p_string_ansi.replace("[/color]", "\u001b[39m"); + } + if (p_string_ansi.contains("[bgcolor")) { + p_string_ansi = p_string_ansi.replace("[bgcolor=black]", "\u001b[40m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=red]", "\u001b[101m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=green]", "\u001b[102m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=lime]", "\u001b[102m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=yellow]", "\u001b[103m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=blue]", "\u001b[104m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=magenta]", "\u001b[105m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=pink]", "\u001b[48;5;218m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=purple]", "\u001b[48;5;98m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=cyan]", "\u001b[106m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=white]", "\u001b[107m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=orange]", "\u001b[48;5;208m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=gray]", "\u001b[100m"); + p_string_ansi = p_string_ansi.replace("[/bgcolor]", "\u001b[49m"); + } + if (p_string_ansi.contains("[fgcolor")) { + p_string_ansi = p_string_ansi.replace("[fgcolor=black]", "\u001b[30;40m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=red]", "\u001b[91;101m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=green]", "\u001b[92;102m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=lime]", "\u001b[92;102m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=yellow]", "\u001b[93;103m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=blue]", "\u001b[94;104m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=magenta]", "\u001b[95;105m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=pink]", "\u001b[38;5;218;48;5;218m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=purple]", "\u001b[38;5;98;48;5;98m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=cyan]", "\u001b[96;106m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=white]", "\u001b[97;107m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=orange]", "\u001b[38;5;208;48;5;208m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=gray]", "\u001b[90;100m"); + p_string_ansi = p_string_ansi.replace("[/fgcolor]", "\u001b[39;49m"); + } + + OS::get_singleton()->print_rich("%s\n", p_string_ansi.utf8().get_data()); + + _global_lock(); + PrintHandlerList *l = print_handler_list; + while (l) { + l->printfunc(l->userdata, p_string, false, true); l = l->next; } @@ -96,7 +187,7 @@ void print_error(String p_string) { _global_lock(); PrintHandlerList *l = print_handler_list; while (l) { - l->printfunc(l->userdata, p_string, true); + l->printfunc(l->userdata, p_string, true, false); l = l->next; } diff --git a/core/string/print_string.h b/core/string/print_string.h index f7d0f25030..823e2c29e8 100644 --- a/core/string/print_string.h +++ b/core/string/print_string.h @@ -35,7 +35,7 @@ extern void (*_print_func)(String); -typedef void (*PrintHandlerFunc)(void *, const String &p_string, bool p_error); +typedef void (*PrintHandlerFunc)(void *, const String &p_string, bool p_error, bool p_rich); struct PrintHandlerList { PrintHandlerFunc printfunc = nullptr; @@ -59,6 +59,7 @@ void remove_print_handler(const PrintHandlerList *p_handler); extern bool _print_line_enabled; extern bool _print_error_enabled; extern void __print_line(String p_string); +extern void __print_line_rich(String p_string); extern void print_error(String p_string); extern void print_verbose(String p_string); @@ -66,9 +67,18 @@ inline void print_line(Variant v) { __print_line(stringify_variants(v)); } +inline void print_line_rich(Variant v) { + __print_line_rich(stringify_variants(v)); +} + template <typename... Args> void print_line(Variant p_var, Args... p_args) { __print_line(stringify_variants(p_var, p_args...)); } +template <typename... Args> +void print_line_rich(Variant p_var, Args... p_args) { + __print_line_rich(stringify_variants(p_var, p_args...)); +} + #endif // PRINT_STRING_H diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h index e5f73171a2..191f21a3dd 100644 --- a/core/templates/hash_map.h +++ b/core/templates/hash_map.h @@ -91,9 +91,9 @@ private: return hash; } - _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_capacity) const { - uint32_t original_pos = p_hash % p_capacity; - return (p_pos - original_pos + p_capacity) % p_capacity; + static _FORCE_INLINE_ uint32_t _get_probe_length(const uint32_t p_pos, const uint32_t p_hash, const uint32_t p_capacity, const uint64_t p_capacity_inv) { + const uint32_t original_pos = fastmod(p_hash, p_capacity_inv, p_capacity); + return fastmod(p_pos - original_pos + p_capacity, p_capacity_inv, p_capacity); } bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { @@ -101,9 +101,10 @@ private: return false; // Failed lookups, no elements } - uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; uint32_t hash = _hash(p_key); - uint32_t pos = hash % capacity; + uint32_t pos = fastmod(hash, capacity_inv, capacity); uint32_t distance = 0; while (true) { @@ -111,7 +112,7 @@ private: return false; } - if (distance > _get_probe_length(pos, hashes[pos], capacity)) { + if (distance > _get_probe_length(pos, hashes[pos], capacity, capacity_inv)) { return false; } @@ -120,17 +121,18 @@ private: return true; } - pos = (pos + 1) % capacity; + pos = fastmod((pos + 1), capacity_inv, capacity); distance++; } } void _insert_with_hash(uint32_t p_hash, HashMapElement<TKey, TValue> *p_value) { - uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; uint32_t hash = p_hash; HashMapElement<TKey, TValue> *value = p_value; uint32_t distance = 0; - uint32_t pos = hash % capacity; + uint32_t pos = fastmod(hash, capacity_inv, capacity); while (true) { if (hashes[pos] == EMPTY_HASH) { @@ -143,14 +145,14 @@ private: } // Not an empty slot, let's check the probing length of the existing one. - uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity); + uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity, capacity_inv); if (existing_probe_len < distance) { SWAP(hash, hashes[pos]); SWAP(value, elements[pos]); distance = existing_probe_len; } - pos = (pos + 1) % capacity; + pos = fastmod((pos + 1), capacity_inv, capacity); distance++; } } @@ -316,13 +318,14 @@ public: return false; } - uint32_t capacity = hash_table_size_primes[capacity_index]; - uint32_t next_pos = (pos + 1) % capacity; - while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity) != 0) { + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t next_pos = fastmod((pos + 1), capacity_inv, capacity); + while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) { SWAP(hashes[next_pos], hashes[pos]); SWAP(elements[next_pos], elements[pos]); pos = next_pos; - next_pos = (pos + 1) % capacity; + next_pos = fastmod((pos + 1), capacity_inv, capacity); } hashes[pos] = EMPTY_HASH; diff --git a/core/templates/hash_set.h b/core/templates/hash_set.h index 2318067dcc..7b3a5d46f8 100644 --- a/core/templates/hash_set.h +++ b/core/templates/hash_set.h @@ -74,9 +74,9 @@ private: return hash; } - _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_capacity) const { - uint32_t original_pos = p_hash % p_capacity; - return (p_pos - original_pos + p_capacity) % p_capacity; + static _FORCE_INLINE_ uint32_t _get_probe_length(const uint32_t p_pos, const uint32_t p_hash, const uint32_t p_capacity, const uint64_t p_capacity_inv) { + const uint32_t original_pos = fastmod(p_hash, p_capacity_inv, p_capacity); + return fastmod(p_pos - original_pos + p_capacity, p_capacity_inv, p_capacity); } bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { @@ -84,9 +84,10 @@ private: return false; // Failed lookups, no elements } - uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; uint32_t hash = _hash(p_key); - uint32_t pos = hash % capacity; + uint32_t pos = fastmod(hash, capacity_inv, capacity); uint32_t distance = 0; while (true) { @@ -94,7 +95,7 @@ private: return false; } - if (distance > _get_probe_length(pos, hashes[pos], capacity)) { + if (distance > _get_probe_length(pos, hashes[pos], capacity, capacity_inv)) { return false; } @@ -103,17 +104,18 @@ private: return true; } - pos = (pos + 1) % capacity; + pos = fastmod(pos + 1, capacity_inv, capacity); distance++; } } uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) { - uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; uint32_t hash = p_hash; uint32_t index = p_index; uint32_t distance = 0; - uint32_t pos = hash % capacity; + uint32_t pos = fastmod(hash, capacity_inv, capacity); while (true) { if (hashes[pos] == EMPTY_HASH) { @@ -124,7 +126,7 @@ private: } // Not an empty slot, let's check the probing length of the existing one. - uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity); + uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity, capacity_inv); if (existing_probe_len < distance) { key_to_hash[index] = pos; SWAP(hash, hashes[pos]); @@ -132,7 +134,7 @@ private: distance = existing_probe_len; } - pos = (pos + 1) % capacity; + pos = fastmod(pos + 1, capacity_inv, capacity); distance++; } } @@ -265,9 +267,10 @@ public: uint32_t key_pos = pos; pos = key_to_hash[pos]; //make hash pos - uint32_t capacity = hash_table_size_primes[capacity_index]; - uint32_t next_pos = (pos + 1) % capacity; - while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity) != 0) { + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t next_pos = fastmod(pos + 1, capacity_inv, capacity); + while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) { uint32_t kpos = hash_to_key[pos]; uint32_t kpos_next = hash_to_key[next_pos]; SWAP(key_to_hash[kpos], key_to_hash[kpos_next]); @@ -275,7 +278,7 @@ public: SWAP(hash_to_key[next_pos], hash_to_key[pos]); pos = next_pos; - next_pos = (pos + 1) % capacity; + next_pos = fastmod(pos + 1, capacity_inv, capacity); } hashes[pos] = EMPTY_HASH; diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 98ff7fa4ce..547534f26a 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -62,7 +62,7 @@ static _FORCE_INLINE_ uint32_t hash_djb2(const char *p_cstr) { uint32_t c; while ((c = *chr++)) { - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + hash = ((hash << 5) + hash) ^ c; /* hash * 33 ^ c */ } return hash; @@ -72,14 +72,14 @@ static _FORCE_INLINE_ uint32_t hash_djb2_buffer(const uint8_t *p_buff, int p_len uint32_t hash = p_prev; for (int i = 0; i < p_len; i++) { - hash = ((hash << 5) + hash) + p_buff[i]; /* hash * 33 + c */ + hash = ((hash << 5) + hash) ^ p_buff[i]; /* hash * 33 + c */ } return hash; } static _FORCE_INLINE_ uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = 5381) { - return ((p_prev << 5) + p_prev) + p_in; + return ((p_prev << 5) + p_prev) ^ p_in; } /** @@ -100,14 +100,76 @@ static _FORCE_INLINE_ uint32_t hash_one_uint64(const uint64_t p_int) { return uint32_t(v); } +#define HASH_MURMUR3_SEED 0x7F07C65 // Murmurhash3 32-bit version. // All MurmurHash versions are public domain software, and the author disclaims all copyright to their code. -static _FORCE_INLINE_ uint32_t rotl32(uint32_t x, int8_t r) { +static _FORCE_INLINE_ uint32_t hash_murmur3_one_32(uint32_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { + p_in *= 0xcc9e2d51; + p_in = (p_in << 15) | (p_in >> 17); + p_in *= 0x1b873593; + + p_seed ^= p_in; + p_seed = (p_seed << 13) | (p_seed >> 19); + p_seed = p_seed * 5 + 0xe6546b64; + + return p_seed; +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_float(float p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { + union { + float f; + uint32_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.f = 0.0; + } else if (Math::is_nan(p_in)) { + u.f = NAN; + } else { + u.f = p_in; + } + + return hash_murmur3_one_32(u.i, p_seed); +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_64(uint64_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { + p_seed = hash_murmur3_one_32(p_in & 0xFFFFFFFF, p_seed); + return hash_murmur3_one_32(p_in >> 32, p_seed); +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_double(double p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { + 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 = NAN; + } else { + u.d = p_in; + } + + return hash_murmur3_one_64(u.i, p_seed); +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_real(real_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { +#ifdef REAL_T_IS_DOUBLE + return hash_murmur3_one_double(p_in, p_seed); +#else + return hash_murmur3_one_float(p_in, p_seed); +#endif +} + +static _FORCE_INLINE_ uint32_t hash_rotl32(uint32_t x, int8_t r) { return (x << r) | (x >> (32 - r)); } -static _FORCE_INLINE_ uint32_t fmix32(uint32_t h) { +static _FORCE_INLINE_ uint32_t hash_fmix32(uint32_t h) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; @@ -117,7 +179,7 @@ static _FORCE_INLINE_ uint32_t fmix32(uint32_t h) { return h; } -static _FORCE_INLINE_ uint32_t hash_murmur3_32(const void *key, int length, const uint32_t seed = 0x7F07C65) { +static _FORCE_INLINE_ uint32_t hash_murmur3_buffer(const void *key, int length, const uint32_t seed = HASH_MURMUR3_SEED) { // Although not required, this is a random prime number. const uint8_t *data = (const uint8_t *)key; const int nblocks = length / 4; @@ -133,11 +195,11 @@ static _FORCE_INLINE_ uint32_t hash_murmur3_32(const void *key, int length, cons uint32_t k1 = blocks[i]; k1 *= c1; - k1 = rotl32(k1, 15); + k1 = hash_rotl32(k1, 15); k1 *= c2; h1 ^= k1; - h1 = rotl32(h1, 13); + h1 = hash_rotl32(h1, 13); h1 = h1 * 5 + 0xe6546b64; } @@ -155,14 +217,14 @@ static _FORCE_INLINE_ uint32_t hash_murmur3_32(const void *key, int length, cons case 1: k1 ^= tail[0]; k1 *= c1; - k1 = rotl32(k1, 15); + k1 = hash_rotl32(k1, 15); k1 *= c2; h1 ^= k1; }; // Finalize with additional bit mixing. h1 ^= length; - return fmix32(h1); + return hash_fmix32(h1); } static _FORCE_INLINE_ uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) { @@ -184,7 +246,7 @@ static _FORCE_INLINE_ uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev } template <class T> -static _FORCE_INLINE_ uint32_t make_uint32_t(T p_in) { +static _FORCE_INLINE_ uint32_t hash_make_uint32_t(T p_in) { union { T t; uint32_t _u32; @@ -213,11 +275,11 @@ static _FORCE_INLINE_ uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_pr } static _FORCE_INLINE_ uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev = 5381) { - return ((p_prev << 5) + p_prev) + p_in; + return ((p_prev << 5) + p_prev) ^ p_in; } template <class T> -static _FORCE_INLINE_ uint64_t make_uint64_t(T p_in) { +static _FORCE_INLINE_ uint64_t hash_make_uint64_t(T p_in) { union { T t; uint64_t _u64; @@ -241,9 +303,9 @@ 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 wchar_t p_wchar) { return fmix32(p_wchar); } - static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return fmix32(p_uchar); } - static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return fmix32(p_uchar); } + static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(p_wchar); } + static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(p_uchar); } + static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(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(); } static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); } @@ -251,21 +313,59 @@ struct HashMapHasherDefault { static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); } static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(p_int); } - static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); } - static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_djb2_one_float(p_double); } - static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return fmix32(p_int); } - static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return fmix32(p_int); } - static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return fmix32(p_int); } - static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return fmix32(p_int); } - static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return fmix32(p_int); } - static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return fmix32(p_int); } - static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) { return hash_murmur3_32(&p_vec, sizeof(Vector2i)); } - static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) { return hash_murmur3_32(&p_vec, sizeof(Vector3i)); } - static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) { return hash_murmur3_32(&p_vec, sizeof(Vector2)); } - static _FORCE_INLINE_ uint32_t hash(const Vector3 &p_vec) { return hash_murmur3_32(&p_vec, sizeof(Vector3)); } - static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) { return hash_murmur3_32(&p_rect, sizeof(Rect2i)); } - static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) { return hash_murmur3_32(&p_rect, sizeof(Rect2)); } - static _FORCE_INLINE_ uint32_t hash(const AABB &p_aabb) { return hash_murmur3_32(&p_aabb, sizeof(AABB)); } + static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_murmur3_one_float(p_float); } + static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_murmur3_one_double(p_double); } + static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) { + uint32_t h = hash_murmur3_one_32(p_vec.x); + h = hash_murmur3_one_32(p_vec.y, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) { + uint32_t h = hash_murmur3_one_32(p_vec.x); + h = hash_murmur3_one_32(p_vec.y, h); + h = hash_murmur3_one_32(p_vec.z, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) { + uint32_t h = hash_murmur3_one_real(p_vec.x); + h = hash_murmur3_one_real(p_vec.y, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Vector3 &p_vec) { + uint32_t h = hash_murmur3_one_real(p_vec.x); + h = hash_murmur3_one_real(p_vec.y, h); + h = hash_murmur3_one_real(p_vec.z, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) { + uint32_t h = hash_murmur3_one_32(p_rect.position.x); + h = hash_murmur3_one_32(p_rect.position.y, h); + h = hash_murmur3_one_32(p_rect.size.x, h); + h = hash_murmur3_one_32(p_rect.size.y, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) { + uint32_t h = hash_murmur3_one_real(p_rect.position.x); + h = hash_murmur3_one_real(p_rect.position.y, h); + h = hash_murmur3_one_real(p_rect.size.x, h); + h = hash_murmur3_one_real(p_rect.size.y, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const AABB &p_aabb) { + uint32_t h = hash_murmur3_one_real(p_aabb.position.x); + h = hash_murmur3_one_real(p_aabb.position.y, h); + h = hash_murmur3_one_real(p_aabb.position.z, h); + h = hash_murmur3_one_real(p_aabb.size.x, h); + h = hash_murmur3_one_real(p_aabb.size.y, h); + h = hash_murmur3_one_real(p_aabb.size.z, h); + return hash_fmix32(h); + } }; template <typename T> @@ -337,4 +437,66 @@ const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = { 1610612741, }; +// Computed with elem_i = UINT64_C (0 x FFFFFFFF FFFFFFFF ) / d_i + 1, where d_i is the i-th element of the above array. +const uint64_t hash_table_size_primes_inv[HASH_TABLE_SIZE_MAX] = { + 3689348814741910324, + 1418980313362273202, + 802032351030850071, + 392483916461905354, + 190172619316593316, + 95578984837873325, + 47420935922132524, + 23987963684927896, + 11955116055547344, + 5991147799191151, + 2998982941588287, + 1501077717772769, + 750081082979285, + 375261795343686, + 187625172388393, + 93822606204624, + 46909513691883, + 23456218233098, + 11728086747027, + 5864041509391, + 2932024948977, + 1466014921160, + 733007198436, + 366503839517, + 183251896093, + 91625960335, + 45812983922, + 22906489714, + 11453246088 +}; + +/** + * Fastmod computes ( n mod d ) given the precomputed c much faster than n % d. + * The implementation of fastmod is based on the following paper by Daniel Lemire et al. + * Faster Remainder by Direct Computation: Applications to Compilers and Software Libraries + * https://arxiv.org/abs/1902.01961 + */ +static _FORCE_INLINE_ uint32_t fastmod(const uint32_t n, const uint64_t c, const uint32_t d) { +#if defined(_MSC_VER) + // Returns the upper 64 bits of the product of two 64-bit unsigned integers. + // This intrinsic function is required since MSVC does not support unsigned 128-bit integers. +#if defined(_M_X64) || defined(_M_ARM64) + return __umulh(c * n, d); +#else + // Fallback to the slower method for 32-bit platforms. + return n % d; +#endif // _M_X64 || _M_ARM64 +#else +#ifdef __SIZEOF_INT128__ + // Prevent compiler warning, because we know what we are doing. + uint64_t lowbits = c * n; + __extension__ typedef unsigned __int128 uint128; + return static_cast<uint64_t>(((uint128)lowbits * d) >> 64); +#else + // Fallback to the slower method if no 128-bit unsigned integer type is available. + return n % d; +#endif // __SIZEOF_INT128__ +#endif // _MSC_VER +} + #endif // HASHFUNCS_H diff --git a/core/templates/local_vector.h b/core/templates/local_vector.h index f4e0748c27..8d687effcf 100644 --- a/core/templates/local_vector.h +++ b/core/templates/local_vector.h @@ -38,7 +38,9 @@ #include <initializer_list> -template <class T, class U = uint32_t, bool force_trivial = false> +// If tight, it grows strictly as much as needed. +// Otherwise, it grows exponentially (the default and what you want in most cases). +template <class T, class U = uint32_t, bool force_trivial = false, bool tight = false> class LocalVector { private: U count = 0; @@ -121,7 +123,7 @@ public: _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); + p_size = tight ? p_size : nearest_power_of_2_templated(p_size); if (p_size > capacity) { capacity = p_size; data = (T *)memrealloc(data, capacity * sizeof(T)); @@ -262,4 +264,7 @@ public: } }; +template <class T, class U = uint32_t, bool force_trivial = false> +using TightLocalVector = LocalVector<T, U, force_trivial, true>; + #endif // LOCAL_VECTOR_H diff --git a/core/templates/lru.h b/core/templates/lru.h index 48ba318b12..55130cbeb0 100644 --- a/core/templates/lru.h +++ b/core/templates/lru.h @@ -35,7 +35,7 @@ #include "hash_map.h" #include "list.h" -template <class TKey, class TData> +template <class TKey, class TData, class Hasher = HashMapHasherDefault, class Comparator = HashMapComparatorDefault<TKey>> class LRUCache { private: struct Pair { @@ -52,7 +52,7 @@ private: typedef typename List<Pair>::Element *Element; List<Pair> _list; - HashMap<TKey, Element> _map; + HashMap<TKey, Element, Hasher, Comparator> _map; size_t capacity; public: diff --git a/core/templates/vector.h b/core/templates/vector.h index 2ac7c7630a..f3f5ed76a7 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -145,6 +145,9 @@ public: Vector<uint8_t> to_byte_array() const { Vector<uint8_t> ret; + if (is_empty()) { + return ret; + } ret.resize(size() * sizeof(T)); memcpy(ret.ptrw(), ptr(), sizeof(T) * size()); return ret; diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 599c3e1dfe..af166e09a3 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -190,13 +190,13 @@ uint32_t Array::recursive_hash(int recursion_count) const { return 0; } - uint32_t h = hash_djb2_one_32(Variant::ARRAY); + uint32_t h = hash_murmur3_one_32(Variant::ARRAY); recursion_count++; for (int i = 0; i < _p->array.size(); i++) { - h = hash_djb2_one_32(_p->array[i].recursive_hash(recursion_count), h); + h = hash_murmur3_one_32(_p->array[i].recursive_hash(recursion_count), h); } - return h; + return hash_fmix32(h); } bool Array::_assign(const Array &p_array) { diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index 516b8f2d51..5453f0d5c6 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -143,7 +143,8 @@ uint32_t Callable::hash() const { return custom->hash(); } else { uint32_t hash = method.hash(); - return hash_djb2_one_64(object, hash); + hash = hash_murmur3_one_64(object, hash); + return hash_fmix32(hash); } } diff --git a/core/variant/callable.h b/core/variant/callable.h index 6a760958d6..bbcf5427ba 100644 --- a/core/variant/callable.h +++ b/core/variant/callable.h @@ -61,6 +61,7 @@ public: CALL_ERROR_TOO_MANY_ARGUMENTS, // expected is number of arguments CALL_ERROR_TOO_FEW_ARGUMENTS, // expected is number of arguments CALL_ERROR_INSTANCE_IS_NULL, + CALL_ERROR_METHOD_NOT_CONST, }; Error error = Error::CALL_OK; int argument = 0; diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index 822021f440..d9f4359ee5 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -298,15 +298,15 @@ uint32_t Dictionary::recursive_hash(int recursion_count) const { return 0; } - uint32_t h = hash_djb2_one_32(Variant::DICTIONARY); + uint32_t h = hash_murmur3_one_32(Variant::DICTIONARY); recursion_count++; for (const KeyValue<Variant, Variant> &E : _p->variant_map) { - h = hash_djb2_one_32(E.key.recursive_hash(recursion_count), h); - h = hash_djb2_one_32(E.value.recursive_hash(recursion_count), h); + h = hash_murmur3_one_32(E.key.recursive_hash(recursion_count), h); + h = hash_murmur3_one_32(E.value.recursive_hash(recursion_count), h); } - return h; + return hash_fmix32(h); } Array Dictionary::keys() const { diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index aa640924e4..ae92d7b5c4 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -2780,7 +2780,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { return hash_one_uint64((uint64_t)_data._int); } break; case FLOAT: { - return hash_djb2_one_float(_data._float); + return hash_murmur3_one_float(_data._float); } break; case STRING: { return reinterpret_cast<const String *>(_data._mem)->hash(); @@ -2788,50 +2788,102 @@ uint32_t Variant::recursive_hash(int recursion_count) const { // math types case VECTOR2: { - return hash_murmur3_32(reinterpret_cast<const Vector2 *>(_data._mem), sizeof(Vector2)); + return HashMapHasherDefault::hash(*reinterpret_cast<const Vector2 *>(_data._mem)); } break; case VECTOR2I: { - return hash_murmur3_32(reinterpret_cast<const Vector2i *>(_data._mem), sizeof(Vector2i)); + return HashMapHasherDefault::hash(*reinterpret_cast<const Vector2i *>(_data._mem)); } break; case RECT2: { - return hash_murmur3_32(reinterpret_cast<const Rect2 *>(_data._mem), sizeof(Rect2)); + return HashMapHasherDefault::hash(*reinterpret_cast<const Rect2 *>(_data._mem)); } break; case RECT2I: { - return hash_murmur3_32(reinterpret_cast<const Rect2i *>(_data._mem), sizeof(Rect2i)); + return HashMapHasherDefault::hash(*reinterpret_cast<const Rect2i *>(_data._mem)); } break; case TRANSFORM2D: { - return hash_murmur3_32(reinterpret_cast<const Transform2D *>(_data._transform2d), sizeof(Transform2D)); + uint32_t h = HASH_MURMUR3_SEED; + const Transform2D &t = *_data._transform2d; + h = hash_murmur3_one_real(t[0].x, h); + h = hash_murmur3_one_real(t[0].y, h); + h = hash_murmur3_one_real(t[1].x, h); + h = hash_murmur3_one_real(t[1].y, h); + h = hash_murmur3_one_real(t[2].x, h); + h = hash_murmur3_one_real(t[2].y, h); + + return hash_fmix32(h); } break; case VECTOR3: { - return hash_murmur3_32(reinterpret_cast<const Vector3 *>(_data._mem), sizeof(Vector3)); + return HashMapHasherDefault::hash(*reinterpret_cast<const Vector3 *>(_data._mem)); } break; case VECTOR3I: { - return hash_murmur3_32(reinterpret_cast<const Vector3i *>(_data._mem), sizeof(Vector3i)); + return HashMapHasherDefault::hash(*reinterpret_cast<const Vector3i *>(_data._mem)); } break; case PLANE: { - return hash_murmur3_32(reinterpret_cast<const Plane *>(_data._mem), sizeof(Plane)); + uint32_t h = HASH_MURMUR3_SEED; + const Plane &p = *reinterpret_cast<const Plane *>(_data._mem); + h = hash_murmur3_one_real(p.normal.x, h); + h = hash_murmur3_one_real(p.normal.y, h); + h = hash_murmur3_one_real(p.normal.z, h); + h = hash_murmur3_one_real(p.d, h); + return hash_fmix32(h); } break; case AABB: { - return hash_murmur3_32(_data._aabb, sizeof(AABB)); + return HashMapHasherDefault::hash(*_data._aabb); } break; case QUATERNION: { - return hash_murmur3_32(reinterpret_cast<const Quaternion *>(_data._mem), sizeof(Quaternion)); + uint32_t h = HASH_MURMUR3_SEED; + const Quaternion &q = *reinterpret_cast<const Quaternion *>(_data._mem); + h = hash_murmur3_one_real(q.x, h); + h = hash_murmur3_one_real(q.y, h); + h = hash_murmur3_one_real(q.z, h); + h = hash_murmur3_one_real(q.w, h); + return hash_fmix32(h); } break; case BASIS: { - return hash_murmur3_32(_data._basis, sizeof(Basis)); + uint32_t h = HASH_MURMUR3_SEED; + const Basis &b = *_data._basis; + h = hash_murmur3_one_real(b[0].x, h); + h = hash_murmur3_one_real(b[0].y, h); + h = hash_murmur3_one_real(b[0].z, h); + h = hash_murmur3_one_real(b[1].x, h); + h = hash_murmur3_one_real(b[1].y, h); + h = hash_murmur3_one_real(b[1].z, h); + h = hash_murmur3_one_real(b[2].x, h); + h = hash_murmur3_one_real(b[2].y, h); + h = hash_murmur3_one_real(b[2].z, h); + return hash_fmix32(h); } break; case TRANSFORM3D: { - return hash_murmur3_32(_data._transform3d, sizeof(Transform3D)); + uint32_t h = HASH_MURMUR3_SEED; + const Transform3D &t = *_data._transform3d; + h = hash_murmur3_one_real(t.basis[0].x, h); + h = hash_murmur3_one_real(t.basis[0].y, h); + h = hash_murmur3_one_real(t.basis[0].z, h); + h = hash_murmur3_one_real(t.basis[1].x, h); + h = hash_murmur3_one_real(t.basis[1].y, h); + h = hash_murmur3_one_real(t.basis[1].z, h); + h = hash_murmur3_one_real(t.basis[2].x, h); + h = hash_murmur3_one_real(t.basis[2].y, h); + h = hash_murmur3_one_real(t.basis[2].z, h); + h = hash_murmur3_one_real(t.origin.x, h); + h = hash_murmur3_one_real(t.origin.y, h); + h = hash_murmur3_one_real(t.origin.z, h); + return hash_fmix32(h); } break; // misc types case COLOR: { - return hash_murmur3_32(reinterpret_cast<const Color *>(_data._mem), sizeof(Color)); + uint32_t h = HASH_MURMUR3_SEED; + const Color &c = *reinterpret_cast<const Color *>(_data._mem); + h = hash_murmur3_one_float(c.r, h); + h = hash_murmur3_one_float(c.g, h); + h = hash_murmur3_one_float(c.b, h); + h = hash_murmur3_one_float(c.a, h); + return hash_fmix32(h); } break; case RID: { return hash_one_uint64(reinterpret_cast<const ::RID *>(_data._mem)->get_id()); } break; case OBJECT: { - return hash_one_uint64(make_uint64_t(_get_obj().obj)); + return hash_one_uint64(hash_make_uint64_t(_get_obj().obj)); } break; case STRING_NAME: { return reinterpret_cast<const StringName *>(_data._mem)->hash(); @@ -2850,7 +2902,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { 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); + return hash_murmur3_one_64(s.get_object_id(), hash); } break; case ARRAY: { const Array &arr = *reinterpret_cast<const Array *>(_data._mem); @@ -2862,9 +2914,9 @@ uint32_t Variant::recursive_hash(int recursion_count) const { int len = arr.size(); if (likely(len)) { const uint8_t *r = arr.ptr(); - return hash_murmur3_32((uint8_t *)&r[0], len); + return hash_murmur3_buffer((uint8_t *)&r[0], len); } else { - return hash_djb2_one_64(0); + return hash_murmur3_one_64(0); } } break; @@ -2873,9 +2925,9 @@ uint32_t Variant::recursive_hash(int recursion_count) const { int len = arr.size(); if (likely(len)) { const int32_t *r = arr.ptr(); - return hash_murmur3_32((uint8_t *)&r[0], len * sizeof(int32_t)); + return hash_murmur3_buffer((uint8_t *)&r[0], len * sizeof(int32_t)); } else { - return hash_djb2_one_64(0); + return hash_murmur3_one_64(0); } } break; @@ -2884,9 +2936,9 @@ uint32_t Variant::recursive_hash(int recursion_count) const { int len = arr.size(); if (likely(len)) { const int64_t *r = arr.ptr(); - return hash_murmur3_32((uint8_t *)&r[0], len * sizeof(int64_t)); + return hash_murmur3_buffer((uint8_t *)&r[0], len * sizeof(int64_t)); } else { - return hash_djb2_one_64(0); + return hash_murmur3_one_64(0); } } break; @@ -2896,9 +2948,13 @@ uint32_t Variant::recursive_hash(int recursion_count) const { if (likely(len)) { const float *r = arr.ptr(); - return hash_murmur3_32((uint8_t *)&r[0], len * sizeof(float)); + uint32_t h = HASH_MURMUR3_SEED; + for (int32_t i = 0; i < len; i++) { + h = hash_murmur3_one_float(r[i], h); + } + return hash_fmix32(h); } else { - return hash_djb2_one_float(0.0); + return hash_murmur3_one_float(0.0); } } break; @@ -2908,14 +2964,18 @@ uint32_t Variant::recursive_hash(int recursion_count) const { if (likely(len)) { const double *r = arr.ptr(); - return hash_murmur3_32((uint8_t *)&r[0], len * sizeof(double)); + uint32_t h = HASH_MURMUR3_SEED; + for (int32_t i = 0; i < len; i++) { + h = hash_murmur3_one_double(r[i], h); + } + return hash_fmix32(h); } else { - return hash_djb2_one_float(0.0); + return hash_murmur3_one_float(0.0); } } break; case PACKED_STRING_ARRAY: { - uint32_t hash = 5831; + uint32_t hash = HASH_MURMUR3_SEED; const Vector<String> &arr = PackedArrayRef<String>::get_array(_data.packed_array); int len = arr.size(); @@ -2923,14 +2983,15 @@ uint32_t Variant::recursive_hash(int recursion_count) const { const String *r = arr.ptr(); for (int i = 0; i < len; i++) { - hash = hash_djb2_one_32(r[i].hash(), hash); + hash = hash_murmur3_one_32(r[i].hash(), hash); } + hash = hash_fmix32(hash); } return hash; } break; case PACKED_VECTOR2_ARRAY: { - uint32_t hash = 5831; + uint32_t hash = HASH_MURMUR3_SEED; const Vector<Vector2> &arr = PackedArrayRef<Vector2>::get_array(_data.packed_array); int len = arr.size(); @@ -2938,15 +2999,16 @@ uint32_t Variant::recursive_hash(int recursion_count) const { const Vector2 *r = arr.ptr(); for (int i = 0; i < len; i++) { - hash = hash_djb2_one_float(r[i].x, hash); - hash = hash_djb2_one_float(r[i].y, hash); + hash = hash_murmur3_one_real(r[i].x, hash); + hash = hash_murmur3_one_real(r[i].y, hash); } + hash = hash_fmix32(hash); } return hash; } break; case PACKED_VECTOR3_ARRAY: { - uint32_t hash = 5831; + uint32_t hash = HASH_MURMUR3_SEED; const Vector<Vector3> &arr = PackedArrayRef<Vector3>::get_array(_data.packed_array); int len = arr.size(); @@ -2954,16 +3016,17 @@ uint32_t Variant::recursive_hash(int recursion_count) const { const Vector3 *r = arr.ptr(); for (int i = 0; i < len; i++) { - hash = hash_djb2_one_float(r[i].x, hash); - hash = hash_djb2_one_float(r[i].y, hash); - hash = hash_djb2_one_float(r[i].z, hash); + hash = hash_murmur3_one_real(r[i].x, hash); + hash = hash_murmur3_one_real(r[i].y, hash); + hash = hash_murmur3_one_real(r[i].z, hash); } + hash = hash_fmix32(hash); } return hash; } break; case PACKED_COLOR_ARRAY: { - uint32_t hash = 5831; + uint32_t hash = HASH_MURMUR3_SEED; const Vector<Color> &arr = PackedArrayRef<Color>::get_array(_data.packed_array); int len = arr.size(); @@ -2971,11 +3034,12 @@ uint32_t Variant::recursive_hash(int recursion_count) const { const Color *r = arr.ptr(); for (int i = 0; i < len; i++) { - hash = hash_djb2_one_float(r[i].r, hash); - hash = hash_djb2_one_float(r[i].g, hash); - hash = hash_djb2_one_float(r[i].b, hash); - hash = hash_djb2_one_float(r[i].a, hash); + hash = hash_murmur3_one_float(r[i].r, hash); + hash = hash_murmur3_one_float(r[i].g, hash); + hash = hash_murmur3_one_float(r[i].b, hash); + hash = hash_murmur3_one_float(r[i].a, hash); } + hash = hash_fmix32(hash); } return hash; @@ -3263,13 +3327,20 @@ Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Varia void Variant::static_assign(const Variant &p_variant) { } -bool Variant::is_shared() const { - switch (type) { +bool Variant::is_type_shared(Variant::Type p_type) { + switch (p_type) { case OBJECT: - return true; case ARRAY: - return true; case DICTIONARY: + 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: return true; default: { } @@ -3278,6 +3349,10 @@ bool Variant::is_shared() const { return false; } +bool Variant::is_shared() const { + return is_type_shared(type); +} + void Variant::_variant_call_error(const String &p_method, Callable::CallError &error) { switch (error.error) { case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { @@ -3331,6 +3406,8 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_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_ERROR_METHOD_NOT_CONST) { + err_text = "Method not const in const instance"; } else if (ce.error == Callable::CallError::CALL_OK) { return "Call OK"; } diff --git a/core/variant/variant.h b/core/variant/variant.h index 726ba120b5..872b374b13 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -297,6 +297,7 @@ public: static String get_type_name(Variant::Type p_type); static bool can_convert(Type p_type_from, Type p_type_to); static bool can_convert_strict(Type p_type_from, Type p_type_to); + static bool is_type_shared(Variant::Type p_type); bool is_ref_counted() const; _FORCE_INLINE_ bool is_num() const { @@ -555,6 +556,7 @@ public: return ret; } + void call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error); static void call_static(Variant::Type p_type, const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error); static String get_call_error_text(const StringName &p_method, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce); @@ -716,6 +718,10 @@ public: 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); + static void get_enums_for_type(Variant::Type p_type, List<StringName> *p_enums); + static void get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_name, List<StringName> *p_enumerations); + static int get_enum_value(Variant::Type p_type, StringName p_enum_name, StringName p_enumeration, bool *r_valid = nullptr); + typedef String (*ObjectDeConstruct)(const Variant &p_object, void *ud); typedef void (*ObjectConstruct)(const String &p_text, void *ud, Variant &r_value); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 6e8dc64811..8e16a767cf 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -753,40 +753,56 @@ struct _VariantCall { static PackedInt32Array func_PackedByteArray_decode_s32_array(PackedByteArray *p_instance) { uint64_t size = p_instance->size(); PackedInt32Array dest; - ERR_FAIL_COND_V_MSG(size < sizeof(int32_t), dest, "Size didn't match array of size int32_t, maybe you are trying to convert to the wrong type?"); + if (size == 0) { + return dest; + } + ERR_FAIL_COND_V_MSG(size % sizeof(int32_t), dest, "PackedByteArray size must be a multiple of 4 (size of 32-bit integer) to convert to PackedInt32Array."); const uint8_t *r = p_instance->ptr(); dest.resize(size / sizeof(int32_t)); - memcpy(dest.ptrw(), r, size); + ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed. + memcpy(dest.ptrw(), r, dest.size() * sizeof(int32_t)); return dest; } static PackedInt64Array func_PackedByteArray_decode_s64_array(PackedByteArray *p_instance) { uint64_t size = p_instance->size(); PackedInt64Array dest; - ERR_FAIL_COND_V_MSG(size < sizeof(int64_t), dest, "Size didn't match array of size int64_t, maybe you are trying to convert to the wrong type?"); + if (size == 0) { + return dest; + } + ERR_FAIL_COND_V_MSG(size % sizeof(int64_t), dest, "PackedByteArray size must be a multiple of 8 (size of 64-bit integer) to convert to PackedInt64Array."); const uint8_t *r = p_instance->ptr(); dest.resize(size / sizeof(int64_t)); - memcpy(dest.ptrw(), r, size); + ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed. + memcpy(dest.ptrw(), r, dest.size() * sizeof(int64_t)); return dest; } static PackedFloat32Array func_PackedByteArray_decode_float_array(PackedByteArray *p_instance) { uint64_t size = p_instance->size(); PackedFloat32Array dest; - ERR_FAIL_COND_V_MSG(size < sizeof(float), dest, "Size didn't match array of size float, maybe you are trying to convert to the wrong type?"); + if (size == 0) { + return dest; + } + ERR_FAIL_COND_V_MSG(size % sizeof(float), dest, "PackedByteArray size must be a multiple of 4 (size of 32-bit float) to convert to PackedFloat32Array."); const uint8_t *r = p_instance->ptr(); dest.resize(size / sizeof(float)); - memcpy(dest.ptrw(), r, size); + ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed. + memcpy(dest.ptrw(), r, dest.size() * sizeof(float)); return dest; } static PackedFloat64Array func_PackedByteArray_decode_double_array(PackedByteArray *p_instance) { uint64_t size = p_instance->size(); PackedFloat64Array dest; - ERR_FAIL_COND_V_MSG(size < sizeof(double), dest, "Size didn't match array of size double, maybe you are trying to convert to the wrong type?"); + if (size == 0) { + return dest; + } + ERR_FAIL_COND_V_MSG(size % sizeof(double), dest, "PackedByteArray size must be a multiple of 8 (size of 64-bit double) to convert to PackedFloat64Array."); const uint8_t *r = p_instance->ptr(); dest.resize(size / sizeof(double)); - memcpy(dest.ptrw(), r, size); + ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed. + memcpy(dest.ptrw(), r, dest.size() * sizeof(double)); return dest; } @@ -944,9 +960,20 @@ struct _VariantCall { constant_data[p_type].variant_value_ordered.push_back(p_constant_name); #endif } + + struct EnumData { + HashMap<StringName, HashMap<StringName, int>> value; + }; + + static EnumData *enum_data; + + static void add_enum_constant(int p_type, StringName p_enum_type_name, StringName p_enumeration_name, int p_enum_value) { + enum_data[p_type].value[p_enum_type_name][p_enumeration_name] = p_enum_value; + } }; _VariantCall::ConstantData *_VariantCall::constant_data = nullptr; +_VariantCall::EnumData *_VariantCall::enum_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) = nullptr; @@ -1020,6 +1047,37 @@ void Variant::callp(const StringName &p_method, const Variant **p_args, int p_ar #endif r_ret = _get_obj().obj->callp(p_method, p_args, p_argcount, r_error); + } 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); + } +} + +void Variant::call_const(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_ref_counted() && 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_const(p_method, p_args, p_argcount, r_error); + //else if (type==Variant::METHOD) { } else { r_error.error = Callable::CallError::CALL_OK; @@ -1031,6 +1089,11 @@ void Variant::callp(const StringName &p_method, const Variant **p_args, int p_ar return; } + if (!imf->is_const) { + r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST; + return; + } + imf->call(this, p_args, p_argcount, r_ret, imf->default_arguments, r_error); } } @@ -1170,19 +1233,19 @@ uint32_t Variant::get_builtin_method_hash(Variant::Type p_type, const StringName 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); - uint32_t hash = hash_djb2_one_32(method->is_const); - hash = hash_djb2_one_32(method->is_static, hash); - hash = hash_djb2_one_32(method->is_vararg, hash); - hash = hash_djb2_one_32(method->has_return_type, hash); + uint32_t hash = hash_murmur3_one_32(method->is_const); + hash = hash_murmur3_one_32(method->is_static, hash); + hash = hash_murmur3_one_32(method->is_vararg, hash); + hash = hash_murmur3_one_32(method->has_return_type, hash); if (method->has_return_type) { - hash = hash_djb2_one_32(method->return_type, hash); + hash = hash_murmur3_one_32(method->return_type, hash); } - hash = hash_djb2_one_32(method->argument_count, hash); + hash = hash_murmur3_one_32(method->argument_count, hash); for (int i = 0; i < method->argument_count; i++) { - hash = method->get_argument_type(i); + hash = hash_murmur3_one_32(method->get_argument_type(i), hash); } - return hash; + return hash_fmix32(hash); } void Variant::get_method_list(List<MethodInfo> *p_list) const { @@ -1300,6 +1363,54 @@ Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_va return E->value; } +void Variant::get_enums_for_type(Variant::Type p_type, List<StringName> *p_enums) { + ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); + + _VariantCall::EnumData &enum_data = _VariantCall::enum_data[p_type]; + + for (const KeyValue<StringName, HashMap<StringName, int>> &E : enum_data.value) { + p_enums->push_back(E.key); + } +} + +void Variant::get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_name, List<StringName> *p_enumerations) { + ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); + + _VariantCall::EnumData &enum_data = _VariantCall::enum_data[p_type]; + + for (const KeyValue<StringName, HashMap<StringName, int>> &E : enum_data.value) { + for (const KeyValue<StringName, int> &V : E.value) { + p_enumerations->push_back(V.key); + } + } +} + +int Variant::get_enum_value(Variant::Type p_type, StringName p_enum_name, StringName p_enumeration, bool *r_valid) { + if (r_valid) { + *r_valid = false; + } + + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, -1); + + _VariantCall::EnumData &enum_data = _VariantCall::enum_data[p_type]; + + HashMap<StringName, HashMap<StringName, int>>::Iterator E = enum_data.value.find(p_enum_name); + if (!E) { + return -1; + } + + HashMap<StringName, int>::Iterator V = E->value.find(p_enumeration); + if (!V) { + return -1; + } + + if (r_valid) { + *r_valid = true; + } + + return V->value; +} + #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); \ @@ -1360,6 +1471,7 @@ Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_va static void _register_variant_builtin_methods() { _VariantCall::constant_data = memnew_arr(_VariantCall::ConstantData, Variant::VARIANT_MAX); + _VariantCall::enum_data = memnew_arr(_VariantCall::EnumData, Variant::VARIANT_MAX); builtin_method_info = memnew_arr(BuiltinMethodMap, Variant::VARIANT_MAX); builtin_method_names = memnew_arr(List<StringName>, Variant::VARIANT_MAX); @@ -1496,6 +1608,7 @@ static void _register_variant_builtin_methods() { 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, bezier_interpolate, sarray("control_1", "control_2", "end", "t"), varray()); bind_method(Vector2, max_axis_index, sarray(), varray()); bind_method(Vector2, min_axis_index, sarray(), varray()); bind_method(Vector2, move_toward, sarray("to", "delta"), varray()); @@ -1583,6 +1696,7 @@ static void _register_variant_builtin_methods() { 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, bezier_interpolate, sarray("control_1", "control_2", "end", "t"), varray()); bind_method(Vector3, move_toward, sarray("to", "delta"), varray()); bind_method(Vector3, dot, sarray("with"), varray()); bind_method(Vector3, cross, sarray("with"), varray()); @@ -1692,6 +1806,7 @@ static void _register_variant_builtin_methods() { bind_method(NodePath, get_subname_count, sarray(), varray()); bind_method(NodePath, hash, sarray(), varray()); bind_method(NodePath, get_subname, sarray("idx"), varray()); + bind_method(NodePath, get_concatenated_names, sarray(), 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()); @@ -2124,6 +2239,10 @@ static void _register_variant_builtin_methods() { _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Y", Vector3::AXIS_Y); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Z", Vector3::AXIS_Z); + _VariantCall::add_enum_constant(Variant::VECTOR3, "Axis", "AXIS_X", Vector3::AXIS_X); + _VariantCall::add_enum_constant(Variant::VECTOR3, "Axis", "AXIS_Y", Vector3::AXIS_Y); + _VariantCall::add_enum_constant(Variant::VECTOR3, "Axis", "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(INFINITY, INFINITY, INFINITY)); @@ -2138,6 +2257,10 @@ static void _register_variant_builtin_methods() { _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3i::AXIS_Y); _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3i::AXIS_Z); + _VariantCall::add_enum_constant(Variant::VECTOR3I, "Axis", "AXIS_X", Vector3i::AXIS_X); + _VariantCall::add_enum_constant(Variant::VECTOR3I, "Axis", "AXIS_Y", Vector3i::AXIS_Y); + _VariantCall::add_enum_constant(Variant::VECTOR3I, "Axis", "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)); @@ -2150,9 +2273,15 @@ static void _register_variant_builtin_methods() { _VariantCall::add_constant(Variant::VECTOR2, "AXIS_X", Vector2::AXIS_X); _VariantCall::add_constant(Variant::VECTOR2, "AXIS_Y", Vector2::AXIS_Y); + _VariantCall::add_enum_constant(Variant::VECTOR2, "Axis", "AXIS_X", Vector2::AXIS_X); + _VariantCall::add_enum_constant(Variant::VECTOR2, "Axis", "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_enum_constant(Variant::VECTOR2I, "Axis", "AXIS_X", Vector2i::AXIS_X); + _VariantCall::add_enum_constant(Variant::VECTOR2I, "Axis", "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(INFINITY, INFINITY)); @@ -2175,6 +2304,13 @@ static void _register_variant_builtin_methods() { _VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_ZXY", Basis::EULER_ORDER_ZXY); _VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_ZYX", Basis::EULER_ORDER_ZYX); + _VariantCall::add_enum_constant(Variant::BASIS, "EulerOrder", "EULER_ORDER_XYZ", Basis::EULER_ORDER_XYZ); + _VariantCall::add_enum_constant(Variant::BASIS, "EulerOrder", "EULER_ORDER_XZY", Basis::EULER_ORDER_XZY); + _VariantCall::add_enum_constant(Variant::BASIS, "EulerOrder", "EULER_ORDER_YXZ", Basis::EULER_ORDER_YXZ); + _VariantCall::add_enum_constant(Variant::BASIS, "EulerOrder", "EULER_ORDER_YZX", Basis::EULER_ORDER_YZX); + _VariantCall::add_enum_constant(Variant::BASIS, "EulerOrder", "EULER_ORDER_ZXY", Basis::EULER_ORDER_ZXY); + _VariantCall::add_enum_constant(Variant::BASIS, "EulerOrder", "EULER_ORDER_ZYX", Basis::EULER_ORDER_ZYX); + _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)); @@ -2213,4 +2349,5 @@ void Variant::_unregister_variant_methods() { memdelete_arr(builtin_method_names); memdelete_arr(builtin_method_info); memdelete_arr(_VariantCall::constant_data); + memdelete_arr(_VariantCall::enum_data); } diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 3696ffae60..e0cfb42e1e 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -304,6 +304,13 @@ public: v->_get_obj().id = ObjectID(); } + static void update_object_id(Variant *v) { + const Object *o = v->_get_obj().obj; + if (o) { + v->_get_obj().id = o->get_instance_id(); + } + } + _FORCE_INLINE_ static void *get_opaque_pointer(Variant *v) { switch (v->type) { case Variant::NIL: diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 66badce268..2bca5f8284 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -231,6 +231,10 @@ struct VariantUtilityFunctions { return Math::cubic_interpolate(from, to, pre, post, weight); } + static inline double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t); + } + static inline double lerp_angle(double from, double to, double weight) { return Math::lerp_angle(from, to, weight); } @@ -267,6 +271,52 @@ struct VariantUtilityFunctions { return Math::db2linear(db); } + static inline Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) { + Variant::Type x_type = p_x.get_type(); + if (x_type != Variant::INT && x_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = x_type; + return Variant(); + } + + Variant::Type min_type = p_min.get_type(); + if (min_type != Variant::INT && min_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = x_type; + return Variant(); + } + + Variant::Type max_type = p_max.get_type(); + if (max_type != Variant::INT && max_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 2; + r_error.expected = x_type; + return Variant(); + } + + Variant value; + + switch (x_type) { + case Variant::INT: { + if (x_type != min_type || x_type != max_type) { + value = wrapf((double)p_x, (double)p_min, (double)p_max); + } else { + value = wrapi((int)p_x, (int)p_min, (int)p_max); + } + } break; + case Variant::FLOAT: { + value = wrapf((double)p_x, (double)p_min, (double)p_max); + } break; + default: + break; + } + + r_error.error = Callable::CallError::CALL_OK; + return value; + } + static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { return Math::wrapi(value, min, max); } @@ -510,6 +560,22 @@ struct VariantUtilityFunctions { r_error.error = Callable::CallError::CALL_OK; } + static inline void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + s = os; + } else { + s += os; + } + } + + print_line_rich(s); + r_error.error = Callable::CallError::CALL_OK; + } + static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { if (OS::get_singleton()->is_stdout_verbose()) { String s; @@ -1204,6 +1270,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(cubic_interpolate, sarray("from", "to", "pre", "post", "weight"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(bezier_interpolate, sarray("start", "control_1", "control_2", "end", "t"), 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); @@ -1216,6 +1283,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(linear2db, sarray("lin"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(db2linear, sarray("db"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDVR3(wrap, sarray("value", "min", "max"), 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); @@ -1254,6 +1322,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(error_string, sarray("error"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(print_rich, 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); @@ -1423,17 +1492,17 @@ uint32_t Variant::get_utility_function_hash(const StringName &p_name) { const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); ERR_FAIL_COND_V(!bfi, 0); - uint32_t hash = hash_djb2_one_32(bfi->is_vararg); - hash = hash_djb2_one_32(bfi->returns_value, hash); + uint32_t hash = hash_murmur3_one_32(bfi->is_vararg); + hash = hash_murmur3_one_32(bfi->returns_value, hash); if (bfi->returns_value) { - hash = hash_djb2_one_32(bfi->return_type, hash); + hash = hash_murmur3_one_32(bfi->return_type, hash); } - hash = hash_djb2_one_32(bfi->argcount, hash); + hash = hash_murmur3_one_32(bfi->argcount, hash); for (int i = 0; i < bfi->argcount; i++) { - hash = hash_djb2_one_32(bfi->get_arg_type(i), hash); + hash = hash_murmur3_one_32(bfi->get_arg_type(i), hash); } - return hash; + return hash_fmix32(hash); } void Variant::get_utility_function_list(List<StringName> *r_functions) { |