diff options
Diffstat (limited to 'core')
44 files changed, 1525 insertions, 900 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 7fdea7d1aa..17d3bdb744 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -74,6 +74,14 @@ int Engine::get_max_fps() const { return _max_fps; } +void Engine::set_audio_output_latency(int p_msec) { + _audio_output_latency = p_msec > 1 ? p_msec : 1; +} + +int Engine::get_audio_output_latency() const { + return _audio_output_latency; +} + uint64_t Engine::get_frames_drawn() { return frames_drawn; } @@ -239,6 +247,10 @@ bool Engine::is_validation_layers_enabled() const { return use_validation_layers; } +bool Engine::is_generate_spirv_debug_info_enabled() const { + return generate_spirv_debug_info; +} + void Engine::set_print_error_messages(bool p_enabled) { CoreGlobals::print_error_enabled = p_enabled; } diff --git a/core/config/engine.h b/core/config/engine.h index 5ea653ba6c..ff88fbc787 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -61,12 +61,14 @@ private: double physics_jitter_fix = 0.5; double _fps = 1; int _max_fps = 0; + int _audio_output_latency = 0; double _time_scale = 1.0; uint64_t _physics_frames = 0; int max_physics_steps_per_frame = 8; double _physics_interpolation_fraction = 0.0f; bool abort_on_gpu_errors = false; bool use_validation_layers = false; + bool generate_spirv_debug_info = false; int32_t gpu_idx = -1; uint64_t _process_frames = 0; @@ -98,6 +100,9 @@ public: virtual void set_max_fps(int p_fps); virtual int get_max_fps() const; + virtual void set_audio_output_latency(int p_msec); + virtual int get_audio_output_latency() const; + virtual double get_frames_per_second() const { return _fps; } uint64_t get_frames_drawn(); @@ -156,6 +161,7 @@ public: bool is_abort_on_gpu_errors_enabled() const; bool is_validation_layers_enabled() const; + bool is_generate_spirv_debug_info_enabled() const; int32_t get_gpu_index() const; Engine(); diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 715ed61770..1bfb745662 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -146,30 +146,30 @@ const PackedStringArray ProjectSettings::_trim_to_supported_features(const Packe #endif // TOOLS_ENABLED String ProjectSettings::localize_path(const String &p_path) const { - if (resource_path.is_empty() || (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) { - return p_path.simplify_path(); + String path = p_path.simplify_path(); + + if (resource_path.is_empty() || (path.is_absolute_path() && !path.begins_with(resource_path))) { + return path; } // Check if we have a special path (like res://) or a protocol identifier. - int p = p_path.find("://"); + int p = path.find("://"); bool found = false; if (p > 0) { found = true; for (int i = 0; i < p; i++) { - if (!is_ascii_alphanumeric_char(p_path[i])) { + if (!is_ascii_alphanumeric_char(path[i])) { found = false; break; } } } if (found) { - return p_path.simplify_path(); + return path; } Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - String path = p_path.replace("\\", "/").simplify_path(); - if (dir->change_dir(path) == OK) { String cwd = dir->get_current_dir(); cwd = cwd.replace("\\", "/"); @@ -187,7 +187,7 @@ String ProjectSettings::localize_path(const String &p_path) const { cwd = cwd.path_join(""); if (!cwd.begins_with(res_path)) { - return p_path; + return path; } return cwd.replace_first(res_path, "res://"); @@ -283,6 +283,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < custom_feature_array.size(); i++) { custom_features.insert(custom_feature_array[i]); } + _queue_changed(); return true; } @@ -324,6 +325,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { } } + _queue_changed(); return true; } @@ -424,6 +426,22 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { } } +void ProjectSettings::_queue_changed() { + if (is_changed || !MessageQueue::get_singleton() || MessageQueue::get_singleton()->get_max_buffer_usage() == 0) { + return; + } + is_changed = true; + callable_mp(this, &ProjectSettings::_emit_changed).call_deferred(); +} + +void ProjectSettings::_emit_changed() { + if (!is_changed) { + return; + } + is_changed = false; + emit_signal("settings_changed"); +} + bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) { if (PackedData::get_singleton()->is_disabled()) { return false; @@ -1225,6 +1243,8 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0)); ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd); + + ADD_SIGNAL(MethodInfo("settings_changed")); } void ProjectSettings::_add_builtin_input_map() { @@ -1260,6 +1280,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC("application/config/name", ""); GLOBAL_DEF_BASIC(PropertyInfo(Variant::DICTIONARY, "application/config/name_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT), ""); + GLOBAL_DEF_BASIC("application/config/version", ""); GLOBAL_DEF_INTERNAL(PropertyInfo(Variant::STRING, "application/config/tags"), PackedStringArray()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"), ""); GLOBAL_DEF("application/run/disable_stdout", false); @@ -1328,6 +1349,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport"), "disabled"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"), "keep"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "display/window/stretch/scale", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), 1.0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/scale_mode", PROPERTY_HINT_ENUM, "fractional,integer"), "fractional"); GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"), 16384); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index b89e6694b0..dba4aa6822 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -43,6 +43,9 @@ class TypedArray; class ProjectSettings : public Object { GDCLASS(ProjectSettings, Object); _THREAD_SAFE_CLASS_ + friend class TestProjectSettingsInternalsAccessor; + + bool is_changed = false; public: typedef HashMap<String, Variant> CustomMap; @@ -114,6 +117,9 @@ protected: bool _property_can_revert(const StringName &p_name) const; bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _queue_changed(); + void _emit_changed(); + static ProjectSettings *singleton; Error _load_settings_text(const String &p_path); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index a73b198be2..05fe393a2f 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -929,6 +929,17 @@ Geometry3D *Geometry3D::get_singleton() { return singleton; } +Vector<Vector3> Geometry3D::compute_convex_mesh_points(const TypedArray<Plane> &p_planes) { + Vector<Plane> planes_vec; + int size = p_planes.size(); + planes_vec.resize(size); + for (int i = 0; i < size; ++i) { + planes_vec.set(i, p_planes[i]); + } + Variant ret = ::Geometry3D::compute_convex_mesh_points(planes_vec.ptr(), size); + return ret; +} + TypedArray<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) { Variant ret = ::Geometry3D::build_box_planes(p_extents); return ret; @@ -961,6 +972,11 @@ Vector3 Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point return ::Geometry3D::get_closest_point_to_segment_uncapped(p_point, s); } +Vector3 Geometry3D::get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { + Vector3 res = ::Geometry3D::triangle_get_barycentric_coords(p_v0, p_v1, p_v2, p_point); + return res; +} + Variant Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { Vector3 res; if (::Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) { @@ -1024,6 +1040,7 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const } void Geometry3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points); ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes); ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); @@ -1034,6 +1051,8 @@ void Geometry3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment_uncapped); + ClassDB::bind_method(D_METHOD("get_triangle_barycentric_coords", "point", "a", "b", "c"), &Geometry3D::get_triangle_barycentric_coords); + ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &Geometry3D::ray_intersects_triangle); ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &Geometry3D::segment_intersects_triangle); ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &Geometry3D::segment_intersects_sphere); diff --git a/core/core_bind.h b/core/core_bind.h index dc0b2a1cf5..5f51b64eb7 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -320,12 +320,14 @@ protected: public: static Geometry3D *get_singleton(); + Vector<Vector3> compute_convex_mesh_points(const TypedArray<Plane> &p_planes); TypedArray<Plane> build_box_planes(const Vector3 &p_extents); TypedArray<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); TypedArray<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); Vector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2); Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); + Vector3 get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); diff --git a/core/doc_data.h b/core/doc_data.h index 0fe7414b98..b8c92a4b67 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -532,6 +532,42 @@ public: } }; + struct EnumDoc { + String description; + bool is_deprecated = false; + bool is_experimental = false; + static EnumDoc from_dict(const Dictionary &p_dict) { + EnumDoc doc; + + if (p_dict.has("description")) { + doc.description = p_dict["description"]; + } + + if (p_dict.has("is_deprecated")) { + doc.is_deprecated = p_dict["is_deprecated"]; + } + + if (p_dict.has("is_experimental")) { + doc.is_experimental = p_dict["is_experimental"]; + } + + return doc; + } + static Dictionary to_dict(const EnumDoc &p_doc) { + Dictionary dict; + + if (!p_doc.description.is_empty()) { + dict["description"] = p_doc.description; + } + + dict["is_deprecated"] = p_doc.is_deprecated; + + dict["is_experimental"] = p_doc.is_experimental; + + return dict; + } + }; + struct ClassDoc { String name; String inherits; @@ -543,7 +579,7 @@ public: Vector<MethodDoc> operators; Vector<MethodDoc> signals; Vector<ConstantDoc> constants; - HashMap<String, String> enums; + HashMap<String, EnumDoc> enums; Vector<PropertyDoc> properties; Vector<MethodDoc> annotations; Vector<ThemeItemDoc> theme_properties; @@ -626,7 +662,7 @@ public: enums = p_dict["enums"]; } for (int i = 0; i < enums.size(); i++) { - doc.enums[enums.get_key_at_index(i)] = enums.get_value_at_index(i); + doc.enums[enums.get_key_at_index(i)] = EnumDoc::from_dict(enums.get_value_at_index(i)); } Array properties; @@ -740,8 +776,8 @@ public: if (!p_doc.enums.is_empty()) { Dictionary enums; - for (const KeyValue<String, String> &E : p_doc.enums) { - enums[E.key] = E.value; + for (const KeyValue<String, EnumDoc> &E : p_doc.enums) { + enums[E.key] = EnumDoc::to_dict(E.value); } dict["enums"] = enums; } diff --git a/core/error/error_macros.h b/core/error/error_macros.h index 65804b7796..c8182975d5 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -786,8 +786,19 @@ void _err_flush_stdout(); ((void)0) /** - * This should be a 'free' assert for program flow and should not be needed in any releases, - * only used in dev builds. + * Note: IN MOST CASES YOU SHOULD NOT USE THIS MACRO. + * Do not use unless you understand the trade-offs. + * + * DEV macros will be compiled out in releases, they are wrapped in DEV_ENABLED. + * + * Prefer WARNINGS / ERR_FAIL macros (which fail without crashing) - ERR_FAIL should be used in most cases. + * Then CRASH_NOW_MSG macros (on rare occasions where error cannot be recovered). + * + * DEV_ASSERT should generally only be used when both of the following conditions are met: + * 1) Bottleneck code where a check in release would be too expensive. + * 2) Situations where the check would fail obviously and straight away during the maintenance of the code + * (i.e. strict conditions that should be true no matter what) + * and that can't fail for other contributors once the code is finished and merged. */ #ifdef DEV_ENABLED #define DEV_ASSERT(m_cond) \ diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 67b55db3db..e39a531d0d 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -485,6 +485,13 @@ void GDExtension::close_library() { ERR_FAIL_COND(library == nullptr); OS::get_singleton()->close_dynamic_library(library); +#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED) + // Delete temporary copy of library if it exists. + if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) { + DirAccess::remove_absolute(temp_lib_path); + } +#endif + library = nullptr; } @@ -607,12 +614,13 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String } bool compatible = true; - if (VERSION_MAJOR < compatibility_minimum[0]) { - compatible = false; - } else if (VERSION_MINOR < compatibility_minimum[1]) { - compatible = false; - } else if (VERSION_PATCH < compatibility_minimum[2]) { - compatible = false; + // Check version lexicographically. + if (VERSION_MAJOR != compatibility_minimum[0]) { + compatible = VERSION_MAJOR > compatibility_minimum[0]; + } else if (VERSION_MINOR != compatibility_minimum[1]) { + compatible = VERSION_MINOR > compatibility_minimum[1]; + } else { + compatible = VERSION_PATCH >= compatibility_minimum[2]; } if (!compatible) { if (r_error) { @@ -640,6 +648,40 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String Ref<GDExtension> lib; lib.instantiate(); String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path); + +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + // If running on the editor on Windows, we copy the library and open the copy. + // This is so the original file isn't locked and can be updated by a compiler. + if (Engine::get_singleton()->is_editor_hint()) { + if (!FileAccess::exists(abs_path)) { + if (r_error) { + *r_error = ERR_FILE_NOT_FOUND; + } + ERR_PRINT("GDExtension library not found: " + library_path); + return Ref<Resource>(); + } + + // Copy the file to the same directory as the original with a prefix in the name. + // This is so relative path to dependencies are satisfied. + String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file()); + + Error copy_err = DirAccess::copy_absolute(abs_path, copy_path); + if (copy_err) { + if (r_error) { + *r_error = ERR_CANT_CREATE; + } + ERR_PRINT("Error copying GDExtension library: " + library_path); + return Ref<Resource>(); + } + FileAccess::set_hidden_attribute(copy_path, true); + + // Save the copied path so it can be deleted later. + lib->set_temp_library_path(copy_path); + + // Use the copy to open the library. + abs_path = copy_path; + } +#endif err = lib->open_library(abs_path, entry_symbol); if (r_error) { @@ -647,6 +689,12 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String } if (err != OK) { +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + // If the DLL fails to load, make sure that temporary DLL copies are cleaned up. + if (Engine::get_singleton()->is_editor_hint()) { + DirAccess::remove_absolute(lib->get_temp_library_path()); + } +#endif // Errors already logged in open_library() return Ref<Resource>(); } diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index b935f8706f..5a0e39302b 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -43,6 +43,9 @@ class GDExtension : public Resource { void *library = nullptr; // pointer if valid, String library_path; +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + String temp_lib_path; +#endif struct Extension { ObjectGDExtension gdextension; @@ -76,6 +79,11 @@ public: Error open_library(const String &p_path, const String &p_entry_symbol); void close_library(); +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + void set_temp_library_path(const String &p_path) { temp_lib_path = p_path; } + String get_temp_library_path() const { return temp_lib_path; } +#endif + enum InitializationLevel { INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE, INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS, diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 7ef956a470..b4541de8fe 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -1048,6 +1048,25 @@ static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDE return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension); } +static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) { + if (!p_object || !p_language) { + return nullptr; + } + + const Object *o = (const Object *)p_object; + ScriptInstanceExtension *script_instance_extension = reinterpret_cast<ScriptInstanceExtension *>(o->get_script_instance()); + if (!script_instance_extension) { + return nullptr; + } + + const ScriptLanguage *language = script_instance_extension->get_language(); + if (language != p_language) { + return nullptr; + } + + return script_instance_extension->instance; +} + static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) { const StringName classname = *reinterpret_cast<const StringName *>(p_classname); const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname); @@ -1216,6 +1235,7 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(ref_get_object); REGISTER_INTERFACE_FUNC(ref_set_object); REGISTER_INTERFACE_FUNC(script_instance_create); + REGISTER_INTERFACE_FUNC(object_get_script_instance); REGISTER_INTERFACE_FUNC(classdb_construct_object); REGISTER_INTERFACE_FUNC(classdb_get_method_bind); REGISTER_INTERFACE_FUNC(classdb_get_class_tag); diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 6c05f3988b..cfc21473d6 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -2126,6 +2126,19 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte */ typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); +/** + * @name object_get_script_instance + * @since 4.2 + * + * Get the script instance data attached to this object. + * + * @param p_object A pointer to the Object. + * @param p_language A pointer to the language expected for this script instance. + * + * @return A GDExtensionScriptInstanceDataPtr that was attached to this object as part of script_instance_create. + */ +typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language); + /* INTERFACE: ClassDB */ /** diff --git a/core/input/input.cpp b/core/input/input.cpp index d481acf005..39f1acf623 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -113,6 +113,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis); ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name); ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid); + ClassDB::bind_method(D_METHOD("get_joy_info", "device"), &Input::get_joy_info); ClassDB::bind_method(D_METHOD("should_ignore_device", "vendor_id", "product_id"), &Input::should_ignore_device); ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads); ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); @@ -437,11 +438,12 @@ static String _hex_str(uint8_t p_byte) { return ret; } -void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) { +void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) { _THREAD_SAFE_METHOD_ Joypad js; js.name = p_connected ? p_name : ""; js.uid = p_connected ? p_guid : ""; + js.info = p_connected ? p_joypad_info : Dictionary(); if (p_connected) { String uidname = p_guid; @@ -473,7 +475,8 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S } joy_names[p_idx] = js; - emit_signal(SNAME("joy_connection_changed"), p_idx, p_connected); + // Ensure this signal is emitted on the main thread, as some platforms (e.g. Linux) call this from a different thread. + call_deferred("emit_signal", SNAME("joy_connection_changed"), p_idx, p_connected); } Vector3 Input::get_gravity() const { @@ -1499,6 +1502,11 @@ String Input::get_joy_guid(int p_device) const { return joy_names[p_device].uid; } +Dictionary Input::get_joy_info(int p_device) const { + ERR_FAIL_COND_V(!joy_names.has(p_device), Dictionary()); + return joy_names[p_device].info; +} + bool Input::should_ignore_device(int p_vendor_id, int p_product_id) const { uint32_t full_id = (((uint32_t)p_vendor_id) << 16) | ((uint16_t)p_product_id); return ignored_device_ids.has(full_id); diff --git a/core/input/input.h b/core/input/input.h index ec16871b72..c63a4e52e3 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -149,6 +149,7 @@ private: HatMask last_hat = HatMask::CENTER; int mapping = -1; int hat_current = 0; + Dictionary info; }; VelocityTrack mouse_velocity_track; @@ -276,7 +277,7 @@ public: Vector2 get_joy_vibration_strength(int p_device); float get_joy_vibration_duration(int p_device); uint64_t get_joy_vibration_timestamp(int p_device); - void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); + void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "", Dictionary p_joypad_info = Dictionary()); Vector3 get_gravity() const; Vector3 get_accelerometer() const; @@ -332,6 +333,7 @@ public: bool is_joy_known(int p_device); String get_joy_guid(int p_device) const; bool should_ignore_device(int p_vendor_id, int p_product_id) const; + Dictionary get_joy_info(int p_device) const; void set_fallback_mapping(String p_guid); void flush_buffered_events(); diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index b669afdc99..6026dbf896 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -583,7 +583,7 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { return mt; } -uint32_t FileAccess::get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const String &p_file) { if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { return 0; } @@ -591,11 +591,10 @@ uint32_t FileAccess::get_unix_permissions(const String &p_file) { Ref<FileAccess> fa = create_for_path(p_file); ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'."); - uint32_t mt = fa->_get_unix_permissions(p_file); - return mt; + return fa->_get_unix_permissions(p_file); } -Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) { +Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { return ERR_UNAVAILABLE; } @@ -607,6 +606,52 @@ Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissi return err; } +bool FileAccess::get_hidden_attribute(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return false; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'."); + + return fa->_get_hidden_attribute(p_file); +} + +Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return ERR_UNAVAILABLE; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + + Error err = fa->_set_hidden_attribute(p_file, p_hidden); + return err; +} + +bool FileAccess::get_read_only_attribute(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return false; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'."); + + return fa->_get_read_only_attribute(p_file); +} + +Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return ERR_UNAVAILABLE; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + + Error err = fa->_set_read_only_attribute(p_file, p_ro); + return err; +} + void FileAccess::store_string(const String &p_string) { if (p_string.length() == 0) { return; @@ -865,6 +910,14 @@ void FileAccess::_bind_methods() { ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists); ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_unix_permissions", "file"), &FileAccess::get_unix_permissions); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_unix_permissions", "file", "permissions"), &FileAccess::set_unix_permissions); + + ClassDB::bind_static_method("FileAccess", D_METHOD("get_hidden_attribute", "file"), &FileAccess::get_hidden_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_hidden_attribute", "file", "hidden"), &FileAccess::set_hidden_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_read_only_attribute", "file", "ro"), &FileAccess::set_read_only_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_read_only_attribute", "file"), &FileAccess::get_read_only_attribute); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian"); BIND_ENUM_CONSTANT(READ); @@ -877,4 +930,17 @@ void FileAccess::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESSION_ZSTD); BIND_ENUM_CONSTANT(COMPRESSION_GZIP); BIND_ENUM_CONSTANT(COMPRESSION_BROTLI); + + BIND_BITFIELD_FLAG(UNIX_READ_OWNER); + BIND_BITFIELD_FLAG(UNIX_WRITE_OWNER); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_OWNER); + BIND_BITFIELD_FLAG(UNIX_READ_GROUP); + BIND_BITFIELD_FLAG(UNIX_WRITE_GROUP); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_GROUP); + BIND_BITFIELD_FLAG(UNIX_READ_OTHER); + BIND_BITFIELD_FLAG(UNIX_WRITE_OTHER); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_OTHER); + BIND_BITFIELD_FLAG(UNIX_SET_USER_ID); + BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID); + BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE); } diff --git a/core/io/file_access.h b/core/io/file_access.h index ad1ac665f3..7b9e66bb83 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -60,6 +60,21 @@ public: WRITE_READ = 7, }; + enum UnixPermissionFlags { + UNIX_EXECUTE_OTHER = 0x001, + UNIX_WRITE_OTHER = 0x002, + UNIX_READ_OTHER = 0x004, + UNIX_EXECUTE_GROUP = 0x008, + UNIX_WRITE_GROUP = 0x010, + UNIX_READ_GROUP = 0x020, + UNIX_EXECUTE_OWNER = 0x040, + UNIX_WRITE_OWNER = 0x080, + UNIX_READ_OWNER = 0x100, + UNIX_RESTRICTED_DELETE = 0x200, + UNIX_SET_GROUP_ID = 0x400, + UNIX_SET_USER_ID = 0x800, + }; + enum CompressionMode { COMPRESSION_FASTLZ = Compression::MODE_FASTLZ, COMPRESSION_DEFLATE = Compression::MODE_DEFLATE, @@ -74,8 +89,13 @@ public: bool big_endian = false; bool real_is_double = false; - virtual uint32_t _get_unix_permissions(const String &p_file) = 0; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0; + virtual BitField<UnixPermissionFlags> _get_unix_permissions(const String &p_file) = 0; + virtual Error _set_unix_permissions(const String &p_file, BitField<UnixPermissionFlags> p_permissions) = 0; + + virtual bool _get_hidden_attribute(const String &p_file) = 0; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) = 0; + virtual bool _get_read_only_attribute(const String &p_file) = 0; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) = 0; protected: static void _bind_methods(); @@ -185,8 +205,13 @@ public: static CreateFunc get_create_func(AccessType p_access); static bool exists(const String &p_name); ///< return true if a file exists static uint64_t get_modified_time(const String &p_file); - static uint32_t get_unix_permissions(const String &p_file); - static Error set_unix_permissions(const String &p_file, uint32_t p_permissions); + static BitField<FileAccess::UnixPermissionFlags> get_unix_permissions(const String &p_file); + static Error set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions); + + static bool get_hidden_attribute(const String &p_file); + static Error set_hidden_attribute(const String &p_file, bool p_hidden); + static bool get_read_only_attribute(const String &p_file); + static Error set_read_only_attribute(const String &p_file, bool p_ro); static void set_backup_save(bool p_enable) { backup_save = p_enable; }; static bool is_backup_save_enabled() { return backup_save; }; @@ -212,5 +237,6 @@ public: VARIANT_ENUM_CAST(FileAccess::CompressionMode); VARIANT_ENUM_CAST(FileAccess::ModeFlags); +VARIANT_BITFIELD_CAST(FileAccess::UnixPermissionFlags); #endif // FILE_ACCESS_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 3e5a1217dd..0f00bd292c 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -365,20 +365,48 @@ uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { } } -uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccessCompressed::_get_unix_permissions(const String &p_file) { if (f.is_valid()) { return f->_get_unix_permissions(p_file); } return 0; } -Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { +Error FileAccessCompressed::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { if (f.is_valid()) { return f->_set_unix_permissions(p_file, p_permissions); } return FAILED; } +bool FileAccessCompressed::_get_hidden_attribute(const String &p_file) { + if (f.is_valid()) { + return f->_get_hidden_attribute(p_file); + } + return false; +} + +Error FileAccessCompressed::_set_hidden_attribute(const String &p_file, bool p_hidden) { + if (f.is_valid()) { + return f->_set_hidden_attribute(p_file, p_hidden); + } + return FAILED; +} + +bool FileAccessCompressed::_get_read_only_attribute(const String &p_file) { + if (f.is_valid()) { + return f->_get_read_only_attribute(p_file); + } + return false; +} + +Error FileAccessCompressed::_set_read_only_attribute(const String &p_file, bool p_ro) { + if (f.is_valid()) { + return f->_set_read_only_attribute(p_file, p_ro); + } + return FAILED; +} + void FileAccessCompressed::close() { _close(); } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 601b74a9c1..bf57eaa07c 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -94,8 +94,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; virtual void close() override; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index c39d19d52b..b689f5b628 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -285,13 +285,46 @@ uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) { return 0; } -uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccessEncrypted::_get_unix_permissions(const String &p_file) { + if (file.is_valid()) { + return file->_get_unix_permissions(p_file); + } return 0; } -Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { - ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet."); - return ERR_UNAVAILABLE; +Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { + if (file.is_valid()) { + return file->_set_unix_permissions(p_file, p_permissions); + } + return FAILED; +} + +bool FileAccessEncrypted::_get_hidden_attribute(const String &p_file) { + if (file.is_valid()) { + return file->_get_hidden_attribute(p_file); + } + return false; +} + +Error FileAccessEncrypted::_set_hidden_attribute(const String &p_file, bool p_hidden) { + if (file.is_valid()) { + return file->_set_hidden_attribute(p_file, p_hidden); + } + return FAILED; +} + +bool FileAccessEncrypted::_get_read_only_attribute(const String &p_file) { + if (file.is_valid()) { + return file->_get_read_only_attribute(p_file); + } + return false; +} + +Error FileAccessEncrypted::_set_read_only_attribute(const String &p_file, bool p_ro) { + if (file.is_valid()) { + return file->_set_read_only_attribute(p_file, p_ro); + } + return FAILED; } void FileAccessEncrypted::close() { diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 9702b5a517..489d213b8f 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -85,8 +85,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; virtual void close() override; diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 43fe6ab658..ac08e5406f 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -68,8 +68,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } virtual void close() override {} diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 1538b302c2..97391a5611 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -150,8 +150,13 @@ class FileAccessPack : public FileAccess { Ref<FileAccess> f; virtual Error open_internal(const String &p_path, int p_mode_flags) override; virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } public: virtual bool is_open() const override; diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index f8b640946c..1062a06238 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -106,8 +106,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } // todo - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } virtual void close() override; diff --git a/core/io/image.cpp b/core/io/image.cpp index 7326563f18..3ca39f98c0 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3017,6 +3017,8 @@ ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_dds_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr; @@ -3488,6 +3490,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer); + ClassDB::bind_method(D_METHOD("load_dds_from_buffer", "buffer"), &Image::load_dds_from_buffer); + ClassDB::bind_method(D_METHOD("load_ktx_from_buffer", "buffer"), &Image::load_ktx_from_buffer); ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0)); @@ -3863,6 +3867,22 @@ Error Image::load_svg_from_string(const String &p_svg_str, float scale) { return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale); } +Error Image::load_dds_from_buffer(const Vector<uint8_t> &p_array) { + ERR_FAIL_NULL_V_MSG( + _dds_mem_loader_func, + ERR_UNAVAILABLE, + "The DDS module isn't enabled. Recompile the Godot editor or export template binary with the `module_dds_enabled=yes` SCons option."); + return _load_from_buffer(p_array, _dds_mem_loader_func); +} + +Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) { + ERR_FAIL_NULL_V_MSG( + _ktx_mem_loader_func, + ERR_UNAVAILABLE, + "The KTX module isn't enabled. Recompile the Godot editor or export template binary with the `module_ktx_enabled=yes` SCons option."); + return _load_from_buffer(p_array, _ktx_mem_loader_func); +} + void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); diff --git a/core/io/image.h b/core/io/image.h index f877b00ee6..cb7c6bff52 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -150,6 +150,8 @@ public: static ImageMemLoadFunc _tga_mem_loader_func; static ImageMemLoadFunc _bmp_mem_loader_func; static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func; + static ImageMemLoadFunc _dds_mem_loader_func; + static ImageMemLoadFunc _ktx_mem_loader_func; static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels); @@ -402,6 +404,8 @@ public: Error load_webp_from_buffer(const Vector<uint8_t> &p_array); Error load_tga_from_buffer(const Vector<uint8_t> &p_array); Error load_bmp_from_buffer(const Vector<uint8_t> &p_array); + Error load_dds_from_buffer(const Vector<uint8_t> &p_array); + Error load_ktx_from_buffer(const Vector<uint8_t> &p_array); Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0); Error load_svg_from_string(const String &p_svg_str, float scale = 1.0); diff --git a/core/io/remote_filesystem_client.cpp b/core/io/remote_filesystem_client.cpp index f22e442a34..1198810441 100644 --- a/core/io/remote_filesystem_client.cpp +++ b/core/io/remote_filesystem_client.cpp @@ -270,7 +270,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int String file = temp_file_cache[i].path; if (temp_file_cache[i].server_modified_time == 0 || server_disconnected) { - // File was removed, or server disconnected before tranferring it. Since it's no longer valid, remove anyway. + // File was removed, or server disconnected before transferring it. Since it's no longer valid, remove anyway. _remove_file(file); continue; } diff --git a/core/io/remote_filesystem_client.h b/core/io/remote_filesystem_client.h index 42eba98eb1..fcb5c1cfc3 100644 --- a/core/io/remote_filesystem_client.h +++ b/core/io/remote_filesystem_client.h @@ -44,8 +44,8 @@ protected: String _get_cache_path() { return cache_path; } struct FileCache { String path; // Local path (as in "folder/to/file.png") - uint64_t server_modified_time; // MD5 checksum. - uint64_t modified_time; + uint64_t server_modified_time = 0; // MD5 checksum. + uint64_t modified_time = 0; }; virtual bool _is_configured() { return !cache_path.is_empty(); } // Can be re-implemented per platform. If so, feel free to ignore get_cache_path() diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 07677337b4..68cdeabac7 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -239,6 +239,7 @@ void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource List<PropertyInfo> plist; get_property_list(&plist); + reset_local_to_scene(); local_scene = p_for_scene; for (const PropertyInfo &E : plist) { @@ -382,6 +383,10 @@ void Resource::setup_local_to_scene() { emit_signal(SNAME("setup_local_to_scene_requested")); } +void Resource::reset_local_to_scene() { + // Restores the state as if setup_local_to_scene() hadn't been called. +} + Node *(*Resource::_get_local_scene_func)() = nullptr; void (*Resource::_update_configuration_warning)() = nullptr; diff --git a/core/io/resource.h b/core/io/resource.h index af8c275a1c..f848bdba99 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -80,6 +80,8 @@ protected: void _set_path(const String &p_path); void _take_over_path(const String &p_path); + virtual void reset_local_to_scene(); + public: static Node *(*_get_local_scene_func)(); //used by editor static void (*_update_configuration_warning)(); //used by editor diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 1fe662b1fa..df0253349c 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -275,10 +275,10 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin #ifdef TOOLS_ENABLED Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); - ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), "Resource file not found: " + p_path + "."); + ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint)); #endif - ERR_FAIL_V_MSG(Ref<Resource>(), "No loader found for resource: " + p_path + "."); + ERR_FAIL_V_MSG(Ref<Resource>(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint)); } void ResourceLoader::_thread_load_function(void *p_userdata) { diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 63f7c80bdd..9ba4c2ff9a 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -194,6 +194,38 @@ real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const { return GET_POINT_UNCHECKED(p_id).weight_scale; } +void AStarGrid2D::fill_solid_region(const Rect2i &p_region, bool p_solid) { + ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); + + Rect2i safe_region = p_region.intersection(region); + int from_x = safe_region.get_position().x; + int from_y = safe_region.get_position().y; + int end_x = safe_region.get_end().x; + int end_y = safe_region.get_end().y; + + for (int x = from_x; x < end_x; x++) { + for (int y = from_y; y < end_y; y++) { + GET_POINT_UNCHECKED(Vector2i(x, y)).solid = p_solid; + } + } +} + +void AStarGrid2D::fill_weight_scale_region(const Rect2i &p_region, real_t p_weight_scale) { + ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale)); + + Rect2i safe_region = p_region.intersection(region); + int from_x = safe_region.get_position().x; + int from_y = safe_region.get_position().y; + int end_x = safe_region.get_end().x; + int end_y = safe_region.get_end().y; + for (int x = from_x; x < end_x; x++) { + for (int y = from_y; y < end_y; y++) { + GET_POINT_UNCHECKED(Vector2i(x, y)).weight_scale = p_weight_scale; + } + } +} + AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) { if (!p_to || p_to->solid) { return nullptr; @@ -606,6 +638,8 @@ void AStarGrid2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_point_solid", "id"), &AStarGrid2D::is_point_solid); ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStarGrid2D::set_point_weight_scale); ClassDB::bind_method(D_METHOD("get_point_weight_scale", "id"), &AStarGrid2D::get_point_weight_scale); + ClassDB::bind_method(D_METHOD("fill_solid_region", "region", "solid"), &AStarGrid2D::fill_solid_region, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("fill_weight_scale_region", "region", "weight_scale"), &AStarGrid2D::fill_weight_scale_region); ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear); ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position); diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index 50df58e0e9..dd5f9d0575 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -177,6 +177,9 @@ public: void set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale); real_t get_point_weight_scale(const Vector2i &p_id) const; + void fill_solid_region(const Rect2i &p_region, bool p_solid = true); + void fill_weight_scale_region(const Rect2i &p_region, real_t p_weight_scale); + void clear(); Vector2 get_point_position(const Vector2i &p_id) const; diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h index 55923e0133..7df8c37e3c 100644 --- a/core/math/delaunay_3d.h +++ b/core/math/delaunay_3d.h @@ -105,8 +105,8 @@ class Delaunay3D { }; _FORCE_INLINE_ static void circum_sphere_compute(const Vector3 *p_points, Simplex *p_simplex) { - // the only part in the algorithm where there may be precision errors is this one, so ensure that - // we do it as maximum precision as possible + // The only part in the algorithm where there may be precision errors is this one, + // so ensure that we do it with the maximum precision possible. R128 v0_x = p_points[p_simplex->points[0]].x; R128 v0_y = p_points[p_simplex->points[0]].y; @@ -121,7 +121,7 @@ class Delaunay3D { R128 v3_y = p_points[p_simplex->points[3]].y; R128 v3_z = p_points[p_simplex->points[3]].z; - //Create the rows of our "unrolled" 3x3 matrix + // Create the rows of our "unrolled" 3x3 matrix. R128 row1_x = v1_x - v0_x; R128 row1_y = v1_y - v0_y; R128 row1_z = v1_z - v0_z; @@ -138,10 +138,10 @@ class Delaunay3D { R128 sq_lenght2 = row2_x * row2_x + row2_y * row2_y + row2_z * row2_z; R128 sq_lenght3 = row3_x * row3_x + row3_y * row3_y + row3_z * row3_z; - //Compute the determinant of said matrix + // Compute the determinant of said matrix. R128 determinant = row1_x * (row2_y * row3_z - row3_y * row2_z) - row2_x * (row1_y * row3_z - row3_y * row1_z) + row3_x * (row1_y * row2_z - row2_y * row1_z); - // Compute the volume of the tetrahedron, and precompute a scalar quantity for re-use in the formula + // Compute the volume of the tetrahedron, and precompute a scalar quantity for reuse in the formula. R128 volume = determinant / R128(6.f); R128 i12volume = R128(1.f) / (volume * R128(12.f)); @@ -149,8 +149,7 @@ class Delaunay3D { R128 center_y = v0_y + i12volume * (-(row2_x * row3_z - row3_x * row2_z) * sq_lenght1 + (row1_x * row3_z - row3_x * row1_z) * sq_lenght2 - (row1_x * row2_z - row2_x * row1_z) * sq_lenght3); R128 center_z = v0_z + i12volume * ((row2_x * row3_y - row3_x * row2_y) * sq_lenght1 - (row1_x * row3_y - row3_x * row1_y) * sq_lenght2 + (row1_x * row2_y - row2_x * row1_y) * sq_lenght3); - //Once we know the center, the radius is clearly the distance to any vertex - + // Once we know the center, the radius is clearly the distance to any vertex. R128 rel1_x = center_x - v0_x; R128 rel1_y = center_y - v0_y; R128 rel1_z = center_z - v0_z; diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index c8c50fb957..e9fd8ad583 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -1661,7 +1661,15 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { c.name = p_extension->class_name; c.is_virtual = p_extension->is_virtual; if (!p_extension->is_abstract) { - c.creation_func = parent->creation_func; + // Find the closest ancestor which is either non-abstract or native (or both). + ClassInfo *concrete_ancestor = parent; + while (concrete_ancestor->creation_func == nullptr && + concrete_ancestor->inherits_ptr != nullptr && + concrete_ancestor->gdextension != nullptr) { + concrete_ancestor = concrete_ancestor->inherits_ptr; + } + ERR_FAIL_NULL_MSG(concrete_ancestor->creation_func, "Extension class " + String(p_extension->class_name) + " cannot extend native abstract class " + String(concrete_ancestor->name)); + c.creation_func = concrete_ancestor->creation_func; } c.inherits = parent->name; c.class_ptr = parent->class_ptr; diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index dd7aba1384..506f8291eb 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -35,10 +35,6 @@ #include "core/object/class_db.h" #include "core/object/script_language.h" -#ifdef DEBUG_ENABLED -#include "core/config/engine.h" -#endif - #ifdef DEV_ENABLED // Includes sanity checks to ensure that a queue set as a thread singleton override // is only ever called from the thread it was set for. @@ -320,34 +316,25 @@ Error CallQueue::flush() { Object *target = message->callable.get_object(); UNLOCK_MUTEX; -#ifdef DEBUG_ENABLED - if (!message->callable.is_valid()) { - // The editor would cause many of these. - if (!Engine::get_singleton()->is_editor_hint()) { - ERR_PRINT("Trying to execute a deferred call/notification/set on a previously freed instance. Consider using queue_free() instead of free()."); - } - } else -#endif - { - switch (message->type & FLAG_MASK) { - case TYPE_CALL: { - if (target || (message->type & FLAG_NULL_IS_OK)) { - Variant *args = (Variant *)(message + 1); - _call_function(message->callable, args, message->args, message->type & FLAG_SHOW_ERROR); - } - } break; - case TYPE_NOTIFICATION: { - if (target) { - target->notification(message->notification); - } - } break; - case TYPE_SET: { - if (target) { - Variant *arg = (Variant *)(message + 1); - target->set(message->callable.get_method(), *arg); - } - } break; - } + + switch (message->type & FLAG_MASK) { + case TYPE_CALL: { + if (target || (message->type & FLAG_NULL_IS_OK)) { + Variant *args = (Variant *)(message + 1); + _call_function(message->callable, args, message->args, message->type & FLAG_SHOW_ERROR); + } + } break; + case TYPE_NOTIFICATION: { + if (target) { + target->notification(message->notification); + } + } break; + case TYPE_SET: { + if (target) { + Variant *arg = (Variant *)(message + 1); + target->set(message->callable.get_method(), *arg); + } + } break; } if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) { diff --git a/core/string/locales.h b/core/string/locales.h index 8a7efb4fd1..840fca65a7 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -1057,8 +1057,8 @@ static const char *script_list[][2] = { { "Hangul", "Hang" }, { "Han", "Hani" }, { "Hanunoo", "Hano" }, - { "Simplified", "Hans" }, - { "Traditional", "Hant" }, + { "Simplified Han", "Hans" }, + { "Traditional Han", "Hant" }, { "Hatran", "Hatr" }, { "Hebrew", "Hebr" }, { "Hiragana", "Hira" }, @@ -1110,7 +1110,7 @@ static const char *script_list[][2] = { { "Mro", "Mroo" }, { "Meitei Mayek", "Mtei" }, { "Multani", "Mult" }, - { "Myanmar (Burmese)", "Mymr" }, + { "Myanmar / Burmese", "Mymr" }, { "Nag Mundari", "Nagm" }, { "Nandinagari", "Nand" }, { "Old North Arabian", "Narb" }, diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 12e6423724..3f11459a1e 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1750,7 +1750,7 @@ Vector<uint8_t> String::hex_decode() const { void String::print_unicode_error(const String &p_message, bool p_critical) const { if (p_critical) { - print_error(vformat("Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message)); + print_error(vformat(U"Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message)); } else { print_error(vformat("Unicode parsing error: %s", p_message)); } @@ -3635,6 +3635,23 @@ String String::repeat(int p_count) const { return new_string; } +String String::reverse() const { + int len = length(); + if (len <= 1) { + return *this; + } + String new_string; + new_string.resize(len + 1); + + const char32_t *src = ptr(); + char32_t *dst = new_string.ptrw(); + for (int i = 0; i < len; i++) { + dst[i] = src[len - i - 1]; + } + dst[len] = _null; + return new_string; +} + String String::left(int p_len) const { if (p_len < 0) { p_len = length() + p_len; @@ -3648,7 +3665,9 @@ String String::left(int p_len) const { return *this; } - return substr(0, p_len); + String s; + s.copy_from_unchecked(&get_data()[0], p_len); + return s; } String String::right(int p_len) const { @@ -3664,7 +3683,9 @@ String String::right(int p_len) const { return *this; } - return substr(length() - p_len); + String s; + s.copy_from_unchecked(&get_data()[length() - p_len], p_len); + return s; } char32_t String::unicode_at(int p_idx) const { diff --git a/core/string/ustring.h b/core/string/ustring.h index 295625395d..f45392eee1 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -305,6 +305,7 @@ public: String replace(const char *p_key, const char *p_with) const; String replacen(const String &p_key, const String &p_with) const; String repeat(int p_count) const; + String reverse() const; String insert(int p_at_pos, const String &p_string) const; String erase(int p_pos, int p_chars = 1) const; String pad_decimals(int p_digits) const; diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h index 98afc7e717..055c52aa63 100644 --- a/core/variant/typed_array.h +++ b/core/variant/typed_array.h @@ -230,4 +230,7 @@ MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY) MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY) MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING) +#undef MAKE_TYPED_ARRAY +#undef MAKE_TYPED_ARRAY_INFO + #endif // TYPED_ARRAY_H diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 10a267e5a9..8a0289898d 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1754,11 +1754,10 @@ String Variant::stringify(int recursion_count) const { case COLOR: return operator Color(); case DICTIONARY: { + ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "{ ... }", "Maximum dictionary recursion reached!"); + recursion_count++; + const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem); - if (recursion_count > MAX_RECURSION) { - ERR_PRINT("Maximum dictionary recursion reached!"); - return "{ ... }"; - } // Add leading and trailing space to Dictionary printing. This distinguishes it // from array printing on fonts that have similar-looking {} and [] characters. @@ -1768,7 +1767,6 @@ String Variant::stringify(int recursion_count) const { Vector<_VariantStrPair> pairs; - recursion_count++; for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { _VariantStrPair sp; sp.key = stringify_variant_clean(E->get(), recursion_count); @@ -1787,6 +1785,7 @@ String Variant::stringify(int recursion_count) const { return str; } + // Packed arrays cannot contain recursive structures, the recursion_count increment is not needed. case PACKED_VECTOR2_ARRAY: { return stringify_vector(operator Vector<Vector2>(), recursion_count); } @@ -1815,13 +1814,10 @@ String Variant::stringify(int recursion_count) const { return stringify_vector(operator Vector<double>(), recursion_count); } case ARRAY: { - Array arr = operator Array(); - if (recursion_count > MAX_RECURSION) { - ERR_PRINT("Maximum array recursion reached!"); - return "[...]"; - } + ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "[...]", "Maximum array recursion reached!"); + recursion_count++; - return stringify_vector(arr, recursion_count); + return stringify_vector(operator Array(), recursion_count); } case OBJECT: { if (_get_obj().obj) { diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index dad9183216..ccf9b82022 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1659,6 +1659,7 @@ static void _register_variant_builtin_methods() { bind_string_methodv(replace, static_cast<String (String::*)(const String &, const String &) const>(&String::replace), sarray("what", "forwhat"), varray()); bind_string_method(replacen, sarray("what", "forwhat"), varray()); bind_string_method(repeat, sarray("count"), varray()); + bind_string_method(reverse, sarray(), varray()); bind_string_method(insert, sarray("position", "what"), varray()); bind_string_method(erase, sarray("position", "chars"), varray(1)); bind_string_method(capitalize, sarray(), varray()); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index c11f726402..fc1f7828a2 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -284,7 +284,7 @@ public: const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); if (b == 0) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -307,7 +307,7 @@ public: const Vector2i &b = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -331,7 +331,7 @@ public: const Vector3i &b = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0 || b.z == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -355,7 +355,7 @@ public: const Vector4i &b = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0 || b.z == 0 || b.w == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 545825011a..4f6bcb58b3 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "variant.h" +#include "variant_utility.h" #include "core/core_string_names.h" #include "core/io/marshalls.h" @@ -40,755 +40,772 @@ #include "core/variant/binder_common.h" #include "core/variant/variant_parser.h" -struct VariantUtilityFunctions { - // Math - - static inline double sin(double arg) { - return Math::sin(arg); - } +// Math +double VariantUtilityFunctions::sin(double arg) { + return Math::sin(arg); +} - static inline double cos(double arg) { - return Math::cos(arg); - } +double VariantUtilityFunctions::cos(double arg) { + return Math::cos(arg); +} - static inline double tan(double arg) { - return Math::tan(arg); - } +double VariantUtilityFunctions::tan(double arg) { + return Math::tan(arg); +} - static inline double sinh(double arg) { - return Math::sinh(arg); - } +double VariantUtilityFunctions::sinh(double arg) { + return Math::sinh(arg); +} - static inline double cosh(double arg) { - return Math::cosh(arg); - } +double VariantUtilityFunctions::cosh(double arg) { + return Math::cosh(arg); +} - static inline double tanh(double arg) { - return Math::tanh(arg); - } +double VariantUtilityFunctions::tanh(double arg) { + return Math::tanh(arg); +} - static inline double asin(double arg) { - return Math::asin(arg); - } +double VariantUtilityFunctions::asin(double arg) { + return Math::asin(arg); +} - static inline double acos(double arg) { - return Math::acos(arg); - } +double VariantUtilityFunctions::acos(double arg) { + return Math::acos(arg); +} - static inline double atan(double arg) { - return Math::atan(arg); - } +double VariantUtilityFunctions::atan(double arg) { + return Math::atan(arg); +} - static inline double atan2(double y, double x) { - return Math::atan2(y, x); - } +double VariantUtilityFunctions::atan2(double y, double x) { + return Math::atan2(y, x); +} - static inline double sqrt(double x) { - return Math::sqrt(x); - } +double VariantUtilityFunctions::sqrt(double x) { + return Math::sqrt(x); +} - static inline double fmod(double b, double r) { - return Math::fmod(b, r); - } +double VariantUtilityFunctions::fmod(double b, double r) { + return Math::fmod(b, r); +} - static inline double fposmod(double b, double r) { - return Math::fposmod(b, r); - } +double VariantUtilityFunctions::fposmod(double b, double r) { + return Math::fposmod(b, r); +} - static inline int64_t posmod(int64_t b, int64_t r) { - return Math::posmod(b, r); - } +int64_t VariantUtilityFunctions::posmod(int64_t b, int64_t r) { + return Math::posmod(b, r); +} - static inline Variant floor(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::floor(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).floor(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).floor(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).floor(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::floor(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).floor(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).floor(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).floor(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double floorf(double x) { - return Math::floor(x); - } +double VariantUtilityFunctions::floorf(double x) { + return Math::floor(x); +} - static inline int64_t floori(double x) { - return int64_t(Math::floor(x)); - } +int64_t VariantUtilityFunctions::floori(double x) { + return int64_t(Math::floor(x)); +} - static inline Variant ceil(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::ceil(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).ceil(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).ceil(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).ceil(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::ceil(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).ceil(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).ceil(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).ceil(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double ceilf(double x) { - return Math::ceil(x); - } +double VariantUtilityFunctions::ceilf(double x) { + return Math::ceil(x); +} - static inline int64_t ceili(double x) { - return int64_t(Math::ceil(x)); - } +int64_t VariantUtilityFunctions::ceili(double x) { + return int64_t(Math::ceil(x)); +} - static inline Variant round(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::round(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).round(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).round(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).round(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::round(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).round(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).round(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).round(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double roundf(double x) { - return Math::round(x); - } +double VariantUtilityFunctions::roundf(double x) { + return Math::round(x); +} - static inline int64_t roundi(double x) { - return int64_t(Math::round(x)); - } +int64_t VariantUtilityFunctions::roundi(double x) { + return int64_t(Math::round(x)); +} - static inline Variant abs(const Variant &x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return ABS(VariantInternalAccessor<int64_t>::get(&x)); - } break; - case Variant::FLOAT: { - return Math::absd(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).abs(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).abs(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).abs(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).abs(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).abs(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).abs(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::abs(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return ABS(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return Math::absd(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).abs(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).abs(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).abs(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).abs(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).abs(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).abs(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double absf(double x) { - return Math::absd(x); - } +double VariantUtilityFunctions::absf(double x) { + return Math::absd(x); +} - static inline int64_t absi(int64_t x) { - return ABS(x); - } +int64_t VariantUtilityFunctions::absi(int64_t x) { + return ABS(x); +} - static inline Variant sign(const Variant &x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return SIGN(VariantInternalAccessor<int64_t>::get(&x)); - } break; - case Variant::FLOAT: { - return SIGN(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).sign(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).sign(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).sign(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).sign(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).sign(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).sign(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::sign(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return SIGN(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return SIGN(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).sign(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).sign(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).sign(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).sign(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).sign(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).sign(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double signf(double x) { - return SIGN(x); - } +double VariantUtilityFunctions::signf(double x) { + return SIGN(x); +} - static inline int64_t signi(int64_t x) { - return SIGN(x); - } +int64_t VariantUtilityFunctions::signi(int64_t x) { + return SIGN(x); +} - static inline double pow(double x, double y) { - return Math::pow(x, y); - } +double VariantUtilityFunctions::pow(double x, double y) { + return Math::pow(x, y); +} - static inline double log(double x) { - return Math::log(x); - } +double VariantUtilityFunctions::log(double x) { + return Math::log(x); +} - static inline double exp(double x) { - return Math::exp(x); - } +double VariantUtilityFunctions::exp(double x) { + return Math::exp(x); +} - static inline bool is_nan(double x) { - return Math::is_nan(x); - } +bool VariantUtilityFunctions::is_nan(double x) { + return Math::is_nan(x); +} - static inline bool is_inf(double x) { - return Math::is_inf(x); - } +bool VariantUtilityFunctions::is_inf(double x) { + return Math::is_inf(x); +} - static inline bool is_equal_approx(double x, double y) { - return Math::is_equal_approx(x, y); - } +bool VariantUtilityFunctions::is_equal_approx(double x, double y) { + return Math::is_equal_approx(x, y); +} - static inline bool is_zero_approx(double x) { - return Math::is_zero_approx(x); - } +bool VariantUtilityFunctions::is_zero_approx(double x) { + return Math::is_zero_approx(x); +} - static inline bool is_finite(double x) { - return Math::is_finite(x); - } +bool VariantUtilityFunctions::is_finite(double x) { + return Math::is_finite(x); +} - static inline double ease(float x, float curve) { - return Math::ease(x, curve); - } +double VariantUtilityFunctions::ease(float x, float curve) { + return Math::ease(x, curve); +} - static inline int step_decimals(float step) { - return Math::step_decimals(step); - } +int VariantUtilityFunctions::step_decimals(float step) { + return Math::step_decimals(step); +} - static inline Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; +Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + return Variant(); + } + + switch (step.get_type()) { + case Variant::INT: { + return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); + } break; + case Variant::FLOAT: { + return snappedf(x, VariantInternalAccessor<double>::get(&step)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step)); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step)); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step)); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step)); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step)); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - - switch (step.get_type()) { - case Variant::INT: { - return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); - } break; - case Variant::FLOAT: { - return snappedf(x, VariantInternalAccessor<double>::get(&step)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step)); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step)); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step)); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step)); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step)); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - } } +} - static inline double snappedf(double x, double step) { - return Math::snapped(x, step); - } +double VariantUtilityFunctions::snappedf(double x, double step) { + return Math::snapped(x, step); +} - static inline int64_t snappedi(double x, int64_t step) { - return Math::snapped(x, step); - } +int64_t VariantUtilityFunctions::snappedi(double x, int64_t step) { + return Math::snapped(x, step); +} - static inline Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (from.get_type() != to.get_type()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = from.get_type(); - r_error.argument = 1; +Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + if (from.get_type() != to.get_type()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = from.get_type(); + r_error.argument = 1; + return Variant(); + } + + switch (from.get_type()) { + case Variant::INT: { + return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); + } break; + case Variant::FLOAT: { + return lerpf(VariantInternalAccessor<double>::get(&from), to, weight); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight); + } break; + case Variant::QUATERNION: { + return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight); + } break; + case Variant::BASIS: { + return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight); + } break; + case Variant::COLOR: { + return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - - switch (from.get_type()) { - case Variant::INT: { - return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); - } break; - case Variant::FLOAT: { - return lerpf(VariantInternalAccessor<double>::get(&from), to, weight); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight); - } break; - case Variant::QUATERNION: { - return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight); - } break; - case Variant::BASIS: { - return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight); - } break; - case Variant::COLOR: { - return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - } } +} - static inline double lerpf(double from, double to, double weight) { - return Math::lerp(from, to, weight); - } +double VariantUtilityFunctions::lerpf(double from, double to, double weight) { + return Math::lerp(from, to, weight); +} - static inline double cubic_interpolate(double from, double to, double pre, double post, double weight) { - return Math::cubic_interpolate(from, to, pre, post, weight); - } +double VariantUtilityFunctions::cubic_interpolate(double from, double to, double pre, double post, double weight) { + return Math::cubic_interpolate(from, to, pre, post, weight); +} - static inline double cubic_interpolate_angle(double from, double to, double pre, double post, double weight) { - return Math::cubic_interpolate_angle(from, to, pre, post, weight); - } +double VariantUtilityFunctions::cubic_interpolate_angle(double from, double to, double pre, double post, double weight) { + return Math::cubic_interpolate_angle(from, to, pre, post, weight); +} - static inline double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, - double to_t, double pre_t, double post_t) { - return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); - } +double VariantUtilityFunctions::cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t) { + return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); +} - static inline double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, - double to_t, double pre_t, double post_t) { - return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); - } +double VariantUtilityFunctions::cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t) { + return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); +} - 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); - } +double VariantUtilityFunctions::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 bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { - return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t); - } +double VariantUtilityFunctions::bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + return Math::bezier_derivative(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); - } +double VariantUtilityFunctions::lerp_angle(double from, double to, double weight) { + return Math::lerp_angle(from, to, weight); +} - static inline double inverse_lerp(double from, double to, double weight) { - return Math::inverse_lerp(from, to, weight); - } +double VariantUtilityFunctions::inverse_lerp(double from, double to, double weight) { + return Math::inverse_lerp(from, to, weight); +} - static inline double remap(double value, double istart, double istop, double ostart, double ostop) { - return Math::remap(value, istart, istop, ostart, ostop); - } +double VariantUtilityFunctions::remap(double value, double istart, double istop, double ostart, double ostop) { + return Math::remap(value, istart, istop, ostart, ostop); +} - static inline double smoothstep(double from, double to, double val) { - return Math::smoothstep(from, to, val); - } +double VariantUtilityFunctions::smoothstep(double from, double to, double val) { + return Math::smoothstep(from, to, val); +} - static inline double move_toward(double from, double to, double delta) { - return Math::move_toward(from, to, delta); - } +double VariantUtilityFunctions::move_toward(double from, double to, double delta) { + return Math::move_toward(from, to, delta); +} - static inline double deg_to_rad(double angle_deg) { - return Math::deg_to_rad(angle_deg); - } +double VariantUtilityFunctions::deg_to_rad(double angle_deg) { + return Math::deg_to_rad(angle_deg); +} - static inline double rad_to_deg(double angle_rad) { - return Math::rad_to_deg(angle_rad); - } +double VariantUtilityFunctions::rad_to_deg(double angle_rad) { + return Math::rad_to_deg(angle_rad); +} - static inline double linear_to_db(double linear) { - return Math::linear_to_db(linear); - } +double VariantUtilityFunctions::linear_to_db(double linear) { + return Math::linear_to_db(linear); +} + +double VariantUtilityFunctions::db_to_linear(double db) { + return Math::db_to_linear(db); +} - static inline double db_to_linear(double db) { - return Math::db_to_linear(db); +Variant VariantUtilityFunctions::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(); } - 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 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::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; - 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: { + 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); - } break; - default: - break; - } - - r_error.error = Callable::CallError::CALL_OK; - return value; + } 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; } - static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { - return Math::wrapi(value, min, max); - } + r_error.error = Callable::CallError::CALL_OK; + return value; +} - static inline double wrapf(double value, double min, double max) { - return Math::wrapf(value, min, max); - } +int64_t VariantUtilityFunctions::wrapi(int64_t value, int64_t min, int64_t max) { + return Math::wrapi(value, min, max); +} - static inline double pingpong(double value, double length) { - return Math::pingpong(value, length); +double VariantUtilityFunctions::wrapf(double value, double min, double max) { + return Math::wrapf(value, min, max); +} + +double VariantUtilityFunctions::pingpong(double value, double length) { + return Math::pingpong(value, length); +} + +Variant VariantUtilityFunctions::max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); } + Variant base = *p_args[0]; + Variant ret; - static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 2; + for (int i = 0; i < p_argcount; i++) { + Variant::Type arg_type = p_args[i]->get_type(); + if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = Variant::FLOAT; + r_error.argument = i; return Variant(); } - Variant base = *p_args[0]; - Variant ret; - - for (int i = 0; i < p_argcount; i++) { - Variant::Type arg_type = p_args[i]->get_type(); - if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = Variant::FLOAT; - r_error.argument = i; - return Variant(); - } - if (i == 0) { - continue; - } - bool valid; - Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); - if (!valid) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); - r_error.argument = i; - return Variant(); - } - if (ret.booleanize()) { - base = *p_args[i]; - } + if (i == 0) { + continue; } - r_error.error = Callable::CallError::CALL_OK; - return base; - } - - static inline double maxf(double x, double y) { - return MAX(x, y); - } - - static inline int64_t maxi(int64_t x, int64_t y) { - return MAX(x, y); - } - - static inline Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 2; + bool valid; + Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = base.get_type(); + r_error.argument = i; return Variant(); } - Variant base = *p_args[0]; - Variant ret; - - for (int i = 0; i < p_argcount; i++) { - Variant::Type arg_type = p_args[i]->get_type(); - if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = Variant::FLOAT; - r_error.argument = i; - return Variant(); - } - if (i == 0) { - continue; - } - bool valid; - Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); - if (!valid) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); - r_error.argument = i; - return Variant(); - } - if (ret.booleanize()) { - base = *p_args[i]; - } + if (ret.booleanize()) { + base = *p_args[i]; } - r_error.error = Callable::CallError::CALL_OK; - return base; - } - - static inline double minf(double x, double y) { - return MIN(x, y); } + r_error.error = Callable::CallError::CALL_OK; + return base; +} - static inline int64_t mini(int64_t x, int64_t y) { - return MIN(x, y); - } +double VariantUtilityFunctions::maxf(double x, double y) { + return MAX(x, y); +} - static inline Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { - Variant value = x; +int64_t VariantUtilityFunctions::maxi(int64_t x, int64_t y) { + return MAX(x, y); +} - Variant ret; +Variant VariantUtilityFunctions::min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); + } + Variant base = *p_args[0]; + Variant ret; - bool valid; - Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); - if (!valid) { + for (int i = 0; i < p_argcount; i++) { + Variant::Type arg_type = p_args[i]->get_type(); + if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = value.get_type(); - r_error.argument = 1; + r_error.expected = Variant::FLOAT; + r_error.argument = i; return Variant(); } - if (ret.booleanize()) { - value = min; + if (i == 0) { + continue; } - Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + bool valid; + Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); if (!valid) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = value.get_type(); - r_error.argument = 2; + r_error.expected = base.get_type(); + r_error.argument = i; return Variant(); } if (ret.booleanize()) { - value = max; + base = *p_args[i]; } + } + r_error.error = Callable::CallError::CALL_OK; + return base; +} - r_error.error = Callable::CallError::CALL_OK; +double VariantUtilityFunctions::minf(double x, double y) { + return MIN(x, y); +} - return value; - } +int64_t VariantUtilityFunctions::mini(int64_t x, int64_t y) { + return MIN(x, y); +} - static inline double clampf(double x, double min, double max) { - return CLAMP(x, min, max); - } +Variant VariantUtilityFunctions::clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { + Variant value = x; - static inline int64_t clampi(int64_t x, int64_t min, int64_t max) { - return CLAMP(x, min, max); - } + Variant ret; - static inline int64_t nearest_po2(int64_t x) { - return nearest_power_of_2_templated(uint64_t(x)); + bool valid; + Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 1; + return Variant(); + } + if (ret.booleanize()) { + value = min; + } + Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 2; + return Variant(); + } + if (ret.booleanize()) { + value = max; } - // Random + r_error.error = Callable::CallError::CALL_OK; - static inline void randomize() { - Math::randomize(); - } + return value; +} - static inline int64_t randi() { - return Math::rand(); - } +double VariantUtilityFunctions::clampf(double x, double min, double max) { + return CLAMP(x, min, max); +} - static inline double randf() { - return Math::randf(); - } +int64_t VariantUtilityFunctions::clampi(int64_t x, int64_t min, int64_t max) { + return CLAMP(x, min, max); +} - static inline double randfn(double mean, double deviation) { - return Math::randfn(mean, deviation); - } +int64_t VariantUtilityFunctions::nearest_po2(int64_t x) { + return nearest_power_of_2_templated(uint64_t(x)); +} - static inline int64_t randi_range(int64_t from, int64_t to) { - return Math::random((int32_t)from, (int32_t)to); - } +// Random - static inline double randf_range(double from, double to) { - return Math::random(from, to); - } +void VariantUtilityFunctions::randomize() { + Math::randomize(); +} - static inline void seed(int64_t s) { - return Math::seed(s); - } +int64_t VariantUtilityFunctions::randi() { + return Math::rand(); +} - static inline PackedInt64Array rand_from_seed(int64_t seed) { - uint64_t s = seed; - PackedInt64Array arr; - arr.resize(2); - arr.write[0] = Math::rand_from_seed(&s); - arr.write[1] = s; - return arr; - } +double VariantUtilityFunctions::randf() { + return Math::randf(); +} - // Utility +double VariantUtilityFunctions::randfn(double mean, double deviation) { + return Math::randfn(mean, deviation); +} - static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) { - if (obj.get_type() == Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_OK; - if (obj.is_ref_counted()) { - Ref<WeakRef> wref = memnew(WeakRef); - Ref<RefCounted> r = obj; - if (r.is_valid()) { - wref->set_ref(r); - } - return wref; - } else { - Ref<WeakRef> wref = memnew(WeakRef); - Object *o = obj.get_validated_object(); - if (o) { - wref->set_obj(o); - } - return wref; - } - } else if (obj.get_type() == Variant::NIL) { - r_error.error = Callable::CallError::CALL_OK; +int64_t VariantUtilityFunctions::randi_range(int64_t from, int64_t to) { + return Math::random((int32_t)from, (int32_t)to); +} + +double VariantUtilityFunctions::randf_range(double from, double to) { + return Math::random(from, to); +} + +void VariantUtilityFunctions::seed(int64_t s) { + return Math::seed(s); +} + +PackedInt64Array VariantUtilityFunctions::rand_from_seed(int64_t seed) { + uint64_t s = seed; + PackedInt64Array arr; + arr.resize(2); + arr.write[0] = Math::rand_from_seed(&s); + arr.write[1] = s; + return arr; +} + +// Utility + +Variant VariantUtilityFunctions::weakref(const Variant &obj, Callable::CallError &r_error) { + if (obj.get_type() == Variant::OBJECT) { + r_error.error = Callable::CallError::CALL_OK; + if (obj.is_ref_counted()) { Ref<WeakRef> wref = memnew(WeakRef); + Ref<RefCounted> r = obj; + if (r.is_valid()) { + wref->set_ref(r); + } return wref; } else { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - return Variant(); + Ref<WeakRef> wref = memnew(WeakRef); + Object *o = obj.get_validated_object(); + if (o) { + wref->set_obj(o); + } + return wref; } + } else if (obj.get_type() == Variant::NIL) { + r_error.error = Callable::CallError::CALL_OK; + Ref<WeakRef> wref = memnew(WeakRef); + return wref; + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return Variant(); } +} - static inline int64_t _typeof(const Variant &obj) { - return obj.get_type(); +int64_t VariantUtilityFunctions::_typeof(const Variant &obj) { + return obj.get_type(); +} + +String VariantUtilityFunctions::str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + return String(); } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - return String(); + if (i == 0) { + s = os; + } else { + s += os; } - 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; - } - } + r_error.error = Callable::CallError::CALL_OK; - r_error.error = Callable::CallError::CALL_OK; + return s; +} - return s; +String VariantUtilityFunctions::error_string(Error error) { + if (error < 0 || error >= ERR_MAX) { + return String("(invalid error code)"); } - static inline String error_string(Error error) { - if (error < 0 || error >= ERR_MAX) { - return String("(invalid error code)"); - } + return String(error_names[error]); +} - return String(error_names[error]); +void VariantUtilityFunctions::print(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; + } } - static inline void print(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(); + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } - } +void VariantUtilityFunctions::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(); - print_line(s); - r_error.error = Callable::CallError::CALL_OK; + if (i == 0) { + s = os; + } else { + s += os; + } } - static inline void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + print_line_rich(s); + r_error.error = Callable::CallError::CALL_OK; +} + +#undef print_verbose + +void VariantUtilityFunctions::print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (OS::get_singleton()->is_stdout_verbose()) { String s; for (int i = 0; i < p_arg_count; i++) { String os = p_args[i]->operator String(); @@ -800,247 +817,227 @@ struct VariantUtilityFunctions { } } - print_line_rich(s); - r_error.error = Callable::CallError::CALL_OK; + // No need to use `print_verbose()` as this call already only happens + // when verbose mode is enabled. This avoids performing string argument concatenation + // when not needed. + print_line(s); } -#undef print_verbose + 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; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); - - if (i == 0) { - s = os; - } else { - s += os; - } - } +void VariantUtilityFunctions::printerr(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(); - // No need to use `print_verbose()` as this call already only happens - // when verbose mode is enabled. This avoids performing string argument concatenation - // when not needed. - print_line(s); + if (i == 0) { + s = os; + } else { + s += os; } - - r_error.error = Callable::CallError::CALL_OK; } - static inline void printerr(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(); + print_error(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } +void VariantUtilityFunctions::printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + s += "\t"; } - - print_error(s); - r_error.error = Callable::CallError::CALL_OK; + s += p_args[i]->operator String(); } - static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - s += "\t"; - } - s += p_args[i]->operator String(); - } - - print_line(s); - r_error.error = Callable::CallError::CALL_OK; - } + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - s += " "; - } - s += p_args[i]->operator String(); +void VariantUtilityFunctions::prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + s += " "; } - - print_line(s); - r_error.error = Callable::CallError::CALL_OK; + s += p_args[i]->operator String(); } - static inline void printraw(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(); + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } +void VariantUtilityFunctions::printraw(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; } + } - OS::get_singleton()->print("%s", s.utf8().get_data()); - r_error.error = Callable::CallError::CALL_OK; + OS::get_singleton()->print("%s", s.utf8().get_data()); + r_error.error = Callable::CallError::CALL_OK; +} + +void VariantUtilityFunctions::push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + if (i == 0) { + s = os; + } else { + s += os; } - 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; - } - } + ERR_PRINT(s); + r_error.error = Callable::CallError::CALL_OK; +} - ERR_PRINT(s); - r_error.error = Callable::CallError::CALL_OK; +void VariantUtilityFunctions::push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + if (i == 0) { + s = os; + } else { + s += os; } - 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; - } - } + WARN_PRINT(s); + r_error.error = Callable::CallError::CALL_OK; +} - WARN_PRINT(s); - r_error.error = Callable::CallError::CALL_OK; - } +String VariantUtilityFunctions::var_to_str(const Variant &p_var) { + String vars; + VariantWriter::write_to_string(p_var, vars); + return vars; +} - static inline String var_to_str(const Variant &p_var) { - String vars; - VariantWriter::write_to_string(p_var, vars); - return vars; - } +Variant VariantUtilityFunctions::str_to_var(const String &p_var) { + VariantParser::StreamString ss; + ss.s = p_var; - static inline Variant str_to_var(const String &p_var) { - VariantParser::StreamString ss; - ss.s = p_var; + String errs; + int line; + Variant ret; + (void)VariantParser::parse(&ss, ret, errs, line); - String errs; - int line; - Variant ret; - (void)VariantParser::parse(&ss, ret, errs, line); + return ret; +} - return ret; +PackedByteArray VariantUtilityFunctions::var_to_bytes(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, false); + if (err != OK) { + return PackedByteArray(); } - static inline PackedByteArray var_to_bytes(const Variant &p_var) { - int len; - Error err = encode_variant(p_var, nullptr, len, false); + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, false); if (err != OK) { return PackedByteArray(); } + } - PackedByteArray barr; - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - err = encode_variant(p_var, w, len, false); - if (err != OK) { - return PackedByteArray(); - } - } + return barr; +} - return barr; +PackedByteArray VariantUtilityFunctions::var_to_bytes_with_objects(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, true); + if (err != OK) { + return PackedByteArray(); } - static inline PackedByteArray var_to_bytes_with_objects(const Variant &p_var) { - int len; - Error err = encode_variant(p_var, nullptr, len, true); + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, true); if (err != OK) { return PackedByteArray(); } - - PackedByteArray barr; - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - err = encode_variant(p_var, w, len, true); - if (err != OK) { - return PackedByteArray(); - } - } - - return barr; } - static inline Variant bytes_to_var(const PackedByteArray &p_arr) { - Variant ret; - { - const uint8_t *r = p_arr.ptr(); - Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); - if (err != OK) { - return Variant(); - } + return barr; +} + +Variant VariantUtilityFunctions::bytes_to_var(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); + if (err != OK) { + return Variant(); } - return ret; } + return ret; +} - static inline Variant bytes_to_var_with_objects(const PackedByteArray &p_arr) { - Variant ret; - { - const uint8_t *r = p_arr.ptr(); - Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); - if (err != OK) { - return Variant(); - } +Variant VariantUtilityFunctions::bytes_to_var_with_objects(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); + if (err != OK) { + return Variant(); } - return ret; } + return ret; +} - static inline int64_t hash(const Variant &p_arr) { - return p_arr.hash(); - } +int64_t VariantUtilityFunctions::hash(const Variant &p_arr) { + return p_arr.hash(); +} - static inline Object *instance_from_id(int64_t p_id) { - ObjectID id = ObjectID((uint64_t)p_id); - Object *ret = ObjectDB::get_instance(id); - return ret; - } +Object *VariantUtilityFunctions::instance_from_id(int64_t p_id) { + ObjectID id = ObjectID((uint64_t)p_id); + Object *ret = ObjectDB::get_instance(id); + return ret; +} - static inline bool is_instance_id_valid(int64_t p_id) { - return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; - } +bool VariantUtilityFunctions::is_instance_id_valid(int64_t p_id) { + return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; +} - static inline bool is_instance_valid(const Variant &p_instance) { - if (p_instance.get_type() != Variant::OBJECT) { - return false; - } - return p_instance.get_validated_object() != nullptr; +bool VariantUtilityFunctions::is_instance_valid(const Variant &p_instance) { + if (p_instance.get_type() != Variant::OBJECT) { + return false; } + return p_instance.get_validated_object() != nullptr; +} - static inline uint64_t rid_allocate_id() { - return RID_AllocBase::_gen_id(); - } +uint64_t VariantUtilityFunctions::rid_allocate_id() { + return RID_AllocBase::_gen_id(); +} - static inline RID rid_from_int64(uint64_t p_base) { - return RID::from_uint64(p_base); - } +RID VariantUtilityFunctions::rid_from_int64(uint64_t p_base) { + return RID::from_uint64(p_base); +} - static inline bool is_same(const Variant &p_a, const Variant &p_b) { - return p_a.identity_compare(p_b); - } -}; +bool VariantUtilityFunctions::is_same(const Variant &p_a, const Variant &p_b) { + return p_a.identity_compare(p_b); +} #ifdef DEBUG_METHODS_ENABLED #define VCALLR *ret = p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...) diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h new file mode 100644 index 0000000000..78f66987cb --- /dev/null +++ b/core/variant/variant_utility.h @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* variant_utility.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef VARIANT_UTILITY_H +#define VARIANT_UTILITY_H + +#include "variant.h" + +struct VariantUtilityFunctions { + // Math + static double sin(double arg); + static double cos(double arg); + static double tan(double arg); + static double sinh(double arg); + static double cosh(double arg); + static double tanh(double arg); + static double asin(double arg); + static double acos(double arg); + static double atan(double arg); + static double atan2(double y, double x); + static double sqrt(double x); + static double fmod(double b, double r); + static double fposmod(double b, double r); + static int64_t posmod(int64_t b, int64_t r); + static Variant floor(Variant x, Callable::CallError &r_error); + static double floorf(double x); + static int64_t floori(double x); + static Variant ceil(Variant x, Callable::CallError &r_error); + static double ceilf(double x); + static int64_t ceili(double x); + static Variant round(Variant x, Callable::CallError &r_error); + static double roundf(double x); + static int64_t roundi(double x); + static Variant abs(const Variant &x, Callable::CallError &r_error); + static double absf(double x); + static int64_t absi(int64_t x); + static Variant sign(const Variant &x, Callable::CallError &r_error); + static double signf(double x); + static int64_t signi(int64_t x); + static double pow(double x, double y); + static double log(double x); + static double exp(double x); + static bool is_nan(double x); + static bool is_inf(double x); + static bool is_equal_approx(double x, double y); + static bool is_zero_approx(double x); + static bool is_finite(double x); + static double ease(float x, float curve); + static int step_decimals(float step); + static Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error); + static double snappedf(double x, double step); + static int64_t snappedi(double x, int64_t step); + static Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error); + static double lerpf(double from, double to, double weight); + static double cubic_interpolate(double from, double to, double pre, double post, double weight); + static double cubic_interpolate_angle(double from, double to, double pre, double post, double weight); + static double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t); + static double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t); + static double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t); + static double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t); + static double lerp_angle(double from, double to, double weight); + static double inverse_lerp(double from, double to, double weight); + static double remap(double value, double istart, double istop, double ostart, double ostop); + static double smoothstep(double from, double to, double val); + static double move_toward(double from, double to, double delta); + static double deg_to_rad(double angle_deg); + static double rad_to_deg(double angle_rad); + static double linear_to_db(double linear); + static double db_to_linear(double db); + static Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error); + static int64_t wrapi(int64_t value, int64_t min, int64_t max); + static double wrapf(double value, double min, double max); + static double pingpong(double value, double length); + static Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static double maxf(double x, double y); + static int64_t maxi(int64_t x, int64_t y); + static Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static double minf(double x, double y); + static int64_t mini(int64_t x, int64_t y); + static Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error); + static double clampf(double x, double min, double max); + static int64_t clampi(int64_t x, int64_t min, int64_t max); + static int64_t nearest_po2(int64_t x); + // Random + static void randomize(); + static int64_t randi(); + static double randf(); + static double randfn(double mean, double deviation); + static int64_t randi_range(int64_t from, int64_t to); + static double randf_range(double from, double to); + static void seed(int64_t s); + static PackedInt64Array rand_from_seed(int64_t seed); + // Utility + static Variant weakref(const Variant &obj, Callable::CallError &r_error); + static int64_t _typeof(const Variant &obj); + static Variant type_convert(const Variant &p_variant, const Variant::Type p_type); + static String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static String error_string(Error error); + static void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); +#undef print_verbose + static void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static String var_to_str(const Variant &p_var); + static Variant str_to_var(const String &p_var); + static PackedByteArray var_to_bytes(const Variant &p_var); + static PackedByteArray var_to_bytes_with_objects(const Variant &p_var); + static Variant bytes_to_var(const PackedByteArray &p_arr); + static Variant bytes_to_var_with_objects(const PackedByteArray &p_arr); + static int64_t hash(const Variant &p_arr); + static Object *instance_from_id(int64_t p_id); + static bool is_instance_id_valid(int64_t p_id); + static bool is_instance_valid(const Variant &p_instance); + static uint64_t rid_allocate_id(); + static RID rid_from_int64(uint64_t p_base); + static bool is_same(const Variant &p_a, const Variant &p_b); +}; + +#endif // VARIANT_UTILITY_H |