diff options
253 files changed, 4003 insertions, 583 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cb25337fad..8cfb00fd8e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,14 +39,14 @@ repos: stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy` - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.4 + rev: v0.6.6 hooks: - id: ruff args: [--fix] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.971 + rev: v1.11.2 hooks: - id: mypy files: \.py$ diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 5b6dcbb567..6d227d6615 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -63,6 +63,44 @@ Copyright: 2011, Ole Kniemeyer, MAXON, www.maxon.net 2007-2014, Juan Linietsky, Ariel Manzur License: Expat and Zlib +Files: ./modules/godot_physics_2d/godot_joints_2d.cpp +Comment: Chipmunk2D Joint Constraints +Copyright: 2007, Scott Lembcke +License: Expat + +Files: ./modules/godot_physics_3d/gjk_epa.cpp + ./modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp + ./modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h + ./modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp + ./modules/godot_physics_3d/joints/godot_hinge_joint_3d_sw.h + ./modules/godot_physics_3d/joints/godot_jacobian_entry_3d_sw.h + ./modules/godot_physics_3d/joints/godot_pin_joint_3d.cpp + ./modules/godot_physics_3d/joints/godot_pin_joint_3d.h + ./modules/godot_physics_3d/joints/godot_slider_joint_3d.cpp + ./modules/godot_physics_3d/joints/godot_slider_joint_3d.h + ./modules/godot_physics_3d/godot_soft_body_3d.cpp + ./modules/godot_physics_3d/godot_soft_body_3d.h + ./modules/godot_physics_3d/godot_shape_3d.cpp + ./modules/godot_physics_3d/godot_shape_3d.h +Comment: Bullet Continuous Collision Detection and Physics Library +Copyright: 2003-2008, Erwin Coumans + 2014-present, Godot Engine contributors + 2007-2014, Juan Linietsky, Ariel Manzur +License: Expat and Zlib + +Files: ./modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp +Comment: Open Dynamics Engine +Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh +License: BSD-3-clause + +Files: ./modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.cpp + ./modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.h +Comment: Bullet Continuous Collision Detection and Physics Library +Copyright: 2007, Starbreeze Studios + 2014-present, Godot Engine contributors + 2007-2014, Juan Linietsky, Ariel Manzur +License: Expat and Zlib + Files: ./modules/lightmapper_rd/lm_compute.glsl Comment: Joint Non-Local Means (JNLM) denoiser Copyright: 2020, Manuel Prandini @@ -93,44 +131,6 @@ Copyright: 2001, Robert Penner 2007-2014, Juan Linietsky, Ariel Manzur License: Expat -Files: ./servers/physics_2d/godot_joints_2d.cpp -Comment: Chipmunk2D Joint Constraints -Copyright: 2007, Scott Lembcke -License: Expat - -Files: ./servers/physics_3d/collision_solver_3d_sat.cpp -Comment: Open Dynamics Engine -Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh -License: BSD-3-clause - -Files: ./servers/physics_3d/gjk_epa.cpp - ./servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp - ./servers/physics_3d/joints/generic_6dof_joint_3d_sw.h - ./servers/physics_3d/joints/hinge_joint_3d_sw.cpp - ./servers/physics_3d/joints/hinge_joint_3d_sw.h - ./servers/physics_3d/joints/jacobian_entry_3d_sw.h - ./servers/physics_3d/joints/pin_joint_3d_sw.cpp - ./servers/physics_3d/joints/pin_joint_3d_sw.h - ./servers/physics_3d/joints/slider_joint_3d_sw.cpp - ./servers/physics_3d/joints/slider_joint_3d_sw.h - ./servers/physics_3d/soft_body_3d_sw.cpp - ./servers/physics_3d/soft_body_3d_sw.h - ./servers/physics_3d/shape_3d_sw.cpp - ./servers/physics_3d/shape_3d_sw.h -Comment: Bullet Continuous Collision Detection and Physics Library -Copyright: 2003-2008, Erwin Coumans - 2014-present, Godot Engine contributors - 2007-2014, Juan Linietsky, Ariel Manzur -License: Expat and Zlib - -Files: ./servers/physics_3d/joints/cone_twist_joint_3d_sw.cpp - ./servers/physics_3d/joints/cone_twist_joint_3d_sw.h -Comment: Bullet Continuous Collision Detection and Physics Library -Copyright: 2007, Starbreeze Studios - 2014-present, Godot Engine contributors - 2007-2014, Juan Linietsky, Ariel Manzur -License: Expat and Zlib - Files: ./servers/rendering/renderer_rd/shaders/ss_effects_downsample.glsl ./servers/rendering/renderer_rd/shaders/ssao_blur.glsl ./servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 9cdc21fe8e..d77c913314 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -267,6 +267,14 @@ bool Engine::is_extra_gpu_memory_tracking_enabled() const { return extra_gpu_memory_tracking; } +void Engine::set_print_to_stdout(bool p_enabled) { + CoreGlobals::print_line_enabled = p_enabled; +} + +bool Engine::is_printing_to_stdout() const { + return CoreGlobals::print_line_enabled; +} + 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 f858eba328..a0b1ffa981 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -128,6 +128,9 @@ public: void set_time_scale(double p_scale); double get_time_scale() const; + void set_print_to_stdout(bool p_enabled); + bool is_printing_to_stdout() const; + void set_print_error_messages(bool p_enabled); bool is_printing_error_messages() const; void print_header(const String &p_string) const; diff --git a/core/core_bind.cpp b/core/core_bind.cpp index bbfbb6e3cd..891e3a28c9 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -184,6 +184,10 @@ void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_form ::ResourceSaver::remove_resource_format_saver(p_format_saver); } +ResourceUID::ID ResourceSaver::get_resource_id_for_path(const String &p_path, bool p_generate) { + return ::ResourceSaver::get_resource_id_for_path(p_path, p_generate); +} + ResourceSaver *ResourceSaver::singleton = nullptr; void ResourceSaver::_bind_methods() { @@ -191,6 +195,7 @@ void ResourceSaver::_bind_methods() { ClassDB::bind_method(D_METHOD("get_recognized_extensions", "type"), &ResourceSaver::get_recognized_extensions); ClassDB::bind_method(D_METHOD("add_resource_format_saver", "format_saver", "at_front"), &ResourceSaver::add_resource_format_saver, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_resource_format_saver", "format_saver"), &ResourceSaver::remove_resource_format_saver); + ClassDB::bind_method(D_METHOD("get_resource_id_for_path", "path", "generate"), &ResourceSaver::get_resource_id_for_path, DEFVAL(false)); BIND_BITFIELD_FLAG(FLAG_NONE); BIND_BITFIELD_FLAG(FLAG_RELATIVE_PATHS); @@ -1848,6 +1853,14 @@ String Engine::get_write_movie_path() const { return ::Engine::get_singleton()->get_write_movie_path(); } +void Engine::set_print_to_stdout(bool p_enabled) { + ::Engine::get_singleton()->set_print_to_stdout(p_enabled); +} + +bool Engine::is_printing_to_stdout() const { + return ::Engine::get_singleton()->is_printing_to_stdout(); +} + void Engine::set_print_error_messages(bool p_enabled) { ::Engine::get_singleton()->set_print_error_messages(p_enabled); } @@ -1916,10 +1929,14 @@ void Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_write_movie_path"), &Engine::get_write_movie_path); + ClassDB::bind_method(D_METHOD("set_print_to_stdout", "enabled"), &Engine::set_print_to_stdout); + ClassDB::bind_method(D_METHOD("is_printing_to_stdout"), &Engine::is_printing_to_stdout); + ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages); ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_to_stdout"), "set_print_to_stdout", "is_printing_to_stdout"); ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_physics_steps_per_frame"), "set_max_physics_steps_per_frame", "get_max_physics_steps_per_frame"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_fps"), "set_max_fps", "get_max_fps"); diff --git a/core/core_bind.h b/core/core_bind.h index 2a31e64425..ce0bde3c05 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -116,6 +116,8 @@ public: void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front); void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver); + ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false); + ResourceSaver() { singleton = this; } }; @@ -567,6 +569,9 @@ public: // `set_write_movie_path()` is not exposed to the scripting API as changing it at run-time has no effect. String get_write_movie_path() const; + void set_print_to_stdout(bool p_enabled); + bool is_printing_to_stdout() const; + void set_print_error_messages(bool p_enabled); bool is_printing_error_messages() const; diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index a7c715c318..93179d9a11 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -47,12 +47,12 @@ static int _get_pad(int p_alignment, int p_n) { } void PCKPacker::_bind_methods() { - ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("pck_start", "pck_path", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false)); ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false)); } -Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &p_key, bool p_encrypt_directory) { +Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const String &p_key, bool p_encrypt_directory) { ERR_FAIL_COND_V_MSG((p_key.is_empty() || !p_key.is_valid_hex_number(false) || p_key.length() != 64), ERR_CANT_CREATE, "Invalid Encryption Key (must be 64 characters long)."); ERR_FAIL_COND_V_MSG(p_alignment <= 0, ERR_CANT_CREATE, "Invalid alignment, must be greater then 0."); @@ -83,8 +83,8 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & } enc_dir = p_encrypt_directory; - file = FileAccess::open(p_file, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, "Can't open file to write: " + String(p_file) + "."); + file = FileAccess::open(p_pck_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, "Can't open file to write: " + String(p_pck_path) + "."); alignment = p_alignment; @@ -106,7 +106,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & return OK; } -Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) { +Error PCKPacker::add_file(const String &p_pck_path, const String &p_src, bool p_encrypt) { ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use."); Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ); @@ -117,7 +117,7 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encr File pf; // Simplify path here and on every 'files' access so that paths that have extra '/' // symbols in them still match to the MD5 hash for the saved path. - pf.path = p_file.simplify_path(); + pf.path = p_pck_path.simplify_path(); pf.src_path = p_src; pf.ofs = ofs; pf.size = f->get_length(); diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h index 8764fc90a0..5aac833532 100644 --- a/core/io/pck_packer.h +++ b/core/io/pck_packer.h @@ -58,8 +58,8 @@ class PCKPacker : public RefCounted { Vector<File> files; public: - Error pck_start(const String &p_file, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false); - Error add_file(const String &p_file, const String &p_src, bool p_encrypt = false); + Error pck_start(const String &p_pck_path, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false); + Error add_file(const String &p_pck_path, const String &p_src, bool p_encrypt = false); Error flush(bool p_verbose = false); PCKPacker() {} diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 6177cba6a4..5f8a4b85a4 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -96,6 +96,7 @@ String Resource::get_path() const { void Resource::set_path_cache(const String &p_path) { path_cache = p_path; + GDVIRTUAL_CALL(_set_path_cache, p_path); } String Resource::generate_scene_unique_id() { @@ -188,6 +189,7 @@ void Resource::disconnect_changed(const Callable &p_callable) { } void Resource::reset_state() { + GDVIRTUAL_CALL(_reset_state); } Error Resource::copy_from(const Ref<Resource> &p_resource) { @@ -495,9 +497,9 @@ void Resource::set_as_translation_remapped(bool p_remapped) { } } -#ifdef TOOLS_ENABLED //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored void Resource::set_id_for_path(const String &p_path, const String &p_id) { +#ifdef TOOLS_ENABLED if (p_id.is_empty()) { ResourceCache::path_cache_lock.write_lock(); ResourceCache::resource_path_cache[p_path].erase(get_path()); @@ -507,9 +509,11 @@ void Resource::set_id_for_path(const String &p_path, const String &p_id) { ResourceCache::resource_path_cache[p_path][get_path()] = p_id; ResourceCache::path_cache_lock.write_unlock(); } +#endif } String Resource::get_id_for_path(const String &p_path) const { +#ifdef TOOLS_ENABLED ResourceCache::path_cache_lock.read_lock(); if (ResourceCache::resource_path_cache[p_path].has(get_path())) { String result = ResourceCache::resource_path_cache[p_path][get_path()]; @@ -519,13 +523,16 @@ String Resource::get_id_for_path(const String &p_path) const { ResourceCache::path_cache_lock.read_unlock(); return ""; } -} +#else + return ""; #endif +} void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path", "path"), &Resource::_set_path); ClassDB::bind_method(D_METHOD("take_over_path", "path"), &Resource::_take_over_path); ClassDB::bind_method(D_METHOD("get_path"), &Resource::get_path); + ClassDB::bind_method(D_METHOD("set_path_cache", "path"), &Resource::set_path_cache); ClassDB::bind_method(D_METHOD("set_name", "name"), &Resource::set_name); ClassDB::bind_method(D_METHOD("get_name"), &Resource::get_name); ClassDB::bind_method(D_METHOD("get_rid"), &Resource::get_rid); @@ -533,6 +540,12 @@ void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("is_local_to_scene"), &Resource::is_local_to_scene); ClassDB::bind_method(D_METHOD("get_local_scene"), &Resource::get_local_scene); ClassDB::bind_method(D_METHOD("setup_local_to_scene"), &Resource::setup_local_to_scene); + ClassDB::bind_method(D_METHOD("reset_state"), &Resource::reset_state); + + ClassDB::bind_method(D_METHOD("set_id_for_path", "path", "id"), &Resource::set_id_for_path); + ClassDB::bind_method(D_METHOD("get_id_for_path", "path"), &Resource::get_id_for_path); + + ClassDB::bind_method(D_METHOD("is_built_in"), &Resource::is_built_in); ClassDB::bind_static_method("Resource", D_METHOD("generate_scene_unique_id"), &Resource::generate_scene_unique_id); ClassDB::bind_method(D_METHOD("set_scene_unique_id", "id"), &Resource::set_scene_unique_id); @@ -552,6 +565,8 @@ void Resource::_bind_methods() { GDVIRTUAL_BIND(_setup_local_to_scene); GDVIRTUAL_BIND(_get_rid); + GDVIRTUAL_BIND(_reset_state); + GDVIRTUAL_BIND(_set_path_cache, "path"); } Resource::Resource() : diff --git a/core/io/resource.h b/core/io/resource.h index 2c1a431255..8966c0233c 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -89,6 +89,9 @@ protected: GDVIRTUAL0RC(RID, _get_rid); + GDVIRTUAL1C(_set_path_cache, String); + GDVIRTUAL0(_reset_state); + public: static Node *(*_get_local_scene_func)(); //used by editor static void (*_update_configuration_warning)(); //used by editor @@ -144,11 +147,9 @@ public: virtual RID get_rid() const; // some resources may offer conversion to RID -#ifdef TOOLS_ENABLED //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored void set_id_for_path(const String &p_path, const String &p_id); String get_id_for_path(const String &p_path) const; -#endif Resource(); ~Resource(); diff --git a/core/object/object.cpp b/core/object/object.cpp index 2d9d468d38..b3a4ec6e2e 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1457,6 +1457,24 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable return s->slot_map.has(*p_callable.get_base_comparator()); } +bool Object::has_connections(const StringName &p_signal) const { + const SignalData *s = signal_map.getptr(p_signal); + if (!s) { + bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal); + if (signal_is_valid) { + return false; + } + + if (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)) { + return false; + } + + ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + "."); + } + + return !s->slot_map.is_empty(); +} + void Object::disconnect(const StringName &p_signal, const Callable &p_callable) { _disconnect(p_signal, p_callable); } @@ -1697,6 +1715,7 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "flags"), &Object::connect, DEFVAL(0)); ClassDB::bind_method(D_METHOD("disconnect", "signal", "callable"), &Object::disconnect); ClassDB::bind_method(D_METHOD("is_connected", "signal", "callable"), &Object::is_connected); + ClassDB::bind_method(D_METHOD("has_connections", "signal"), &Object::has_connections); ClassDB::bind_method(D_METHOD("set_block_signals", "enable"), &Object::set_block_signals); ClassDB::bind_method(D_METHOD("is_blocking_signals"), &Object::is_blocking_signals); diff --git a/core/object/object.h b/core/object/object.h index 1274247d71..763e2974b9 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -932,6 +932,7 @@ public: MTVIRTUAL Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0); MTVIRTUAL void disconnect(const StringName &p_signal, const Callable &p_callable); MTVIRTUAL bool is_connected(const StringName &p_signal, const Callable &p_callable) const; + MTVIRTUAL bool has_connections(const StringName &p_signal) const; template <typename... VarArgs> void call_deferred(const StringName &p_name, VarArgs... p_args) { diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index d2fc7392c8..c5856a8a81 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -191,7 +191,17 @@ void Script::reload_from_file() { set_source_code(rel->get_source_code()); set_last_modified_time(rel->get_last_modified_time()); - reload(); + // Only reload the script when there are no compilation errors to prevent printing the error messages twice. + if (rel->is_valid()) { + if (Engine::get_singleton()->is_editor_hint() && is_tool()) { + get_language()->reload_tool_script(this, true); + } else { + // It's important to set p_keep_state to true in order to manage reloading scripts + // that are currently instantiated. + reload(true); + } + } + #else Resource::reload_from_file(); #endif diff --git a/core/object/script_language.h b/core/object/script_language.h index d0023d70e8..3ddfbb3e7d 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -112,7 +112,10 @@ class Script : public Resource { OBJ_SAVE_TYPE(Script); protected: - virtual bool editor_can_reload_from_file() override { return false; } // this is handled by editor better + // Scripts are reloaded via the Script Editor when edited in Godot, + // the LSP server when edited in a connected external editor, or + // through EditorFileSystem::_update_script_documentation when updated directly on disk. + virtual bool editor_can_reload_from_file() override { return false; } void _notification(int p_what); static void _bind_methods(); diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 2683addd4b..391a203d5b 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -4626,7 +4626,7 @@ bool String::is_absolute_path() const { String String::validate_ascii_identifier() const { if (is_empty()) { - return "_"; // Empty string is not a valid identifier; + return "_"; // Empty string is not a valid identifier. } String result; @@ -4647,6 +4647,29 @@ String String::validate_ascii_identifier() const { return result; } +String String::validate_unicode_identifier() const { + if (is_empty()) { + return "_"; // Empty string is not a valid identifier. + } + + String result; + if (is_unicode_identifier_start(operator[](0))) { + result = *this; + } else { + result = "_" + *this; + } + + int len = result.length(); + char32_t *buffer = result.ptrw(); + for (int i = 0; i < len; i++) { + if (!is_unicode_identifier_continue(buffer[i])) { + buffer[i] = '_'; + } + } + + return result; +} + bool String::is_valid_ascii_identifier() const { int len = length(); diff --git a/core/string/ustring.h b/core/string/ustring.h index 11f15031f9..5d4b209c25 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -460,6 +460,7 @@ public: static String get_invalid_node_name_characters(bool p_allow_internal = false); String validate_node_name() const; String validate_ascii_identifier() const; + String validate_unicode_identifier() const; String validate_filename() const; bool is_valid_ascii_identifier() const; diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index 9dff5c1e91..bb2d0313f6 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -545,6 +545,13 @@ bool Signal::is_connected(const Callable &p_callable) const { return obj->is_connected(name, p_callable); } +bool Signal::has_connections() const { + Object *obj = get_object(); + ERR_FAIL_NULL_V(obj, false); + + return obj->has_connections(name); +} + Array Signal::get_connections() const { Object *obj = get_object(); if (!obj) { diff --git a/core/variant/callable.h b/core/variant/callable.h index 63757d9d6e..e3c940a0e5 100644 --- a/core/variant/callable.h +++ b/core/variant/callable.h @@ -192,6 +192,7 @@ public: Error connect(const Callable &p_callable, uint32_t p_flags = 0); void disconnect(const Callable &p_callable); bool is_connected(const Callable &p_callable) const; + bool has_connections() const; Array get_connections() const; Signal(const Object *p_object, const StringName &p_name); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 178c0bc4ec..63fb5e8d94 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -2121,6 +2121,7 @@ static void _register_variant_builtin_methods_misc() { bind_method(Signal, disconnect, sarray("callable"), varray()); bind_method(Signal, is_connected, sarray("callable"), varray()); bind_method(Signal, get_connections, sarray(), varray()); + bind_method(Signal, has_connections, sarray(), varray()); bind_custom(Signal, emit, _VariantCall::func_Signal_emit, false, Variant); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index ac39a4135f..0bd8b830e0 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -923,7 +923,10 @@ public: static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { bool valid = true; String result = do_mod(*VariantGetInternalPtr<S>::get_ptr(left), &valid); - ERR_FAIL_COND_MSG(!valid, result); + if (unlikely(!valid)) { + *VariantGetInternalPtr<String>::get_ptr(r_ret) = *VariantGetInternalPtr<S>::get_ptr(left); + ERR_FAIL_MSG(vformat("String formatting error: %s.", result)); + } *VariantGetInternalPtr<String>::get_ptr(r_ret) = result; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -948,7 +951,10 @@ public: static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { bool valid = true; String result = do_mod(*VariantGetInternalPtr<S>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right), &valid); - ERR_FAIL_COND_MSG(!valid, result); + if (unlikely(!valid)) { + *VariantGetInternalPtr<String>::get_ptr(r_ret) = *VariantGetInternalPtr<S>::get_ptr(left); + ERR_FAIL_MSG(vformat("String formatting error: %s.", result)); + } *VariantGetInternalPtr<String>::get_ptr(r_ret) = result; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -976,7 +982,10 @@ public: static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { bool valid = true; String result = do_mod(*VariantGetInternalPtr<S>::get_ptr(left), right->get_validated_object(), &valid); - ERR_FAIL_COND_MSG(!valid, result); + if (unlikely(!valid)) { + *VariantGetInternalPtr<String>::get_ptr(r_ret) = *VariantGetInternalPtr<S>::get_ptr(left); + ERR_FAIL_MSG(vformat("String formatting error: %s.", result)); + } *VariantGetInternalPtr<String>::get_ptr(r_ret) = result; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -1003,7 +1012,10 @@ public: static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { bool valid = true; String result = do_mod(*VariantGetInternalPtr<S>::get_ptr(left), *VariantGetInternalPtr<T>::get_ptr(right), &valid); - ERR_FAIL_COND_MSG(!valid, result); + if (unlikely(!valid)) { + *VariantGetInternalPtr<String>::get_ptr(r_ret) = *VariantGetInternalPtr<S>::get_ptr(left); + ERR_FAIL_MSG(vformat("String formatting error: %s.", result)); + } *VariantGetInternalPtr<String>::get_ptr(r_ret) = result; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -1492,7 +1504,10 @@ public: } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { Object *l = right->get_validated_object(); - ERR_FAIL_NULL(l); + if (unlikely(!l)) { + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = false; + ERR_FAIL_MSG("Invalid base object for 'in'."); + } const String &a = *VariantGetInternalPtr<String>::get_ptr(left); bool valid; @@ -1526,7 +1541,10 @@ public: } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { Object *l = right->get_validated_object(); - ERR_FAIL_NULL(l); + if (unlikely(!l)) { + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = false; + ERR_FAIL_MSG("Invalid base object for 'in'."); + } const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(left); bool valid; diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index f222cbc969..a86f41cd9c 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -856,7 +856,7 @@ GD.Print("a", "b", a); // Prints ab[1, 2, 3] [/csharp] [/codeblocks] - [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print] or [method print_rich]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. + [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print] or [method print_rich]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. See also [member Engine.print_to_stdout] and [member ProjectSettings.application/run/disable_stdout]. </description> </method> <method name="print_rich" qualifiers="vararg"> diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index 764f4a65b5..18dccfa5a9 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -57,7 +57,7 @@ </member> <member name="button_pressed" type="bool" setter="set_pressed" getter="is_pressed" default="false"> If [code]true[/code], the button's state is pressed. Means the button is pressed down or toggled (if [member toggle_mode] is active). Only works if [member toggle_mode] is [code]true[/code]. - [b]Note:[/b] Setting [member button_pressed] will result in [signal toggled] to be emitted. If you want to change the pressed state without emitting that signal, use [method set_pressed_no_signal]. + [b]Note:[/b] Changing the value of [member button_pressed] will result in [signal toggled] to be emitted. If you want to change the pressed state without emitting that signal, use [method set_pressed_no_signal]. </member> <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" default="false" keywords="enabled"> If [code]true[/code], the button is in disabled state and can't be clicked or toggled. diff --git a/doc/classes/CameraFeed.xml b/doc/classes/CameraFeed.xml index 974f6d4a33..8033c0880b 100644 --- a/doc/classes/CameraFeed.xml +++ b/doc/classes/CameraFeed.xml @@ -34,6 +34,17 @@ Returns the position of camera on the device. </description> </method> + <method name="set_format"> + <return type="bool" /> + <param index="0" name="index" type="int" /> + <param index="1" name="parameters" type="Dictionary" /> + <description> + Sets the feed format parameters for the given index in the [member formats] array. Returns [code]true[/code] on success. By default YUYV encoded stream is transformed to FEED_RGB. YUYV encoded stream output format can be changed with [param parameters].output value: + [code]separate[/code] will result in FEED_YCBCR_SEP + [code]grayscale[/code] will result in desaturated FEED_RGB + [code]copy[/code] will result in FEED_YCBCR + </description> + </method> </methods> <members> <member name="feed_is_active" type="bool" setter="set_active" getter="is_active" default="false"> @@ -42,7 +53,22 @@ <member name="feed_transform" type="Transform2D" setter="set_transform" getter="get_transform" default="Transform2D(1, 0, 0, -1, 0, 1)"> The transform applied to the camera's image. </member> + <member name="formats" type="Array" setter="" getter="get_formats" default="[]"> + Formats supported by the feed. Each entry is a [Dictionary] describing format parameters. + </member> </members> + <signals> + <signal name="format_changed"> + <description> + Emitted when the format has changed. + </description> + </signal> + <signal name="frame_changed"> + <description> + Emitted when a new frame is available. + </description> + </signal> + </signals> <constants> <constant name="FEED_NOIMAGE" value="0" enum="FeedDataType"> No image set for the feed. diff --git a/doc/classes/CameraServer.xml b/doc/classes/CameraServer.xml index 020b5d887b..b09010147e 100644 --- a/doc/classes/CameraServer.xml +++ b/doc/classes/CameraServer.xml @@ -6,7 +6,7 @@ <description> The [CameraServer] keeps track of different cameras accessible in Godot. These are external cameras such as webcams or the cameras on your phone. It is notably used to provide AR modules with a video feed from the camera. - [b]Note:[/b] This class is currently only implemented on macOS and iOS. To get a [CameraFeed] on iOS, the camera plugin from [url=https://github.com/godotengine/godot-ios-plugins]godot-ios-plugins[/url] is required. On other platforms, no [CameraFeed]s will be available. + [b]Note:[/b] This class is currently only implemented on Linux, macOS, and iOS, on other platforms no [CameraFeed]s will be available. To get a [CameraFeed] on iOS, the camera plugin from [url=https://github.com/godotengine/godot-ios-plugins]godot-ios-plugins[/url] is required. </description> <tutorials> </tutorials> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 0a0223c550..bc17c8b008 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -528,6 +528,7 @@ <description> Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its ancestors are also visible. If any ancestor is hidden, this node will not be visible in the scene tree, and is therefore not drawn (see [method _draw]). Visibility is checked only in parent nodes that inherit from [CanvasItem], [CanvasLayer], and [Window]. If the parent is of any other type (such as [Node], [AnimationPlayer], or [Node3D]), it is assumed to be visible. + [b]Note:[/b] This method does not take [member visibility_layer] into account, so even if this method returns [code]true[/code] the node might end up not being rendered. </description> </method> <method name="make_canvas_position_local" qualifiers="const"> @@ -622,7 +623,7 @@ The rendering layer in which this [CanvasItem] is rendered by [Viewport] nodes. A [Viewport] will render a [CanvasItem] if it and all its parents share a layer with the [Viewport]'s canvas cull mask. </member> <member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true"> - If [code]true[/code], this [CanvasItem] is drawn. The node is only visible if all of its ancestors are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]). + If [code]true[/code], this [CanvasItem] may be drawn. Whether this [CanvasItem] is actually drawn depends on the visibility of all of its [CanvasItem] ancestors. In other words: this [CanvasItem] will be drawn when [method is_visible_in_tree] returns [code]true[/code] and all [CanvasItem] ancestors share at least one [member visibility_layer] with this [CanvasItem]. [b]Note:[/b] For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead. </member> <member name="y_sort_enabled" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false"> @@ -647,17 +648,17 @@ </signal> <signal name="hidden"> <description> - Emitted when becoming hidden. + Emitted when the [CanvasItem] is hidden, i.e. it's no longer visible in the tree (see [method is_visible_in_tree]). </description> </signal> <signal name="item_rect_changed"> <description> - Emitted when the item's [Rect2] boundaries (position or size) have changed, or when an action is taking place that may have impacted these boundaries (e.g. changing [member Sprite2D.texture]). + Emitted when the [CanvasItem]'s boundaries (position or size) change, or when an action took place that may have affected these boundaries (e.g. changing [member Sprite2D.texture]). </description> </signal> <signal name="visibility_changed"> <description> - Emitted when the visibility (hidden/visible) changes. + Emitted when the [CanvasItem]'s visibility changes, either because its own [member visible] property changed or because its visibility in the tree changed (see [method is_visible_in_tree]). </description> </signal> </signals> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index a151d8a41e..5eb8ac6199 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -904,7 +904,7 @@ </member> <member name="interface/inspector/delimitate_all_container_and_resources" type="bool" setter="" getter=""> If [code]true[/code], add a margin around Array, Dictionary, and Resource Editors that are not already colored. - [b]Note:[/b] If [member interface/inspector/nested_color_mode] is set to [b]Containers & Resources[/b] this parameter will have no effect since those editors will already be colored + [b]Note:[/b] If [member interface/inspector/nested_color_mode] is set to [b]Containers & Resources[/b] this parameter will have no effect since those editors will already be colored. </member> <member name="interface/inspector/disable_folding" type="bool" setter="" getter=""> If [code]true[/code], forces all property groups to be expanded in the Inspector dock and prevents collapsing them. @@ -1200,6 +1200,9 @@ <member name="text_editor/behavior/files/trim_trailing_whitespace_on_save" type="bool" setter="" getter=""> If [code]true[/code], trims trailing whitespace when saving a script. Trailing whitespace refers to tab and space characters placed at the end of lines. Since these serve no practical purpose, they can and should be removed to make version control diffs less noisy. </member> + <member name="text_editor/behavior/general/empty_selection_clipboard" type="bool" setter="" getter=""> + If [code]true[/code], copying or cutting without a selection is performed on all lines with a caret. Otherwise, copy and cut require a selection. + </member> <member name="text_editor/behavior/indent/auto_indent" type="bool" setter="" getter=""> If [code]true[/code], automatically indents code when pressing the [kbd]Enter[/kbd] key based on blocks above the new line. </member> diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index ca78054875..bba5157053 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -337,6 +337,10 @@ [b]Note:[/b] This property does not impact the editor's Errors tab when running a project from the editor. [b]Warning:[/b] If set to [code]false[/code] anywhere in the project, important error messages may be hidden even if they are emitted from other scripts. In a [code]@tool[/code] script, this will also impact the editor itself. Do [i]not[/i] report bugs before ensuring error messages are enabled (as they are by default). </member> + <member name="print_to_stdout" type="bool" setter="set_print_to_stdout" getter="is_printing_to_stdout" default="true"> + If [code]false[/code], stops printing messages (for example using [method @GlobalScope.print]) to the console, log files, and editor Output log. This property is equivalent to the [member ProjectSettings.application/run/disable_stdout] project setting. + [b]Note:[/b] This does not stop printing errors or warnings produced by scripts to the console or log files, for more details see [member print_error_messages]. + </member> <member name="time_scale" type="float" setter="set_time_scale" getter="get_time_scale" default="1.0"> The speed multiplier at which the in-game clock updates, compared to real time. For example, if set to [code]2.0[/code] the game runs twice as fast, and if set to [code]0.5[/code] the game runs half as fast. This value affects [Timer], [SceneTreeTimer], and all other simulations that make use of [code]delta[/code] time (such as [method Node._process] and [method Node._physics_process]). diff --git a/doc/classes/EngineDebugger.xml b/doc/classes/EngineDebugger.xml index 7583520da0..bcc1ac5299 100644 --- a/doc/classes/EngineDebugger.xml +++ b/doc/classes/EngineDebugger.xml @@ -87,7 +87,7 @@ <method name="line_poll"> <return type="void" /> <description> - Forces a processing loop of debugger events. The purpose of this method is just processing events every now and then when the script might get too busy, so that bugs like infinite loops can be caught + Forces a processing loop of debugger events. The purpose of this method is just processing events every now and then when the script might get too busy, so that bugs like infinite loops can be caught. </description> </method> <method name="profiler_add_frame_data"> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 8c26509812..de3295fbe0 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -414,7 +414,7 @@ Linear tonemapper operator. Reads the linear data and passes it on unmodified. This can cause bright lighting to look blown out, with noticeable clipping in the output colors. </constant> <constant name="TONE_MAPPER_REINHARDT" value="1" enum="ToneMapper"> - Reinhardt tonemapper operator. Performs a variation on rendered pixels' colors by this formula: [code]color = color / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull. + Reinhard tonemapper operator. Performs a variation on rendered pixels' colors by this formula: [code]color = color * (1 + color / (white * white)) / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull. When [member tonemap_white] is left at the default value of [code]1.0[/code] this is identical to [constant TONE_MAPPER_LINEAR] while also being slightly less performant. </constant> <constant name="TONE_MAPPER_FILMIC" value="2" enum="ToneMapper"> Filmic tonemapper operator. This avoids clipping bright highlights, with a resulting image that usually looks more vivid than [constant TONE_MAPPER_REINHARDT]. diff --git a/doc/classes/ExternalTexture.xml b/doc/classes/ExternalTexture.xml new file mode 100644 index 0000000000..6f27b62f24 --- /dev/null +++ b/doc/classes/ExternalTexture.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ExternalTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Texture which displays the content of an external buffer. + </brief_description> + <description> + Displays the content of an external buffer provided by the platform. + Requires the [url=https://registry.khronos.org/OpenGL/extensions/OES/OES_EGL_image_external.txt]OES_EGL_image_external[/url] extension (OpenGL) or [url=https://registry.khronos.org/vulkan/specs/1.1-extensions/html/vkspec.html#VK_ANDROID_external_memory_android_hardware_buffer]VK_ANDROID_external_memory_android_hardware_buffer[/url] extension (Vulkan). + [b]Note:[/b] This is currently only supported in Android builds. + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_external_texture_id" qualifiers="const"> + <return type="int" /> + <description> + Returns the external texture ID. + Depending on your use case, you may need to pass this to platform APIs, for example, when creating an [code]android.graphics.SurfaceTexture[/code] on Android. + </description> + </method> + <method name="set_external_buffer_id"> + <return type="void" /> + <param index="0" name="external_buffer_id" type="int" /> + <description> + Sets the external buffer ID. + Depending on your use case, you may need to call this with data received from a platform API, for example, [code]SurfaceTexture.getHardwareBuffer()[/code] on Android. + </description> + </method> + </methods> + <members> + <member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" /> + <member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2(256, 256)"> + External texture size. + </member> + </members> +</class> diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml index 1b8fa00772..c230bf5ad2 100644 --- a/doc/classes/FontFile.xml +++ b/doc/classes/FontFile.xml @@ -58,7 +58,7 @@ <return type="void" /> <param index="0" name="cache_index" type="int" /> <description> - Removes all font sizes from the cache entry + Removes all font sizes from the cache entry. </description> </method> <method name="clear_textures"> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index d218f720a3..9c460e6d62 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -151,12 +151,24 @@ Returns [code]true[/code] if the user has text in the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] (IME). </description> </method> + <method name="has_redo" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if a "redo" action is available. + </description> + </method> <method name="has_selection" qualifiers="const"> <return type="bool" /> <description> Returns [code]true[/code] if the user has selected text. </description> </method> + <method name="has_undo" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if an "undo" action is available. + </description> + </method> <method name="insert_text_at_caret"> <return type="void" /> <param index="0" name="text" type="String" /> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index d0d0876108..2767a11e80 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -824,6 +824,14 @@ Returns the name of the translation domain used by [method tr] and [method tr_n]. See also [TranslationServer]. </description> </method> + <method name="has_connections" qualifiers="const"> + <return type="bool" /> + <param index="0" name="signal" type="StringName" /> + <description> + Returns [code]true[/code] if any connection exists on the given [param signal] name. + [b]Note:[/b] In C#, [param signal] must be in snake_case when referring to built-in Godot methods. Prefer using the names exposed in the [code]SignalName[/code] class to avoid allocating a new [StringName] on each call. + </description> + </method> <method name="has_meta" qualifiers="const"> <return type="bool" /> <param index="0" name="name" type="StringName" /> diff --git a/doc/classes/OptimizedTranslation.xml b/doc/classes/OptimizedTranslation.xml index 124f430f1b..bc158984d7 100644 --- a/doc/classes/OptimizedTranslation.xml +++ b/doc/classes/OptimizedTranslation.xml @@ -14,6 +14,7 @@ <param index="0" name="from" type="Translation" /> <description> Generates and sets an optimized translation from the given [Translation] resource. + [b]Note:[/b] This method is intended to be used in the editor. It does nothing when called from an exported project. </description> </method> </methods> diff --git a/doc/classes/PCKPacker.xml b/doc/classes/PCKPacker.xml index 494e9966ac..ec0300c068 100644 --- a/doc/classes/PCKPacker.xml +++ b/doc/classes/PCKPacker.xml @@ -42,12 +42,12 @@ </method> <method name="pck_start"> <return type="int" enum="Error" /> - <param index="0" name="pck_name" type="String" /> + <param index="0" name="pck_path" type="String" /> <param index="1" name="alignment" type="int" default="32" /> <param index="2" name="key" type="String" default=""0000000000000000000000000000000000000000000000000000000000000000"" /> <param index="3" name="encrypt_directory" type="bool" default="false" /> <description> - Creates a new PCK file with the name [param pck_name]. The [code].pck[/code] file extension isn't added automatically, so it should be part of [param pck_name] (even though it's not required). + Creates a new PCK file at the file path [param pck_path]. The [code].pck[/code] file extension isn't added automatically, so it should be part of [param pck_path] (even though it's not required). </description> </method> </methods> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 758e98ad85..7b6d8d0cd3 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -314,11 +314,11 @@ </member> <member name="application/run/disable_stderr" type="bool" setter="" getter="" default="false"> If [code]true[/code], disables printing to standard error. If [code]true[/code], this also hides error and warning messages printed by [method @GlobalScope.push_error] and [method @GlobalScope.push_warning]. See also [member application/run/disable_stdout]. - Changes to this setting will only be applied upon restarting the application. + Changes to this setting will only be applied upon restarting the application. To control this at runtime, use [member Engine.print_error_messages]. </member> <member name="application/run/disable_stdout" type="bool" setter="" getter="" default="false"> If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. See also [member application/run/disable_stderr]. - Changes to this setting will only be applied upon restarting the application. + Changes to this setting will only be applied upon restarting the application. To control this at runtime, use [member Engine.print_to_stdout]. </member> <member name="application/run/enable_alt_space_menu" type="bool" setter="" getter="" default="false"> If [code]true[/code], allows the [kbd]Alt + Space[/kbd] keys to display the window menu. This menu allows the user to perform various window management operations such as moving, resizing, or minimizing the window. @@ -2215,6 +2215,7 @@ <member name="physics/2d/physics_engine" type="String" setter="" getter="" default=""DEFAULT""> Sets which physics engine to use for 2D physics. "DEFAULT" and "GodotPhysics2D" are the same, as there is currently no alternative 2D physics server implemented. + "Dummy" is a 2D physics server that does nothing and returns only dummy values, effectively disabling all 2D physics functionality. </member> <member name="physics/2d/run_on_separate_thread" type="bool" setter="" getter="" default="false"> If [code]true[/code], the 2D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 2D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process. @@ -2293,6 +2294,7 @@ <member name="physics/3d/physics_engine" type="String" setter="" getter="" default=""DEFAULT""> Sets which physics engine to use for 3D physics. "DEFAULT" and "GodotPhysics3D" are the same, as there is currently no alternative 3D physics server implemented. + "Dummy" is a 3D physics server that does nothing and returns only dummy values, effectively disabling all 3D physics functionality. </member> <member name="physics/3d/run_on_separate_thread" type="bool" setter="" getter="" default="false"> If [code]true[/code], the 3D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 3D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process. diff --git a/doc/classes/RDPipelineDepthStencilState.xml b/doc/classes/RDPipelineDepthStencilState.xml index b8245f97af..dc1e70eb55 100644 --- a/doc/classes/RDPipelineDepthStencilState.xml +++ b/doc/classes/RDPipelineDepthStencilState.xml @@ -19,7 +19,7 @@ The operation to perform on the stencil buffer for back pixels that pass the stencil test but fail the depth test. </member> <member name="back_op_fail" type="int" setter="set_back_op_fail" getter="get_back_op_fail" enum="RenderingDevice.StencilOperation" default="1"> - The operation to perform on the stencil buffer for back pixels that fail the stencil test + The operation to perform on the stencil buffer for back pixels that fail the stencil test. </member> <member name="back_op_pass" type="int" setter="set_back_op_pass" getter="get_back_op_pass" enum="RenderingDevice.StencilOperation" default="1"> The operation to perform on the stencil buffer for back pixels that pass the stencil test. diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index ddd52c6835..2ff7e934e9 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -905,7 +905,7 @@ <param index="4" name="mipmaps" type="int" default="1" /> <param index="5" name="slice_type" type="int" enum="RenderingDevice.TextureSliceType" default="0" /> <description> - Creates a shared texture using the specified [param view] and the texture information from [param with_texture]'s [param layer] and [param mipmap]. The number of included mipmaps from the original texture can be controlled using the [param mipmaps] parameter. Only relevant for textures with multiple layers, such as 3D textures, texture arrays and cubemaps. For single-layer textures, use [method texture_create_shared] + Creates a shared texture using the specified [param view] and the texture information from [param with_texture]'s [param layer] and [param mipmap]. The number of included mipmaps from the original texture can be controlled using the [param mipmaps] parameter. Only relevant for textures with multiple layers, such as 3D textures, texture arrays and cubemaps. For single-layer textures, use [method texture_create_shared]. For 2D textures (which only have one layer), [param layer] must be [code]0[/code]. [b]Note:[/b] Layer slicing is only supported for 2D texture arrays, not 3D textures or cubemaps. </description> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index c62770cf1f..4b2ce6f45e 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3532,7 +3532,7 @@ <method name="texture_2d_placeholder_create"> <return type="RID" /> <description> - Creates a placeholder for a 2-dimensional layered texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]texture_2d_layered_*[/code] RenderingServer functions, although it does nothing when used. See also [method texture_2d_layered_placeholder_create] + Creates a placeholder for a 2-dimensional layered texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]texture_2d_layered_*[/code] RenderingServer functions, although it does nothing when used. See also [method texture_2d_layered_placeholder_create]. Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method. [b]Note:[/b] The equivalent resource is [PlaceholderTexture2D]. </description> @@ -5219,7 +5219,7 @@ Output color as they came in. This can cause bright lighting to look blown out, with noticeable clipping in the output colors. </constant> <constant name="ENV_TONE_MAPPER_REINHARD" value="1" enum="EnvironmentToneMapper"> - Use the Reinhard tonemapper. Performs a variation on rendered pixels' colors by this formula: [code]color = color / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull. + Use the Reinhard tonemapper. Performs a variation on rendered pixels' colors by this formula: [code]color = color * (1 + color / (white * white)) / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull. When [member Environment.tonemap_white] is left at the default value of [code]1.0[/code] this is identical to [constant ENV_TONE_MAPPER_LINEAR] while also being slightly less performant. </constant> <constant name="ENV_TONE_MAPPER_FILMIC" value="2" enum="EnvironmentToneMapper"> Use the filmic tonemapper. This avoids clipping bright highlights, with a resulting image that usually looks more vivid than [constant ENV_TONE_MAPPER_REINHARD]. @@ -5660,7 +5660,10 @@ <constant name="GLOBAL_VAR_TYPE_SAMPLERCUBE" value="27" enum="GlobalShaderParameterType"> Cubemap sampler global shader parameter ([code]global uniform samplerCube ...[/code]). Exposed as a [Cubemap] in the editor UI. </constant> - <constant name="GLOBAL_VAR_TYPE_MAX" value="28" enum="GlobalShaderParameterType"> + <constant name="GLOBAL_VAR_TYPE_SAMPLEREXT" value="28" enum="GlobalShaderParameterType"> + External sampler global shader parameter ([code]global uniform samplerExternalOES ...[/code]). Exposed as a [ExternalTexture] in the editor UI. + </constant> + <constant name="GLOBAL_VAR_TYPE_MAX" value="29" enum="GlobalShaderParameterType"> Represents the size of the [enum GlobalShaderParameterType] enum. </constant> <constant name="RENDERING_INFO_TOTAL_OBJECTS_IN_FRAME" value="0" enum="RenderingInfo"> diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml index fe09472c14..18d4047339 100644 --- a/doc/classes/Resource.xml +++ b/doc/classes/Resource.xml @@ -20,6 +20,19 @@ Override this method to return a custom [RID] when [method get_rid] is called. </description> </method> + <method name="_reset_state" qualifiers="virtual"> + <return type="void" /> + <description> + For resources that use a variable number of properties, either via [method Object._validate_property] or [method Object._get_property_list], this method should be implemented to correctly clear the resource's state. + </description> + </method> + <method name="_set_path_cache" qualifiers="virtual const"> + <return type="void" /> + <param index="0" name="path" type="String" /> + <description> + Sets the resource's path to [param path] without involving the resource cache. + </description> + </method> <method name="_setup_local_to_scene" qualifiers="virtual"> <return type="void" /> <description> @@ -68,6 +81,14 @@ Generates a unique identifier for a resource to be contained inside a [PackedScene], based on the current date, time, and a random value. The returned string is only composed of letters ([code]a[/code] to [code]y[/code]) and numbers ([code]0[/code] to [code]8[/code]). See also [member resource_scene_unique_id]. </description> </method> + <method name="get_id_for_path" qualifiers="const"> + <return type="String" /> + <param index="0" name="path" type="String" /> + <description> + Returns the unique identifier for the resource with the given [param path] from the resource cache. If the resource is not loaded and cached, an empty string is returned. + [b]Note:[/b] This method is only implemented when running in an editor context. At runtime, it returns an empty string. + </description> + </method> <method name="get_local_scene" qualifiers="const"> <return type="Node" /> <description> @@ -80,6 +101,34 @@ Returns the [RID] of this resource (or an empty RID). Many resources (such as [Texture2D], [Mesh], and so on) are high-level abstractions of resources stored in a specialized server ([DisplayServer], [RenderingServer], etc.), so this function will return the original [RID]. </description> </method> + <method name="is_built_in" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the resource is built-in (from the engine) or [code]false[/code] if it is user-defined. + </description> + </method> + <method name="reset_state"> + <return type="void" /> + <description> + For resources that use a variable number of properties, either via [method Object._validate_property] or [method Object._get_property_list], override [method _reset_state] to correctly clear the resource's state. + </description> + </method> + <method name="set_id_for_path"> + <return type="void" /> + <param index="0" name="path" type="String" /> + <param index="1" name="id" type="String" /> + <description> + Sets the unique identifier to [param id] for the resource with the given [param path] in the resource cache. If the unique identifier is empty, the cache entry using [param path] is removed if it exists. + [b]Note:[/b] This method is only implemented when running in an editor context. + </description> + </method> + <method name="set_path_cache"> + <return type="void" /> + <param index="0" name="path" type="String" /> + <description> + Sets the resource's path to [param path] without involving the resource cache. + </description> + </method> <method name="setup_local_to_scene" deprecated="This method should only be called internally."> <return type="void" /> <description> diff --git a/doc/classes/ResourceSaver.xml b/doc/classes/ResourceSaver.xml index 42c9bd7a3c..05c749bc24 100644 --- a/doc/classes/ResourceSaver.xml +++ b/doc/classes/ResourceSaver.xml @@ -26,6 +26,14 @@ Returns the list of extensions available for saving a resource of a given type. </description> </method> + <method name="get_resource_id_for_path"> + <return type="int" /> + <param index="0" name="path" type="String" /> + <param index="1" name="generate" type="bool" default="false" /> + <description> + Returns the resource ID for the given path. If [param generate] is [code]true[/code], a new resource ID will be generated if one for the path is not found. If [param generate] is [code]false[/code] and the path is not found, [constant ResourceUID.INVALID_ID] is returned. + </description> + </method> <method name="remove_resource_format_saver"> <return type="void" /> <param index="0" name="format_saver" type="ResourceFormatSaver" /> diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml index 2181194fd4..405bba3564 100644 --- a/doc/classes/ScrollContainer.xml +++ b/doc/classes/ScrollContainer.xml @@ -102,6 +102,9 @@ <constant name="SCROLL_MODE_SHOW_NEVER" value="3" enum="ScrollMode"> Scrolling enabled, scrollbar will be hidden. </constant> + <constant name="SCROLL_MODE_RESERVE" value="4" enum="ScrollMode"> + Combines [constant SCROLL_MODE_AUTO] and [constant SCROLL_MODE_SHOW_ALWAYS]. The scrollbar is only visible if necessary, but the content size is adjusted as if it was always visible. It's useful for ensuring that content size stays the same regardless if the scrollbar is visible. + </constant> </constants> <theme_items> <theme_item name="panel" data_type="style" type="StyleBox"> diff --git a/doc/classes/Signal.xml b/doc/classes/Signal.xml index 7d6ff1e9b0..c970ccb094 100644 --- a/doc/classes/Signal.xml +++ b/doc/classes/Signal.xml @@ -48,7 +48,7 @@ <param index="0" name="object" type="Object" /> <param index="1" name="signal" type="StringName" /> <description> - Creates a new [Signal] named [param signal] in the specified [param object]. + Creates a [Signal] object referencing a signal named [param signal] in the specified [param object]. </description> </constructor> </constructors> @@ -109,6 +109,12 @@ Returns the ID of the object emitting this signal (see [method Object.get_instance_id]). </description> </method> + <method name="has_connections" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if any [Callable] is connected to this signal. + </description> + </method> <method name="is_connected" qualifiers="const"> <return type="bool" /> <param index="0" name="callable" type="Callable" /> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index a8bd068b1c..9c1525d8f8 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -241,7 +241,7 @@ <param index="0" name="count" type="int" enum="SurfaceTool.SkinWeightCount" /> <description> Set to [constant SKIN_8_WEIGHTS] to indicate that up to 8 bone influences per vertex may be used. - By default, only 4 bone influences are used ([constant SKIN_4_WEIGHTS]) + By default, only 4 bone influences are used ([constant SKIN_4_WEIGHTS]). [b]Note:[/b] This function takes an enum, not the exact number of weights. </description> </method> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 9fada9db35..42558bf992 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -1299,6 +1299,9 @@ <member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true" keywords="readonly, disabled, enabled"> If [code]false[/code], existing text cannot be modified and new text cannot be added. </member> + <member name="empty_selection_clipboard_enabled" type="bool" setter="set_empty_selection_clipboard_enabled" getter="is_empty_selection_clipboard_enabled" default="true"> + If [code]true[/code], copying or cutting without a selection is performed on all lines with a caret. Otherwise, copy and cut require a selection. + </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" /> <member name="highlight_all_occurrences" type="bool" setter="set_highlight_all_occurrences" getter="is_highlight_all_occurrences_enabled" default="false"> If [code]true[/code], all occurrences of the selected text will be highlighted. diff --git a/doc/classes/VisualShaderNodeRemap.xml b/doc/classes/VisualShaderNodeRemap.xml index 102672ff60..d4ecb2dd31 100644 --- a/doc/classes/VisualShaderNodeRemap.xml +++ b/doc/classes/VisualShaderNodeRemap.xml @@ -8,4 +8,34 @@ </description> <tutorials> </tutorials> + <members> + <member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeRemap.OpType" default="0"> + </member> + </members> + <constants> + <constant name="OP_TYPE_SCALAR" value="0" enum="OpType"> + A floating-point scalar type. + </constant> + <constant name="OP_TYPE_VECTOR_2D" value="1" enum="OpType"> + A 2D vector type. + </constant> + <constant name="OP_TYPE_VECTOR_2D_SCALAR" value="2" enum="OpType"> + The [code]value[/code] port uses a 2D vector type, while the [code]input min[/code], [code]input max[/code], [code]output min[/code], and [code]output max[/code] ports use a floating-point scalar type. + </constant> + <constant name="OP_TYPE_VECTOR_3D" value="3" enum="OpType"> + A 3D vector type. + </constant> + <constant name="OP_TYPE_VECTOR_3D_SCALAR" value="4" enum="OpType"> + The [code]value[/code] port uses a 3D vector type, while the [code]input min[/code], [code]input max[/code], [code]output min[/code], and [code]output max[/code] ports use a floating-point scalar type. + </constant> + <constant name="OP_TYPE_VECTOR_4D" value="5" enum="OpType"> + A 4D vector type. + </constant> + <constant name="OP_TYPE_VECTOR_4D_SCALAR" value="6" enum="OpType"> + The [code]value[/code] port uses a 4D vector type, while the [code]input min[/code], [code]input max[/code], [code]output min[/code], and [code]output max[/code] ports use a floating-point scalar type. + </constant> + <constant name="OP_TYPE_MAX" value="7" enum="OpType"> + Represents the size of the [enum OpType] enum. + </constant> + </constants> </class> diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 101660881b..e1a6aa4a98 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -150,7 +150,7 @@ PACKED_ARRAY_TYPES: List[str] = [ "PackedByteArray", "PackedColorArray", "PackedFloat32Array", - "Packedfloat64Array", + "PackedFloat64Array", "PackedInt32Array", "PackedInt64Array", "PackedStringArray", @@ -949,13 +949,17 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: inherits = class_def.inherits.strip() f.write(f'**{translate("Inherits:")}** ') first = True - while inherits in state.classes: + while inherits is not None: if not first: f.write(" **<** ") else: first = False f.write(make_type(inherits, state)) + + if inherits not in state.classes: + break # Parent unknown. + inode = state.classes[inherits].inherits if inode: inherits = inode.strip() diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index df7aba265b..3c959f0143 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -2184,7 +2184,9 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb glBindFramebuffer(GL_FRAMEBUFFER, render_target->fbo); glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4); glBindTexture(GL_TEXTURE_2D, render_target->backbuffer); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p_backbuffer_has_mipmaps ? render_target->mipmap_count - 1 : 0); + if (render_target->backbuffer != 0) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p_backbuffer_has_mipmaps ? render_target->mipmap_count - 1 : 0); + } } if (render_target->is_transparent || p_to_backbuffer) { diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 19ef3d416c..e79f1db08d 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -349,9 +349,6 @@ RasterizerGLES3::RasterizerGLES3() { } } - // Disable OpenGL linear to sRGB conversion, because Godot will always do this conversion itself. - glDisable(GL_FRAMEBUFFER_SRGB); - // OpenGL needs to be initialized before initializing the Rasterizers config = memnew(GLES3::Config); utilities = memnew(GLES3::Utilities); @@ -368,6 +365,11 @@ RasterizerGLES3::RasterizerGLES3() { fog = memnew(GLES3::Fog); canvas = memnew(RasterizerCanvasGLES3()); scene = memnew(RasterizerSceneGLES3()); + + // Disable OpenGL linear to sRGB conversion, because Godot will always do this conversion itself. + if (config->srgb_framebuffer_supported) { + glDisable(GL_FRAMEBUFFER_SRGB); + } } RasterizerGLES3::~RasterizerGLES3() { diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 5a0f394db0..b73debf04a 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -189,6 +189,14 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant } builder.append("\n"); //make sure defines begin at newline + // Optional support for external textures. + if (GLES3::Config::get_singleton()->external_texture_supported) { + builder.append("#extension GL_OES_EGL_image_external : enable\n"); + builder.append("#extension GL_OES_EGL_image_external_essl3 : enable\n"); + } else { + builder.append("#define samplerExternalOES sampler2D\n"); + } + // Insert multiview extension loading, because it needs to appear before // any non-preprocessor code (like the "precision highp..." lines below). builder.append("#ifdef USE_MULTIVIEW\n"); diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index e358230747..76881c8032 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -262,6 +262,8 @@ void main() { color_interp = color; + vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy; + if (use_pixel_snap) { vertex = floor(vertex + 0.5); // precision issue on some hardware creates artifacts within texture @@ -269,8 +271,6 @@ void main() { uv += 1e-5; } - vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy; - vertex_interp = vertex; uv_interp = uv; diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl index fb915aeb38..6738bdf748 100644 --- a/drivers/gles3/shaders/tonemap_inc.glsl +++ b/drivers/gles3/shaders/tonemap_inc.glsl @@ -76,8 +76,12 @@ vec3 tonemap_aces(vec3 color, float p_white) { return color_tonemapped / p_white_tonemapped; } +// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt vec3 tonemap_reinhard(vec3 color, float p_white) { - return (p_white * color + color) / (color * p_white + p_white); + float white_squared = p_white * p_white; + vec3 white_squared_color = white_squared * color; + // Equivalent to color * (1 + color / white_squared) / (1 + color) + return (white_squared_color + color * color) / (white_squared_color + white_squared); } #define TONEMAPPER_LINEAR 0 @@ -85,7 +89,7 @@ vec3 tonemap_reinhard(vec3 color, float p_white) { #define TONEMAPPER_FILMIC 2 #define TONEMAPPER_ACES 3 -vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always outputs clamped [0;1] color +vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR // Ensure color values passed to tonemappers are positive. // They can be negative in the case of negative lights, which leads to undesired behavior. if (tonemapper == TONEMAPPER_LINEAR) { diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index d6472c44c1..0100719151 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -88,6 +88,7 @@ Config::Config() { etc2_supported = false; s3tc_supported = true; rgtc_supported = true; //RGTC - core since OpenGL version 3.0 + srgb_framebuffer_supported = true; } else { float_texture_supported = extensions.has("GL_EXT_color_buffer_float"); etc2_supported = true; @@ -100,6 +101,7 @@ Config::Config() { s3tc_supported = extensions.has("GL_EXT_texture_compression_dxt1") || extensions.has("GL_EXT_texture_compression_s3tc") || extensions.has("WEBGL_compressed_texture_s3tc"); #endif rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc"); + srgb_framebuffer_supported = extensions.has("GL_EXT_sRGB_write_control"); } glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units); @@ -138,6 +140,7 @@ Config::Config() { // These are GLES only rt_msaa_supported = extensions.has("GL_EXT_multisampled_render_to_texture"); rt_msaa_multiview_supported = extensions.has("GL_OVR_multiview_multisampled_render_to_texture"); + external_texture_supported = extensions.has("GL_OES_EGL_image_external_essl3"); if (multiview_supported) { eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR"); @@ -166,6 +169,13 @@ Config::Config() { rt_msaa_multiview_supported = false; } } + + if (external_texture_supported) { + eglEGLImageTargetTexture2DOES = (PFNEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES"); + if (eglEGLImageTargetTexture2DOES == nullptr) { + external_texture_supported = false; + } + } #endif force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index ff72fc5b58..d60f295d66 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -45,6 +45,7 @@ typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, typedef void (*PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean); typedef void (*PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei); typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei); +typedef void (*PFNEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum, void *); #endif namespace GLES3 { @@ -79,6 +80,7 @@ public: bool astc_supported = false; bool astc_hdr_supported = false; bool astc_layered_supported = false; + bool srgb_framebuffer_supported = false; bool force_vertex_shading = false; @@ -91,6 +93,7 @@ public: bool rt_msaa_supported = false; bool rt_msaa_multiview_supported = false; bool multiview_supported = false; + bool external_texture_supported = false; // Adreno 3XX compatibility bool disable_particles_workaround = false; // set to 'true' to disable 'GPUParticles' @@ -104,6 +107,7 @@ public: PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr; PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr; PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr; + PFNEGLIMAGETARGETTEXTURE2DOESPROC eglEGLImageTargetTexture2DOES = nullptr; #endif static Config *get_singleton() { return singleton; }; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 7d5af48384..c29c741c2a 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -675,6 +675,7 @@ static const GLenum target_from_type[ShaderLanguage::TYPE_MAX] = { GL_TEXTURE_3D, // TYPE_USAMPLER3D, GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBE, GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBEARRAY, + _GL_TEXTURE_EXTERNAL_OES, // TYPE_SAMPLEREXT GL_TEXTURE_2D, // TYPE_STRUCT }; @@ -946,6 +947,9 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: { ERR_PRINT_ONCE("Type: SamplerCubeArray not supported in GL Compatibility rendering backend, please use another type."); } break; + case ShaderLanguage::TYPE_SAMPLEREXT: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_EXT); + } break; case ShaderLanguage::TYPE_ISAMPLER3D: case ShaderLanguage::TYPE_USAMPLER3D: @@ -1949,6 +1953,7 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture "sampler2DArray", "sampler3D", "samplerCube", + "samplerExternalOES" }; RS::GlobalShaderParameterType gvtype = RS::GLOBAL_VAR_TYPE_MAX; @@ -2661,7 +2666,11 @@ static void bind_uniforms_generic(const Vector<RID> &p_textures, const Vector<Sh const ShaderCompiler::GeneratedCode::Texture &texture_uniform = texture_uniforms[texture_uniform_index]; if (texture) { glActiveTexture(GL_TEXTURE0 + texture_offset + ti); - glBindTexture(target_from_type[texture_uniform.type], texture->tex_id); + GLenum target = target_from_type[texture_uniform.type]; + if (target == _GL_TEXTURE_EXTERNAL_OES && !GLES3::Config::get_singleton()->external_texture_supported) { + target = GL_TEXTURE_2D; + } + glBindTexture(target, texture->tex_id); if (texture->render_target) { texture->render_target->used_in_frame = true; } diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index bd824a076e..27ba89aa5f 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -153,6 +153,11 @@ TextureStorage::TextureStorage() { } { + default_gl_textures[DEFAULT_GL_TEXTURE_EXT] = texture_allocate(); + texture_external_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_EXT], 1, 1, 0); + } + + { unsigned char pixel_data[4 * 4 * 4]; for (int i = 0; i < 16; i++) { pixel_data[i * 4 + 0] = 0; @@ -769,6 +774,48 @@ void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_im texture_set_data(p_texture, p_image); } +void TextureStorage::texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) { + Texture texture; + texture.active = true; + texture.alloc_width = texture.width = p_width; + texture.alloc_height = texture.height = p_height; + texture.real_format = texture.format = Image::FORMAT_RGB8; + texture.type = Texture::TYPE_2D; + + if (GLES3::Config::get_singleton()->external_texture_supported) { + texture.target = _GL_TEXTURE_EXTERNAL_OES; + } else { + texture.target = GL_TEXTURE_2D; + } + + glGenTextures(1, &texture.tex_id); + glBindTexture(texture.target, texture.tex_id); + +#ifdef ANDROID_ENABLED + if (texture.target == _GL_TEXTURE_EXTERNAL_OES) { + if (p_external_buffer) { + GLES3::Config::get_singleton()->eglEGLImageTargetTexture2DOES(_GL_TEXTURE_EXTERNAL_OES, reinterpret_cast<void *>(p_external_buffer)); + } + texture.total_data_size = 0; + } else +#endif + { + // If external textures aren't supported, allocate an empty 1x1 texture. + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + texture.total_data_size = 3; + } + + glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(texture.tex_id, texture.total_data_size, "Texture External"); + texture_owner.initialize_rid(p_texture, texture); + + glBindTexture(texture.target, 0); +} + void TextureStorage::texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) { ERR_FAIL_COND(p_layers.is_empty()); @@ -930,6 +977,22 @@ void TextureStorage::texture_3d_update(RID p_texture, const Vector<Ref<Image>> & GLES3::Utilities::get_singleton()->texture_resize_data(tex->tex_id, tex->total_data_size); } +void TextureStorage::texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) { + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL(tex); + + tex->alloc_width = tex->width = p_width; + tex->alloc_height = tex->height = p_height; + +#ifdef ANDROID_ENABLED + if (tex->target == _GL_TEXTURE_EXTERNAL_OES && p_external_buffer) { + glBindTexture(_GL_TEXTURE_EXTERNAL_OES, tex->tex_id); + GLES3::Config::get_singleton()->eglEGLImageTargetTexture2DOES(_GL_TEXTURE_EXTERNAL_OES, reinterpret_cast<void *>(p_external_buffer)); + glBindTexture(_GL_TEXTURE_EXTERNAL_OES, 0); + } +#endif +} + void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_NULL(tex); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index b1d4630978..2bf8546c0f 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -126,6 +126,7 @@ enum DefaultGLTexture { DEFAULT_GL_TEXTURE_3D_BLACK, DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE, DEFAULT_GL_TEXTURE_2D_UINT, + DEFAULT_GL_TEXTURE_EXT, DEFAULT_GL_TEXTURE_MAX }; @@ -512,12 +513,14 @@ public: virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override; virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override; virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override; + virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override; virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override; virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override; virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override; + virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override; virtual void texture_proxy_update(RID p_proxy, RID p_base) override; //these two APIs can be used together or in combination with the others. diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 59783c58b5..40b58d2c62 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -820,7 +820,7 @@ FindReplaceBar::FindReplaceBar() { /*** CODE EDITOR ****/ -static constexpr float ZOOM_FACTOR_PRESETS[7] = { 0.25f, 0.5f, 0.75f, 1.0f, 1.5f, 2.0f, 3.0f }; +static constexpr float ZOOM_FACTOR_PRESETS[8] = { 0.5f, 0.75f, 0.9f, 1.0f, 1.1f, 1.25f, 1.5f, 2.0f }; // This function should be used to handle shortcuts that could otherwise // be handled too late if they weren't handled here. @@ -1090,6 +1090,9 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_draw_spaces(EDITOR_GET("text_editor/appearance/whitespace/draw_spaces")); text_editor->add_theme_constant_override("line_spacing", EDITOR_GET("text_editor/appearance/whitespace/line_spacing")); + // Behavior: General + text_editor->set_empty_selection_clipboard_enabled(EDITOR_GET("text_editor/behavior/general/empty_selection_clipboard")); + // Behavior: Navigation text_editor->set_scroll_past_end_of_file_enabled(EDITOR_GET("text_editor/behavior/navigation/scroll_past_end_of_file")); text_editor->set_smooth_scroll_enabled(EDITOR_GET("text_editor/behavior/navigation/smooth_scrolling")); @@ -1716,8 +1719,7 @@ void CodeTextEditor::_zoom_to(float p_zoom_factor) { } void CodeTextEditor::set_zoom_factor(float p_zoom_factor) { - int preset_count = sizeof(ZOOM_FACTOR_PRESETS) / sizeof(float); - zoom_factor = CLAMP(p_zoom_factor, ZOOM_FACTOR_PRESETS[0], ZOOM_FACTOR_PRESETS[preset_count - 1]); + zoom_factor = CLAMP(p_zoom_factor, 0.25f, 3.0f); int neutral_font_size = int(EDITOR_GET("interface/editor/code_font_size")) * EDSCALE; int new_font_size = Math::round(zoom_factor * neutral_font_size); @@ -1843,7 +1845,8 @@ CodeTextEditor::CodeTextEditor() { status_bar->add_child(zoom_button); zoom_button->set_flat(true); zoom_button->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER); - zoom_button->set_tooltip_text(TTR("Zoom factor")); + zoom_button->set_tooltip_text( + TTR("Zoom factor") + "\n" + vformat(TTR("%sMouse wheel, %s/%s: Finetune\n%s: Reset"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL), ED_GET_SHORTCUT("script_editor/zoom_in")->get_as_text(), ED_GET_SHORTCUT("script_editor/zoom_out")->get_as_text(), ED_GET_SHORTCUT("script_editor/reset_zoom")->get_as_text())); zoom_button->set_text("100 %"); PopupMenu *zoom_menu = zoom_button->get_popup(); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index 0eabe53360..1613d1d62f 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -88,14 +88,9 @@ void EditorAutoloadSettings::_notification(int p_what) { } bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, String *r_error) { - if (!p_name.is_valid_ascii_identifier()) { + if (!p_name.is_valid_unicode_identifier()) { if (r_error) { - *r_error = TTR("Invalid name.") + " "; - if (p_name.size() > 0 && p_name.left(1).is_numeric()) { - *r_error += TTR("Cannot begin with a digit."); - } else { - *r_error += TTR("Valid characters:") + " a-z, A-Z, 0-9 or _"; - } + *r_error = TTR("Invalid name.") + " " + TTR("Must be a valid Unicode identifier."); } return false; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index adae6599c1..2b51071b15 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1974,18 +1974,16 @@ void EditorFileSystem::_update_script_documentation() { for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *lang = ScriptServer::get_language(i); if (lang->supports_documentation() && efd->files[index]->type == lang->get_type()) { - // Reloading the script from disk if resource already in memory. Otherwise, the - // ResourceLoader::load will return the last loaded version of the script (without the modifications). - // The only have the script already loaded here is to edit the script outside the - // editor without being connected to the LSP server. - Ref<Resource> res = ResourceCache::get_ref(path); - if (res.is_valid()) { - res->reload_from_file(); - } + bool should_reload_script = _should_reload_script(path); Ref<Script> scr = ResourceLoader::load(path); if (scr.is_null()) { continue; } + if (should_reload_script) { + // Reloading the script from disk. Otherwise, the ResourceLoader::load will + // return the last loaded version of the script (without the modifications). + scr->reload_from_file(); + } Vector<DocData::ClassDoc> docs = scr->get_documentation(); for (int j = 0; j < docs.size(); j++) { EditorHelp::get_doc_data()->add_doc(docs[j]); @@ -2007,6 +2005,25 @@ void EditorFileSystem::_update_script_documentation() { update_script_paths_documentation.clear(); } +bool EditorFileSystem::_should_reload_script(const String &p_path) { + if (first_scan) { + return false; + } + + Ref<Script> scr = ResourceCache::get_ref(p_path); + if (scr.is_null()) { + // Not a script or not already loaded. + return false; + } + + // Scripts are reloaded via the script editor if they are currently opened. + if (ScriptEditor::get_singleton()->get_open_scripts().has(scr)) { + return false; + } + + return true; +} + void EditorFileSystem::_process_update_pending() { _update_script_classes(); // Parse documentation second, as it requires the class names to be loaded diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index e02127cb13..e53187c1d7 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -295,6 +295,7 @@ class EditorFileSystem : public Node { void _update_script_documentation(); void _process_update_pending(); void _process_removed_files(const HashSet<String> &p_processed_files); + bool _should_reload_script(const String &p_path); Mutex update_scene_mutex; HashSet<String> update_scene_paths; diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index 47ff21bc82..47f16f219f 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -530,7 +530,7 @@ bool EditorHelpSearch::Runner::_phase_fill_member_items_init() { return true; } -TreeItem *EditorHelpSearch::Runner::_create_category_item(TreeItem *p_parent, const String &p_class, const StringName &p_icon, const String &p_metatype, const String &p_text) { +TreeItem *EditorHelpSearch::Runner::_create_category_item(TreeItem *p_parent, const String &p_class, const StringName &p_icon, const String &p_text, const String &p_metatype) { const String item_meta = "class_" + p_metatype + ":" + p_class; TreeItem *item = nullptr; @@ -620,7 +620,7 @@ bool EditorHelpSearch::Runner::_phase_fill_member_items() { if ((search_flags & SEARCH_PROPERTIES) && !class_doc->properties.is_empty()) { TreeItem *parent_item = item; if (search_all) { - parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberProperty"), TTRC("Prtoperties"), "propertiess"); + parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberProperty"), TTRC("Properties"), "properties"); } for (const DocData::PropertyDoc &property_doc : class_doc->properties) { _create_property_item(parent_item, class_doc, &property_doc); diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h index b8b3c26b41..f4d0c6c89e 100644 --- a/editor/editor_help_search.h +++ b/editor/editor_help_search.h @@ -198,7 +198,7 @@ class EditorHelpSearch::Runner : public RefCounted { TreeItem *_create_class_hierarchy(const ClassMatch &p_match); TreeItem *_create_class_hierarchy(const DocData::ClassDoc *p_class_doc, const String &p_matching_keyword, bool p_gray); TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray, const String &p_matching_keyword); - TreeItem *_create_category_item(TreeItem *p_parent, const String &p_class, const StringName &p_icon, const String &p_metatype, const String &p_type); + TreeItem *_create_category_item(TreeItem *p_parent, const String &p_class, const StringName &p_icon, const String &p_text, const String &p_metatype); TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match); TreeItem *_create_constructor_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match); TreeItem *_create_operator_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 2b4b6a5b7e..f248d03140 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -926,11 +926,7 @@ void EditorNode::_resources_changed(const Vector<String> &p_resources) { } if (!res->editor_can_reload_from_file()) { - Ref<Script> scr = res; - // Scripts are reloaded via the script editor. - if (scr.is_null() || ScriptEditor::get_singleton()->get_open_scripts().has(scr)) { - continue; - } + continue; } if (!res->get_path().is_resource_file() && !res->get_path().is_absolute_path()) { continue; @@ -1206,7 +1202,7 @@ void EditorNode::_reload_modified_scenes() { editor_data.set_edited_scene(i); _remove_edited_scene(false); - Error err = load_scene(filename, false, false, true, false, true); + Error err = load_scene(filename, false, false, false, true); if (err != OK) { ERR_PRINT(vformat("Failed to load scene: %s", filename)); } @@ -3935,7 +3931,7 @@ int EditorNode::new_scene() { return idx; } -Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_clear_errors, bool p_force_open_imported, bool p_silent_change_tab) { +Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_force_open_imported, bool p_silent_change_tab) { if (!is_inside_tree()) { defer_load_scene = p_scene; return OK; @@ -3958,10 +3954,6 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b } } - if (p_clear_errors && !load_errors_queued_to_display) { - load_errors->clear(); - } - String lpath = ProjectSettings::get_singleton()->localize_path(p_scene); if (!lpath.begins_with("res://")) { @@ -4939,6 +4931,12 @@ void EditorNode::_progress_dialog_visibility_changed() { } } +void EditorNode::_load_error_dialog_visibility_changed() { + if (!load_error_dialog->is_visible()) { + load_errors->clear(); + } +} + String EditorNode::_get_system_info() const { String distribution_name = OS::get_singleton()->get_distribution_name(); if (distribution_name.is_empty()) { @@ -4956,8 +4954,10 @@ String EditorNode::_get_system_info() const { godot_version += " " + hash; } + String display_session_type; #ifdef LINUXBSD_ENABLED - const String display_server = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").capitalize().replace(" ", ""); // `replace` is necessary, because `capitalize` introduces a whitespace between "x" and "11". + // `replace` is necessary, because `capitalize` introduces a whitespace between "x" and "11". + display_session_type = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").capitalize().replace(" ", ""); #endif // LINUXBSD_ENABLED String driver_name = OS::get_singleton()->get_current_rendering_driver_name().to_lower(); String rendering_method = OS::get_singleton()->get_current_rendering_method().to_lower(); @@ -5018,16 +5018,33 @@ String EditorNode::_get_system_info() const { // Join info. Vector<String> info; info.push_back(godot_version); + String distribution_display_session_type = distribution_name; if (!distribution_version.is_empty()) { - info.push_back(distribution_name + " " + distribution_version); - } else { - info.push_back(distribution_name); + distribution_display_session_type += " " + distribution_version; } -#ifdef LINUXBSD_ENABLED - if (!display_server.is_empty()) { - info.push_back(display_server); + if (!display_session_type.is_empty()) { + distribution_display_session_type += " on " + display_session_type; } + info.push_back(distribution_display_session_type); + + String display_driver_window_mode; +#ifdef LINUXBSD_ENABLED + // `replace` is necessary, because `capitalize` introduces a whitespace between "x" and "11". + display_driver_window_mode = DisplayServer::get_singleton()->get_name().capitalize().replace(" ", "") + " display driver"; #endif // LINUXBSD_ENABLED + if (!display_driver_window_mode.is_empty()) { + display_driver_window_mode += ", "; + } + display_driver_window_mode += get_viewport()->is_embedding_subwindows() ? "Single-window" : "Multi-window"; + + if (DisplayServer::get_singleton()->get_screen_count() == 1) { + display_driver_window_mode += ", " + itos(DisplayServer::get_singleton()->get_screen_count()) + " monitor"; + } else { + display_driver_window_mode += ", " + itos(DisplayServer::get_singleton()->get_screen_count()) + " monitors"; + } + + info.push_back(display_driver_window_mode); + info.push_back(vformat("%s (%s)", driver_name, rendering_method)); String graphics; @@ -5046,7 +5063,7 @@ String EditorNode::_get_system_info() const { } info.push_back(graphics); - info.push_back(vformat("%s (%d Threads)", processor_name, processor_count)); + info.push_back(vformat("%s (%d threads)", processor_name, processor_count)); return String(" - ").join(info); } @@ -5900,7 +5917,7 @@ void EditorNode::reload_scene(const String &p_path) { // Reload scene. _remove_scene(scene_idx, false); - load_scene(p_path, true, false, true, true); + load_scene(p_path, true, false, true); // Adjust index so tab is back a the previous position. editor_data.move_edited_scene_to_index(scene_idx); @@ -6426,7 +6443,7 @@ void EditorNode::_inherit_imported(const String &p_action) { } void EditorNode::_open_imported() { - load_scene(open_import_request, true, false, true, true); + load_scene(open_import_request, true, false, true); } void EditorNode::dim_editor(bool p_dimming) { @@ -7833,6 +7850,7 @@ EditorNode::EditorNode() { load_error_dialog->set_unparent_when_invisible(true); load_error_dialog->add_child(load_errors); load_error_dialog->set_title(TTR("Load Errors")); + load_error_dialog->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorNode::_load_error_dialog_visibility_changed)); execute_outputs = memnew(RichTextLabel); execute_outputs->set_selection_enabled(true); diff --git a/editor/editor_node.h b/editor/editor_node.h index 109cacdf0e..7ef38b4edb 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -660,6 +660,7 @@ private: void _remove_all_not_owned_children(Node *p_node, Node *p_owner); void _progress_dialog_visibility_changed(); + void _load_error_dialog_visibility_changed(); protected: friend class FileSystemDock; @@ -778,7 +779,7 @@ public: void fix_dependencies(const String &p_for_file); int new_scene(); - Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false); + Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_force_open_imported = false, bool p_silent_change_tab = false); Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false); HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node, bool p_node_references_only); diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index bc7bfcfa58..27cbb9810c 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -30,10 +30,12 @@ #include "editor_sectioned_inspector.h" +#include "editor/editor_inspector.h" #include "editor/editor_property_name_processor.h" -#include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/themes/editor_scale.h" +#include "scene/gui/check_button.h" +#include "scene/gui/tree.h" static bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) { if (p_property_path.containsn(p_filter)) { @@ -307,18 +309,35 @@ void SectionedInspector::register_search_box(LineEdit *p_box) { search_box->connect(SceneStringName(text_changed), callable_mp(this, &SectionedInspector::_search_changed)); } +void SectionedInspector::register_advanced_toggle(CheckButton *p_toggle) { + advanced_toggle = p_toggle; + advanced_toggle->connect(SceneStringName(toggled), callable_mp(this, &SectionedInspector::_advanced_toggled)); + _advanced_toggled(advanced_toggle->is_pressed()); +} + void SectionedInspector::_search_changed(const String &p_what) { + if (advanced_toggle) { + if (p_what.is_empty()) { + advanced_toggle->set_pressed_no_signal(!restrict_to_basic); + advanced_toggle->set_disabled(false); + advanced_toggle->set_tooltip_text(String()); + } else { + advanced_toggle->set_pressed_no_signal(true); + advanced_toggle->set_disabled(true); + advanced_toggle->set_tooltip_text(TTR("Advanced settings are always shown when searching.")); + } + } update_category_list(); } -EditorInspector *SectionedInspector::get_inspector() { - return inspector; +void SectionedInspector::_advanced_toggled(bool p_toggled_on) { + restrict_to_basic = !p_toggled_on; + update_category_list(); + inspector->set_restrict_to_basic_settings(restrict_to_basic); } -void SectionedInspector::set_restrict_to_basic_settings(bool p_restrict) { - restrict_to_basic = p_restrict; - update_category_list(); - inspector->set_restrict_to_basic_settings(p_restrict); +EditorInspector *SectionedInspector::get_inspector() { + return inspector; } SectionedInspector::SectionedInspector() : diff --git a/editor/editor_sectioned_inspector.h b/editor/editor_sectioned_inspector.h index 3c9eb78571..75f1569d36 100644 --- a/editor/editor_sectioned_inspector.h +++ b/editor/editor_sectioned_inspector.h @@ -31,11 +31,14 @@ #ifndef EDITOR_SECTIONED_INSPECTOR_H #define EDITOR_SECTIONED_INSPECTOR_H -#include "editor/editor_inspector.h" #include "scene/gui/split_container.h" -#include "scene/gui/tree.h" +class CheckButton; +class EditorInspector; +class LineEdit; class SectionedInspectorFilter; +class Tree; +class TreeItem; class SectionedInspector : public HSplitContainer { GDCLASS(SectionedInspector, HSplitContainer); @@ -48,6 +51,7 @@ class SectionedInspector : public HSplitContainer { HashMap<String, TreeItem *> section_map; EditorInspector *inspector = nullptr; LineEdit *search_box = nullptr; + CheckButton *advanced_toggle = nullptr; String selected_category; @@ -57,9 +61,12 @@ class SectionedInspector : public HSplitContainer { void _section_selected(); void _search_changed(const String &p_what); + void _advanced_toggled(bool p_toggled_on); public: void register_search_box(LineEdit *p_box); + void register_advanced_toggle(CheckButton *p_toggle); + EditorInspector *get_inspector(); void edit(Object *p_object); String get_full_item_path(const String &p_item); @@ -67,7 +74,6 @@ public: void set_current_section(const String &p_section); String get_current_section() const; - void set_restrict_to_basic_settings(bool p_restrict); void update_category_list(); SectionedInspector(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 963c45b573..c972a6ab27 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -668,6 +668,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "text_editor/appearance/whitespace/line_spacing", 4, "0,50,1") // Behavior + // Behavior: General + _initial_set("text_editor/behavior/general/empty_selection_clipboard", true); + // Behavior: Navigation _initial_set("text_editor/behavior/navigation/move_caret_on_right_click", true, true); _initial_set("text_editor/behavior/navigation/scroll_past_end_of_file", false, true); diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index b133847823..d07608d852 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -33,6 +33,7 @@ #include "core/input/input_map.h" #include "core/os/keyboard.h" #include "editor/debugger/editor_debugger_node.h" +#include "editor/editor_inspector.h" #include "editor/editor_log.h" #include "editor/editor_node.h" #include "editor/editor_property_name_processor.h" @@ -806,7 +807,6 @@ void EditorSettingsDialog::_focus_current_search_box() { void EditorSettingsDialog::_advanced_toggled(bool p_button_pressed) { EditorSettings::get_singleton()->set("_editor_settings_advanced_mode", p_button_pressed); - inspector->set_restrict_to_basic_settings(!p_button_pressed); } void EditorSettingsDialog::_editor_restart() { @@ -860,8 +860,8 @@ EditorSettingsDialog::EditorSettingsDialog() { inspector = memnew(SectionedInspector); inspector->get_inspector()->set_use_filter(true); - inspector->set_restrict_to_basic_settings(!use_advanced); inspector->register_search_box(search_box); + inspector->register_advanced_toggle(advanced_switch); inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL); tab_general->add_child(inspector); inspector->get_inspector()->connect("property_edited", callable_mp(this, &EditorSettingsDialog::_settings_property_edited)); diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index ce2dbe7cb1..e0de6bbcb1 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -840,7 +840,7 @@ GroupsEditor::GroupsEditor() { add_child(hbc); add = memnew(Button); - add->set_flat(true); + add->set_theme_type_variation("FlatMenuButton"); add->set_tooltip_text(TTR("Add a new group.")); add->connect(SceneStringName(pressed), callable_mp(this, &GroupsEditor::_show_add_group_dialog)); hbc->add_child(add); diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp index f3770adcb7..a579224ecd 100644 --- a/editor/import/3d/resource_importer_obj.cpp +++ b/editor/import/3d/resource_importer_obj.cpp @@ -246,6 +246,8 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool smoothing = true; const uint32_t no_smoothing_smooth_group = (uint32_t)-1; + bool uses_uvs = false; + while (true) { String l = f->get_line().strip_edges(); while (l.length() && l[l.length() - 1] == '\\') { @@ -320,6 +322,17 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, idx = 1 ^ idx; } + // Check UVs before faces as we may need to generate dummy tangents if there are no UVs. + if (face[idx].size() >= 2 && !face[idx][1].is_empty()) { + int uv = face[idx][1].to_int() - 1; + if (uv < 0) { + uv += uvs.size() + 1; + } + ERR_FAIL_INDEX_V(uv, uvs.size(), ERR_FILE_CORRUPT); + surf_tool->set_uv(uvs[uv]); + uses_uvs = true; + } + if (face[idx].size() == 3) { int norm = face[idx][2].to_int() - 1; if (norm < 0) { @@ -327,28 +340,19 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, } ERR_FAIL_INDEX_V(norm, normals.size(), ERR_FILE_CORRUPT); surf_tool->set_normal(normals[norm]); - if (generate_tangents && uvs.is_empty()) { + if (generate_tangents && !uses_uvs) { // We can't generate tangents without UVs, so create dummy tangents. Vector3 tan = Vector3(normals[norm].z, -normals[norm].x, normals[norm].y).cross(normals[norm].normalized()).normalized(); surf_tool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0)); } } else { // No normals, use a dummy tangent since normals and tangents will be generated. - if (generate_tangents && uvs.is_empty()) { + if (generate_tangents && !uses_uvs) { // We can't generate tangents without UVs, so create dummy tangents. surf_tool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0)); } } - if (face[idx].size() >= 2 && !face[idx][1].is_empty()) { - int uv = face[idx][1].to_int() - 1; - if (uv < 0) { - uv += uvs.size() + 1; - } - ERR_FAIL_INDEX_V(uv, uvs.size(), ERR_FILE_CORRUPT); - surf_tool->set_uv(uvs[uv]); - } - int vtx = face[idx][0].to_int() - 1; if (vtx < 0) { vtx += vertices.size() + 1; @@ -407,7 +411,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, surf_tool->generate_normals(); } - if (generate_tangents && uvs.size()) { + if (generate_tangents && uses_uvs) { surf_tool->generate_tangents(); } @@ -426,10 +430,11 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, Array array = surf_tool->commit_to_arrays(); - if (mesh_flags & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && generate_tangents) { - // Compression is enabled, so let's validate that the normals and tangents are correct. + if (mesh_flags & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && generate_tangents && uses_uvs) { + // Compression is enabled, so let's validate that the normals and generated tangents are correct. Vector<Vector3> norms = array[Mesh::ARRAY_NORMAL]; Vector<float> tangents = array[Mesh::ARRAY_TANGENT]; + ERR_FAIL_COND_V(tangents.is_empty(), ERR_FILE_CORRUPT); for (int vert = 0; vert < norms.size(); vert++) { Vector3 tan = Vector3(tangents[vert * 4 + 0], tangents[vert * 4 + 1], tangents[vert * 4 + 2]); if (abs(tan.dot(norms[vert])) > 0.0001) { @@ -454,6 +459,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, surf_tool->clear(); surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES); + uses_uvs = false; } if (l.begins_with("o ") || f->eof_reached()) { diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp index 31e3022f6b..23e803f722 100644 --- a/editor/import_defaults_editor.cpp +++ b/editor/import_defaults_editor.cpp @@ -33,11 +33,8 @@ #include "core/config/project_settings.h" #include "core/io/resource_importer.h" #include "editor/action_map_editor.h" -#include "editor/editor_autoload_settings.h" +#include "editor/editor_inspector.h" #include "editor/editor_sectioned_inspector.h" -#include "editor/localization_editor.h" -#include "editor/plugins/editor_plugin_settings.h" -#include "editor/shader_globals_editor.h" #include "scene/gui/center_container.h" class ImportDefaultsEditorSettings : public Object { diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index 3c07e85758..921467ccbc 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -74,15 +74,20 @@ void LocalizationEditor::add_translation(const String &p_translation) { void LocalizationEditor::_translation_add(const PackedStringArray &p_paths) { PackedStringArray translations = GLOBAL_GET("internationalization/locale/translations"); - for (int i = 0; i < p_paths.size(); i++) { - if (!translations.has(p_paths[i])) { + int count = 0; + for (const String &path : p_paths) { + if (!translations.has(path)) { // Don't add duplicate translation paths. - translations.push_back(p_paths[i]); + translations.push_back(path); + count += 1; } } + if (count == 0) { + return; + } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(vformat(TTR("Add %d Translations"), p_paths.size())); + undo_redo->create_action(vformat(TTRN("Add %d Translation", "Add %d Translations", count), count)); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", translations); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", GLOBAL_GET("internationalization/locale/translations")); undo_redo->add_do_method(this, "update_translations"); @@ -136,15 +141,20 @@ void LocalizationEditor::_translation_res_add(const PackedStringArray &p_paths) prev = remaps; } - for (int i = 0; i < p_paths.size(); i++) { - if (!remaps.has(p_paths[i])) { + int count = 0; + for (const String &path : p_paths) { + if (!remaps.has(path)) { // Don't overwrite with an empty remap array if an array already exists for the given path. - remaps[p_paths[i]] = PackedStringArray(); + remaps[path] = PackedStringArray(); + count += 1; } } + if (count == 0) { + return; + } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(vformat(TTR("Translation Resource Remap: Add %d Path(s)"), p_paths.size())); + undo_redo->create_action(vformat(TTRN("Translation Resource Remap: Add %d Path", "Translation Resource Remap: Add %d Paths", count), count)); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", prev); undo_redo->add_do_method(this, "update_translations"); @@ -176,7 +186,7 @@ void LocalizationEditor::_translation_res_option_add(const PackedStringArray &p_ remaps[key] = r; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(vformat(TTR("Translation Resource Remap: Add %d Remap(s)"), p_paths.size())); + undo_redo->create_action(vformat(TTRN("Translation Resource Remap: Add %d Remap", "Translation Resource Remap: Add %d Remaps", p_paths.size()), p_paths.size())); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps")); undo_redo->add_do_method(this, "update_translations"); @@ -326,14 +336,19 @@ void LocalizationEditor::_translation_res_option_delete(Object *p_item, int p_co void LocalizationEditor::_pot_add(const PackedStringArray &p_paths) { PackedStringArray pot_translations = GLOBAL_GET("internationalization/locale/translations_pot_files"); - for (int i = 0; i < p_paths.size(); i++) { - if (!pot_translations.has(p_paths[i])) { - pot_translations.push_back(p_paths[i]); + int count = 0; + for (const String &path : p_paths) { + if (!pot_translations.has(path)) { + pot_translations.push_back(path); + count += 1; } } + if (count == 0) { + return; + } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(vformat(TTR("Add %d file(s) for POT generation"), p_paths.size())); + undo_redo->create_action(vformat(TTRN("Add %d file for POT generation", "Add %d files for POT generation", count), count)); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", pot_translations); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", GLOBAL_GET("internationalization/locale/translations_pot_files")); undo_redo->add_do_method(this, "update_translations"); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 9579388d46..54730ec674 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1483,7 +1483,7 @@ void ScriptEditor::_menu_option(int p_option) { current->apply_code(); - Error err = scr->reload(false); // Always hard reload the script before running. + Error err = scr->reload(true); // Always hard reload the script before running. if (err != OK || !scr->is_valid()) { EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it contains errors, check the output log."), EditorToaster::SEVERITY_WARNING); return; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 7e8fba8b9e..b45b30b52e 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1813,9 +1813,9 @@ static String _get_dropped_resource_line(const Ref<Resource> &p_resource, bool p } if (is_script) { - variable_name = variable_name.to_pascal_case().validate_ascii_identifier(); + variable_name = variable_name.to_pascal_case().validate_unicode_identifier(); } else { - variable_name = variable_name.to_snake_case().to_upper().validate_ascii_identifier(); + variable_name = variable_name.to_snake_case().to_upper().validate_unicode_identifier(); } return vformat("const %s = preload(%s)", variable_name, _quote_drop_data(path)); } @@ -1927,13 +1927,13 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data path = sn->get_path_to(node); } for (const String &segment : path.split("/")) { - if (!segment.is_valid_ascii_identifier()) { + if (!segment.is_valid_unicode_identifier()) { path = _quote_drop_data(path); break; } } - String variable_name = String(node->get_name()).to_snake_case().validate_ascii_identifier(); + String variable_name = String(node->get_name()).to_snake_case().validate_unicode_identifier(); if (use_type) { StringName class_name = node->get_class_name(); Ref<Script> node_script = node->get_script(); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index c378bf315b..f98a30ebb3 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -3600,6 +3600,16 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, const Vector<Vari fmaFunc->set_op_type((VisualShaderNodeMultiplyAdd::OpType)(int)p_ops[0]); } } + + // REMAP + { + VisualShaderNodeRemap *remap_func = Object::cast_to<VisualShaderNodeRemap>(p_node); + + if (remap_func) { + ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT); + remap_func->set_op_type((VisualShaderNodeRemap::OpType)(int)p_ops[0]); + } + } } void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, const String &p_resource_path, int p_node_idx) { @@ -7089,6 +7099,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Pow", "Scalar/Functions", "VisualShaderNodeFloatOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeFloatOp::OP_POW }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Radians", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeFloatFunc::FUNC_RADIANS }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Reciprocal", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("1.0 / scalar"), { VisualShaderNodeFloatFunc::FUNC_RECIPROCAL }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Remap", "Scalar/Functions", "VisualShaderNodeRemap", TTR("Remaps a value from the input range to the output range."), { VisualShaderNodeRemap::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Round", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeFloatFunc::FUNC_ROUND }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("RoundEven", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeFloatFunc::FUNC_ROUNDEVEN }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Saturate", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeFloatFunc::FUNC_SATURATE }, VisualShaderNode::PORT_TYPE_SCALAR)); @@ -7206,7 +7217,6 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("DistanceFade", "Utility", "VisualShaderNodeDistanceFade", TTR("The distance fade effect fades out each pixel based on its distance to another object."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("ProximityFade", "Utility", "VisualShaderNodeProximityFade", TTR("The proximity fade effect fades out each pixel based on its distance to another object."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("RandomRange", "Utility", "VisualShaderNodeRandomRange", TTR("Returns a random value between the minimum and maximum input values."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Remap", "Utility", "VisualShaderNodeRemap", TTR("Remaps a given input from the input range to the output range."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("RotationByAxis", "Utility", "VisualShaderNodeRotationByAxis", TTR("Builds a rotation matrix from the given axis and angle, multiply the input vector by it and returns both this vector and a matrix."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); // VECTOR @@ -7340,6 +7350,12 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Refract", "Vector/Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Refract", "Vector/Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Refract", "Vector/Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Remap", "Vector/Functions", "VisualShaderNodeRemap", TTR("Remaps a vector from the input range to the output range."), { VisualShaderNodeRemap::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Remap", "Vector/Functions", "VisualShaderNodeRemap", TTR("Remaps a vector from the input range to the output range."), { VisualShaderNodeRemap::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Remap", "Vector/Functions", "VisualShaderNodeRemap", TTR("Remaps a vector from the input range to the output range."), { VisualShaderNodeRemap::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("RemapS", "Vector/Functions", "VisualShaderNodeRemap", TTR("Remaps a vector from the input range to the output range. Ranges defined with scalars."), { VisualShaderNodeRemap::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("RemapS", "Vector/Functions", "VisualShaderNodeRemap", TTR("Remaps a vector from the input range to the output range. Ranges defined with scalars."), { VisualShaderNodeRemap::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("RemapS", "Vector/Functions", "VisualShaderNodeRemap", TTR("Remaps a vector from the input range to the output range. Ranges defined with scalars."), { VisualShaderNodeRemap::OP_TYPE_VECTOR_4D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Round", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Round", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Round", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index d00fdc2123..5767293718 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -31,7 +31,7 @@ #include "project_settings_editor.h" #include "core/config/project_settings.h" -#include "editor/editor_log.h" +#include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -106,7 +106,6 @@ void ProjectSettingsEditor::_update_advanced(bool p_is_advanced) { void ProjectSettingsEditor::_advanced_toggled(bool p_button_pressed) { EditorSettings::get_singleton()->set_project_metadata("project_settings", "advanced_mode", p_button_pressed); _update_advanced(p_button_pressed); - general_settings_inspector->set_restrict_to_basic_settings(!p_button_pressed); } void ProjectSettingsEditor::_setting_selected(const String &p_path) { @@ -688,6 +687,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { general_settings_inspector = memnew(SectionedInspector); general_settings_inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL); general_settings_inspector->register_search_box(search_box); + general_settings_inspector->register_advanced_toggle(advanced); general_settings_inspector->get_inspector()->set_use_filter(true); general_settings_inspector->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_setting_selected)); general_settings_inspector->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_setting_edited)); @@ -771,11 +771,10 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { bool use_advanced = EditorSettings::get_singleton()->get_project_metadata("project_settings", "advanced_mode", false); if (use_advanced) { - advanced->set_pressed_no_signal(true); + advanced->set_pressed(true); } _update_advanced(use_advanced); - general_settings_inspector->set_restrict_to_basic_settings(!use_advanced); import_defaults_editor = memnew(ImportDefaultsEditor); import_defaults_editor->set_name(TTR("Import Defaults")); diff --git a/editor/renames_map_3_to_4.cpp b/editor/renames_map_3_to_4.cpp index 8eab3fbea9..ae7c86a5e5 100644 --- a/editor/renames_map_3_to_4.cpp +++ b/editor/renames_map_3_to_4.cpp @@ -1518,7 +1518,6 @@ const char *RenamesMap3To4::class_renames[][2] = { { "EditorSceneImporterGLTF", "EditorSceneFormatImporterGLTF" }, { "EditorSpatialGizmo", "EditorNode3DGizmo" }, { "EditorSpatialGizmoPlugin", "EditorNode3DGizmoPlugin" }, - { "ExternalTexture", "ImageTexture" }, { "GIProbe", "VoxelGI" }, { "GIProbeData", "VoxelGIData" }, { "Generic6DOFJoint", "Generic6DOFJoint3D" }, diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index c05f60545d..85e5cd6ea0 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -31,6 +31,7 @@ #include "shader_globals_editor.h" #include "core/config/project_settings.h" +#include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "editor/editor_undo_redo_manager.h" #include "servers/rendering/shader_language.h" diff --git a/main/main.cpp b/main/main.cpp index fa50a3039f..75797e31de 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -80,9 +80,11 @@ #include "servers/navigation_server_2d.h" #include "servers/navigation_server_2d_dummy.h" #include "servers/physics_server_2d.h" +#include "servers/physics_server_2d_dummy.h" #ifndef _3D_DISABLED #include "servers/physics_server_3d.h" +#include "servers/physics_server_3d_dummy.h" #include "servers/xr_server.h" #endif // _3D_DISABLED @@ -320,7 +322,15 @@ void initialize_physics() { // Physics server not found, Use the default physics physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server(); } - ERR_FAIL_NULL(physics_server_3d); + + // Fall back to dummy if no default server has been registered. + if (!physics_server_3d) { + WARN_PRINT(vformat("Falling back to dummy PhysicsServer3D; 3D physics functionality will be disabled. If this is intended, set the %s project setting to Dummy.", PhysicsServer3DManager::setting_property_name)); + physics_server_3d = memnew(PhysicsServer3DDummy); + } + + // Should be impossible, but make sure it's not null. + ERR_FAIL_NULL_MSG(physics_server_3d, "Failed to initialize PhysicsServer3D."); physics_server_3d->init(); #endif // _3D_DISABLED @@ -331,7 +341,15 @@ void initialize_physics() { // Physics server not found, Use the default physics physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server(); } - ERR_FAIL_NULL(physics_server_2d); + + // Fall back to dummy if no default server has been registered. + if (!physics_server_2d) { + WARN_PRINT(vformat("Falling back to dummy PhysicsServer2D; 2D physics functionality will be disabled. If this is intended, set the %s project setting to Dummy.", PhysicsServer2DManager::setting_property_name)); + physics_server_2d = memnew(PhysicsServer2DDummy); + } + + // Should be impossible, but make sure it's not null. + ERR_FAIL_NULL_MSG(physics_server_2d, "Failed to initialize PhysicsServer2D."); physics_server_2d->init(); } diff --git a/modules/camera/SCsub b/modules/camera/SCsub index 9a6147d433..ba7fca8794 100644 --- a/modules/camera/SCsub +++ b/modules/camera/SCsub @@ -5,10 +5,16 @@ Import("env_modules") env_camera = env_modules.Clone() -if env["platform"] == "windows": +if env["platform"] in ["windows", "macos", "linuxbsd"]: env_camera.add_source_files(env.modules_sources, "register_types.cpp") + +if env["platform"] == "windows": env_camera.add_source_files(env.modules_sources, "camera_win.cpp") elif env["platform"] == "macos": - env_camera.add_source_files(env.modules_sources, "register_types.cpp") env_camera.add_source_files(env.modules_sources, "camera_macos.mm") + +elif env["platform"] == "linuxbsd": + env_camera.add_source_files(env.modules_sources, "camera_linux.cpp") + env_camera.add_source_files(env.modules_sources, "camera_feed_linux.cpp") + env_camera.add_source_files(env.modules_sources, "buffer_decoder.cpp") diff --git a/modules/camera/buffer_decoder.cpp b/modules/camera/buffer_decoder.cpp new file mode 100644 index 0000000000..024a68f080 --- /dev/null +++ b/modules/camera/buffer_decoder.cpp @@ -0,0 +1,212 @@ +/**************************************************************************/ +/* buffer_decoder.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "buffer_decoder.h" + +#include "servers/camera/camera_feed.h" + +#include <linux/videodev2.h> + +BufferDecoder::BufferDecoder(CameraFeed *p_camera_feed) { + camera_feed = p_camera_feed; + width = camera_feed->get_format().width; + height = camera_feed->get_format().height; + image.instantiate(); +} + +AbstractYuyvBufferDecoder::AbstractYuyvBufferDecoder(CameraFeed *p_camera_feed) : + BufferDecoder(p_camera_feed) { + switch (camera_feed->get_format().pixel_format) { + case V4L2_PIX_FMT_YYUV: + component_indexes = new int[4]{ 0, 1, 2, 3 }; + break; + case V4L2_PIX_FMT_YVYU: + component_indexes = new int[4]{ 0, 2, 3, 1 }; + break; + case V4L2_PIX_FMT_UYVY: + component_indexes = new int[4]{ 1, 3, 0, 2 }; + break; + case V4L2_PIX_FMT_VYUY: + component_indexes = new int[4]{ 1, 3, 2, 0 }; + break; + default: + component_indexes = new int[4]{ 0, 2, 1, 3 }; + } +} + +AbstractYuyvBufferDecoder::~AbstractYuyvBufferDecoder() { + delete[] component_indexes; +} + +SeparateYuyvBufferDecoder::SeparateYuyvBufferDecoder(CameraFeed *p_camera_feed) : + AbstractYuyvBufferDecoder(p_camera_feed) { + y_image_data.resize(width * height); + cbcr_image_data.resize(width * height); + y_image.instantiate(); + cbcr_image.instantiate(); +} + +void SeparateYuyvBufferDecoder::decode(StreamingBuffer p_buffer) { + uint8_t *y_dst = (uint8_t *)y_image_data.ptrw(); + uint8_t *uv_dst = (uint8_t *)cbcr_image_data.ptrw(); + uint8_t *src = (uint8_t *)p_buffer.start; + uint8_t *y0_src = src + component_indexes[0]; + uint8_t *y1_src = src + component_indexes[1]; + uint8_t *u_src = src + component_indexes[2]; + uint8_t *v_src = src + component_indexes[3]; + + for (int i = 0; i < width * height; i += 2) { + *y_dst++ = *y0_src; + *y_dst++ = *y1_src; + *uv_dst++ = *u_src; + *uv_dst++ = *v_src; + + y0_src += 4; + y1_src += 4; + u_src += 4; + v_src += 4; + } + + if (y_image.is_valid()) { + y_image->set_data(width, height, false, Image::FORMAT_L8, y_image_data); + } else { + y_image.instantiate(width, height, false, Image::FORMAT_RGB8, y_image_data); + } + if (cbcr_image.is_valid()) { + cbcr_image->set_data(width, height, false, Image::FORMAT_L8, cbcr_image_data); + } else { + cbcr_image.instantiate(width, height, false, Image::FORMAT_RGB8, cbcr_image_data); + } + + camera_feed->set_YCbCr_imgs(y_image, cbcr_image); +} + +YuyvToGrayscaleBufferDecoder::YuyvToGrayscaleBufferDecoder(CameraFeed *p_camera_feed) : + AbstractYuyvBufferDecoder(p_camera_feed) { + image_data.resize(width * height); +} + +void YuyvToGrayscaleBufferDecoder::decode(StreamingBuffer p_buffer) { + uint8_t *dst = (uint8_t *)image_data.ptrw(); + uint8_t *src = (uint8_t *)p_buffer.start; + uint8_t *y0_src = src + component_indexes[0]; + uint8_t *y1_src = src + component_indexes[1]; + + for (int i = 0; i < width * height; i += 2) { + *dst++ = *y0_src; + *dst++ = *y1_src; + + y0_src += 4; + y1_src += 4; + } + + if (image.is_valid()) { + image->set_data(width, height, false, Image::FORMAT_L8, image_data); + } else { + image.instantiate(width, height, false, Image::FORMAT_RGB8, image_data); + } + + camera_feed->set_RGB_img(image); +} + +YuyvToRgbBufferDecoder::YuyvToRgbBufferDecoder(CameraFeed *p_camera_feed) : + AbstractYuyvBufferDecoder(p_camera_feed) { + image_data.resize(width * height * 3); +} + +void YuyvToRgbBufferDecoder::decode(StreamingBuffer p_buffer) { + uint8_t *src = (uint8_t *)p_buffer.start; + uint8_t *y0_src = src + component_indexes[0]; + uint8_t *y1_src = src + component_indexes[1]; + uint8_t *u_src = src + component_indexes[2]; + uint8_t *v_src = src + component_indexes[3]; + uint8_t *dst = (uint8_t *)image_data.ptrw(); + + for (int i = 0; i < width * height; i += 2) { + int u = *u_src; + int v = *v_src; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dst++ = CLAMP(*y0_src + v1, 0, 255); + *dst++ = CLAMP(*y0_src - rg, 0, 255); + *dst++ = CLAMP(*y0_src + u1, 0, 255); + + *dst++ = CLAMP(*y1_src + v1, 0, 255); + *dst++ = CLAMP(*y1_src - rg, 0, 255); + *dst++ = CLAMP(*y1_src + u1, 0, 255); + + y0_src += 4; + y1_src += 4; + u_src += 4; + v_src += 4; + } + + if (image.is_valid()) { + image->set_data(width, height, false, Image::FORMAT_RGB8, image_data); + } else { + image.instantiate(width, height, false, Image::FORMAT_RGB8, image_data); + } + + camera_feed->set_RGB_img(image); +} + +CopyBufferDecoder::CopyBufferDecoder(CameraFeed *p_camera_feed, bool p_rgba) : + BufferDecoder(p_camera_feed) { + rgba = p_rgba; + image_data.resize(width * height * (rgba ? 4 : 2)); +} + +void CopyBufferDecoder::decode(StreamingBuffer p_buffer) { + uint8_t *dst = (uint8_t *)image_data.ptrw(); + memcpy(dst, p_buffer.start, p_buffer.length); + + if (image.is_valid()) { + image->set_data(width, height, false, rgba ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8, image_data); + } else { + image.instantiate(width, height, false, rgba ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8, image_data); + } + + camera_feed->set_RGB_img(image); +} + +JpegBufferDecoder::JpegBufferDecoder(CameraFeed *p_camera_feed) : + BufferDecoder(p_camera_feed) { +} + +void JpegBufferDecoder::decode(StreamingBuffer p_buffer) { + image_data.resize(p_buffer.length); + uint8_t *dst = (uint8_t *)image_data.ptrw(); + memcpy(dst, p_buffer.start, p_buffer.length); + if (image->load_jpg_from_buffer(image_data) == OK) { + camera_feed->set_RGB_img(image); + } +} diff --git a/modules/camera/buffer_decoder.h b/modules/camera/buffer_decoder.h new file mode 100644 index 0000000000..97cc66b6da --- /dev/null +++ b/modules/camera/buffer_decoder.h @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* buffer_decoder.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 BUFFER_DECODER_H +#define BUFFER_DECODER_H + +#include "core/io/image.h" +#include "core/templates/vector.h" + +class CameraFeed; + +struct StreamingBuffer { + void *start = nullptr; + size_t length = 0; +}; + +class BufferDecoder { +protected: + CameraFeed *camera_feed = nullptr; + Ref<Image> image; + int width = 0; + int height = 0; + +public: + virtual void decode(StreamingBuffer p_buffer) = 0; + + BufferDecoder(CameraFeed *p_camera_feed); + virtual ~BufferDecoder() {} +}; + +class AbstractYuyvBufferDecoder : public BufferDecoder { +protected: + int *component_indexes = nullptr; + +public: + AbstractYuyvBufferDecoder(CameraFeed *p_camera_feed); + ~AbstractYuyvBufferDecoder(); +}; + +class SeparateYuyvBufferDecoder : public AbstractYuyvBufferDecoder { +private: + Vector<uint8_t> y_image_data; + Vector<uint8_t> cbcr_image_data; + Ref<Image> y_image; + Ref<Image> cbcr_image; + +public: + SeparateYuyvBufferDecoder(CameraFeed *p_camera_feed); + virtual void decode(StreamingBuffer p_buffer) override; +}; + +class YuyvToGrayscaleBufferDecoder : public AbstractYuyvBufferDecoder { +private: + Vector<uint8_t> image_data; + +public: + YuyvToGrayscaleBufferDecoder(CameraFeed *p_camera_feed); + virtual void decode(StreamingBuffer p_buffer) override; +}; + +class YuyvToRgbBufferDecoder : public AbstractYuyvBufferDecoder { +private: + Vector<uint8_t> image_data; + +public: + YuyvToRgbBufferDecoder(CameraFeed *p_camera_feed); + virtual void decode(StreamingBuffer p_buffer) override; +}; + +class CopyBufferDecoder : public BufferDecoder { +private: + Vector<uint8_t> image_data; + bool rgba = false; + +public: + CopyBufferDecoder(CameraFeed *p_camera_feed, bool p_rgba); + virtual void decode(StreamingBuffer p_buffer) override; +}; + +class JpegBufferDecoder : public BufferDecoder { +private: + Vector<uint8_t> image_data; + +public: + JpegBufferDecoder(CameraFeed *p_camera_feed); + virtual void decode(StreamingBuffer p_buffer) override; +}; + +#endif // BUFFER_DECODER_H diff --git a/modules/camera/camera_feed_linux.cpp b/modules/camera/camera_feed_linux.cpp new file mode 100644 index 0000000000..9ed8eb0d0a --- /dev/null +++ b/modules/camera/camera_feed_linux.cpp @@ -0,0 +1,363 @@ +/**************************************************************************/ +/* camera_feed_linux.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "camera_feed_linux.h" + +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <unistd.h> + +void CameraFeedLinux::update_buffer_thread_func(void *p_func) { + if (p_func) { + CameraFeedLinux *camera_feed_linux = (CameraFeedLinux *)p_func; + camera_feed_linux->_update_buffer(); + } +} + +void CameraFeedLinux::_update_buffer() { + while (!exit_flag.is_set()) { + _read_frame(); + usleep(10000); + } +} + +void CameraFeedLinux::_query_device(const String &p_device_name) { + file_descriptor = open(p_device_name.ascii(), O_RDWR | O_NONBLOCK, 0); + ERR_FAIL_COND_MSG(file_descriptor == -1, vformat("Cannot open file descriptor for %s. Error: %d.", p_device_name, errno)); + + struct v4l2_capability capability; + if (ioctl(file_descriptor, VIDIOC_QUERYCAP, &capability) == -1) { + ERR_FAIL_MSG(vformat("Cannot query device. Error: %d.", errno)); + } + name = String((char *)capability.card); + + for (int index = 0;; index++) { + struct v4l2_fmtdesc fmtdesc; + memset(&fmtdesc, 0, sizeof(fmtdesc)); + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmtdesc.index = index; + + if (ioctl(file_descriptor, VIDIOC_ENUM_FMT, &fmtdesc) == -1) { + break; + } + + for (int res_index = 0;; res_index++) { + struct v4l2_frmsizeenum frmsizeenum; + memset(&frmsizeenum, 0, sizeof(frmsizeenum)); + frmsizeenum.pixel_format = fmtdesc.pixelformat; + frmsizeenum.index = res_index; + + if (ioctl(file_descriptor, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == -1) { + break; + } + + for (int framerate_index = 0;; framerate_index++) { + struct v4l2_frmivalenum frmivalenum; + memset(&frmivalenum, 0, sizeof(frmivalenum)); + frmivalenum.pixel_format = fmtdesc.pixelformat; + frmivalenum.width = frmsizeenum.discrete.width; + frmivalenum.height = frmsizeenum.discrete.height; + frmivalenum.index = framerate_index; + + if (ioctl(file_descriptor, VIDIOC_ENUM_FRAMEINTERVALS, &frmivalenum) == -1) { + if (framerate_index == 0) { + _add_format(fmtdesc, frmsizeenum.discrete, -1, 1); + } + break; + } + + _add_format(fmtdesc, frmsizeenum.discrete, frmivalenum.discrete.numerator, frmivalenum.discrete.denominator); + } + } + } + + close(file_descriptor); +} + +void CameraFeedLinux::_add_format(v4l2_fmtdesc p_description, v4l2_frmsize_discrete p_size, int p_frame_numerator, int p_frame_denominator) { + FeedFormat feed_format; + feed_format.width = p_size.width; + feed_format.height = p_size.height; + feed_format.format = String((char *)p_description.description); + feed_format.frame_numerator = p_frame_numerator; + feed_format.frame_denominator = p_frame_denominator; + feed_format.pixel_format = p_description.pixelformat; + print_verbose(vformat("%s %dx%d@%d/%dfps", (char *)p_description.description, p_size.width, p_size.height, p_frame_denominator, p_frame_numerator)); + formats.push_back(feed_format); +} + +bool CameraFeedLinux::_request_buffers() { + struct v4l2_requestbuffers requestbuffers; + + memset(&requestbuffers, 0, sizeof(requestbuffers)); + requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + requestbuffers.memory = V4L2_MEMORY_MMAP; + requestbuffers.count = 4; + + if (ioctl(file_descriptor, VIDIOC_REQBUFS, &requestbuffers) == -1) { + ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_REQBUFS) error: %d.", errno)); + } + + ERR_FAIL_COND_V_MSG(requestbuffers.count < 2, false, "Not enough buffers granted."); + + buffer_count = requestbuffers.count; + buffers = new StreamingBuffer[buffer_count]; + + for (unsigned int i = 0; i < buffer_count; i++) { + struct v4l2_buffer buffer; + + memset(&buffer, 0, sizeof(buffer)); + buffer.type = requestbuffers.type; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = i; + + if (ioctl(file_descriptor, VIDIOC_QUERYBUF, &buffer) == -1) { + delete[] buffers; + ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_QUERYBUF) error: %d.", errno)); + } + + buffers[i].length = buffer.length; + buffers[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, buffer.m.offset); + + if (buffers[i].start == MAP_FAILED) { + for (unsigned int b = 0; b < i; b++) { + _unmap_buffers(i); + } + delete[] buffers; + ERR_FAIL_V_MSG(false, "Mapping buffers failed."); + } + } + + return true; +} + +bool CameraFeedLinux::_start_capturing() { + for (unsigned int i = 0; i < buffer_count; i++) { + struct v4l2_buffer buffer; + + memset(&buffer, 0, sizeof(buffer)); + buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = i; + + if (ioctl(file_descriptor, VIDIOC_QBUF, &buffer) == -1) { + ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_QBUF) error: %d.", errno)); + } + } + + enum v4l2_buf_type type; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (ioctl(file_descriptor, VIDIOC_STREAMON, &type) == -1) { + ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_STREAMON) error: %d.", errno)); + } + + return true; +} + +void CameraFeedLinux::_read_frame() { + struct v4l2_buffer buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buffer.memory = V4L2_MEMORY_MMAP; + + if (ioctl(file_descriptor, VIDIOC_DQBUF, &buffer) == -1) { + if (errno != EAGAIN) { + print_error(vformat("ioctl(VIDIOC_DQBUF) error: %d.", errno)); + exit_flag.set(); + } + return; + } + + buffer_decoder->decode(buffers[buffer.index]); + + if (ioctl(file_descriptor, VIDIOC_QBUF, &buffer) == -1) { + print_error(vformat("ioctl(VIDIOC_QBUF) error: %d.", errno)); + } + + emit_signal(SNAME("frame_changed")); +} + +void CameraFeedLinux::_stop_capturing() { + enum v4l2_buf_type type; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (ioctl(file_descriptor, VIDIOC_STREAMOFF, &type) == -1) { + print_error(vformat("ioctl(VIDIOC_STREAMOFF) error: %d.", errno)); + } +} + +void CameraFeedLinux::_unmap_buffers(unsigned int p_count) { + for (unsigned int i = 0; i < p_count; i++) { + munmap(buffers[i].start, buffers[i].length); + } +} + +void CameraFeedLinux::_start_thread() { + exit_flag.clear(); + thread = memnew(Thread); + thread->start(CameraFeedLinux::update_buffer_thread_func, this); +} + +String CameraFeedLinux::get_device_name() const { + return device_name; +} + +bool CameraFeedLinux::activate_feed() { + file_descriptor = open(device_name.ascii(), O_RDWR | O_NONBLOCK, 0); + if (_request_buffers() && _start_capturing()) { + buffer_decoder = _create_buffer_decoder(); + _start_thread(); + return true; + } + ERR_FAIL_V_MSG(false, "Could not activate feed."); +} + +BufferDecoder *CameraFeedLinux::_create_buffer_decoder() { + switch (formats[selected_format].pixel_format) { + case V4L2_PIX_FMT_MJPEG: + case V4L2_PIX_FMT_JPEG: + return memnew(JpegBufferDecoder(this)); + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YYUV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: { + String output = parameters["output"]; + if (output == "separate") { + return memnew(SeparateYuyvBufferDecoder(this)); + } + if (output == "grayscale") { + return memnew(YuyvToGrayscaleBufferDecoder(this)); + } + if (output == "copy") { + return memnew(CopyBufferDecoder(this, false)); + } + return memnew(YuyvToRgbBufferDecoder(this)); + } + default: + return memnew(CopyBufferDecoder(this, true)); + } +} + +void CameraFeedLinux::deactivate_feed() { + exit_flag.set(); + thread->wait_to_finish(); + memdelete(thread); + _stop_capturing(); + _unmap_buffers(buffer_count); + delete[] buffers; + memdelete(buffer_decoder); + for (int i = 0; i < CameraServer::FEED_IMAGES; i++) { + RID placeholder = RenderingServer::get_singleton()->texture_2d_placeholder_create(); + RenderingServer::get_singleton()->texture_replace(texture[i], placeholder); + } + base_width = 0; + base_height = 0; + close(file_descriptor); + + emit_signal(SNAME("format_changed")); +} + +Array CameraFeedLinux::get_formats() const { + Array result; + for (const FeedFormat &format : formats) { + Dictionary dictionary; + dictionary["width"] = format.width; + dictionary["height"] = format.height; + dictionary["format"] = format.format; + dictionary["frame_numerator"] = format.frame_numerator; + dictionary["frame_denominator"] = format.frame_denominator; + result.push_back(dictionary); + } + return result; +} + +CameraFeed::FeedFormat CameraFeedLinux::get_format() const { + return formats[selected_format]; +} + +bool CameraFeedLinux::set_format(int p_index, const Dictionary &p_parameters) { + ERR_FAIL_COND_V_MSG(active, false, "Feed is active."); + ERR_FAIL_INDEX_V_MSG(p_index, formats.size(), false, "Invalid format index."); + + parameters = p_parameters.duplicate(); + selected_format = p_index; + + FeedFormat feed_format = formats[p_index]; + + file_descriptor = open(device_name.ascii(), O_RDWR | O_NONBLOCK, 0); + + struct v4l2_format format; + memset(&format, 0, sizeof(format)); + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + format.fmt.pix.width = feed_format.width; + format.fmt.pix.height = feed_format.height; + format.fmt.pix.pixelformat = feed_format.pixel_format; + + if (ioctl(file_descriptor, VIDIOC_S_FMT, &format) == -1) { + close(file_descriptor); + ERR_FAIL_V_MSG(false, vformat("Cannot set format, error: %d.", errno)); + } + + if (feed_format.frame_numerator > 0) { + struct v4l2_streamparm param; + memset(¶m, 0, sizeof(param)); + + param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + param.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + param.parm.capture.timeperframe.numerator = feed_format.frame_numerator; + param.parm.capture.timeperframe.denominator = feed_format.frame_denominator; + + if (ioctl(file_descriptor, VIDIOC_S_PARM, ¶m) == -1) { + close(file_descriptor); + ERR_FAIL_V_MSG(false, vformat("Cannot set framerate, error: %d.", errno)); + } + } + close(file_descriptor); + + emit_signal(SNAME("format_changed")); + + return true; +} + +CameraFeedLinux::CameraFeedLinux(const String &p_device_name) : + CameraFeed() { + device_name = p_device_name; + _query_device(device_name); + set_format(0, Dictionary()); +} + +CameraFeedLinux::~CameraFeedLinux() { + if (is_active()) { + deactivate_feed(); + } +} diff --git a/modules/camera/camera_feed_linux.h b/modules/camera/camera_feed_linux.h new file mode 100644 index 0000000000..bf29201c99 --- /dev/null +++ b/modules/camera/camera_feed_linux.h @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* camera_feed_linux.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 CAMERA_FEED_LINUX_H +#define CAMERA_FEED_LINUX_H + +#include "buffer_decoder.h" + +#include "core/os/thread.h" +#include "servers/camera/camera_feed.h" + +#include <linux/videodev2.h> + +struct StreamingBuffer; + +class CameraFeedLinux : public CameraFeed { +private: + SafeFlag exit_flag; + Thread *thread = nullptr; + String device_name; + int file_descriptor = -1; + StreamingBuffer *buffers = nullptr; + unsigned int buffer_count = 0; + BufferDecoder *buffer_decoder = nullptr; + + static void update_buffer_thread_func(void *p_func); + + void _update_buffer(); + void _query_device(const String &p_device_name); + void _add_format(v4l2_fmtdesc description, v4l2_frmsize_discrete size, int frame_numerator, int frame_denominator); + bool _request_buffers(); + bool _start_capturing(); + void _read_frame(); + void _stop_capturing(); + void _unmap_buffers(unsigned int p_count); + BufferDecoder *_create_buffer_decoder(); + void _start_thread(); + +public: + String get_device_name() const; + bool activate_feed(); + void deactivate_feed(); + bool set_format(int p_index, const Dictionary &p_parameters); + Array get_formats() const; + FeedFormat get_format() const; + + CameraFeedLinux(const String &p_device_name); + virtual ~CameraFeedLinux(); +}; + +#endif // CAMERA_FEED_LINUX_H diff --git a/modules/camera/camera_linux.cpp b/modules/camera/camera_linux.cpp new file mode 100644 index 0000000000..0cfb6b7b9e --- /dev/null +++ b/modules/camera/camera_linux.cpp @@ -0,0 +1,169 @@ +/**************************************************************************/ +/* camera_linux.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "camera_linux.h" + +#include "camera_feed_linux.h" + +#include <dirent.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> + +void CameraLinux::camera_thread_func(void *p_camera_linux) { + if (p_camera_linux) { + CameraLinux *camera_linux = (CameraLinux *)p_camera_linux; + camera_linux->_update_devices(); + } +} + +void CameraLinux::_update_devices() { + while (!exit_flag.is_set()) { + { + MutexLock lock(camera_mutex); + + for (int i = feeds.size() - 1; i >= 0; i--) { + Ref<CameraFeedLinux> feed = (Ref<CameraFeedLinux>)feeds[i]; + String device_name = feed->get_device_name(); + if (!_is_active(device_name)) { + remove_feed(feed); + } + } + + DIR *devices = opendir("/dev"); + + if (devices) { + struct dirent *device; + + while ((device = readdir(devices)) != nullptr) { + if (strncmp(device->d_name, "video", 5) != 0) { + continue; + } + String device_name = String("/dev/") + String(device->d_name); + if (!_has_device(device_name)) { + _add_device(device_name); + } + } + } + + closedir(devices); + } + + usleep(1000000); + } +} + +bool CameraLinux::_has_device(const String &p_device_name) { + for (int i = 0; i < feeds.size(); i++) { + Ref<CameraFeedLinux> feed = (Ref<CameraFeedLinux>)feeds[i]; + if (feed->get_device_name() == p_device_name) { + return true; + } + } + return false; +} + +void CameraLinux::_add_device(const String &p_device_name) { + int file_descriptor = _open_device(p_device_name); + + if (file_descriptor != -1) { + if (_is_video_capture_device(file_descriptor)) { + Ref<CameraFeedLinux> feed = memnew(CameraFeedLinux(p_device_name)); + add_feed(feed); + } + } + + close(file_descriptor); +} + +int CameraLinux::_open_device(const String &p_device_name) { + struct stat s; + + if (stat(p_device_name.ascii(), &s) == -1) { + return -1; + } + + if (!S_ISCHR(s.st_mode)) { + return -1; + } + + return open(p_device_name.ascii(), O_RDWR | O_NONBLOCK, 0); +} + +// TODO any cheaper/cleaner way to check if file descriptor is invalid? +bool CameraLinux::_is_active(const String &p_device_name) { + struct v4l2_capability capability; + bool result = false; + int file_descriptor = _open_device(p_device_name); + if (file_descriptor != -1 && ioctl(file_descriptor, VIDIOC_QUERYCAP, &capability) != -1) { + result = true; + } + close(file_descriptor); + return result; +} + +bool CameraLinux::_is_video_capture_device(int p_file_descriptor) { + struct v4l2_capability capability; + + if (ioctl(p_file_descriptor, VIDIOC_QUERYCAP, &capability) == -1) { + print_verbose("Cannot query device"); + return false; + } + + if (!(capability.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + print_verbose(vformat("%s is no video capture device\n", String((char *)capability.card))); + return false; + } + + if (!(capability.capabilities & V4L2_CAP_STREAMING)) { + print_verbose(vformat("%s does not support streaming", String((char *)capability.card))); + return false; + } + + return _can_query_format(p_file_descriptor, V4L2_BUF_TYPE_VIDEO_CAPTURE); +} + +bool CameraLinux::_can_query_format(int p_file_descriptor, int p_type) { + struct v4l2_format format; + memset(&format, 0, sizeof(format)); + format.type = p_type; + + return ioctl(p_file_descriptor, VIDIOC_G_FMT, &format) != -1; +} + +CameraLinux::CameraLinux() { + camera_thread.start(CameraLinux::camera_thread_func, this); +}; + +CameraLinux::~CameraLinux() { + exit_flag.set(); + camera_thread.wait_to_finish(); +} diff --git a/modules/camera/camera_linux.h b/modules/camera/camera_linux.h new file mode 100644 index 0000000000..66f6aa0ffb --- /dev/null +++ b/modules/camera/camera_linux.h @@ -0,0 +1,60 @@ +/**************************************************************************/ +/* camera_linux.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 CAMERA_LINUX_H +#define CAMERA_LINUX_H + +#include "core/os/mutex.h" +#include "core/os/thread.h" +#include "servers/camera_server.h" + +class CameraLinux : public CameraServer { +private: + SafeFlag exit_flag; + Thread camera_thread; + Mutex camera_mutex; + + static void camera_thread_func(void *p_camera_linux); + + void _update_devices(); + bool _has_device(const String &p_device_name); + void _add_device(const String &p_device_name); + void _remove_device(const String &p_device_name); + int _open_device(const String &p_device_name); + bool _is_active(const String &p_device_name); + bool _is_video_capture_device(int p_file_descriptor); + bool _can_query_format(int p_file_descriptor, int p_type); + +public: + CameraLinux(); + ~CameraLinux(); +}; + +#endif // CAMERA_LINUX_H diff --git a/modules/camera/config.py b/modules/camera/config.py index d2b2542dd9..7b368d2193 100644 --- a/modules/camera/config.py +++ b/modules/camera/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return platform == "macos" or platform == "windows" + return platform == "macos" or platform == "windows" or platform == "linuxbsd" def configure(env): diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp index feee6769f8..666ea8ba65 100644 --- a/modules/camera/register_types.cpp +++ b/modules/camera/register_types.cpp @@ -30,6 +30,9 @@ #include "register_types.h" +#if defined(LINUXBSD_ENABLED) +#include "camera_linux.h" +#endif #if defined(WINDOWS_ENABLED) #include "camera_win.h" #endif @@ -42,6 +45,9 @@ void initialize_camera_module(ModuleInitializationLevel p_level) { return; } +#if defined(LINUXBSD_ENABLED) + CameraServer::make_default<CameraLinux>(); +#endif #if defined(WINDOWS_ENABLED) CameraServer::make_default<CameraWindows>(); #endif diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index cf1cd55355..73f2b1d618 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -97,8 +97,8 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri } processed_template = processed_template.replace("_BASE_", p_base_class_name) - .replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_ascii_identifier()) - .replace("_CLASS_", p_class_name.to_pascal_case().validate_ascii_identifier()) + .replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_unicode_identifier()) + .replace("_CLASS_", p_class_name.to_pascal_case().validate_unicode_identifier()) .replace("_TS_", _get_indentation()); scr->set_source_code(processed_template); return scr; diff --git a/modules/godot_physics_2d/SCsub b/modules/godot_physics_2d/SCsub new file mode 100644 index 0000000000..5d93da5ecf --- /dev/null +++ b/modules/godot_physics_2d/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import('env') + +env.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/godot_physics_2d/config.py b/modules/godot_physics_2d/config.py new file mode 100644 index 0000000000..d22f9454ed --- /dev/null +++ b/modules/godot_physics_2d/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return True + + +def configure(env): + pass diff --git a/servers/physics_2d/godot_area_2d.cpp b/modules/godot_physics_2d/godot_area_2d.cpp index d6c786706c..d6c786706c 100644 --- a/servers/physics_2d/godot_area_2d.cpp +++ b/modules/godot_physics_2d/godot_area_2d.cpp diff --git a/servers/physics_2d/godot_area_2d.h b/modules/godot_physics_2d/godot_area_2d.h index e6c3b45d6c..e6c3b45d6c 100644 --- a/servers/physics_2d/godot_area_2d.h +++ b/modules/godot_physics_2d/godot_area_2d.h diff --git a/servers/physics_2d/godot_area_pair_2d.cpp b/modules/godot_physics_2d/godot_area_pair_2d.cpp index ca12e30c29..ca12e30c29 100644 --- a/servers/physics_2d/godot_area_pair_2d.cpp +++ b/modules/godot_physics_2d/godot_area_pair_2d.cpp diff --git a/servers/physics_2d/godot_area_pair_2d.h b/modules/godot_physics_2d/godot_area_pair_2d.h index eb091288a9..eb091288a9 100644 --- a/servers/physics_2d/godot_area_pair_2d.h +++ b/modules/godot_physics_2d/godot_area_pair_2d.h diff --git a/servers/physics_2d/godot_body_2d.cpp b/modules/godot_physics_2d/godot_body_2d.cpp index c401e6eee7..c401e6eee7 100644 --- a/servers/physics_2d/godot_body_2d.cpp +++ b/modules/godot_physics_2d/godot_body_2d.cpp diff --git a/servers/physics_2d/godot_body_2d.h b/modules/godot_physics_2d/godot_body_2d.h index 529305dbb2..529305dbb2 100644 --- a/servers/physics_2d/godot_body_2d.h +++ b/modules/godot_physics_2d/godot_body_2d.h diff --git a/servers/physics_2d/godot_body_direct_state_2d.cpp b/modules/godot_physics_2d/godot_body_direct_state_2d.cpp index b34c70831d..b34c70831d 100644 --- a/servers/physics_2d/godot_body_direct_state_2d.cpp +++ b/modules/godot_physics_2d/godot_body_direct_state_2d.cpp diff --git a/servers/physics_2d/godot_body_direct_state_2d.h b/modules/godot_physics_2d/godot_body_direct_state_2d.h index 90b7c1d369..90b7c1d369 100644 --- a/servers/physics_2d/godot_body_direct_state_2d.h +++ b/modules/godot_physics_2d/godot_body_direct_state_2d.h diff --git a/servers/physics_2d/godot_body_pair_2d.cpp b/modules/godot_physics_2d/godot_body_pair_2d.cpp index 6c2d28dc92..6c2d28dc92 100644 --- a/servers/physics_2d/godot_body_pair_2d.cpp +++ b/modules/godot_physics_2d/godot_body_pair_2d.cpp diff --git a/servers/physics_2d/godot_body_pair_2d.h b/modules/godot_physics_2d/godot_body_pair_2d.h index 4e9bfa6022..4e9bfa6022 100644 --- a/servers/physics_2d/godot_body_pair_2d.h +++ b/modules/godot_physics_2d/godot_body_pair_2d.h diff --git a/servers/physics_2d/godot_broad_phase_2d.cpp b/modules/godot_physics_2d/godot_broad_phase_2d.cpp index eb6bc21d60..eb6bc21d60 100644 --- a/servers/physics_2d/godot_broad_phase_2d.cpp +++ b/modules/godot_physics_2d/godot_broad_phase_2d.cpp diff --git a/servers/physics_2d/godot_broad_phase_2d.h b/modules/godot_physics_2d/godot_broad_phase_2d.h index f3c07a69bb..f3c07a69bb 100644 --- a/servers/physics_2d/godot_broad_phase_2d.h +++ b/modules/godot_physics_2d/godot_broad_phase_2d.h diff --git a/servers/physics_2d/godot_broad_phase_2d_bvh.cpp b/modules/godot_physics_2d/godot_broad_phase_2d_bvh.cpp index 59623a2667..59623a2667 100644 --- a/servers/physics_2d/godot_broad_phase_2d_bvh.cpp +++ b/modules/godot_physics_2d/godot_broad_phase_2d_bvh.cpp diff --git a/servers/physics_2d/godot_broad_phase_2d_bvh.h b/modules/godot_physics_2d/godot_broad_phase_2d_bvh.h index 6c1fae5cb2..6c1fae5cb2 100644 --- a/servers/physics_2d/godot_broad_phase_2d_bvh.h +++ b/modules/godot_physics_2d/godot_broad_phase_2d_bvh.h diff --git a/servers/physics_2d/godot_collision_object_2d.cpp b/modules/godot_physics_2d/godot_collision_object_2d.cpp index 9851cac140..9851cac140 100644 --- a/servers/physics_2d/godot_collision_object_2d.cpp +++ b/modules/godot_physics_2d/godot_collision_object_2d.cpp diff --git a/servers/physics_2d/godot_collision_object_2d.h b/modules/godot_physics_2d/godot_collision_object_2d.h index 129fa27ff3..129fa27ff3 100644 --- a/servers/physics_2d/godot_collision_object_2d.h +++ b/modules/godot_physics_2d/godot_collision_object_2d.h diff --git a/servers/physics_2d/godot_collision_solver_2d.cpp b/modules/godot_physics_2d/godot_collision_solver_2d.cpp index a1acbe9cf0..a1acbe9cf0 100644 --- a/servers/physics_2d/godot_collision_solver_2d.cpp +++ b/modules/godot_physics_2d/godot_collision_solver_2d.cpp diff --git a/servers/physics_2d/godot_collision_solver_2d.h b/modules/godot_physics_2d/godot_collision_solver_2d.h index 1c09714f76..1c09714f76 100644 --- a/servers/physics_2d/godot_collision_solver_2d.h +++ b/modules/godot_physics_2d/godot_collision_solver_2d.h diff --git a/servers/physics_2d/godot_collision_solver_2d_sat.cpp b/modules/godot_physics_2d/godot_collision_solver_2d_sat.cpp index daa9982b2e..daa9982b2e 100644 --- a/servers/physics_2d/godot_collision_solver_2d_sat.cpp +++ b/modules/godot_physics_2d/godot_collision_solver_2d_sat.cpp diff --git a/servers/physics_2d/godot_collision_solver_2d_sat.h b/modules/godot_physics_2d/godot_collision_solver_2d_sat.h index c9183f7ecb..c9183f7ecb 100644 --- a/servers/physics_2d/godot_collision_solver_2d_sat.h +++ b/modules/godot_physics_2d/godot_collision_solver_2d_sat.h diff --git a/servers/physics_2d/godot_constraint_2d.h b/modules/godot_physics_2d/godot_constraint_2d.h index f4136f6643..f4136f6643 100644 --- a/servers/physics_2d/godot_constraint_2d.h +++ b/modules/godot_physics_2d/godot_constraint_2d.h diff --git a/servers/physics_2d/godot_joints_2d.cpp b/modules/godot_physics_2d/godot_joints_2d.cpp index 5c76eb9dad..5c76eb9dad 100644 --- a/servers/physics_2d/godot_joints_2d.cpp +++ b/modules/godot_physics_2d/godot_joints_2d.cpp diff --git a/servers/physics_2d/godot_joints_2d.h b/modules/godot_physics_2d/godot_joints_2d.h index c6a1fdb692..c6a1fdb692 100644 --- a/servers/physics_2d/godot_joints_2d.h +++ b/modules/godot_physics_2d/godot_joints_2d.h diff --git a/servers/physics_2d/godot_physics_server_2d.cpp b/modules/godot_physics_2d/godot_physics_server_2d.cpp index 8df17992ea..8df17992ea 100644 --- a/servers/physics_2d/godot_physics_server_2d.cpp +++ b/modules/godot_physics_2d/godot_physics_server_2d.cpp diff --git a/servers/physics_2d/godot_physics_server_2d.h b/modules/godot_physics_2d/godot_physics_server_2d.h index 991cf67c95..991cf67c95 100644 --- a/servers/physics_2d/godot_physics_server_2d.h +++ b/modules/godot_physics_2d/godot_physics_server_2d.h diff --git a/servers/physics_2d/godot_shape_2d.cpp b/modules/godot_physics_2d/godot_shape_2d.cpp index d77b1a77e3..d77b1a77e3 100644 --- a/servers/physics_2d/godot_shape_2d.cpp +++ b/modules/godot_physics_2d/godot_shape_2d.cpp diff --git a/servers/physics_2d/godot_shape_2d.h b/modules/godot_physics_2d/godot_shape_2d.h index 28c69574a0..28c69574a0 100644 --- a/servers/physics_2d/godot_shape_2d.h +++ b/modules/godot_physics_2d/godot_shape_2d.h diff --git a/servers/physics_2d/godot_space_2d.cpp b/modules/godot_physics_2d/godot_space_2d.cpp index 2966818beb..2966818beb 100644 --- a/servers/physics_2d/godot_space_2d.cpp +++ b/modules/godot_physics_2d/godot_space_2d.cpp diff --git a/servers/physics_2d/godot_space_2d.h b/modules/godot_physics_2d/godot_space_2d.h index ded3b08d5b..ded3b08d5b 100644 --- a/servers/physics_2d/godot_space_2d.h +++ b/modules/godot_physics_2d/godot_space_2d.h diff --git a/servers/physics_2d/godot_step_2d.cpp b/modules/godot_physics_2d/godot_step_2d.cpp index bbaec8be2b..bbaec8be2b 100644 --- a/servers/physics_2d/godot_step_2d.cpp +++ b/modules/godot_physics_2d/godot_step_2d.cpp diff --git a/servers/physics_2d/godot_step_2d.h b/modules/godot_physics_2d/godot_step_2d.h index c08c6379de..c08c6379de 100644 --- a/servers/physics_2d/godot_step_2d.h +++ b/modules/godot_physics_2d/godot_step_2d.h diff --git a/modules/godot_physics_2d/register_types.cpp b/modules/godot_physics_2d/register_types.cpp new file mode 100644 index 0000000000..57422b1814 --- /dev/null +++ b/modules/godot_physics_2d/register_types.cpp @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* register_types.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "register_types.h" + +#include "godot_physics_server_2d.h" +#include "servers/physics_server_2d.h" +#include "servers/physics_server_2d_wrap_mt.h" + +static PhysicsServer2D *_createGodotPhysics2DCallback() { +#ifdef THREADS_ENABLED + bool using_threads = GLOBAL_GET("physics/2d/run_on_separate_thread"); +#else + bool using_threads = false; +#endif + + PhysicsServer2D *physics_server_2d = memnew(GodotPhysicsServer2D(using_threads)); + + return memnew(PhysicsServer2DWrapMT(physics_server_2d, using_threads)); +} + +void initialize_godot_physics_2d_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) { + return; + } + PhysicsServer2DManager::get_singleton()->register_server("GodotPhysics2D", callable_mp_static(_createGodotPhysics2DCallback)); + PhysicsServer2DManager::get_singleton()->set_default_server("GodotPhysics2D"); +} + +void uninitialize_godot_physics_2d_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) { + return; + } +} diff --git a/modules/godot_physics_2d/register_types.h b/modules/godot_physics_2d/register_types.h new file mode 100644 index 0000000000..1d2d1301b9 --- /dev/null +++ b/modules/godot_physics_2d/register_types.h @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* register_types.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 GODOT_PHYSICS_2D_REGISTER_TYPES_H +#define GODOT_PHYSICS_2D_REGISTER_TYPES_H + +#include "modules/register_module_types.h" + +void initialize_godot_physics_2d_module(ModuleInitializationLevel p_level); +void uninitialize_godot_physics_2d_module(ModuleInitializationLevel p_level); + +#endif // GODOT_PHYSICS_2D_REGISTER_TYPES_H diff --git a/modules/godot_physics_3d/SCsub b/modules/godot_physics_3d/SCsub new file mode 100644 index 0000000000..41a59cd24e --- /dev/null +++ b/modules/godot_physics_3d/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import('env') + +env.add_source_files(env.modules_sources, "*.cpp") + +SConscript("joints/SCsub") diff --git a/modules/godot_physics_3d/config.py b/modules/godot_physics_3d/config.py new file mode 100644 index 0000000000..a42f27fbe1 --- /dev/null +++ b/modules/godot_physics_3d/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return not env["disable_3d"] + + +def configure(env): + pass diff --git a/servers/physics_3d/gjk_epa.cpp b/modules/godot_physics_3d/gjk_epa.cpp index e5678914fe..e5678914fe 100644 --- a/servers/physics_3d/gjk_epa.cpp +++ b/modules/godot_physics_3d/gjk_epa.cpp diff --git a/servers/physics_3d/gjk_epa.h b/modules/godot_physics_3d/gjk_epa.h index 48fda9969f..48fda9969f 100644 --- a/servers/physics_3d/gjk_epa.h +++ b/modules/godot_physics_3d/gjk_epa.h diff --git a/servers/physics_3d/godot_area_3d.cpp b/modules/godot_physics_3d/godot_area_3d.cpp index d0b287b058..d0b287b058 100644 --- a/servers/physics_3d/godot_area_3d.cpp +++ b/modules/godot_physics_3d/godot_area_3d.cpp diff --git a/servers/physics_3d/godot_area_3d.h b/modules/godot_physics_3d/godot_area_3d.h index 2c1a782630..2c1a782630 100644 --- a/servers/physics_3d/godot_area_3d.h +++ b/modules/godot_physics_3d/godot_area_3d.h diff --git a/servers/physics_3d/godot_area_pair_3d.cpp b/modules/godot_physics_3d/godot_area_pair_3d.cpp index aaa96f5a28..aaa96f5a28 100644 --- a/servers/physics_3d/godot_area_pair_3d.cpp +++ b/modules/godot_physics_3d/godot_area_pair_3d.cpp diff --git a/servers/physics_3d/godot_area_pair_3d.h b/modules/godot_physics_3d/godot_area_pair_3d.h index a2c5df0f7a..a2c5df0f7a 100644 --- a/servers/physics_3d/godot_area_pair_3d.h +++ b/modules/godot_physics_3d/godot_area_pair_3d.h diff --git a/servers/physics_3d/godot_body_3d.cpp b/modules/godot_physics_3d/godot_body_3d.cpp index 669c4b985b..669c4b985b 100644 --- a/servers/physics_3d/godot_body_3d.cpp +++ b/modules/godot_physics_3d/godot_body_3d.cpp diff --git a/servers/physics_3d/godot_body_3d.h b/modules/godot_physics_3d/godot_body_3d.h index 81b668122a..81b668122a 100644 --- a/servers/physics_3d/godot_body_3d.h +++ b/modules/godot_physics_3d/godot_body_3d.h diff --git a/servers/physics_3d/godot_body_direct_state_3d.cpp b/modules/godot_physics_3d/godot_body_direct_state_3d.cpp index 0af746c68d..0af746c68d 100644 --- a/servers/physics_3d/godot_body_direct_state_3d.cpp +++ b/modules/godot_physics_3d/godot_body_direct_state_3d.cpp diff --git a/servers/physics_3d/godot_body_direct_state_3d.h b/modules/godot_physics_3d/godot_body_direct_state_3d.h index 8066050c9f..8066050c9f 100644 --- a/servers/physics_3d/godot_body_direct_state_3d.h +++ b/modules/godot_physics_3d/godot_body_direct_state_3d.h diff --git a/servers/physics_3d/godot_body_pair_3d.cpp b/modules/godot_physics_3d/godot_body_pair_3d.cpp index 84fae73616..84fae73616 100644 --- a/servers/physics_3d/godot_body_pair_3d.cpp +++ b/modules/godot_physics_3d/godot_body_pair_3d.cpp diff --git a/servers/physics_3d/godot_body_pair_3d.h b/modules/godot_physics_3d/godot_body_pair_3d.h index a8f5180dd5..a8f5180dd5 100644 --- a/servers/physics_3d/godot_body_pair_3d.h +++ b/modules/godot_physics_3d/godot_body_pair_3d.h diff --git a/servers/physics_3d/godot_broad_phase_3d.cpp b/modules/godot_physics_3d/godot_broad_phase_3d.cpp index ebd11fb51f..ebd11fb51f 100644 --- a/servers/physics_3d/godot_broad_phase_3d.cpp +++ b/modules/godot_physics_3d/godot_broad_phase_3d.cpp diff --git a/servers/physics_3d/godot_broad_phase_3d.h b/modules/godot_physics_3d/godot_broad_phase_3d.h index f70321be64..f70321be64 100644 --- a/servers/physics_3d/godot_broad_phase_3d.h +++ b/modules/godot_physics_3d/godot_broad_phase_3d.h diff --git a/servers/physics_3d/godot_broad_phase_3d_bvh.cpp b/modules/godot_physics_3d/godot_broad_phase_3d_bvh.cpp index 0faa56b52e..0faa56b52e 100644 --- a/servers/physics_3d/godot_broad_phase_3d_bvh.cpp +++ b/modules/godot_physics_3d/godot_broad_phase_3d_bvh.cpp diff --git a/servers/physics_3d/godot_broad_phase_3d_bvh.h b/modules/godot_physics_3d/godot_broad_phase_3d_bvh.h index 63968dea64..63968dea64 100644 --- a/servers/physics_3d/godot_broad_phase_3d_bvh.h +++ b/modules/godot_physics_3d/godot_broad_phase_3d_bvh.h diff --git a/servers/physics_3d/godot_collision_object_3d.cpp b/modules/godot_physics_3d/godot_collision_object_3d.cpp index 283614a43d..283614a43d 100644 --- a/servers/physics_3d/godot_collision_object_3d.cpp +++ b/modules/godot_physics_3d/godot_collision_object_3d.cpp diff --git a/servers/physics_3d/godot_collision_object_3d.h b/modules/godot_physics_3d/godot_collision_object_3d.h index bf28bcc45a..bf28bcc45a 100644 --- a/servers/physics_3d/godot_collision_object_3d.h +++ b/modules/godot_physics_3d/godot_collision_object_3d.h diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/modules/godot_physics_3d/godot_collision_solver_3d.cpp index db48111eea..db48111eea 100644 --- a/servers/physics_3d/godot_collision_solver_3d.cpp +++ b/modules/godot_physics_3d/godot_collision_solver_3d.cpp diff --git a/servers/physics_3d/godot_collision_solver_3d.h b/modules/godot_physics_3d/godot_collision_solver_3d.h index 36ea79576e..36ea79576e 100644 --- a/servers/physics_3d/godot_collision_solver_3d.h +++ b/modules/godot_physics_3d/godot_collision_solver_3d.h diff --git a/servers/physics_3d/godot_collision_solver_3d_sat.cpp b/modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp index c53c8481f4..c53c8481f4 100644 --- a/servers/physics_3d/godot_collision_solver_3d_sat.cpp +++ b/modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp diff --git a/servers/physics_3d/godot_collision_solver_3d_sat.h b/modules/godot_physics_3d/godot_collision_solver_3d_sat.h index 49fcab3933..49fcab3933 100644 --- a/servers/physics_3d/godot_collision_solver_3d_sat.h +++ b/modules/godot_physics_3d/godot_collision_solver_3d_sat.h diff --git a/servers/physics_3d/godot_constraint_3d.h b/modules/godot_physics_3d/godot_constraint_3d.h index a833aba93f..a833aba93f 100644 --- a/servers/physics_3d/godot_constraint_3d.h +++ b/modules/godot_physics_3d/godot_constraint_3d.h diff --git a/servers/physics_3d/godot_joint_3d.h b/modules/godot_physics_3d/godot_joint_3d.h index 3207723cb4..3207723cb4 100644 --- a/servers/physics_3d/godot_joint_3d.h +++ b/modules/godot_physics_3d/godot_joint_3d.h diff --git a/servers/physics_3d/godot_physics_server_3d.cpp b/modules/godot_physics_3d/godot_physics_server_3d.cpp index 8a7b4e0f07..6d0949acbe 100644 --- a/servers/physics_3d/godot_physics_server_3d.cpp +++ b/modules/godot_physics_3d/godot_physics_server_3d.cpp @@ -1629,8 +1629,6 @@ void GodotPhysicsServer3D::init() { } void GodotPhysicsServer3D::step(real_t p_step) { -#ifndef _3D_DISABLED - if (!active) { return; } @@ -1646,7 +1644,6 @@ void GodotPhysicsServer3D::step(real_t p_step) { active_objects += E->get_active_objects(); collision_pairs += E->get_collision_pairs(); } -#endif } void GodotPhysicsServer3D::sync() { @@ -1654,8 +1651,6 @@ void GodotPhysicsServer3D::sync() { } void GodotPhysicsServer3D::flush_queries() { -#ifndef _3D_DISABLED - if (!active) { return; } @@ -1703,7 +1698,6 @@ void GodotPhysicsServer3D::flush_queries() { values.push_front("physics_3d"); EngineDebugger::profiler_add_frame_data("servers", values); } -#endif } void GodotPhysicsServer3D::end_sync() { diff --git a/servers/physics_3d/godot_physics_server_3d.h b/modules/godot_physics_3d/godot_physics_server_3d.h index 040e673dcd..040e673dcd 100644 --- a/servers/physics_3d/godot_physics_server_3d.h +++ b/modules/godot_physics_3d/godot_physics_server_3d.h diff --git a/servers/physics_3d/godot_shape_3d.cpp b/modules/godot_physics_3d/godot_shape_3d.cpp index 70b6bcf19e..70b6bcf19e 100644 --- a/servers/physics_3d/godot_shape_3d.cpp +++ b/modules/godot_physics_3d/godot_shape_3d.cpp diff --git a/servers/physics_3d/godot_shape_3d.h b/modules/godot_physics_3d/godot_shape_3d.h index dbd58ead68..dbd58ead68 100644 --- a/servers/physics_3d/godot_shape_3d.h +++ b/modules/godot_physics_3d/godot_shape_3d.h diff --git a/servers/physics_3d/godot_soft_body_3d.cpp b/modules/godot_physics_3d/godot_soft_body_3d.cpp index 7284076a47..7284076a47 100644 --- a/servers/physics_3d/godot_soft_body_3d.cpp +++ b/modules/godot_physics_3d/godot_soft_body_3d.cpp diff --git a/servers/physics_3d/godot_soft_body_3d.h b/modules/godot_physics_3d/godot_soft_body_3d.h index e23f4bb9f5..e23f4bb9f5 100644 --- a/servers/physics_3d/godot_soft_body_3d.h +++ b/modules/godot_physics_3d/godot_soft_body_3d.h diff --git a/servers/physics_3d/godot_space_3d.cpp b/modules/godot_physics_3d/godot_space_3d.cpp index 9a6ba776b4..9a6ba776b4 100644 --- a/servers/physics_3d/godot_space_3d.cpp +++ b/modules/godot_physics_3d/godot_space_3d.cpp diff --git a/servers/physics_3d/godot_space_3d.h b/modules/godot_physics_3d/godot_space_3d.h index f476be5934..f476be5934 100644 --- a/servers/physics_3d/godot_space_3d.h +++ b/modules/godot_physics_3d/godot_space_3d.h diff --git a/servers/physics_3d/godot_step_3d.cpp b/modules/godot_physics_3d/godot_step_3d.cpp index d09a3b4e6d..d09a3b4e6d 100644 --- a/servers/physics_3d/godot_step_3d.cpp +++ b/modules/godot_physics_3d/godot_step_3d.cpp diff --git a/servers/physics_3d/godot_step_3d.h b/modules/godot_physics_3d/godot_step_3d.h index 1c9b0af422..1c9b0af422 100644 --- a/servers/physics_3d/godot_step_3d.h +++ b/modules/godot_physics_3d/godot_step_3d.h diff --git a/modules/godot_physics_3d/joints/SCsub b/modules/godot_physics_3d/joints/SCsub new file mode 100644 index 0000000000..5d93da5ecf --- /dev/null +++ b/modules/godot_physics_3d/joints/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import('env') + +env.add_source_files(env.modules_sources, "*.cpp") diff --git a/servers/physics_3d/joints/godot_cone_twist_joint_3d.cpp b/modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.cpp index 4091422789..4091422789 100644 --- a/servers/physics_3d/joints/godot_cone_twist_joint_3d.cpp +++ b/modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.cpp diff --git a/servers/physics_3d/joints/godot_cone_twist_joint_3d.h b/modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.h index 8b749e4914..f3b683a8f3 100644 --- a/servers/physics_3d/joints/godot_cone_twist_joint_3d.h +++ b/modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.h @@ -52,8 +52,8 @@ subject to the following restrictions: Written by: Marcus Hennix */ -#include "servers/physics_3d/godot_joint_3d.h" -#include "servers/physics_3d/joints/godot_jacobian_entry_3d.h" +#include "../godot_joint_3d.h" +#include "godot_jacobian_entry_3d.h" // GodotConeTwistJoint3D can be used to simulate ragdoll joints (upper arm, leg etc). class GodotConeTwistJoint3D : public GodotJoint3D { diff --git a/servers/physics_3d/joints/godot_generic_6dof_joint_3d.cpp b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp index 226f8a0f7f..226f8a0f7f 100644 --- a/servers/physics_3d/joints/godot_generic_6dof_joint_3d.cpp +++ b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp diff --git a/servers/physics_3d/joints/godot_generic_6dof_joint_3d.h b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h index 69a5266438..9ee6dd2791 100644 --- a/servers/physics_3d/joints/godot_generic_6dof_joint_3d.h +++ b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h @@ -35,8 +35,8 @@ Adapted to Godot from the Bullet library. */ -#include "servers/physics_3d/godot_joint_3d.h" -#include "servers/physics_3d/joints/godot_jacobian_entry_3d.h" +#include "../godot_joint_3d.h" +#include "godot_jacobian_entry_3d.h" /* Bullet Continuous Collision Detection and Physics Library diff --git a/servers/physics_3d/joints/godot_hinge_joint_3d.cpp b/modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp index 3d423f70e2..3d423f70e2 100644 --- a/servers/physics_3d/joints/godot_hinge_joint_3d.cpp +++ b/modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp diff --git a/servers/physics_3d/joints/godot_hinge_joint_3d.h b/modules/godot_physics_3d/joints/godot_hinge_joint_3d.h index eab60c1909..7f83509468 100644 --- a/servers/physics_3d/joints/godot_hinge_joint_3d.h +++ b/modules/godot_physics_3d/joints/godot_hinge_joint_3d.h @@ -35,8 +35,8 @@ Adapted to Godot from the Bullet library. */ -#include "servers/physics_3d/godot_joint_3d.h" -#include "servers/physics_3d/joints/godot_jacobian_entry_3d.h" +#include "../godot_joint_3d.h" +#include "godot_jacobian_entry_3d.h" /* Bullet Continuous Collision Detection and Physics Library diff --git a/servers/physics_3d/joints/godot_jacobian_entry_3d.h b/modules/godot_physics_3d/joints/godot_jacobian_entry_3d.h index d0c3c48ae6..d0c3c48ae6 100644 --- a/servers/physics_3d/joints/godot_jacobian_entry_3d.h +++ b/modules/godot_physics_3d/joints/godot_jacobian_entry_3d.h diff --git a/servers/physics_3d/joints/godot_pin_joint_3d.cpp b/modules/godot_physics_3d/joints/godot_pin_joint_3d.cpp index 05ae0839e4..05ae0839e4 100644 --- a/servers/physics_3d/joints/godot_pin_joint_3d.cpp +++ b/modules/godot_physics_3d/joints/godot_pin_joint_3d.cpp diff --git a/servers/physics_3d/joints/godot_pin_joint_3d.h b/modules/godot_physics_3d/joints/godot_pin_joint_3d.h index 2d19bcb626..62d3068e09 100644 --- a/servers/physics_3d/joints/godot_pin_joint_3d.h +++ b/modules/godot_physics_3d/joints/godot_pin_joint_3d.h @@ -35,8 +35,8 @@ Adapted to Godot from the Bullet library. */ -#include "servers/physics_3d/godot_joint_3d.h" -#include "servers/physics_3d/joints/godot_jacobian_entry_3d.h" +#include "../godot_joint_3d.h" +#include "godot_jacobian_entry_3d.h" /* Bullet Continuous Collision Detection and Physics Library diff --git a/servers/physics_3d/joints/godot_slider_joint_3d.cpp b/modules/godot_physics_3d/joints/godot_slider_joint_3d.cpp index b9dca94b37..b9dca94b37 100644 --- a/servers/physics_3d/joints/godot_slider_joint_3d.cpp +++ b/modules/godot_physics_3d/joints/godot_slider_joint_3d.cpp diff --git a/servers/physics_3d/joints/godot_slider_joint_3d.h b/modules/godot_physics_3d/joints/godot_slider_joint_3d.h index ad79fc51b0..99fabf8638 100644 --- a/servers/physics_3d/joints/godot_slider_joint_3d.h +++ b/modules/godot_physics_3d/joints/godot_slider_joint_3d.h @@ -35,8 +35,8 @@ Adapted to Godot from the Bullet library. */ -#include "servers/physics_3d/godot_joint_3d.h" -#include "servers/physics_3d/joints/godot_jacobian_entry_3d.h" +#include "../godot_joint_3d.h" +#include "godot_jacobian_entry_3d.h" /* Bullet Continuous Collision Detection and Physics Library diff --git a/modules/godot_physics_3d/register_types.cpp b/modules/godot_physics_3d/register_types.cpp new file mode 100644 index 0000000000..1b1690cf59 --- /dev/null +++ b/modules/godot_physics_3d/register_types.cpp @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* register_types.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "register_types.h" + +#include "godot_physics_server_3d.h" +#include "servers/physics_server_3d.h" +#include "servers/physics_server_3d_wrap_mt.h" + +static PhysicsServer3D *_createGodotPhysics3DCallback() { +#ifdef THREADS_ENABLED + bool using_threads = GLOBAL_GET("physics/3d/run_on_separate_thread"); +#else + bool using_threads = false; +#endif + + PhysicsServer3D *physics_server_3d = memnew(GodotPhysicsServer3D(using_threads)); + + return memnew(PhysicsServer3DWrapMT(physics_server_3d, using_threads)); +} + +void initialize_godot_physics_3d_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) { + return; + } + PhysicsServer3DManager::get_singleton()->register_server("GodotPhysics3D", callable_mp_static(_createGodotPhysics3DCallback)); + PhysicsServer3DManager::get_singleton()->set_default_server("GodotPhysics3D"); +} + +void uninitialize_godot_physics_3d_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) { + return; + } +} diff --git a/modules/godot_physics_3d/register_types.h b/modules/godot_physics_3d/register_types.h new file mode 100644 index 0000000000..998fb4a1ee --- /dev/null +++ b/modules/godot_physics_3d/register_types.h @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* register_types.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 GODOT_PHYSICS_3D_REGISTER_TYPES_H +#define GODOT_PHYSICS_3D_REGISTER_TYPES_H + +#include "modules/register_module_types.h" + +void initialize_godot_physics_3d_module(ModuleInitializationLevel p_level); +void uninitialize_godot_physics_3d_module(ModuleInitializationLevel p_level); + +#endif // GODOT_PHYSICS_3D_REGISTER_TYPES_H diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index e84b4e92c7..788b46ab9a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Reflection; using GodotTools.Build; using GodotTools.Ides; using GodotTools.Ides.Rider; @@ -701,6 +702,23 @@ namespace GodotTools private static IntPtr InternalCreateInstance(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize) { Internal.Initialize(unmanagedCallbacks, unmanagedCallbacksSize); + + var populateConstructorMethod = + AppDomain.CurrentDomain + .GetAssemblies() + .First(x => x.GetName().Name == "GodotSharpEditor") + .GetType("Godot.EditorConstructors")? + .GetMethod("AddEditorConstructors", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + + if (populateConstructorMethod == null) + { + throw new MissingMethodException("Godot.EditorConstructors", + "AddEditorConstructors"); + } + + populateConstructorMethod.Invoke(null, null); + return new GodotSharpEditor().NativeInstance; } } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index d0adf39fb2..a467aae2e9 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -77,6 +77,10 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { #define BINDINGS_GLOBAL_SCOPE_CLASS "GD" #define BINDINGS_NATIVE_NAME_FIELD "NativeName" +#define BINDINGS_CLASS_CONSTRUCTOR "Constructors" +#define BINDINGS_CLASS_CONSTRUCTOR_EDITOR "EditorConstructors" +#define BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY "BuiltInMethodConstructors" + #define CS_PARAM_MEMORYOWN "memoryOwn" #define CS_PARAM_METHODBIND "method" #define CS_PARAM_INSTANCE "ptr" @@ -1737,6 +1741,69 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) { compile_items.push_back(output_file); } + // Generate source file for built-in type constructor dictionary. + + { + StringBuilder cs_built_in_ctors_content; + + cs_built_in_ctors_content.append("namespace " BINDINGS_NAMESPACE ";\n\n"); + cs_built_in_ctors_content.append("using System;\n" + "using System.Collections.Generic;\n" + "\n"); + cs_built_in_ctors_content.append("internal static class " BINDINGS_CLASS_CONSTRUCTOR "\n{"); + + cs_built_in_ctors_content.append(MEMBER_BEGIN "internal static readonly Dictionary<string, Func<IntPtr, GodotObject>> " BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY ";\n"); + + cs_built_in_ctors_content.append(MEMBER_BEGIN "public static GodotObject Invoke(string nativeTypeNameStr, IntPtr nativeObjectPtr)\n"); + cs_built_in_ctors_content.append(INDENT1 OPEN_BLOCK); + cs_built_in_ctors_content.append(INDENT2 "if (!" BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY ".TryGetValue(nativeTypeNameStr, out var constructor))\n"); + cs_built_in_ctors_content.append(INDENT3 "throw new InvalidOperationException(\"Wrapper class not found for type: \" + nativeTypeNameStr);\n"); + cs_built_in_ctors_content.append(INDENT2 "return constructor(nativeObjectPtr);\n"); + cs_built_in_ctors_content.append(INDENT1 CLOSE_BLOCK); + + cs_built_in_ctors_content.append(MEMBER_BEGIN "static " BINDINGS_CLASS_CONSTRUCTOR "()\n"); + cs_built_in_ctors_content.append(INDENT1 OPEN_BLOCK); + cs_built_in_ctors_content.append(INDENT2 BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY " = new();\n"); + + for (const KeyValue<StringName, TypeInterface> &E : obj_types) { + const TypeInterface &itype = E.value; + + if (itype.api_type != ClassDB::API_CORE || itype.is_singleton_instance) { + continue; + } + + if (itype.is_deprecated) { + cs_built_in_ctors_content.append("#pragma warning disable CS0618\n"); + } + + cs_built_in_ctors_content.append(INDENT2 BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY ".Add(\""); + cs_built_in_ctors_content.append(itype.name); + cs_built_in_ctors_content.append("\", " CS_PARAM_INSTANCE " => new "); + cs_built_in_ctors_content.append(itype.proxy_name); + if (itype.is_singleton && !itype.is_compat_singleton) { + cs_built_in_ctors_content.append("Instance"); + } + cs_built_in_ctors_content.append("(" CS_PARAM_INSTANCE "));\n"); + + if (itype.is_deprecated) { + cs_built_in_ctors_content.append("#pragma warning restore CS0618\n"); + } + } + + cs_built_in_ctors_content.append(INDENT1 CLOSE_BLOCK); + + cs_built_in_ctors_content.append(CLOSE_BLOCK); + + String constructors_file = path::join(base_gen_dir, BINDINGS_CLASS_CONSTRUCTOR ".cs"); + Error err = _save_file(constructors_file, cs_built_in_ctors_content); + + if (err != OK) { + return err; + } + + compile_items.push_back(constructors_file); + } + // Generate native calls StringBuilder cs_icalls_content; @@ -1844,6 +1911,57 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) { compile_items.push_back(output_file); } + // Generate source file for editor type constructor dictionary. + + { + StringBuilder cs_built_in_ctors_content; + + cs_built_in_ctors_content.append("namespace " BINDINGS_NAMESPACE ";\n\n"); + cs_built_in_ctors_content.append("internal static class " BINDINGS_CLASS_CONSTRUCTOR_EDITOR "\n{"); + + cs_built_in_ctors_content.append(MEMBER_BEGIN "private static void AddEditorConstructors()\n"); + cs_built_in_ctors_content.append(INDENT1 OPEN_BLOCK); + cs_built_in_ctors_content.append(INDENT2 "var builtInMethodConstructors = " BINDINGS_CLASS_CONSTRUCTOR "." BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY ";\n"); + + for (const KeyValue<StringName, TypeInterface> &E : obj_types) { + const TypeInterface &itype = E.value; + + if (itype.api_type != ClassDB::API_EDITOR || itype.is_singleton_instance) { + continue; + } + + if (itype.is_deprecated) { + cs_built_in_ctors_content.append("#pragma warning disable CS0618\n"); + } + + cs_built_in_ctors_content.append(INDENT2 "builtInMethodConstructors.Add(\""); + cs_built_in_ctors_content.append(itype.name); + cs_built_in_ctors_content.append("\", " CS_PARAM_INSTANCE " => new "); + cs_built_in_ctors_content.append(itype.proxy_name); + if (itype.is_singleton && !itype.is_compat_singleton) { + cs_built_in_ctors_content.append("Instance"); + } + cs_built_in_ctors_content.append("(" CS_PARAM_INSTANCE "));\n"); + + if (itype.is_deprecated) { + cs_built_in_ctors_content.append("#pragma warning restore CS0618\n"); + } + } + + cs_built_in_ctors_content.append(INDENT1 CLOSE_BLOCK); + + cs_built_in_ctors_content.append(CLOSE_BLOCK); + + String constructors_file = path::join(base_gen_dir, BINDINGS_CLASS_CONSTRUCTOR_EDITOR ".cs"); + Error err = _save_file(constructors_file, cs_built_in_ctors_content); + + if (err != OK) { + return err; + } + + compile_items.push_back(constructors_file); + } + // Generate native calls StringBuilder cs_icalls_content; @@ -2210,6 +2328,15 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str << CLOSE_BLOCK_L2 CLOSE_BLOCK_L1; } + output << MEMBER_BEGIN "internal " << itype.proxy_name << "(IntPtr " CS_PARAM_INSTANCE ") : this(" + << (itype.memory_own ? "true" : "false") << ")\n" OPEN_BLOCK_L1 + << INDENT2 "NativePtr = " CS_PARAM_INSTANCE ";\n" + << INDENT2 "unsafe\n" INDENT2 OPEN_BLOCK + << INDENT3 "ConstructAndInitialize(null, " + << BINDINGS_NATIVE_NAME_FIELD ", CachedType, refCounted: " + << (itype.is_ref_counted ? "true" : "false") << ");\n" + << CLOSE_BLOCK_L2 CLOSE_BLOCK_L1; + // Add.. em.. trick constructor. Sort of. output.append(MEMBER_BEGIN "internal "); output.append(itype.proxy_name); @@ -2934,11 +3061,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output) { String arguments_sig; - String delegate_type_params; - - if (!p_isignal.arguments.is_empty()) { - delegate_type_params += "<"; - } // Retrieve information from the arguments const ArgumentInterface &first = p_isignal.arguments.front()->get(); @@ -2959,18 +3081,13 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf if (&iarg != &first) { arguments_sig += ", "; - delegate_type_params += ", "; } - arguments_sig += arg_type->cs_type; + String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters); + + arguments_sig += arg_cs_type; arguments_sig += " "; arguments_sig += iarg.name; - - delegate_type_params += arg_type->cs_type; - } - - if (!p_isignal.arguments.is_empty()) { - delegate_type_params += ">"; } // Generate signal @@ -3019,8 +3136,14 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf p_output << ", "; } - p_output << sformat(arg_type->cs_variant_to_managed, - "args[" + itos(idx) + "]", arg_type->cs_type, arg_type->name); + if (arg_type->cname == name_cache.type_Array_generic || arg_type->cname == name_cache.type_Dictionary_generic) { + String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters); + + p_output << "new " << arg_cs_type << "(" << sformat(arg_type->cs_variant_to_managed, "args[" + itos(idx) + "]", arg_type->cs_type, arg_type->name) << ")"; + } else { + p_output << sformat(arg_type->cs_variant_to_managed, + "args[" + itos(idx) + "]", arg_type->cs_type, arg_type->name); + } idx++; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 901700067d..91d49854c7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -93,27 +93,15 @@ namespace Godot.Bridge internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName, IntPtr godotObject) { - // TODO: Optimize with source generators and delegate pointers. - try { using var stringName = StringName.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(nativeTypeName))); string nativeTypeNameStr = stringName.ToString(); - Type nativeType = TypeGetProxyClass(nativeTypeNameStr) ?? throw new InvalidOperationException( - "Wrapper class not found for type: " + nativeTypeNameStr); - var obj = (GodotObject)FormatterServices.GetUninitializedObject(nativeType); + var instance = Constructors.Invoke(nativeTypeNameStr, godotObject); - var ctor = nativeType.GetConstructor( - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, - null, Type.EmptyTypes, null); - - obj.NativePtr = godotObject; - - _ = ctor!.Invoke(obj, null); - - return GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(obj)); + return GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(instance)); } catch (Exception e) { @@ -308,66 +296,6 @@ namespace Godot.Bridge } } - private static Type? TypeGetProxyClass(string nativeTypeNameStr) - { - // Performance is not critical here as this will be replaced with a generated dictionary. - - if (nativeTypeNameStr[0] == '_') - nativeTypeNameStr = nativeTypeNameStr.Substring(1); - - Type? wrapperType = typeof(GodotObject).Assembly.GetType("Godot." + nativeTypeNameStr); - - if (wrapperType == null) - { - wrapperType = GetTypeByGodotClassAttr(typeof(GodotObject).Assembly, nativeTypeNameStr); - } - - if (wrapperType == null) - { - var editorAssembly = AppDomain.CurrentDomain.GetAssemblies() - .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor"); - - if (editorAssembly != null) - { - wrapperType = editorAssembly.GetType("Godot." + nativeTypeNameStr); - - if (wrapperType == null) - { - wrapperType = GetTypeByGodotClassAttr(editorAssembly, nativeTypeNameStr); - } - } - } - - static Type? GetTypeByGodotClassAttr(Assembly assembly, string nativeTypeNameStr) - { - var types = assembly.GetTypes(); - foreach (var type in types) - { - var attr = type.GetCustomAttribute<GodotClassNameAttribute>(); - if (attr?.Name == nativeTypeNameStr) - { - return type; - } - } - return null; - } - - static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed; - - if (wrapperType != null && IsStatic(wrapperType)) - { - // A static class means this is a Godot singleton class. Try to get the Instance proxy type. - wrapperType = TypeGetProxyClass($"{wrapperType.Name}Instance"); - if (wrapperType == null) - { - // Otherwise, fallback to GodotObject. - return typeof(GodotObject); - } - } - - return wrapperType; - } - // Called from GodotPlugins // ReSharper disable once UnusedMember.Local public static void LookupScriptsInAssembly(Assembly assembly) @@ -732,15 +660,7 @@ namespace Godot.Bridge { Type native = GodotObject.InternalGetClassNativeBase(scriptType); - string typeName = scriptType.Name; - if (scriptType.IsGenericType) - { - var sb = new StringBuilder(); - AppendTypeName(sb, scriptType); - typeName = sb.ToString(); - } - - godot_string className = Marshaling.ConvertStringToNative(typeName); + godot_string className = Marshaling.ConvertStringToNative(ReflectionUtils.ConstructTypeName(scriptType)); bool isTool = scriptType.IsDefined(typeof(ToolAttribute), inherit: false); @@ -773,24 +693,6 @@ namespace Godot.Bridge outTypeInfo->IsGenericTypeDefinition = scriptType.IsGenericTypeDefinition.ToGodotBool(); outTypeInfo->IsConstructedGenericType = scriptType.IsConstructedGenericType.ToGodotBool(); - static void AppendTypeName(StringBuilder sb, Type type) - { - sb.Append(type.Name); - if (type.IsGenericType) - { - sb.Append('<'); - for (int i = 0; i < type.GenericTypeArguments.Length; i++) - { - Type typeArg = type.GenericTypeArguments[i]; - AppendTypeName(sb, typeArg); - if (i != type.GenericTypeArguments.Length - 1) - { - sb.Append(", "); - } - } - sb.Append('>'); - } - } } [UnmanagedCallersOnly] @@ -1104,7 +1006,7 @@ namespace Godot.Bridge interopProperties[i] = interopProperty; } - using godot_string currentClassName = Marshaling.ConvertStringToNative(type.Name); + using godot_string currentClassName = Marshaling.ConvertStringToNative(ReflectionUtils.ConstructTypeName(type)); addPropInfoFunc(scriptPtr, ¤tClassName, interopProperties, length); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs index c094eaed77..a429931399 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs @@ -29,6 +29,17 @@ namespace Godot } } + internal GodotObject(IntPtr nativePtr) : this(false) + { + // NativePtr must be non-zero before calling ConstructAndInitialize to avoid invoking the constructor NativeCtor. + // We don't want to invoke the constructor, because we already have a constructed instance in nativePtr. + NativePtr = nativePtr; + unsafe + { + ConstructAndInitialize(NativeCtor, NativeName, _cachedType, refCounted: false); + } + } + internal unsafe void ConstructAndInitialize( delegate* unmanaged<godot_bool, IntPtr> nativeCtor, StringName nativeName, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs index ee605f8d8f..27989b5c81 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using System.Text; #nullable enable @@ -7,10 +10,186 @@ namespace Godot; internal class ReflectionUtils { + private static readonly HashSet<Type>? _tupleTypeSet; + private static readonly Dictionary<Type, string>? _builtinTypeNameDictionary; + private static readonly bool _isEditorHintCached; + + static ReflectionUtils() + { + _isEditorHintCached = Engine.IsEditorHint(); + if (!_isEditorHintCached) + { + return; + } + + _tupleTypeSet = new HashSet<Type> + { + // ValueTuple with only one element should be treated as normal generic type. + //typeof(ValueTuple<>), + typeof(ValueTuple<,>), + typeof(ValueTuple<,,>), + typeof(ValueTuple<,,,>), + typeof(ValueTuple<,,,,>), + typeof(ValueTuple<,,,,,>), + typeof(ValueTuple<,,,,,,>), + typeof(ValueTuple<,,,,,,,>), + }; + + _builtinTypeNameDictionary ??= new Dictionary<Type, string> + { + { typeof(sbyte), "sbyte" }, + { typeof(byte), "byte" }, + { typeof(short), "short" }, + { typeof(ushort), "ushort" }, + { typeof(int), "int" }, + { typeof(uint), "uint" }, + { typeof(long), "long" }, + { typeof(ulong), "ulong" }, + { typeof(nint), "nint" }, + { typeof(nuint), "nuint" }, + { typeof(float), "float" }, + { typeof(double), "double" }, + { typeof(decimal), "decimal" }, + { typeof(bool), "bool" }, + { typeof(char), "char" }, + { typeof(string), "string" }, + { typeof(object), "object" }, + }; + } + public static Type? FindTypeInLoadedAssemblies(string assemblyName, string typeFullName) { return AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.GetName().Name == assemblyName)? .GetType(typeFullName); } + + public static string ConstructTypeName(Type type) + { + if (!_isEditorHintCached) + { + return type.Name; + } + + if (type is { IsArray: false, IsGenericType: false }) + { + return GetSimpleTypeName(type); + } + + var typeNameBuilder = new StringBuilder(); + AppendType(typeNameBuilder, type); + return typeNameBuilder.ToString(); + + static void AppendType(StringBuilder sb, Type type) + { + if (type.IsArray) + { + AppendArray(sb, type); + } + else if (type.IsGenericType) + { + AppendGeneric(sb, type); + } + else + { + sb.Append(GetSimpleTypeName(type)); + } + } + + static void AppendArray(StringBuilder sb, Type type) + { + // Append inner most non-array element. + var elementType = type.GetElementType()!; + while (elementType.IsArray) + { + elementType = elementType.GetElementType()!; + } + + AppendType(sb, elementType); + // Append brackets. + AppendArrayBrackets(sb, type); + + static void AppendArrayBrackets(StringBuilder sb, Type type) + { + while (type != null && type.IsArray) + { + int rank = type.GetArrayRank(); + sb.Append('['); + sb.Append(',', rank - 1); + sb.Append(']'); + type = type.GetElementType(); + } + } + } + + static void AppendGeneric(StringBuilder sb, Type type) + { + var genericArgs = type.GenericTypeArguments; + var genericDefinition = type.GetGenericTypeDefinition(); + + // Nullable<T> + if (genericDefinition == typeof(Nullable<>)) + { + AppendType(sb, genericArgs[0]); + sb.Append('?'); + return; + } + + // ValueTuple + Debug.Assert(_tupleTypeSet != null); + if (_tupleTypeSet.Contains(genericDefinition)) + { + sb.Append('('); + while (true) + { + // We assume that ValueTuple has 1~8 elements. + // And the 8th element (TRest) is always another ValueTuple. + + // This is a hard coded tuple element length check. + if (genericArgs.Length != 8) + { + AppendParamTypes(sb, genericArgs); + break; + } + else + { + AppendParamTypes(sb, genericArgs.AsSpan(0, 7)); + sb.Append(", "); + + // TRest should be a ValueTuple! + var nextTuple = genericArgs[7]; + + genericArgs = nextTuple.GenericTypeArguments; + } + } + sb.Append(')'); + return; + } + + // Normal generic + var typeName = type.Name.AsSpan(); + sb.Append(typeName[..typeName.LastIndexOf('`')]); + sb.Append('<'); + AppendParamTypes(sb, genericArgs); + sb.Append('>'); + + static void AppendParamTypes(StringBuilder sb, ReadOnlySpan<Type> genericArgs) + { + int n = genericArgs.Length - 1; + for (int i = 0; i < n; i += 1) + { + AppendType(sb, genericArgs[i]); + sb.Append(", "); + } + + AppendType(sb, genericArgs[n]); + } + } + + static string GetSimpleTypeName(Type type) + { + Debug.Assert(_builtinTypeNameDictionary != null); + return _builtinTypeNameDictionary.TryGetValue(type, out string? name) ? name : type.Name; + } + } } diff --git a/modules/multiplayer/editor/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp index 3a51712c70..f5f20d6931 100644 --- a/modules/multiplayer/editor/editor_network_profiler.cpp +++ b/modules/multiplayer/editor/editor_network_profiler.cpp @@ -193,6 +193,9 @@ void EditorNetworkProfiler::_update_button_text() { } void EditorNetworkProfiler::started() { + _clear_pressed(); + activate->set_disabled(false); + if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_network_profiler", false)) { set_profiling(true); refresh_timer->start(); @@ -200,6 +203,7 @@ void EditorNetworkProfiler::started() { } void EditorNetworkProfiler::stopped() { + activate->set_disabled(true); set_profiling(false); refresh_timer->stop(); } @@ -218,6 +222,7 @@ void EditorNetworkProfiler::_clear_pressed() { set_bandwidth(0, 0); refresh_rpc_data(); refresh_replication_data(); + clear_button->set_disabled(true); } void EditorNetworkProfiler::_autostart_toggled(bool p_toggled_on) { @@ -235,6 +240,9 @@ void EditorNetworkProfiler::_replication_button_clicked(TreeItem *p_item, int p_ } void EditorNetworkProfiler::add_rpc_frame_data(const RPCNodeInfo &p_frame) { + if (clear_button->is_disabled()) { + clear_button->set_disabled(false); + } dirty = true; if (!rpc_data.has(p_frame.node)) { rpc_data.insert(p_frame.node, p_frame); @@ -251,6 +259,9 @@ void EditorNetworkProfiler::add_rpc_frame_data(const RPCNodeInfo &p_frame) { } void EditorNetworkProfiler::add_sync_frame_data(const SyncInfo &p_frame) { + if (clear_button->is_disabled()) { + clear_button->set_disabled(false); + } dirty = true; if (!sync_data.has(p_frame.synchronizer)) { sync_data[p_frame.synchronizer] = p_frame; @@ -292,11 +303,13 @@ EditorNetworkProfiler::EditorNetworkProfiler() { activate = memnew(Button); activate->set_toggle_mode(true); activate->set_text(TTR("Start")); + activate->set_disabled(true); activate->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_activate_pressed)); hb->add_child(activate); clear_button = memnew(Button); clear_button->set_text(TTR("Clear")); + clear_button->set_disabled(true); clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_clear_pressed)); hb->add_child(clear_button); diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml index 338d632524..813c9d582e 100644 --- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml +++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml @@ -178,6 +178,15 @@ [param layer] is a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct. </description> </method> + <method name="_set_android_surface_swapchain_create_info_and_get_next_pointer" qualifiers="virtual"> + <return type="int" /> + <param index="0" name="property_values" type="Dictionary" /> + <param index="1" name="next_pointer" type="void*" /> + <description> + Adds additional data structures to Android surface swapchains created by [OpenXRCompositionLayer]. + [param property_values] contains the values of the properties returned by [method _get_viewport_composition_layer_extension_properties]. + </description> + </method> <method name="_set_hand_joint_locations_and_get_next_pointer" qualifiers="virtual"> <return type="int" /> <param index="0" name="hand_index" type="int" /> diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp index 83e45ffe7f..dc30b95b27 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp +++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp @@ -144,7 +144,6 @@ bool OpenXRCompositionLayerExtension::create_android_surface_swapchain(XrSwapcha OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); ERR_FAIL_NULL_V(openxr_api, false); - // @todo We need a way to add to the next pointer chain. XrResult result = xrCreateSwapchainAndroidSurfaceKHR(openxr_api->get_session(), p_info, r_swapchain, r_surface); if (XR_FAILED(result)) { print_line("OpenXR: Failed to create Android surface swapchain [", openxr_api->get_error_string(result), "]"); @@ -254,11 +253,19 @@ void OpenXRViewportCompositionLayerProvider::create_android_surface() { ERR_FAIL_COND(android_surface.swapchain != XR_NULL_HANDLE || android_surface.surface.is_valid()); ERR_FAIL_COND(!openxr_api || !openxr_api->is_running()); + void *next_pointer = nullptr; + for (OpenXRExtensionWrapper *wrapper : openxr_api->get_registered_extension_wrappers()) { + void *np = wrapper->set_android_surface_swapchain_create_info_and_get_next_pointer(extension_property_values, next_pointer); + if (np != nullptr) { + next_pointer = np; + } + } + // The XR_FB_android_surface_swapchain_create extension mandates that format, sampleCount, // faceCount, arraySize, and mipCount must be zero. XrSwapchainCreateInfo info = { XR_TYPE_SWAPCHAIN_CREATE_INFO, // type - nullptr, // next + next_pointer, // next 0, // createFlags XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, // usageFlags 0, // format diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index 09a9556dfa..95b537d1b4 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -97,10 +97,11 @@ public: virtual void on_state_loss_pending() {} // `on_state_loss_pending` is called when the OpenXR session state is changed to loss pending. virtual void on_state_exiting() {} // `on_state_exiting` is called when the OpenXR session state is changed to exiting. - virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) { return p_next_pointer; } // Add additional data structures to composition layers created via OpenXRCompositionLayer. + virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const Dictionary &p_property_values, void *p_next_pointer) { return p_next_pointer; } // Add additional data structures to composition layers created via OpenXRCompositionLayer. virtual void on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) {} // `on_viewport_composition_layer_destroyed` is called when a composition layer created via OpenXRCompositionLayer is destroyed. virtual void get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list) {} // Get additional property definitions for OpenXRCompositionLayer. virtual Dictionary get_viewport_composition_layer_extension_property_defaults() { return Dictionary(); } // Get the default values for the additional property definitions for OpenXRCompositionLayer. + virtual void *set_android_surface_swapchain_create_info_and_get_next_pointer(const Dictionary &p_property_values, void *p_next_pointer) { return p_next_pointer; } // `on_event_polled` is called when there is an OpenXR event to process. // Should return true if the event was handled, false otherwise. diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp index e09ca484d5..07ca476421 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp @@ -65,6 +65,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() { GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_properties); GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_property_defaults); GDVIRTUAL_BIND(_on_viewport_composition_layer_destroyed, "layer"); + GDVIRTUAL_BIND(_set_android_surface_swapchain_create_info_and_get_next_pointer, "property_values", "next_pointer"); ClassDB::bind_method(D_METHOD("get_openxr_api"), &OpenXRExtensionWrapperExtension::get_openxr_api); ClassDB::bind_method(D_METHOD("register_extension_wrapper"), &OpenXRExtensionWrapperExtension::register_extension_wrapper); @@ -249,7 +250,7 @@ bool OpenXRExtensionWrapperExtension::on_event_polled(const XrEventDataBuffer &p return false; } -void *OpenXRExtensionWrapperExtension::set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) { +void *OpenXRExtensionWrapperExtension::set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const Dictionary &p_property_values, void *p_next_pointer) { uint64_t pointer = 0; if (GDVIRTUAL_CALL(_set_viewport_composition_layer_and_get_next_pointer, GDExtensionConstPtr<void>(p_layer), p_property_values, GDExtensionPtr<void>(p_next_pointer), pointer)) { @@ -279,6 +280,16 @@ Dictionary OpenXRExtensionWrapperExtension::get_viewport_composition_layer_exten return property_defaults; } +void *OpenXRExtensionWrapperExtension::set_android_surface_swapchain_create_info_and_get_next_pointer(const Dictionary &p_property_values, void *p_next_pointer) { + uint64_t pointer = 0; + + if (GDVIRTUAL_CALL(_set_android_surface_swapchain_create_info_and_get_next_pointer, p_property_values, GDExtensionPtr<void>(p_next_pointer), pointer)) { + return reinterpret_cast<void *>(pointer); + } + + return p_next_pointer; +} + Ref<OpenXRAPIExtension> OpenXRExtensionWrapperExtension::get_openxr_api() { return openxr_api; } diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h index e37853903b..5cdf288c93 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h @@ -121,15 +121,17 @@ public: GDVIRTUAL1R(bool, _on_event_polled, GDExtensionConstPtr<void>); - virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) override; + virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const Dictionary &p_property_values, void *p_next_pointer) override; virtual void on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) override; virtual void get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list) override; virtual Dictionary get_viewport_composition_layer_extension_property_defaults() override; + virtual void *set_android_surface_swapchain_create_info_and_get_next_pointer(const Dictionary &p_property_values, void *p_next_pointer) override; GDVIRTUAL3R(uint64_t, _set_viewport_composition_layer_and_get_next_pointer, GDExtensionConstPtr<void>, Dictionary, GDExtensionPtr<void>); GDVIRTUAL1(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr<void>); GDVIRTUAL0R(TypedArray<Dictionary>, _get_viewport_composition_layer_extension_properties); GDVIRTUAL0R(Dictionary, _get_viewport_composition_layer_extension_property_defaults); + GDVIRTUAL2R(uint64_t, _set_android_surface_swapchain_create_info_and_get_next_pointer, Dictionary, GDExtensionPtr<void>); Ref<OpenXRAPIExtension> get_openxr_api(); diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 73b6f6c1c9..c67be5a2b3 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -1247,7 +1247,7 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) { return false; } - set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Main depth swapchain"); + set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain()), "Main depth swapchain"); } // We create our velocity swapchain if: diff --git a/modules/raycast/godot_update_embree.py b/modules/raycast/godot_update_embree.py index c179060365..c4fff330c9 100644 --- a/modules/raycast/godot_update_embree.py +++ b/modules/raycast/godot_update_embree.py @@ -4,8 +4,8 @@ import re import shutil import stat import subprocess -from types import TracebackType -from typing import Any, Callable, Tuple, Type +import sys +from typing import Any, Callable git_tag = "v4.3.1" @@ -100,9 +100,7 @@ subprocess.run(["git", "checkout", git_tag]) commit_hash = str(subprocess.check_output(["git", "rev-parse", "HEAD"], universal_newlines=True)).strip() -def on_rm_error( - function: Callable[..., Any], path: str, excinfo: Tuple[Type[Exception], Exception, TracebackType] -) -> None: +def on_rm_error(function: Callable[..., Any], path: str, excinfo: Exception) -> None: """ Error handler for `shutil.rmtree()`. @@ -113,10 +111,12 @@ def on_rm_error( os.unlink(path) -# 3.12 Python and beyond should replace `onerror` with `onexc`. # We remove the .git directory because it contains # a lot of read-only files that are problematic on Windows. -shutil.rmtree(".git", onerror=on_rm_error) +if sys.version_info >= (3, 12): + shutil.rmtree(".git", onexc=on_rm_error) +else: + shutil.rmtree(".git", onerror=on_rm_error) # type: ignore all_files = set(cpp_files) diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp index 6bdb261b50..4305bf842a 100644 --- a/modules/upnp/upnp.cpp +++ b/modules/upnp/upnp.cpp @@ -131,7 +131,11 @@ void UPNP::parse_igd(Ref<UPNPDevice> dev, UPNPDev *devlist) { GetUPNPUrls(&urls, &data, dev->get_description_url().utf8().get_data(), 0); char addr[16]; +#if MINIUPNPC_API_VERSION >= 18 + int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16, nullptr, 0); +#else int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16); +#endif if (i != 1) { FreeUPNPUrls(&urls); diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index a36748efc1..3fbbc263a0 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -1344,20 +1344,28 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win if (rendering_context->initialize() != OK) { memdelete(rendering_context); rendering_context = nullptr; - r_error = ERR_CANT_CREATE; + bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3"); + if (fallback_to_opengl3 && rendering_driver != "opengl3") { + WARN_PRINT("Your video card drivers seem not to support the required Vulkan version, switching to OpenGL 3."); + rendering_driver = "opengl3"; + OS::get_singleton()->set_current_rendering_method("gl_compatibility"); + OS::get_singleton()->set_current_rendering_driver_name(rendering_driver); + } else { + r_error = ERR_CANT_CREATE; - if (p_rendering_driver == "vulkan") { - OS::get_singleton()->alert( - vformat("Your video card drivers seem not to support the required Vulkan version.\n\n" - "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n" - "You can enable the OpenGL 3 driver by starting the engine from the\n" - "command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n" - "If you recently updated your video card drivers, try rebooting.", - executable_name), - "Unable to initialize Vulkan video driver"); - } + if (p_rendering_driver == "vulkan") { + OS::get_singleton()->alert( + vformat("Your video card drivers seem not to support the required Vulkan version.\n\n" + "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n" + "You can enable the OpenGL 3 driver by starting the engine from the\n" + "command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n" + "If you recently updated your video card drivers, try rebooting.", + executable_name), + "Unable to initialize Vulkan video driver"); + } - ERR_FAIL_MSG(vformat("Could not initialize %s", rendering_driver)); + ERR_FAIL_MSG(vformat("Could not initialize %s", rendering_driver)); + } } driver_found = true; diff --git a/platform/windows/detect.py b/platform/windows/detect.py index db4c743595..9adab7284c 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -620,18 +620,16 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") sys.exit(255) - env.Append(CCFLAGS=["-flto=thin"]) - env.Append(LINKFLAGS=["-flto=thin"]) + env.AppendUnique(CCFLAGS=["-flto=thin"]) elif env["use_llvm"]: - env.Append(CCFLAGS=["-flto"]) - env.Append(LINKFLAGS=["-flto"]) + env.AppendUnique(CCFLAGS=["-flto"]) else: env.AppendUnique(CCFLAGS=["/GL"]) - env.AppendUnique(ARFLAGS=["/LTCG"]) - if env["progress"]: - env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"]) - else: - env.AppendUnique(LINKFLAGS=["/LTCG"]) + if env["progress"]: + env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"]) + else: + env.AppendUnique(LINKFLAGS=["/LTCG"]) + env.AppendUnique(ARFLAGS=["/LTCG"]) if vcvars_msvc_config: env.Prepend(CPPPATH=[p for p in str(os.getenv("INCLUDE")).split(";")]) diff --git a/pyproject.toml b/pyproject.toml index 59b6d09a03..78c8d8c20e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ warn_unreachable = true namespace_packages = true explicit_package_bases = true exclude = ["thirdparty/"] +python_version = "3.8" [tool.ruff] extend-exclude = ["thirdparty"] @@ -69,6 +70,7 @@ ignore-words-list = """\ numer, ot, outin, + parm, requestor, te, textin, diff --git a/scene/2d/parallax_2d.cpp b/scene/2d/parallax_2d.cpp index fdb2d2cdd0..c6176390dc 100644 --- a/scene/2d/parallax_2d.cpp +++ b/scene/2d/parallax_2d.cpp @@ -83,7 +83,11 @@ void Parallax2D::_validate_property(PropertyInfo &p_property) const { void Parallax2D::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_pos) { if (!ignore_camera_scroll) { if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) { - set_screen_offset((p_adj_screen_pos + Vector2(0.5, 0.5)).floor()); + Size2 vps = get_viewport_rect().size; + Vector2 offset; + offset.x = ((int)vps.width % 2) ? 0.0 : 0.5; + offset.y = ((int)vps.height % 2) ? 0.0 : 0.5; + set_screen_offset((p_adj_screen_pos + offset).floor()); } else { set_screen_offset(p_adj_screen_pos); } diff --git a/scene/2d/physics/shape_cast_2d.cpp b/scene/2d/physics/shape_cast_2d.cpp index b92978bcad..dd9d589165 100644 --- a/scene/2d/physics/shape_cast_2d.cpp +++ b/scene/2d/physics/shape_cast_2d.cpp @@ -34,7 +34,7 @@ #include "scene/2d/physics/collision_object_2d.h" #include "scene/2d/physics/physics_body_2d.h" #include "scene/resources/2d/circle_shape_2d.h" -#include "servers/physics_2d/godot_physics_server_2d.h" +#include "servers/physics_server_2d.h" void ShapeCast2D::set_target_position(const Vector2 &p_point) { target_position = p_point; diff --git a/scene/3d/physics/character_body_3d.cpp b/scene/3d/physics/character_body_3d.cpp index dda3ea9cca..e3815e8219 100644 --- a/scene/3d/physics/character_body_3d.cpp +++ b/scene/3d/physics/character_body_3d.cpp @@ -60,8 +60,13 @@ bool CharacterBody3D::move_and_slide() { // We need to check the platform_rid object still exists before accessing. // A valid RID is no guarantee that the object has not been deleted. - if (ObjectDB::get_instance(platform_object_id)) { - //this approach makes sure there is less delay between the actual body velocity and the one we saved + + // We can only perform the ObjectDB lifetime check on Object derived objects. + // Note that physics also creates RIDs for non-Object derived objects, these cannot + // be lifetime checked through ObjectDB, and therefore there is a still a vulnerability + // to dangling RIDs (access after free) in this scenario. + if (platform_object_id.is_null() || ObjectDB::get_instance(platform_object_id)) { + // This approach makes sure there is less delay between the actual body velocity and the one we saved. bs = PhysicsServer3D::get_singleton()->body_get_direct_state(platform_rid); } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index c3287035ff..635228670d 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -831,6 +831,9 @@ void CodeEdit::_cut_internal(int p_caret) { delete_selection(p_caret); return; } + if (!is_empty_selection_clipboard_enabled()) { + return; + } if (p_caret == -1) { delete_lines(); } else { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index ae709cf7a4..43782409a8 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -2764,6 +2764,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect); + ClassDB::bind_method(D_METHOD("has_undo"), &LineEdit::has_undo); + ClassDB::bind_method(D_METHOD("has_redo"), &LineEdit::has_redo); ClassDB::bind_method(D_METHOD("has_selection"), &LineEdit::has_selection); ClassDB::bind_method(D_METHOD("get_selected_text"), &LineEdit::get_selected_text); ClassDB::bind_method(D_METHOD("get_selection_from_column"), &LineEdit::get_selection_from_column); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index f1902bade4..2211bd76fc 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -58,7 +58,7 @@ Size2 ScrollContainer::get_minimum_size() const { if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) { min_size.x = largest_child_min_size.x; - bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > size.y); + bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || vertical_scroll_mode == SCROLL_MODE_RESERVE || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > size.y); if (v_scroll_show && v_scroll->get_parent() == this) { min_size.x += v_scroll->get_minimum_size().x; } @@ -66,7 +66,7 @@ Size2 ScrollContainer::get_minimum_size() const { if (vertical_scroll_mode == SCROLL_MODE_DISABLED) { min_size.y = largest_child_min_size.y; - bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > size.x); + bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || horizontal_scroll_mode == SCROLL_MODE_RESERVE || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > size.x); if (h_scroll_show && h_scroll->get_parent() == this) { min_size.y += h_scroll->get_minimum_size().y; } @@ -92,6 +92,15 @@ void ScrollContainer::_cancel_drag() { } } +bool ScrollContainer::_is_h_scroll_visible() const { + // Scrolls may have been moved out for reasons. + return h_scroll->is_visible() && h_scroll->get_parent() == this; +} + +bool ScrollContainer::_is_v_scroll_visible() const { + return v_scroll->is_visible() && v_scroll->get_parent() == this; +} + void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { ERR_FAIL_COND(p_gui_input.is_null()); @@ -298,11 +307,11 @@ void ScrollContainer::_reposition_children() { ofs += theme_cache.panel_style->get_offset(); bool rtl = is_layout_rtl(); - if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons + if (_is_h_scroll_visible() || horizontal_scroll_mode == SCROLL_MODE_RESERVE) { size.y -= h_scroll->get_minimum_size().y; } - if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { //scrolls may have been moved out for reasons + if (_is_v_scroll_visible() || vertical_scroll_mode == SCROLL_MODE_RESERVE) { size.x -= v_scroll->get_minimum_size().x; } @@ -324,7 +333,7 @@ void ScrollContainer::_reposition_children() { r.size.height = MAX(size.height, minsize.height); } r.position += ofs; - if (rtl && v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { + if (rtl && _is_v_scroll_visible()) { r.position.x += v_scroll->get_minimum_size().x; } r.position = r.position.floor(); @@ -436,14 +445,14 @@ void ScrollContainer::update_scrollbars() { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); - h_scroll->set_visible(horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.width > size.width)); - v_scroll->set_visible(vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.height > size.height)); + h_scroll->set_visible(horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || ((horizontal_scroll_mode == SCROLL_MODE_AUTO || horizontal_scroll_mode == SCROLL_MODE_RESERVE) && largest_child_min_size.width > size.width)); + v_scroll->set_visible(vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || ((vertical_scroll_mode == SCROLL_MODE_AUTO || vertical_scroll_mode == SCROLL_MODE_RESERVE) && largest_child_min_size.height > size.height)); h_scroll->set_max(largest_child_min_size.width); - h_scroll->set_page((v_scroll->is_visible() && v_scroll->get_parent() == this) ? size.width - vmin.width : size.width); + h_scroll->set_page(_is_v_scroll_visible() ? size.width - vmin.width : size.width); v_scroll->set_max(largest_child_min_size.height); - v_scroll->set_page((h_scroll->is_visible() && h_scroll->get_parent() == this) ? size.height - hmin.height : size.height); + v_scroll->set_page(_is_h_scroll_visible() ? size.height - hmin.height : size.height); // Avoid scrollbar overlapping. _updating_scrollbars = true; @@ -603,14 +612,15 @@ void ScrollContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical", PROPERTY_HINT_NONE, "suffix:px"), "set_v_scroll", "get_v_scroll"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_horizontal_custom_step", PROPERTY_HINT_RANGE, "-1,4096,suffix:px"), "set_horizontal_custom_step", "get_horizontal_custom_step"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical_custom_step", PROPERTY_HINT_RANGE, "-1,4096,suffix:px"), "set_vertical_custom_step", "get_vertical_custom_step"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_vertical_scroll_mode", "get_vertical_scroll_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show,Reserve"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show,Reserve"), "set_vertical_scroll_mode", "get_vertical_scroll_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone"); BIND_ENUM_CONSTANT(SCROLL_MODE_DISABLED); BIND_ENUM_CONSTANT(SCROLL_MODE_AUTO); BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_ALWAYS); BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_NEVER); + BIND_ENUM_CONSTANT(SCROLL_MODE_RESERVE); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, panel_style, "panel"); diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index 02146618cd..afd3c8bd57 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -44,6 +44,7 @@ public: SCROLL_MODE_AUTO, SCROLL_MODE_SHOW_ALWAYS, SCROLL_MODE_SHOW_NEVER, + SCROLL_MODE_RESERVE, }; private: @@ -75,6 +76,9 @@ private: void _cancel_drag(); + bool _is_h_scroll_visible() const; + bool _is_v_scroll_visible() const; + protected: Size2 get_minimum_size() const override; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 888e680219..0e8d76d294 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1265,7 +1265,7 @@ void TextEdit::_notification(int p_what) { } if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word - if (is_ascii_alphabet_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '_' || lookup_symbol_word[0] == '.') { + if (is_unicode_identifier_start(lookup_symbol_word[0]) || lookup_symbol_word[0] == '.') { Color highlight_underline_color = !editable ? theme_cache.font_readonly_color : theme_cache.font_color; int lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); int lookup_symbol_word_len = lookup_symbol_word.length(); @@ -3367,6 +3367,14 @@ bool TextEdit::is_middle_mouse_paste_enabled() const { return middle_mouse_paste_enabled; } +void TextEdit::set_empty_selection_clipboard_enabled(bool p_enabled) { + empty_selection_clipboard_enabled = p_enabled; +} + +bool TextEdit::is_empty_selection_clipboard_enabled() const { + return empty_selection_clipboard_enabled; +} + // Text manipulation void TextEdit::clear() { setting_text = true; @@ -6569,6 +6577,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_middle_mouse_paste_enabled", "enabled"), &TextEdit::set_middle_mouse_paste_enabled); ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &TextEdit::is_middle_mouse_paste_enabled); + ClassDB::bind_method(D_METHOD("set_empty_selection_clipboard_enabled", "enabled"), &TextEdit::set_empty_selection_clipboard_enabled); + ClassDB::bind_method(D_METHOD("is_empty_selection_clipboard_enabled"), &TextEdit::is_empty_selection_clipboard_enabled); + // Text manipulation ClassDB::bind_method(D_METHOD("clear"), &TextEdit::clear); @@ -6962,6 +6973,7 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_and_drop_selection_enabled"), "set_drag_and_drop_selection_enabled", "is_drag_and_drop_selection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "empty_selection_clipboard_enabled"), "set_empty_selection_clipboard_enabled", "is_empty_selection_clipboard_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Arbitrary:1,Word:2,Word (Smart):3"), "set_autowrap_mode", "get_autowrap_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_wrapped_lines"), "set_indent_wrapped_lines", "is_indent_wrapped_lines"); @@ -7216,6 +7228,10 @@ void TextEdit::_cut_internal(int p_caret) { return; } + if (!empty_selection_clipboard_enabled) { + return; + } + // Remove full lines. begin_complex_operation(); begin_multicaret_edit(); @@ -7246,6 +7262,10 @@ void TextEdit::_copy_internal(int p_caret) { return; } + if (!empty_selection_clipboard_enabled) { + return; + } + // Copy full lines. StringBuilder clipboard; Vector<Point2i> line_ranges; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index c5f838020b..94b105d486 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -319,6 +319,7 @@ private: bool shortcut_keys_enabled = true; bool virtual_keyboard_enabled = true; bool middle_mouse_paste_enabled = true; + bool empty_selection_clipboard_enabled = true; // Overridable actions. String cut_copy_line = ""; @@ -770,6 +771,9 @@ public: void set_middle_mouse_paste_enabled(bool p_enabled); bool is_middle_mouse_paste_enabled() const; + void set_empty_selection_clipboard_enabled(bool p_enabled); + bool is_empty_selection_clipboard_enabled() const; + // Text manipulation void clear(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index eb3448e1a2..d921cc5b67 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -4019,4 +4019,9 @@ bool Node::is_connected(const StringName &p_signal, const Callable &p_callable) return Object::is_connected(p_signal, p_callable); } +bool Node::has_connections(const StringName &p_signal) const { + ERR_THREAD_GUARD_V(false); + return Object::has_connections(p_signal); +} + #endif diff --git a/scene/main/node.h b/scene/main/node.h index 4560ed085c..298cbc7e59 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -797,6 +797,7 @@ public: virtual Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0) override; virtual void disconnect(const StringName &p_signal, const Callable &p_callable) override; virtual bool is_connected(const StringName &p_signal, const Callable &p_callable) const override; + virtual bool has_connections(const StringName &p_signal) const override; #endif Node(); ~Node(); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 76678e609a..09227e260f 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -117,6 +117,7 @@ #include "scene/resources/compressed_texture.h" #include "scene/resources/curve_texture.h" #include "scene/resources/environment.h" +#include "scene/resources/external_texture.h" #include "scene/resources/font.h" #include "scene/resources/gradient.h" #include "scene/resources/gradient_texture.h" @@ -926,6 +927,7 @@ void register_scene_types() { GDREGISTER_CLASS(GradientTexture2D); GDREGISTER_CLASS(AnimatedTexture); GDREGISTER_CLASS(CameraTexture); + GDREGISTER_CLASS(ExternalTexture); GDREGISTER_VIRTUAL_CLASS(TextureLayered); GDREGISTER_ABSTRACT_CLASS(ImageTextureLayered); GDREGISTER_VIRTUAL_CLASS(Texture3D); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 2a576a5062..9abc6a02d2 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -4808,9 +4808,9 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol continue; // This track is exhausted (all keys were added already), don't consider. } } - - uint32_t key_frame = double(track_get_key_time(uncomp_track, time_tracks[i].key_index)) / frame_len; - + double key_time = track_get_key_time(uncomp_track, time_tracks[i].key_index); + double result = key_time / frame_len; + uint32_t key_frame = Math::fast_ftoi(result); if (time_tracks[i].needs_start_frame && key_frame > base_page_frame) { start_frame = true; best_frame = base_page_frame; diff --git a/scene/resources/camera_texture.cpp b/scene/resources/camera_texture.cpp index b575a099ed..b219f89e59 100644 --- a/scene/resources/camera_texture.cpp +++ b/scene/resources/camera_texture.cpp @@ -47,6 +47,11 @@ void CameraTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_is_active"), "set_camera_active", "get_camera_active"); } +void CameraTexture::_on_format_changed() { + // FIXME: `emit_changed` is more appropriate, but causes errors for some reason. + callable_mp((Resource *)this, &Resource::emit_changed).call_deferred(); +} + int CameraTexture::get_width() const { Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id); if (feed.is_valid()) { @@ -82,13 +87,26 @@ RID CameraTexture::get_rid() const { } Ref<Image> CameraTexture::get_image() const { - // not (yet) supported - return Ref<Image>(); + return RenderingServer::get_singleton()->texture_2d_get(get_rid()); } void CameraTexture::set_camera_feed_id(int p_new_id) { + Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id); + if (feed.is_valid()) { + if (feed->is_connected("format_changed", callable_mp(this, &CameraTexture::_on_format_changed))) { + feed->disconnect("format_changed", callable_mp(this, &CameraTexture::_on_format_changed)); + } + } + camera_feed_id = p_new_id; + + feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id); + if (feed.is_valid()) { + feed->connect("format_changed", callable_mp(this, &CameraTexture::_on_format_changed)); + } + notify_property_list_changed(); + callable_mp((Resource *)this, &Resource::emit_changed).call_deferred(); } int CameraTexture::get_camera_feed_id() const { @@ -98,6 +116,7 @@ int CameraTexture::get_camera_feed_id() const { void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) { which_feed = p_which; notify_property_list_changed(); + callable_mp((Resource *)this, &Resource::emit_changed).call_deferred(); } CameraServer::FeedImage CameraTexture::get_which_feed() const { @@ -109,6 +128,7 @@ void CameraTexture::set_camera_active(bool p_active) { if (feed.is_valid()) { feed->set_active(p_active); notify_property_list_changed(); + callable_mp((Resource *)this, &Resource::emit_changed).call_deferred(); } } diff --git a/scene/resources/camera_texture.h b/scene/resources/camera_texture.h index 521121f9ea..dd216a72d6 100644 --- a/scene/resources/camera_texture.h +++ b/scene/resources/camera_texture.h @@ -43,6 +43,7 @@ private: protected: static void _bind_methods(); + void _on_format_changed(); public: virtual int get_width() const override; diff --git a/scene/resources/external_texture.cpp b/scene/resources/external_texture.cpp new file mode 100644 index 0000000000..0552bbd081 --- /dev/null +++ b/scene/resources/external_texture.cpp @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* external_texture.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "external_texture.h" + +void ExternalTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &ExternalTexture::set_size); + ClassDB::bind_method(D_METHOD("get_external_texture_id"), &ExternalTexture::get_external_texture_id); + ClassDB::bind_method(D_METHOD("set_external_buffer_id", "external_buffer_id"), &ExternalTexture::set_external_buffer_id); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); +} + +uint64_t ExternalTexture::get_external_texture_id() const { + return RenderingServer::get_singleton()->texture_get_native_handle(texture); +} + +void ExternalTexture::set_size(const Size2 &p_size) { + if (p_size.width > 0 && p_size.height > 0 && p_size != size) { + size = p_size; + RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer); + emit_changed(); + } +} + +Size2 ExternalTexture::get_size() const { + return size; +} + +void ExternalTexture::set_external_buffer_id(uint64_t p_external_buffer) { + if (p_external_buffer != external_buffer) { + external_buffer = p_external_buffer; + RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer); + } +} + +int ExternalTexture::get_width() const { + return size.width; +} + +int ExternalTexture::get_height() const { + return size.height; +} + +bool ExternalTexture::has_alpha() const { + return false; +} + +RID ExternalTexture::get_rid() const { + return texture; +} + +ExternalTexture::ExternalTexture() { + texture = RenderingServer::get_singleton()->texture_external_create(size.width, size.height); +} + +ExternalTexture::~ExternalTexture() { + if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); + RenderingServer::get_singleton()->free(texture); + } +} diff --git a/scene/resources/external_texture.h b/scene/resources/external_texture.h new file mode 100644 index 0000000000..96bcd8d0fe --- /dev/null +++ b/scene/resources/external_texture.h @@ -0,0 +1,66 @@ +/**************************************************************************/ +/* external_texture.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 EXTERNAL_TEXTURE_H +#define EXTERNAL_TEXTURE_H + +#include "scene/resources/texture.h" + +// External textures as defined by OES_EGL_image_external (GLES) or VK_ANDROID_external_memory_android_hardware_buffer (Vulkan). +class ExternalTexture : public Texture2D { + GDCLASS(ExternalTexture, Texture2D); + +private: + RID texture; + Size2 size = Size2(256, 256); + uint64_t external_buffer = 0; + +protected: + static void _bind_methods(); + +public: + uint64_t get_external_texture_id() const; + + virtual Size2 get_size() const override; + void set_size(const Size2 &p_size); + + void set_external_buffer_id(uint64_t p_external_buffer); + + virtual int get_width() const override; + virtual int get_height() const override; + + virtual RID get_rid() const override; + virtual bool has_alpha() const override; + + ExternalTexture(); + ~ExternalTexture(); +}; + +#endif // EXTERNAL_TEXTURE_H diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 26666538af..3db1ab9338 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -8081,17 +8081,28 @@ int VisualShaderNodeRemap::get_input_port_count() const { } VisualShaderNodeRemap::PortType VisualShaderNodeRemap::get_input_port_type(int p_port) const { - switch (p_port) { - case 0: - return PORT_TYPE_SCALAR; - case 1: - return PORT_TYPE_SCALAR; - case 2: - return PORT_TYPE_SCALAR; - case 3: - return PORT_TYPE_SCALAR; - case 4: - return PORT_TYPE_SCALAR; + switch (op_type) { + case OP_TYPE_VECTOR_2D: + return PORT_TYPE_VECTOR_2D; + case OP_TYPE_VECTOR_2D_SCALAR: + if (p_port == 0) { + return PORT_TYPE_VECTOR_2D; + } + break; + case OP_TYPE_VECTOR_3D: + return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_3D_SCALAR: + if (p_port == 0) { + return PORT_TYPE_VECTOR_3D; + } + break; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + if (p_port == 0) { + return PORT_TYPE_VECTOR_4D; + } + break; default: break; } @@ -8123,23 +8134,159 @@ int VisualShaderNodeRemap::get_output_port_count() const { } VisualShaderNodeRemap::PortType VisualShaderNodeRemap::get_output_port_type(int p_port) const { - return PORT_TYPE_SCALAR; + switch (op_type) { + case OP_TYPE_VECTOR_2D: + case OP_TYPE_VECTOR_2D_SCALAR: + return PORT_TYPE_VECTOR_2D; + case OP_TYPE_VECTOR_3D: + case OP_TYPE_VECTOR_3D_SCALAR: + return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + case OP_TYPE_VECTOR_4D_SCALAR: + return PORT_TYPE_VECTOR_4D; + default: + return PORT_TYPE_SCALAR; + } } String VisualShaderNodeRemap::get_output_port_name(int p_port) const { return "value"; } +void VisualShaderNodeRemap::set_op_type(OpType p_op_type) { + ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX)); + if (op_type == p_op_type) { + return; + } + switch (p_op_type) { + case OP_TYPE_SCALAR: { + set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); + set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); + set_input_port_default_value(2, 1.0, get_input_port_default_value(2)); + set_input_port_default_value(3, 0.0, get_input_port_default_value(3)); + set_input_port_default_value(4, 1.0, get_input_port_default_value(4)); + } break; + case OP_TYPE_VECTOR_2D: { + set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); + set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); + set_input_port_default_value(2, Vector2(1.0, 1.0), get_input_port_default_value(2)); + set_input_port_default_value(3, Vector2(), get_input_port_default_value(3)); + set_input_port_default_value(4, Vector2(1.0, 1.0), get_input_port_default_value(4)); + } break; + case OP_TYPE_VECTOR_2D_SCALAR: { + set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); + set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); + set_input_port_default_value(2, 1.0, get_input_port_default_value(2)); + set_input_port_default_value(3, 0.0, get_input_port_default_value(3)); + set_input_port_default_value(4, 1.0, get_input_port_default_value(4)); + } break; + case OP_TYPE_VECTOR_3D: { + set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); + set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); + set_input_port_default_value(2, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(2)); + set_input_port_default_value(3, Vector3(), get_input_port_default_value(3)); + set_input_port_default_value(4, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(4)); + } break; + case OP_TYPE_VECTOR_3D_SCALAR: { + set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); + set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); + set_input_port_default_value(2, 1.0, get_input_port_default_value(2)); + set_input_port_default_value(3, 0.0, get_input_port_default_value(3)); + set_input_port_default_value(4, 1.0, get_input_port_default_value(4)); + } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(1.0, 1.0, 1.0, 1.0), get_input_port_default_value(2)); + set_input_port_default_value(3, Quaternion(), get_input_port_default_value(3)); + set_input_port_default_value(4, Quaternion(1.0, 1.0, 1.0, 1.0), get_input_port_default_value(4)); + } break; + case OP_TYPE_VECTOR_4D_SCALAR: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); + set_input_port_default_value(2, 1.0, get_input_port_default_value(2)); + set_input_port_default_value(3, 0.0, get_input_port_default_value(3)); + set_input_port_default_value(4, 1.0, get_input_port_default_value(4)); + } break; + default: + break; + } + op_type = p_op_type; + emit_changed(); +} + +VisualShaderNodeRemap::OpType VisualShaderNodeRemap::get_op_type() const { + return op_type; +} + String VisualShaderNodeRemap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; code += " {\n"; - code += vformat(" float __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]); - code += vformat(" float __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]); - code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + switch (op_type) { + case OP_TYPE_SCALAR: { + code += vformat(" float __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" float __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + } break; + case OP_TYPE_VECTOR_2D: { + code += vformat(" vec2 __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" vec2 __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + } break; + case OP_TYPE_VECTOR_2D_SCALAR: { + code += vformat(" vec2 __input_range = vec2(%s - %s);\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" vec2 __output_range = vec2(%s - %s);\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = vec2(%s) + __output_range * ((%s - vec2(%s)) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + } break; + case OP_TYPE_VECTOR_3D: { + code += vformat(" vec3 __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" vec3 __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + } break; + case OP_TYPE_VECTOR_3D_SCALAR: { + code += vformat(" vec3 __input_range = vec3(%s - %s);\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" vec3 __output_range = vec3(%s - %s);\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = vec3(%s) + __output_range * ((%s - vec3(%s)) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + } break; + case OP_TYPE_VECTOR_4D: { + code += vformat(" vec4 __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" vec4 __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + } break; + case OP_TYPE_VECTOR_4D_SCALAR: { + code += vformat(" vec4 __input_range = vec4(%s - %s);\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" vec4 __output_range = vec4(%s - %s);\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = vec4(%s) + __output_range * ((%s - vec4(%s)) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + } break; + default: + break; + } code += " }\n"; return code; } +Vector<StringName> VisualShaderNodeRemap::get_editable_properties() const { + Vector<StringName> props; + props.push_back("op_type"); + return props; +} + +void VisualShaderNodeRemap::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeRemap::set_op_type); + ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeRemap::get_op_type); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type"); + + BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_MAX); +} + VisualShaderNodeRemap::VisualShaderNodeRemap() { set_input_port_default_value(1, 0.0); set_input_port_default_value(2, 1.0); diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index ff02e55fb2..67dc8f7353 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -3068,10 +3068,30 @@ public: VisualShaderNodeRandomRange(); }; +/////////////////////////////////////// +/// Remap +/////////////////////////////////////// + class VisualShaderNodeRemap : public VisualShaderNode { GDCLASS(VisualShaderNodeRemap, VisualShaderNode); public: + enum OpType { + OP_TYPE_SCALAR, + OP_TYPE_VECTOR_2D, + OP_TYPE_VECTOR_2D_SCALAR, + OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_3D_SCALAR, + OP_TYPE_VECTOR_4D, + OP_TYPE_VECTOR_4D_SCALAR, + OP_TYPE_MAX, + }; + +protected: + OpType op_type = OP_TYPE_SCALAR; + static void _bind_methods(); + +public: virtual String get_caption() const override; virtual int get_input_port_count() const override; @@ -3082,13 +3102,26 @@ public: virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + void set_op_type(OpType p_op_type); + OpType get_op_type() const; + + virtual Vector<StringName> get_editable_properties() const override; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; - virtual Category get_category() const override { return CATEGORY_UTILITY; } + virtual Category get_category() const override { + if (op_type == OP_TYPE_SCALAR) { + return CATEGORY_SCALAR; + } else { + return CATEGORY_VECTOR; + } + } VisualShaderNodeRemap(); }; +VARIANT_ENUM_CAST(VisualShaderNodeRemap::OpType) + class VisualShaderNodeRotationByAxis : public VisualShaderNode { GDCLASS(VisualShaderNodeRotationByAxis, VisualShaderNode); diff --git a/scu_builders.py b/scu_builders.py index fc5461196f..3c04d66668 100644 --- a/scu_builders.py +++ b/scu_builders.py @@ -322,6 +322,9 @@ def generate_scu_files(max_includes_per_scu): process_folder(["modules/openxr"], ["register_types"]) process_folder(["modules/openxr/action_map"]) process_folder(["modules/openxr/editor"]) + process_folder(["modules/godot_physics_2d"]) + process_folder(["modules/godot_physics_3d"]) + process_folder(["modules/godot_physics_3d/joints"]) process_folder(["modules/csg"]) process_folder(["modules/gdscript"]) @@ -348,9 +351,6 @@ def generate_scu_files(max_includes_per_scu): process_folder(["servers/rendering/renderer_rd/effects"]) process_folder(["servers/rendering/renderer_rd/environment"]) process_folder(["servers/rendering/renderer_rd/storage_rd"]) - process_folder(["servers/physics_2d"]) - process_folder(["servers/physics_3d"]) - process_folder(["servers/physics_3d/joints"]) process_folder(["servers/audio"]) process_folder(["servers/audio/effects"]) diff --git a/servers/SCsub b/servers/SCsub index 736bed68ec..28180a7bb2 100644 --- a/servers/SCsub +++ b/servers/SCsub @@ -25,10 +25,7 @@ SConscript("navigation/SCsub") SConscript("rendering/SCsub") SConscript("text/SCsub") -SConscript("physics_2d/SCsub") - if not env["disable_3d"]: - SConscript("physics_3d/SCsub") env.add_source_files(env.servers_sources, "physics_server_3d.cpp") env.add_source_files(env.servers_sources, "physics_server_3d_wrap_mt.cpp") SConscript("xr/SCsub") diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp index 0661ffd576..8f6a40481d 100644 --- a/servers/camera/camera_feed.cpp +++ b/servers/camera/camera_feed.cpp @@ -56,9 +56,16 @@ void CameraFeed::_bind_methods() { ClassDB::bind_method(D_METHOD("get_datatype"), &CameraFeed::get_datatype); + ClassDB::bind_method(D_METHOD("get_formats"), &CameraFeed::get_formats); + ClassDB::bind_method(D_METHOD("set_format", "index", "parameters"), &CameraFeed::set_format); + + ADD_SIGNAL(MethodInfo("frame_changed")); + ADD_SIGNAL(MethodInfo("format_changed")); + ADD_GROUP("Feed", "feed_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "feed_is_active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "feed_transform"), "set_transform", "get_transform"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "formats"), "", "get_formats"); BIND_ENUM_CONSTANT(FEED_NOIMAGE); BIND_ENUM_CONSTANT(FEED_RGB); @@ -84,13 +91,11 @@ void CameraFeed::set_active(bool p_is_active) { } else if (p_is_active) { // attempt to activate this feed if (activate_feed()) { - print_line("Activate " + name); active = true; } } else { // just deactivate it deactivate_feed(); - print_line("Deactivate " + name); active = false; } } @@ -183,6 +188,8 @@ void CameraFeed::set_RGB_img(const Ref<Image> &p_rgb_img) { RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_rgb_img); RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture); + + emit_signal(SNAME("format_changed")); } else { RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_rgb_img); } @@ -204,6 +211,8 @@ void CameraFeed::set_YCbCr_img(const Ref<Image> &p_ycbcr_img) { RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_ycbcr_img); RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture); + + emit_signal(SNAME("format_changed")); } else { RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_ycbcr_img); } @@ -235,6 +244,8 @@ void CameraFeed::set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_c RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_cbcr_img); RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_CBCR_IMAGE], new_texture); } + + emit_signal(SNAME("format_changed")); } else { RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_Y_IMAGE], p_y_img); RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_CBCR_IMAGE], p_cbcr_img); @@ -252,3 +263,16 @@ bool CameraFeed::activate_feed() { void CameraFeed::deactivate_feed() { // nothing to do here } + +bool CameraFeed::set_format(int p_index, const Dictionary &p_parameters) { + return false; +} + +Array CameraFeed::get_formats() const { + return Array(); +} + +CameraFeed::FeedFormat CameraFeed::get_format() const { + FeedFormat feed_format = {}; + return feed_format; +} diff --git a/servers/camera/camera_feed.h b/servers/camera/camera_feed.h index b85a44cfae..5d1f54be07 100644 --- a/servers/camera/camera_feed.h +++ b/servers/camera/camera_feed.h @@ -60,14 +60,26 @@ public: private: int id; // unique id for this, for internal use in case feeds are removed - int base_width; - int base_height; protected: + struct FeedFormat { + int width = 0; + int height = 0; + String format; + int frame_numerator = 0; + int frame_denominator = 0; + uint32_t pixel_format = 0; + }; + String name; // name of our camera feed FeedDataType datatype; // type of texture data stored FeedPosition position; // position of camera on the device Transform2D transform; // display transform + int base_width = 0; + int base_height = 0; + Vector<FeedFormat> formats; + Dictionary parameters; + int selected_format = -1; bool active; // only when active do we actually update the camera texture each frame RID texture[CameraServer::FEED_IMAGES]; // texture images needed for this @@ -102,6 +114,10 @@ public: void set_YCbCr_img(const Ref<Image> &p_ycbcr_img); void set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img); + virtual bool set_format(int p_index, const Dictionary &p_parameters); + virtual Array get_formats() const; + virtual FeedFormat get_format() const; + virtual bool activate_feed(); virtual void deactivate_feed(); }; diff --git a/servers/physics_2d/SCsub b/servers/physics_2d/SCsub deleted file mode 100644 index 86681f9c74..0000000000 --- a/servers/physics_2d/SCsub +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -env.add_source_files(env.servers_sources, "*.cpp") diff --git a/servers/physics_3d/SCsub b/servers/physics_3d/SCsub deleted file mode 100644 index df7b521693..0000000000 --- a/servers/physics_3d/SCsub +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -env.add_source_files(env.servers_sources, "*.cpp") - -SConscript("joints/SCsub") diff --git a/servers/physics_3d/joints/SCsub b/servers/physics_3d/joints/SCsub deleted file mode 100644 index 86681f9c74..0000000000 --- a/servers/physics_3d/joints/SCsub +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -env.add_source_files(env.servers_sources, "*.cpp") diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index da25621ba5..f4f9a2e8b7 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -972,7 +972,9 @@ String PhysicsServer2DManager::get_server_name(int p_id) { } PhysicsServer2D *PhysicsServer2DManager::new_default_server() { - ERR_FAIL_COND_V(default_server_id == -1, nullptr); + if (default_server_id == -1) { + return nullptr; + } Variant ret; Callable::CallError ce; physics_2d_servers[default_server_id].create_callback.callp(nullptr, 0, ret, ce); diff --git a/servers/physics_server_2d_dummy.h b/servers/physics_server_2d_dummy.h new file mode 100644 index 0000000000..1f211d7ce0 --- /dev/null +++ b/servers/physics_server_2d_dummy.h @@ -0,0 +1,350 @@ +/**************************************************************************/ +/* physics_server_2d_dummy.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 PHYSICS_SERVER_2D_DUMMY_H +#define PHYSICS_SERVER_2D_DUMMY_H + +#include "physics_server_2d.h" + +class PhysicsDirectBodyState2DDummy : public PhysicsDirectBodyState2D { + GDCLASS(PhysicsDirectBodyState2DDummy, PhysicsDirectBodyState2D); + + PhysicsDirectSpaceState2D *space_state_dummy = nullptr; + +public: + virtual Vector2 get_total_gravity() const override { return Vector2(); } + virtual real_t get_total_linear_damp() const override { return 0; } + virtual real_t get_total_angular_damp() const override { return 0; } + + virtual Vector2 get_center_of_mass() const override { return Vector2(); } + virtual Vector2 get_center_of_mass_local() const override { return Vector2(); } + virtual real_t get_inverse_mass() const override { return 0; } + virtual real_t get_inverse_inertia() const override { return 0; } + + virtual void set_linear_velocity(const Vector2 &p_velocity) override {} + virtual Vector2 get_linear_velocity() const override { return Vector2(); } + + virtual void set_angular_velocity(real_t p_velocity) override {} + virtual real_t get_angular_velocity() const override { return 0; } + + virtual void set_transform(const Transform2D &p_transform) override {} + virtual Transform2D get_transform() const override { return Transform2D(); } + + virtual Vector2 get_velocity_at_local_position(const Vector2 &p_position) const override { return Vector2(); } + + virtual void apply_central_impulse(const Vector2 &p_impulse) override {} + virtual void apply_torque_impulse(real_t p_torque) override {} + virtual void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) override {} + + virtual void apply_central_force(const Vector2 &p_force) override {} + virtual void apply_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) override {} + virtual void apply_torque(real_t p_torque) override {} + + virtual void add_constant_central_force(const Vector2 &p_force) override {} + virtual void add_constant_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) override {} + virtual void add_constant_torque(real_t p_torque) override {} + + virtual void set_constant_force(const Vector2 &p_force) override {} + virtual Vector2 get_constant_force() const override { return Vector2(); } + + virtual void set_constant_torque(real_t p_torque) override {} + virtual real_t get_constant_torque() const override { return 0; } + + virtual void set_sleep_state(bool p_enable) override {} + virtual bool is_sleeping() const override { return false; } + + virtual int get_contact_count() const override { return 0; } + + virtual Vector2 get_contact_local_position(int p_contact_idx) const override { return Vector2(); } + virtual Vector2 get_contact_local_normal(int p_contact_idx) const override { return Vector2(); } + virtual int get_contact_local_shape(int p_contact_idx) const override { return 0; } + virtual Vector2 get_contact_local_velocity_at_position(int p_contact_idx) const override { return Vector2(); } + + virtual RID get_contact_collider(int p_contact_idx) const override { return RID(); } + virtual Vector2 get_contact_collider_position(int p_contact_idx) const override { return Vector2(); } + virtual ObjectID get_contact_collider_id(int p_contact_idx) const override { return ObjectID(); } + virtual Object *get_contact_collider_object(int p_contact_idx) const override { return nullptr; } + virtual int get_contact_collider_shape(int p_contact_idx) const override { return 0; } + virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override { return Vector2(); } + virtual Vector2 get_contact_impulse(int p_contact_idx) const override { return Vector2(); } + + virtual real_t get_step() const override { return 0; } + virtual void integrate_forces() override {} + + virtual PhysicsDirectSpaceState2D *get_space_state() override { return space_state_dummy; } + + PhysicsDirectBodyState2DDummy(PhysicsDirectSpaceState2D *p_space_state_dummy) { + space_state_dummy = p_space_state_dummy; + } +}; + +class PhysicsDirectSpaceState2DDummy : public PhysicsDirectSpaceState2D { + GDCLASS(PhysicsDirectSpaceState2DDummy, PhysicsDirectSpaceState2D); + +public: + virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) override { return false; } + + virtual int intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) override { return 0; } + + virtual int intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) override { return 0; } + virtual bool cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe) override { return false; } + virtual bool collide_shape(const ShapeParameters &p_parameters, Vector2 *r_results, int p_result_max, int &r_result_count) override { return false; } + virtual bool rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) override { return false; } +}; + +class PhysicsServer2DDummy : public PhysicsServer2D { + GDCLASS(PhysicsServer2DDummy, PhysicsServer2D); + + PhysicsDirectSpaceState2DDummy *space_state_dummy = nullptr; + PhysicsDirectBodyState2DDummy *body_state_dummy = nullptr; + +public: + virtual RID world_boundary_shape_create() override { return RID(); } + virtual RID separation_ray_shape_create() override { return RID(); } + virtual RID segment_shape_create() override { return RID(); } + virtual RID circle_shape_create() override { return RID(); } + virtual RID rectangle_shape_create() override { return RID(); } + virtual RID capsule_shape_create() override { return RID(); } + virtual RID convex_polygon_shape_create() override { return RID(); } + virtual RID concave_polygon_shape_create() override { return RID(); } + + virtual void shape_set_data(RID p_shape, const Variant &p_data) override {} + virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias) override {} + + virtual ShapeType shape_get_type(RID p_shape) const override { return ShapeType::SHAPE_CIRCLE; } + virtual Variant shape_get_data(RID p_shape) const override { return Variant(); } + virtual real_t shape_get_custom_solver_bias(RID p_shape) const override { return 0; } + + virtual bool shape_collide(RID p_shape_A, const Transform2D &p_xform_A, const Vector2 &p_motion_A, RID p_shape_B, const Transform2D &p_xform_B, const Vector2 &p_motion_B, Vector2 *r_results, int p_result_max, int &r_result_count) override { return false; } + + /* SPACE API */ + + virtual RID space_create() override { return RID(); } + virtual void space_set_active(RID p_space, bool p_active) override {} + virtual bool space_is_active(RID p_space) const override { return false; } + + virtual void space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) override {} + virtual real_t space_get_param(RID p_space, SpaceParameter p_param) const override { return 0; } + + virtual PhysicsDirectSpaceState2D *space_get_direct_state(RID p_space) override { return space_state_dummy; } + + virtual void space_set_debug_contacts(RID p_space, int p_max_contacts) override {} + virtual Vector<Vector2> space_get_contacts(RID p_space) const override { return Vector<Vector2>(); } + virtual int space_get_contact_count(RID p_space) const override { return 0; } + + /* AREA API */ + + virtual RID area_create() override { return RID(); } + + virtual void area_set_space(RID p_area, RID p_space) override {} + virtual RID area_get_space(RID p_area) const override { return RID(); } + + virtual void area_add_shape(RID p_area, RID p_shape, const Transform2D &p_transform = Transform2D(), bool p_disabled = false) override {} + virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape) override {} + virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform2D &p_transform) override {} + + virtual int area_get_shape_count(RID p_area) const override { return 0; } + virtual RID area_get_shape(RID p_area, int p_shape_idx) const override { return RID(); } + virtual Transform2D area_get_shape_transform(RID p_area, int p_shape_idx) const override { return Transform2D(); } + + virtual void area_remove_shape(RID p_area, int p_shape_idx) override {} + virtual void area_clear_shapes(RID p_area) override {} + + virtual void area_set_shape_disabled(RID p_area, int p_shape, bool p_disabled) override {} + + virtual void area_attach_object_instance_id(RID p_area, ObjectID p_id) override {} + virtual ObjectID area_get_object_instance_id(RID p_area) const override { return ObjectID(); } + + virtual void area_attach_canvas_instance_id(RID p_area, ObjectID p_id) override {} + virtual ObjectID area_get_canvas_instance_id(RID p_area) const override { return ObjectID(); } + + virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) override {} + virtual void area_set_transform(RID p_area, const Transform2D &p_transform) override {} + + virtual Variant area_get_param(RID p_parea, AreaParameter p_param) const override { return Variant(); } + virtual Transform2D area_get_transform(RID p_area) const override { return Transform2D(); } + + virtual void area_set_collision_layer(RID p_area, uint32_t p_layer) override {} + virtual uint32_t area_get_collision_layer(RID p_area) const override { return 0; } + + virtual void area_set_collision_mask(RID p_area, uint32_t p_mask) override {} + virtual uint32_t area_get_collision_mask(RID p_area) const override { return 0; } + + virtual void area_set_monitorable(RID p_area, bool p_monitorable) override {} + virtual void area_set_pickable(RID p_area, bool p_pickable) override {} + + virtual void area_set_monitor_callback(RID p_area, const Callable &p_callback) override {} + virtual void area_set_area_monitor_callback(RID p_area, const Callable &p_callback) override {} + + /* BODY API */ + + virtual RID body_create() override { return RID(); } + + virtual void body_set_space(RID p_body, RID p_space) override {} + virtual RID body_get_space(RID p_body) const override { return RID(); } + + virtual void body_set_mode(RID p_body, BodyMode p_mode) override {} + virtual BodyMode body_get_mode(RID p_body) const override { return BodyMode::BODY_MODE_STATIC; } + + virtual void body_add_shape(RID p_body, RID p_shape, const Transform2D &p_transform = Transform2D(), bool p_disabled = false) override {} + virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape) override {} + virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform2D &p_transform) override {} + + virtual int body_get_shape_count(RID p_body) const override { return 0; } + virtual RID body_get_shape(RID p_body, int p_shape_idx) const override { return RID(); } + virtual Transform2D body_get_shape_transform(RID p_body, int p_shape_idx) const override { return Transform2D(); } + + virtual void body_set_shape_disabled(RID p_body, int p_shape, bool p_disabled) override {} + virtual void body_set_shape_as_one_way_collision(RID p_body, int p_shape, bool p_enabled, real_t p_margin = 0) override {} + + virtual void body_remove_shape(RID p_body, int p_shape_idx) override {} + virtual void body_clear_shapes(RID p_body) override {} + + virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) override {} + virtual ObjectID body_get_object_instance_id(RID p_body) const override { return ObjectID(); } + + virtual void body_attach_canvas_instance_id(RID p_body, ObjectID p_id) override {} + virtual ObjectID body_get_canvas_instance_id(RID p_body) const override { return ObjectID(); } + + virtual void body_set_continuous_collision_detection_mode(RID p_body, CCDMode p_mode) override {} + virtual CCDMode body_get_continuous_collision_detection_mode(RID p_body) const override { return CCDMode::CCD_MODE_DISABLED; } + + virtual void body_set_collision_layer(RID p_body, uint32_t p_layer) override {} + virtual uint32_t body_get_collision_layer(RID p_body) const override { return 0; } + + virtual void body_set_collision_mask(RID p_body, uint32_t p_mask) override {} + virtual uint32_t body_get_collision_mask(RID p_body) const override { return 0; } + + virtual void body_set_collision_priority(RID p_body, real_t p_priority) override {} + virtual real_t body_get_collision_priority(RID p_body) const override { return 0; } + + virtual void body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) override {} + virtual Variant body_get_param(RID p_body, BodyParameter p_param) const override { return Variant(); } + + virtual void body_reset_mass_properties(RID p_body) override {} + + virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) override {} + virtual Variant body_get_state(RID p_body, BodyState p_state) const override { return Variant(); } + + virtual void body_apply_central_impulse(RID p_body, const Vector2 &p_impulse) override {} + virtual void body_apply_torque_impulse(RID p_body, real_t p_torque) override {} + virtual void body_apply_impulse(RID p_body, const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) override {} + + virtual void body_apply_central_force(RID p_body, const Vector2 &p_force) override {} + virtual void body_apply_force(RID p_body, const Vector2 &p_force, const Vector2 &p_position = Vector2()) override {} + virtual void body_apply_torque(RID p_body, real_t p_torque) override {} + + virtual void body_add_constant_central_force(RID p_body, const Vector2 &p_force) override {} + virtual void body_add_constant_force(RID p_body, const Vector2 &p_force, const Vector2 &p_position = Vector2()) override {} + virtual void body_add_constant_torque(RID p_body, real_t p_torque) override {} + + virtual void body_set_constant_force(RID p_body, const Vector2 &p_force) override {} + virtual Vector2 body_get_constant_force(RID p_body) const override { return Vector2(); } + + virtual void body_set_constant_torque(RID p_body, real_t p_torque) override {} + virtual real_t body_get_constant_torque(RID p_body) const override { return 0; } + + virtual void body_set_axis_velocity(RID p_body, const Vector2 &p_axis_velocity) override {} + + virtual void body_add_collision_exception(RID p_body, RID p_body_b) override {} + virtual void body_remove_collision_exception(RID p_body, RID p_body_b) override {} + virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {} + + virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override {} + virtual int body_get_max_contacts_reported(RID p_body) const override { return 0; } + + virtual void body_set_contacts_reported_depth_threshold(RID p_body, real_t p_threshold) override {} + virtual real_t body_get_contacts_reported_depth_threshold(RID p_body) const override { return 0; } + + virtual void body_set_omit_force_integration(RID p_body, bool p_omit) override {} + virtual bool body_is_omitting_force_integration(RID p_body) const override { return false; } + + virtual void body_set_state_sync_callback(RID p_body, const Callable &p_callable) override {} + virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) override {} + + virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) override { return false; } + + virtual void body_set_pickable(RID p_body, bool p_pickable) override {} + + virtual PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) override { return body_state_dummy; } + + virtual bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override { return false; } + + /* JOINT API */ + + virtual RID joint_create() override { return RID(); } + + virtual void joint_clear(RID p_joint) override {} + + virtual void joint_set_param(RID p_joint, JointParam p_param, real_t p_value) override {} + virtual real_t joint_get_param(RID p_joint, JointParam p_param) const override { return 0; } + + virtual void joint_disable_collisions_between_bodies(RID p_joint, const bool p_disable) override {} + virtual bool joint_is_disabled_collisions_between_bodies(RID p_joint) const override { return false; } + + virtual void joint_make_pin(RID p_joint, const Vector2 &p_anchor, RID p_body_a, RID p_body_b = RID()) override {} + virtual void joint_make_groove(RID p_joint, const Vector2 &p_a_groove1, const Vector2 &p_a_groove2, const Vector2 &p_b_anchor, RID p_body_a, RID p_body_b) override {} + virtual void joint_make_damped_spring(RID p_joint, const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, RID p_body_a, RID p_body_b = RID()) override {} + + virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value) override {} + virtual real_t pin_joint_get_param(RID p_joint, PinJointParam p_param) const override { return 0; } + + virtual void pin_joint_set_flag(RID p_joint, PinJointFlag p_flag, bool p_enabled) override {} + virtual bool pin_joint_get_flag(RID p_joint, PinJointFlag p_flag) const override { return false; } + + virtual void damped_spring_joint_set_param(RID p_joint, DampedSpringParam p_param, real_t p_value) override {} + virtual real_t damped_spring_joint_get_param(RID p_joint, DampedSpringParam p_param) const override { return 0; } + + virtual JointType joint_get_type(RID p_joint) const override { return JointType::JOINT_TYPE_PIN; } + + /* MISC */ + + virtual void free(RID p_rid) override {} + + virtual void set_active(bool p_active) override {} + virtual void init() override { + space_state_dummy = memnew(PhysicsDirectSpaceState2DDummy); + body_state_dummy = memnew(PhysicsDirectBodyState2DDummy(space_state_dummy)); + } + virtual void step(real_t p_step) override {} + virtual void sync() override {} + virtual void flush_queries() override {} + virtual void end_sync() override {} + virtual void finish() override { + memdelete(body_state_dummy); + memdelete(space_state_dummy); + } + + virtual bool is_flushing_queries() const override { return false; } + + virtual int get_process_info(ProcessInfo p_info) override { return 0; } +}; + +#endif // PHYSICS_SERVER_2D_DUMMY_H diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index 698805bbdd..ee48151863 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -1201,7 +1201,9 @@ String PhysicsServer3DManager::get_server_name(int p_id) { } PhysicsServer3D *PhysicsServer3DManager::new_default_server() { - ERR_FAIL_COND_V(default_server_id == -1, nullptr); + if (default_server_id == -1) { + return nullptr; + } Variant ret; Callable::CallError ce; physics_servers[default_server_id].create_callback.callp(nullptr, 0, ret, ce); diff --git a/servers/physics_server_3d_dummy.h b/servers/physics_server_3d_dummy.h new file mode 100644 index 0000000000..209a541fea --- /dev/null +++ b/servers/physics_server_3d_dummy.h @@ -0,0 +1,436 @@ +/**************************************************************************/ +/* physics_server_3d_dummy.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 PHYSICS_SERVER_3D_DUMMY_H +#define PHYSICS_SERVER_3D_DUMMY_H + +#include "servers/physics_server_3d.h" + +class PhysicsDirectBodyState3DDummy : public PhysicsDirectBodyState3D { + GDCLASS(PhysicsDirectBodyState3DDummy, PhysicsDirectBodyState3D); + + PhysicsDirectSpaceState3D *space_state_dummy = nullptr; + +public: + virtual Vector3 get_total_gravity() const override { return Vector3(); } + virtual real_t get_total_angular_damp() const override { return 0; } + virtual real_t get_total_linear_damp() const override { return 0; } + + virtual Vector3 get_center_of_mass() const override { return Vector3(); } + virtual Vector3 get_center_of_mass_local() const override { return Vector3(); } + virtual Basis get_principal_inertia_axes() const override { return Basis(); } + virtual real_t get_inverse_mass() const override { return 0; } + virtual Vector3 get_inverse_inertia() const override { return Vector3(); } + virtual Basis get_inverse_inertia_tensor() const override { return Basis(); } + + virtual void set_linear_velocity(const Vector3 &p_velocity) override {} + virtual Vector3 get_linear_velocity() const override { return Vector3(); } + + virtual void set_angular_velocity(const Vector3 &p_velocity) override {} + virtual Vector3 get_angular_velocity() const override { return Vector3(); } + + virtual void set_transform(const Transform3D &p_transform) override {} + virtual Transform3D get_transform() const override { return Transform3D(); } + + virtual Vector3 get_velocity_at_local_position(const Vector3 &p_position) const override { return Vector3(); } + + virtual void apply_central_impulse(const Vector3 &p_impulse) override {} + virtual void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override {} + virtual void apply_torque_impulse(const Vector3 &p_impulse) override {} + + virtual void apply_central_force(const Vector3 &p_force) override {} + virtual void apply_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override {} + virtual void apply_torque(const Vector3 &p_torque) override {} + + virtual void add_constant_central_force(const Vector3 &p_force) override {} + virtual void add_constant_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override {} + virtual void add_constant_torque(const Vector3 &p_torque) override {} + + virtual void set_constant_force(const Vector3 &p_force) override {} + virtual Vector3 get_constant_force() const override { return Vector3(); } + + virtual void set_constant_torque(const Vector3 &p_torque) override {} + virtual Vector3 get_constant_torque() const override { return Vector3(); } + + virtual void set_sleep_state(bool p_sleep) override {} + virtual bool is_sleeping() const override { return false; } + + virtual int get_contact_count() const override { return 0; } + + virtual Vector3 get_contact_local_position(int p_contact_idx) const override { return Vector3(); } + virtual Vector3 get_contact_local_normal(int p_contact_idx) const override { return Vector3(); } + virtual Vector3 get_contact_impulse(int p_contact_idx) const override { return Vector3(); } + virtual int get_contact_local_shape(int p_contact_idx) const override { return 0; } + virtual Vector3 get_contact_local_velocity_at_position(int p_contact_idx) const override { return Vector3(); } + + virtual RID get_contact_collider(int p_contact_idx) const override { return RID(); } + virtual Vector3 get_contact_collider_position(int p_contact_idx) const override { return Vector3(); } + virtual ObjectID get_contact_collider_id(int p_contact_idx) const override { return ObjectID(); } + virtual Object *get_contact_collider_object(int p_contact_idx) const override { return nullptr; } + virtual int get_contact_collider_shape(int p_contact_idx) const override { return 0; } + virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const override { return Vector3(); } + + virtual real_t get_step() const override { return 0; } + virtual void integrate_forces() override {} + + virtual PhysicsDirectSpaceState3D *get_space_state() override { return space_state_dummy; } + + PhysicsDirectBodyState3DDummy(PhysicsDirectSpaceState3D *p_space_state_dummy) { + space_state_dummy = p_space_state_dummy; + } +}; + +class PhysicsDirectSpaceState3DDummy : public PhysicsDirectSpaceState3D { + GDCLASS(PhysicsDirectSpaceState3DDummy, PhysicsDirectSpaceState3D); + +public: + virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) override { return false; } + + virtual int intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) override { return 0; } + + virtual int intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) override { return 0; } + virtual bool cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe, ShapeRestInfo *r_info = nullptr) override { return false; } + virtual bool collide_shape(const ShapeParameters &p_parameters, Vector3 *r_results, int p_result_max, int &r_result_count) override { return false; } + virtual bool rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) override { return false; } + + virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const override { return Vector3(); } +}; + +class PhysicsServer3DDummy : public PhysicsServer3D { + GDCLASS(PhysicsServer3DDummy, PhysicsServer3D); + + PhysicsDirectBodyState3DDummy *body_state_dummy = nullptr; + PhysicsDirectSpaceState3DDummy *space_state_dummy = nullptr; + +public: + virtual RID world_boundary_shape_create() override { return RID(); } + virtual RID separation_ray_shape_create() override { return RID(); } + virtual RID sphere_shape_create() override { return RID(); } + virtual RID box_shape_create() override { return RID(); } + virtual RID capsule_shape_create() override { return RID(); } + virtual RID cylinder_shape_create() override { return RID(); } + virtual RID convex_polygon_shape_create() override { return RID(); } + virtual RID concave_polygon_shape_create() override { return RID(); } + virtual RID heightmap_shape_create() override { return RID(); } + virtual RID custom_shape_create() override { return RID(); } + + virtual void shape_set_data(RID p_shape, const Variant &p_data) override {} + virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias) override {} + + virtual ShapeType shape_get_type(RID p_shape) const override { return SHAPE_SPHERE; } + virtual Variant shape_get_data(RID p_shape) const override { return Variant(); } + + virtual void shape_set_margin(RID p_shape, real_t p_margin) override {} + virtual real_t shape_get_margin(RID p_shape) const override { return 0; } + + virtual real_t shape_get_custom_solver_bias(RID p_shape) const override { return 0; } + + /* SPACE API */ + + virtual RID space_create() override { return RID(); } + virtual void space_set_active(RID p_space, bool p_active) override {} + virtual bool space_is_active(RID p_space) const override { return false; } + + virtual void space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) override {} + virtual real_t space_get_param(RID p_space, SpaceParameter p_param) const override { return 0; } + + virtual PhysicsDirectSpaceState3D *space_get_direct_state(RID p_space) override { return space_state_dummy; } + + virtual void space_set_debug_contacts(RID p_space, int p_max_contacts) override {} + virtual Vector<Vector3> space_get_contacts(RID p_space) const override { return Vector<Vector3>(); } + virtual int space_get_contact_count(RID p_space) const override { return 0; } + + /* AREA API */ + + virtual RID area_create() override { return RID(); } + + virtual void area_set_space(RID p_area, RID p_space) override {} + virtual RID area_get_space(RID p_area) const override { return RID(); } + + virtual void area_add_shape(RID p_area, RID p_shape, const Transform3D &p_transform = Transform3D(), bool p_disabled = false) override {} + virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape) override {} + virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform3D &p_transform) override {} + + virtual int area_get_shape_count(RID p_area) const override { return 0; } + virtual RID area_get_shape(RID p_area, int p_shape_idx) const override { return RID(); } + virtual Transform3D area_get_shape_transform(RID p_area, int p_shape_idx) const override { return Transform3D(); } + + virtual void area_remove_shape(RID p_area, int p_shape_idx) override {} + virtual void area_clear_shapes(RID p_area) override {} + + virtual void area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) override {} + + virtual void area_attach_object_instance_id(RID p_area, ObjectID p_id) override {} + virtual ObjectID area_get_object_instance_id(RID p_area) const override { return ObjectID(); } + + virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) override {} + virtual void area_set_transform(RID p_area, const Transform3D &p_transform) override {} + + virtual Variant area_get_param(RID p_parea, AreaParameter p_param) const override { return Variant(); } + virtual Transform3D area_get_transform(RID p_area) const override { return Transform3D(); } + + virtual void area_set_collision_layer(RID p_area, uint32_t p_layer) override {} + virtual uint32_t area_get_collision_layer(RID p_area) const override { return 0; } + + virtual void area_set_collision_mask(RID p_area, uint32_t p_mask) override {} + virtual uint32_t area_get_collision_mask(RID p_area) const override { return 0; } + + virtual void area_set_monitorable(RID p_area, bool p_monitorable) override {} + + virtual void area_set_monitor_callback(RID p_area, const Callable &p_callback) override {} + virtual void area_set_area_monitor_callback(RID p_area, const Callable &p_callback) override {} + + virtual void area_set_ray_pickable(RID p_area, bool p_enable) override {} + + /* BODY API */ + + virtual RID body_create() override { return RID(); } + + virtual void body_set_space(RID p_body, RID p_space) override {} + virtual RID body_get_space(RID p_body) const override { return RID(); } + + virtual void body_set_mode(RID p_body, BodyMode p_mode) override {} + virtual BodyMode body_get_mode(RID p_body) const override { return BodyMode::BODY_MODE_STATIC; } + + virtual void body_add_shape(RID p_body, RID p_shape, const Transform3D &p_transform = Transform3D(), bool p_disabled = false) override {} + virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape) override {} + virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform3D &p_transform) override {} + + virtual int body_get_shape_count(RID p_body) const override { return 0; } + virtual RID body_get_shape(RID p_body, int p_shape_idx) const override { return RID(); } + virtual Transform3D body_get_shape_transform(RID p_body, int p_shape_idx) const override { return Transform3D(); } + + virtual void body_remove_shape(RID p_body, int p_shape_idx) override {} + virtual void body_clear_shapes(RID p_body) override {} + + virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) override {} + + virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) override {} + virtual ObjectID body_get_object_instance_id(RID p_body) const override { return ObjectID(); } + + virtual void body_set_enable_continuous_collision_detection(RID p_body, bool p_enable) override {} + virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const override { return false; } + + virtual void body_set_collision_layer(RID p_body, uint32_t p_layer) override {} + virtual uint32_t body_get_collision_layer(RID p_body) const override { return 0; } + + virtual void body_set_collision_mask(RID p_body, uint32_t p_mask) override {} + virtual uint32_t body_get_collision_mask(RID p_body) const override { return 0; } + + virtual void body_set_collision_priority(RID p_body, real_t p_priority) override {} + virtual real_t body_get_collision_priority(RID p_body) const override { return 0; } + + virtual void body_set_user_flags(RID p_body, uint32_t p_flags) override {} + virtual uint32_t body_get_user_flags(RID p_body) const override { return 0; } + + virtual void body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) override {} + virtual Variant body_get_param(RID p_body, BodyParameter p_param) const override { return Variant(); } + + virtual void body_reset_mass_properties(RID p_body) override {} + + virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) override {} + virtual Variant body_get_state(RID p_body, BodyState p_state) const override { return Variant(); } + + virtual void body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) override {} + virtual void body_apply_impulse(RID p_body, const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override {} + virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) override {} + + virtual void body_apply_central_force(RID p_body, const Vector3 &p_force) override {} + virtual void body_apply_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position = Vector3()) override {} + virtual void body_apply_torque(RID p_body, const Vector3 &p_torque) override {} + + virtual void body_add_constant_central_force(RID p_body, const Vector3 &p_force) override {} + virtual void body_add_constant_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position = Vector3()) override {} + virtual void body_add_constant_torque(RID p_body, const Vector3 &p_torque) override {} + + virtual void body_set_constant_force(RID p_body, const Vector3 &p_force) override {} + virtual Vector3 body_get_constant_force(RID p_body) const override { return Vector3(); } + + virtual void body_set_constant_torque(RID p_body, const Vector3 &p_torque) override {} + virtual Vector3 body_get_constant_torque(RID p_body) const override { return Vector3(); } + + virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity) override {} + + virtual void body_set_axis_lock(RID p_body, BodyAxis p_axis, bool p_lock) override {} + virtual bool body_is_axis_locked(RID p_body, BodyAxis p_axis) const override { return false; } + + virtual void body_add_collision_exception(RID p_body, RID p_body_b) override {} + virtual void body_remove_collision_exception(RID p_body, RID p_body_b) override {} + virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {} + + virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override {} + virtual int body_get_max_contacts_reported(RID p_body) const override { return 0; } + + virtual void body_set_contacts_reported_depth_threshold(RID p_body, real_t p_threshold) override {} + virtual real_t body_get_contacts_reported_depth_threshold(RID p_body) const override { return 0; } + + virtual void body_set_omit_force_integration(RID p_body, bool p_omit) override {} + virtual bool body_is_omitting_force_integration(RID p_body) const override { return false; } + + virtual void body_set_state_sync_callback(RID p_body, const Callable &p_callable) override {} + virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) override {} + + virtual void body_set_ray_pickable(RID p_body, bool p_enable) override {} + + virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override { return body_state_dummy; } + + virtual bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override { return false; } + + /* SOFT BODY */ + + virtual RID soft_body_create() override { return RID(); } + + virtual void soft_body_update_rendering_server(RID p_body, PhysicsServer3DRenderingServerHandler *p_rendering_server_handler) override {} + + virtual void soft_body_set_space(RID p_body, RID p_space) override {} + virtual RID soft_body_get_space(RID p_body) const override { return RID(); } + + virtual void soft_body_set_mesh(RID p_body, RID p_mesh) override {} + + virtual AABB soft_body_get_bounds(RID p_body) const override { return AABB(); } + + virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) override {} + virtual uint32_t soft_body_get_collision_layer(RID p_body) const override { return 0; } + + virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) override {} + virtual uint32_t soft_body_get_collision_mask(RID p_body) const override { return 0; } + + virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b) override {} + virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b) override {} + virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {} + + virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) override {} + virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const override { return Variant(); } + + virtual void soft_body_set_transform(RID p_body, const Transform3D &p_transform) override {} + + virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) override {} + + virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) override {} + virtual int soft_body_get_simulation_precision(RID p_body) const override { return 0; } + + virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) override {} + virtual real_t soft_body_get_total_mass(RID p_body) const override { return 0; } + + virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) override {} + virtual real_t soft_body_get_linear_stiffness(RID p_body) const override { return 0; } + + virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) override {} + virtual real_t soft_body_get_pressure_coefficient(RID p_body) const override { return 0; } + + virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) override {} + virtual real_t soft_body_get_damping_coefficient(RID p_body) const override { return 0; } + + virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) override {} + virtual real_t soft_body_get_drag_coefficient(RID p_body) const override { return 0; } + + virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) override {} + virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) const override { return Vector3(); } + + virtual void soft_body_remove_all_pinned_points(RID p_body) override {} + virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) override {} + virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) const override { return false; } + + /* JOINT API */ + + virtual RID joint_create() override { return RID(); } + + virtual void joint_clear(RID p_joint) override {} + + virtual JointType joint_get_type(RID p_joint) const override { return JointType::JOINT_TYPE_PIN; } + + virtual void joint_set_solver_priority(RID p_joint, int p_priority) override {} + virtual int joint_get_solver_priority(RID p_joint) const override { return 0; } + + virtual void joint_disable_collisions_between_bodies(RID p_joint, bool p_disable) override {} + virtual bool joint_is_disabled_collisions_between_bodies(RID p_joint) const override { return false; } + + virtual void joint_make_pin(RID p_joint, RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B) override {} + + virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value) override {} + virtual real_t pin_joint_get_param(RID p_joint, PinJointParam p_param) const override { return 0; } + + virtual void pin_joint_set_local_a(RID p_joint, const Vector3 &p_A) override {} + virtual Vector3 pin_joint_get_local_a(RID p_joint) const override { return Vector3(); } + + virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_B) override {} + virtual Vector3 pin_joint_get_local_b(RID p_joint) const override { return Vector3(); } + + virtual void joint_make_hinge(RID p_joint, RID p_body_A, const Transform3D &p_hinge_A, RID p_body_B, const Transform3D &p_hinge_B) override {} + virtual void joint_make_hinge_simple(RID p_joint, RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B) override {} + + virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, real_t p_value) override {} + virtual real_t hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const override { return 0; } + + virtual void hinge_joint_set_flag(RID p_joint, HingeJointFlag p_flag, bool p_enabled) override {} + virtual bool hinge_joint_get_flag(RID p_joint, HingeJointFlag p_flag) const override { return false; } + + virtual void joint_make_slider(RID p_joint, RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) override {} + + virtual void slider_joint_set_param(RID p_joint, SliderJointParam p_param, real_t p_value) override {} + virtual real_t slider_joint_get_param(RID p_joint, SliderJointParam p_param) const override { return 0; } + + virtual void joint_make_cone_twist(RID p_joint, RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) override {} + + virtual void cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, real_t p_value) override {} + virtual real_t cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const override { return 0; } + + virtual void joint_make_generic_6dof(RID p_joint, RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) override {} + + virtual void generic_6dof_joint_set_param(RID p_joint, Vector3::Axis, G6DOFJointAxisParam p_param, real_t p_value) override {} + virtual real_t generic_6dof_joint_get_param(RID p_joint, Vector3::Axis, G6DOFJointAxisParam p_param) const override { return 0; } + + virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis, G6DOFJointAxisFlag p_flag, bool p_enable) override {} + virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis, G6DOFJointAxisFlag p_flag) const override { return false; } + + /* MISC */ + + virtual void free(RID p_rid) override {} + + virtual void set_active(bool p_active) override {} + virtual void init() override { + space_state_dummy = memnew(PhysicsDirectSpaceState3DDummy); + body_state_dummy = memnew(PhysicsDirectBodyState3DDummy(space_state_dummy)); + } + virtual void step(real_t p_step) override {} + virtual void sync() override {} + virtual void flush_queries() override {} + virtual void end_sync() override {} + virtual void finish() override { + memdelete(body_state_dummy); + memdelete(space_state_dummy); + } + + virtual bool is_flushing_queries() const override { return false; } + + virtual int get_process_info(ProcessInfo p_info) override { return 0; } +}; + +#endif // PHYSICS_SERVER_3D_DUMMY_H diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index fc2aae10c7..18ee863083 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -81,16 +81,16 @@ // 2D physics and navigation. #include "navigation_server_2d.h" -#include "physics_2d/godot_physics_server_2d.h" #include "physics_server_2d.h" +#include "physics_server_2d_dummy.h" #include "physics_server_2d_wrap_mt.h" #include "servers/extensions/physics_server_2d_extension.h" // 3D physics and navigation (3D navigation is needed for 2D). #include "navigation_server_3d.h" #ifndef _3D_DISABLED -#include "physics_3d/godot_physics_server_3d.h" #include "physics_server_3d.h" +#include "physics_server_3d_dummy.h" #include "physics_server_3d_wrap_mt.h" #include "servers/extensions/physics_server_3d_extension.h" #include "xr/xr_body_tracker.h" @@ -106,29 +106,13 @@ ShaderTypes *shader_types = nullptr; #ifndef _3D_DISABLED -static PhysicsServer3D *_createGodotPhysics3DCallback() { -#ifdef THREADS_ENABLED - bool using_threads = GLOBAL_GET("physics/3d/run_on_separate_thread"); -#else - bool using_threads = false; -#endif - - PhysicsServer3D *physics_server_3d = memnew(GodotPhysicsServer3D(using_threads)); - - return memnew(PhysicsServer3DWrapMT(physics_server_3d, using_threads)); +static PhysicsServer3D *_create_dummy_physics_server_3d() { + return memnew(PhysicsServer3DDummy); } #endif // _3D_DISABLED -static PhysicsServer2D *_createGodotPhysics2DCallback() { -#ifdef THREADS_ENABLED - bool using_threads = GLOBAL_GET("physics/2d/run_on_separate_thread"); -#else - bool using_threads = false; -#endif - - PhysicsServer2D *physics_server_2d = memnew(GodotPhysicsServer2D(using_threads)); - - return memnew(PhysicsServer2DWrapMT(physics_server_2d, using_threads)); +static PhysicsServer2D *_create_dummy_physics_server_2d() { + return memnew(PhysicsServer2DDummy); } static bool has_server_feature_callback(const String &p_feature) { @@ -289,8 +273,7 @@ void register_server_types() { GLOBAL_DEF(PropertyInfo(Variant::STRING, PhysicsServer2DManager::setting_property_name, PROPERTY_HINT_ENUM, "DEFAULT"), "DEFAULT"); - PhysicsServer2DManager::get_singleton()->register_server("GodotPhysics2D", callable_mp_static(_createGodotPhysics2DCallback)); - PhysicsServer2DManager::get_singleton()->set_default_server("GodotPhysics2D"); + PhysicsServer2DManager::get_singleton()->register_server("Dummy", callable_mp_static(_create_dummy_physics_server_2d)); GDREGISTER_ABSTRACT_CLASS(NavigationServer2D); GDREGISTER_CLASS(NavigationPathQueryParameters2D); @@ -323,8 +306,7 @@ void register_server_types() { GLOBAL_DEF(PropertyInfo(Variant::STRING, PhysicsServer3DManager::setting_property_name, PROPERTY_HINT_ENUM, "DEFAULT"), "DEFAULT"); - PhysicsServer3DManager::get_singleton()->register_server("GodotPhysics3D", callable_mp_static(_createGodotPhysics3DCallback)); - PhysicsServer3DManager::get_singleton()->set_default_server("GodotPhysics3D"); + PhysicsServer3DManager::get_singleton()->register_server("Dummy", callable_mp_static(_create_dummy_physics_server_3d)); GDREGISTER_ABSTRACT_CLASS(XRInterface); GDREGISTER_CLASS(XRVRS); diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h index 1a566b518d..6da7446e69 100644 --- a/servers/rendering/dummy/storage/texture_storage.h +++ b/servers/rendering/dummy/storage/texture_storage.h @@ -90,12 +90,14 @@ public: }; virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override {} virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override {} + virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override {} virtual void texture_proxy_initialize(RID p_texture, RID p_base) override {} //all slices, then all the mipmaps, must be coherent virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override { return RID(); } virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override {} virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override {} + virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override {} virtual void texture_proxy_update(RID p_proxy, RID p_base) override {} //these two APIs can be used together or in combination with the others. diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index e6a745d3b4..39c3e9b168 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -200,6 +200,8 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c #endif builder.append(String("#define RENDER_DRIVER_") + OS::get_singleton()->get_current_rendering_driver_name().to_upper() + "\n"); + builder.append("#define samplerExternalOES sampler2D\n"); + builder.append("#define textureExternalOES texture2D\n"); } break; case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index 5e19c24246..704aafdfa5 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -214,6 +214,8 @@ void main() { color_interp = color; + vertex = (canvas_data.canvas_transform * vec4(vertex, 0.0, 1.0)).xy; + if (canvas_data.use_pixel_snap) { vertex = floor(vertex + 0.5); // precision issue on some hardware creates artifacts within texture @@ -221,8 +223,6 @@ void main() { uv += 1e-5; } - vertex = (canvas_data.canvas_transform * vec4(vertex, 0.0, 1.0)).xy; - vertex_interp = vertex; uv_interp = uv; diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl index 841f02f673..fa3b45a962 100644 --- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl @@ -256,8 +256,12 @@ vec3 tonemap_aces(vec3 color, float white) { return color_tonemapped / white_tonemapped; } +// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt vec3 tonemap_reinhard(vec3 color, float white) { - return (white * color + color) / (color * white + white); + float white_squared = white * white; + vec3 white_squared_color = white_squared * color; + // Equivalent to color * (1 + color / white_squared) / (1 + color) + return (white_squared_color + color * color) / (white_squared_color + white_squared); } vec3 linear_to_srgb(vec3 color) { @@ -272,7 +276,7 @@ vec3 linear_to_srgb(vec3 color) { #define TONEMAPPER_FILMIC 2 #define TONEMAPPER_ACES 3 -vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color +vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR // Ensure color values passed to tonemappers are positive. // They can be negative in the case of negative lights, which leads to undesired behavior. if (params.tonemapper == TONEMAPPER_LINEAR) { diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index 9f390c99f9..3bfc1bd15c 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -1639,6 +1639,7 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture "sampler2DArray", "sampler3D", "samplerCube", + "samplerExternalOES", }; RS::GlobalShaderParameterType gvtype = RS::GLOBAL_VAR_TYPE_MAX; diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index c885ad52d1..0025fc5ab7 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -128,7 +128,7 @@ private: h = hash_murmur3_one_32(p_val.layers, h); h = hash_murmur3_one_32(p_val.mipmap, h); h = hash_murmur3_one_32(p_val.mipmaps, h); - h = hash_murmur3_one_32(p_val.texture_view.format_override); + h = hash_murmur3_one_32(p_val.texture_view.format_override, h); h = hash_murmur3_one_32(p_val.texture_view.swizzle_r, h); h = hash_murmur3_one_32(p_val.texture_view.swizzle_g, h); h = hash_murmur3_one_32(p_val.texture_view.swizzle_b, h); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 81ab384edc..e5a8dbb9b2 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1087,6 +1087,9 @@ void TextureStorage::texture_3d_initialize(RID p_texture, Image::Format p_format texture_owner.initialize_rid(p_texture, texture); } +void TextureStorage::texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) { +} + void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) { Texture *tex = texture_owner.get_or_null(p_base); ERR_FAIL_NULL(tex); @@ -1361,6 +1364,9 @@ void TextureStorage::texture_3d_update(RID p_texture, const Vector<Ref<Image>> & RD::get_singleton()->texture_update(tex->rd_texture, 0, all_data); } +void TextureStorage::texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) { +} + void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_NULL(tex); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index d352690fff..9de4ff7b6b 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -491,12 +491,14 @@ public: virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override; virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override; virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override; + virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override; virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override; virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override; virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override; + virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override; virtual void texture_proxy_update(RID p_proxy, RID p_base) override; //these two APIs can be used together or in combination with the others. diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 781d29ffaa..4d6435f48a 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -41,14 +41,31 @@ static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport, RendererCanvasCull::Canvas *p_canvas, RendererViewport::Viewport::CanvasData *p_canvas_data, const Vector2 &p_vp_size) { Transform2D xf = p_viewport->global_transform; + Vector2 pixel_snap_offset; + if (p_viewport->snap_2d_transforms_to_pixel) { + // We use `floor(p + 0.5)` to snap canvas items, but `ceil(p - 0.5)` + // to snap viewport transform because the viewport transform is inverse + // to the camera transform. Also, if the viewport size is not divisible + // by 2, the center point is offset by 0.5 px and we need to add 0.5 + // before rounding to cancel it out. + pixel_snap_offset.x = (p_viewport->size.width % 2) ? 0.0 : -0.5; + pixel_snap_offset.y = (p_viewport->size.height % 2) ? 0.0 : -0.5; + } + float scale = 1.0; if (p_viewport->canvas_map.has(p_canvas->parent)) { Transform2D c_xform = p_viewport->canvas_map[p_canvas->parent].transform; + if (p_viewport->snap_2d_transforms_to_pixel) { + c_xform.columns[2] = (c_xform.columns[2] * p_canvas->parent_scale + pixel_snap_offset).ceil() / p_canvas->parent_scale; + } xf = xf * c_xform; scale = p_canvas->parent_scale; } Transform2D c_xform = p_canvas_data->transform; + if (p_viewport->snap_2d_transforms_to_pixel) { + c_xform.columns[2] = (c_xform.columns[2] + pixel_snap_offset).ceil(); + } xf = xf * c_xform; if (scale != 1.0 && !RSG::canvas->disable_scale) { diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index fb4f4aa756..2dcdc3f254 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -162,6 +162,17 @@ public: return ret; \ } +#define FUNCRIDTEX3(m_type, m_type1, m_type2, m_type3) \ + virtual RID m_type##_create(m_type1 p1, m_type2 p2, m_type3 p3) override { \ + RID ret = RSG::texture_storage->texture_allocate(); \ + if (Thread::get_caller_id() == server_thread || RSG::texture_storage->can_create_resources_async()) { \ + RSG::texture_storage->m_type##_initialize(ret, p1, p2, p3); \ + } else { \ + command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret, p1, p2, p3); \ + } \ + return ret; \ + } + #define FUNCRIDTEX6(m_type, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \ virtual RID m_type##_create(m_type1 p1, m_type2 p2, m_type3 p3, m_type4 p4, m_type5 p5, m_type6 p6) override { \ RID ret = RSG::texture_storage->texture_allocate(); \ @@ -177,6 +188,7 @@ public: FUNCRIDTEX1(texture_2d, const Ref<Image> &) FUNCRIDTEX2(texture_2d_layered, const Vector<Ref<Image>> &, TextureLayeredType) FUNCRIDTEX6(texture_3d, Image::Format, int, int, int, bool, const Vector<Ref<Image>> &) + FUNCRIDTEX3(texture_external, int, int, uint64_t) FUNCRIDTEX1(texture_proxy, RID) // Called directly, not through the command queue. @@ -187,6 +199,7 @@ public: //these go through command queue if they are in another thread FUNC3(texture_2d_update, RID, const Ref<Image> &, int) FUNC2(texture_3d_update, RID, const Vector<Ref<Image>> &) + FUNC4(texture_external_update, RID, int, int, uint64_t) FUNC2(texture_proxy_update, RID, RID) //these also go pass-through diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 5f97fa0c9b..3a0b9cf158 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -113,6 +113,8 @@ static int _get_datatype_alignment(SL::DataType p_type) { return 16; case SL::TYPE_SAMPLERCUBEARRAY: return 16; + case SL::TYPE_SAMPLEREXT: + return 16; case SL::TYPE_STRUCT: return 0; case SL::TYPE_MAX: { diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 5a3c5d2fd0..14bd3841df 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -315,6 +315,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_TYPE_USAMPLER3D, "usampler3D", KCF_SAMPLER_DATATYPE, {}, {} }, { TK_TYPE_SAMPLERCUBE, "samplerCube", KCF_SAMPLER_DATATYPE, {}, {} }, { TK_TYPE_SAMPLERCUBEARRAY, "samplerCubeArray", KCF_SAMPLER_DATATYPE, {}, {} }, + { TK_TYPE_SAMPLEREXT, "samplerExternalOES", KCF_SAMPLER_DATATYPE, {}, {} }, // interpolation qualifiers @@ -1027,7 +1028,8 @@ bool ShaderLanguage::is_token_datatype(TokenType p_type) { p_type == TK_TYPE_ISAMPLER3D || p_type == TK_TYPE_USAMPLER3D || p_type == TK_TYPE_SAMPLERCUBE || - p_type == TK_TYPE_SAMPLERCUBEARRAY); + p_type == TK_TYPE_SAMPLERCUBEARRAY || + p_type == TK_TYPE_SAMPLEREXT); } ShaderLanguage::DataType ShaderLanguage::get_token_datatype(TokenType p_type) { @@ -1162,6 +1164,8 @@ String ShaderLanguage::get_datatype_name(DataType p_type) { return "samplerCube"; case TYPE_SAMPLERCUBEARRAY: return "samplerCubeArray"; + case TYPE_SAMPLEREXT: + return "samplerExternalOES"; case TYPE_STRUCT: return "struct"; case TYPE_MAX: @@ -3169,6 +3173,8 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLEREXT, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLEREXT, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, // textureProj @@ -4482,7 +4488,8 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<Scalar> &p_value, case ShaderLanguage::TYPE_USAMPLER2D: case ShaderLanguage::TYPE_USAMPLER3D: case ShaderLanguage::TYPE_SAMPLERCUBE: - case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: { + case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: + case ShaderLanguage::TYPE_SAMPLEREXT: { // Texture types, likely not relevant here. break; } @@ -4707,6 +4714,17 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform pi.hint_string = "Texture3D"; } } break; + case ShaderLanguage::TYPE_SAMPLEREXT: { + if (p_uniform.array_size > 0) { + pi.type = Variant::ARRAY; + pi.hint = PROPERTY_HINT_ARRAY_TYPE; + pi.hint_string = MAKE_RESOURCE_TYPE_HINT("ExternalTexture"); + } else { + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "ExternalTexture"; + } + } break; case ShaderLanguage::TYPE_STRUCT: { // FIXME: Implement this. } break; @@ -4780,6 +4798,8 @@ uint32_t ShaderLanguage::get_datatype_size(ShaderLanguage::DataType p_type) { return 16; case TYPE_SAMPLERCUBEARRAY: return 16; + case TYPE_SAMPLEREXT: + return 16; case TYPE_STRUCT: return 0; case TYPE_MAX: { @@ -4921,6 +4941,7 @@ ShaderLanguage::DataType ShaderLanguage::get_scalar_type(DataType p_type) { TYPE_UINT, TYPE_FLOAT, TYPE_FLOAT, + TYPE_FLOAT, TYPE_VOID, }; @@ -4963,6 +4984,7 @@ int ShaderLanguage::get_cardinality(DataType p_type) { 1, 1, 1, + 1, }; static_assert(sizeof(cardinality_table) / sizeof(*cardinality_table) == TYPE_MAX); diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index b0d579dfe7..48df77f6bb 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -91,6 +91,7 @@ public: TK_TYPE_USAMPLER3D, TK_TYPE_SAMPLERCUBE, TK_TYPE_SAMPLERCUBEARRAY, + TK_TYPE_SAMPLEREXT, TK_INTERPOLATION_FLAT, TK_INTERPOLATION_SMOOTH, TK_CONST, @@ -235,6 +236,7 @@ public: TYPE_USAMPLER3D, TYPE_SAMPLERCUBE, TYPE_SAMPLERCUBEARRAY, + TYPE_SAMPLEREXT, TYPE_STRUCT, TYPE_MAX }; diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h index 6e1cb9eb7f..fd17b052ee 100644 --- a/servers/rendering/storage/texture_storage.h +++ b/servers/rendering/storage/texture_storage.h @@ -69,12 +69,14 @@ public: virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) = 0; virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) = 0; virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) = 0; + virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) = 0; virtual void texture_proxy_initialize(RID p_texture, RID p_base) = 0; //all slices, then all the mipmaps, must be coherent virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) = 0; virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) = 0; + virtual void texture_external_update(RID p_proxy, int p_width, int p_height, uint64_t p_external_buffer) = 0; virtual void texture_proxy_update(RID p_proxy, RID p_base) = 0; //these two APIs can be used together or in combination with the others. diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index a1c05fa2e7..0ad56961c0 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3405,6 +3405,7 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_SAMPLER2DARRAY); BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_SAMPLER3D); BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_SAMPLERCUBE); + BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_SAMPLEREXT); BIND_ENUM_CONSTANT(GLOBAL_VAR_TYPE_MAX); /* Free */ diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 2b45b73f09..a130ae0ba2 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -137,12 +137,14 @@ public: virtual RID texture_2d_create(const Ref<Image> &p_image) = 0; virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, TextureLayeredType p_layered_type) = 0; virtual RID texture_3d_create(Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) = 0; //all slices, then all the mipmaps, must be coherent + virtual RID texture_external_create(int p_width, int p_height, uint64_t p_external_buffer = 0) = 0; virtual RID texture_proxy_create(RID p_base) = 0; virtual RID texture_create_from_native_handle(TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, TextureLayeredType p_layered_type = TEXTURE_LAYERED_2D_ARRAY) = 0; virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) = 0; + virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer = 0) = 0; virtual void texture_proxy_update(RID p_texture, RID p_proxy_to) = 0; // These two APIs can be used together or in combination with the others. @@ -1647,6 +1649,7 @@ public: GLOBAL_VAR_TYPE_SAMPLER2DARRAY, GLOBAL_VAR_TYPE_SAMPLER3D, GLOBAL_VAR_TYPE_SAMPLERCUBE, + GLOBAL_VAR_TYPE_SAMPLEREXT, GLOBAL_VAR_TYPE_MAX }; diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index a9f615af84..301771a3de 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -1888,7 +1888,7 @@ TEST_CASE("[String] validate_node_name") { CHECK(name_with_invalid_chars.validate_node_name() == "Name with invalid characters ____removed!"); } -TEST_CASE("[String] validate_identifier") { +TEST_CASE("[String] validate_ascii_identifier") { String empty_string; CHECK(empty_string.validate_ascii_identifier() == "_"); @@ -1902,6 +1902,20 @@ TEST_CASE("[String] validate_identifier") { CHECK(name_with_invalid_chars.validate_ascii_identifier() == "Invalid_characters_______"); } +TEST_CASE("[String] validate_unicode_identifier") { + String empty_string; + CHECK(empty_string.validate_unicode_identifier() == "_"); + + String numeric_only = "12345"; + CHECK(numeric_only.validate_unicode_identifier() == "_12345"); + + String name_with_spaces = "Name with spaces"; + CHECK(name_with_spaces.validate_unicode_identifier() == "Name_with_spaces"); + + String name_with_invalid_chars = U"Invalid characters:@*#&世界"; + CHECK(name_with_invalid_chars.validate_unicode_identifier() == U"Invalid_characters_____世界"); +} + TEST_CASE("[String] Variant indexed get") { Variant s = String("abcd"); bool valid = false; diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h index 9ec1b812df..ef630ad4f7 100644 --- a/tests/scene/test_code_edit.h +++ b/tests/scene/test_code_edit.h @@ -4779,6 +4779,31 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") { CHECK(code_edit->get_caret_column(3) == 0); } + SUBCASE("[SceneTree][CodeEdit] cut when empty selection clipboard disabled") { + DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton()); + code_edit->set_empty_selection_clipboard_enabled(false); + DS->clipboard_set(""); + + code_edit->set_text("this is\nsome\n"); + code_edit->set_caret_line(0); + code_edit->set_caret_column(6); + MessageQueue::get_singleton()->flush(); + SIGNAL_DISCARD("text_set"); + SIGNAL_DISCARD("text_changed"); + SIGNAL_DISCARD("lines_edited_from"); + SIGNAL_DISCARD("caret_changed"); + + code_edit->cut(); + MessageQueue::get_singleton()->flush(); + CHECK(DS->clipboard_get() == ""); + CHECK(code_edit->get_text() == "this is\nsome\n"); + CHECK(code_edit->get_caret_line() == 0); + CHECK(code_edit->get_caret_column() == 6); + SIGNAL_CHECK_FALSE("caret_changed"); + SIGNAL_CHECK_FALSE("text_changed"); + SIGNAL_CHECK_FALSE("lines_edited_from"); + } + SUBCASE("[SceneTree][CodeEdit] new line") { // Add a new line. code_edit->set_text("test new line"); diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 46a5046b21..c41eebdf3a 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -3585,6 +3585,54 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK_FALSE("lines_edited_from"); } + SUBCASE("[TextEdit] cut when empty selection clipboard disabled") { + text_edit->set_empty_selection_clipboard_enabled(false); + DS->clipboard_set(""); + + text_edit->set_text("this is\nsome\n"); + text_edit->set_caret_line(0); + text_edit->set_caret_column(6); + MessageQueue::get_singleton()->flush(); + SIGNAL_DISCARD("text_set"); + SIGNAL_DISCARD("text_changed"); + SIGNAL_DISCARD("lines_edited_from"); + SIGNAL_DISCARD("caret_changed"); + + text_edit->cut(); + MessageQueue::get_singleton()->flush(); + CHECK(DS->clipboard_get() == ""); + CHECK(text_edit->get_text() == "this is\nsome\n"); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 6); + SIGNAL_CHECK_FALSE("caret_changed"); + SIGNAL_CHECK_FALSE("text_changed"); + SIGNAL_CHECK_FALSE("lines_edited_from"); + } + + SUBCASE("[TextEdit] copy when empty selection clipboard disabled") { + text_edit->set_empty_selection_clipboard_enabled(false); + DS->clipboard_set(""); + + text_edit->set_text("this is\nsome\n"); + text_edit->set_caret_line(0); + text_edit->set_caret_column(6); + MessageQueue::get_singleton()->flush(); + SIGNAL_DISCARD("text_set"); + SIGNAL_DISCARD("text_changed"); + SIGNAL_DISCARD("lines_edited_from"); + SIGNAL_DISCARD("caret_changed"); + + text_edit->copy(); + MessageQueue::get_singleton()->flush(); + CHECK(DS->clipboard_get() == ""); + CHECK(text_edit->get_text() == "this is\nsome\n"); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 6); + SIGNAL_CHECK_FALSE("caret_changed"); + SIGNAL_CHECK_FALSE("text_changed"); + SIGNAL_CHECK_FALSE("lines_edited_from"); + } + SIGNAL_UNWATCH(text_edit, "text_set"); SIGNAL_UNWATCH(text_edit, "text_changed"); SIGNAL_UNWATCH(text_edit, "lines_edited_from"); diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h index 9d02c41719..dde37944ec 100644 --- a/tests/scene/test_viewport.h +++ b/tests/scene/test_viewport.h @@ -38,6 +38,7 @@ #include "scene/main/canvas_layer.h" #include "scene/main/window.h" #include "scene/resources/2d/rectangle_shape_2d.h" +#include "servers/physics_server_2d_dummy.h" #include "tests/test_macros.h" @@ -1550,6 +1551,12 @@ int TestArea2D::counter = 0; TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") { // FIXME: MOUSE_MODE_CAPTURED if-conditions are not testable, because DisplayServerMock doesn't support it. + // NOTE: This test requires a real physics server. + PhysicsServer2DDummy *physics_server_2d_dummy = Object::cast_to<PhysicsServer2DDummy>(PhysicsServer2D::get_singleton()); + if (physics_server_2d_dummy) { + return; + } + struct PickingCollider { TestArea2D *a; CollisionShape2D *c; diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 949e4f0b33..12ff3ad4bc 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -174,8 +174,10 @@ #include "servers/navigation_server_3d.h" #endif // _3D_DISABLED #include "servers/physics_server_2d.h" +#include "servers/physics_server_2d_dummy.h" #ifndef _3D_DISABLED #include "servers/physics_server_3d.h" +#include "servers/physics_server_3d_dummy.h" #endif // _3D_DISABLED #include "servers/rendering/rendering_server_default.h" @@ -290,10 +292,16 @@ struct GodotTestCaseListener : public doctest::IReporter { #ifndef _3D_DISABLED physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server(); + if (!physics_server_3d) { + physics_server_3d = memnew(PhysicsServer3DDummy); + } physics_server_3d->init(); #endif // _3D_DISABLED physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server(); + if (!physics_server_2d) { + physics_server_2d = memnew(PhysicsServer2DDummy); + } physics_server_2d->init(); #ifndef _3D_DISABLED diff --git a/thirdparty/README.md b/thirdparty/README.md index a6686c539a..4c47b91c8c 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -614,7 +614,7 @@ to solve some MSVC warnings. See the patches in the `patches` directory. ## miniupnpc - Upstream: https://github.com/miniupnp/miniupnp -- Version: 2.2.7 (d4d5ec7d48c093b37b2ea5d7171ede21ce9d7ff2, 2024) +- Version: 2.2.8 (b55145ec095652289a59c33603f3abafee898273, 2024) - License: BSD-3-Clause Files extracted from upstream source: diff --git a/thirdparty/miniupnpc/include/miniupnpc.h b/thirdparty/miniupnpc/include/miniupnpc.h index 808c6ad975..fd951a0836 100644 --- a/thirdparty/miniupnpc/include/miniupnpc.h +++ b/thirdparty/miniupnpc/include/miniupnpc.h @@ -1,9 +1,9 @@ -/* $Id: miniupnpc.h,v 1.63 2024/01/04 00:45:17 nanard Exp $ */ +/* $Id: miniupnpc.h,v 1.66 2024/06/08 22:13:14 nanard Exp $ */ /* vim: tabstop=4 shiftwidth=4 noexpandtab * Project: miniupnp * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author: Thomas Bernard - * Copyright (c) 2005-2022 Thomas Bernard + * Copyright (c) 2005-2024 Thomas Bernard * This software is subjects to the conditions detailed * in the LICENCE file provided within this distribution */ #ifndef MINIUPNPC_H_INCLUDED @@ -20,8 +20,8 @@ #define UPNPDISCOVER_MEMORY_ERROR (-102) /* versions : */ -#define MINIUPNPC_VERSION "2.2.6" -#define MINIUPNPC_API_VERSION 17 +#define MINIUPNPC_VERSION "2.2.8" +#define MINIUPNPC_API_VERSION 18 /* Source port: Using "1" as an alias for 1900 for backwards compatibility @@ -108,9 +108,11 @@ struct UPNPUrls { * return values : * 0 = NO IGD found * 1 = A valid connected IGD has been found - * 2 = A valid IGD has been found but it reported as + * 2 = A valid connected IGD has been found but its + * IP address is reserved (non routable) + * 3 = A valid IGD has been found but it reported as * not connected - * 3 = an UPnP device has been found but was not recognized as an IGD + * 4 = an UPnP device has been found but was not recognized as an IGD * * In any non zero return case, the urls and data structures * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to @@ -119,8 +121,9 @@ struct UPNPUrls { MINIUPNP_LIBSPEC int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, - struct IGDdatas * data, - char * lanaddr, int lanaddrlen); + struct IGDdatas * data, + char * lanaddr, int lanaddrlen, + char * wanaddr, int wanaddrlen); /* UPNP_GetIGDFromUrl() * Used when skipping the discovery process. diff --git a/thirdparty/miniupnpc/src/addr_is_reserved.c b/thirdparty/miniupnpc/src/addr_is_reserved.c index 18c6424201..145b504823 100644 --- a/thirdparty/miniupnpc/src/addr_is_reserved.c +++ b/thirdparty/miniupnpc/src/addr_is_reserved.c @@ -3,7 +3,7 @@ * Project : miniupnp * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas BERNARD - * copyright (c) 2005-2021 Thomas Bernard + * copyright (c) 2005-2024 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENSE file. */ #ifdef _WIN32 @@ -21,6 +21,9 @@ typedef unsigned long uint32_t; #include <netinet/in.h> #include <arpa/inet.h> #endif /* _WIN32 */ +#ifdef DEBUG +#include <stdio.h> +#endif /* List of IP address blocks which are private / reserved and therefore not suitable for public external IP addresses */ #define IP(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) @@ -71,8 +74,12 @@ int addr_is_reserved(const char * addr_str) address = ntohl(addr_n); for (i = 0; i < sizeof(reserved)/sizeof(reserved[0]); ++i) { - if ((address >> reserved[i].rmask) == (reserved[i].address >> reserved[i].rmask)) + if ((address >> reserved[i].rmask) == (reserved[i].address >> reserved[i].rmask)) { +#ifdef DEBUG + printf("IP address %s is reserved\n", addr_str); +#endif return 1; + } } return 0; diff --git a/thirdparty/miniupnpc/src/minisoap.c b/thirdparty/miniupnpc/src/minisoap.c index 903ac5ffc6..5c6bf01684 100644 --- a/thirdparty/miniupnpc/src/minisoap.c +++ b/thirdparty/miniupnpc/src/minisoap.c @@ -2,7 +2,7 @@ /* vim: tabstop=4 shiftwidth=4 noexpandtab * Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005-2023 Thomas Bernard + * Copyright (c) 2005-2024 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * @@ -83,7 +83,7 @@ int soapPostSubmit(SOCKET fd, * Using HTTP/1.1 means we need to support chunked transfer-encoding : * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked * transfer encoding. */ - /* Connection: Close is normally there only in HTTP/1.1 but who knows */ + /* Connection: close is normally there only in HTTP/1.1 but who knows */ portstr[0] = '\0'; if(port != 80) snprintf(portstr, sizeof(portstr), ":%hu", port); @@ -98,9 +98,8 @@ int soapPostSubmit(SOCKET fd, "Content-Type: text/xml; charset=\"utf-8\"\r\n" #endif "SOAPAction: \"%s\"\r\n" - "Connection: Close\r\n" + "Connection: close\r\n" "Cache-Control: no-cache\r\n" /* ??? */ - "Pragma: no-cache\r\n" "\r\n", url, httpversion, host, portstr, bodysize, action); if ((unsigned int)headerssize >= sizeof(headerbuf)) diff --git a/thirdparty/miniupnpc/src/minissdpc.c b/thirdparty/miniupnpc/src/minissdpc.c index 98c5b37463..57cb99962e 100644 --- a/thirdparty/miniupnpc/src/minissdpc.c +++ b/thirdparty/miniupnpc/src/minissdpc.c @@ -1,9 +1,9 @@ -/* $Id: minissdpc.c,v 1.49 2021/05/13 11:00:36 nanard Exp $ */ +/* $Id: minissdpc.c,v 1.51 2024/05/16 00:12:05 nanard Exp $ */ /* vim: tabstop=4 shiftwidth=4 noexpandtab * Project : miniupnp * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas BERNARD - * copyright (c) 2005-2021 Thomas Bernard + * copyright (c) 2005-2024 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #include <stdio.h> @@ -548,7 +548,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[], #ifdef _WIN32 unsigned long _ttl = (unsigned long)ttl; #endif - int linklocal = 1; + int linklocal = 0; /* try first with site-local multicast */ int sentok; if(error) @@ -1007,9 +1007,10 @@ ssdpDiscoverDevices(const char * const deviceTypes[], /* switch linklocal flag */ if(linklocal) { linklocal = 0; - --deviceIndex; } else { + /* try again with linklocal multicast */ linklocal = 1; + --deviceIndex; } } } diff --git a/thirdparty/miniupnpc/src/miniupnpc.c b/thirdparty/miniupnpc/src/miniupnpc.c index 696af93237..9da1496b37 100644 --- a/thirdparty/miniupnpc/src/miniupnpc.c +++ b/thirdparty/miniupnpc/src/miniupnpc.c @@ -3,7 +3,7 @@ * Project : miniupnp * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas BERNARD - * copyright (c) 2005-2021 Thomas Bernard + * copyright (c) 2005-2024 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENSE file. */ #include <stdlib.h> @@ -92,15 +92,15 @@ MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGD #endif } -/* simpleUPnPcommand2 : +/* simpleUPnPcommand : * not so simple ! * return values : * pointer - OK * NULL - error */ -static char * -simpleUPnPcommand2(SOCKET s, const char * url, const char * service, - const char * action, struct UPNParg * args, - int * bufsize, const char * httpversion) +char * +simpleUPnPcommand(int s, const char * url, const char * service, + const char * action, struct UPNParg * args, + int * bufsize) { char hostname[MAXHOSTNAMELEN+1]; unsigned short port = 0; @@ -197,15 +197,15 @@ simpleUPnPcommand2(SOCKET s, const char * url, const char * service, return NULL; } if(!parseURL(url, hostname, &port, &path, NULL)) return NULL; - if(ISINVALID(s)) { + if(ISINVALID((SOCKET)s)) { s = connecthostport(hostname, port, 0); - if(ISINVALID(s)) { + if(ISINVALID((SOCKET)s)) { /* failed to connect */ return NULL; } } - n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion); + n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, "1.1"); if(n<=0) { #ifdef DEBUG printf("Error sending SOAP request\n"); @@ -229,33 +229,6 @@ simpleUPnPcommand2(SOCKET s, const char * url, const char * service, return buf; } -/* simpleUPnPcommand : - * not so simple ! - * return values : - * pointer - OK - * NULL - error */ -char * -simpleUPnPcommand(int s, const char * url, const char * service, - const char * action, struct UPNParg * args, - int * bufsize) -{ - char * buf; - -#if 1 - buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1"); -#else - buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.0"); - if (!buf || *bufsize == 0) - { -#if DEBUG - printf("Error or no result from SOAP request; retrying with HTTP/1.1\n"); -#endif - buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1"); - } -#endif - return buf; -} - /* upnpDiscoverDevices() : * return a chained list of all devices found or NULL if * no devices was found. @@ -534,9 +507,11 @@ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data) * -1 = Internal error * 0 = NO IGD found * 1 = A valid connected IGD has been found - * 2 = A valid IGD has been found but it reported as + * 2 = A valid connected IGD has been found but its + * IP address is reserved (non routable) + * 3 = A valid IGD has been found but it reported as * not connected - * 3 = an UPnP device has been found but was not recognized as an IGD + * 4 = an UPnP device has been found but was not recognized as an IGD * * In any positive non zero return case, the urls and data structures * passed as parameters are set. Don't forget to call FreeUPNPUrls(urls) to @@ -545,11 +520,13 @@ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data) MINIUPNP_LIBSPEC int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, - struct IGDdatas * data, - char * lanaddr, int lanaddrlen) + struct IGDdatas * data, + char * lanaddr, int lanaddrlen, + char * wanaddr, int wanaddrlen) { struct xml_desc { char lanaddr[40]; + char wanaddr[40]; char * xml; int size; int is_igd; @@ -557,8 +534,8 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPDev * dev; int ndev = 0; int i; - int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */ - char extIpAddr[16]; + int state = -1; /* state 1 : IGD connected. State 2 : connected with reserved IP. + * State 3 : IGD. State 4 : anything */ int status_code = -1; if(!devlist) @@ -602,7 +579,7 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, } } /* iterate the list to find a device depending on state */ - for(state = 1; state <= 3; state++) + for(state = 1; state <= 4; state++) { for(dev = devlist, i = 0; dev; dev = dev->pNext, i++) { @@ -611,14 +588,14 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, memset(data, 0, sizeof(struct IGDdatas)); memset(urls, 0, sizeof(struct UPNPUrls)); parserootdesc(desc[i].xml, desc[i].size, data); - if(desc[i].is_igd || state >= 3 ) + if(desc[i].is_igd || state >= 4 ) { int is_connected; GetUPNPUrls(urls, data, dev->descURL, dev->scope_id); - /* in state 2 and 3 we don't test if device is connected ! */ - if(state >= 2) + /* in state 3 and 4 we don't test if device is connected ! */ + if(state >= 3) goto free_and_return; is_connected = UPNPIGD_IsConnected(urls, data); #ifdef DEBUG @@ -626,9 +603,11 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, urls->controlURL, is_connected); #endif /* checks that status is connected AND there is a external IP address assigned */ - if(is_connected && - (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) { - if(!addr_is_reserved(extIpAddr)) + if(is_connected) { + if(state >= 2) + goto free_and_return; + if(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, desc[i].wanaddr) == 0 + && !addr_is_reserved(desc[i].wanaddr)) goto free_and_return; } FreeUPNPUrls(urls); @@ -647,9 +626,11 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, printf("UPNPIGD_IsConnected(%s) = %d\n", urls->controlURL, is_connected); #endif - if(is_connected && - (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) { - if(!addr_is_reserved(extIpAddr)) + if(is_connected) { + if(state >= 2) + goto free_and_return; + if(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, desc[i].wanaddr) == 0 + && !addr_is_reserved(desc[i].wanaddr)) goto free_and_return; } FreeUPNPUrls(urls); @@ -661,8 +642,12 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, } state = 0; free_and_return: - if (lanaddr != NULL && state >= 1 && state <= 3 && i < ndev) - strncpy(lanaddr, desc[i].lanaddr, lanaddrlen); + if (state >= 1 && state <= 4 && i < ndev) { + if (lanaddr != NULL) + strncpy(lanaddr, desc[i].lanaddr, lanaddrlen); + if (wanaddr != NULL) + strncpy(wanaddr, desc[i].wanaddr, wanaddrlen); + } for(i = 0; i < ndev; i++) free(desc[i].xml); free(desc); diff --git a/thirdparty/miniupnpc/src/miniupnpcstrings.h b/thirdparty/miniupnpc/src/miniupnpcstrings.h index f5730111af..d40c2455ba 100644 --- a/thirdparty/miniupnpc/src/miniupnpcstrings.h +++ b/thirdparty/miniupnpc/src/miniupnpcstrings.h @@ -2,7 +2,7 @@ #define MINIUPNPCSTRINGS_H_INCLUDED #define OS_STRING "Godot Engine/1.0" -#define MINIUPNPC_VERSION_STRING "2.2.7" +#define MINIUPNPC_VERSION_STRING "2.2.8" #if 0 /* according to "UPnP Device Architecture 1.0" */ diff --git a/thirdparty/miniupnpc/src/miniwget.c b/thirdparty/miniupnpc/src/miniwget.c index e76a5e516b..a7a32dfdba 100644 --- a/thirdparty/miniupnpc/src/miniwget.c +++ b/thirdparty/miniupnpc/src/miniwget.c @@ -2,7 +2,7 @@ /* Project : miniupnp * Website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas Bernard - * Copyright (c) 2005-2023 Thomas Bernard + * Copyright (c) 2005-2024 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -443,9 +443,8 @@ miniwget3(const char * host, len = snprintf(buf, sizeof(buf), "GET %s HTTP/%s\r\n" "Host: %s:%d\r\n" - "Connection: Close\r\n" + "Connection: close\r\n" "User-Agent: " OS_STRING " " UPNP_VERSION_STRING " MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" - "\r\n", path, httpversion, host, port); if ((unsigned int)len >= sizeof(buf)) @@ -474,41 +473,6 @@ miniwget3(const char * host, return content; } -/* miniwget2() : - * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */ -static void * -miniwget2(const char * host, - unsigned short port, const char * path, - int * size, char * addr_str, int addr_str_len, - unsigned int scope_id, int * status_code) -{ - char * respbuffer; - -#if 1 - respbuffer = miniwget3(host, port, path, size, - addr_str, addr_str_len, "1.1", - scope_id, status_code); -#else - respbuffer = miniwget3(host, port, path, size, - addr_str, addr_str_len, "1.0", - scope_id, status_code); - if (*size == 0) - { -#ifdef DEBUG - printf("Retrying with HTTP/1.1\n"); -#endif - free(respbuffer); - respbuffer = miniwget3(host, port, path, size, - addr_str, addr_str_len, "1.1", - scope_id, status_code); - } -#endif - return respbuffer; -} - - - - /* parseURL() * arguments : * url : source string not modified @@ -639,7 +603,7 @@ miniwget(const char * url, int * size, printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", hostname, port, path, scope_id); #endif - return miniwget2(hostname, port, path, size, 0, 0, scope_id, status_code); + return miniwget3(hostname, port, path, size, 0, 0, "1.1", scope_id, status_code); } void * @@ -660,5 +624,5 @@ miniwget_getaddr(const char * url, int * size, printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", hostname, port, path, scope_id); #endif - return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, status_code); + return miniwget3(hostname, port, path, size, addr, addrlen, "1.1", scope_id, status_code); } diff --git a/thirdparty/miniupnpc/src/win32_snprintf.h b/thirdparty/miniupnpc/src/win32_snprintf.h index 1fc284ecff..5064df63bc 100644 --- a/thirdparty/miniupnpc/src/win32_snprintf.h +++ b/thirdparty/miniupnpc/src/win32_snprintf.h @@ -23,9 +23,9 @@ (defined(_MSC_VER) && _MSC_VER < 1900) /* Visual Studio older than 2015 */ || \ (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) && defined(__NO_ISOCEXT)) /* mingw32 without iso c ext */ || \ (defined(__MINGW64_VERSION_MAJOR) && /* mingw-w64 not ... */ !( \ - (defined (__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO != 0)) /* ... with ansi stdio */ || \ + (defined (__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO != 0) /* ... with ansi stdio */ || \ (__MINGW64_VERSION_MAJOR >= 6 && defined(_UCRT)) /* ... at least 6.0.0 with ucrt */ || \ - (__MINGW64_VERSION_MAJOR >= 8 && !defined(__NO_ISOCEXT)) /* ... at least 8.0.0 with iso c ext */ || \ + (__MINGW64_VERSION_MAJOR >= 8 && !defined(__NO_ISOCEXT))) /* ... at least 8.0.0 with iso c ext */ || \ 0) || \ 0) |