diff options
127 files changed, 5085 insertions, 1513 deletions
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 90629204e6..0c21576517 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -30,6 +30,14 @@ jobs: # Skip debug symbols, they're way too big with MSVC. sconsflags: debug_symbols=no vsproj=yes vsproj_gen_only=no windows_subsystem=console bin: "./bin/godot.windows.editor.x86_64.exe" + artifact: true + + - name: Editor w/ clang-cl (target=editor, tests=yes, use_llvm=yes) + cache-name: windows-editor-clang + target: editor + tests: true + sconsflags: debug_symbols=no windows_subsystem=console use_llvm=yes + bin: ./bin/godot.windows.editor.x86_64.llvm.exe - name: Template (target=template_release) cache-name: windows-template @@ -37,6 +45,7 @@ jobs: tests: true sconsflags: debug_symbols=no tests=yes bin: "./bin/godot.windows.template_release.x86_64.console.exe" + artifact: true steps: - uses: actions/checkout@v4 @@ -84,10 +93,12 @@ jobs: continue-on-error: true - name: Prepare artifact + if: ${{ matrix.artifact }} run: | Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force - name: Upload artifact + if: ${{ matrix.artifact }} uses: ./.github/actions/upload-artifact with: name: ${{ matrix.cache-name }} diff --git a/SConstruct b/SConstruct index e1c943bd45..0297cd6e61 100644 --- a/SConstruct +++ b/SConstruct @@ -803,7 +803,7 @@ elif env.msvc: env.Append(CXXFLAGS=["/EHsc"]) # Configure compiler warnings -if env.msvc: # MSVC +if env.msvc and not methods.using_clang(env): # MSVC if env["warnings"] == "no": env.Append(CCFLAGS=["/w"]) else: @@ -853,8 +853,11 @@ else: # GCC, Clang # for putting them in `Set` or `Map`. We don't mind about unreliable ordering. common_warnings += ["-Wno-ordered-compare-function-pointers"] + # clang-cl will interpret `-Wall` as `-Weverything`, workaround with compatibility cast + W_ALL = "-Wall" if not env.msvc else "-W3" + if env["warnings"] == "extra": - env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings) + env.Append(CCFLAGS=[W_ALL, "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings) env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"]) if methods.using_gcc(env): env.Append( @@ -876,9 +879,9 @@ else: # GCC, Clang elif methods.using_clang(env) or methods.using_emcc(env): env.Append(CCFLAGS=["-Wimplicit-fallthrough"]) elif env["warnings"] == "all": - env.Append(CCFLAGS=["-Wall"] + common_warnings) + env.Append(CCFLAGS=[W_ALL] + common_warnings) elif env["warnings"] == "moderate": - env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + common_warnings) + env.Append(CCFLAGS=[W_ALL, "-Wno-unused"] + common_warnings) else: # 'no' env.Append(CCFLAGS=["-w"]) @@ -1032,7 +1035,9 @@ if env["vsproj"]: if env["compiledb"]: if env.scons_version < (4, 0, 0): # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later. - print_error("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version) + print_error( + "The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version + ) Exit(255) env.Tool("compilation_db") diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index 21a9014626..9dca47a0b4 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -144,9 +144,8 @@ void RemoteDebuggerPeerTCP::_read_in() { Error err = decode_variant(var, buf, in_pos, &read); ERR_CONTINUE(read != in_pos || err != OK); ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array."); - mutex.lock(); + MutexLock lock(mutex); in_queue.push_back(var); - mutex.unlock(); } } } diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index c9e609cddc..e764b9c112 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -678,12 +678,10 @@ Error GDExtension::open_library(const String &p_path, const Ref<GDExtensionLoade ERR_FAIL_NULL_V_MSG(p_loader, FAILED, "Can't open GDExtension without a loader."); loader = p_loader; - String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path); + Error err = loader->open_library(p_path); - Error err = loader->open_library(abs_path); - - ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path); - ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path); + ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + p_path); + ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + p_path); err = loader->initialize(&gdextension_get_proc_address, this, &initialization); diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp index eeae6b1996..01efe0d96e 100644 --- a/core/extension/gdextension_manager.cpp +++ b/core/extension/gdextension_manager.cpp @@ -32,14 +32,18 @@ #include "core/extension/gdextension_compat_hashes.h" #include "core/extension/gdextension_library_loader.h" +#include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/object/script_language.h" -GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension) { +GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load) { if (level >= 0) { // Already initialized up to some level. - int32_t minimum_level = p_extension->get_minimum_library_initialization_level(); - if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) { - return LOAD_STATUS_NEEDS_RESTART; + int32_t minimum_level = 0; + if (!p_first_load) { + minimum_level = p_extension->get_minimum_library_initialization_level(); + if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) { + return LOAD_STATUS_NEEDS_RESTART; + } } // Initialize up to current level. for (int32_t i = minimum_level; i <= level; i++) { @@ -51,10 +55,20 @@ GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(cons gdextension_class_icon_paths[kv.key] = kv.value; } +#ifdef TOOLS_ENABLED + // Signals that a new extension is loaded so GDScript can register new class names. + emit_signal("extension_loaded", p_extension); +#endif + return LOAD_STATUS_OK; } GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref<GDExtension> &p_extension) { +#ifdef TOOLS_ENABLED + // Signals that a new extension is unloading so GDScript can unregister class names. + emit_signal("extension_unloading", p_extension); +#endif + if (level >= 0) { // Already initialized up to some level. // Deinitialize down from current level. for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) { @@ -89,7 +103,7 @@ GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(co return LOAD_STATUS_FAILED; } - LoadStatus status = _load_extension_internal(extension); + LoadStatus status = _load_extension_internal(extension, true); if (status != LOAD_STATUS_OK) { return status; } @@ -135,7 +149,7 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String return LOAD_STATUS_FAILED; } - status = _load_extension_internal(extension); + status = _load_extension_internal(extension, false); if (status != LOAD_STATUS_OK) { return status; } @@ -274,6 +288,71 @@ void GDExtensionManager::reload_extensions() { #endif } +bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_extensions) { + Vector<String> extensions_added; + Vector<String> extensions_removed; + + for (const String &E : p_extensions) { + if (!is_extension_loaded(E)) { + extensions_added.push_back(E); + } + } + + Vector<String> loaded_extensions = get_loaded_extensions(); + for (const String &loaded_extension : loaded_extensions) { + if (!p_extensions.has(loaded_extension)) { + // The extension may not have a .gdextension file. + if (!FileAccess::exists(loaded_extension)) { + extensions_removed.push_back(loaded_extension); + } + } + } + + String extension_list_config_file = GDExtension::get_extension_list_config_file(); + if (p_extensions.size()) { + if (extensions_added.size() || extensions_removed.size()) { + // Extensions were added or removed. + Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE); + for (const String &E : p_extensions) { + f->store_line(E); + } + } + } else { + if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) { + // Extensions were removed. + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + da->remove(extension_list_config_file); + } + } + + bool needs_restart = false; + for (const String &extension : extensions_added) { + GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extension); + if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { + needs_restart = true; + } + } + + for (const String &extension : extensions_removed) { + GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extension); + if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { + needs_restart = true; + } + } + +#ifdef TOOLS_ENABLED + if (extensions_added.size() || extensions_removed.size()) { + // Emitting extensions_reloaded so EditorNode can reload Inspector and regenerate documentation. + emit_signal("extensions_reloaded"); + + // Reload all scripts to clear out old references. + callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred(); + } +#endif + + return needs_restart; +} + GDExtensionManager *GDExtensionManager::get_singleton() { return singleton; } @@ -294,6 +373,8 @@ void GDExtensionManager::_bind_methods() { BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART); ADD_SIGNAL(MethodInfo("extensions_reloaded")); + ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension"))); + ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension"))); } GDExtensionManager *GDExtensionManager::singleton = nullptr; diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h index b488189604..39a600474c 100644 --- a/core/extension/gdextension_manager.h +++ b/core/extension/gdextension_manager.h @@ -54,7 +54,7 @@ public: }; private: - LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension); + LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load); LoadStatus _unload_extension_internal(const Ref<GDExtension> &p_extension); #ifdef TOOLS_ENABLED @@ -85,6 +85,7 @@ public: void load_extensions(); void reload_extensions(); + bool ensure_extensions_loaded(const HashSet<String> &p_extensions); GDExtensionManager(); ~GDExtensionManager(); diff --git a/core/io/json.cpp b/core/io/json.cpp index 61051727c1..664ff7857b 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -588,10 +588,756 @@ void JSON::_bind_methods() { ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line); ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message); + ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_classes", "allow_scripts"), &JSON::to_native, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "allow_classes", "allow_scripts"), &JSON::from_native, DEFVAL(false), DEFVAL(false)); + ADD_PROPERTY(PropertyInfo(Variant::NIL, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), "set_data", "get_data"); // Ensures that it can be serialized as binary. } -//// +#define GDTYPE "__gdtype" +#define VALUES "values" +#define PASS_ARG p_allow_classes, p_allow_scripts + +Variant JSON::from_native(const Variant &p_variant, bool p_allow_classes, bool p_allow_scripts) { + switch (p_variant.get_type()) { + case Variant::NIL: { + Dictionary nil; + nil[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return nil; + } break; + case Variant::BOOL: { + return p_variant; + } break; + case Variant::INT: { + return p_variant; + } break; + case Variant::FLOAT: { + return p_variant; + } break; + case Variant::STRING: { + return p_variant; + } break; + case Variant::VECTOR2: { + Dictionary d; + Vector2 v = p_variant; + Array values; + values.push_back(v.x); + values.push_back(v.y); + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::VECTOR2I: { + Dictionary d; + Vector2i v = p_variant; + Array values; + values.push_back(v.x); + values.push_back(v.y); + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::RECT2: { + Dictionary d; + Rect2 r = p_variant; + d["position"] = from_native(r.position); + d["size"] = from_native(r.size); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::RECT2I: { + Dictionary d; + Rect2i r = p_variant; + d["position"] = from_native(r.position); + d["size"] = from_native(r.size); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::VECTOR3: { + Dictionary d; + Vector3 v = p_variant; + Array values; + values.push_back(v.x); + values.push_back(v.y); + values.push_back(v.z); + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::VECTOR3I: { + Dictionary d; + Vector3i v = p_variant; + Array values; + values.push_back(v.x); + values.push_back(v.y); + values.push_back(v.z); + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::TRANSFORM2D: { + Dictionary d; + Transform2D t = p_variant; + d["x"] = from_native(t[0]); + d["y"] = from_native(t[1]); + d["origin"] = from_native(t[2]); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::VECTOR4: { + Dictionary d; + Vector4 v = p_variant; + Array values; + values.push_back(v.x); + values.push_back(v.y); + values.push_back(v.z); + values.push_back(v.w); + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::VECTOR4I: { + Dictionary d; + Vector4i v = p_variant; + Array values; + values.push_back(v.x); + values.push_back(v.y); + values.push_back(v.z); + values.push_back(v.w); + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PLANE: { + Dictionary d; + Plane p = p_variant; + d["normal"] = from_native(p.normal); + d["d"] = p.d; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::QUATERNION: { + Dictionary d; + Quaternion q = p_variant; + Array values; + values.push_back(q.x); + values.push_back(q.y); + values.push_back(q.z); + values.push_back(q.w); + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::AABB: { + Dictionary d; + AABB aabb = p_variant; + d["position"] = from_native(aabb.position); + d["size"] = from_native(aabb.size); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::BASIS: { + Dictionary d; + Basis t = p_variant; + d["x"] = from_native(t.get_column(0)); + d["y"] = from_native(t.get_column(1)); + d["z"] = from_native(t.get_column(2)); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::TRANSFORM3D: { + Dictionary d; + Transform3D t = p_variant; + d["basis"] = from_native(t.basis); + d["origin"] = from_native(t.origin); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PROJECTION: { + Dictionary d; + Projection t = p_variant; + d["x"] = from_native(t[0]); + d["y"] = from_native(t[1]); + d["z"] = from_native(t[2]); + d["w"] = from_native(t[3]); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::COLOR: { + Dictionary d; + Color c = p_variant; + Array values; + values.push_back(c.r); + values.push_back(c.g); + values.push_back(c.b); + values.push_back(c.a); + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::STRING_NAME: { + Dictionary d; + d["name"] = String(p_variant); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::NODE_PATH: { + Dictionary d; + d["path"] = String(p_variant); + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::RID: { + Dictionary d; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::OBJECT: { + Object *obj = p_variant.get_validated_object(); + + if (p_allow_classes && obj) { + Dictionary d; + List<PropertyInfo> property_list; + obj->get_property_list(&property_list); + + d["type"] = obj->get_class(); + Dictionary p; + for (const PropertyInfo &P : property_list) { + if (P.usage & PROPERTY_USAGE_STORAGE) { + if (P.name == "script" && !p_allow_scripts) { + continue; + } + p[P.name] = from_native(obj->get(P.name), PASS_ARG); + } + } + d["properties"] = p; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } else { + Dictionary nil; + nil[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return nil; + } + } break; + case Variant::CALLABLE: + case Variant::SIGNAL: { + Dictionary nil; + nil[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return nil; + } break; + case Variant::DICTIONARY: { + Dictionary d = p_variant; + List<Variant> keys; + d.get_key_list(&keys); + bool all_strings = true; + for (const Variant &K : keys) { + if (K.get_type() != Variant::STRING) { + all_strings = false; + break; + } + } + + if (all_strings) { + Dictionary ret_dict; + for (const Variant &K : keys) { + ret_dict[K] = from_native(d[K], PASS_ARG); + } + return ret_dict; + } else { + Dictionary ret; + Array pairs; + for (const Variant &K : keys) { + Dictionary pair; + pair["key"] = from_native(K, PASS_ARG); + pair["value"] = from_native(d[K], PASS_ARG); + pairs.push_back(pair); + } + ret["pairs"] = pairs; + ret[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return ret; + } + } break; + case Variant::ARRAY: { + Array arr = p_variant; + Array ret; + for (int i = 0; i < arr.size(); i++) { + ret.push_back(from_native(arr[i], PASS_ARG)); + } + return ret; + } break; + case Variant::PACKED_BYTE_ARRAY: { + Dictionary d; + PackedByteArray arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + values.push_back(arr[i]); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PACKED_INT32_ARRAY: { + Dictionary d; + PackedInt32Array arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + values.push_back(arr[i]); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + + } break; + case Variant::PACKED_INT64_ARRAY: { + Dictionary d; + PackedInt64Array arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + values.push_back(arr[i]); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + Dictionary d; + PackedFloat32Array arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + values.push_back(arr[i]); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PACKED_FLOAT64_ARRAY: { + Dictionary d; + PackedFloat64Array arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + values.push_back(arr[i]); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PACKED_STRING_ARRAY: { + Dictionary d; + PackedStringArray arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + values.push_back(arr[i]); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PACKED_VECTOR2_ARRAY: { + Dictionary d; + PackedVector2Array arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + Vector2 v = arr[i]; + values.push_back(v.x); + values.push_back(v.y); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PACKED_VECTOR3_ARRAY: { + Dictionary d; + PackedVector3Array arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + Vector3 v = arr[i]; + values.push_back(v.x); + values.push_back(v.y); + values.push_back(v.z); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PACKED_COLOR_ARRAY: { + Dictionary d; + PackedColorArray arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + Color v = arr[i]; + values.push_back(v.r); + values.push_back(v.g); + values.push_back(v.b); + values.push_back(v.a); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + case Variant::PACKED_VECTOR4_ARRAY: { + Dictionary d; + PackedVector4Array arr = p_variant; + Array values; + for (int i = 0; i < arr.size(); i++) { + Vector4 v = arr[i]; + values.push_back(v.x); + values.push_back(v.y); + values.push_back(v.z); + values.push_back(v.w); + } + d[VALUES] = values; + d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return d; + } break; + default: { + ERR_PRINT(vformat("Unhandled conversion from native Variant type '%s' to JSON.", Variant::get_type_name(p_variant.get_type()))); + } break; + } + + Dictionary nil; + nil[GDTYPE] = Variant::get_type_name(p_variant.get_type()); + return nil; +} + +Variant JSON::to_native(const Variant &p_json, bool p_allow_classes, bool p_allow_scripts) { + switch (p_json.get_type()) { + case Variant::BOOL: { + return p_json; + } break; + case Variant::INT: { + return p_json; + } break; + case Variant::FLOAT: { + return p_json; + } break; + case Variant::STRING: { + return p_json; + } break; + case Variant::STRING_NAME: { + return p_json; + } break; + case Variant::CALLABLE: { + return p_json; + } break; + case Variant::DICTIONARY: { + Dictionary d = p_json; + if (d.has(GDTYPE)) { + // Specific Godot Variant types serialized to JSON. + String type = d[GDTYPE]; + if (type == Variant::get_type_name(Variant::VECTOR2)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() != 2, Variant()); + Vector2 v; + v.x = values[0]; + v.y = values[1]; + return v; + } else if (type == Variant::get_type_name(Variant::VECTOR2I)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() != 2, Variant()); + Vector2i v; + v.x = values[0]; + v.y = values[1]; + return v; + } else if (type == Variant::get_type_name(Variant::RECT2)) { + ERR_FAIL_COND_V(!d.has("position"), Variant()); + ERR_FAIL_COND_V(!d.has("size"), Variant()); + Rect2 r; + r.position = to_native(d["position"]); + r.size = to_native(d["size"]); + return r; + } else if (type == Variant::get_type_name(Variant::RECT2I)) { + ERR_FAIL_COND_V(!d.has("position"), Variant()); + ERR_FAIL_COND_V(!d.has("size"), Variant()); + Rect2i r; + r.position = to_native(d["position"]); + r.size = to_native(d["size"]); + return r; + } else if (type == Variant::get_type_name(Variant::VECTOR3)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() != 3, Variant()); + Vector3 v; + v.x = values[0]; + v.y = values[1]; + v.z = values[2]; + return v; + } else if (type == Variant::get_type_name(Variant::VECTOR3I)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() != 3, Variant()); + Vector3i v; + v.x = values[0]; + v.y = values[1]; + v.z = values[2]; + return v; + } else if (type == Variant::get_type_name(Variant::TRANSFORM2D)) { + ERR_FAIL_COND_V(!d.has("x"), Variant()); + ERR_FAIL_COND_V(!d.has("y"), Variant()); + ERR_FAIL_COND_V(!d.has("origin"), Variant()); + Transform2D t; + t[0] = to_native(d["x"]); + t[1] = to_native(d["y"]); + t[2] = to_native(d["origin"]); + return t; + } else if (type == Variant::get_type_name(Variant::VECTOR4)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() != 4, Variant()); + Vector4 v; + v.x = values[0]; + v.y = values[1]; + v.z = values[2]; + v.w = values[3]; + return v; + } else if (type == Variant::get_type_name(Variant::VECTOR4I)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() != 4, Variant()); + Vector4i v; + v.x = values[0]; + v.y = values[1]; + v.z = values[2]; + v.w = values[3]; + return v; + } else if (type == Variant::get_type_name(Variant::PLANE)) { + ERR_FAIL_COND_V(!d.has("normal"), Variant()); + ERR_FAIL_COND_V(!d.has("d"), Variant()); + Plane p; + p.normal = to_native(d["normal"]); + p.d = d["d"]; + return p; + } else if (type == Variant::get_type_name(Variant::QUATERNION)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() != 4, Variant()); + Quaternion v; + v.x = values[0]; + v.y = values[1]; + v.z = values[2]; + v.w = values[3]; + return v; + } else if (type == Variant::get_type_name(Variant::AABB)) { + ERR_FAIL_COND_V(!d.has("position"), Variant()); + ERR_FAIL_COND_V(!d.has("size"), Variant()); + AABB r; + r.position = to_native(d["position"]); + r.size = to_native(d["size"]); + return r; + } else if (type == Variant::get_type_name(Variant::BASIS)) { + ERR_FAIL_COND_V(!d.has("x"), Variant()); + ERR_FAIL_COND_V(!d.has("y"), Variant()); + ERR_FAIL_COND_V(!d.has("z"), Variant()); + Basis b; + b.set_column(0, to_native(d["x"])); + b.set_column(1, to_native(d["y"])); + b.set_column(2, to_native(d["z"])); + return b; + } else if (type == Variant::get_type_name(Variant::TRANSFORM3D)) { + ERR_FAIL_COND_V(!d.has("basis"), Variant()); + ERR_FAIL_COND_V(!d.has("origin"), Variant()); + Transform3D t; + t.basis = to_native(d["basis"]); + t.origin = to_native(d["origin"]); + return t; + } else if (type == Variant::get_type_name(Variant::PROJECTION)) { + ERR_FAIL_COND_V(!d.has("x"), Variant()); + ERR_FAIL_COND_V(!d.has("y"), Variant()); + ERR_FAIL_COND_V(!d.has("z"), Variant()); + ERR_FAIL_COND_V(!d.has("w"), Variant()); + Projection p; + p[0] = to_native(d["x"]); + p[1] = to_native(d["y"]); + p[2] = to_native(d["z"]); + p[3] = to_native(d["w"]); + return p; + } else if (type == Variant::get_type_name(Variant::COLOR)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() != 4, Variant()); + Color c; + c.r = values[0]; + c.g = values[1]; + c.b = values[2]; + c.a = values[3]; + return c; + } else if (type == Variant::get_type_name(Variant::NODE_PATH)) { + ERR_FAIL_COND_V(!d.has("path"), Variant()); + NodePath np = d["path"]; + return np; + } else if (type == Variant::get_type_name(Variant::STRING_NAME)) { + ERR_FAIL_COND_V(!d.has("name"), Variant()); + StringName s = d["name"]; + return s; + } else if (type == Variant::get_type_name(Variant::OBJECT)) { + ERR_FAIL_COND_V(!d.has("type"), Variant()); + ERR_FAIL_COND_V(!d.has("properties"), Variant()); + + ERR_FAIL_COND_V(!p_allow_classes, Variant()); + + String obj_type = d["type"]; + bool is_script = obj_type == "Script" || ClassDB::is_parent_class(obj_type, "Script"); + ERR_FAIL_COND_V(!p_allow_scripts && is_script, Variant()); + Object *obj = ClassDB::instantiate(obj_type); + ERR_FAIL_NULL_V(obj, Variant()); + + Dictionary p = d["properties"]; + + List<Variant> keys; + p.get_key_list(&keys); + + for (const Variant &K : keys) { + String property = K; + Variant value = to_native(p[K], PASS_ARG); + obj->set(property, value); + } + + Variant v(obj); + + return v; + } else if (type == Variant::get_type_name(Variant::DICTIONARY)) { + ERR_FAIL_COND_V(!d.has("pairs"), Variant()); + Array pairs = d["pairs"]; + Dictionary r; + for (int i = 0; i < pairs.size(); i++) { + Dictionary p = pairs[i]; + ERR_CONTINUE(!p.has("key")); + ERR_CONTINUE(!p.has("value")); + r[to_native(p["key"], PASS_ARG)] = to_native(p["value"]); + } + return r; + } else if (type == Variant::get_type_name(Variant::ARRAY)) { + ERR_PRINT(vformat("Unexpected Array with '%s' key. Arrays are supported natively.", GDTYPE)); + } else if (type == Variant::get_type_name(Variant::PACKED_BYTE_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + PackedByteArray pbarr; + pbarr.resize(values.size()); + for (int i = 0; i < pbarr.size(); i++) { + pbarr.write[i] = values[i]; + } + return pbarr; + } else if (type == Variant::get_type_name(Variant::PACKED_INT32_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + PackedInt32Array arr; + arr.resize(values.size()); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = values[i]; + } + return arr; + } else if (type == Variant::get_type_name(Variant::PACKED_INT64_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + PackedInt64Array arr; + arr.resize(values.size()); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = values[i]; + } + return arr; + } else if (type == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + PackedFloat32Array arr; + arr.resize(values.size()); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = values[i]; + } + return arr; + } else if (type == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + PackedFloat64Array arr; + arr.resize(values.size()); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = values[i]; + } + return arr; + } else if (type == Variant::get_type_name(Variant::PACKED_STRING_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + PackedStringArray arr; + arr.resize(values.size()); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = values[i]; + } + return arr; + } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() % 2 != 0, Variant()); + PackedVector2Array arr; + arr.resize(values.size() / 2); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = Vector2(values[i * 2 + 0], values[i * 2 + 1]); + } + return arr; + } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR3_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() % 3 != 0, Variant()); + PackedVector3Array arr; + arr.resize(values.size() / 3); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = Vector3(values[i * 3 + 0], values[i * 3 + 1], values[i * 3 + 2]); + } + return arr; + } else if (type == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() % 4 != 0, Variant()); + PackedColorArray arr; + arr.resize(values.size() / 4); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = Color(values[i * 4 + 0], values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]); + } + return arr; + } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR4_ARRAY)) { + ERR_FAIL_COND_V(!d.has(VALUES), Variant()); + Array values = d[VALUES]; + ERR_FAIL_COND_V(values.size() % 4 != 0, Variant()); + PackedVector4Array arr; + arr.resize(values.size() / 4); + for (int i = 0; i < arr.size(); i++) { + arr.write[i] = Vector4(values[i * 4 + 0], values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]); + } + return arr; + } else { + return Variant(); + } + } else { + // Regular dictionary with string keys. + List<Variant> keys; + d.get_key_list(&keys); + Dictionary r; + for (const Variant &K : keys) { + r[K] = to_native(d[K], PASS_ARG); + } + return r; + } + } break; + case Variant::ARRAY: { + Array arr = p_json; + Array ret; + ret.resize(arr.size()); + for (int i = 0; i < arr.size(); i++) { + ret[i] = to_native(arr[i], PASS_ARG); + } + return ret; + } break; + default: { + ERR_PRINT(vformat("Unhandled conversion from JSON type '%s' to native Variant type.", Variant::get_type_name(p_json.get_type()))); + return Variant(); + } + } + + return Variant(); +} + +#undef GDTYPE +#undef VALUES +#undef PASS_ARG //////////// diff --git a/core/io/json.h b/core/io/json.h index 801fa29b4b..67b5e09afa 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -94,6 +94,9 @@ public: void set_data(const Variant &p_data); inline int get_error_line() const { return err_line; } inline String get_error_message() const { return err_str; } + + static Variant from_native(const Variant &p_variant, bool p_allow_classes = false, bool p_allow_scripts = false); + static Variant to_native(const Variant &p_json, bool p_allow_classes = false, bool p_allow_scripts = false); }; class ResourceFormatLoaderJSON : public ResourceFormatLoader { diff --git a/core/io/packed_data_container.h b/core/io/packed_data_container.h index cc9996101e..f4ffa09022 100644 --- a/core/io/packed_data_container.h +++ b/core/io/packed_data_container.h @@ -36,7 +36,7 @@ class PackedDataContainer : public Resource { GDCLASS(PackedDataContainer, Resource); - enum { + enum : uint32_t { TYPE_DICT = 0xFFFFFFFF, TYPE_ARRAY = 0xFFFFFFFE, }; diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 598c99c188..ff12dc5851 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -60,32 +60,32 @@ void Resource::set_path(const String &p_path, bool p_take_over) { p_take_over = false; // Can't take over an empty path } - ResourceCache::lock.lock(); + { + MutexLock lock(ResourceCache::lock); - if (!path_cache.is_empty()) { - ResourceCache::resources.erase(path_cache); - } + if (!path_cache.is_empty()) { + ResourceCache::resources.erase(path_cache); + } - path_cache = ""; + path_cache = ""; - Ref<Resource> existing = ResourceCache::get_ref(p_path); + Ref<Resource> existing = ResourceCache::get_ref(p_path); - if (existing.is_valid()) { - if (p_take_over) { - existing->path_cache = String(); - ResourceCache::resources.erase(p_path); - } else { - ResourceCache::lock.unlock(); - ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion)."); + if (existing.is_valid()) { + if (p_take_over) { + existing->path_cache = String(); + ResourceCache::resources.erase(p_path); + } else { + ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion)."); + } } - } - path_cache = p_path; + path_cache = p_path; - if (!path_cache.is_empty()) { - ResourceCache::resources[path_cache] = this; + if (!path_cache.is_empty()) { + ResourceCache::resources[path_cache] = this; + } } - ResourceCache::lock.unlock(); _resource_path_changed(); } @@ -486,15 +486,13 @@ void Resource::set_as_translation_remapped(bool p_remapped) { return; } - ResourceCache::lock.lock(); + MutexLock lock(ResourceCache::lock); if (p_remapped) { ResourceLoader::remapped_list.add(&remapped_list); } else { ResourceLoader::remapped_list.remove(&remapped_list); } - - ResourceCache::lock.unlock(); } #ifdef TOOLS_ENABLED @@ -564,14 +562,13 @@ Resource::~Resource() { return; } - ResourceCache::lock.lock(); + MutexLock lock(ResourceCache::lock); // Only unregister from the cache if this is the actual resource listed there. // (Other resources can have the same value in `path_cache` if loaded with `CACHE_IGNORE`.) HashMap<String, Resource *>::Iterator E = ResourceCache::resources.find(path_cache); if (likely(E && E->value == this)) { ResourceCache::resources.remove(E); } - ResourceCache::lock.unlock(); } HashMap<String, Resource *> ResourceCache::resources; @@ -600,18 +597,20 @@ void ResourceCache::clear() { } bool ResourceCache::has(const String &p_path) { - lock.lock(); + Resource **res = nullptr; - Resource **res = resources.getptr(p_path); + { + MutexLock mutex_lock(lock); - if (res && (*res)->get_reference_count() == 0) { - // This resource is in the process of being deleted, ignore its existence. - (*res)->path_cache = String(); - resources.erase(p_path); - res = nullptr; - } + res = resources.getptr(p_path); - lock.unlock(); + if (res && (*res)->get_reference_count() == 0) { + // This resource is in the process of being deleted, ignore its existence. + (*res)->path_cache = String(); + resources.erase(p_path); + res = nullptr; + } + } if (!res) { return false; @@ -622,28 +621,27 @@ bool ResourceCache::has(const String &p_path) { Ref<Resource> ResourceCache::get_ref(const String &p_path) { Ref<Resource> ref; - lock.lock(); - - Resource **res = resources.getptr(p_path); + { + MutexLock mutex_lock(lock); + Resource **res = resources.getptr(p_path); - if (res) { - ref = Ref<Resource>(*res); - } + if (res) { + ref = Ref<Resource>(*res); + } - if (res && !ref.is_valid()) { - // This resource is in the process of being deleted, ignore its existence - (*res)->path_cache = String(); - resources.erase(p_path); - res = nullptr; + if (res && !ref.is_valid()) { + // This resource is in the process of being deleted, ignore its existence + (*res)->path_cache = String(); + resources.erase(p_path); + res = nullptr; + } } - lock.unlock(); - return ref; } void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) { - lock.lock(); + MutexLock mutex_lock(lock); LocalVector<String> to_remove; @@ -663,14 +661,9 @@ void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) { for (const String &E : to_remove) { resources.erase(E); } - - lock.unlock(); } int ResourceCache::get_cached_resource_count() { - lock.lock(); - int rc = resources.size(); - lock.unlock(); - - return rc; + MutexLock mutex_lock(lock); + return resources.size(); } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 3e809ae762..7cf101b0de 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -227,28 +227,27 @@ void ResourceFormatLoader::_bind_methods() { // This should be robust enough to be called redundantly without issues. void ResourceLoader::LoadToken::clear() { - thread_load_mutex.lock(); - WorkerThreadPool::TaskID task_to_await = 0; - // User-facing tokens shouldn't be deleted until completely claimed. - DEV_ASSERT(user_rc == 0 && user_path.is_empty()); - - if (!local_path.is_empty()) { // Empty is used for the special case where the load task is not registered. - DEV_ASSERT(thread_load_tasks.has(local_path)); - ThreadLoadTask &load_task = thread_load_tasks[local_path]; - if (load_task.task_id && !load_task.awaited) { - task_to_await = load_task.task_id; + { + MutexLock thread_load_lock(thread_load_mutex); + // User-facing tokens shouldn't be deleted until completely claimed. + DEV_ASSERT(user_rc == 0 && user_path.is_empty()); + + if (!local_path.is_empty()) { // Empty is used for the special case where the load task is not registered. + DEV_ASSERT(thread_load_tasks.has(local_path)); + ThreadLoadTask &load_task = thread_load_tasks[local_path]; + if (load_task.task_id && !load_task.awaited) { + task_to_await = load_task.task_id; + } + // Removing a task which is still in progress would be catastrophic. + // Tokens must be alive until the task thread function is done. + DEV_ASSERT(load_task.status == THREAD_LOAD_FAILED || load_task.status == THREAD_LOAD_LOADED); + thread_load_tasks.erase(local_path); + local_path.clear(); } - // Removing a task which is still in progress would be catastrophic. - // Tokens must be alive until the task thread function is done. - DEV_ASSERT(load_task.status == THREAD_LOAD_FAILED || load_task.status == THREAD_LOAD_LOADED); - thread_load_tasks.erase(local_path); - local_path.clear(); } - thread_load_mutex.unlock(); - // If task is unused, await it here, locally, now the token data is consistent. if (task_to_await) { PREPARE_FOR_WTP_WAIT @@ -265,7 +264,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin const String &original_path = p_original_path.is_empty() ? p_path : p_original_path; load_nesting++; if (load_paths_stack.size()) { - thread_load_mutex.lock(); + MutexLock thread_load_lock(thread_load_mutex); const String &parent_task_path = load_paths_stack.get(load_paths_stack.size() - 1); HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path); // Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources). @@ -273,7 +272,6 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin if (E && !is_remapped_load) { E->value.sub_tasks.insert(p_original_path); } - thread_load_mutex.unlock(); } load_paths_stack.push_back(original_path); @@ -318,13 +316,13 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin void ResourceLoader::_run_load_task(void *p_userdata) { ThreadLoadTask &load_task = *(ThreadLoadTask *)p_userdata; - thread_load_mutex.lock(); - if (cleaning_tasks) { - load_task.status = THREAD_LOAD_FAILED; - thread_load_mutex.unlock(); - return; + { + MutexLock thread_load_lock(thread_load_mutex); + if (cleaning_tasks) { + load_task.status = THREAD_LOAD_FAILED; + return; + } } - thread_load_mutex.unlock(); // Thread-safe either if it's the current thread or a brand new one. CallQueue *own_mq_override = nullptr; @@ -1170,17 +1168,17 @@ String ResourceLoader::path_remap(const String &p_path) { } void ResourceLoader::reload_translation_remaps() { - ResourceCache::lock.lock(); - List<Resource *> to_reload; - SelfList<Resource> *E = remapped_list.first(); - while (E) { - to_reload.push_back(E->self()); - E = E->next(); - } + { + MutexLock lock(ResourceCache::lock); + SelfList<Resource> *E = remapped_list.first(); - ResourceCache::lock.unlock(); + while (E) { + to_reload.push_back(E->self()); + E = E->next(); + } + } //now just make sure to not delete any of these resources while changing locale.. while (to_reload.front()) { diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 5c793a676f..c929b29ee9 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -271,6 +271,22 @@ void ClassDB::get_extensions_class_list(List<StringName> *p_classes) { p_classes->sort_custom<StringName::AlphCompare>(); } + +void ClassDB::get_extension_class_list(const Ref<GDExtension> &p_extension, List<StringName> *p_classes) { + OBJTYPE_RLOCK; + + for (const KeyValue<StringName, ClassInfo> &E : classes) { + if (E.value.api != API_EXTENSION && E.value.api != API_EDITOR_EXTENSION) { + continue; + } + if (!E.value.gdextension || E.value.gdextension->library != p_extension.ptr()) { + continue; + } + p_classes->push_back(E.key); + } + + p_classes->sort_custom<StringName::AlphCompare>(); +} #endif void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { diff --git a/core/object/class_db.h b/core/object/class_db.h index d6a95b58e2..620092a6c4 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -285,6 +285,7 @@ public: static void get_class_list(List<StringName> *p_classes); #ifdef TOOLS_ENABLED static void get_extensions_class_list(List<StringName> *p_classes); + static void get_extension_class_list(const Ref<GDExtension> &p_extension, List<StringName> *p_classes); static ObjectGDExtension *get_placeholder_extension(const StringName &p_class); #endif static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes); diff --git a/core/object/object.cpp b/core/object/object.cpp index a2330ecd04..651f4e77a4 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1023,6 +1023,14 @@ void Object::remove_meta(const StringName &p_name) { set_meta(p_name, Variant()); } +void Object::merge_meta_from(const Object *p_src) { + List<StringName> meta_keys; + p_src->get_meta_list(&meta_keys); + for (const StringName &key : meta_keys) { + set_meta(key, p_src->get_meta(key)); + } +} + TypedArray<Dictionary> Object::_get_property_list_bind() const { List<PropertyInfo> lpi; get_property_list(&lpi); @@ -1904,7 +1912,7 @@ void Object::set_instance_binding(void *p_token, void *p_binding, const GDExtens void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks) { void *binding = nullptr; - _instance_binding_mutex.lock(); + MutexLock instance_binding_lock(_instance_binding_mutex); for (uint32_t i = 0; i < _instance_binding_count; i++) { if (_instance_bindings[i].token == p_token) { binding = _instance_bindings[i].binding; @@ -1935,14 +1943,12 @@ void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindi _instance_binding_count++; } - _instance_binding_mutex.unlock(); - return binding; } bool Object::has_instance_binding(void *p_token) { bool found = false; - _instance_binding_mutex.lock(); + MutexLock instance_binding_lock(_instance_binding_mutex); for (uint32_t i = 0; i < _instance_binding_count; i++) { if (_instance_bindings[i].token == p_token) { found = true; @@ -1950,14 +1956,12 @@ bool Object::has_instance_binding(void *p_token) { } } - _instance_binding_mutex.unlock(); - return found; } void Object::free_instance_binding(void *p_token) { bool found = false; - _instance_binding_mutex.lock(); + MutexLock instance_binding_lock(_instance_binding_mutex); for (uint32_t i = 0; i < _instance_binding_count; i++) { if (!found && _instance_bindings[i].token == p_token) { if (_instance_bindings[i].free_callback) { @@ -1976,7 +1980,6 @@ void Object::free_instance_binding(void *p_token) { if (found) { _instance_binding_count--; } - _instance_binding_mutex.unlock(); } #ifdef TOOLS_ENABLED diff --git a/core/object/object.h b/core/object/object.h index ba6b309542..4752e72d67 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -667,7 +667,7 @@ protected: _FORCE_INLINE_ bool _instance_binding_reference(bool p_reference) { bool can_die = true; if (_instance_bindings) { - _instance_binding_mutex.lock(); + MutexLock instance_binding_lock(_instance_binding_mutex); for (uint32_t i = 0; i < _instance_binding_count; i++) { if (_instance_bindings[i].reference_callback) { if (!_instance_bindings[i].reference_callback(_instance_bindings[i].token, _instance_bindings[i].binding, p_reference)) { @@ -675,7 +675,6 @@ protected: } } } - _instance_binding_mutex.unlock(); } return can_die; } @@ -895,6 +894,7 @@ public: MTVIRTUAL void remove_meta(const StringName &p_name); MTVIRTUAL Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const; MTVIRTUAL void get_meta_list(List<StringName> *p_list) const; + MTVIRTUAL void merge_meta_from(const Object *p_src); #ifdef TOOLS_ENABLED void set_edited(bool p_edited); diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp index 25ad3bf964..36a3f9d99b 100644 --- a/core/object/worker_thread_pool.cpp +++ b/core/object/worker_thread_pool.cpp @@ -127,9 +127,8 @@ void WorkerThreadPool::_process_task(Task *p_task) { if (finished_users == max_users) { // Get rid of the group, because nobody else is using it. - task_mutex.lock(); + MutexLock task_lock(task_mutex); group_allocator.free(p_task->group); - task_mutex.unlock(); } // For groups, tasks get rid of themselves. @@ -349,17 +348,13 @@ WorkerThreadPool::TaskID WorkerThreadPool::add_task(const Callable &p_action, bo } bool WorkerThreadPool::is_task_completed(TaskID p_task_id) const { - task_mutex.lock(); + MutexLock task_lock(task_mutex); const Task *const *taskp = tasks.getptr(p_task_id); if (!taskp) { - task_mutex.unlock(); ERR_FAIL_V_MSG(false, "Invalid Task ID"); // Invalid task } - bool completed = (*taskp)->completed; - task_mutex.unlock(); - - return completed; + return (*taskp)->completed; } Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) { @@ -522,10 +517,9 @@ void WorkerThreadPool::yield() { } void WorkerThreadPool::notify_yield_over(TaskID p_task_id) { - task_mutex.lock(); + MutexLock task_lock(task_mutex); Task **taskp = tasks.getptr(p_task_id); if (!taskp) { - task_mutex.unlock(); ERR_FAIL_MSG("Invalid Task ID."); } Task *task = *taskp; @@ -534,7 +528,6 @@ void WorkerThreadPool::notify_yield_over(TaskID p_task_id) { // This avoids a race condition where a task is created and yield-over called before it's processed. task->pending_notify_yield_over = true; } - task_mutex.unlock(); return; } @@ -542,8 +535,6 @@ void WorkerThreadPool::notify_yield_over(TaskID p_task_id) { td.yield_is_over = true; td.signaled = true; td.cond_var.notify_one(); - - task_mutex.unlock(); } WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { @@ -601,26 +592,20 @@ WorkerThreadPool::GroupID WorkerThreadPool::add_group_task(const Callable &p_act } uint32_t WorkerThreadPool::get_group_processed_element_count(GroupID p_group) const { - task_mutex.lock(); + MutexLock task_lock(task_mutex); const Group *const *groupp = groups.getptr(p_group); if (!groupp) { - task_mutex.unlock(); ERR_FAIL_V_MSG(0, "Invalid Group ID"); } - uint32_t elements = (*groupp)->completed_index.get(); - task_mutex.unlock(); - return elements; + return (*groupp)->completed_index.get(); } bool WorkerThreadPool::is_group_task_completed(GroupID p_group) const { - task_mutex.lock(); + MutexLock task_lock(task_mutex); const Group *const *groupp = groups.getptr(p_group); if (!groupp) { - task_mutex.unlock(); ERR_FAIL_V_MSG(false, "Invalid Group ID"); } - bool completed = (*groupp)->completed.is_set(); - task_mutex.unlock(); - return completed; + return (*groupp)->completed.is_set(); } void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { @@ -644,15 +629,13 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { if (finished_users == max_users) { // All tasks using this group are gone (finished before the group), so clear the group too. - task_mutex.lock(); + MutexLock task_lock(task_mutex); group_allocator.free(group); - task_mutex.unlock(); } } - task_mutex.lock(); // This mutex is needed when Physics 2D and/or 3D is selected to run on a separate thread. + MutexLock task_lock(task_mutex); // This mutex is needed when Physics 2D and/or 3D is selected to run on a separate thread. groups.erase(p_group); - task_mutex.unlock(); #endif } diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 5d59d65f92..28077fc8c5 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -191,11 +191,10 @@ StringName::StringName(const StringName &p_name) { } void StringName::assign_static_unique_class_name(StringName *ptr, const char *p_name) { - mutex.lock(); + MutexLock lock(mutex); if (*ptr == StringName()) { *ptr = StringName(p_name, true); } - mutex.unlock(); } StringName::StringName(const char *p_name, bool p_static) { diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 61b90e2a26..fa49767d46 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -214,11 +214,11 @@ struct VariantCaster<char32_t> { template <> struct PtrToArg<char32_t> { _FORCE_INLINE_ static char32_t convert(const void *p_ptr) { - return char32_t(*reinterpret_cast<const int *>(p_ptr)); + return char32_t(*reinterpret_cast<const int64_t *>(p_ptr)); } typedef int64_t EncodeT; _FORCE_INLINE_ static void encode(char32_t p_val, const void *p_ptr) { - *(int *)p_ptr = p_val; + *(int64_t *)p_ptr = p_val; } }; diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml index 0e5de79b25..d4084e84a0 100644 --- a/doc/classes/EditorExportPlatform.xml +++ b/doc/classes/EditorExportPlatform.xml @@ -11,11 +11,217 @@ <link title="Console support in Godot">$DOCS_URL/tutorials/platform/consoles.html</link> </tutorials> <methods> + <method name="add_message"> + <return type="void" /> + <param index="0" name="type" type="int" enum="EditorExportPlatform.ExportMessageType" /> + <param index="1" name="category" type="String" /> + <param index="2" name="message" type="String" /> + <description> + Adds a message to the export log that will be displayed when exporting ends. + </description> + </method> + <method name="clear_messages"> + <return type="void" /> + <description> + Clears the export log. + </description> + </method> + <method name="create_preset"> + <return type="EditorExportPreset" /> + <description> + Create a new preset for this platform. + </description> + </method> + <method name="export_pack"> + <return type="int" enum="Error" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="path" type="String" /> + <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" default="0" /> + <description> + Creates a PCK archive at [param path] for the specified [param preset]. + </description> + </method> + <method name="export_project"> + <return type="int" enum="Error" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="path" type="String" /> + <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" default="0" /> + <description> + Creates a full project at [param path] for the specified [param preset]. + </description> + </method> + <method name="export_project_files"> + <return type="int" enum="Error" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="save_cb" type="Callable" /> + <param index="3" name="shared_cb" type="Callable" default="Callable()" /> + <description> + Exports project files for the specified preset. This method can be used to implement custom export format, other than PCK and ZIP. One of the callbacks is called for each exported file. + [param save_cb] is called for all exported files and have the following arguments: [code]file_path: String[/code], [code]file_data: PackedByteArray[/code], [code]file_index: int[/code], [code]file_count: int[/code], [code]encryption_include_filters: PackedStringArray[/code], [code]encryption_exclude_filters: PackedStringArray[/code], [code]encryption_key: PackedByteArray[/code]. + [param shared_cb] is called for exported native shared/static libraries and have the following arguments: [code]file_path: String[/code], [code]tags: PackedStringArray[/code], [code]target_folder: String[/code]. + [b]Note:[/b] [code]file_index[/code] and [code]file_count[/code] are intended for progress tracking only and aren't necesserely unique and precise. + </description> + </method> + <method name="export_zip"> + <return type="int" enum="Error" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="path" type="String" /> + <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" default="0" /> + <description> + Create a ZIP archive at [param path] for the specified [param preset]. + </description> + </method> + <method name="find_export_template" qualifiers="const"> + <return type="Dictionary" /> + <param index="0" name="template_file_name" type="String" /> + <description> + Locates export template for the platform, and returns [Dictionary] with the following keys: [code]path: String[/code] and [code]error: String[/code]. This method is provided for convenience and custom export platforms aren't required to use it or keep export templates stored in the same way official templates are. + </description> + </method> + <method name="gen_export_flags"> + <return type="PackedStringArray" /> + <param index="0" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" /> + <description> + Generates array of command line arguments for the default export templates for the debug flags and editor settings. + </description> + </method> + <method name="get_current_presets" qualifiers="const"> + <return type="Array" /> + <description> + Returns array of [EditorExportPreset]s for this platform. + </description> + </method> + <method name="get_forced_export_files" qualifiers="static"> + <return type="PackedStringArray" /> + <description> + Returns array of core file names that always should be exported regardless of preset config. + </description> + </method> + <method name="get_message_category" qualifiers="const"> + <return type="String" /> + <param index="0" name="index" type="int" /> + <description> + Returns message category, for the message with [param index]. + </description> + </method> + <method name="get_message_count" qualifiers="const"> + <return type="int" /> + <description> + Returns number of messages in the export log. + </description> + </method> + <method name="get_message_text" qualifiers="const"> + <return type="String" /> + <param index="0" name="index" type="int" /> + <description> + Returns message text, for the message with [param index]. + </description> + </method> + <method name="get_message_type" qualifiers="const"> + <return type="int" enum="EditorExportPlatform.ExportMessageType" /> + <param index="0" name="index" type="int" /> + <description> + Returns message type, for the message with [param index]. + </description> + </method> <method name="get_os_name" qualifiers="const"> <return type="String" /> <description> Returns the name of the export operating system handled by this [EditorExportPlatform] class, as a friendly string. Possible return values are [code]Windows[/code], [code]Linux[/code], [code]macOS[/code], [code]Android[/code], [code]iOS[/code], and [code]Web[/code]. </description> </method> + <method name="get_worst_message_type" qualifiers="const"> + <return type="int" enum="EditorExportPlatform.ExportMessageType" /> + <description> + Returns most severe message type currently present in the export log. + </description> + </method> + <method name="save_pack"> + <return type="Dictionary" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="path" type="String" /> + <param index="3" name="embed" type="bool" default="false" /> + <description> + Saves PCK archive and returns [Dictionary] with the following keys: [code]result: Error[/code], [code]so_files: Array[/code] (array of the shared/static objects which contains dictionaries with the following keys: [code]path: String[/code], [code]tags: PackedStringArray[/code], and [code]target_folder: String[/code]). + If [param embed] is [code]true[/code], PCK content is appended to the end of [param path] file and return [Dictionary] additionally include following keys: [code]embedded_start: int[/code] (embedded PCK offset) and [code]embedded_size: int[/code] (embedded PCK size). + </description> + </method> + <method name="save_zip"> + <return type="Dictionary" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="path" type="String" /> + <description> + Saves ZIP archive and returns [Dictionary] with the following keys: [code]result: Error[/code], [code]so_files: Array[/code] (array of the shared/static objects which contains dictionaries with the following keys: [code]path: String[/code], [code]tags: PackedStringArray[/code], and [code]target_folder: String[/code]). + </description> + </method> + <method name="ssh_push_to_remote" qualifiers="const"> + <return type="int" enum="Error" /> + <param index="0" name="host" type="String" /> + <param index="1" name="port" type="String" /> + <param index="2" name="scp_args" type="PackedStringArray" /> + <param index="3" name="src_file" type="String" /> + <param index="4" name="dst_file" type="String" /> + <description> + Uploads specified file over SCP protocol to the remote host. + </description> + </method> + <method name="ssh_run_on_remote" qualifiers="const"> + <return type="int" enum="Error" /> + <param index="0" name="host" type="String" /> + <param index="1" name="port" type="String" /> + <param index="2" name="ssh_arg" type="PackedStringArray" /> + <param index="3" name="cmd_args" type="String" /> + <param index="4" name="output" type="Array" default="[]" /> + <param index="5" name="port_fwd" type="int" default="-1" /> + <description> + Executes specified command on the remote host via SSH protocol and returns command output in the [param output]. + </description> + </method> + <method name="ssh_run_on_remote_no_wait" qualifiers="const"> + <return type="int" /> + <param index="0" name="host" type="String" /> + <param index="1" name="port" type="String" /> + <param index="2" name="ssh_args" type="PackedStringArray" /> + <param index="3" name="cmd_args" type="String" /> + <param index="4" name="port_fwd" type="int" default="-1" /> + <description> + Executes specified command on the remote host via SSH protocol and returns process ID (on the remote host) without waiting for command to finish. + </description> + </method> </methods> + <constants> + <constant name="EXPORT_MESSAGE_NONE" value="0" enum="ExportMessageType"> + Invalid message type used as the default value when no type is specified. + </constant> + <constant name="EXPORT_MESSAGE_INFO" value="1" enum="ExportMessageType"> + Message type for informational messages that have no effect on the export. + </constant> + <constant name="EXPORT_MESSAGE_WARNING" value="2" enum="ExportMessageType"> + Message type for warning messages that should be addressed but still allow to complete the export. + </constant> + <constant name="EXPORT_MESSAGE_ERROR" value="3" enum="ExportMessageType"> + Message type for error messages that must be addressed and fail the export. + </constant> + <constant name="DEBUG_FLAG_DUMB_CLIENT" value="1" enum="DebugFlags" is_bitfield="true"> + Flag is set if remotely debugged project is expected to use remote file system. If set, [method gen_export_flags] will add [code]--remove-fs[/code] and [code]--remote-fs-password[/code] (if password is set in the editor settings) command line arguments to the list. + </constant> + <constant name="DEBUG_FLAG_REMOTE_DEBUG" value="2" enum="DebugFlags" is_bitfield="true"> + Flag is set if remote debug is enabled. If set, [method gen_export_flags] will add [code]--remote-debug[/code] and [code]--breakpoints[/code] (if breakpoints are selected in the script editor or added by the plugin) command line arguments to the list. + </constant> + <constant name="DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST" value="4" enum="DebugFlags" is_bitfield="true"> + Flag is set if remotely debugged project is running on the localhost. If set, [method gen_export_flags] will use [code]localhost[/code] instead of [member EditorSettings.network/debug/remote_host] as remote debugger host. + </constant> + <constant name="DEBUG_FLAG_VIEW_COLLISIONS" value="8" enum="DebugFlags" is_bitfield="true"> + Flag is set if "Visible Collision Shapes" remote debug option is enabled. If set, [method gen_export_flags] will add [code]--debug-collisions[/code] command line arguments to the list. + </constant> + <constant name="DEBUG_FLAG_VIEW_NAVIGATION" value="16" enum="DebugFlags" is_bitfield="true"> + Flag is set if Visible Navigation" remote debug option is enabled. If set, [method gen_export_flags] will add [code]--debug-navigation[/code] command line arguments to the list. + </constant> + </constants> </class> diff --git a/doc/classes/EditorExportPlatformExtension.xml b/doc/classes/EditorExportPlatformExtension.xml new file mode 100644 index 0000000000..ef589e2f58 --- /dev/null +++ b/doc/classes/EditorExportPlatformExtension.xml @@ -0,0 +1,282 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="EditorExportPlatformExtension" inherits="EditorExportPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Base class for custom [EditorExportPlatform] implementations (plugins). + </brief_description> + <description> + External [EditorExportPlatform] implementations should inherit from this class. + To use [EditorExportPlatform], register it using the [method EditorPlugin.add_export_platform] method first. + </description> + <tutorials> + </tutorials> + <methods> + <method name="_can_export" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <description> + [b]Optional.[/b] + Returns [code]true[/code], if specified [param preset] is valid and can be exported. Use [method set_config_error] and [method set_config_missing_templates] to set error details. + Usual implementation can call [method _has_valid_export_configuration] and [method _has_valid_project_configuration] to determine if export is possible. + </description> + </method> + <method name="_cleanup" qualifiers="virtual"> + <return type="void" /> + <description> + [b]Optional.[/b] + Called by the editor before platform is unregistered. + </description> + </method> + <method name="_export_pack" qualifiers="virtual"> + <return type="int" enum="Error" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="path" type="String" /> + <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" /> + <description> + [b]Optional.[/b] + Creates a PCK archive at [param path] for the specified [param preset]. + This method is called when "Export PCK/ZIP" button is pressed in the export dialog, and PCK is selected as a file type. + </description> + </method> + <method name="_export_project" qualifiers="virtual"> + <return type="int" enum="Error" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="path" type="String" /> + <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" /> + <description> + [b]Required.[/b] + Creates a full project at [param path] for the specified [param preset]. + This method is called when "Export" button is pressed in the export dialog. + This method implementation can call [method EditorExportPlatform.save_pack] or [method EditorExportPlatform.save_zip] to use default PCK/ZIP export process, or calls [method EditorExportPlatform.export_project_files] and implement custom callback for processing each exported file. + </description> + </method> + <method name="_export_zip" qualifiers="virtual"> + <return type="int" enum="Error" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <param index="2" name="path" type="String" /> + <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" /> + <description> + [b]Optional.[/b] + Create a ZIP archive at [param path] for the specified [param preset]. + This method is called when "Export PCK/ZIP" button is pressed in the export dialog, and ZIP is selected as a file type. + </description> + </method> + <method name="_get_binary_extensions" qualifiers="virtual const"> + <return type="PackedStringArray" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <description> + [b]Required.[/b] + Returns array of supported binary extensions for the full project export. + </description> + </method> + <method name="_get_debug_protocol" qualifiers="virtual const"> + <return type="String" /> + <description> + [b]Optional.[/b] + Returns protocol used for remote debugging. Default implementation return [code]tcp://[/code]. + </description> + </method> + <method name="_get_device_architecture" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="device" type="int" /> + <description> + [b]Optional.[/b] + Returns device architecture for one-click deploy. + </description> + </method> + <method name="_get_export_option_visibility" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="option" type="String" /> + <description> + [b]Optional.[/b] + Validates [param option] and returns visibility for the specified [param preset]. Default implementation return [code]true[/code] for all options. + </description> + </method> + <method name="_get_export_option_warning" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="option" type="StringName" /> + <description> + [b]Optional.[/b] + Validates [param option] and returns warning message for the specified [param preset]. Default implementation return empty string for all options. + </description> + </method> + <method name="_get_export_options" qualifiers="virtual const"> + <return type="Dictionary[]" /> + <description> + [b]Optional.[/b] + Returns a property list, as an [Array] of dictionaries. Each [Dictionary] must at least contain the [code]name: StringName[/code] and [code]type: Variant.Type[/code] entries. + Additionally, the following keys are supported: + - [code]hint: PropertyHint[/code] + - [code]hint_string: String[/code] + - [code]usage: PropertyUsageFlags[/code] + - [code]class_name: StringName[/code] + - [code]default_value: Variant[/code], default value of the property. + - [code]update_visibility: bool[/code], if set to [code]true[/code], [method _get_export_option_visibility] is called for each property when this property is changed. + - [code]required: bool[/code], if set to [code]true[/code], this property warnings are critical, and should be resolved to make export possible. This value is a hint for the [method _has_valid_export_configuration] implementation, and not used by the engine directly. + See also [method Object._get_property_list]. + </description> + </method> + <method name="_get_logo" qualifiers="virtual const"> + <return type="Texture2D" /> + <description> + [b]Required.[/b] + Returns platform logo displayed in the export dialog, logo should be 32x32 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale]. + </description> + </method> + <method name="_get_name" qualifiers="virtual const"> + <return type="String" /> + <description> + [b]Required.[/b] + Returns export platform name. + </description> + </method> + <method name="_get_option_icon" qualifiers="virtual const"> + <return type="ImageTexture" /> + <param index="0" name="device" type="int" /> + <description> + [b]Optional.[/b] + Returns one-click deploy menu item icon for the specified [param device], icon should be 16x16 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale]. + </description> + </method> + <method name="_get_option_label" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="device" type="int" /> + <description> + [b]Optional.[/b] + Returns one-click deploy menu item label for the specified [param device]. + </description> + </method> + <method name="_get_option_tooltip" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="device" type="int" /> + <description> + [b]Optional.[/b] + Returns one-click deploy menu item tooltip for the specified [param device]. + </description> + </method> + <method name="_get_options_count" qualifiers="virtual const"> + <return type="int" /> + <description> + [b]Optional.[/b] + Returns number one-click deploy devices (or other one-click option displayed in the menu). + </description> + </method> + <method name="_get_options_tooltip" qualifiers="virtual const"> + <return type="String" /> + <description> + [b]Optional.[/b] + Returns tooltip of the one-click deploy menu button. + </description> + </method> + <method name="_get_os_name" qualifiers="virtual const"> + <return type="String" /> + <description> + [b]Required.[/b] + Returns target OS name. + </description> + </method> + <method name="_get_platform_features" qualifiers="virtual const"> + <return type="PackedStringArray" /> + <description> + [b]Required.[/b] + Returns array of platform specific features. + </description> + </method> + <method name="_get_preset_features" qualifiers="virtual const"> + <return type="PackedStringArray" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <description> + [b]Required.[/b] + Returns array of platform specific features for the specified [param preset]. + </description> + </method> + <method name="_get_run_icon" qualifiers="virtual const"> + <return type="Texture2D" /> + <description> + [b]Optional.[/b] + Returns icon of the one-click deploy menu button, icon should be 16x16 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale]. + </description> + </method> + <method name="_has_valid_export_configuration" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="debug" type="bool" /> + <description> + [b]Required.[/b] + Returns [code]true[/code] if export configuration is valid. + </description> + </method> + <method name="_has_valid_project_configuration" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <description> + [b]Required.[/b] + Returns [code]true[/code] if project configuration is valid. + </description> + </method> + <method name="_is_executable" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="path" type="String" /> + <description> + [b]Optional.[/b] + Returns [code]true[/code] if specified file is a valid executable (native executable or script) for the target platform. + </description> + </method> + <method name="_poll_export" qualifiers="virtual"> + <return type="bool" /> + <description> + [b]Optional.[/b] + Returns [code]true[/code] if one-click deploy options are changed and editor interface should be updated. + </description> + </method> + <method name="_run" qualifiers="virtual"> + <return type="int" enum="Error" /> + <param index="0" name="preset" type="EditorExportPreset" /> + <param index="1" name="device" type="int" /> + <param index="2" name="debug_flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" /> + <description> + [b]Optional.[/b] + This method is called when [param device] one-click deploy menu option is selected. + Implementation should export project to a temporary location, upload and run it on the specific [param device], or perform another action associated with the menu item. + </description> + </method> + <method name="_should_update_export_options" qualifiers="virtual"> + <return type="bool" /> + <description> + [b]Optional.[/b] + Returns [code]true[/code] if export options list is changed and presets should be updated. + </description> + </method> + <method name="get_config_error" qualifiers="const"> + <return type="String" /> + <description> + Returns current configuration error message text. This method should be called only from the [method _can_export], [method _has_valid_export_configuration], or [method _has_valid_project_configuration] implementations. + </description> + </method> + <method name="get_config_missing_templates" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] is export templates are missing from the current configuration. This method should be called only from the [method _can_export], [method _has_valid_export_configuration], or [method _has_valid_project_configuration] implementations. + </description> + </method> + <method name="set_config_error" qualifiers="const"> + <return type="void" /> + <param index="0" name="error_text" type="String" /> + <description> + Sets current configuration error message text. This method should be called only from the [method _can_export], [method _has_valid_export_configuration], or [method _has_valid_project_configuration] implementations. + </description> + </method> + <method name="set_config_missing_templates" qualifiers="const"> + <return type="void" /> + <param index="0" name="missing_templates" type="bool" /> + <description> + Set to [code]true[/code] is export templates are missing from the current configuration. This method should be called only from the [method _can_export], [method _has_valid_export_configuration], or [method _has_valid_project_configuration] implementations. + </description> + </method> + </methods> +</class> diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index 9ef911a68d..42e1968eb0 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -305,6 +305,18 @@ In case of a directory code-sign will error if you place non code object in directory. </description> </method> + <method name="get_export_platform" qualifiers="const"> + <return type="EditorExportPlatform" /> + <description> + Returns currently used export platform. + </description> + </method> + <method name="get_export_preset" qualifiers="const"> + <return type="EditorExportPreset" /> + <description> + Returns currently used export preset. + </description> + </method> <method name="get_option" qualifiers="const"> <return type="Variant" /> <param index="0" name="name" type="StringName" /> diff --git a/doc/classes/EditorExportPreset.xml b/doc/classes/EditorExportPreset.xml new file mode 100644 index 0000000000..bba79364e4 --- /dev/null +++ b/doc/classes/EditorExportPreset.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="EditorExportPreset" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Export preset configuration. + </brief_description> + <description> + Export preset configuration. Instances of [EditorExportPreset] by editor UI and intended to be used a read-only configuration passed to the [EditorExportPlatform] methods when exporting the project. + </description> + <tutorials> + </tutorials> + <methods> + <method name="are_advanced_options_enabled" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code], is "Advanced" toggle is enabled in the export dialog. + </description> + </method> + <method name="get_custom_features" qualifiers="const"> + <return type="String" /> + <description> + Returns string with a comma separated list of custom features. + </description> + </method> + <method name="get_customized_files" qualifiers="const"> + <return type="Dictionary" /> + <description> + Returns [Dictionary] of files selected in the "Resources" tab of the export dialog. Dictionary keys are file names and values are export mode - [code]"strip[/code], [code]"keep"[/code], or [code]"remove"[/code]. See also [method get_file_export_mode]. + </description> + </method> + <method name="get_customized_files_count" qualifiers="const"> + <return type="int" /> + <description> + Returns number of files selected in the "Resources" tab of the export dialog. + </description> + </method> + <method name="get_encrypt_directory" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code], PCK directory encryption is enabled in the export dialog. + </description> + </method> + <method name="get_encrypt_pck" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code], PCK encryption is enabled in the export dialog. + </description> + </method> + <method name="get_encryption_ex_filter" qualifiers="const"> + <return type="String" /> + <description> + Returns file filters to exclude during PCK encryption. + </description> + </method> + <method name="get_encryption_in_filter" qualifiers="const"> + <return type="String" /> + <description> + Returns file filters to include during PCK encryption. + </description> + </method> + <method name="get_encryption_key" qualifiers="const"> + <return type="String" /> + <description> + Returns PCK encryption key. + </description> + </method> + <method name="get_exclude_filter" qualifiers="const"> + <return type="String" /> + <description> + Returns file filters to exclude during export. + </description> + </method> + <method name="get_export_filter" qualifiers="const"> + <return type="int" enum="EditorExportPreset.ExportFilter" /> + <description> + Returns export file filter mode selected in the "Resources" tab of the export dialog. + </description> + </method> + <method name="get_export_path" qualifiers="const"> + <return type="String" /> + <description> + Returns export target path. + </description> + </method> + <method name="get_file_export_mode" qualifiers="const"> + <return type="int" enum="EditorExportPreset.FileExportMode" /> + <param index="0" name="path" type="String" /> + <param index="1" name="default" type="int" enum="EditorExportPreset.FileExportMode" default="0" /> + <description> + Returns file export mode for the specified file. + </description> + </method> + <method name="get_files_to_export" qualifiers="const"> + <return type="PackedStringArray" /> + <description> + Returns array of files to export. + </description> + </method> + <method name="get_include_filter" qualifiers="const"> + <return type="String" /> + <description> + Returns file filters to include during export. + </description> + </method> + <method name="get_or_env" qualifiers="const"> + <return type="Variant" /> + <param index="0" name="name" type="StringName" /> + <param index="1" name="env_var" type="String" /> + <description> + Returns export option value or value of environment variable if it is set. + </description> + </method> + <method name="get_preset_name" qualifiers="const"> + <return type="String" /> + <description> + Returns export preset name. + </description> + </method> + <method name="get_script_export_mode" qualifiers="const"> + <return type="int" /> + <description> + Returns script export mode. + </description> + </method> + <method name="get_version" qualifiers="const"> + <return type="String" /> + <param index="0" name="name" type="StringName" /> + <param index="1" name="windows_version" type="bool" /> + <description> + Returns the preset's version number, or fall back to the [member ProjectSettings.application/config/version] project setting if set to an empty string. + If [param windows_version] is [code]true[/code], formats the returned version number to be compatible with Windows executable metadata. + </description> + </method> + <method name="has" qualifiers="const"> + <return type="bool" /> + <param index="0" name="property" type="StringName" /> + <description> + Returns [code]true[/code] if preset has specified property. + </description> + </method> + <method name="has_export_file"> + <return type="bool" /> + <param index="0" name="path" type="String" /> + <description> + Returns [code]true[/code] if specified file is exported. + </description> + </method> + <method name="is_dedicated_server" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if dedicated server export mode is selected in the export dialog. + </description> + </method> + <method name="is_runnable" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if "Runnable" toggle is enabled in the export dialog. + </description> + </method> + </methods> + <constants> + <constant name="EXPORT_ALL_RESOURCES" value="0" enum="ExportFilter"> + </constant> + <constant name="EXPORT_SELECTED_SCENES" value="1" enum="ExportFilter"> + </constant> + <constant name="EXPORT_SELECTED_RESOURCES" value="2" enum="ExportFilter"> + </constant> + <constant name="EXCLUDE_SELECTED_RESOURCES" value="3" enum="ExportFilter"> + </constant> + <constant name="EXPORT_CUSTOMIZED" value="4" enum="ExportFilter"> + </constant> + <constant name="MODE_FILE_NOT_CUSTOMIZED" value="0" enum="FileExportMode"> + </constant> + <constant name="MODE_FILE_STRIP" value="1" enum="FileExportMode"> + </constant> + <constant name="MODE_FILE_KEEP" value="2" enum="FileExportMode"> + </constant> + <constant name="MODE_FILE_REMOVE" value="3" enum="FileExportMode"> + </constant> + <constant name="MODE_SCRIPT_TEXT" value="0" enum="ScriptExportMode"> + </constant> + <constant name="MODE_SCRIPT_BINARY_TOKENS" value="1" enum="ScriptExportMode"> + </constant> + <constant name="MODE_SCRIPT_BINARY_TOKENS_COMPRESSED" value="2" enum="ScriptExportMode"> + </constant> + </constants> +</class> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index ed3233b1ae..37f8b2213b 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -455,6 +455,13 @@ Adds a [Script] as debugger plugin to the Debugger. The script must extend [EditorDebuggerPlugin]. </description> </method> + <method name="add_export_platform"> + <return type="void" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <description> + Registers a new [EditorExportPlatform]. Export platforms provides functionality of exporting to the specific platform. + </description> + </method> <method name="add_export_plugin"> <return type="void" /> <param index="0" name="plugin" type="EditorExportPlugin" /> @@ -653,6 +660,13 @@ Removes the debugger plugin with given script from the Debugger. </description> </method> + <method name="remove_export_platform"> + <return type="void" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <description> + Removes an export platform registered by [method add_export_platform]. + </description> + </method> <method name="remove_export_plugin"> <return type="void" /> <param index="0" name="plugin" type="EditorExportPlugin" /> diff --git a/doc/classes/GDExtensionManager.xml b/doc/classes/GDExtensionManager.xml index 211bc023c0..97d2d08752 100644 --- a/doc/classes/GDExtensionManager.xml +++ b/doc/classes/GDExtensionManager.xml @@ -56,6 +56,20 @@ </method> </methods> <signals> + <signal name="extension_loaded"> + <param index="0" name="extension" type="GDExtension" /> + <description> + Emitted after the editor has finished loading a new extension. + [b]Note:[/b] This signal is only emitted in editor builds. + </description> + </signal> + <signal name="extension_unloading"> + <param index="0" name="extension" type="GDExtension" /> + <description> + Emitted before the editor starts unloading an extension. + [b]Note:[/b] This signal is only emitted in editor builds. + </description> + </signal> <signal name="extensions_reloaded"> <description> Emitted after the editor has finished reloading one or more extensions. diff --git a/doc/classes/JSON.xml b/doc/classes/JSON.xml index 8a19aa39bf..fe5fdfa89a 100644 --- a/doc/classes/JSON.xml +++ b/doc/classes/JSON.xml @@ -37,6 +37,16 @@ <tutorials> </tutorials> <methods> + <method name="from_native" qualifiers="static"> + <return type="Variant" /> + <param index="0" name="variant" type="Variant" /> + <param index="1" name="allow_classes" type="bool" default="false" /> + <param index="2" name="allow_scripts" type="bool" default="false" /> + <description> + Converts a native engine type to a JSON-compliant dictionary. + By default, classes and scripts are ignored for security reasons, unless [param allow_classes] or [param allow_scripts] are specified. + </description> + </method> <method name="get_error_line" qualifiers="const"> <return type="int" /> <description> @@ -123,6 +133,16 @@ [/codeblock] </description> </method> + <method name="to_native" qualifiers="static"> + <return type="Variant" /> + <param index="0" name="json" type="Variant" /> + <param index="1" name="allow_classes" type="bool" default="false" /> + <param index="2" name="allow_scripts" type="bool" default="false" /> + <description> + Converts a JSON-compliant dictionary that was created with [method from_native] back to native engine types. + By default, classes and scripts are ignored for security reasons, unless [param allow_classes] or [param allow_scripts] are specified. + </description> + </method> </methods> <members> <member name="data" type="Variant" setter="set_data" getter="get_data" default="null"> diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml index 50adecccf5..5cf077c266 100644 --- a/doc/classes/ScriptEditor.xml +++ b/doc/classes/ScriptEditor.xml @@ -10,6 +10,12 @@ <tutorials> </tutorials> <methods> + <method name="get_breakpoints"> + <return type="PackedStringArray" /> + <description> + Returns array of breakpoints. + </description> + </method> <method name="get_current_editor" qualifiers="const"> <return type="ScriptEditorBase" /> <description> diff --git a/doc/classes/VehicleWheel3D.xml b/doc/classes/VehicleWheel3D.xml index 92b2297bf4..7b4952580c 100644 --- a/doc/classes/VehicleWheel3D.xml +++ b/doc/classes/VehicleWheel3D.xml @@ -18,6 +18,18 @@ Returns [code]null[/code] if the wheel is not in contact with a surface, or the contact body is not a [PhysicsBody3D]. </description> </method> + <method name="get_contact_normal" qualifiers="const"> + <return type="Vector3" /> + <description> + Returns the normal of the suspension's collision in world space if the wheel is in contact. If the wheel isn't in contact with anything, returns a vector pointing directly along the suspension axis toward the vehicle in world space. + </description> + </method> + <method name="get_contact_point" qualifiers="const"> + <return type="Vector3" /> + <description> + Returns the point of the suspension's collision in world space if the wheel is in contact. If the wheel isn't in contact with anything, returns the maximum point of the wheel's ray cast in world space, which is defined by [code]wheel_rest_length + wheel_radius[/code]. + </description> + </method> <method name="get_rpm" qualifiers="const"> <return type="float" /> <description> diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 81e7220439..ed00dd235f 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -203,7 +203,7 @@ struct LightmapInstance { class LightStorage : public RendererLightStorage { public: - enum ShadowAtlastQuadrant { + enum ShadowAtlastQuadrant : uint32_t { QUADRANT_SHIFT = 27, OMNI_LIGHT_FLAG = 1 << 26, SHADOW_INDEX_MASK = OMNI_LIGHT_FLAG - 1, diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 1d1f2ce415..f7632842ed 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -71,7 +71,9 @@ struct DirAccessWindowsPrivate { String DirAccessWindows::fix_path(const String &p_path) const { String r_path = DirAccess::fix_path(p_path.trim_prefix(R"(\\?\)").replace("\\", "/")); - + if (r_path.ends_with(":")) { + r_path += "/"; + } if (r_path.is_relative_path()) { r_path = current_dir.trim_prefix(R"(\\?\)").replace("\\", "/").path_join(r_path); } else if (r_path == ".") { @@ -304,7 +306,7 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) { } } - return MoveFileW((LPCWSTR)(path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) != 0 ? OK : FAILED; + return MoveFileW((LPCWSTR)(path.utf16().get_data()), (LPCWSTR)(new_path.utf16().get_data())) != 0 ? OK : FAILED; } } diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index f4403780c2..69b130a0f1 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -41,6 +41,7 @@ #include "editor/gui/editor_spin_slider.h" #include "editor/gui/scene_tree_editor.h" #include "editor/inspector_dock.h" +#include "editor/multi_node_edit.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/themes/editor_scale.h" #include "scene/3d/mesh_instance_3d.h" @@ -3661,7 +3662,7 @@ void AnimationTrackEditor::update_keying() { EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history(); if (is_visible_in_tree() && animation.is_valid() && editor_history->get_path_size() > 0) { Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0)); - keying_enabled = Object::cast_to<Node>(obj) != nullptr; + keying_enabled = Object::cast_to<Node>(obj) != nullptr || Object::cast_to<MultiNodeEdit>(obj) != nullptr; } if (keying_enabled == keying) { @@ -4078,19 +4079,20 @@ void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant _query_insert(id); } -void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists) { +void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_property, bool p_only_if_exists, bool p_advance) { ERR_FAIL_NULL(root); // Let's build a node path. - Node *node = p_node; - String path = root->get_path_to(node, true); + String path = root->get_path_to(p_node, true); + + Variant value = p_node->get(p_property); - if (Object::cast_to<AnimationPlayer>(node) && p_property == "current_animation") { - if (node == AnimationPlayerEditor::get_singleton()->get_player()) { + if (Object::cast_to<AnimationPlayer>(p_node) && p_property == "current_animation") { + if (p_node == AnimationPlayerEditor::get_singleton()->get_player()) { EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players.")); return; } - _insert_animation_key(path, p_value); + _insert_animation_key(path, value); return; } @@ -4118,26 +4120,26 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p InsertData id; id.path = np; id.track_idx = i; - id.value = p_value; + id.value = value; id.type = Animation::TYPE_VALUE; // TRANSLATORS: This describes the target of new animation track, will be inserted into another string. id.query = vformat(TTR("property '%s'"), p_property); - id.advance = false; + id.advance = p_advance; // Dialog insert. _query_insert(id); inserted = true; } else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) { - Variant value; + Variant actual_value; String track_path = animation->track_get_path(i); if (track_path == np) { - value = p_value; // All good. + actual_value = value; // All good. } else { int sep = track_path.rfind(":"); if (sep != -1) { String base_path = track_path.substr(0, sep); if (base_path == np) { String value_name = track_path.substr(sep + 1); - value = p_value.get(value_name); + actual_value = value.get(value_name); } else { continue; } @@ -4149,10 +4151,10 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p InsertData id; id.path = animation->track_get_path(i); id.track_idx = i; - id.value = value; + id.value = actual_value; id.type = Animation::TYPE_BEZIER; id.query = vformat(TTR("property '%s'"), p_property); - id.advance = false; + id.advance = p_advance; // Dialog insert. _query_insert(id); inserted = true; @@ -4165,105 +4167,41 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p InsertData id; id.path = np; id.track_idx = -1; - id.value = p_value; + id.value = value; id.type = Animation::TYPE_VALUE; id.query = vformat(TTR("property '%s'"), p_property); - id.advance = false; + id.advance = p_advance; // Dialog insert. _query_insert(id); } -void AnimationTrackEditor::insert_value_key(const String &p_property, const Variant &p_value, bool p_advance) { +void AnimationTrackEditor::insert_value_key(const String &p_property, bool p_advance) { EditorSelectionHistory *history = EditorNode::get_singleton()->get_editor_selection_history(); ERR_FAIL_NULL(root); ERR_FAIL_COND(history->get_path_size() == 0); Object *obj = ObjectDB::get_instance(history->get_path_object(0)); - ERR_FAIL_NULL(Object::cast_to<Node>(obj)); - - // Let's build a node path. - Node *node = Object::cast_to<Node>(obj); - String path = root->get_path_to(node, true); - - if (Object::cast_to<AnimationPlayer>(node) && p_property == "current_animation") { - if (node == AnimationPlayerEditor::get_singleton()->get_player()) { - EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players.")); - return; - } - _insert_animation_key(path, p_value); - return; - } - - for (int i = 1; i < history->get_path_size(); i++) { - String prop = history->get_path_property(i); - ERR_FAIL_COND(prop.is_empty()); - path += ":" + prop; - } - - path += ":" + p_property; - NodePath np = path; + Ref<MultiNodeEdit> multi_node_edit(obj); + if (multi_node_edit.is_valid()) { + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + ERR_FAIL_NULL(edited_scene); - // Locate track. - - bool inserted = false; + make_insert_queue(); - make_insert_queue(); - for (int i = 0; i < animation->get_track_count(); i++) { - if (animation->track_get_type(i) == Animation::TYPE_VALUE) { - if (animation->track_get_path(i) != np) { - continue; - } - - InsertData id; - id.path = np; - id.track_idx = i; - id.value = p_value; - id.type = Animation::TYPE_VALUE; - id.query = vformat(TTR("property '%s'"), p_property); - id.advance = p_advance; - // Dialog insert. - _query_insert(id); - inserted = true; - } else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) { - Variant value; - if (animation->track_get_path(i) == np) { - value = p_value; // All good. - } else { - String tpath = animation->track_get_path(i); - int index = tpath.rfind(":"); - if (NodePath(tpath.substr(0, index + 1)) == np) { - String subindex = tpath.substr(index + 1, tpath.length() - index); - value = p_value.get(subindex); - } else { - continue; - } - } - - InsertData id; - id.path = animation->track_get_path(i); - id.track_idx = i; - id.value = value; - id.type = Animation::TYPE_BEZIER; - id.query = vformat(TTR("property '%s'"), p_property); - id.advance = p_advance; - // Dialog insert. - _query_insert(id); - inserted = true; + for (int i = 0; i < multi_node_edit->get_node_count(); ++i) { + Node *node = edited_scene->get_node(multi_node_edit->get_node(i)); + insert_node_value_key(node, p_property, false, p_advance); } - } - commit_insert_queue(); - if (!inserted) { - InsertData id; - id.path = np; - id.track_idx = -1; - id.value = p_value; - id.type = Animation::TYPE_VALUE; - id.query = vformat(TTR("property '%s'"), p_property); - id.advance = p_advance; - // Dialog insert. - _query_insert(id); + commit_insert_queue(); + } else { + Node *node = Object::cast_to<Node>(obj); + ERR_FAIL_NULL(node); + + make_insert_queue(); + insert_node_value_key(node, p_property, false, p_advance); + commit_insert_queue(); } } diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 59ee6535ac..a517ba8b43 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -713,8 +713,8 @@ public: void cleanup(); void set_anim_pos(float p_pos); - void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false); - void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance); + void insert_node_value_key(Node *p_node, const String &p_property, bool p_only_if_exists = false, bool p_advance = false); + void insert_value_key(const String &p_property, bool p_advance); void insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant &p_value); bool has_track(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type); void make_insert_queue(); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index d8144cbfae..dd8aa523c4 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1548,7 +1548,8 @@ void CodeTextEditor::_set_show_warnings_panel(bool p_show) { } void CodeTextEditor::_toggle_scripts_pressed() { - ScriptEditor::get_singleton()->toggle_scripts_panel(); + ERR_FAIL_NULL(toggle_scripts_list); + toggle_scripts_list->set_visible(!toggle_scripts_list->is_visible()); update_toggle_scripts_button(); } @@ -1723,16 +1724,18 @@ void CodeTextEditor::set_code_complete_func(CodeTextEditorCodeCompleteFunc p_cod code_complete_ud = p_ud; } +void CodeTextEditor::set_toggle_list_control(Control *p_control) { + toggle_scripts_list = p_control; +} + void CodeTextEditor::show_toggle_scripts_button() { toggle_scripts_button->show(); } void CodeTextEditor::update_toggle_scripts_button() { - if (is_layout_rtl()) { - toggle_scripts_button->set_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back"))); - } else { - toggle_scripts_button->set_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"))); - } + ERR_FAIL_NULL(toggle_scripts_list); + bool forward = toggle_scripts_list->is_visible() == is_layout_rtl(); + toggle_scripts_button->set_icon(get_editor_theme_icon(forward ? SNAME("Forward") : SNAME("Back"))); toggle_scripts_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text())); } diff --git a/editor/code_editor.h b/editor/code_editor.h index d209d5c7a2..e56405a4b2 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -161,6 +161,7 @@ class CodeTextEditor : public VBoxContainer { HBoxContainer *status_bar = nullptr; Button *toggle_scripts_button = nullptr; + Control *toggle_scripts_list = nullptr; Button *error_button = nullptr; Button *warning_button = nullptr; @@ -285,6 +286,7 @@ public: void validate_script(); + void set_toggle_list_control(Control *p_control); void show_toggle_scripts_button(); void update_toggle_scripts_button(); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 474a45cf2b..bae9062ff1 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -243,18 +243,27 @@ void EditorFileSystem::_first_scan_filesystem() { first_scan_root_dir = memnew(ScannedDirectory); first_scan_root_dir->full_path = "res://"; HashSet<String> existing_class_names; + HashSet<String> extensions; ep.step(TTR("Scanning file structure..."), 0, true); nb_files_total = _scan_new_dir(first_scan_root_dir, d); // This loads the global class names from the scripts and ensures that even if the // global_script_class_cache.cfg was missing or invalid, the global class names are valid in ScriptServer. + // At the same time, to prevent looping multiple times in all files, it looks for extensions. ep.step(TTR("Loading global class names..."), 1, true); - _first_scan_process_scripts(first_scan_root_dir, existing_class_names); + _first_scan_process_scripts(first_scan_root_dir, existing_class_names, extensions); // Removing invalid global class to prevent having invalid paths in ScriptServer. _remove_invalid_global_class_names(existing_class_names); + // Processing extensions to add new extensions or remove invalid ones. + // Important to do it in the first scan so custom types, new class names, custom importers, etc... + // from extensions are ready to go before plugins, autoloads and resources validation/importation. + // At this point, a restart of the editor should not be needed so we don't use the return value. + ep.step(TTR("Verifying GDExtensions..."), 2, true); + GDExtensionManager::get_singleton()->ensure_extensions_loaded(extensions); + // Now that all the global class names should be loaded, create autoloads and plugins. // This is done after loading the global class names because autoloads and plugins can use // global class names. @@ -267,9 +276,9 @@ void EditorFileSystem::_first_scan_filesystem() { ep.step(TTR("Starting file scan..."), 5, true); } -void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names) { +void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions) { for (ScannedDirectory *scan_sub_dir : p_scan_dir->subdirs) { - _first_scan_process_scripts(scan_sub_dir, p_existing_class_names); + _first_scan_process_scripts(scan_sub_dir, p_existing_class_names, p_extensions); } for (const String &scan_file : p_scan_dir->files) { @@ -285,6 +294,8 @@ void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_sca if (!script_class_name.is_empty()) { p_existing_class_names.insert(script_class_name); } + } else if (type == SNAME("GDExtension")) { + p_extensions.insert(path); } } } @@ -1855,25 +1866,26 @@ void EditorFileSystem::_update_script_classes() { return; } - update_script_mutex.lock(); + { + MutexLock update_script_lock(update_script_mutex); - EditorProgress *ep = nullptr; - if (update_script_paths.size() > 1) { - ep = memnew(EditorProgress("update_scripts_classes", TTR("Registering global classes..."), update_script_paths.size())); - } + EditorProgress *ep = nullptr; + if (update_script_paths.size() > 1) { + ep = memnew(EditorProgress("update_scripts_classes", TTR("Registering global classes..."), update_script_paths.size())); + } - int step_count = 0; - for (const KeyValue<String, ScriptInfo> &E : update_script_paths) { - _register_global_class_script(E.key, E.key, E.value.type, E.value.script_class_name, E.value.script_class_extends, E.value.script_class_icon_path); - if (ep) { - ep->step(E.value.script_class_name, step_count++, false); + int step_count = 0; + for (const KeyValue<String, ScriptInfo> &E : update_script_paths) { + _register_global_class_script(E.key, E.key, E.value.type, E.value.script_class_name, E.value.script_class_extends, E.value.script_class_icon_path); + if (ep) { + ep->step(E.value.script_class_name, step_count++, false); + } } - } - memdelete_notnull(ep); + memdelete_notnull(ep); - update_script_paths.clear(); - update_script_mutex.unlock(); + update_script_paths.clear(); + } ScriptServer::save_global_classes(); EditorNode::get_editor_data().script_class_save_icon_paths(); @@ -1894,7 +1906,7 @@ void EditorFileSystem::_update_script_documentation() { return; } - update_script_mutex.lock(); + MutexLock update_script_lock(update_script_mutex); EditorProgress *ep = nullptr; if (update_script_paths_documentation.size() > 1) { @@ -1933,7 +1945,6 @@ void EditorFileSystem::_update_script_documentation() { memdelete_notnull(ep); update_script_paths_documentation.clear(); - update_script_mutex.unlock(); } void EditorFileSystem::_process_update_pending() { @@ -1945,7 +1956,7 @@ void EditorFileSystem::_process_update_pending() { } void EditorFileSystem::_queue_update_script_class(const String &p_path, const String &p_type, const String &p_script_class_name, const String &p_script_class_extends, const String &p_script_class_icon_path) { - update_script_mutex.lock(); + MutexLock update_script_lock(update_script_mutex); ScriptInfo si; si.type = p_type; @@ -1955,8 +1966,6 @@ void EditorFileSystem::_queue_update_script_class(const String &p_path, const St update_script_paths.insert(p_path, si); update_script_paths_documentation.insert(p_path); - - update_script_mutex.unlock(); } void EditorFileSystem::_update_scene_groups() { @@ -1970,31 +1979,32 @@ void EditorFileSystem::_update_scene_groups() { } int step_count = 0; - update_scene_mutex.lock(); - for (const String &path : update_scene_paths) { - ProjectSettings::get_singleton()->remove_scene_groups_cache(path); + { + MutexLock update_scene_lock(update_scene_mutex); + for (const String &path : update_scene_paths) { + ProjectSettings::get_singleton()->remove_scene_groups_cache(path); - int index = -1; - EditorFileSystemDirectory *efd = find_file(path, &index); + int index = -1; + EditorFileSystemDirectory *efd = find_file(path, &index); - if (!efd || index < 0) { - // The file was removed. - continue; - } + if (!efd || index < 0) { + // The file was removed. + continue; + } - const HashSet<StringName> scene_groups = PackedScene::get_scene_groups(path); - if (!scene_groups.is_empty()) { - ProjectSettings::get_singleton()->add_scene_groups_cache(path, scene_groups); - } + const HashSet<StringName> scene_groups = PackedScene::get_scene_groups(path); + if (!scene_groups.is_empty()) { + ProjectSettings::get_singleton()->add_scene_groups_cache(path, scene_groups); + } - if (ep) { - ep->step(efd->files[index]->file, step_count++, false); + if (ep) { + ep->step(efd->files[index]->file, step_count++, false); + } } - } - memdelete_notnull(ep); - update_scene_paths.clear(); - update_scene_mutex.unlock(); + memdelete_notnull(ep); + update_scene_paths.clear(); + } ProjectSettings::get_singleton()->save_scene_groups_cache(); } @@ -2009,9 +2019,8 @@ void EditorFileSystem::_update_pending_scene_groups() { } void EditorFileSystem::_queue_update_scene_groups(const String &p_path) { - update_scene_mutex.lock(); + MutexLock update_scene_lock(update_scene_mutex); update_scene_paths.insert(p_path); - update_scene_mutex.unlock(); } void EditorFileSystem::_get_all_scenes(EditorFileSystemDirectory *p_dir, HashSet<String> &r_list) { @@ -3016,57 +3025,7 @@ bool EditorFileSystem::_scan_extensions() { _scan_extensions_dir(d, extensions); - //verify against loaded extensions - - Vector<String> extensions_added; - Vector<String> extensions_removed; - - for (const String &E : extensions) { - if (!GDExtensionManager::get_singleton()->is_extension_loaded(E)) { - extensions_added.push_back(E); - } - } - - Vector<String> loaded_extensions = GDExtensionManager::get_singleton()->get_loaded_extensions(); - for (int i = 0; i < loaded_extensions.size(); i++) { - if (!extensions.has(loaded_extensions[i])) { - // The extension may not have a .gdextension file. - if (!FileAccess::exists(loaded_extensions[i])) { - extensions_removed.push_back(loaded_extensions[i]); - } - } - } - - String extension_list_config_file = GDExtension::get_extension_list_config_file(); - if (extensions.size()) { - if (extensions_added.size() || extensions_removed.size()) { //extensions were added or removed - Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE); - for (const String &E : extensions) { - f->store_line(E); - } - } - } else { - if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) { //extensions were removed - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - da->remove(extension_list_config_file); - } - } - - bool needs_restart = false; - for (int i = 0; i < extensions_added.size(); i++) { - GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extensions_added[i]); - if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { - needs_restart = true; - } - } - for (int i = 0; i < extensions_removed.size(); i++) { - GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extensions_removed[i]); - if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { - needs_restart = true; - } - } - - return needs_restart; + return GDExtensionManager::get_singleton()->ensure_extensions_loaded(extensions); } void EditorFileSystem::_bind_methods() { diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 1bc24416eb..ca4a64bfac 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -187,7 +187,7 @@ class EditorFileSystem : public Node { void _scan_filesystem(); void _first_scan_filesystem(); - void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names); + void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions); HashSet<String> late_update_files; diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index ff5bc6ba87..eb97337b37 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -134,18 +134,38 @@ void EditorHelpSearch::_native_action_cb(const String &p_item_string) { } void EditorHelpSearch::_update_results() { - String term = search_box->get_text(); + const String term = search_box->get_text().strip_edges(); int search_flags = filter_combo->get_selected_id(); - if (case_sensitive_button->is_pressed()) { - search_flags |= SEARCH_CASE_SENSITIVE; - } - if (hierarchy_button->is_pressed()) { - search_flags |= SEARCH_SHOW_HIERARCHY; - } - search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags))); - set_process(true); + // Process separately if term is not short, or is "@" for annotations. + if (term.length() > 1 || term == "@") { + case_sensitive_button->set_disabled(false); + hierarchy_button->set_disabled(false); + + if (case_sensitive_button->is_pressed()) { + search_flags |= SEARCH_CASE_SENSITIVE; + } + if (hierarchy_button->is_pressed()) { + search_flags |= SEARCH_SHOW_HIERARCHY; + } + + search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags))); + + // Clear old search flags to force rebuild on short term. + old_search_flags = 0; + set_process(true); + } else { + // Disable hierarchy and case sensitive options, not used for short searches. + case_sensitive_button->set_disabled(true); + hierarchy_button->set_disabled(true); + + // Always show hierarchy for short searches. + search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags | SEARCH_SHOW_HIERARCHY))); + + old_search_flags = search_flags; + set_process(true); + } } void EditorHelpSearch::_search_box_gui_input(const Ref<InputEvent> &p_event) { @@ -205,6 +225,8 @@ void EditorHelpSearch::_notification(int p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { tree_cache.clear(); + results_tree->get_vscroll_bar()->set_value(0); + search = Ref<Runner>(); callable_mp(results_tree, &Tree::clear).call_deferred(); // Wait for the Tree's mouse event propagation. get_ok_button()->set_disabled(true); EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "search_help", Rect2(get_position(), get_size())); @@ -278,6 +300,7 @@ void EditorHelpSearch::popup_dialog(const String &p_term) { popup_centered_ratio(0.5F); } + old_search_flags = 0; if (p_term.is_empty()) { search_box->clear(); } else { @@ -401,6 +424,237 @@ bool EditorHelpSearch::Runner::_is_class_disabled_by_feature_profile(const Strin return false; } +bool EditorHelpSearch::Runner::_fill() { + bool phase_done = false; + switch (phase) { + case PHASE_MATCH_CLASSES_INIT: + phase_done = _phase_fill_classes_init(); + break; + case PHASE_MATCH_CLASSES: + phase_done = _phase_fill_classes(); + break; + case PHASE_CLASS_ITEMS_INIT: + case PHASE_CLASS_ITEMS: + phase_done = true; + break; + case PHASE_MEMBER_ITEMS_INIT: + phase_done = _phase_fill_member_items_init(); + break; + case PHASE_MEMBER_ITEMS: + phase_done = _phase_fill_member_items(); + break; + case PHASE_SELECT_MATCH: + phase_done = _phase_select_match(); + break; + case PHASE_MAX: + return true; + default: + WARN_PRINT("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search."); + return true; + } + + if (phase_done) { + phase++; + } + return false; +} + +bool EditorHelpSearch::Runner::_phase_fill_classes_init() { + // Initialize fill. + iterator_stack.clear(); + matched_classes.clear(); + matched_item = nullptr; + match_highest_score = 0; + + // Initialize stack of iterators to fill, in reverse. + iterator_stack.push_back(EditorHelp::get_doc_data()->inheriting[""].back()); + + return true; +} + +bool EditorHelpSearch::Runner::_phase_fill_classes() { + if (iterator_stack.is_empty()) { + return true; + } + + if (iterator_stack[iterator_stack.size() - 1]) { + DocData::ClassDoc *class_doc = EditorHelp::get_doc_data()->class_list.getptr(iterator_stack[iterator_stack.size() - 1]->get()); + + // Decrement stack. + iterator_stack[iterator_stack.size() - 1] = iterator_stack[iterator_stack.size() - 1]->prev(); + + // Drop last element of stack if empty. + if (!iterator_stack[iterator_stack.size() - 1]) { + iterator_stack.resize(iterator_stack.size() - 1); + } + + if (!class_doc || class_doc->name.is_empty()) { + return false; + } + + // If class matches the flags, add it to the matched stack. + const bool class_matched = + (search_flags & SEARCH_CLASSES) || + ((search_flags & SEARCH_CONSTRUCTORS) && !class_doc->constructors.is_empty()) || + ((search_flags & SEARCH_METHODS) && !class_doc->methods.is_empty()) || + ((search_flags & SEARCH_OPERATORS) && !class_doc->operators.is_empty()) || + ((search_flags & SEARCH_SIGNALS) && !class_doc->signals.is_empty()) || + ((search_flags & SEARCH_CONSTANTS) && !class_doc->constants.is_empty()) || + ((search_flags & SEARCH_PROPERTIES) && !class_doc->properties.is_empty()) || + ((search_flags & SEARCH_THEME_ITEMS) && !class_doc->theme_properties.is_empty()) || + ((search_flags & SEARCH_ANNOTATIONS) && !class_doc->annotations.is_empty()); + + if (class_matched) { + if (term.is_empty() || class_doc->name.containsn(term)) { + matched_classes.push_back(Pair<DocData::ClassDoc *, String>(class_doc, String())); + } else if (String keyword = _match_keywords(term, class_doc->keywords); !keyword.is_empty()) { + matched_classes.push_back(Pair<DocData::ClassDoc *, String>(class_doc, keyword)); + } + } + + // Add inheriting classes, in reverse. + if (class_doc && EditorHelp::get_doc_data()->inheriting.has(class_doc->name)) { + iterator_stack.push_back(EditorHelp::get_doc_data()->inheriting[class_doc->name].back()); + } + + return false; + } + + // Drop last element of stack if empty. + if (!iterator_stack[iterator_stack.size() - 1]) { + iterator_stack.resize(iterator_stack.size() - 1); + } + + return iterator_stack.is_empty(); +} + +bool EditorHelpSearch::Runner::_phase_fill_member_items_init() { + // Prepare tree. + class_items.clear(); + _populate_cache(); + + 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) { + const String item_meta = "class_" + p_metatype + ":" + p_class; + + TreeItem *item = nullptr; + if (_find_or_create_item(p_parent, item_meta, item)) { + item->set_icon(0, ui_service->get_editor_theme_icon(p_icon)); + item->set_text(0, p_text); + item->set_metadata(0, item_meta); + } + item->set_collapsed(true); + + return item; +} + +bool EditorHelpSearch::Runner::_phase_fill_member_items() { + if (matched_classes.is_empty()) { + return true; + } + + // Pop working item from stack. + Pair<DocData::ClassDoc *, String> match = matched_classes[matched_classes.size() - 1]; + DocData::ClassDoc *class_doc = match.first; + const String &keyword = match.second; + matched_classes.resize(matched_classes.size() - 1); + + if (class_doc) { + TreeItem *item = _create_class_hierarchy(class_doc, keyword, !(search_flags & SEARCH_CLASSES)); + + // If the class has no inheriting classes, fold its item. + item->set_collapsed(!item->get_first_child()); + + if (search_flags & SEARCH_CLASSES) { + item->clear_custom_color(0); + item->clear_custom_color(1); + } else { + item->set_custom_color(0, disabled_color); + item->set_custom_color(1, disabled_color); + } + + // Create common header if required. + const bool search_all = (search_flags & SEARCH_ALL) == SEARCH_ALL; + + if ((search_flags & SEARCH_CONSTRUCTORS) && !class_doc->constructors.is_empty()) { + TreeItem *parent_item = item; + if (search_all) { + parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberConstructor"), TTRC("Constructors"), "constructors"); + } + for (const DocData::MethodDoc &constructor_doc : class_doc->constructors) { + _create_constructor_item(parent_item, class_doc, &constructor_doc); + } + } + if ((search_flags & SEARCH_METHODS) && !class_doc->methods.is_empty()) { + TreeItem *parent_item = item; + if (search_all) { + parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberMethod"), TTRC("Methods"), "methods"); + } + for (const DocData::MethodDoc &method_doc : class_doc->methods) { + _create_method_item(parent_item, class_doc, &method_doc); + } + } + if ((search_flags & SEARCH_OPERATORS) && !class_doc->operators.is_empty()) { + TreeItem *parent_item = item; + if (search_all) { + parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberOperator"), TTRC("Operators"), "operators"); + } + for (const DocData::MethodDoc &operator_doc : class_doc->operators) { + _create_operator_item(parent_item, class_doc, &operator_doc); + } + } + if ((search_flags & SEARCH_SIGNALS) && !class_doc->signals.is_empty()) { + TreeItem *parent_item = item; + if (search_all) { + parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberSignal"), TTRC("Signals"), "signals"); + } + for (const DocData::MethodDoc &signal_doc : class_doc->signals) { + _create_signal_item(parent_item, class_doc, &signal_doc); + } + } + if ((search_flags & SEARCH_CONSTANTS) && !class_doc->constants.is_empty()) { + TreeItem *parent_item = item; + if (search_all) { + parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberConstant"), TTRC("Constants"), "constants"); + } + for (const DocData::ConstantDoc &constant_doc : class_doc->constants) { + _create_constant_item(parent_item, class_doc, &constant_doc); + } + } + 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"); + } + for (const DocData::PropertyDoc &property_doc : class_doc->properties) { + _create_property_item(parent_item, class_doc, &property_doc); + } + } + if ((search_flags & SEARCH_THEME_ITEMS) && !class_doc->theme_properties.is_empty()) { + TreeItem *parent_item = item; + if (search_all) { + parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberTheme"), TTRC("Theme Properties"), "theme_items"); + } + for (const DocData::ThemeItemDoc &theme_property_doc : class_doc->theme_properties) { + _create_theme_property_item(parent_item, class_doc, &theme_property_doc); + } + } + if ((search_flags & SEARCH_ANNOTATIONS) && !class_doc->annotations.is_empty()) { + TreeItem *parent_item = item; + if (search_all) { + parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberAnnotation"), TTRC("Annotations"), "annotations"); + } + for (const DocData::MethodDoc &annotation_doc : class_doc->annotations) { + _create_annotation_item(parent_item, class_doc, &annotation_doc); + } + } + } + + return matched_classes.is_empty(); +} + bool EditorHelpSearch::Runner::_slice() { bool phase_done = false; switch (phase) { @@ -430,7 +684,7 @@ bool EditorHelpSearch::Runner::_slice() { default: WARN_PRINT("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search."); return true; - }; + } if (phase_done) { phase++; @@ -450,9 +704,11 @@ bool EditorHelpSearch::Runner::_phase_match_classes_init() { matched_item = nullptr; match_highest_score = 0; - terms = term.split_spaces(); - if (terms.is_empty()) { - terms.append(term); + if (!term.is_empty()) { + terms = term.split_spaces(); + if (terms.is_empty()) { + terms.append(term); + } } return true; @@ -480,78 +736,71 @@ bool EditorHelpSearch::Runner::_phase_match_classes() { // Match class name. if (search_flags & SEARCH_CLASSES) { - // If the search term is empty, add any classes which are not script docs or which don't start with - // a double-quotation. This will ensure that only C++ classes and explicitly named classes will - // be added. - match.name = (term.is_empty() && (!class_doc->is_script_doc || class_doc->name[0] != '\"')) || _match_string(term, class_doc->name); + match.name = _match_string(term, class_doc->name); match.keyword = _match_keywords(term, class_doc->keywords); } - // Match members only if the term is long enough, to avoid slow performance from building a large tree. - // Make an exception for annotations, since there are not that many of them. - if (term.length() > 1 || term == "@") { - if (search_flags & SEARCH_CONSTRUCTORS) { - _match_method_name_and_push_back(class_doc->constructors, &match.constructors); - } - if (search_flags & SEARCH_METHODS) { - _match_method_name_and_push_back(class_doc->methods, &match.methods); - } - if (search_flags & SEARCH_OPERATORS) { - _match_method_name_and_push_back(class_doc->operators, &match.operators); - } - if (search_flags & SEARCH_SIGNALS) { - for (int i = 0; i < class_doc->signals.size(); i++) { - MemberMatch<DocData::MethodDoc> signal; - signal.name = _all_terms_in_name(class_doc->signals[i].name); - signal.keyword = _match_keywords_in_all_terms(class_doc->signals[i].keywords); - if (signal.name || !signal.keyword.is_empty()) { - signal.doc = const_cast<DocData::MethodDoc *>(&class_doc->signals[i]); - match.signals.push_back(signal); - } + if (search_flags & SEARCH_CONSTRUCTORS) { + _match_method_name_and_push_back(class_doc->constructors, &match.constructors); + } + if (search_flags & SEARCH_METHODS) { + _match_method_name_and_push_back(class_doc->methods, &match.methods); + } + if (search_flags & SEARCH_OPERATORS) { + _match_method_name_and_push_back(class_doc->operators, &match.operators); + } + if (search_flags & SEARCH_SIGNALS) { + for (const DocData::MethodDoc &signal_doc : class_doc->signals) { + MemberMatch<DocData::MethodDoc> signal; + signal.name = _all_terms_in_name(signal_doc.name); + signal.keyword = _match_keywords_in_all_terms(signal_doc.keywords); + if (signal.name || !signal.keyword.is_empty()) { + signal.doc = &signal_doc; + match.signals.push_back(signal); } } - if (search_flags & SEARCH_CONSTANTS) { - for (int i = 0; i < class_doc->constants.size(); i++) { - MemberMatch<DocData::ConstantDoc> constant; - constant.name = _all_terms_in_name(class_doc->constants[i].name); - constant.keyword = _match_keywords_in_all_terms(class_doc->constants[i].keywords); - if (constant.name || !constant.keyword.is_empty()) { - constant.doc = const_cast<DocData::ConstantDoc *>(&class_doc->constants[i]); - match.constants.push_back(constant); - } + } + if (search_flags & SEARCH_CONSTANTS) { + for (const DocData::ConstantDoc &constant_doc : class_doc->constants) { + MemberMatch<DocData::ConstantDoc> constant; + constant.name = _all_terms_in_name(constant_doc.name); + constant.keyword = _match_keywords_in_all_terms(constant_doc.keywords); + if (constant.name || !constant.keyword.is_empty()) { + constant.doc = &constant_doc; + match.constants.push_back(constant); } } - if (search_flags & SEARCH_PROPERTIES) { - for (int i = 0; i < class_doc->properties.size(); i++) { - MemberMatch<DocData::PropertyDoc> property; - property.name = _all_terms_in_name(class_doc->properties[i].name); - property.keyword = _match_keywords_in_all_terms(class_doc->properties[i].keywords); - if (property.name || !property.keyword.is_empty()) { - property.doc = const_cast<DocData::PropertyDoc *>(&class_doc->properties[i]); - match.properties.push_back(property); - } + } + if (search_flags & SEARCH_PROPERTIES) { + for (const DocData::PropertyDoc &property_doc : class_doc->properties) { + MemberMatch<DocData::PropertyDoc> property; + property.name = _all_terms_in_name(property_doc.name); + property.keyword = _match_keywords_in_all_terms(property_doc.keywords); + if (property.name || !property.keyword.is_empty()) { + property.doc = &property_doc; + match.properties.push_back(property); } } - if (search_flags & SEARCH_THEME_ITEMS) { - for (int i = 0; i < class_doc->theme_properties.size(); i++) { - MemberMatch<DocData::ThemeItemDoc> theme_property; - theme_property.name = _all_terms_in_name(class_doc->theme_properties[i].name); - theme_property.keyword = _match_keywords_in_all_terms(class_doc->theme_properties[i].keywords); - if (theme_property.name || !theme_property.keyword.is_empty()) { - theme_property.doc = const_cast<DocData::ThemeItemDoc *>(&class_doc->theme_properties[i]); - match.theme_properties.push_back(theme_property); - } + } + if (search_flags & SEARCH_THEME_ITEMS) { + for (const DocData::ThemeItemDoc &theme_property_doc : class_doc->theme_properties) { + MemberMatch<DocData::ThemeItemDoc> theme_property; + theme_property.name = _all_terms_in_name(theme_property_doc.name); + theme_property.keyword = _match_keywords_in_all_terms(theme_property_doc.keywords); + if (theme_property.name || !theme_property.keyword.is_empty()) { + theme_property.doc = &theme_property_doc; + match.theme_properties.push_back(theme_property); } } - if (search_flags & SEARCH_ANNOTATIONS) { - for (int i = 0; i < class_doc->annotations.size(); i++) { - MemberMatch<DocData::MethodDoc> annotation; - annotation.name = _all_terms_in_name(class_doc->annotations[i].name); - annotation.keyword = _match_keywords_in_all_terms(class_doc->annotations[i].keywords); - if (annotation.name || !annotation.keyword.is_empty()) { - annotation.doc = const_cast<DocData::MethodDoc *>(&class_doc->annotations[i]); - match.annotations.push_back(annotation); - } + } + if (search_flags & SEARCH_ANNOTATIONS) { + for (const DocData::MethodDoc &annotation_doc : class_doc->annotations) { + MemberMatch<DocData::MethodDoc> annotation; + annotation.name = _all_terms_in_name(annotation_doc.name); + annotation.keyword = _match_keywords_in_all_terms(annotation_doc.keywords); + if (annotation.name || !annotation.keyword.is_empty()) { + annotation.doc = &annotation_doc; + match.annotations.push_back(annotation); } } } @@ -564,9 +813,11 @@ bool EditorHelpSearch::Runner::_phase_match_classes() { } if (!iterator_stack.is_empty()) { + // Iterate on stack. if (iterator_stack[iterator_stack.size() - 1]) { iterator_stack[iterator_stack.size() - 1] = iterator_stack[iterator_stack.size() - 1]->next(); } + // Drop last element of stack. if (!iterator_stack[iterator_stack.size() - 1]) { iterator_stack.resize(iterator_stack.size() - 1); } @@ -661,36 +912,32 @@ bool EditorHelpSearch::Runner::_phase_member_items() { return false; } + // Pick appropriate parent item if showing hierarchy, otherwise pick root. TreeItem *parent_item = (search_flags & SEARCH_SHOW_HIERARCHY) ? class_items[match.doc->name] : root_item; - bool constructor_created = false; - for (int i = 0; i < match.methods.size(); i++) { - String text = match.methods[i].doc->name; - if (!constructor_created) { - if (match.doc->name == match.methods[i].doc->name) { - text += " " + TTR("(constructors)"); - constructor_created = true; - } - } else { - if (match.doc->name == match.methods[i].doc->name) { - continue; - } - } - _create_method_item(parent_item, match.doc, text, match.methods[i]); + + for (const MemberMatch<DocData::MethodDoc> &constructor_item : match.constructors) { + _create_constructor_item(parent_item, match.doc, constructor_item); } - for (int i = 0; i < match.signals.size(); i++) { - _create_signal_item(parent_item, match.doc, match.signals[i]); + for (const MemberMatch<DocData::MethodDoc> &method_item : match.methods) { + _create_method_item(parent_item, match.doc, method_item); } - for (int i = 0; i < match.constants.size(); i++) { - _create_constant_item(parent_item, match.doc, match.constants[i]); + for (const MemberMatch<DocData::MethodDoc> &operator_item : match.operators) { + _create_operator_item(parent_item, match.doc, operator_item); } - for (int i = 0; i < match.properties.size(); i++) { - _create_property_item(parent_item, match.doc, match.properties[i]); + for (const MemberMatch<DocData::MethodDoc> &signal_item : match.signals) { + _create_signal_item(parent_item, match.doc, signal_item); } - for (int i = 0; i < match.theme_properties.size(); i++) { - _create_theme_property_item(parent_item, match.doc, match.theme_properties[i]); + for (const MemberMatch<DocData::ConstantDoc> &constant_item : match.constants) { + _create_constant_item(parent_item, match.doc, constant_item); } - for (int i = 0; i < match.annotations.size(); i++) { - _create_annotation_item(parent_item, match.doc, match.annotations[i]); + for (const MemberMatch<DocData::PropertyDoc> &property_item : match.properties) { + _create_property_item(parent_item, match.doc, property_item); + } + for (const MemberMatch<DocData::ThemeItemDoc> &theme_property_item : match.theme_properties) { + _create_theme_property_item(parent_item, match.doc, theme_property_item); + } + for (const MemberMatch<DocData::MethodDoc> &annotation_item : match.annotations) { + _create_annotation_item(parent_item, match.doc, annotation_item); } ++iterator_match; @@ -704,7 +951,7 @@ bool EditorHelpSearch::Runner::_phase_select_match() { return true; } -void EditorHelpSearch::Runner::_match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, Vector<MemberMatch<DocData::MethodDoc>> *r_match_methods) { +void EditorHelpSearch::Runner::_match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, LocalVector<MemberMatch<DocData::MethodDoc>> *r_match_methods) { // Constructors, Methods, Operators... for (int i = 0; i < p_methods.size(); i++) { String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? p_methods[i].name : p_methods[i].name.to_lower(); @@ -765,12 +1012,12 @@ void EditorHelpSearch::Runner::_match_item(TreeItem *p_item, const String &p_tex return; } - float inverse_length = 1.f / float(p_text.length()); + float inverse_length = 1.0f / float(p_text.length()); // Favor types where search term is a substring close to the start of the type. float w = 0.5f; int pos = p_text.findn(term); - float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.f, .9f - w); + float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.0f, 0.9f - w); // Favor shorter items: they resemble the search term more. w = 0.1f; @@ -781,7 +1028,8 @@ void EditorHelpSearch::Runner::_match_item(TreeItem *p_item, const String &p_tex score *= 0.9f; } - if (match_highest_score == 0 || score > match_highest_score) { + // Replace current match if term is short as we are searching in reverse. + if (match_highest_score == 0 || score > match_highest_score || (score == match_highest_score && term.length() == 1)) { matched_item = p_item; match_highest_score = score; } @@ -820,6 +1068,29 @@ String EditorHelpSearch::Runner::_build_keywords_tooltip(const String &p_keyword return tooltip.left(-2); } +TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const DocData::ClassDoc *p_class_doc, const String &p_matching_keyword, bool p_gray) { + if (p_class_doc->name.is_empty()) { + return nullptr; + } + if (TreeItem **found = class_items.getptr(p_class_doc->name)) { + return *found; + } + + // Ensure parent nodes are created first. + TreeItem *parent_item = root_item; + if (!p_class_doc->inherits.is_empty()) { + if (class_items.has(p_class_doc->inherits)) { + parent_item = class_items[p_class_doc->inherits]; + } else if (const DocData::ClassDoc *found = EditorHelp::get_doc_data()->class_list.getptr(p_class_doc->inherits)) { + parent_item = _create_class_hierarchy(found, String(), true); + } + } + + TreeItem *class_item = _create_class_item(parent_item, p_class_doc, p_gray, p_matching_keyword); + class_items[p_class_doc->name] = class_item; + return class_item; +} + TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_match) { if (p_match.doc->name.is_empty()) { return nullptr; @@ -887,6 +1158,8 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental.")); } } + // Cached item might be collapsed. + item->set_collapsed(false); if (p_gray) { item->set_custom_color(0, disabled_color); @@ -902,7 +1175,9 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const item->set_text(0, p_doc->name + " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword))); } - _match_item(item, p_doc->name); + if (!term.is_empty()) { + _match_item(item, p_doc->name); + } for (const String &keyword : p_doc->keywords.split(",")) { _match_item(item, keyword.strip_edges(), true); } @@ -910,44 +1185,73 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const return item; } -TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const MemberMatch<DocData::MethodDoc> &p_match) { +TreeItem *EditorHelpSearch::Runner::_create_constructor_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) { + String tooltip = p_class_doc->name + "("; + String text = p_class_doc->name + "("; + for (int i = 0; i < p_match.doc->arguments.size(); i++) { + const DocData::ArgumentDoc &arg = p_match.doc->arguments[i]; + tooltip += arg.type + " " + arg.name; + text += arg.type; + if (!arg.default_value.is_empty()) { + tooltip += " = " + arg.default_value; + } + if (i < p_match.doc->arguments.size() - 1) { + tooltip += ", "; + text += ", "; + } + } + tooltip += ")"; + tooltip += _build_keywords_tooltip(p_match.doc->keywords); + text += ")"; + return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberConstructor"), p_match.doc->name, text, TTRC("Constructor"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); +} + +TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) { String tooltip = _build_method_tooltip(p_class_doc, p_match.doc); - return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_match.doc->name, p_text, TTRC("Method"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); + return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberMethod"), p_match.doc->name, p_match.doc->name, TTRC("Method"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); +} + +TreeItem *EditorHelpSearch::Runner::_create_operator_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) { + String tooltip = _build_method_tooltip(p_class_doc, p_match.doc); + String text = p_match.doc->name; + if (!p_match.doc->arguments.is_empty()) { + text += "(" + p_match.doc->arguments[0].type + ")"; + } + return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberOperator"), p_match.doc->name, text, TTRC("Operator"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); } TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) { String tooltip = _build_method_tooltip(p_class_doc, p_match.doc); - return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_match.doc->name, p_match.doc->name, TTRC("Signal"), "signal", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); + return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberSignal"), p_match.doc->name, p_match.doc->name, TTRC("Signal"), "signal", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); } TreeItem *EditorHelpSearch::Runner::_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) { String tooltip = _build_method_tooltip(p_class_doc, p_match.doc); // Hide the redundant leading @ symbol. String text = p_match.doc->name.substr(1); - return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_match.doc->name, text, TTRC("Annotation"), "annotation", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); + return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberAnnotation"), p_match.doc->name, text, TTRC("Annotation"), "annotation", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); } TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ConstantDoc> &p_match) { String tooltip = p_class_doc->name + "." + p_match.doc->name; tooltip += _build_keywords_tooltip(p_match.doc->keywords); - return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_match.doc->name, p_match.doc->name, TTRC("Constant"), "constant", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); + return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberConstant"), p_match.doc->name, p_match.doc->name, TTRC("Constant"), "constant", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); } TreeItem *EditorHelpSearch::Runner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::PropertyDoc> &p_match) { String tooltip = p_match.doc->type + " " + p_class_doc->name + "." + p_match.doc->name; tooltip += "\n " + p_class_doc->name + "." + p_match.doc->setter + "(value) setter"; tooltip += "\n " + p_class_doc->name + "." + p_match.doc->getter + "() getter"; - tooltip += _build_keywords_tooltip(p_match.doc->keywords); - return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_match.doc->name, p_match.doc->name, TTRC("Property"), "property", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); + return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberProperty"), p_match.doc->name, p_match.doc->name, TTRC("Property"), "property", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword); } TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ThemeItemDoc> &p_match) { String tooltip = p_match.doc->type + " " + p_class_doc->name + "." + p_match.doc->name; tooltip += _build_keywords_tooltip(p_match.doc->keywords); - return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_match.doc->name, p_match.doc->name, TTRC("Theme Property"), "theme_item", p_match.doc->keywords, tooltip, false, false, p_match.name ? String() : p_match.keyword); + return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberTheme"), p_match.doc->name, p_match.doc->name, TTRC("Theme Property"), "theme_item", p_match.doc->keywords, tooltip, false, false, p_match.name ? String() : p_match.keyword); } -TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword) { +TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const StringName &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword) { const String item_meta = "class_" + p_metatype + ":" + p_class_name + ":" + p_name; TreeItem *item = nullptr; @@ -978,7 +1282,10 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons } item->set_text(0, text); - _match_item(item, p_name); + // Don't match member items for short searches. + if (term.length() > 1 || term == "@") { + _match_item(item, p_name); + } for (const String &keyword : p_keywords.split(",")) { _match_item(item, keyword.strip_edges(), true); } @@ -989,9 +1296,17 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons bool EditorHelpSearch::Runner::work(uint64_t slot) { // Return true when the search has been completed, otherwise false. const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot; - while (!_slice()) { - if (OS::get_singleton()->get_ticks_usec() > until) { - return false; + if (term.length() > 1 || term == "@") { + while (!_slice()) { + if (OS::get_singleton()->get_ticks_usec() > until) { + return false; + } + } + } else { + while (!_fill()) { + if (OS::get_singleton()->get_ticks_usec() > until) { + return false; + } } } return true; @@ -1001,7 +1316,7 @@ EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, ui_service(p_icon_service), results_tree(p_results_tree), tree_cache(p_tree_cache), - term((p_search_flags & SEARCH_CASE_SENSITIVE) == 0 ? p_term.strip_edges().to_lower() : p_term.strip_edges()), + term((p_search_flags & SEARCH_CASE_SENSITIVE) == 0 ? p_term.to_lower() : p_term), search_flags(p_search_flags), disabled_color(ui_service->get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor))) { } diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h index 58061dae4c..b8b3c26b41 100644 --- a/editor/editor_help_search.h +++ b/editor/editor_help_search.h @@ -63,6 +63,7 @@ class EditorHelpSearch : public ConfirmationDialog { Tree *results_tree = nullptr; bool old_search = false; String old_term; + int old_search_flags = 0; class Runner; Ref<Runner> search; @@ -119,26 +120,30 @@ class EditorHelpSearch::Runner : public RefCounted { template <typename T> struct MemberMatch { - T *doc = nullptr; + const T *doc = nullptr; bool name = false; String keyword; + + MemberMatch() {} + MemberMatch(const T *p_doc) : + doc(p_doc) {} }; struct ClassMatch { - DocData::ClassDoc *doc = nullptr; + const DocData::ClassDoc *doc = nullptr; bool name = false; String keyword; - Vector<MemberMatch<DocData::MethodDoc>> constructors; - Vector<MemberMatch<DocData::MethodDoc>> methods; - Vector<MemberMatch<DocData::MethodDoc>> operators; - Vector<MemberMatch<DocData::MethodDoc>> signals; - Vector<MemberMatch<DocData::ConstantDoc>> constants; - Vector<MemberMatch<DocData::PropertyDoc>> properties; - Vector<MemberMatch<DocData::ThemeItemDoc>> theme_properties; - Vector<MemberMatch<DocData::MethodDoc>> annotations; + LocalVector<MemberMatch<DocData::MethodDoc>> constructors; + LocalVector<MemberMatch<DocData::MethodDoc>> methods; + LocalVector<MemberMatch<DocData::MethodDoc>> operators; + LocalVector<MemberMatch<DocData::MethodDoc>> signals; + LocalVector<MemberMatch<DocData::ConstantDoc>> constants; + LocalVector<MemberMatch<DocData::PropertyDoc>> properties; + LocalVector<MemberMatch<DocData::ThemeItemDoc>> theme_properties; + LocalVector<MemberMatch<DocData::MethodDoc>> annotations; bool required() { - return name || !keyword.is_empty() || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size() || annotations.size(); + return name || !keyword.is_empty() || !constructors.is_empty() || !methods.is_empty() || !operators.is_empty() || !signals.is_empty() || !constants.is_empty() || !properties.is_empty() || !theme_properties.is_empty() || !annotations.is_empty(); } }; @@ -155,6 +160,7 @@ class EditorHelpSearch::Runner : public RefCounted { LocalVector<RBSet<String, NaturalNoCaseComparator>::Element *> iterator_stack; HashMap<String, ClassMatch> matches; HashMap<String, ClassMatch>::Iterator iterator_match; + LocalVector<Pair<DocData::ClassDoc *, String>> matched_classes; TreeItem *root_item = nullptr; HashMap<String, TreeItem *> class_items; TreeItem *matched_item = nullptr; @@ -165,6 +171,12 @@ class EditorHelpSearch::Runner : public RefCounted { void _populate_cache(); bool _find_or_create_item(TreeItem *p_parent, const String &p_item_meta, TreeItem *&r_item); + bool _fill(); + bool _phase_fill_classes_init(); + bool _phase_fill_classes(); + bool _phase_fill_member_items_init(); + bool _phase_fill_member_items(); + bool _slice(); bool _phase_match_classes_init(); bool _phase_match_classes(); @@ -177,21 +189,25 @@ class EditorHelpSearch::Runner : public RefCounted { String _build_method_tooltip(const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) const; String _build_keywords_tooltip(const String &p_keywords) const; - void _match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, Vector<MemberMatch<DocData::MethodDoc>> *r_match_methods); + void _match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, LocalVector<MemberMatch<DocData::MethodDoc>> *r_match_methods); bool _all_terms_in_name(const String &p_name) const; String _match_keywords_in_all_terms(const String &p_keywords) const; bool _match_string(const String &p_term, const String &p_string) const; String _match_keywords(const String &p_term, const String &p_keywords) const; void _match_item(TreeItem *p_item, const String &p_text, bool p_is_keywords = false); 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_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const MemberMatch<DocData::MethodDoc> &p_match); + 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_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); TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match); TreeItem *_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match); TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ConstantDoc> &p_match); TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::PropertyDoc> &p_match); TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ThemeItemDoc> &p_match); - TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword); + TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const StringName &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword); public: bool work(uint64_t slot = 100000); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 735d382185..21f3f31685 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -481,6 +481,9 @@ void EditorNode::_gdextensions_reloaded() { // In case the developer is inspecting an object that will be changed by the reload. InspectorDock::get_inspector_singleton()->update_tree(); + // Reload script editor to revalidate GDScript if classes are added or removed. + ScriptEditor::get_singleton()->reload_scripts(true); + // Regenerate documentation. EditorHelp::generate_doc(); } @@ -4949,7 +4952,9 @@ bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringNa } void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) { - if (singleton->cmdline_export_mode) { + if (!singleton) { + return; + } else if (singleton->cmdline_export_mode) { print_line(p_task + ": begin: " + p_label + " steps: " + itos(p_steps)); } else if (singleton->progress_dialog) { singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel); @@ -4957,7 +4962,9 @@ void EditorNode::progress_add_task(const String &p_task, const String &p_label, } bool EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) { - if (singleton->cmdline_export_mode) { + if (!singleton) { + return false; + } else if (singleton->cmdline_export_mode) { print_line("\t" + p_task + ": step " + itos(p_step) + ": " + p_state); return false; } else if (singleton->progress_dialog) { @@ -4968,7 +4975,9 @@ bool EditorNode::progress_task_step(const String &p_task, const String &p_state, } void EditorNode::progress_end_task(const String &p_task) { - if (singleton->cmdline_export_mode) { + if (!singleton) { + return; + } else if (singleton->cmdline_export_mode) { print_line(p_task + ": end"); } else if (singleton->progress_dialog) { singleton->progress_dialog->end_task(p_task); diff --git a/editor/editor_properties_vector.cpp b/editor/editor_properties_vector.cpp index a40055cf85..9ff8bf674d 100644 --- a/editor/editor_properties_vector.cpp +++ b/editor/editor_properties_vector.cpp @@ -130,9 +130,11 @@ void EditorPropertyVectorN::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { if (linked->is_visible()) { - const String key = vformat("%s:%s", get_edited_object()->get_class(), get_edited_property()); - linked->set_pressed_no_signal(EditorSettings::get_singleton()->get_project_metadata("linked_properties", key, true)); - _update_ratio(); + if (get_edited_object()) { + const String key = vformat("%s:%s", get_edited_object()->get_class(), get_edited_property()); + linked->set_pressed_no_signal(EditorSettings::get_singleton()->get_project_metadata("linked_properties", key, true)); + _update_ratio(); + } } } break; diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index 5d378820ae..e0e1ef6d19 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -141,7 +141,7 @@ Error EditorRunNative::start_run_native(int p_id) { emit_signal(SNAME("native_run"), preset); - int flags = 0; + BitField<EditorExportPlatform::DebugFlags> flags = 0; bool deploy_debug_remote = is_deploy_debug_remote_enabled(); bool deploy_dumb = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false); @@ -149,16 +149,16 @@ Error EditorRunNative::start_run_native(int p_id) { bool debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); if (deploy_debug_remote) { - flags |= EditorExportPlatform::DEBUG_FLAG_REMOTE_DEBUG; + flags.set_flag(EditorExportPlatform::DEBUG_FLAG_REMOTE_DEBUG); } if (deploy_dumb) { - flags |= EditorExportPlatform::DEBUG_FLAG_DUMB_CLIENT; + flags.set_flag(EditorExportPlatform::DEBUG_FLAG_DUMB_CLIENT); } if (debug_collisions) { - flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_COLLISIONS; + flags.set_flag(EditorExportPlatform::DEBUG_FLAG_VIEW_COLLISIONS); } if (debug_navigation) { - flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION; + flags.set_flag(EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION); } eep->clear_messages(); diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 72ab186036..975a601ae1 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -124,7 +124,17 @@ void EditorExport::_bind_methods() { void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) { export_platforms.push_back(p_platform); + should_update_presets = true; + should_reload_presets = true; +} + +void EditorExport::remove_export_platform(const Ref<EditorExportPlatform> &p_platform) { + export_platforms.erase(p_platform); + p_platform->cleanup(); + + should_update_presets = true; + should_reload_presets = true; } int EditorExport::get_export_platform_count() { @@ -244,7 +254,7 @@ void EditorExport::load_config() { if (!preset.is_valid()) { index++; - ERR_CONTINUE(!preset.is_valid()); + continue; // Unknown platform, skip without error (platform might be loaded later). } preset->set_name(config->get_value(section, "name")); @@ -343,6 +353,12 @@ void EditorExport::load_config() { void EditorExport::update_export_presets() { HashMap<StringName, List<EditorExportPlatform::ExportOption>> platform_options; + if (should_reload_presets) { + should_reload_presets = false; + export_presets.clear(); + load_config(); + } + for (int i = 0; i < export_platforms.size(); i++) { Ref<EditorExportPlatform> platform = export_platforms[i]; diff --git a/editor/export/editor_export.h b/editor/export/editor_export.h index f8cb90dc39..ebb2038f53 100644 --- a/editor/export/editor_export.h +++ b/editor/export/editor_export.h @@ -47,6 +47,7 @@ class EditorExport : public Node { Timer *save_timer = nullptr; bool block_save = false; bool should_update_presets = false; + bool should_reload_presets = false; static EditorExport *singleton; @@ -66,6 +67,7 @@ public: void add_export_platform(const Ref<EditorExportPlatform> &p_platform); int get_export_platform_count(); Ref<EditorExportPlatform> get_export_platform(int p_idx); + void remove_export_platform(const Ref<EditorExportPlatform> &p_platform); void add_export_preset(const Ref<EditorExportPreset> &p_preset, int p_at_pos = -1); int get_export_preset_count() const; diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 8b31eda3bc..7ad589a58d 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -71,7 +71,7 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) p_log->add_text(" "); p_log->add_text(get_name()); p_log->add_text(" - "); - if (p_err == OK) { + if (p_err == OK && get_worst_message_type() < EditorExportPlatform::EXPORT_MESSAGE_ERROR) { if (get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_WARNING) { p_log->add_image(p_log->get_editor_theme_icon(SNAME("StatusWarning")), 16 * EDSCALE, 16 * EDSCALE, Color(1.0, 1.0, 1.0), INLINE_ALIGNMENT_CENTER); p_log->add_text(" "); @@ -167,58 +167,6 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) return has_messages; } -void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags) { - String host = EDITOR_GET("network/debug/remote_host"); - int remote_port = (int)EDITOR_GET("network/debug/remote_port"); - - if (EditorSettings::get_singleton()->has_setting("export/android/use_wifi_for_remote_debug") && EDITOR_GET("export/android/use_wifi_for_remote_debug")) { - host = EDITOR_GET("export/android/wifi_remote_debug_host"); - } else if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) { - host = "localhost"; - } - - if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { - int port = EDITOR_GET("filesystem/file_server/port"); - String passwd = EDITOR_GET("filesystem/file_server/password"); - r_flags.push_back("--remote-fs"); - r_flags.push_back(host + ":" + itos(port)); - if (!passwd.is_empty()) { - r_flags.push_back("--remote-fs-password"); - r_flags.push_back(passwd); - } - } - - if (p_flags & DEBUG_FLAG_REMOTE_DEBUG) { - r_flags.push_back("--remote-debug"); - - r_flags.push_back(get_debug_protocol() + host + ":" + String::num(remote_port)); - - List<String> breakpoints; - ScriptEditor::get_singleton()->get_breakpoints(&breakpoints); - - if (breakpoints.size()) { - r_flags.push_back("--breakpoints"); - String bpoints; - for (const List<String>::Element *E = breakpoints.front(); E; E = E->next()) { - bpoints += E->get().replace(" ", "%20"); - if (E->next()) { - bpoints += ","; - } - } - - r_flags.push_back(bpoints); - } - } - - if (p_flags & DEBUG_FLAG_VIEW_COLLISIONS) { - r_flags.push_back("--debug-collisions"); - } - - if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) { - r_flags.push_back("--debug-navigation"); - } -} - Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export."); @@ -530,7 +478,7 @@ HashSet<String> EditorExportPlatform::get_features(const Ref<EditorExportPreset> return result; } -EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { HashSet<String> features = p_platform.get_features(p_preset, p_debug); Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); //initial export plugin callback @@ -919,6 +867,55 @@ Vector<String> EditorExportPlatform::get_forced_export_files() { return files; } +Error EditorExportPlatform::_script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + Callable cb = ((ScriptCallbackData *)p_userdata)->file_cb; + ERR_FAIL_COND_V(!cb.is_valid(), FAILED); + + Variant path = p_path; + Variant data = p_data; + Variant file = p_file; + Variant total = p_total; + Variant enc_in = p_enc_in_filters; + Variant enc_ex = p_enc_ex_filters; + Variant enc_key = p_key; + + Variant ret; + Callable::CallError ce; + const Variant *args[7] = { &path, &data, &file, &total, &enc_in, &enc_ex, &enc_key }; + + cb.callp(args, 7, ret, ce); + ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, vformat("Failed to execute file save callback: %s.", Variant::get_callable_error_text(cb, args, 7, ce))); + + return (Error)ret.operator int(); +} + +Error EditorExportPlatform::_script_add_shared_object(void *p_userdata, const SharedObject &p_so) { + Callable cb = ((ScriptCallbackData *)p_userdata)->so_cb; + if (!cb.is_valid()) { + return OK; // Optional. + } + + Variant path = p_so.path; + Variant tags = p_so.tags; + Variant target = p_so.target; + + Variant ret; + Callable::CallError ce; + const Variant *args[3] = { &path, &tags, &target }; + + cb.callp(args, 3, ret, ce); + ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, vformat("Failed to execute shared object save callback: %s.", Variant::get_callable_error_text(cb, args, 3, ce))); + + return (Error)ret.operator int(); +} + +Error EditorExportPlatform::_export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func) { + ScriptCallbackData data; + data.file_cb = p_save_func; + data.so_cb = p_so_func; + return export_project_files(p_preset, p_debug, _script_save_file, &data, _script_add_shared_object); +} + Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) { //figure out paths of files that will be exported HashSet<String> paths; @@ -1425,7 +1422,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & return p_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key); } -Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObject &p_so) { +Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const SharedObject &p_so) { PackData *pack_data = (PackData *)p_userdata; if (pack_data->so_files) { pack_data->so_files->push_back(p_so); @@ -1434,6 +1431,15 @@ Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObj return OK; } +Error EditorExportPlatform::_zip_add_shared_object(void *p_userdata, const SharedObject &p_so) { + ZipData *zip_data = (ZipData *)p_userdata; + if (zip_data->so_files) { + zip_data->so_files->push_back(p_so); + } + + return OK; +} + void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) { String dir = p_folder.is_empty() ? p_root_path : p_root_path.path_join(p_folder); @@ -1551,6 +1557,54 @@ void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_ da->list_dir_end(); } +Dictionary EditorExportPlatform::_save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed) { + Vector<SharedObject> so_files; + int64_t embedded_start = 0; + int64_t embedded_size = 0; + Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, p_embed, &embedded_start, &embedded_size); + + Dictionary ret; + ret["result"] = err_code; + if (err_code == OK) { + Array arr; + for (const SharedObject &E : so_files) { + Dictionary so; + so["path"] = E.path; + so["tags"] = E.tags; + so["target_folder"] = E.target; + arr.push_back(so); + } + ret["so_files"] = arr; + if (p_embed) { + ret["embedded_start"] = embedded_start; + ret["embedded_size"] = embedded_size; + } + } + + return ret; +} + +Dictionary EditorExportPlatform::_save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { + Vector<SharedObject> so_files; + Error err_code = save_zip(p_preset, p_debug, p_path, &so_files); + + Dictionary ret; + ret["result"] = err_code; + if (err_code == OK) { + Array arr; + for (const SharedObject &E : so_files) { + Dictionary so; + so["path"] = E.path; + so["tags"] = E.tags; + so["target_folder"] = E.target; + arr.push_back(so); + } + ret["so_files"] = arr; + } + + return ret; +} + Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) { EditorProgress ep("savepack", TTR("Packing"), 102, true); @@ -1570,7 +1624,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b pd.f = ftmp; pd.so_files = p_so_files; - Error err = export_project_files(p_preset, p_debug, _save_pack_file, &pd, _add_shared_object); + Error err = export_project_files(p_preset, p_debug, _save_pack_file, &pd, _pack_add_shared_object); // Close temp file. pd.f.unref(); @@ -1777,7 +1831,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b return OK; } -Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { +Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files) { EditorProgress ep("savezip", TTR("Packing"), 102, true); Ref<FileAccess> io_fa; @@ -1787,8 +1841,9 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bo ZipData zd; zd.ep = &ep; zd.zip = zip; + zd.so_files = p_so_files; - Error err = export_project_files(p_preset, p_debug, _save_zip_file, &zd); + Error err = export_project_files(p_preset, p_debug, _save_zip_file, &zd, _zip_add_shared_object); if (err != OK && err != ERR_SKIP) { add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files.")); } @@ -1798,45 +1853,48 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bo return OK; } -Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); return save_pack(p_preset, p_debug, p_path); } -Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); return save_zip(p_preset, p_debug, p_path); } -void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags) { +Vector<String> EditorExportPlatform::gen_export_flags(BitField<EditorExportPlatform::DebugFlags> p_flags) { + Vector<String> ret; String host = EDITOR_GET("network/debug/remote_host"); int remote_port = (int)EDITOR_GET("network/debug/remote_port"); - if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) { + if (get_name() == "Android" && EditorSettings::get_singleton()->has_setting("export/android/use_wifi_for_remote_debug") && EDITOR_GET("export/android/use_wifi_for_remote_debug")) { + host = EDITOR_GET("export/android/wifi_remote_debug_host"); + } else if (p_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST)) { host = "localhost"; } - if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { + if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) { int port = EDITOR_GET("filesystem/file_server/port"); String passwd = EDITOR_GET("filesystem/file_server/password"); - r_flags.push_back("--remote-fs"); - r_flags.push_back(host + ":" + itos(port)); + ret.push_back("--remote-fs"); + ret.push_back(host + ":" + itos(port)); if (!passwd.is_empty()) { - r_flags.push_back("--remote-fs-password"); - r_flags.push_back(passwd); + ret.push_back("--remote-fs-password"); + ret.push_back(passwd); } } - if (p_flags & DEBUG_FLAG_REMOTE_DEBUG) { - r_flags.push_back("--remote-debug"); + if (p_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG)) { + ret.push_back("--remote-debug"); - r_flags.push_back(get_debug_protocol() + host + ":" + String::num(remote_port)); + ret.push_back(get_debug_protocol() + host + ":" + String::num(remote_port)); List<String> breakpoints; ScriptEditor::get_singleton()->get_breakpoints(&breakpoints); if (breakpoints.size()) { - r_flags.push_back("--breakpoints"); + ret.push_back("--breakpoints"); String bpoints; for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) { bpoints += E->get().replace(" ", "%20"); @@ -1845,17 +1903,18 @@ void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags } } - r_flags.push_back(bpoints); + ret.push_back(bpoints); } } - if (p_flags & DEBUG_FLAG_VIEW_COLLISIONS) { - r_flags.push_back("--debug-collisions"); + if (p_flags.has_flag(DEBUG_FLAG_VIEW_COLLISIONS)) { + ret.push_back("--debug-collisions"); } - if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) { - r_flags.push_back("--debug-navigation"); + if (p_flags.has_flag(DEBUG_FLAG_VIEW_NAVIGATION)) { + ret.push_back("--debug-navigation"); } + return ret; } bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { @@ -2035,8 +2094,61 @@ Error EditorExportPlatform::ssh_push_to_remote(const String &p_host, const Strin return OK; } +Array EditorExportPlatform::get_current_presets() const { + Array ret; + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i); + if (ep->get_platform() == this) { + ret.push_back(ep); + } + } + return ret; +} + void EditorExportPlatform::_bind_methods() { ClassDB::bind_method(D_METHOD("get_os_name"), &EditorExportPlatform::get_os_name); + + ClassDB::bind_method(D_METHOD("create_preset"), &EditorExportPlatform::create_preset); + + ClassDB::bind_method(D_METHOD("find_export_template", "template_file_name"), &EditorExportPlatform::_find_export_template); + ClassDB::bind_method(D_METHOD("get_current_presets"), &EditorExportPlatform::get_current_presets); + + ClassDB::bind_method(D_METHOD("save_pack", "preset", "debug", "path", "embed"), &EditorExportPlatform::_save_pack, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("save_zip", "preset", "debug", "path"), &EditorExportPlatform::_save_zip); + + ClassDB::bind_method(D_METHOD("gen_export_flags", "flags"), &EditorExportPlatform::gen_export_flags); + + ClassDB::bind_method(D_METHOD("export_project_files", "preset", "debug", "save_cb", "shared_cb"), &EditorExportPlatform::_export_project_files, DEFVAL(Callable())); + + ClassDB::bind_method(D_METHOD("export_project", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_project, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("export_pack", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_pack, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("export_zip", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_zip, DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("clear_messages"), &EditorExportPlatform::clear_messages); + ClassDB::bind_method(D_METHOD("add_message", "type", "category", "message"), &EditorExportPlatform::add_message); + ClassDB::bind_method(D_METHOD("get_message_count"), &EditorExportPlatform::get_message_count); + + ClassDB::bind_method(D_METHOD("get_message_type", "index"), &EditorExportPlatform::_get_message_type); + ClassDB::bind_method(D_METHOD("get_message_category", "index"), &EditorExportPlatform::_get_message_category); + ClassDB::bind_method(D_METHOD("get_message_text", "index"), &EditorExportPlatform::_get_message_text); + ClassDB::bind_method(D_METHOD("get_worst_message_type"), &EditorExportPlatform::get_worst_message_type); + + ClassDB::bind_method(D_METHOD("ssh_run_on_remote", "host", "port", "ssh_arg", "cmd_args", "output", "port_fwd"), &EditorExportPlatform::_ssh_run_on_remote, DEFVAL(Array()), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("ssh_run_on_remote_no_wait", "host", "port", "ssh_args", "cmd_args", "port_fwd"), &EditorExportPlatform::_ssh_run_on_remote_no_wait, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("ssh_push_to_remote", "host", "port", "scp_args", "src_file", "dst_file"), &EditorExportPlatform::ssh_push_to_remote); + + ClassDB::bind_static_method("EditorExportPlatform", D_METHOD("get_forced_export_files"), &EditorExportPlatform::get_forced_export_files); + + BIND_ENUM_CONSTANT(EXPORT_MESSAGE_NONE); + BIND_ENUM_CONSTANT(EXPORT_MESSAGE_INFO); + BIND_ENUM_CONSTANT(EXPORT_MESSAGE_WARNING); + BIND_ENUM_CONSTANT(EXPORT_MESSAGE_ERROR); + + BIND_BITFIELD_FLAG(DEBUG_FLAG_DUMB_CLIENT); + BIND_BITFIELD_FLAG(DEBUG_FLAG_REMOTE_DEBUG); + BIND_BITFIELD_FLAG(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST); + BIND_BITFIELD_FLAG(DEBUG_FLAG_VIEW_COLLISIONS); + BIND_BITFIELD_FLAG(DEBUG_FLAG_VIEW_NAVIGATION); } EditorExportPlatform::EditorExportPlatform() { diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 3fd75ff67f..a800bb95e6 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -56,6 +56,14 @@ public: typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so); + enum DebugFlags { + DEBUG_FLAG_DUMB_CLIENT = 1, + DEBUG_FLAG_REMOTE_DEBUG = 2, + DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4, + DEBUG_FLAG_VIEW_COLLISIONS = 8, + DEBUG_FLAG_VIEW_NAVIGATION = 16, + }; + enum ExportMessageType { EXPORT_MESSAGE_NONE, EXPORT_MESSAGE_INFO, @@ -92,6 +100,7 @@ private: struct ZipData { void *zip = nullptr; EditorProgress *ep = nullptr; + Vector<SharedObject> *so_files = nullptr; }; Vector<ExportMessage> messages; @@ -101,13 +110,22 @@ private: void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths); static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so); + static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so); + + struct ScriptCallbackData { + Callable file_cb; + Callable so_cb; + }; + + static Error _script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + static Error _script_add_shared_object(void *p_userdata, const SharedObject &p_so); void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude); void _edit_filter_list(HashSet<String> &r_list, const String &p_filter, bool exclude); - static Error _add_shared_object(void *p_userdata, const SharedObject &p_so); - struct FileExportCache { uint64_t source_modified_time = 0; String source_md5; @@ -126,19 +144,46 @@ private: protected: struct ExportNotifier { - ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags); + ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags); ~ExportNotifier(); }; HashSet<String> get_features(const Ref<EditorExportPreset> &p_preset, bool p_debug) const; - bool exists_export_template(const String &template_file_name, String *err) const; - String find_export_template(const String &template_file_name, String *err = nullptr) const; - void gen_export_flags(Vector<String> &r_flags, int p_flags); - void gen_debug_flags(Vector<String> &r_flags, int p_flags); + Dictionary _find_export_template(const String &p_template_file_name) const { + Dictionary ret; + String err; + + String path = find_export_template(p_template_file_name, &err); + ret["result"] = (err.is_empty() && !path.is_empty()) ? OK : FAILED; + ret["path"] = path; + ret["error_string"] = err; + + return ret; + } + + bool exists_export_template(const String &p_template_file_name, String *r_err) const; + String find_export_template(const String &p_template_file_name, String *r_err = nullptr) const; + Vector<String> gen_export_flags(BitField<EditorExportPlatform::DebugFlags> p_flags); virtual void zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name); + Error _ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, Array r_output = Array(), int p_port_fwd = -1) const { + String pipe; + Error err = ssh_run_on_remote(p_host, p_port, p_ssh_args, p_cmd_args, &pipe, p_port_fwd); + r_output.push_back(pipe); + return err; + } + OS::ProcessID _ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, int p_port_fwd = -1) const { + OS::ProcessID pid = 0; + Error err = ssh_run_on_remote_no_wait(p_host, p_port, p_ssh_args, p_cmd_args, &pid, p_port_fwd); + if (err != OK) { + return -1; + } else { + return pid; + } + } + Error ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, String *r_out = nullptr, int p_port_fwd = -1) const; Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid = nullptr, int p_port_fwd = -1) const; Error ssh_push_to_remote(const String &p_host, const String &p_port, const Vector<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const; @@ -195,6 +240,21 @@ public: return messages[p_index]; } + virtual ExportMessageType _get_message_type(int p_index) const { + ERR_FAIL_INDEX_V(p_index, messages.size(), EXPORT_MESSAGE_NONE); + return messages[p_index].msg_type; + } + + virtual String _get_message_category(int p_index) const { + ERR_FAIL_INDEX_V(p_index, messages.size(), String()); + return messages[p_index].category; + } + + virtual String _get_message_text(int p_index) const { + ERR_FAIL_INDEX_V(p_index, messages.size(), String()); + return messages[p_index].text; + } + virtual ExportMessageType get_worst_message_type() const { ExportMessageType worst_type = EXPORT_MESSAGE_NONE; for (int i = 0; i < messages.size(); i++) { @@ -216,10 +276,16 @@ public: virtual String get_name() const = 0; virtual Ref<Texture2D> get_logo() const = 0; + Array get_current_presets() const; + + Error _export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func); Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr); + Dictionary _save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed = false); + Dictionary _save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); + Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr); - Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); + Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr); virtual bool poll_export() { return false; } virtual int get_options_count() const { return 0; } @@ -229,31 +295,26 @@ public: virtual String get_option_tooltip(int p_device) const { return ""; } virtual String get_device_architecture(int p_device) const { return ""; } - enum DebugFlags { - DEBUG_FLAG_DUMB_CLIENT = 1, - DEBUG_FLAG_REMOTE_DEBUG = 2, - DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4, - DEBUG_FLAG_VIEW_COLLISIONS = 8, - DEBUG_FLAG_VIEW_NAVIGATION = 16, - }; - virtual void cleanup() {} - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; } + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { return OK; } virtual Ref<Texture2D> get_run_icon() const { return get_logo(); } - bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const; + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const; virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const = 0; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const = 0; - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) = 0; - virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); - virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) = 0; + virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0); + virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0); virtual void get_platform_features(List<String> *r_features) const = 0; - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) = 0; + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features){}; virtual String get_debug_protocol() const { return "tcp://"; } EditorExportPlatform(); }; +VARIANT_ENUM_CAST(EditorExportPlatform::ExportMessageType) +VARIANT_BITFIELD_CAST(EditorExportPlatform::DebugFlags); + #endif // EDITOR_EXPORT_PLATFORM_H diff --git a/editor/export/editor_export_platform_extension.cpp b/editor/export/editor_export_platform_extension.cpp new file mode 100644 index 0000000000..808a2076e2 --- /dev/null +++ b/editor/export/editor_export_platform_extension.cpp @@ -0,0 +1,317 @@ +/**************************************************************************/ +/* editor_export_platform_extension.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 "editor_export_platform_extension.h" + +void EditorExportPlatformExtension::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_config_error", "error_text"), &EditorExportPlatformExtension::set_config_error); + ClassDB::bind_method(D_METHOD("get_config_error"), &EditorExportPlatformExtension::get_config_error); + + ClassDB::bind_method(D_METHOD("set_config_missing_templates", "missing_templates"), &EditorExportPlatformExtension::set_config_missing_templates); + ClassDB::bind_method(D_METHOD("get_config_missing_templates"), &EditorExportPlatformExtension::get_config_missing_templates); + + GDVIRTUAL_BIND(_get_preset_features, "preset"); + GDVIRTUAL_BIND(_is_executable, "path"); + GDVIRTUAL_BIND(_get_export_options); + GDVIRTUAL_BIND(_should_update_export_options); + GDVIRTUAL_BIND(_get_export_option_visibility, "preset", "option"); + GDVIRTUAL_BIND(_get_export_option_warning, "preset", "option"); + + GDVIRTUAL_BIND(_get_os_name); + GDVIRTUAL_BIND(_get_name); + GDVIRTUAL_BIND(_get_logo); + + GDVIRTUAL_BIND(_poll_export); + GDVIRTUAL_BIND(_get_options_count); + GDVIRTUAL_BIND(_get_options_tooltip); + + GDVIRTUAL_BIND(_get_option_icon, "device"); + GDVIRTUAL_BIND(_get_option_label, "device"); + GDVIRTUAL_BIND(_get_option_tooltip, "device"); + GDVIRTUAL_BIND(_get_device_architecture, "device"); + + GDVIRTUAL_BIND(_cleanup); + + GDVIRTUAL_BIND(_run, "preset", "device", "debug_flags"); + GDVIRTUAL_BIND(_get_run_icon); + + GDVIRTUAL_BIND(_can_export, "preset", "debug"); + GDVIRTUAL_BIND(_has_valid_export_configuration, "preset", "debug"); + GDVIRTUAL_BIND(_has_valid_project_configuration, "preset"); + + GDVIRTUAL_BIND(_get_binary_extensions, "preset"); + + GDVIRTUAL_BIND(_export_project, "preset", "debug", "path", "flags"); + GDVIRTUAL_BIND(_export_pack, "preset", "debug", "path", "flags"); + GDVIRTUAL_BIND(_export_zip, "preset", "debug", "path", "flags"); + + GDVIRTUAL_BIND(_get_platform_features); + + GDVIRTUAL_BIND(_get_debug_protocol); +} + +void EditorExportPlatformExtension::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { + Vector<String> ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_preset_features, p_preset, ret) && r_features) { + for (const String &E : ret) { + r_features->push_back(E); + } + } +} + +bool EditorExportPlatformExtension::is_executable(const String &p_path) const { + bool ret = false; + GDVIRTUAL_CALL(_is_executable, p_path, ret); + return ret; +} + +void EditorExportPlatformExtension::get_export_options(List<ExportOption> *r_options) const { + TypedArray<Dictionary> ret; + if (GDVIRTUAL_CALL(_get_export_options, ret) && r_options) { + for (const Variant &var : ret) { + const Dictionary &d = var; + ERR_CONTINUE(!d.has("name")); + ERR_CONTINUE(!d.has("type")); + + PropertyInfo pinfo = PropertyInfo::from_dict(d); + ERR_CONTINUE(pinfo.name.is_empty() && (pinfo.usage & PROPERTY_USAGE_STORAGE)); + ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX); + + Variant default_value; + if (d.has("default_value")) { + default_value = d["default_value"]; + } + bool update_visibility = false; + if (d.has("update_visibility")) { + update_visibility = d["update_visibility"]; + } + bool required = false; + if (d.has("required")) { + required = d["required"]; + } + + r_options->push_back(ExportOption(pinfo, default_value, update_visibility, required)); + } + } +} + +bool EditorExportPlatformExtension::should_update_export_options() { + bool ret = false; + GDVIRTUAL_CALL(_should_update_export_options, ret); + return ret; +} + +bool EditorExportPlatformExtension::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const { + bool ret = true; + GDVIRTUAL_CALL(_get_export_option_visibility, Ref<EditorExportPreset>(p_preset), p_option, ret); + return ret; +} + +String EditorExportPlatformExtension::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const { + String ret; + GDVIRTUAL_CALL(_get_export_option_warning, Ref<EditorExportPreset>(p_preset), p_name, ret); + return ret; +} + +String EditorExportPlatformExtension::get_os_name() const { + String ret; + GDVIRTUAL_REQUIRED_CALL(_get_os_name, ret); + return ret; +} + +String EditorExportPlatformExtension::get_name() const { + String ret; + GDVIRTUAL_REQUIRED_CALL(_get_name, ret); + return ret; +} + +Ref<Texture2D> EditorExportPlatformExtension::get_logo() const { + Ref<Texture2D> ret; + GDVIRTUAL_REQUIRED_CALL(_get_logo, ret); + return ret; +} + +bool EditorExportPlatformExtension::poll_export() { + bool ret = false; + GDVIRTUAL_CALL(_poll_export, ret); + return ret; +} + +int EditorExportPlatformExtension::get_options_count() const { + int ret = 0; + GDVIRTUAL_CALL(_get_options_count, ret); + return ret; +} + +String EditorExportPlatformExtension::get_options_tooltip() const { + String ret; + GDVIRTUAL_CALL(_get_options_tooltip, ret); + return ret; +} + +Ref<ImageTexture> EditorExportPlatformExtension::get_option_icon(int p_index) const { + Ref<ImageTexture> ret; + if (GDVIRTUAL_CALL(_get_option_icon, p_index, ret)) { + return ret; + } + return EditorExportPlatform::get_option_icon(p_index); +} + +String EditorExportPlatformExtension::get_option_label(int p_device) const { + String ret; + GDVIRTUAL_CALL(_get_option_label, p_device, ret); + return ret; +} + +String EditorExportPlatformExtension::get_option_tooltip(int p_device) const { + String ret; + GDVIRTUAL_CALL(_get_option_tooltip, p_device, ret); + return ret; +} + +String EditorExportPlatformExtension::get_device_architecture(int p_device) const { + String ret; + GDVIRTUAL_CALL(_get_device_architecture, p_device, ret); + return ret; +} + +void EditorExportPlatformExtension::cleanup() { + GDVIRTUAL_CALL(_cleanup); +} + +Error EditorExportPlatformExtension::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { + Error ret = OK; + GDVIRTUAL_CALL(_run, p_preset, p_device, p_debug_flags, ret); + return ret; +} + +Ref<Texture2D> EditorExportPlatformExtension::get_run_icon() const { + Ref<Texture2D> ret; + if (GDVIRTUAL_CALL(_get_run_icon, ret)) { + return ret; + } + return EditorExportPlatform::get_run_icon(); +} + +bool EditorExportPlatformExtension::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { + bool ret = false; + config_error = r_error; + config_missing_templates = r_missing_templates; + if (GDVIRTUAL_CALL(_can_export, p_preset, p_debug, ret)) { + r_error = config_error; + r_missing_templates = config_missing_templates; + return ret; + } + return EditorExportPlatform::can_export(p_preset, r_error, r_missing_templates, p_debug); +} + +bool EditorExportPlatformExtension::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { + bool ret = false; + config_error = r_error; + config_missing_templates = r_missing_templates; + if (GDVIRTUAL_REQUIRED_CALL(_has_valid_export_configuration, p_preset, p_debug, ret)) { + r_error = config_error; + r_missing_templates = config_missing_templates; + } + return ret; +} + +bool EditorExportPlatformExtension::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { + bool ret = false; + config_error = r_error; + if (GDVIRTUAL_REQUIRED_CALL(_has_valid_project_configuration, p_preset, ret)) { + r_error = config_error; + } + return ret; +} + +List<String> EditorExportPlatformExtension::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> ret_list; + Vector<String> ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_binary_extensions, p_preset, ret)) { + for (const String &E : ret) { + ret_list.push_back(E); + } + } + return ret_list; +} + +Error EditorExportPlatformExtension::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + Error ret = FAILED; + GDVIRTUAL_REQUIRED_CALL(_export_project, p_preset, p_debug, p_path, p_flags, ret); + return ret; +} + +Error EditorExportPlatformExtension::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + Error ret = FAILED; + if (GDVIRTUAL_CALL(_export_pack, p_preset, p_debug, p_path, p_flags, ret)) { + return ret; + } + return save_pack(p_preset, p_debug, p_path); +} + +Error EditorExportPlatformExtension::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + Error ret = FAILED; + if (GDVIRTUAL_CALL(_export_zip, p_preset, p_debug, p_path, p_flags, ret)) { + return ret; + } + return save_zip(p_preset, p_debug, p_path); +} + +void EditorExportPlatformExtension::get_platform_features(List<String> *r_features) const { + Vector<String> ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_platform_features, ret) && r_features) { + for (const String &E : ret) { + r_features->push_back(E); + } + } +} + +String EditorExportPlatformExtension::get_debug_protocol() const { + String ret; + if (GDVIRTUAL_CALL(_get_debug_protocol, ret)) { + return ret; + } + return EditorExportPlatform::get_debug_protocol(); +} + +EditorExportPlatformExtension::EditorExportPlatformExtension() { + //NOP +} + +EditorExportPlatformExtension::~EditorExportPlatformExtension() { + //NOP +} diff --git a/editor/export/editor_export_platform_extension.h b/editor/export/editor_export_platform_extension.h new file mode 100644 index 0000000000..6391e65ac1 --- /dev/null +++ b/editor/export/editor_export_platform_extension.h @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* editor_export_platform_extension.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 EDITOR_EXPORT_PLATFORM_EXTENSION_H +#define EDITOR_EXPORT_PLATFORM_EXTENSION_H + +#include "editor_export_platform.h" +#include "editor_export_preset.h" + +class EditorExportPlatformExtension : public EditorExportPlatform { + GDCLASS(EditorExportPlatformExtension, EditorExportPlatform); + + mutable String config_error; + mutable bool config_missing_templates = false; + +protected: + static void _bind_methods(); + +public: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override; + GDVIRTUAL1RC(Vector<String>, _get_preset_features, Ref<EditorExportPreset>); + + virtual bool is_executable(const String &p_path) const override; + GDVIRTUAL1RC(bool, _is_executable, const String &); + + virtual void get_export_options(List<ExportOption> *r_options) const override; + GDVIRTUAL0RC(TypedArray<Dictionary>, _get_export_options); + + virtual bool should_update_export_options() override; + GDVIRTUAL0R(bool, _should_update_export_options); + + virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override; + GDVIRTUAL2RC(bool, _get_export_option_visibility, Ref<EditorExportPreset>, const String &); + + virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override; + GDVIRTUAL2RC(String, _get_export_option_warning, Ref<EditorExportPreset>, const StringName &); + + virtual String get_os_name() const override; + GDVIRTUAL0RC(String, _get_os_name); + + virtual String get_name() const override; + GDVIRTUAL0RC(String, _get_name); + + virtual Ref<Texture2D> get_logo() const override; + GDVIRTUAL0RC(Ref<Texture2D>, _get_logo); + + virtual bool poll_export() override; + GDVIRTUAL0R(bool, _poll_export); + + virtual int get_options_count() const override; + GDVIRTUAL0RC(int, _get_options_count); + + virtual String get_options_tooltip() const override; + GDVIRTUAL0RC(String, _get_options_tooltip); + + virtual Ref<ImageTexture> get_option_icon(int p_index) const override; + GDVIRTUAL1RC(Ref<ImageTexture>, _get_option_icon, int); + + virtual String get_option_label(int p_device) const override; + GDVIRTUAL1RC(String, _get_option_label, int); + + virtual String get_option_tooltip(int p_device) const override; + GDVIRTUAL1RC(String, _get_option_tooltip, int); + + virtual String get_device_architecture(int p_device) const override; + GDVIRTUAL1RC(String, _get_device_architecture, int); + + virtual void cleanup() override; + GDVIRTUAL0(_cleanup); + + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override; + GDVIRTUAL3R(Error, _run, Ref<EditorExportPreset>, int, BitField<EditorExportPlatform::DebugFlags>); + + virtual Ref<Texture2D> get_run_icon() const override; + GDVIRTUAL0RC(Ref<Texture2D>, _get_run_icon); + + void set_config_error(const String &p_error) const { + config_error = p_error; + } + String get_config_error() const { + return config_error; + } + + void set_config_missing_templates(bool p_missing_templates) const { + config_missing_templates = p_missing_templates; + } + bool get_config_missing_templates() const { + return config_missing_templates; + } + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; + GDVIRTUAL2RC(bool, _can_export, Ref<EditorExportPreset>, bool); + + virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; + GDVIRTUAL2RC(bool, _has_valid_export_configuration, Ref<EditorExportPreset>, bool); + + virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; + GDVIRTUAL1RC(bool, _has_valid_project_configuration, Ref<EditorExportPreset>); + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; + GDVIRTUAL1RC(Vector<String>, _get_binary_extensions, Ref<EditorExportPreset>); + + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; + GDVIRTUAL4R(Error, _export_project, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>); + + virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; + GDVIRTUAL4R(Error, _export_pack, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>); + + virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; + GDVIRTUAL4R(Error, _export_zip, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>); + + virtual void get_platform_features(List<String> *r_features) const override; + GDVIRTUAL0RC(Vector<String>, _get_platform_features); + + virtual String get_debug_protocol() const override; + GDVIRTUAL0RC(String, _get_debug_protocol); + + EditorExportPlatformExtension(); + ~EditorExportPlatformExtension(); +}; + +#endif // EDITOR_EXPORT_PLATFORM_EXTENSION_H diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index cdaf18b346..24d89b7f34 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -115,7 +115,7 @@ bool EditorExportPlatformPC::has_valid_project_configuration(const Ref<EditorExp return true; } -Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); Error err = prepare_template(p_preset, p_debug, p_path, p_flags); @@ -129,7 +129,7 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr return err; } -Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { if (!DirAccess::exists(p_path.get_base_dir())) { add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("The given export path doesn't exist.")); return ERR_FILE_BAD_PATH; @@ -182,7 +182,7 @@ Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_ return err; } -Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { String pck_path; if (p_preset->get("binary_format/embed_pck")) { pck_path = p_path; diff --git a/editor/export/editor_export_platform_pc.h b/editor/export/editor_export_platform_pc.h index 53574c2333..668ddaf47e 100644 --- a/editor/export/editor_export_platform_pc.h +++ b/editor/export/editor_export_platform_pc.h @@ -54,13 +54,13 @@ public: virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); virtual String get_template_file_name(const String &p_target, const String &p_arch) const = 0; - virtual Error prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags); - virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { return OK; }; - virtual Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags); + virtual Error prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags); + virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { return OK; } + virtual Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags); void set_name(const String &p_name); void set_os_name(const String &p_name); diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index 28d0750d5a..f56dd61e3b 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -48,6 +48,14 @@ Ref<EditorExportPreset> EditorExportPlugin::get_export_preset() const { return export_preset; } +Ref<EditorExportPlatform> EditorExportPlugin::get_export_platform() const { + if (export_preset.is_valid()) { + return export_preset->get_platform(); + } else { + return Ref<EditorExportPlatform>(); + } +} + void EditorExportPlugin::add_file(const String &p_path, const Vector<uint8_t> &p_file, bool p_remap) { ExtraFile ef; ef.data = p_file; @@ -321,6 +329,9 @@ void EditorExportPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("skip"), &EditorExportPlugin::skip); ClassDB::bind_method(D_METHOD("get_option", "name"), &EditorExportPlugin::get_option); + ClassDB::bind_method(D_METHOD("get_export_preset"), &EditorExportPlugin::get_export_preset); + ClassDB::bind_method(D_METHOD("get_export_platform"), &EditorExportPlugin::get_export_platform); + GDVIRTUAL_BIND(_export_file, "path", "type", "features"); GDVIRTUAL_BIND(_export_begin, "features", "is_debug", "path", "flags"); GDVIRTUAL_BIND(_export_end); diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h index 56eea85010..7a355614c7 100644 --- a/editor/export/editor_export_plugin.h +++ b/editor/export/editor_export_plugin.h @@ -91,6 +91,7 @@ class EditorExportPlugin : public RefCounted { protected: void set_export_preset(const Ref<EditorExportPreset> &p_preset); Ref<EditorExportPreset> get_export_preset() const; + Ref<EditorExportPlatform> get_export_platform() const; void add_file(const String &p_path, const Vector<uint8_t> &p_file, bool p_remap); void add_shared_object(const String &p_path, const Vector<String> &tags, const String &p_target = String()); diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp index e2e3e9d154..9f805666d0 100644 --- a/editor/export/editor_export_preset.cpp +++ b/editor/export/editor_export_preset.cpp @@ -62,6 +62,48 @@ bool EditorExportPreset::_get(const StringName &p_name, Variant &r_ret) const { void EditorExportPreset::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_property_warning", "name"), &EditorExportPreset::_get_property_warning); + + ClassDB::bind_method(D_METHOD("has", "property"), &EditorExportPreset::has); + + ClassDB::bind_method(D_METHOD("get_files_to_export"), &EditorExportPreset::get_files_to_export); + ClassDB::bind_method(D_METHOD("get_customized_files"), &EditorExportPreset::get_customized_files); + ClassDB::bind_method(D_METHOD("get_customized_files_count"), &EditorExportPreset::get_customized_files_count); + ClassDB::bind_method(D_METHOD("has_export_file", "path"), &EditorExportPreset::has_export_file); + ClassDB::bind_method(D_METHOD("get_file_export_mode", "path", "default"), &EditorExportPreset::get_file_export_mode, DEFVAL(MODE_FILE_NOT_CUSTOMIZED)); + + ClassDB::bind_method(D_METHOD("get_preset_name"), &EditorExportPreset::get_name); + ClassDB::bind_method(D_METHOD("is_runnable"), &EditorExportPreset::is_runnable); + ClassDB::bind_method(D_METHOD("are_advanced_options_enabled"), &EditorExportPreset::are_advanced_options_enabled); + ClassDB::bind_method(D_METHOD("is_dedicated_server"), &EditorExportPreset::is_dedicated_server); + ClassDB::bind_method(D_METHOD("get_export_filter"), &EditorExportPreset::get_export_filter); + ClassDB::bind_method(D_METHOD("get_include_filter"), &EditorExportPreset::get_include_filter); + ClassDB::bind_method(D_METHOD("get_exclude_filter"), &EditorExportPreset::get_exclude_filter); + ClassDB::bind_method(D_METHOD("get_custom_features"), &EditorExportPreset::get_custom_features); + ClassDB::bind_method(D_METHOD("get_export_path"), &EditorExportPreset::get_export_path); + ClassDB::bind_method(D_METHOD("get_encryption_in_filter"), &EditorExportPreset::get_enc_in_filter); + ClassDB::bind_method(D_METHOD("get_encryption_ex_filter"), &EditorExportPreset::get_enc_ex_filter); + ClassDB::bind_method(D_METHOD("get_encrypt_pck"), &EditorExportPreset::get_enc_pck); + ClassDB::bind_method(D_METHOD("get_encrypt_directory"), &EditorExportPreset::get_enc_directory); + ClassDB::bind_method(D_METHOD("get_encryption_key"), &EditorExportPreset::get_script_encryption_key); + ClassDB::bind_method(D_METHOD("get_script_export_mode"), &EditorExportPreset::get_script_export_mode); + + ClassDB::bind_method(D_METHOD("get_or_env", "name", "env_var"), &EditorExportPreset::_get_or_env); + ClassDB::bind_method(D_METHOD("get_version", "name", "windows_version"), &EditorExportPreset::get_version); + + BIND_ENUM_CONSTANT(EXPORT_ALL_RESOURCES); + BIND_ENUM_CONSTANT(EXPORT_SELECTED_SCENES); + BIND_ENUM_CONSTANT(EXPORT_SELECTED_RESOURCES); + BIND_ENUM_CONSTANT(EXCLUDE_SELECTED_RESOURCES); + BIND_ENUM_CONSTANT(EXPORT_CUSTOMIZED); + + BIND_ENUM_CONSTANT(MODE_FILE_NOT_CUSTOMIZED); + BIND_ENUM_CONSTANT(MODE_FILE_STRIP); + BIND_ENUM_CONSTANT(MODE_FILE_KEEP); + BIND_ENUM_CONSTANT(MODE_FILE_REMOVE); + + BIND_ENUM_CONSTANT(MODE_SCRIPT_TEXT); + BIND_ENUM_CONSTANT(MODE_SCRIPT_BINARY_TOKENS); + BIND_ENUM_CONSTANT(MODE_SCRIPT_BINARY_TOKENS_COMPRESSED); } String EditorExportPreset::_get_property_warning(const StringName &p_name) const { diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h index c6a8808af1..f220477461 100644 --- a/editor/export/editor_export_preset.h +++ b/editor/export/editor_export_preset.h @@ -168,6 +168,9 @@ public: void set_script_export_mode(int p_mode); int get_script_export_mode() const; + Variant _get_or_env(const StringName &p_name, const String &p_env_var) const { + return get_or_env(p_name, p_env_var); + } Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const; // Return the preset's version number, or fall back to the @@ -183,4 +186,8 @@ public: EditorExportPreset(); }; +VARIANT_ENUM_CAST(EditorExportPreset::ExportFilter); +VARIANT_ENUM_CAST(EditorExportPreset::FileExportMode); +VARIANT_ENUM_CAST(EditorExportPreset::ScriptExportMode); + #endif // EDITOR_EXPORT_PRESET_H diff --git a/editor/icons/MemberConstructor.svg b/editor/icons/MemberConstructor.svg new file mode 100644 index 0000000000..0e61768739 --- /dev/null +++ b/editor/icons/MemberConstructor.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M9 6c-1.33 2.67-1.33 4.33 0 7h2c-1.33-2.67-1.33-4.33 0-7zm4 0c1.33 2.67 1.33 4.33 0 7h2c1.33-2.67 1.33-4.33 0-7zM1 11a1 1 0 0 0 0 2 1 1 0 0 0 0-2zm6 2v-2H6a1 1 0 0 1 0-2h1V7H6a3 3 0 0 0 0 6z"/></svg>
\ No newline at end of file diff --git a/editor/icons/MemberOperator.svg b/editor/icons/MemberOperator.svg new file mode 100644 index 0000000000..a00d990e26 --- /dev/null +++ b/editor/icons/MemberOperator.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M9 6c-1.33 2.67-1.33 4.33 0 7h2c-1.33-2.67-1.33-4.33 0-7zm4 0c1.33 2.67 1.33 4.33 0 7h2c1.33-2.67 1.33-4.33 0-7zM1 11a1 1 0 0 0 0 2 1 1 0 0 0 0-2zm4-4a3 3 0 0 0 0 6 3 3 0 0 0 0-6zm0 2a1 1 0 0 1 0 2 1 1 0 0 1 0-2z"/></svg>
\ No newline at end of file diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 853e3e813b..b2795e4984 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -649,6 +649,9 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R String name = p_node->get_name(); NodePath original_path = p_root->get_path_to(p_node); // Used to detect renames due to import hints. + Ref<Resource> original_meta = memnew(Resource); // Create temp resource to hold original meta + original_meta->merge_meta_from(p_node); + bool isroot = p_node == p_root; if (!isroot && _teststr(name, "noimp")) { @@ -1022,6 +1025,8 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R print_verbose(vformat("Fix: Renamed %s to %s", original_path, new_path)); r_node_renames.push_back({ original_path, p_node }); } + // If we created new node instead, merge meta values from the original node. + p_node->merge_meta_from(*original_meta); } return p_node; @@ -2452,6 +2457,8 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ mesh_node->set_transform(src_mesh_node->get_transform()); mesh_node->set_skin(src_mesh_node->get_skin()); mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path()); + mesh_node->merge_meta_from(src_mesh_node); + if (src_mesh_node->get_mesh().is_valid()) { Ref<ArrayMesh> mesh; if (!src_mesh_node->get_mesh()->has_mesh()) { @@ -2599,6 +2606,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ for (int i = 0; i < mesh->get_surface_count(); i++) { mesh_node->set_surface_override_material(i, src_mesh_node->get_surface_material(i)); } + mesh->merge_meta_from(*src_mesh_node->get_mesh()); } } @@ -3114,7 +3122,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p progress.step(TTR("Saving..."), 104); int flags = 0; - if (EDITOR_GET("filesystem/on_save/compress_binary_resources")) { + if (EditorSettings::get_singleton() && EDITOR_GET("filesystem/on_save/compress_binary_resources")) { flags |= ResourceSaver::FLAG_COMPRESS; } diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 660e4647a1..bed9983461 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -2165,7 +2165,7 @@ void AnimationPlayerEditorPlugin::_property_keyed(const String &p_keyed, const V return; } te->_clear_selection(); - te->insert_value_key(p_keyed, p_value, p_advance); + te->insert_value_key(p_keyed, p_advance); } void AnimationPlayerEditorPlugin::_transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index ec42cb31be..6e41e98360 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -4331,13 +4331,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, Node2D *n2d = Object::cast_to<Node2D>(ci); if (key_pos && p_location) { - te->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing); + te->insert_node_value_key(n2d, "position", p_on_existing); } if (key_rot && p_rotation) { - te->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing); + te->insert_node_value_key(n2d, "rotation", p_on_existing); } if (key_scale && p_scale) { - te->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing); + te->insert_node_value_key(n2d, "scale", p_on_existing); } if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) { @@ -4363,13 +4363,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, if (has_chain && ik_chain.size()) { for (Node2D *&F : ik_chain) { if (key_pos) { - te->insert_node_value_key(F, "position", F->get_position(), p_on_existing); + te->insert_node_value_key(F, "position", p_on_existing); } if (key_rot) { - te->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing); + te->insert_node_value_key(F, "rotation", p_on_existing); } if (key_scale) { - te->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing); + te->insert_node_value_key(F, "scale", p_on_existing); } } } @@ -4379,13 +4379,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, Control *ctrl = Object::cast_to<Control>(ci); if (key_pos) { - te->insert_node_value_key(ctrl, "position", ctrl->get_position(), p_on_existing); + te->insert_node_value_key(ctrl, "position", p_on_existing); } if (key_rot) { - te->insert_node_value_key(ctrl, "rotation", ctrl->get_rotation(), p_on_existing); + te->insert_node_value_key(ctrl, "rotation", p_on_existing); } if (key_scale) { - te->insert_node_value_key(ctrl, "size", ctrl->get_size(), p_on_existing); + te->insert_node_value_key(ctrl, "size", p_on_existing); } } } diff --git a/editor/plugins/editor_plugin.cpp b/editor/plugins/editor_plugin.cpp index d9f60e155d..8ce667568f 100644 --- a/editor/plugins/editor_plugin.cpp +++ b/editor/plugins/editor_plugin.cpp @@ -40,6 +40,7 @@ #include "editor/editor_translation_parser.h" #include "editor/editor_undo_redo_manager.h" #include "editor/export/editor_export.h" +#include "editor/export/editor_export_platform.h" #include "editor/gui/editor_bottom_panel.h" #include "editor/gui/editor_title_bar.h" #include "editor/import/3d/resource_importer_scene.h" @@ -441,6 +442,16 @@ void EditorPlugin::remove_export_plugin(const Ref<EditorExportPlugin> &p_exporte EditorExport::get_singleton()->remove_export_plugin(p_exporter); } +void EditorPlugin::add_export_platform(const Ref<EditorExportPlatform> &p_platform) { + ERR_FAIL_COND(p_platform.is_null()); + EditorExport::get_singleton()->add_export_platform(p_platform); +} + +void EditorPlugin::remove_export_platform(const Ref<EditorExportPlatform> &p_platform) { + ERR_FAIL_COND(p_platform.is_null()); + EditorExport::get_singleton()->remove_export_platform(p_platform); +} + void EditorPlugin::add_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin) { ERR_FAIL_COND(!p_gizmo_plugin.is_valid()); Node3DEditor::get_singleton()->add_gizmo_plugin(p_gizmo_plugin); @@ -608,6 +619,8 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_scene_post_import_plugin", "scene_import_plugin"), &EditorPlugin::remove_scene_post_import_plugin); ClassDB::bind_method(D_METHOD("add_export_plugin", "plugin"), &EditorPlugin::add_export_plugin); ClassDB::bind_method(D_METHOD("remove_export_plugin", "plugin"), &EditorPlugin::remove_export_plugin); + ClassDB::bind_method(D_METHOD("add_export_platform", "platform"), &EditorPlugin::add_export_platform); + ClassDB::bind_method(D_METHOD("remove_export_platform", "platform"), &EditorPlugin::remove_export_platform); ClassDB::bind_method(D_METHOD("add_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::add_node_3d_gizmo_plugin); ClassDB::bind_method(D_METHOD("remove_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::remove_node_3d_gizmo_plugin); ClassDB::bind_method(D_METHOD("add_inspector_plugin", "plugin"), &EditorPlugin::add_inspector_plugin); diff --git a/editor/plugins/editor_plugin.h b/editor/plugins/editor_plugin.h index f6c4b35407..2e0771f906 100644 --- a/editor/plugins/editor_plugin.h +++ b/editor/plugins/editor_plugin.h @@ -41,6 +41,7 @@ class PopupMenu; class EditorDebuggerPlugin; class EditorExport; class EditorExportPlugin; +class EditorExportPlatform; class EditorImportPlugin; class EditorInspectorPlugin; class EditorInterface; @@ -224,6 +225,9 @@ public: void add_export_plugin(const Ref<EditorExportPlugin> &p_exporter); void remove_export_plugin(const Ref<EditorExportPlugin> &p_exporter); + void add_export_platform(const Ref<EditorExportPlatform> &p_platform); + void remove_export_platform(const Ref<EditorExportPlatform> &p_platform); + void add_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin); void remove_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index a33fc47955..d7de5a7223 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -578,8 +578,8 @@ void ScriptEditor::_clear_breakpoints() { script_editor_cache->get_sections(&cached_editors); for (const String &E : cached_editors) { Array breakpoints = _get_cached_breakpoints_for_script(E); - for (int i = 0; i < breakpoints.size(); i++) { - EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoints[i] + 1, false); + for (int breakpoint : breakpoints) { + EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoint + 1, false); } if (breakpoints.size() > 0) { @@ -1820,6 +1820,48 @@ void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) { emit_signal(SNAME("editor_script_changed"), p_script); } +Vector<String> ScriptEditor::_get_breakpoints() { + Vector<String> ret; + HashSet<String> loaded_scripts; + for (int i = 0; i < tab_container->get_tab_count(); i++) { + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); + if (!se) { + continue; + } + + Ref<Script> scr = se->get_edited_resource(); + if (scr.is_null()) { + continue; + } + + String base = scr->get_path(); + loaded_scripts.insert(base); + if (base.is_empty() || base.begins_with("local://")) { + continue; + } + + PackedInt32Array bpoints = se->get_breakpoints(); + for (int32_t bpoint : bpoints) { + ret.push_back(base + ":" + itos((int)bpoint + 1)); + } + } + + // Load breakpoints that are in closed scripts. + List<String> cached_editors; + script_editor_cache->get_sections(&cached_editors); + for (const String &E : cached_editors) { + if (loaded_scripts.has(E)) { + continue; + } + + Array breakpoints = _get_cached_breakpoints_for_script(E); + for (int breakpoint : breakpoints) { + ret.push_back(E + ":" + itos((int)breakpoint + 1)); + } + } + return ret; +} + void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { HashSet<String> loaded_scripts; for (int i = 0; i < tab_container->get_tab_count(); i++) { @@ -1829,19 +1871,19 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { } Ref<Script> scr = se->get_edited_resource(); - if (scr == nullptr) { + if (scr.is_null()) { continue; } String base = scr->get_path(); loaded_scripts.insert(base); - if (base.begins_with("local://") || base.is_empty()) { + if (base.is_empty() || base.begins_with("local://")) { continue; } PackedInt32Array bpoints = se->get_breakpoints(); - for (int j = 0; j < bpoints.size(); j++) { - p_breakpoints->push_back(base + ":" + itos((int)bpoints[j] + 1)); + for (int32_t bpoint : bpoints) { + p_breakpoints->push_back(base + ":" + itos((int)bpoint + 1)); } } @@ -1854,8 +1896,8 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { } Array breakpoints = _get_cached_breakpoints_for_script(E); - for (int i = 0; i < breakpoints.size(); i++) { - p_breakpoints->push_back(E + ":" + itos((int)breakpoints[i] + 1)); + for (int breakpoint : breakpoints) { + p_breakpoints->push_back(E + ":" + itos((int)breakpoint + 1)); } } } @@ -2944,8 +2986,8 @@ void ScriptEditor::_files_moved(const String &p_old_file, const String &p_new_fi // If Script, update breakpoints with debugger. Array breakpoints = _get_cached_breakpoints_for_script(p_new_file); - for (int i = 0; i < breakpoints.size(); i++) { - int line = (int)breakpoints[i] + 1; + for (int breakpoint : breakpoints) { + int line = (int)breakpoint + 1; EditorDebuggerNode::get_singleton()->set_breakpoint(p_old_file, line, false); if (!p_new_file.begins_with("local://") && ResourceLoader::exists(p_new_file, "Script")) { EditorDebuggerNode::get_singleton()->set_breakpoint(p_new_file, line, true); @@ -2969,8 +3011,8 @@ void ScriptEditor::_file_removed(const String &p_removed_file) { // Check closed. if (script_editor_cache->has_section(p_removed_file)) { Array breakpoints = _get_cached_breakpoints_for_script(p_removed_file); - for (int i = 0; i < breakpoints.size(); i++) { - EditorDebuggerNode::get_singleton()->set_breakpoint(p_removed_file, (int)breakpoints[i] + 1, false); + for (int breakpoint : breakpoints) { + EditorDebuggerNode::get_singleton()->set_breakpoint(p_removed_file, (int)breakpoint + 1, false); } script_editor_cache->erase_section(p_removed_file); } @@ -3425,8 +3467,8 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { } Array breakpoints = _get_cached_breakpoints_for_script(E); - for (int i = 0; i < breakpoints.size(); i++) { - EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoints[i] + 1, true); + for (int breakpoint : breakpoints) { + EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoint + 1, true); } } @@ -3972,6 +4014,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_help_tab_goto", &ScriptEditor::_help_tab_goto); ClassDB::bind_method("get_current_editor", &ScriptEditor::_get_current_editor); ClassDB::bind_method("get_open_script_editors", &ScriptEditor::_get_open_script_editors); + ClassDB::bind_method("get_breakpoints", &ScriptEditor::_get_breakpoints); ClassDB::bind_method(D_METHOD("register_syntax_highlighter", "syntax_highlighter"), &ScriptEditor::register_syntax_highlighter); ClassDB::bind_method(D_METHOD("unregister_syntax_highlighter", "syntax_highlighter"), &ScriptEditor::unregister_syntax_highlighter); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 66885790a7..e0fac5d0c6 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -539,6 +539,7 @@ public: _FORCE_INLINE_ bool edit(const Ref<Resource> &p_resource, bool p_grab_focus = true) { return edit(p_resource, -1, 0, p_grab_focus); } bool edit(const Ref<Resource> &p_resource, int p_line, int p_col, bool p_grab_focus = true); + Vector<String> _get_breakpoints(); void get_breakpoints(List<String> *p_breakpoints); PackedStringArray get_unsaved_scripts() const; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index eb39a27d02..34557b26b4 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -2373,6 +2373,7 @@ void ScriptTextEditor::_enable_code_editor() { ScriptTextEditor::ScriptTextEditor() { code_editor = memnew(CodeTextEditor); + code_editor->set_toggle_list_control(ScriptEditor::get_singleton()->get_left_list_split()); code_editor->add_theme_constant_override("separation", 2); code_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); code_editor->set_code_complete_func(_code_complete_scripts, this); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 04a392768a..ea049756b7 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -149,7 +149,9 @@ void ShaderEditorPlugin::edit(Object *p_object) { } } es.shader_inc = Ref<ShaderInclude>(si); - es.shader_editor = memnew(TextShaderEditor); + TextShaderEditor *text_shader = memnew(TextShaderEditor); + text_shader->get_code_editor()->set_toggle_list_control(left_panel); + es.shader_editor = text_shader; es.shader_editor->edit_shader_include(si); shader_tabs->add_child(es.shader_editor); } else { @@ -166,7 +168,9 @@ void ShaderEditorPlugin::edit(Object *p_object) { if (vs.is_valid()) { es.shader_editor = memnew(VisualShaderEditor); } else { - es.shader_editor = memnew(TextShaderEditor); + TextShaderEditor *text_shader = memnew(TextShaderEditor); + text_shader->get_code_editor()->set_toggle_list_control(left_panel); + es.shader_editor = text_shader; } shader_tabs->add_child(es.shader_editor); es.shader_editor->edit_shader(es.shader); @@ -434,6 +438,10 @@ void ShaderEditorPlugin::_close_shader(int p_index) { edited_shaders.remove_at(p_index); _update_shader_list(); EditorUndoRedoManager::get_singleton()->clear_history(); // To prevent undo on deleted graphs. + + if (shader_tabs->get_tab_count() == 0) { + left_panel->show(); // Make sure the panel is visible, because it can't be toggled without open shaders. + } } void ShaderEditorPlugin::_close_builtin_shaders_from_scene(const String &p_scene) { @@ -768,10 +776,10 @@ ShaderEditorPlugin::ShaderEditorPlugin() { Ref<Shortcut> make_floating_shortcut = ED_SHORTCUT_AND_COMMAND("shader_editor/make_floating", TTR("Make Floating")); window_wrapper->set_wrapped_control(main_split, make_floating_shortcut); - VBoxContainer *vb = memnew(VBoxContainer); + left_panel = memnew(VBoxContainer); HBoxContainer *menu_hb = memnew(HBoxContainer); - vb->add_child(menu_hb); + left_panel->add_child(menu_hb); file_menu = memnew(MenuButton); file_menu->set_text(TTR("File")); file_menu->set_shortcut_context(main_split); @@ -803,14 +811,14 @@ ShaderEditorPlugin::ShaderEditorPlugin() { shader_list = memnew(ItemList); shader_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); - vb->add_child(shader_list); + left_panel->add_child(shader_list); shader_list->connect(SceneStringName(item_selected), callable_mp(this, &ShaderEditorPlugin::_shader_selected)); shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked)); shader_list->set_allow_rmb_select(true); SET_DRAG_FORWARDING_GCD(shader_list, ShaderEditorPlugin); - main_split->add_child(vb); - vb->set_custom_minimum_size(Size2(200, 300) * EDSCALE); + main_split->add_child(left_panel); + left_panel->set_custom_minimum_size(Size2(200, 300) * EDSCALE); shader_tabs = memnew(TabContainer); shader_tabs->set_tabs_visible(false); @@ -823,7 +831,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() { button = EditorNode::get_bottom_panel()->add_item(TTR("Shader Editor"), window_wrapper, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_shader_editor_bottom_panel", TTR("Toggle Shader Editor Bottom Panel"), KeyModifierMask::ALT | Key::S)); shader_create_dialog = memnew(ShaderCreateDialog); - vb->add_child(shader_create_dialog); + main_split->add_child(shader_create_dialog); shader_create_dialog->connect("shader_created", callable_mp(this, &ShaderEditorPlugin::_shader_created)); shader_create_dialog->connect("shader_include_created", callable_mp(this, &ShaderEditorPlugin::_shader_include_created)); } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 31be64a56c..43e6af79fa 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -40,6 +40,7 @@ class ShaderCreateDialog; class ShaderEditor; class TabContainer; class TextShaderEditor; +class VBoxContainer; class VisualShaderEditor; class WindowWrapper; @@ -82,6 +83,7 @@ class ShaderEditorPlugin : public EditorPlugin { }; HSplitContainer *main_split = nullptr; + VBoxContainer *left_panel = nullptr; ItemList *shader_list = nullptr; TabContainer *shader_tabs = nullptr; diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 230d5809aa..dc4d4db3f8 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -47,6 +47,7 @@ #include "scene/3d/physics/physics_body_3d.h" #include "scene/gui/separator.h" #include "scene/gui/texture_rect.h" +#include "scene/property_utils.h" #include "scene/resources/3d/capsule_shape_3d.h" #include "scene/resources/skeleton_profile.h" #include "scene/resources/surface_tool.h" @@ -65,7 +66,7 @@ void BoneTransformEditor::create_editors() { // Position property. position_property = memnew(EditorPropertyVector3()); - position_property->setup(-10000, 10000, 0.001f, true); + position_property->setup(-10000, 10000, 0.001, true); position_property->set_label("Position"); position_property->set_selectable(false); position_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed)); @@ -74,7 +75,7 @@ void BoneTransformEditor::create_editors() { // Rotation property. rotation_property = memnew(EditorPropertyQuaternion()); - rotation_property->setup(-10000, 10000, 0.001f, true); + rotation_property->setup(-10000, 10000, 0.001, true); rotation_property->set_label("Rotation"); rotation_property->set_selectable(false); rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed)); @@ -83,7 +84,7 @@ void BoneTransformEditor::create_editors() { // Scale property. scale_property = memnew(EditorPropertyVector3()); - scale_property->setup(-10000, 10000, 0.001f, true); + scale_property->setup(-10000, 10000, 0.001, true, true); scale_property->set_label("Scale"); scale_property->set_selectable(false); scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed)); @@ -97,7 +98,7 @@ void BoneTransformEditor::create_editors() { // Transform/Matrix property. rest_matrix = memnew(EditorPropertyTransform3D()); - rest_matrix->setup(-10000, 10000, 0.001f, true); + rest_matrix->setup(-10000, 10000, 0.001, true); rest_matrix->set_label("Transform"); rest_matrix->set_selectable(false); rest_section->get_vbox()->add_child(rest_matrix); @@ -122,6 +123,13 @@ void BoneTransformEditor::_value_changed(const String &p_property, const Variant undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS); undo_redo->add_undo_property(skeleton, p_property, skeleton->get(p_property)); undo_redo->add_do_property(skeleton, p_property, p_value); + + Skeleton3DEditor *se = Skeleton3DEditor::get_singleton(); + if (se) { + undo_redo->add_do_method(se, "update_joint_tree"); + undo_redo->add_undo_method(se, "update_joint_tree"); + } + undo_redo->commit_action(); } } @@ -189,26 +197,31 @@ void BoneTransformEditor::_update_properties() { if (split[2] == "enabled") { enabled_checkbox->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY); enabled_checkbox->update_property(); + enabled_checkbox->update_editor_property_status(); enabled_checkbox->queue_redraw(); } if (split[2] == "position") { position_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY); position_property->update_property(); + position_property->update_editor_property_status(); position_property->queue_redraw(); } if (split[2] == "rotation") { rotation_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY); rotation_property->update_property(); + rotation_property->update_editor_property_status(); rotation_property->queue_redraw(); } if (split[2] == "scale") { scale_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY); scale_property->update_property(); + scale_property->update_editor_property_status(); scale_property->queue_redraw(); } if (split[2] == "rest") { rest_matrix->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY); rest_matrix->update_property(); + rest_matrix->update_editor_property_status(); rest_matrix->queue_redraw(); } } @@ -232,6 +245,11 @@ void Skeleton3DEditor::set_bone_options_enabled(const bool p_bone_options_enable skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_SELECTED_POSES_TO_RESTS, !p_bone_options_enabled); }; +void Skeleton3DEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("update_all"), &Skeleton3DEditor::update_all); + ClassDB::bind_method(D_METHOD("update_joint_tree"), &Skeleton3DEditor::update_joint_tree); +} + void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) { if (!skeleton) { return; @@ -294,6 +312,10 @@ void Skeleton3DEditor::reset_pose(const bool p_all_bones) { ur->add_undo_method(skeleton, "set_bone_pose_scale", selected_bone, skeleton->get_bone_pose_scale(selected_bone)); ur->add_do_method(skeleton, "reset_bone_pose", selected_bone); } + + ur->add_undo_method(this, "update_joint_tree"); + ur->add_do_method(this, "update_joint_tree"); + ur->commit_action(); } @@ -357,6 +379,10 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) { ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone)); ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone)); } + + ur->add_undo_method(this, "update_joint_tree"); + ur->add_do_method(this, "update_joint_tree"); + ur->commit_action(); } @@ -620,9 +646,12 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se } ur->add_undo_method(skeleton_node, "set_bone_parent", p_selected_boneidx, skeleton_node->get_bone_parent(p_selected_boneidx)); ur->add_do_method(skeleton_node, "set_bone_parent", p_selected_boneidx, p_target_boneidx); + + ur->add_undo_method(this, "update_joint_tree"); + ur->add_do_method(this, "update_joint_tree"); + skeleton_node->set_bone_parent(p_selected_boneidx, p_target_boneidx); - update_joint_tree(); ur->commit_action(); } @@ -655,6 +684,107 @@ void Skeleton3DEditor::_joint_tree_selection_changed() { void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button) { } +void Skeleton3DEditor::_joint_tree_button_clicked(Object *p_item, int p_column, int p_id, MouseButton p_button) { + if (!skeleton) { + return; + } + + TreeItem *tree_item = Object::cast_to<TreeItem>(p_item); + if (tree_item) { + String tree_item_metadata = tree_item->get_metadata(0); + + String bone_enabled_property = tree_item_metadata + "/enabled"; + String bone_parent_property = tree_item_metadata + "/parent"; + String bone_name_property = tree_item_metadata + "/name"; + String bone_position_property = tree_item_metadata + "/position"; + String bone_rotation_property = tree_item_metadata + "/rotation"; + String bone_scale_property = tree_item_metadata + "/scale"; + String bone_rest_property = tree_item_metadata + "/rest"; + + Variant current_enabled = skeleton->get(bone_enabled_property); + Variant current_parent = skeleton->get(bone_parent_property); + Variant current_name = skeleton->get(bone_name_property); + Variant current_position = skeleton->get(bone_position_property); + Variant current_rotation = skeleton->get(bone_rotation_property); + Variant current_scale = skeleton->get(bone_scale_property); + Variant current_rest = skeleton->get(bone_rest_property); + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Revert Bone")); + + bool can_revert_enabled = EditorPropertyRevert::can_property_revert(skeleton, bone_enabled_property, ¤t_enabled); + if (can_revert_enabled) { + bool is_valid = false; + Variant new_enabled = EditorPropertyRevert::get_property_revert_value(skeleton, bone_enabled_property, &is_valid); + if (is_valid) { + ur->add_undo_method(skeleton, "set", bone_enabled_property, current_enabled); + ur->add_do_method(skeleton, "set", bone_enabled_property, new_enabled); + } + } + + bool can_revert_parent = EditorPropertyRevert::can_property_revert(skeleton, bone_parent_property, ¤t_parent); + if (can_revert_parent) { + bool is_valid = false; + Variant new_parent = EditorPropertyRevert::get_property_revert_value(skeleton, bone_parent_property, &is_valid); + if (is_valid) { + ur->add_undo_method(skeleton, "set", bone_parent_property, current_parent); + ur->add_do_method(skeleton, "set", bone_parent_property, new_parent); + } + } + bool can_revert_name = EditorPropertyRevert::can_property_revert(skeleton, bone_name_property, ¤t_name); + if (can_revert_name) { + bool is_valid = false; + Variant new_name = EditorPropertyRevert::get_property_revert_value(skeleton, bone_name_property, &is_valid); + if (is_valid) { + ur->add_undo_method(skeleton, "set", bone_name_property, current_name); + ur->add_do_method(skeleton, "set", bone_name_property, new_name); + } + } + bool can_revert_position = EditorPropertyRevert::can_property_revert(skeleton, bone_position_property, ¤t_position); + if (can_revert_position) { + bool is_valid = false; + Variant new_position = EditorPropertyRevert::get_property_revert_value(skeleton, bone_position_property, &is_valid); + if (is_valid) { + ur->add_undo_method(skeleton, "set", bone_position_property, current_position); + ur->add_do_method(skeleton, "set", bone_position_property, new_position); + } + } + bool can_revert_rotation = EditorPropertyRevert::can_property_revert(skeleton, bone_rotation_property, ¤t_rotation); + if (can_revert_rotation) { + bool is_valid = false; + Variant new_rotation = EditorPropertyRevert::get_property_revert_value(skeleton, bone_rotation_property, &is_valid); + if (is_valid) { + ur->add_undo_method(skeleton, "set", bone_rotation_property, current_rotation); + ur->add_do_method(skeleton, "set", bone_rotation_property, new_rotation); + } + } + bool can_revert_scale = EditorPropertyRevert::can_property_revert(skeleton, bone_scale_property, ¤t_scale); + if (can_revert_scale) { + bool is_valid = false; + Variant new_scale = EditorPropertyRevert::get_property_revert_value(skeleton, bone_scale_property, &is_valid); + if (is_valid) { + ur->add_undo_method(skeleton, "set", bone_scale_property, current_scale); + ur->add_do_method(skeleton, "set", bone_scale_property, new_scale); + } + } + bool can_revert_rest = EditorPropertyRevert::can_property_revert(skeleton, bone_rest_property, ¤t_rest); + if (can_revert_rest) { + bool is_valid = false; + Variant new_rest = EditorPropertyRevert::get_property_revert_value(skeleton, bone_rest_property, &is_valid); + if (is_valid) { + ur->add_undo_method(skeleton, "set", bone_rest_property, current_rest); + ur->add_do_method(skeleton, "set", bone_rest_property, new_rest); + } + } + + ur->add_undo_method(this, "update_all"); + ur->add_do_method(this, "update_all"); + + ur->commit_action(); + } + return; +} + void Skeleton3DEditor::_update_properties() { if (pose_editor) { pose_editor->_update_properties(); @@ -693,15 +823,52 @@ void Skeleton3DEditor::update_joint_tree() { joint_item->set_selectable(0, true); joint_item->set_metadata(0, "bones/" + itos(current_bone_idx)); + String bone_enabled_property = "bones/" + itos(current_bone_idx) + "/enabled"; + String bone_parent_property = "bones/" + itos(current_bone_idx) + "/parent"; + String bone_name_property = "bones/" + itos(current_bone_idx) + "/name"; + String bone_position_property = "bones/" + itos(current_bone_idx) + "/position"; + String bone_rotation_property = "bones/" + itos(current_bone_idx) + "/rotation"; + String bone_scale_property = "bones/" + itos(current_bone_idx) + "/scale"; + String bone_rest_property = "bones/" + itos(current_bone_idx) + "/rest"; + + Variant current_enabled = skeleton->get(bone_enabled_property); + Variant current_parent = skeleton->get(bone_parent_property); + Variant current_name = skeleton->get(bone_name_property); + Variant current_position = skeleton->get(bone_position_property); + Variant current_rotation = skeleton->get(bone_rotation_property); + Variant current_scale = skeleton->get(bone_scale_property); + Variant current_rest = skeleton->get(bone_rest_property); + + bool can_revert_enabled = EditorPropertyRevert::can_property_revert(skeleton, bone_enabled_property, ¤t_enabled); + bool can_revert_parent = EditorPropertyRevert::can_property_revert(skeleton, bone_parent_property, ¤t_parent); + bool can_revert_name = EditorPropertyRevert::can_property_revert(skeleton, bone_name_property, ¤t_name); + bool can_revert_position = EditorPropertyRevert::can_property_revert(skeleton, bone_position_property, ¤t_position); + bool can_revert_rotation = EditorPropertyRevert::can_property_revert(skeleton, bone_rotation_property, ¤t_rotation); + bool can_revert_scale = EditorPropertyRevert::can_property_revert(skeleton, bone_scale_property, ¤t_scale); + bool can_revert_rest = EditorPropertyRevert::can_property_revert(skeleton, bone_rest_property, ¤t_rest); + + if (can_revert_enabled || can_revert_parent || can_revert_name || can_revert_position || can_revert_rotation || can_revert_scale || can_revert_rest) { + joint_item->add_button(0, get_editor_theme_icon(SNAME("ReloadSmall")), JOINT_BUTTON_REVERT, false, TTR("Revert")); + } + // Add the bone's children to the list of bones to be processed. Vector<int> current_bone_child_bones = skeleton->get_bone_children(current_bone_idx); int child_bone_size = current_bone_child_bones.size(); for (int i = 0; i < child_bone_size; i++) { bones_to_process.push_back(current_bone_child_bones[i]); } + + if (current_bone_idx == selected_bone) { + joint_item->select(0); + } } } +void Skeleton3DEditor::update_all() { + _update_properties(); + update_joint_tree(); +} + void Skeleton3DEditor::create_editors() { set_h_size_flags(SIZE_EXPAND_FILL); set_focus_mode(FOCUS_ALL); @@ -840,6 +1007,7 @@ void Skeleton3DEditor::_notification(int p_what) { joint_tree->connect(SceneStringName(item_selected), callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed)); joint_tree->connect("item_mouse_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select)); + joint_tree->connect("button_clicked", callable_mp(this, &Skeleton3DEditor::_joint_tree_button_clicked)); #ifdef TOOLS_ENABLED skeleton->connect(SceneStringName(pose_updated), callable_mp(this, &Skeleton3DEditor::_draw_gizmo)); skeleton->connect(SceneStringName(pose_updated), callable_mp(this, &Skeleton3DEditor::_update_properties)); @@ -1337,6 +1505,10 @@ void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, c ur->add_undo_method(skeleton, "set_bone_pose_scale", p_ids[i], se->get_bone_original_scale()); } } + + ur->add_do_method(se, "update_joint_tree"); + ur->add_undo_method(se, "update_joint_tree"); + ur->commit_action(); } diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index 79dc16ae2f..0bb58aac23 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -96,6 +96,8 @@ public: class Skeleton3DEditor : public VBoxContainer { GDCLASS(Skeleton3DEditor, VBoxContainer); + static void _bind_methods(); + friend class Skeleton3DEditorPlugin; enum SkeletonOption { @@ -116,6 +118,10 @@ class Skeleton3DEditor : public VBoxContainer { Skeleton3D *skeleton = nullptr; + enum { + JOINT_BUTTON_REVERT = 0, + }; + Tree *joint_tree = nullptr; BoneTransformEditor *rest_editor = nullptr; BoneTransformEditor *pose_editor = nullptr; @@ -149,6 +155,7 @@ class Skeleton3DEditor : public VBoxContainer { EditorFileDialog *file_export_lib = nullptr; void update_joint_tree(); + void update_all(); void create_editors(); @@ -189,6 +196,7 @@ class Skeleton3DEditor : public VBoxContainer { void _joint_tree_selection_changed(); void _joint_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button); + void _joint_tree_button_clicked(Object *p_item, int p_column, int p_id, MouseButton p_button); void _update_properties(); void _subgizmo_selection_change(); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index cb1f6c1ec6..c1bcd43b2e 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -35,6 +35,7 @@ #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "scene/gui/menu_button.h" +#include "scene/gui/split_container.h" void TextEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) { ERR_FAIL_COND(p_highlighter.is_null()); @@ -606,6 +607,7 @@ TextEditor::TextEditor() { code_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); code_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); code_editor->show_toggle_scripts_button(); + code_editor->set_toggle_list_control(ScriptEditor::get_singleton()->get_left_list_split()); update_settings(); diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index 4d8f3298b6..0ff7aaa3fe 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -1256,4 +1256,5 @@ TextShaderEditor::TextShaderEditor() { add_child(disk_changed); _editor_settings_changed(); + code_editor->show_toggle_scripts_button(); // TODO: Disabled for now, because it doesn't work properly. } diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 610ad3efdf..8e135e7eae 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -46,6 +46,7 @@ #include "editor/editor_translation_parser.h" #include "editor/editor_undo_redo_manager.h" #include "editor/export/editor_export_platform.h" +#include "editor/export/editor_export_platform_extension.h" #include "editor/export/editor_export_platform_pc.h" #include "editor/export/editor_export_plugin.h" #include "editor/filesystem_dock.h" @@ -161,6 +162,8 @@ void register_editor_types() { GDREGISTER_CLASS(EditorExportPlugin); GDREGISTER_ABSTRACT_CLASS(EditorExportPlatform); GDREGISTER_ABSTRACT_CLASS(EditorExportPlatformPC); + GDREGISTER_CLASS(EditorExportPlatformExtension); + GDREGISTER_ABSTRACT_CLASS(EditorExportPreset); register_exporter_types(); diff --git a/main/main.cpp b/main/main.cpp index 791562a9bb..101fc642b6 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -3394,7 +3394,8 @@ void Main::setup_boot_logo() { boot_logo.instantiate(); Error load_err = ImageLoader::load_image(boot_logo_path, boot_logo); if (load_err) { - ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + "'. Loading default splash."); + String msg = (boot_logo_path.ends_with(".png") ? "" : "The only supported format is PNG."); + ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + +"'. " + msg + " Loading default splash."); } } } else { diff --git a/methods.py b/methods.py index 6d81b35aff..bfd08cfc7b 100644 --- a/methods.py +++ b/methods.py @@ -163,7 +163,7 @@ def add_source_files(self, sources, files, allow_gen=False): def disable_warnings(self): # 'self' is the environment - if self.msvc: + if self.msvc and not using_clang(self): # We have to remove existing warning level defines before appending /w, # otherwise we get: "warning D9025 : overriding '/W3' with '/w'" self["CCFLAGS"] = [x for x in self["CCFLAGS"] if not (x.startswith("/W") or x.startswith("/w"))] @@ -817,21 +817,20 @@ def get_compiler_version(env): "apple_patch3": -1, } - if not env.msvc: - # Not using -dumpversion as some GCC distros only return major, and - # Clang used to return hardcoded 4.2.1: # https://reviews.llvm.org/D56803 - try: - version = ( - subprocess.check_output([env.subst(env["CXX"]), "--version"], shell=(os.name == "nt")) - .strip() - .decode("utf-8") - ) - except (subprocess.CalledProcessError, OSError): - print_warning("Couldn't parse CXX environment variable to infer compiler version.") - return ret - else: + if env.msvc and not using_clang(env): # TODO: Implement for MSVC return ret + + # Not using -dumpversion as some GCC distros only return major, and + # Clang used to return hardcoded 4.2.1: # https://reviews.llvm.org/D56803 + try: + version = subprocess.check_output( + [env.subst(env["CXX"]), "--version"], shell=(os.name == "nt"), encoding="utf-8" + ).strip() + except (subprocess.CalledProcessError, OSError): + print_warning("Couldn't parse CXX environment variable to infer compiler version.") + return ret + match = re.search( r"(?:(?<=version )|(?<=\) )|(?<=^))" r"(?P<major>\d+)" diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index c72755642b..36c5ad0937 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -57,6 +57,7 @@ #include "scene/scene_string_names.h" #ifdef TOOLS_ENABLED +#include "core/extension/gdextension_manager.h" #include "editor/editor_paths.h" #endif @@ -2177,9 +2178,26 @@ void GDScriptLanguage::_add_global(const StringName &p_name, const Variant &p_va global_array.write[globals[p_name]] = p_value; return; } - globals[p_name] = global_array.size(); - global_array.push_back(p_value); - _global_array = global_array.ptrw(); + + if (global_array_empty_indexes.size()) { + int index = global_array_empty_indexes[global_array_empty_indexes.size() - 1]; + globals[p_name] = index; + global_array.write[index] = p_value; + global_array_empty_indexes.resize(global_array_empty_indexes.size() - 1); + } else { + globals[p_name] = global_array.size(); + global_array.push_back(p_value); + _global_array = global_array.ptrw(); + } +} + +void GDScriptLanguage::_remove_global(const StringName &p_name) { + if (!globals.has(p_name)) { + return; + } + global_array_empty_indexes.push_back(globals[p_name]); + global_array.write[globals[p_name]] = Variant::NIL; + globals.erase(p_name); } void GDScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) { @@ -2237,11 +2255,40 @@ void GDScriptLanguage::init() { _add_global(E.name, E.ptr); } +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + GDExtensionManager::get_singleton()->connect("extension_loaded", callable_mp(this, &GDScriptLanguage::_extension_loaded)); + GDExtensionManager::get_singleton()->connect("extension_unloading", callable_mp(this, &GDScriptLanguage::_extension_unloading)); + } +#endif + #ifdef TESTS_ENABLED GDScriptTests::GDScriptTestRunner::handle_cmdline(); #endif } +#ifdef TOOLS_ENABLED +void GDScriptLanguage::_extension_loaded(const Ref<GDExtension> &p_extension) { + List<StringName> class_list; + ClassDB::get_extension_class_list(p_extension, &class_list); + for (const StringName &n : class_list) { + if (globals.has(n)) { + continue; + } + Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(n)); + _add_global(n, nc); + } +} + +void GDScriptLanguage::_extension_unloading(const Ref<GDExtension> &p_extension) { + List<StringName> class_list; + ClassDB::get_extension_class_list(p_extension, &class_list); + for (const StringName &n : class_list) { + _remove_global(n); + } +} +#endif + String GDScriptLanguage::get_type() const { return "GDScript"; } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 6527a0ea4d..4d21651365 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -417,6 +417,7 @@ class GDScriptLanguage : public ScriptLanguage { Vector<Variant> global_array; HashMap<StringName, int> globals; HashMap<StringName, Variant> named_globals; + Vector<int> global_array_empty_indexes; struct CallLevel { Variant *stack = nullptr; @@ -448,6 +449,7 @@ class GDScriptLanguage : public ScriptLanguage { int _debug_max_call_stack = 0; void _add_global(const StringName &p_name, const Variant &p_value); + void _remove_global(const StringName &p_name); friend class GDScriptInstance; @@ -467,6 +469,11 @@ class GDScriptLanguage : public ScriptLanguage { HashMap<String, ObjectID> orphan_subclasses; +#ifdef TOOLS_ENABLED + void _extension_loaded(const Ref<GDExtension> &p_extension); + void _extension_unloading(const Ref<GDExtension> &p_extension); +#endif + public: int calls; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index c4cef0287f..582305d900 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -260,6 +260,7 @@ void GDScriptParser::override_completion_context(const Node *p_for_node, Complet context.current_line = tokenizer->get_cursor_line(); context.current_argument = p_argument; context.node = p_node; + context.parser = this; completion_context = context; } diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index ebeed015e9..10534594d3 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -63,6 +63,13 @@ The [param bake_fps] parameter overrides the bake_fps in [param state]. </description> </method> + <method name="get_supported_gltf_extensions" qualifiers="static"> + <return type="PackedStringArray" /> + <description> + Returns a list of all support glTF extensions, including extensions supported directly by the engine, and extensions supported by user plugins registering [GLTFDocumentExtension] classes. + [b]Note:[/b] If this method is run before a GLTFDocumentExtension is registered, its extensions won't be included in the list. Be sure to only run this method after all extensions are registered. If you run this when the engine starts, consider waiting a frame before calling this method to ensure all extensions are registered. + </description> + </method> <method name="register_gltf_document_extension" qualifiers="static"> <return type="void" /> <param index="0" name="extension" type="GLTFDocumentExtension" /> diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index cd25b93e6c..69973a34dd 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -69,6 +69,24 @@ #include <stdlib.h> #include <cstdint> +static void _attach_extras_to_meta(const Dictionary &p_extras, Ref<Resource> p_node) { + if (!p_extras.is_empty()) { + p_node->set_meta("extras", p_extras); + } +} + +static void _attach_meta_to_extras(Ref<Resource> p_node, Dictionary &p_json) { + if (p_node->has_meta("extras")) { + Dictionary node_extras = p_node->get_meta("extras"); + if (p_json.has("extras")) { + Dictionary extras = p_json["extras"]; + extras.merge(node_extras); + } else { + p_json["extras"] = node_extras; + } + } +} + static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) { Ref<ImporterMesh> importer_mesh; importer_mesh.instantiate(); @@ -101,6 +119,7 @@ static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) { array, p_mesh->surface_get_blend_shape_arrays(surface_i), p_mesh->surface_get_lods(surface_i), mat, mat_name, p_mesh->surface_get_format(surface_i)); } + importer_mesh->merge_meta_from(*p_mesh); return importer_mesh; } @@ -458,7 +477,7 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> p_state) { if (extensions.is_empty()) { node.erase("extensions"); } - + _attach_meta_to_extras(gltf_node, node); nodes.push_back(node); } if (!nodes.is_empty()) { @@ -624,6 +643,10 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> p_state) { } } + if (n.has("extras")) { + _attach_extras_to_meta(n["extras"], node); + } + if (n.has("children")) { const Array &children = n["children"]; for (int j = 0; j < children.size(); j++) { @@ -2727,6 +2750,8 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) { Dictionary e; e["targetNames"] = target_names; + gltf_mesh["extras"] = e; + _attach_meta_to_extras(import_mesh, gltf_mesh); weights.resize(target_names.size()); for (int name_i = 0; name_i < target_names.size(); name_i++) { @@ -2742,8 +2767,6 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) { ERR_FAIL_COND_V(target_names.size() != weights.size(), FAILED); - gltf_mesh["extras"] = e; - gltf_mesh["primitives"] = primitives; meshes.push_back(gltf_mesh); @@ -2776,6 +2799,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) { Array primitives = d["primitives"]; const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary(); + _attach_extras_to_meta(extras, mesh); Ref<ImporterMesh> import_mesh; import_mesh.instantiate(); String mesh_name = "mesh"; @@ -4170,6 +4194,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) { } d["extensions"] = extensions; + _attach_meta_to_extras(material, d); materials.push_back(d); } if (!materials.size()) { @@ -4372,6 +4397,10 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { } } } + + if (material_dict.has("extras")) { + _attach_extras_to_meta(material_dict["extras"], material); + } p_state->materials.push_back(material); } @@ -5161,6 +5190,7 @@ ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> p_s return mi; } mi->set_mesh(import_mesh); + import_mesh->merge_meta_from(*mesh); return mi; } @@ -5285,6 +5315,7 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> p_state, Node *p_current, gltf_root = current_node_i; p_state->root_nodes.push_back(gltf_root); } + gltf_node->merge_meta_from(p_current); _create_gltf_node(p_state, p_current, current_node_i, p_gltf_parent, gltf_root, gltf_node); for (int node_i = 0; node_i < p_current->get_child_count(); node_i++) { _convert_scene_node(p_state, p_current->get_child(node_i), current_node_i, gltf_root); @@ -5676,6 +5707,8 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn current_node->set_transform(gltf_node->transform); } + current_node->merge_meta_from(*gltf_node); + p_state->scene_nodes.insert(p_node_index, current_node); for (int i = 0; i < gltf_node->children.size(); ++i) { _generate_scene_node(p_state, gltf_node->children[i], current_node, p_scene_root); @@ -7060,6 +7093,8 @@ void GLTFDocument::_bind_methods() { &GLTFDocument::register_gltf_document_extension, DEFVAL(false)); ClassDB::bind_static_method("GLTFDocument", D_METHOD("unregister_gltf_document_extension", "extension"), &GLTFDocument::unregister_gltf_document_extension); + ClassDB::bind_static_method("GLTFDocument", D_METHOD("get_supported_gltf_extensions"), + &GLTFDocument::get_supported_gltf_extensions); } void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> p_state) { @@ -7100,6 +7135,36 @@ Vector<Ref<GLTFDocumentExtension>> GLTFDocument::get_all_gltf_document_extension return all_document_extensions; } +Vector<String> GLTFDocument::get_supported_gltf_extensions() { + HashSet<String> set = get_supported_gltf_extensions_hashset(); + Vector<String> vec; + for (const String &s : set) { + vec.append(s); + } + vec.sort(); + return vec; +} + +HashSet<String> GLTFDocument::get_supported_gltf_extensions_hashset() { + HashSet<String> supported_extensions; + // If the extension is supported directly in GLTFDocument, list it here. + // Other built-in extensions are supported by GLTFDocumentExtension classes. + supported_extensions.insert("GODOT_single_root"); + supported_extensions.insert("KHR_lights_punctual"); + supported_extensions.insert("KHR_materials_emissive_strength"); + supported_extensions.insert("KHR_materials_pbrSpecularGlossiness"); + supported_extensions.insert("KHR_materials_unlit"); + supported_extensions.insert("KHR_texture_transform"); + for (Ref<GLTFDocumentExtension> ext : all_document_extensions) { + ERR_CONTINUE(ext.is_null()); + Vector<String> ext_supported_extensions = ext->get_supported_extensions(); + for (int i = 0; i < ext_supported_extensions.size(); ++i) { + supported_extensions.insert(ext_supported_extensions[i]); + } + } + return supported_extensions; +} + PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> p_state, Error *r_err) { Error err = _encode_buffer_glb(p_state, ""); if (r_err) { @@ -7452,19 +7517,7 @@ Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) { Vector<String> ext_array = p_state->json["extensionsRequired"]; p_state->extensions_required = ext_array; } - HashSet<String> supported_extensions; - supported_extensions.insert("KHR_lights_punctual"); - supported_extensions.insert("KHR_materials_pbrSpecularGlossiness"); - supported_extensions.insert("KHR_texture_transform"); - supported_extensions.insert("KHR_materials_unlit"); - supported_extensions.insert("KHR_materials_emissive_strength"); - for (Ref<GLTFDocumentExtension> ext : document_extensions) { - ERR_CONTINUE(ext.is_null()); - Vector<String> ext_supported_extensions = ext->get_supported_extensions(); - for (int i = 0; i < ext_supported_extensions.size(); ++i) { - supported_extensions.insert(ext_supported_extensions[i]); - } - } + HashSet<String> supported_extensions = get_supported_gltf_extensions_hashset(); Error ret = OK; for (int i = 0; i < p_state->extensions_required.size(); i++) { if (!supported_extensions.has(p_state->extensions_required[i])) { diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index d37544750d..b3e6dcf54a 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -92,6 +92,8 @@ public: static void unregister_gltf_document_extension(Ref<GLTFDocumentExtension> p_extension); static void unregister_all_gltf_document_extensions(); static Vector<Ref<GLTFDocumentExtension>> get_all_gltf_document_extensions(); + static Vector<String> get_supported_gltf_extensions(); + static HashSet<String> get_supported_gltf_extensions_hashset(); void set_naming_version(int p_version); int get_naming_version() const; diff --git a/modules/gltf/tests/test_gltf_extras.h b/modules/gltf/tests/test_gltf_extras.h new file mode 100644 index 0000000000..96aadf3023 --- /dev/null +++ b/modules/gltf/tests/test_gltf_extras.h @@ -0,0 +1,165 @@ +/**************************************************************************/ +/* test_gltf_extras.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 TEST_GLTF_EXTRAS_H +#define TEST_GLTF_EXTRAS_H + +#include "tests/test_macros.h" + +#ifdef TOOLS_ENABLED + +#include "core/os/os.h" +#include "editor/import/3d/resource_importer_scene.h" +#include "modules/gltf/editor/editor_scene_importer_gltf.h" +#include "modules/gltf/gltf_document.h" +#include "modules/gltf/gltf_state.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/main/window.h" +#include "scene/resources/3d/primitive_meshes.h" +#include "scene/resources/material.h" +#include "scene/resources/packed_scene.h" + +namespace TestGltfExtras { + +static Node *_gltf_export_then_import(Node *p_root, String &p_tempfilebase) { + Ref<GLTFDocument> doc; + doc.instantiate(); + Ref<GLTFState> state; + state.instantiate(); + Error err = doc->append_from_scene(p_root, state, EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS); + CHECK_MESSAGE(err == OK, "GLTF state generation failed."); + err = doc->write_to_filesystem(state, p_tempfilebase + ".gltf"); + CHECK_MESSAGE(err == OK, "Writing GLTF to cache dir failed."); + + // Setting up importers. + Ref<ResourceImporterScene> import_scene = memnew(ResourceImporterScene("PackedScene", true)); + ResourceFormatImporter::get_singleton()->add_importer(import_scene); + Ref<EditorSceneFormatImporterGLTF> import_gltf; + import_gltf.instantiate(); + ResourceImporterScene::add_scene_importer(import_gltf); + + // GTLF importer behaves differently outside of editor, it's too late to modify Engine::get_editor_hint + // as the registration of runtime extensions already happened, so remove them. See modules/gltf/register_types.cpp + GLTFDocument::unregister_all_gltf_document_extensions(); + + HashMap<StringName, Variant> options(20); + options["nodes/root_type"] = ""; + options["nodes/root_name"] = ""; + options["nodes/apply_root_scale"] = true; + options["nodes/root_scale"] = 1.0; + options["meshes/ensure_tangents"] = true; + options["meshes/generate_lods"] = false; + options["meshes/create_shadow_meshes"] = true; + options["meshes/light_baking"] = 1; + options["meshes/lightmap_texel_size"] = 0.2; + options["meshes/force_disable_compression"] = false; + options["skins/use_named_skins"] = true; + options["animation/import"] = true; + options["animation/fps"] = 30; + options["animation/trimming"] = false; + options["animation/remove_immutable_tracks"] = true; + options["import_script/path"] = ""; + options["_subresources"] = Dictionary(); + options["gltf/naming_version"] = 1; + + // Process gltf file, note that this generates `.scn` resource from the 2nd argument. + err = import_scene->import(p_tempfilebase + ".gltf", p_tempfilebase, options, nullptr, nullptr, nullptr); + CHECK_MESSAGE(err == OK, "GLTF import failed."); + ResourceImporterScene::remove_scene_importer(import_gltf); + + Ref<PackedScene> packed_scene = ResourceLoader::load(p_tempfilebase + ".scn", "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); + CHECK_MESSAGE(err == OK, "Loading scene failed."); + Node *p_scene = packed_scene->instantiate(); + return p_scene; +} + +TEST_CASE("[SceneTree][Node] GLTF test mesh and material meta export and import") { + // Setup scene. + Ref<StandardMaterial3D> original_material = memnew(StandardMaterial3D); + original_material->set_albedo(Color(1.0, .0, .0)); + original_material->set_name("material"); + Dictionary material_dict; + material_dict["node_type"] = "material"; + original_material->set_meta("extras", material_dict); + + Ref<PlaneMesh> original_meshdata = memnew(PlaneMesh); + original_meshdata->set_name("planemesh"); + Dictionary meshdata_dict; + meshdata_dict["node_type"] = "planemesh"; + original_meshdata->set_meta("extras", meshdata_dict); + original_meshdata->surface_set_material(0, original_material); + + MeshInstance3D *original_mesh_instance = memnew(MeshInstance3D); + original_mesh_instance->set_mesh(original_meshdata); + original_mesh_instance->set_name("mesh_instance_3d"); + Dictionary mesh_instance_dict; + mesh_instance_dict["node_type"] = "mesh_instance_3d"; + original_mesh_instance->set_meta("extras", mesh_instance_dict); + + Node3D *original = memnew(Node3D); + SceneTree::get_singleton()->get_root()->add_child(original); + original->add_child(original_mesh_instance); + original->set_name("node3d"); + Dictionary node_dict; + node_dict["node_type"] = "node3d"; + original->set_meta("extras", node_dict); + original->set_meta("meta_not_nested_under_extras", "should not propagate"); + + // Convert to GLFT and back. + String tempfile = OS::get_singleton()->get_cache_path().path_join("gltf_extras"); + Node *loaded = _gltf_export_then_import(original, tempfile); + + // Compare the results. + CHECK(loaded->get_name() == "node3d"); + CHECK(Dictionary(loaded->get_meta("extras")).size() == 1); + CHECK(Dictionary(loaded->get_meta("extras"))["node_type"] == "node3d"); + CHECK_FALSE(loaded->has_meta("meta_not_nested_under_extras")); + CHECK_FALSE(Dictionary(loaded->get_meta("extras")).has("meta_not_nested_under_extras")); + + MeshInstance3D *mesh_instance_3d = Object::cast_to<MeshInstance3D>(loaded->find_child("mesh_instance_3d", false, true)); + CHECK(mesh_instance_3d->get_name() == "mesh_instance_3d"); + CHECK(Dictionary(mesh_instance_3d->get_meta("extras"))["node_type"] == "mesh_instance_3d"); + + Ref<Mesh> mesh = mesh_instance_3d->get_mesh(); + CHECK(Dictionary(mesh->get_meta("extras"))["node_type"] == "planemesh"); + + Ref<Material> material = mesh->surface_get_material(0); + CHECK(material->get_name() == "material"); + CHECK(Dictionary(material->get_meta("extras"))["node_type"] == "material"); + + memdelete(original_mesh_instance); + memdelete(original); + memdelete(loaded); +} +} // namespace TestGltfExtras + +#endif // TOOLS_ENABLED + +#endif // TEST_GLTF_EXTRAS_H diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 5720f844bb..c6c35efb59 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -57,7 +57,7 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { mp3dec_frame_info_t frame_info; mp3d_sample_t *buf_frame = nullptr; - int samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels); + int samples_mixed = mp3dec_ex_read_frame(&mp3d, &buf_frame, &frame_info, mp3_stream->channels); if (samples_mixed) { p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]); @@ -70,7 +70,7 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { if (beat_loop && (int)frames_mixed >= beat_length_frames) { for (int i = 0; i < FADE_SIZE; i++) { - samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels); + samples_mixed = mp3dec_ex_read_frame(&mp3d, &buf_frame, &frame_info, mp3_stream->channels); loop_fade[i] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]); if (!samples_mixed) { break; @@ -138,7 +138,7 @@ void AudioStreamPlaybackMP3::seek(double p_time) { } frames_mixed = uint32_t(mp3_stream->sample_rate * p_time); - mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels); + mp3dec_ex_seek(&mp3d, (uint64_t)frames_mixed * mp3_stream->channels); } void AudioStreamPlaybackMP3::tag_used_streams() { @@ -181,10 +181,7 @@ Variant AudioStreamPlaybackMP3::get_parameter(const StringName &p_name) const { } AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() { - if (mp3d) { - mp3dec_ex_close(mp3d); - memfree(mp3d); - } + mp3dec_ex_close(&mp3d); } Ref<AudioStreamPlayback> AudioStreamMP3::instantiate_playback() { @@ -197,9 +194,8 @@ Ref<AudioStreamPlayback> AudioStreamMP3::instantiate_playback() { mp3s.instantiate(); mp3s->mp3_stream = Ref<AudioStreamMP3>(this); - mp3s->mp3d = (mp3dec_ex_t *)memalloc(sizeof(mp3dec_ex_t)); - int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE); + int errorcode = mp3dec_ex_open_buf(&mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE); mp3s->frames_mixed = 0; mp3s->active = false; diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h index 81e8f8633c..39d389b8cd 100644 --- a/modules/minimp3/audio_stream_mp3.h +++ b/modules/minimp3/audio_stream_mp3.h @@ -49,7 +49,7 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled { bool looping_override = false; bool looping = false; - mp3dec_ex_t *mp3d = nullptr; + mp3dec_ex_t mp3d = {}; uint32_t frames_mixed = 0; bool active = false; int loops = 0; diff --git a/modules/mono/editor/hostfxr_resolver.cpp b/modules/mono/editor/hostfxr_resolver.cpp index 7fa482969e..9c37ac810a 100644 --- a/modules/mono/editor/hostfxr_resolver.cpp +++ b/modules/mono/editor/hostfxr_resolver.cpp @@ -216,6 +216,7 @@ bool get_default_installation_dir(String &r_dotnet_root) { #endif } +#ifndef WINDOWS_ENABLED bool get_install_location_from_file(const String &p_file_path, String &r_dotnet_root) { Error err = OK; Ref<FileAccess> f = FileAccess::open(p_file_path, FileAccess::READ, &err); @@ -233,6 +234,7 @@ bool get_install_location_from_file(const String &p_file_path, String &r_dotnet_ r_dotnet_root = line; return true; } +#endif bool get_dotnet_self_registered_dir(String &r_dotnet_root) { #if defined(WINDOWS_ENABLED) @@ -260,7 +262,7 @@ bool get_dotnet_self_registered_dir(String &r_dotnet_root) { return false; } - r_dotnet_root = String::utf16((const char16_t *)buffer.ptr()); + r_dotnet_root = String::utf16((const char16_t *)buffer.ptr()).replace("\\", "/"); RegCloseKey(hkey); return true; #else diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 33b92f6266..78983187c7 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -87,57 +87,55 @@ void NavMeshGenerator2D::sync() { return; } - baking_navmesh_mutex.lock(); - generator_task_mutex.lock(); + MutexLock baking_navmesh_lock(baking_navmesh_mutex); + { + MutexLock generator_task_lock(generator_task_mutex); - LocalVector<WorkerThreadPool::TaskID> finished_task_ids; + LocalVector<WorkerThreadPool::TaskID> finished_task_ids; - for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask2D *> &E : generator_tasks) { - if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) { - WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); - finished_task_ids.push_back(E.key); + for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask2D *> &E : generator_tasks) { + if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); + finished_task_ids.push_back(E.key); - NavMeshGeneratorTask2D *generator_task = E.value; - DEV_ASSERT(generator_task->status == NavMeshGeneratorTask2D::TaskStatus::BAKING_FINISHED); + NavMeshGeneratorTask2D *generator_task = E.value; + DEV_ASSERT(generator_task->status == NavMeshGeneratorTask2D::TaskStatus::BAKING_FINISHED); - baking_navmeshes.erase(generator_task->navigation_mesh); - if (generator_task->callback.is_valid()) { - generator_emit_callback(generator_task->callback); + baking_navmeshes.erase(generator_task->navigation_mesh); + if (generator_task->callback.is_valid()) { + generator_emit_callback(generator_task->callback); + } + memdelete(generator_task); } - memdelete(generator_task); } - } - for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) { - generator_tasks.erase(finished_task_id); + for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) { + generator_tasks.erase(finished_task_id); + } } - - generator_task_mutex.unlock(); - baking_navmesh_mutex.unlock(); } void NavMeshGenerator2D::cleanup() { - baking_navmesh_mutex.lock(); - generator_task_mutex.lock(); + MutexLock baking_navmesh_lock(baking_navmesh_mutex); + { + MutexLock generator_task_lock(generator_task_mutex); - baking_navmeshes.clear(); + baking_navmeshes.clear(); - for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask2D *> &E : generator_tasks) { - WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); - NavMeshGeneratorTask2D *generator_task = E.value; - memdelete(generator_task); - } - generator_tasks.clear(); + for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask2D *> &E : generator_tasks) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); + NavMeshGeneratorTask2D *generator_task = E.value; + memdelete(generator_task); + } + generator_tasks.clear(); - generator_rid_rwlock.write_lock(); - for (NavMeshGeometryParser2D *parser : generator_parsers) { - generator_parser_owner.free(parser->self); + generator_rid_rwlock.write_lock(); + for (NavMeshGeometryParser2D *parser : generator_parsers) { + generator_parser_owner.free(parser->self); + } + generator_parsers.clear(); + generator_rid_rwlock.write_unlock(); } - generator_parsers.clear(); - generator_rid_rwlock.write_unlock(); - - generator_task_mutex.unlock(); - baking_navmesh_mutex.unlock(); } void NavMeshGenerator2D::finish() { @@ -212,7 +210,7 @@ void NavMeshGenerator2D::bake_from_source_geometry_data_async(Ref<NavigationPoly baking_navmeshes.insert(p_navigation_mesh); baking_navmesh_mutex.unlock(); - generator_task_mutex.lock(); + MutexLock generator_task_lock(generator_task_mutex); NavMeshGeneratorTask2D *generator_task = memnew(NavMeshGeneratorTask2D); generator_task->navigation_mesh = p_navigation_mesh; generator_task->source_geometry_data = p_source_geometry_data; @@ -220,14 +218,11 @@ void NavMeshGenerator2D::bake_from_source_geometry_data_async(Ref<NavigationPoly generator_task->status = NavMeshGeneratorTask2D::TaskStatus::BAKING_STARTED; generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator2D::generator_thread_bake, generator_task, NavMeshGenerator2D::baking_use_high_priority_threads, "NavMeshGeneratorBake2D"); generator_tasks.insert(generator_task->thread_task_id, generator_task); - generator_task_mutex.unlock(); } bool NavMeshGenerator2D::is_baking(Ref<NavigationPolygon> p_navigation_polygon) { - baking_navmesh_mutex.lock(); - bool baking = baking_navmeshes.has(p_navigation_polygon); - baking_navmesh_mutex.unlock(); - return baking; + MutexLock baking_navmesh_lock(baking_navmesh_mutex); + return baking_navmeshes.has(p_navigation_polygon); } void NavMeshGenerator2D::generator_thread_bake(void *p_arg) { diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp index d17724baa0..e92a9d304b 100644 --- a/modules/navigation/3d/nav_mesh_generator_3d.cpp +++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp @@ -100,57 +100,55 @@ void NavMeshGenerator3D::sync() { return; } - baking_navmesh_mutex.lock(); - generator_task_mutex.lock(); + MutexLock baking_navmesh_lock(baking_navmesh_mutex); + { + MutexLock generator_task_lock(generator_task_mutex); - LocalVector<WorkerThreadPool::TaskID> finished_task_ids; + LocalVector<WorkerThreadPool::TaskID> finished_task_ids; - for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) { - if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) { - WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); - finished_task_ids.push_back(E.key); + for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) { + if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); + finished_task_ids.push_back(E.key); - NavMeshGeneratorTask3D *generator_task = E.value; - DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED); + NavMeshGeneratorTask3D *generator_task = E.value; + DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED); - baking_navmeshes.erase(generator_task->navigation_mesh); - if (generator_task->callback.is_valid()) { - generator_emit_callback(generator_task->callback); + baking_navmeshes.erase(generator_task->navigation_mesh); + if (generator_task->callback.is_valid()) { + generator_emit_callback(generator_task->callback); + } + memdelete(generator_task); } - memdelete(generator_task); } - } - for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) { - generator_tasks.erase(finished_task_id); + for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) { + generator_tasks.erase(finished_task_id); + } } - - generator_task_mutex.unlock(); - baking_navmesh_mutex.unlock(); } void NavMeshGenerator3D::cleanup() { - baking_navmesh_mutex.lock(); - generator_task_mutex.lock(); + MutexLock baking_navmesh_lock(baking_navmesh_mutex); + { + MutexLock generator_task_lock(generator_task_mutex); - baking_navmeshes.clear(); + baking_navmeshes.clear(); - for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) { - WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); - NavMeshGeneratorTask3D *generator_task = E.value; - memdelete(generator_task); - } - generator_tasks.clear(); + for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); + NavMeshGeneratorTask3D *generator_task = E.value; + memdelete(generator_task); + } + generator_tasks.clear(); - generator_rid_rwlock.write_lock(); - for (NavMeshGeometryParser3D *parser : generator_parsers) { - generator_parser_owner.free(parser->self); + generator_rid_rwlock.write_lock(); + for (NavMeshGeometryParser3D *parser : generator_parsers) { + generator_parser_owner.free(parser->self); + } + generator_parsers.clear(); + generator_rid_rwlock.write_unlock(); } - generator_parsers.clear(); - generator_rid_rwlock.write_unlock(); - - generator_task_mutex.unlock(); - baking_navmesh_mutex.unlock(); } void NavMeshGenerator3D::finish() { @@ -226,7 +224,7 @@ void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh baking_navmeshes.insert(p_navigation_mesh); baking_navmesh_mutex.unlock(); - generator_task_mutex.lock(); + MutexLock generator_task_lock(generator_task_mutex); NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D); generator_task->navigation_mesh = p_navigation_mesh; generator_task->source_geometry_data = p_source_geometry_data; @@ -234,14 +232,11 @@ void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED; generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D")); generator_tasks.insert(generator_task->thread_task_id, generator_task); - generator_task_mutex.unlock(); } bool NavMeshGenerator3D::is_baking(Ref<NavigationMesh> p_navigation_mesh) { - baking_navmesh_mutex.lock(); - bool baking = baking_navmeshes.has(p_navigation_mesh); - baking_navmesh_mutex.unlock(); - return baking; + MutexLock baking_navmesh_lock(baking_navmesh_mutex); + return baking_navmeshes.has(p_navigation_mesh); } void NavMeshGenerator3D::generator_thread_bake(void *p_arg) { diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index 82e8854b7a..c18ee7155b 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -36,6 +36,7 @@ #include "core/math/math_defs.h" #include "core/object/worker_thread_pool.h" +#include "servers/navigation/navigation_globals.h" #include <KdTree2d.h> #include <KdTree3d.h> @@ -55,21 +56,21 @@ class NavMap : public NavRid { /// To find the polygons edges the vertices are displaced in a grid where /// each cell has the following cell_size and cell_height. - real_t cell_size = 0.25; // Must match ProjectSettings default 3D cell_size and NavigationMesh cell_size. - real_t cell_height = 0.25; // Must match ProjectSettings default 3D cell_height and NavigationMesh cell_height. + real_t cell_size = NavigationDefaults3D::navmesh_cell_size; + real_t cell_height = NavigationDefaults3D::navmesh_cell_height; // For the inter-region merging to work, internal rasterization is performed. - float merge_rasterizer_cell_size = 0.25; - float merge_rasterizer_cell_height = 0.25; + float merge_rasterizer_cell_size = NavigationDefaults3D::navmesh_cell_size; + float merge_rasterizer_cell_height = NavigationDefaults3D::navmesh_cell_height; // This value is used to control sensitivity of internal rasterizer. float merge_rasterizer_cell_scale = 1.0; bool use_edge_connections = true; /// This value is used to detect the near edges to connect. - real_t edge_connection_margin = 0.25; + real_t edge_connection_margin = NavigationDefaults3D::edge_connection_margin; /// This value is used to limit how far links search to find polygons to connect to. - real_t link_connection_radius = 1.0; + real_t link_connection_radius = NavigationDefaults3D::link_connection_radius; bool regenerate_polygons = true; bool regenerate_links = true; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index d0c22e9e4d..4bf09d3c84 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -136,11 +136,12 @@ hb_position_t TextServerAdvanced::_bmp_get_glyph_h_advance(hb_font_t *p_font, vo return 0; } - if (!bm_font->face->glyph_map.has(p_glyph)) { + HashMap<int32_t, FontGlyph>::Iterator E = bm_font->face->glyph_map.find(p_glyph); + if (!E) { return 0; } - return bm_font->face->glyph_map[p_glyph].advance.x * 64; + return E->value.advance.x * 64; } hb_position_t TextServerAdvanced::_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) { @@ -150,11 +151,12 @@ hb_position_t TextServerAdvanced::_bmp_get_glyph_v_advance(hb_font_t *p_font, vo return 0; } - if (!bm_font->face->glyph_map.has(p_glyph)) { + HashMap<int32_t, FontGlyph>::Iterator E = bm_font->face->glyph_map.find(p_glyph); + if (!E) { return 0; } - return -bm_font->face->glyph_map[p_glyph].advance.y * 64; + return -E->value.advance.y * 64; } hb_position_t TextServerAdvanced::_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) { @@ -178,11 +180,12 @@ hb_bool_t TextServerAdvanced::_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p return false; } - if (!bm_font->face->glyph_map.has(p_glyph)) { + HashMap<int32_t, FontGlyph>::Iterator E = bm_font->face->glyph_map.find(p_glyph); + if (!E) { return false; } - *r_x = bm_font->face->glyph_map[p_glyph].advance.x * 32; + *r_x = E->value.advance.x * 32; *r_y = -bm_font->face->ascent * 64; return true; @@ -195,14 +198,15 @@ hb_bool_t TextServerAdvanced::_bmp_get_glyph_extents(hb_font_t *p_font, void *p_ return false; } - if (!bm_font->face->glyph_map.has(p_glyph)) { + HashMap<int32_t, FontGlyph>::Iterator E = bm_font->face->glyph_map.find(p_glyph); + if (!E) { return false; } r_extents->x_bearing = 0; r_extents->y_bearing = 0; - r_extents->width = bm_font->face->glyph_map[p_glyph].rect.size.x * 64; - r_extents->height = bm_font->face->glyph_map[p_glyph].rect.size.y * 64; + r_extents->width = E->value.rect.size.x * 64; + r_extents->height = E->value.rect.size.y * 64; return true; } @@ -1188,18 +1192,21 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma /* Font Cache */ /*************************************************************************/ -_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const { - ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false); +_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph) const { + FontForSizeAdvanced *fd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size, fd), false); int32_t glyph_index = p_glyph & 0xffffff; // Remove subpixel shifts. - FontForSizeAdvanced *fd = p_font_data->cache[p_size]; - if (fd->glyph_map.has(p_glyph)) { - return fd->glyph_map[p_glyph].found; + HashMap<int32_t, FontGlyph>::Iterator E = fd->glyph_map.find(p_glyph); + if (E) { + r_glyph = E->value; + return E->value.found; } if (glyph_index == 0) { // Non graphical or invalid glyph, do not render. - fd->glyph_map[p_glyph] = FontGlyph(); + E = fd->glyph_map.insert(p_glyph, FontGlyph()); + r_glyph = E->value; return true; } @@ -1235,7 +1242,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, int error = FT_Load_Glyph(fd->face, glyph_index, flags); if (error) { - fd->glyph_map[p_glyph] = FontGlyph(); + E = fd->glyph_map.insert(p_glyph, FontGlyph()); + r_glyph = E->value; return false; } @@ -1339,17 +1347,22 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, cleanup_stroker: FT_Stroker_Done(stroker); } - fd->glyph_map[p_glyph] = gl; + E = fd->glyph_map.insert(p_glyph, gl); + r_glyph = E->value; return gl.found; } #endif - fd->glyph_map[p_glyph] = FontGlyph(); + E = fd->glyph_map.insert(p_glyph, FontGlyph()); + r_glyph = E->value; return false; } -_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const { +_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size, FontForSizeAdvanced *&r_cache_for_size) const { ERR_FAIL_COND_V(p_size.x <= 0, false); - if (p_font_data->cache.has(p_size)) { + + HashMap<Vector2i, FontForSizeAdvanced *>::Iterator E = p_font_data->cache.find(p_size); + if (E) { + r_cache_for_size = E->value; return true; } @@ -1840,7 +1853,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f // Init bitmap font. fd->hb_handle = _bmp_font_create(fd, nullptr); } - p_font_data->cache[p_size] = fd; + p_font_data->cache.insert(p_size, fd); + r_cache_for_size = fd; return true; } @@ -1864,9 +1878,10 @@ hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_ MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), nullptr); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), nullptr); - return fd->cache[size]->hb_handle; + return ffsd->hb_handle; } RID TextServerAdvanced::_create_font() { @@ -1989,7 +2004,8 @@ void TextServerAdvanced::_font_set_style(const RID &p_font_rid, BitField<FontSty MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->style_flags = p_style; } @@ -1999,7 +2015,8 @@ BitField<TextServer::FontStyle> TextServerAdvanced::_font_get_style(const RID &p MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0); return fd->style_flags; } @@ -2009,7 +2026,8 @@ void TextServerAdvanced::_font_set_style_name(const RID &p_font_rid, const Strin MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->style_name = p_name; } @@ -2019,7 +2037,8 @@ String TextServerAdvanced::_font_get_style_name(const RID &p_font_rid) const { MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), String()); return fd->style_name; } @@ -2029,7 +2048,8 @@ void TextServerAdvanced::_font_set_weight(const RID &p_font_rid, int64_t p_weigh MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->weight = CLAMP(p_weight, 100, 999); } @@ -2039,7 +2059,8 @@ int64_t TextServerAdvanced::_font_get_weight(const RID &p_font_rid) const { MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 400); return fd->weight; } @@ -2049,7 +2070,8 @@ void TextServerAdvanced::_font_set_stretch(const RID &p_font_rid, int64_t p_stre MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->stretch = CLAMP(p_stretch, 50, 200); } @@ -2059,7 +2081,8 @@ int64_t TextServerAdvanced::_font_get_stretch(const RID &p_font_rid) const { MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 100); return fd->stretch; } @@ -2069,7 +2092,8 @@ void TextServerAdvanced::_font_set_name(const RID &p_font_rid, const String &p_n MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->font_name = p_name; } @@ -2079,7 +2103,8 @@ String TextServerAdvanced::_font_get_name(const RID &p_font_rid) const { MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), String()); return fd->font_name; } @@ -2089,9 +2114,10 @@ Dictionary TextServerAdvanced::_font_get_ot_name_strings(const RID &p_font_rid) MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary()); - hb_face_t *hb_face = hb_font_get_face(fd->cache[size]->hb_handle); + hb_face_t *hb_face = hb_font_get_face(ffsd->hb_handle); unsigned int num_entries = 0; const hb_ot_name_entry_t *names = hb_ot_name_list_names(hb_face, &num_entries); @@ -2599,8 +2625,9 @@ void TextServerAdvanced::_font_set_ascent(const RID &p_font_rid, int64_t p_size, MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->ascent = p_ascent; + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->ascent = p_ascent; } double TextServerAdvanced::_font_get_ascent(const RID &p_font_rid, int64_t p_size) const { @@ -2610,18 +2637,19 @@ double TextServerAdvanced::_font_get_ascent(const RID &p_font_rid, int64_t p_siz MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->ascent * (double)p_size / (double)fd->msdf_source_size; + return ffsd->ascent * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->ascent * (double)p_size / (double)fd->fixed_size; + return ffsd->ascent * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->ascent * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->ascent * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->ascent; + return ffsd->ascent; } } @@ -2631,8 +2659,9 @@ void TextServerAdvanced::_font_set_descent(const RID &p_font_rid, int64_t p_size Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->descent = p_descent; + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->descent = p_descent; } double TextServerAdvanced::_font_get_descent(const RID &p_font_rid, int64_t p_size) const { @@ -2642,18 +2671,19 @@ double TextServerAdvanced::_font_get_descent(const RID &p_font_rid, int64_t p_si MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->descent * (double)p_size / (double)fd->msdf_source_size; + return ffsd->descent * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->descent * (double)p_size / (double)fd->fixed_size; + return ffsd->descent * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->descent * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->descent * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->descent; + return ffsd->descent; } } @@ -2664,8 +2694,9 @@ void TextServerAdvanced::_font_set_underline_position(const RID &p_font_rid, int MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->underline_position = p_underline_position; + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->underline_position = p_underline_position; } double TextServerAdvanced::_font_get_underline_position(const RID &p_font_rid, int64_t p_size) const { @@ -2675,18 +2706,19 @@ double TextServerAdvanced::_font_get_underline_position(const RID &p_font_rid, i MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->underline_position * (double)p_size / (double)fd->msdf_source_size; + return ffsd->underline_position * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->underline_position * (double)p_size / (double)fd->fixed_size; + return ffsd->underline_position * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->underline_position * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->underline_position * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->underline_position; + return ffsd->underline_position; } } @@ -2697,8 +2729,9 @@ void TextServerAdvanced::_font_set_underline_thickness(const RID &p_font_rid, in MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->underline_thickness = p_underline_thickness; + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->underline_thickness = p_underline_thickness; } double TextServerAdvanced::_font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const { @@ -2708,18 +2741,19 @@ double TextServerAdvanced::_font_get_underline_thickness(const RID &p_font_rid, MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->msdf_source_size; + return ffsd->underline_thickness * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->fixed_size; + return ffsd->underline_thickness * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->underline_thickness * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->underline_thickness * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->underline_thickness; + return ffsd->underline_thickness; } } @@ -2730,13 +2764,15 @@ void TextServerAdvanced::_font_set_scale(const RID &p_font_rid, int64_t p_size, MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face) { + if (ffsd->face) { return; // Do not override scale for dynamic fonts, it's calculated automatically. } #endif - fd->cache[size]->scale = p_scale; + ffsd->scale = p_scale; } double TextServerAdvanced::_font_get_scale(const RID &p_font_rid, int64_t p_size) const { @@ -2746,18 +2782,19 @@ double TextServerAdvanced::_font_get_scale(const RID &p_font_rid, int64_t p_size MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->scale * (double)p_size / (double)fd->msdf_source_size; + return ffsd->scale * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->scale * (double)p_size / (double)fd->fixed_size; + return ffsd->scale * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->scale * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->scale * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->scale / fd->cache[size]->oversampling; + return ffsd->scale / ffsd->oversampling; } } @@ -2768,9 +2805,10 @@ int64_t TextServerAdvanced::_font_get_texture_count(const RID &p_font_rid, const MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0); - return fd->cache[size]->textures.size(); + return ffsd->textures.size(); } void TextServerAdvanced::_font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) { @@ -2779,8 +2817,9 @@ void TextServerAdvanced::_font_clear_textures(const RID &p_font_rid, const Vecto MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->textures.clear(); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->textures.clear(); } void TextServerAdvanced::_font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) { @@ -2789,10 +2828,11 @@ void TextServerAdvanced::_font_remove_texture(const RID &p_font_rid, const Vecto MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ERR_FAIL_INDEX(p_texture_index, ffsd->textures.size()); - fd->cache[size]->textures.remove_at(p_texture_index); + ffsd->textures.remove_at(p_texture_index); } void TextServerAdvanced::_font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) { @@ -2802,13 +2842,14 @@ void TextServerAdvanced::_font_set_texture_image(const RID &p_font_rid, const Ve MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); ERR_FAIL_COND(p_texture_index < 0); - if (p_texture_index >= fd->cache[size]->textures.size()) { - fd->cache[size]->textures.resize(p_texture_index + 1); + if (p_texture_index >= ffsd->textures.size()) { + ffsd->textures.resize(p_texture_index + 1); } - ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + ShelfPackTexture &tex = ffsd->textures.write[p_texture_index]; tex.image = p_image; tex.texture_w = p_image->get_width(); @@ -2829,10 +2870,11 @@ Ref<Image> TextServerAdvanced::_font_get_texture_image(const RID &p_font_rid, co MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); - ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Ref<Image>()); + ERR_FAIL_INDEX_V(p_texture_index, ffsd->textures.size(), Ref<Image>()); - const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index]; + const ShelfPackTexture &tex = ffsd->textures[p_texture_index]; return tex.image; } @@ -2843,13 +2885,14 @@ void TextServerAdvanced::_font_set_texture_offsets(const RID &p_font_rid, const MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); ERR_FAIL_COND(p_texture_index < 0); - if (p_texture_index >= fd->cache[size]->textures.size()) { - fd->cache[size]->textures.resize(p_texture_index + 1); + if (p_texture_index >= ffsd->textures.size()) { + ffsd->textures.resize(p_texture_index + 1); } - ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + ShelfPackTexture &tex = ffsd->textures.write[p_texture_index]; tex.shelves.clear(); for (int32_t i = 0; i < p_offsets.size(); i += 4) { tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3])); @@ -2862,10 +2905,11 @@ PackedInt32Array TextServerAdvanced::_font_get_texture_offsets(const RID &p_font MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); - ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), PackedInt32Array()); + ERR_FAIL_INDEX_V(p_texture_index, ffsd->textures.size(), PackedInt32Array()); - const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index]; + const ShelfPackTexture &tex = ffsd->textures[p_texture_index]; PackedInt32Array ret; ret.resize(tex.shelves.size() * 4); @@ -2887,10 +2931,11 @@ PackedInt32Array TextServerAdvanced::_font_get_glyph_list(const RID &p_font_rid, MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), PackedInt32Array()); PackedInt32Array ret; - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + const HashMap<int32_t, FontGlyph> &gl = ffsd->glyph_map; for (const KeyValue<int32_t, FontGlyph> &E : gl) { ret.push_back(E.key); } @@ -2903,9 +2948,10 @@ void TextServerAdvanced::_font_clear_glyphs(const RID &p_font_rid, const Vector2 MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - fd->cache[size]->glyph_map.clear(); + ffsd->glyph_map.clear(); } void TextServerAdvanced::_font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) { @@ -2914,9 +2960,10 @@ void TextServerAdvanced::_font_remove_glyph(const RID &p_font_rid, const Vector2 MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - fd->cache[size]->glyph_map.erase(p_glyph); + ffsd->glyph_map.erase(p_glyph); } double TextServerAdvanced::_get_extra_advance(RID p_font_rid, int p_font_size) const { @@ -2940,22 +2987,22 @@ Vector2 TextServerAdvanced::_font_get_glyph_advance(const RID &p_font_rid, int64 MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - Vector2 ea; if (fd->embolden != 0.0) { ea.x = fd->embolden * double(size.x) / 64.0; @@ -2963,17 +3010,17 @@ Vector2 TextServerAdvanced::_font_get_glyph_advance(const RID &p_font_rid, int64 double scale = _font_get_scale(p_font_rid, p_size); if (fd->msdf) { - return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size; + return (fgl.advance + ea) * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->fixed_size; + return (fgl.advance + ea) * (double)p_size / (double)fd->fixed_size; } else { - return (gl[p_glyph | mod].advance + ea) * Math::round((double)p_size / (double)fd->fixed_size); + return (fgl.advance + ea) * Math::round((double)p_size / (double)fd->fixed_size); } } else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE))) { - return (gl[p_glyph | mod].advance + ea).round(); + return (fgl.advance + ea).round(); } else { - return gl[p_glyph | mod].advance + ea; + return fgl.advance + ea; } } @@ -2984,12 +3031,13 @@ void TextServerAdvanced::_font_set_glyph_advance(const RID &p_font_rid, int64_t MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].advance = p_advance; - gl[p_glyph].found = true; + fgl.advance = p_advance; + fgl.found = true; } Vector2 TextServerAdvanced::_font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -2999,32 +3047,32 @@ Vector2 TextServerAdvanced::_font_get_glyph_offset(const RID &p_font_rid, const MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - if (fd->msdf) { - return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size; + return fgl.rect.position * (double)p_size.x / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->fixed_size; + return fgl.rect.position * (double)p_size.x / (double)fd->fixed_size; } else { - return gl[p_glyph | mod].rect.position * Math::round((double)p_size.x / (double)fd->fixed_size); + return fgl.rect.position * Math::round((double)p_size.x / (double)fd->fixed_size); } } else { - return gl[p_glyph | mod].rect.position; + return fgl.rect.position; } } @@ -3035,12 +3083,13 @@ void TextServerAdvanced::_font_set_glyph_offset(const RID &p_font_rid, const Vec MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].rect.position = p_offset; - gl[p_glyph].found = true; + fgl.rect.position = p_offset; + fgl.found = true; } Vector2 TextServerAdvanced::_font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -3050,32 +3099,32 @@ Vector2 TextServerAdvanced::_font_get_glyph_size(const RID &p_font_rid, const Ve MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - if (fd->msdf) { - return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size; + return fgl.rect.size * (double)p_size.x / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->fixed_size; + return fgl.rect.size * (double)p_size.x / (double)fd->fixed_size; } else { - return gl[p_glyph | mod].rect.size * Math::round((double)p_size.x / (double)fd->fixed_size); + return fgl.rect.size * Math::round((double)p_size.x / (double)fd->fixed_size); } } else { - return gl[p_glyph | mod].rect.size; + return fgl.rect.size; } } @@ -3086,12 +3135,13 @@ void TextServerAdvanced::_font_set_glyph_size(const RID &p_font_rid, const Vecto MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].rect.size = p_gl_size; - gl[p_glyph].found = true; + fgl.rect.size = p_gl_size; + fgl.found = true; } Rect2 TextServerAdvanced::_font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -3101,22 +3151,23 @@ Rect2 TextServerAdvanced::_font_get_glyph_uv_rect(const RID &p_font_rid, const V MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Rect2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Rect2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph | mod].uv_rect; + return fgl.uv_rect; } void TextServerAdvanced::_font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) { @@ -3126,12 +3177,13 @@ void TextServerAdvanced::_font_set_glyph_uv_rect(const RID &p_font_rid, const Ve MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].uv_rect = p_uv_rect; - gl[p_glyph].found = true; + fgl.uv_rect = p_uv_rect; + fgl.found = true; } int64_t TextServerAdvanced::_font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -3141,22 +3193,23 @@ int64_t TextServerAdvanced::_font_get_glyph_texture_idx(const RID &p_font_rid, c MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), -1); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return -1; // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph | mod].texture_idx; + return fgl.texture_idx; } void TextServerAdvanced::_font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) { @@ -3166,12 +3219,13 @@ void TextServerAdvanced::_font_set_glyph_texture_idx(const RID &p_font_rid, cons MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].texture_idx = p_texture_idx; - gl[p_glyph].found = true; + fgl.texture_idx = p_texture_idx; + fgl.found = true; } RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -3181,27 +3235,28 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), RID()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), RID()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return RID(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID()); + ERR_FAIL_COND_V(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size(), RID()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph | mod].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { - ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; + if (fgl.texture_idx != -1) { + if (ffsd->textures[fgl.texture_idx].dirty) { + ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref<Image> img = tex.image; if (fd->mipmaps && !img->has_mipmaps()) { img = tex.image->duplicate(); @@ -3214,7 +3269,7 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid(); + return ffsd->textures[fgl.texture_idx].texture->get_rid(); } } @@ -3228,27 +3283,28 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Size2()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Size2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Size2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2()); + ERR_FAIL_COND_V(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size(), Size2()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph | mod].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { - ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; + if (fgl.texture_idx != -1) { + if (ffsd->textures[fgl.texture_idx].dirty) { + ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref<Image> img = tex.image; if (fd->mipmaps && !img->has_mipmaps()) { img = tex.image->duplicate(); @@ -3261,7 +3317,7 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size(); + return ffsd->textures[fgl.texture_idx].texture->get_size(); } } @@ -3275,7 +3331,8 @@ Dictionary TextServerAdvanced::_font_get_glyph_contours(const RID &p_font_rid, i MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary()); #ifdef MODULE_FREETYPE_ENABLED PackedVector3Array points; @@ -3283,20 +3340,20 @@ Dictionary TextServerAdvanced::_font_get_glyph_contours(const RID &p_font_rid, i int32_t index = p_index & 0xffffff; // Remove subpixel shifts. - int error = FT_Load_Glyph(fd->cache[size]->face, index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + int error = FT_Load_Glyph(ffsd->face, index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); ERR_FAIL_COND_V(error, Dictionary()); if (fd->embolden != 0.f) { FT_Pos strength = fd->embolden * p_size * 4; // 26.6 fractional units (1 / 64). - FT_Outline_Embolden(&fd->cache[size]->face->glyph->outline, strength); + FT_Outline_Embolden(&ffsd->face->glyph->outline, strength); } if (fd->transform != Transform2D()) { FT_Matrix mat = { FT_Fixed(fd->transform[0][0] * 65536), FT_Fixed(fd->transform[0][1] * 65536), FT_Fixed(fd->transform[1][0] * 65536), FT_Fixed(fd->transform[1][1] * 65536) }; // 16.16 fractional units (1 / 65536). - FT_Outline_Transform(&fd->cache[size]->face->glyph->outline, &mat); + FT_Outline_Transform(&ffsd->face->glyph->outline, &mat); } - double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale; + double scale = (1.0 / 64.0) / ffsd->oversampling * ffsd->scale; if (fd->msdf) { scale = scale * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { @@ -3306,13 +3363,13 @@ Dictionary TextServerAdvanced::_font_get_glyph_contours(const RID &p_font_rid, i scale = scale * Math::round((double)p_size / (double)fd->fixed_size); } } - for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) { - points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i]))); + for (short i = 0; i < ffsd->face->glyph->outline.n_points; i++) { + points.push_back(Vector3(ffsd->face->glyph->outline.points[i].x * scale, -ffsd->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(ffsd->face->glyph->outline.tags[i]))); } - for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) { - contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]); + for (short i = 0; i < ffsd->face->glyph->outline.n_contours; i++) { + contours.push_back(ffsd->face->glyph->outline.contours[i]); } - bool orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); + bool orientation = (FT_Outline_Get_Orientation(&ffsd->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); Dictionary out; out["points"] = points; @@ -3331,7 +3388,8 @@ TypedArray<Vector2i> TextServerAdvanced::_font_get_kerning_list(const RID &p_fon MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), TypedArray<Vector2i>()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), TypedArray<Vector2i>()); TypedArray<Vector2i> ret; for (const KeyValue<Vector2i, Vector2> &E : fd->cache[size]->kerning_map) { @@ -3347,8 +3405,9 @@ void TextServerAdvanced::_font_clear_kerning_map(const RID &p_font_rid, int64_t MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->kerning_map.clear(); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->kerning_map.clear(); } void TextServerAdvanced::_font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) { @@ -3358,8 +3417,9 @@ void TextServerAdvanced::_font_remove_kerning(const RID &p_font_rid, int64_t p_s MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->kerning_map.erase(p_glyph_pair); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->kerning_map.erase(p_glyph_pair); } void TextServerAdvanced::_font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { @@ -3369,8 +3429,9 @@ void TextServerAdvanced::_font_set_kerning(const RID &p_font_rid, int64_t p_size MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning; + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->kerning_map[p_glyph_pair] = p_kerning; } Vector2 TextServerAdvanced::_font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const { @@ -3380,9 +3441,10 @@ Vector2 TextServerAdvanced::_font_get_kerning(const RID &p_font_rid, int64_t p_s MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2()); - const HashMap<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map; + const HashMap<Vector2i, Vector2> &kern = ffsd->kerning_map; if (kern.has(p_glyph_pair)) { if (fd->msdf) { @@ -3398,9 +3460,9 @@ Vector2 TextServerAdvanced::_font_get_kerning(const RID &p_font_rid, int64_t p_s } } else { #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face) { + if (ffsd->face) { FT_Vector delta; - FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta); + FT_Get_Kerning(ffsd->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta); if (fd->msdf) { return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { @@ -3426,14 +3488,15 @@ int64_t TextServerAdvanced::_font_get_glyph_index(const RID &p_font_rid, int64_t MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0); #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face) { + if (ffsd->face) { if (p_variation_selector) { - return FT_Face_GetCharVariantIndex(fd->cache[size]->face, p_char, p_variation_selector); + return FT_Face_GetCharVariantIndex(ffsd->face, p_char, p_variation_selector); } else { - return FT_Get_Char_Index(fd->cache[size]->face, p_char); + return FT_Get_Char_Index(ffsd->face, p_char); } } else { return (int64_t)p_char; @@ -3449,23 +3512,24 @@ int64_t TextServerAdvanced::_font_get_char_from_glyph_index(const RID &p_font_ri MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0); #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->inv_glyph_map.is_empty()) { - FT_Face face = fd->cache[size]->face; + if (ffsd->inv_glyph_map.is_empty()) { + FT_Face face = ffsd->face; FT_UInt gindex; FT_ULong charcode = FT_Get_First_Char(face, &gindex); while (gindex != 0) { if (charcode != 0) { - fd->cache[size]->inv_glyph_map[gindex] = charcode; + ffsd->inv_glyph_map[gindex] = charcode; } charcode = FT_Get_Next_Char(face, charcode, &gindex); } } - if (fd->cache[size]->inv_glyph_map.has(p_glyph_index)) { - return fd->cache[size]->inv_glyph_map[p_glyph_index]; + if (ffsd->inv_glyph_map.has(p_glyph_index)) { + return ffsd->inv_glyph_map[p_glyph_index]; } else { return 0; } @@ -3482,17 +3546,19 @@ bool TextServerAdvanced::_font_has_char(const RID &p_font_rid, int64_t p_char) c } MutexLock lock(fd->mutex); + FontForSizeAdvanced *ffsd = nullptr; if (fd->cache.is_empty()) { - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0), ffsd), false); + } else { + ffsd = fd->cache.begin()->value; } - FontForSizeAdvanced *at_size = fd->cache.begin()->value; #ifdef MODULE_FREETYPE_ENABLED - if (at_size && at_size->face) { - return FT_Get_Char_Index(at_size->face, p_char) != 0; + if (ffsd->face) { + return FT_Get_Char_Index(ffsd->face, p_char) != 0; } #endif - return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false; + return ffsd->glyph_map.has((int32_t)p_char); } String TextServerAdvanced::_font_get_supported_chars(const RID &p_font_rid) const { @@ -3500,30 +3566,30 @@ String TextServerAdvanced::_font_get_supported_chars(const RID &p_font_rid) cons ERR_FAIL_NULL_V(fd, String()); MutexLock lock(fd->mutex); + FontForSizeAdvanced *ffsd = nullptr; if (fd->cache.is_empty()) { - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String()); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0), ffsd), String()); + } else { + ffsd = fd->cache.begin()->value; } - FontForSizeAdvanced *at_size = fd->cache.begin()->value; String chars; #ifdef MODULE_FREETYPE_ENABLED - if (at_size && at_size->face) { + if (ffsd->face) { FT_UInt gindex; - FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + FT_ULong charcode = FT_Get_First_Char(ffsd->face, &gindex); while (gindex != 0) { if (charcode != 0) { chars = chars + String::chr(charcode); } - charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + charcode = FT_Get_Next_Char(ffsd->face, charcode, &gindex); } return chars; } #endif - if (at_size) { - const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map; - for (const KeyValue<int32_t, FontGlyph> &E : gl) { - chars = chars + String::chr(E.key); - } + const HashMap<int32_t, FontGlyph> &gl = ffsd->glyph_map; + for (const KeyValue<int32_t, FontGlyph> &E : gl) { + chars = chars + String::chr(E.key); } return chars; } @@ -3533,10 +3599,12 @@ PackedInt32Array TextServerAdvanced::_font_get_supported_glyphs(const RID &p_fon ERR_FAIL_NULL_V(fd, PackedInt32Array()); MutexLock lock(fd->mutex); + FontForSizeAdvanced *at_size = nullptr; if (fd->cache.is_empty()) { - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array()); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0), at_size), PackedInt32Array()); + } else { + at_size = fd->cache.begin()->value; } - FontForSizeAdvanced *at_size = fd->cache.begin()->value; PackedInt32Array glyphs; #ifdef MODULE_FREETYPE_ENABLED @@ -3567,25 +3635,27 @@ void TextServerAdvanced::_font_render_range(const RID &p_font_rid, const Vector2 MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); for (int64_t i = p_start; i <= p_end; i++) { #ifdef MODULE_FREETYPE_ENABLED - int32_t idx = FT_Get_Char_Index(fd->cache[size]->face, i); - if (fd->cache[size]->face) { + int32_t idx = FT_Get_Char_Index(ffsd->face, i); + if (ffsd->face) { + FontGlyph fgl; if (fd->msdf) { - _ensure_glyph(fd, size, (int32_t)idx); + _ensure_glyph(fd, size, (int32_t)idx, fgl); } else { for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24), fgl); } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl); } else { - _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24), fgl); } } } @@ -3600,24 +3670,26 @@ void TextServerAdvanced::_font_render_glyph(const RID &p_font_rid, const Vector2 MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); #ifdef MODULE_FREETYPE_ENABLED int32_t idx = p_index & 0xffffff; // Remove subpixel shifts. - if (fd->cache[size]->face) { + if (ffsd->face) { + FontGlyph fgl; if (fd->msdf) { - _ensure_glyph(fd, size, (int32_t)idx); + _ensure_glyph(fd, size, (int32_t)idx, fgl); } else { for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24), fgl); } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl); } else { - _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24), fgl); } } } @@ -3634,16 +3706,17 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED - if (!fd->msdf && fd->cache[size]->face) { + if (!fd->msdf && ffsd->face) { // LCD layout, bits 24, 25, 26 if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { lcd_aa = true; index = index | (layout << 24); @@ -3660,24 +3733,24 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca } #endif - if (!_ensure_glyph(fd, size, index)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, index, fgl)) { return; // Invalid or non-graphical glyph, do not display errors, nothing to draw. } - const FontGlyph &gl = fd->cache[size]->glyph_map[index]; - if (gl.found) { - ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + if (fgl.found) { + ERR_FAIL_COND(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size()); - if (gl.texture_idx != -1) { + if (fgl.texture_idx != -1) { Color modulate = p_color; #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face && fd->cache[size]->textures[gl.texture_idx].image.is_valid() && (fd->cache[size]->textures[gl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { + if (ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { modulate.r = modulate.g = modulate.b = 1.0; } #endif if (RenderingServer::get_singleton() != nullptr) { - if (fd->cache[size]->textures[gl.texture_idx].dirty) { - ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; + if (ffsd->textures[fgl.texture_idx].dirty) { + ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref<Image> img = tex.image; if (fd->mipmaps && !img->has_mipmaps()) { img = tex.image->duplicate(); @@ -3690,12 +3763,12 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca } tex.dirty = false; } - RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + RID texture = ffsd->textures[fgl.texture_idx].texture->get_rid(); if (fd->msdf) { Point2 cpos = p_pos; - cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; - Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size; + Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { double scale = _font_get_scale(p_font_rid, p_size); Point2 cpos = p_pos; @@ -3708,8 +3781,8 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca cpos.y = Math::floor(cpos.y); cpos.x = Math::floor(cpos.x); } - Vector2 gpos = gl.rect.position; - Size2 csize = gl.rect.size; + Vector2 gpos = fgl.rect.position; + Size2 csize = fgl.rect.size; if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { double gl_scale = (double)p_size / (double)fd->fixed_size; @@ -3723,9 +3796,9 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca } cpos += gpos; if (lcd_aa) { - RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate); } else { - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false); } } } @@ -3742,16 +3815,17 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size)); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED - if (!fd->msdf && fd->cache[size]->face) { + if (!fd->msdf && ffsd->face) { // LCD layout, bits 24, 25, 26 if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { lcd_aa = true; index = index | (layout << 24); @@ -3768,24 +3842,24 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R } #endif - if (!_ensure_glyph(fd, size, index)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, index, fgl)) { return; // Invalid or non-graphical glyph, do not display errors, nothing to draw. } - const FontGlyph &gl = fd->cache[size]->glyph_map[index]; - if (gl.found) { - ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + if (fgl.found) { + ERR_FAIL_COND(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size()); - if (gl.texture_idx != -1) { + if (fgl.texture_idx != -1) { Color modulate = p_color; #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face && fd->cache[size]->textures[gl.texture_idx].image.is_valid() && (fd->cache[size]->textures[gl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { + if (ffsd->face && fd->cache[size]->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { modulate.r = modulate.g = modulate.b = 1.0; } #endif if (RenderingServer::get_singleton() != nullptr) { - if (fd->cache[size]->textures[gl.texture_idx].dirty) { - ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; + if (ffsd->textures[fgl.texture_idx].dirty) { + ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref<Image> img = tex.image; if (fd->mipmaps && !img->has_mipmaps()) { img = tex.image->duplicate(); @@ -3798,12 +3872,12 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R } tex.dirty = false; } - RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + RID texture = ffsd->textures[fgl.texture_idx].texture->get_rid(); if (fd->msdf) { Point2 cpos = p_pos; - cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; - Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size; + Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { Point2 cpos = p_pos; double scale = _font_get_scale(p_font_rid, p_size); @@ -3816,8 +3890,8 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R cpos.y = Math::floor(cpos.y); cpos.x = Math::floor(cpos.x); } - Vector2 gpos = gl.rect.position; - Size2 csize = gl.rect.size; + Vector2 gpos = fgl.rect.position; + Size2 csize = fgl.rect.size; if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { double gl_scale = (double)p_size / (double)fd->fixed_size; @@ -3831,9 +3905,9 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R } cpos += gpos; if (lcd_aa) { - RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate); } else { - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false); } } } @@ -3898,7 +3972,8 @@ bool TextServerAdvanced::_font_is_script_supported(const RID &p_font_rid, const return fd->script_support_overrides[p_script]; } else { Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), false); return fd->supported_scripts.has(hb_tag_from_string(p_script.ascii().get_data(), -1)); } } @@ -3945,7 +4020,8 @@ void TextServerAdvanced::_font_set_opentype_feature_overrides(const RID &p_font_ MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->feature_overrides = p_overrides; } @@ -3963,7 +4039,8 @@ Dictionary TextServerAdvanced::_font_supported_feature_list(const RID &p_font_ri MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary()); return fd->supported_features; } @@ -3973,7 +4050,8 @@ Dictionary TextServerAdvanced::_font_supported_variation_list(const RID &p_font_ MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + FontForSizeAdvanced *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary()); return fd->supported_varaitions; } @@ -4048,7 +4126,7 @@ int64_t TextServerAdvanced::_convert_pos_inv(const ShapedTextDataAdvanced *p_sd, } void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped, bool p_text) { - p_shaped->valid = false; + p_shaped->valid.clear(); p_shaped->sort_valid = false; p_shaped->line_breaks_valid = false; p_shaped->justification_ops_valid = false; @@ -4404,7 +4482,7 @@ bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const V sd->objects[p_key].rect.size = p_size; sd->objects[p_key].inline_align = p_inline_align; sd->objects[p_key].baseline = p_baseline; - if (sd->valid) { + if (sd->valid.is_set()) { // Recalc string metrics. sd->ascent = 0; sd->descent = 0; @@ -4548,7 +4626,7 @@ RID TextServerAdvanced::_shaped_text_substr(const RID &p_shaped, int64_t p_start if (sd->parent != RID()) { return _shaped_text_substr(sd->parent, p_start, p_length); } - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID()); @@ -4576,7 +4654,7 @@ RID TextServerAdvanced::_shaped_text_substr(const RID &p_shaped, int64_t p_start } bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_length) const { - if (p_new_sd->valid) { + if (p_new_sd->valid.is_set()) { return true; } @@ -4725,7 +4803,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S _realign(p_new_sd); } - p_new_sd->valid = true; + p_new_sd->valid.set(); return true; } @@ -4743,7 +4821,7 @@ double TextServerAdvanced::_shaped_text_fit_to_width(const RID &p_shaped, double ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } if (!sd->justification_ops_valid) { @@ -4900,7 +4978,7 @@ double TextServerAdvanced::_shaped_text_tab_align(const RID &p_shaped, const Pac ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } if (!sd->line_breaks_valid) { @@ -5117,7 +5195,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ ERR_FAIL_NULL_MSG(sd, "ShapedTextDataAdvanced invalid."); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { _shaped_text_shape(p_shaped_line); } @@ -5144,7 +5222,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ Vector<ShapedTextDataAdvanced::Span> &spans = sd->spans; if (sd->parent != RID()) { ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent); - ERR_FAIL_COND(!parent_sd->valid); + ERR_FAIL_COND(!parent_sd->valid.is_set()); spans = parent_sd->spans; } @@ -5355,7 +5433,7 @@ void TextServerAdvanced::_update_chars(ShapedTextDataAdvanced *p_sd) const { Vector<ShapedTextDataAdvanced::Span> &spans = p_sd->spans; if (p_sd->parent != RID()) { ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(p_sd->parent); - ERR_FAIL_COND(!parent_sd->valid); + ERR_FAIL_COND(!parent_sd->valid.is_set()); spans = parent_sd->spans; } @@ -5403,7 +5481,7 @@ PackedInt32Array TextServerAdvanced::_shaped_text_get_character_breaks(const RID ERR_FAIL_NULL_V(sd, PackedInt32Array()); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } @@ -5417,7 +5495,7 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) { ERR_FAIL_NULL_V(sd, false); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { _shaped_text_shape(p_shaped); } @@ -5688,7 +5766,7 @@ bool TextServerAdvanced::_shaped_text_update_justification_ops(const RID &p_shap ERR_FAIL_NULL_V(sd, false); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { _shaped_text_shape(p_shaped); } if (!sd->line_breaks_valid) { @@ -6068,7 +6146,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } @@ -6128,7 +6206,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star gl.index = glyph_info[i].codepoint; if (gl.index != 0) { - _ensure_glyph(fd, fss, gl.index | mod); + FontGlyph fgl; + _ensure_glyph(fd, fss, gl.index | mod, fgl); if (subpos) { gl.x_off = (double)glyph_pos[i].x_offset / (64.0 / scale); } else if (p_sd->orientation == ORIENTATION_HORIZONTAL) { @@ -6240,7 +6319,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { ERR_FAIL_NULL_V(sd, false); MutexLock lock(sd->mutex); - if (sd->valid) { + if (sd->valid.is_set()) { return true; } @@ -6248,13 +6327,13 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { if (sd->parent != RID()) { _shaped_text_shape(sd->parent); ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent); - ERR_FAIL_COND_V(!parent_sd->valid, false); + ERR_FAIL_COND_V(!parent_sd->valid.is_set(), false); ERR_FAIL_COND_V(!_shape_substr(sd, parent_sd, sd->start, sd->end - sd->start), false); return true; } if (sd->text.length() == 0) { - sd->valid = true; + sd->valid.set(); return true; } @@ -6447,16 +6526,16 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { } _realign(sd); - sd->valid = true; - return sd->valid; + sd->valid.set(); + return sd->valid.is_set(); } bool TextServerAdvanced::_shaped_text_is_ready(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL_V(sd, false); - MutexLock lock(sd->mutex); - return sd->valid; + // Atomic read is safe and faster. + return sd->valid.is_set(); } const Glyph *TextServerAdvanced::_shaped_text_get_glyphs(const RID &p_shaped) const { @@ -6464,7 +6543,7 @@ const Glyph *TextServerAdvanced::_shaped_text_get_glyphs(const RID &p_shaped) co ERR_FAIL_NULL_V(sd, nullptr); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->glyphs.ptr(); @@ -6475,7 +6554,7 @@ int64_t TextServerAdvanced::_shaped_text_get_glyph_count(const RID &p_shaped) co ERR_FAIL_NULL_V(sd, 0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->glyphs.size(); @@ -6486,7 +6565,7 @@ const Glyph *TextServerAdvanced::_shaped_text_sort_logical(const RID &p_shaped) ERR_FAIL_NULL_V(sd, nullptr); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } @@ -6526,7 +6605,7 @@ Rect2 TextServerAdvanced::_shaped_text_get_object_rect(const RID &p_shaped, cons MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2()); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->objects[p_key].rect; @@ -6547,7 +6626,7 @@ int64_t TextServerAdvanced::_shaped_text_get_object_glyph(const RID &p_shaped, c MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), -1); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } const ShapedTextDataAdvanced::EmbeddedObject &obj = sd->objects[p_key]; @@ -6566,7 +6645,7 @@ Size2 TextServerAdvanced::_shaped_text_get_size(const RID &p_shaped) const { ERR_FAIL_NULL_V(sd, Size2()); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) { @@ -6581,7 +6660,7 @@ double TextServerAdvanced::_shaped_text_get_ascent(const RID &p_shaped) const { ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->ascent + sd->extra_spacing[SPACING_TOP]; @@ -6592,7 +6671,7 @@ double TextServerAdvanced::_shaped_text_get_descent(const RID &p_shaped) const { ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->descent + sd->extra_spacing[SPACING_BOTTOM]; @@ -6603,7 +6682,7 @@ double TextServerAdvanced::_shaped_text_get_width(const RID &p_shaped) const { ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return Math::ceil(sd->text_trimmed ? sd->width_trimmed : sd->width); @@ -6614,7 +6693,7 @@ double TextServerAdvanced::_shaped_text_get_underline_position(const RID &p_shap ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } @@ -6626,7 +6705,7 @@ double TextServerAdvanced::_shaped_text_get_underline_thickness(const RID &p_sha ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } @@ -7424,10 +7503,15 @@ bool TextServerAdvanced::_is_valid_letter(uint64_t p_unicode) const { return u_isalpha(p_unicode); } +void TextServerAdvanced::_update_settings() { + lcd_subpixel_layout.set((TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout")); +} + TextServerAdvanced::TextServerAdvanced() { _insert_num_systems_lang(); _insert_feature_sets(); _bmp_create_font_funcs(); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextServerAdvanced::_update_settings)); } void TextServerAdvanced::_cleanup() { diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index fdebb8e4cd..448be9ebe4 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -74,6 +74,7 @@ #include <godot_cpp/templates/hash_map.hpp> #include <godot_cpp/templates/hash_set.hpp> #include <godot_cpp/templates/rid_owner.hpp> +#include <godot_cpp/templates/safe_refcount.hpp> #include <godot_cpp/templates/vector.hpp> using namespace godot; @@ -85,6 +86,7 @@ using namespace godot; #include "core/object/worker_thread_pool.h" #include "core/templates/hash_map.h" #include "core/templates/rid_owner.h" +#include "core/templates/safe_refcount.h" #include "scene/resources/image_texture.h" #include "servers/text/text_server_extension.h" @@ -151,6 +153,9 @@ class TextServerAdvanced : public TextServerExtension { HashMap<StringName, int32_t> feature_sets; HashMap<int32_t, FeatureInfo> feature_sets_inv; + SafeNumeric<TextServer::FontLCDSubpixelLayout> lcd_subpixel_layout{ TextServer::FontLCDSubpixelLayout::FONT_LCD_SUBPIXEL_LAYOUT_NONE }; + void _update_settings(); + void _insert_num_systems_lang(); void _insert_feature_sets(); _FORCE_INLINE_ void _insert_feature(const StringName &p_name, int32_t p_tag, Variant::Type p_vtype = Variant::INT, bool p_hidden = false); @@ -327,7 +332,7 @@ class TextServerAdvanced : public TextServerExtension { int extra_spacing[4] = { 0, 0, 0, 0 }; double baseline_offset = 0.0; - HashMap<Vector2i, FontForSizeAdvanced *, VariantHasher, VariantComparator> cache; + HashMap<Vector2i, FontForSizeAdvanced *> cache; bool face_init = false; HashSet<uint32_t> supported_scripts; @@ -359,8 +364,8 @@ class TextServerAdvanced : public TextServerExtension { #ifdef MODULE_FREETYPE_ENABLED _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const; #endif - _FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; - _FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const; + _FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph) const; + _FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size, FontForSizeAdvanced *&r_cache_for_size) const; _FORCE_INLINE_ void _font_clear_cache(FontAdvanced *p_font_data); static void _generateMTSDF_threaded(void *p_td, uint32_t p_y); @@ -487,7 +492,7 @@ class TextServerAdvanced : public TextServerExtension { /* Shaped data */ TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction. int base_para_direction = UBIDI_DEFAULT_LTR; - bool valid = false; // String is shaped. + SafeFlag valid{ false }; // String is shaped. bool line_breaks_valid = false; // Line and word break flags are populated (and virtual zero width spaces inserted). bool justification_ops_valid = false; // Virtual elongation glyphs are added to the string. bool sort_valid = false; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index a7ddfc719e..540ba19cac 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -624,18 +624,21 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma /* Font Cache */ /*************************************************************************/ -_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const { - ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false); +_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph) const { + FontForSizeFallback *fd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size, fd), false); int32_t glyph_index = p_glyph & 0xffffff; // Remove subpixel shifts. - FontForSizeFallback *fd = p_font_data->cache[p_size]; - if (fd->glyph_map.has(p_glyph)) { - return fd->glyph_map[p_glyph].found; + HashMap<int32_t, FontGlyph>::Iterator E = fd->glyph_map.find(p_glyph); + if (E) { + r_glyph = E->value; + return E->value.found; } if (glyph_index == 0) { // Non graphical or invalid glyph, do not render. - fd->glyph_map[p_glyph] = FontGlyph(); + E = fd->glyph_map.insert(p_glyph, FontGlyph()); + r_glyph = E->value; return true; } @@ -673,7 +676,8 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, int error = FT_Load_Glyph(fd->face, glyph_index, flags); if (error) { - fd->glyph_map[p_glyph] = FontGlyph(); + E = fd->glyph_map.insert(p_glyph, FontGlyph()); + r_glyph = E->value; return false; } @@ -777,20 +781,26 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, cleanup_stroker: FT_Stroker_Done(stroker); } - fd->glyph_map[p_glyph] = gl; + E = fd->glyph_map.insert(p_glyph, gl); + r_glyph = E->value; return gl.found; } #endif - fd->glyph_map[p_glyph] = FontGlyph(); + E = fd->glyph_map.insert(p_glyph, FontGlyph()); + r_glyph = E->value; return false; } -_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const { +_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size, FontForSizeFallback *&r_cache_for_size) const { ERR_FAIL_COND_V(p_size.x <= 0, false); - if (p_font_data->cache.has(p_size)) { + + HashMap<Vector2i, FontForSizeFallback *>::Iterator E = p_font_data->cache.find(p_size); + if (E) { + r_cache_for_size = E->value; return true; } + r_cache_for_size = nullptr; FontForSizeFallback *fd = memnew(FontForSizeFallback); fd->size = p_size; if (p_font_data->data_ptr && (p_font_data->data_size > 0)) { @@ -973,7 +983,9 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } - p_font_data->cache[p_size] = fd; + + p_font_data->cache.insert(p_size, fd); + r_cache_for_size = fd; return true; } @@ -1041,7 +1053,8 @@ void TextServerFallback::_font_set_style(const RID &p_font_rid, BitField<FontSty MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->style_flags = p_style; } @@ -1119,7 +1132,8 @@ BitField<TextServer::FontStyle> TextServerFallback::_font_get_style(const RID &p MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0); return fd->style_flags; } @@ -1129,7 +1143,8 @@ void TextServerFallback::_font_set_style_name(const RID &p_font_rid, const Strin MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->style_name = p_name; } @@ -1139,7 +1154,8 @@ String TextServerFallback::_font_get_style_name(const RID &p_font_rid) const { MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), String()); return fd->style_name; } @@ -1149,7 +1165,8 @@ void TextServerFallback::_font_set_weight(const RID &p_font_rid, int64_t p_weigh MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->weight = CLAMP(p_weight, 100, 999); } @@ -1159,7 +1176,8 @@ int64_t TextServerFallback::_font_get_weight(const RID &p_font_rid) const { MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 400); return fd->weight; } @@ -1169,7 +1187,8 @@ void TextServerFallback::_font_set_stretch(const RID &p_font_rid, int64_t p_stre MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->stretch = CLAMP(p_stretch, 50, 200); } @@ -1179,7 +1198,8 @@ int64_t TextServerFallback::_font_get_stretch(const RID &p_font_rid) const { MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 100); return fd->stretch; } @@ -1189,7 +1209,8 @@ void TextServerFallback::_font_set_name(const RID &p_font_rid, const String &p_n MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->font_name = p_name; } @@ -1199,7 +1220,8 @@ String TextServerFallback::_font_get_name(const RID &p_font_rid) const { MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), String()); return fd->font_name; } @@ -1607,8 +1629,9 @@ void TextServerFallback::_font_set_ascent(const RID &p_font_rid, int64_t p_size, MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->ascent = p_ascent; + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->ascent = p_ascent; } double TextServerFallback::_font_get_ascent(const RID &p_font_rid, int64_t p_size) const { @@ -1618,18 +1641,19 @@ double TextServerFallback::_font_get_ascent(const RID &p_font_rid, int64_t p_siz MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->ascent * (double)p_size / (double)fd->msdf_source_size; + return ffsd->ascent * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->ascent * (double)p_size / (double)fd->fixed_size; + return ffsd->ascent * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->ascent * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->ascent * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->ascent; + return ffsd->ascent; } } @@ -1639,8 +1663,9 @@ void TextServerFallback::_font_set_descent(const RID &p_font_rid, int64_t p_size Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->descent = p_descent; + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->descent = p_descent; } double TextServerFallback::_font_get_descent(const RID &p_font_rid, int64_t p_size) const { @@ -1650,18 +1675,19 @@ double TextServerFallback::_font_get_descent(const RID &p_font_rid, int64_t p_si MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->descent * (double)p_size / (double)fd->msdf_source_size; + return ffsd->descent * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->descent * (double)p_size / (double)fd->fixed_size; + return ffsd->descent * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->descent * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->descent * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->descent; + return ffsd->descent; } } @@ -1672,8 +1698,9 @@ void TextServerFallback::_font_set_underline_position(const RID &p_font_rid, int MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->underline_position = p_underline_position; + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->underline_position = p_underline_position; } double TextServerFallback::_font_get_underline_position(const RID &p_font_rid, int64_t p_size) const { @@ -1683,18 +1710,19 @@ double TextServerFallback::_font_get_underline_position(const RID &p_font_rid, i MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->underline_position * (double)p_size / (double)fd->msdf_source_size; + return ffsd->underline_position * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->underline_position * (double)p_size / (double)fd->fixed_size; + return ffsd->underline_position * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->underline_position * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->underline_position * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->underline_position; + return ffsd->underline_position; } } @@ -1705,8 +1733,9 @@ void TextServerFallback::_font_set_underline_thickness(const RID &p_font_rid, in MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->underline_thickness = p_underline_thickness; + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->underline_thickness = p_underline_thickness; } double TextServerFallback::_font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const { @@ -1716,18 +1745,19 @@ double TextServerFallback::_font_get_underline_thickness(const RID &p_font_rid, MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->msdf_source_size; + return ffsd->underline_thickness * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->fixed_size; + return ffsd->underline_thickness * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->underline_thickness * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->underline_thickness * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->underline_thickness; + return ffsd->underline_thickness; } } @@ -1738,13 +1768,14 @@ void TextServerFallback::_font_set_scale(const RID &p_font_rid, int64_t p_size, MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face) { + if (ffsd->face) { return; // Do not override scale for dynamic fonts, it's calculated automatically. } #endif - fd->cache[size]->scale = p_scale; + ffsd->scale = p_scale; } double TextServerFallback::_font_get_scale(const RID &p_font_rid, int64_t p_size) const { @@ -1754,18 +1785,19 @@ double TextServerFallback::_font_get_scale(const RID &p_font_rid, int64_t p_size MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.0); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0); if (fd->msdf) { - return fd->cache[size]->scale * (double)p_size / (double)fd->msdf_source_size; + return ffsd->scale * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return fd->cache[size]->scale * (double)p_size / (double)fd->fixed_size; + return ffsd->scale * (double)p_size / (double)fd->fixed_size; } else { - return fd->cache[size]->scale * Math::round((double)p_size / (double)fd->fixed_size); + return ffsd->scale * Math::round((double)p_size / (double)fd->fixed_size); } } else { - return fd->cache[size]->scale / fd->cache[size]->oversampling; + return ffsd->scale / ffsd->oversampling; } } @@ -1776,9 +1808,10 @@ int64_t TextServerFallback::_font_get_texture_count(const RID &p_font_rid, const MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0); - return fd->cache[size]->textures.size(); + return ffsd->textures.size(); } void TextServerFallback::_font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) { @@ -1787,8 +1820,9 @@ void TextServerFallback::_font_clear_textures(const RID &p_font_rid, const Vecto MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->textures.clear(); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->textures.clear(); } void TextServerFallback::_font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) { @@ -1797,10 +1831,11 @@ void TextServerFallback::_font_remove_texture(const RID &p_font_rid, const Vecto MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ERR_FAIL_INDEX(p_texture_index, ffsd->textures.size()); - fd->cache[size]->textures.remove_at(p_texture_index); + ffsd->textures.remove_at(p_texture_index); } void TextServerFallback::_font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) { @@ -1810,13 +1845,14 @@ void TextServerFallback::_font_set_texture_image(const RID &p_font_rid, const Ve MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); ERR_FAIL_COND(p_texture_index < 0); - if (p_texture_index >= fd->cache[size]->textures.size()) { - fd->cache[size]->textures.resize(p_texture_index + 1); + if (p_texture_index >= ffsd->textures.size()) { + ffsd->textures.resize(p_texture_index + 1); } - ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + ShelfPackTexture &tex = ffsd->textures.write[p_texture_index]; tex.image = p_image; tex.texture_w = p_image->get_width(); @@ -1837,10 +1873,11 @@ Ref<Image> TextServerFallback::_font_get_texture_image(const RID &p_font_rid, co MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); - ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Ref<Image>()); + ERR_FAIL_INDEX_V(p_texture_index, ffsd->textures.size(), Ref<Image>()); - const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index]; + const ShelfPackTexture &tex = ffsd->textures[p_texture_index]; return tex.image; } @@ -1851,13 +1888,14 @@ void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); ERR_FAIL_COND(p_texture_index < 0); - if (p_texture_index >= fd->cache[size]->textures.size()) { - fd->cache[size]->textures.resize(p_texture_index + 1); + if (p_texture_index >= ffsd->textures.size()) { + ffsd->textures.resize(p_texture_index + 1); } - ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + ShelfPackTexture &tex = ffsd->textures.write[p_texture_index]; tex.shelves.clear(); for (int32_t i = 0; i < p_offsets.size(); i += 4) { tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3])); @@ -1870,10 +1908,11 @@ PackedInt32Array TextServerFallback::_font_get_texture_offsets(const RID &p_font MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); - ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), PackedInt32Array()); + ERR_FAIL_INDEX_V(p_texture_index, ffsd->textures.size(), PackedInt32Array()); - const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index]; + const ShelfPackTexture &tex = ffsd->textures[p_texture_index]; PackedInt32Array ret; ret.resize(tex.shelves.size() * 4); @@ -1895,10 +1934,11 @@ PackedInt32Array TextServerFallback::_font_get_glyph_list(const RID &p_font_rid, MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), PackedInt32Array()); PackedInt32Array ret; - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + const HashMap<int32_t, FontGlyph> &gl = ffsd->glyph_map; for (const KeyValue<int32_t, FontGlyph> &E : gl) { ret.push_back(E.key); } @@ -1911,9 +1951,10 @@ void TextServerFallback::_font_clear_glyphs(const RID &p_font_rid, const Vector2 MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - fd->cache[size]->glyph_map.clear(); + ffsd->glyph_map.clear(); } void TextServerFallback::_font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) { @@ -1922,9 +1963,10 @@ void TextServerFallback::_font_remove_glyph(const RID &p_font_rid, const Vector2 MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - fd->cache[size]->glyph_map.erase(p_glyph); + ffsd->glyph_map.erase(p_glyph); } Vector2 TextServerFallback::_font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const { @@ -1934,22 +1976,22 @@ Vector2 TextServerFallback::_font_get_glyph_advance(const RID &p_font_rid, int64 MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - Vector2 ea; if (fd->embolden != 0.0) { ea.x = fd->embolden * double(size.x) / 64.0; @@ -1957,17 +1999,17 @@ Vector2 TextServerFallback::_font_get_glyph_advance(const RID &p_font_rid, int64 double scale = _font_get_scale(p_font_rid, p_size); if (fd->msdf) { - return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size; + return (fgl.advance + ea) * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->fixed_size; + return (fgl.advance + ea) * (double)p_size / (double)fd->fixed_size; } else { - return (gl[p_glyph | mod].advance + ea) * Math::round((double)p_size / (double)fd->fixed_size); + return (fgl.advance + ea) * Math::round((double)p_size / (double)fd->fixed_size); } } else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE))) { - return (gl[p_glyph | mod].advance + ea).round(); + return (fgl.advance + ea).round(); } else { - return gl[p_glyph | mod].advance + ea; + return fgl.advance + ea; } } @@ -1978,12 +2020,13 @@ void TextServerFallback::_font_set_glyph_advance(const RID &p_font_rid, int64_t MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].advance = p_advance; - gl[p_glyph].found = true; + fgl.advance = p_advance; + fgl.found = true; } Vector2 TextServerFallback::_font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -1993,32 +2036,32 @@ Vector2 TextServerFallback::_font_get_glyph_offset(const RID &p_font_rid, const MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - if (fd->msdf) { - return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size; + return fgl.rect.position * (double)p_size.x / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->fixed_size; + return fgl.rect.position * (double)p_size.x / (double)fd->fixed_size; } else { - return gl[p_glyph | mod].rect.position * Math::round((double)p_size.x / (double)fd->fixed_size); + return fgl.rect.position * Math::round((double)p_size.x / (double)fd->fixed_size); } } else { - return gl[p_glyph | mod].rect.position; + return fgl.rect.position; } } @@ -2029,12 +2072,13 @@ void TextServerFallback::_font_set_glyph_offset(const RID &p_font_rid, const Vec MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].rect.position = p_offset; - gl[p_glyph].found = true; + fgl.rect.position = p_offset; + fgl.found = true; } Vector2 TextServerFallback::_font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -2044,32 +2088,32 @@ Vector2 TextServerFallback::_font_get_glyph_size(const RID &p_font_rid, const Ve MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - if (fd->msdf) { - return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size; + return fgl.rect.size * (double)p_size.x / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { - return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->fixed_size; + return fgl.rect.size * (double)p_size.x / (double)fd->fixed_size; } else { - return gl[p_glyph | mod].rect.size * Math::round((double)p_size.x / (double)fd->fixed_size); + return fgl.rect.size * Math::round((double)p_size.x / (double)fd->fixed_size); } } else { - return gl[p_glyph | mod].rect.size; + return fgl.rect.size; } } @@ -2080,12 +2124,13 @@ void TextServerFallback::_font_set_glyph_size(const RID &p_font_rid, const Vecto MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].rect.size = p_gl_size; - gl[p_glyph].found = true; + fgl.rect.size = p_gl_size; + fgl.found = true; } Rect2 TextServerFallback::_font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -2095,22 +2140,23 @@ Rect2 TextServerFallback::_font_get_glyph_uv_rect(const RID &p_font_rid, const V MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Rect2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Rect2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph | mod].uv_rect; + return fgl.uv_rect; } void TextServerFallback::_font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) { @@ -2120,12 +2166,13 @@ void TextServerFallback::_font_set_glyph_uv_rect(const RID &p_font_rid, const Ve MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].uv_rect = p_uv_rect; - gl[p_glyph].found = true; + fgl.uv_rect = p_uv_rect; + fgl.found = true; } int64_t TextServerFallback::_font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -2135,22 +2182,23 @@ int64_t TextServerFallback::_font_get_glyph_texture_idx(const RID &p_font_rid, c MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), -1); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return -1; // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph | mod].texture_idx; + return fgl.texture_idx; } void TextServerFallback::_font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) { @@ -2160,12 +2208,13 @@ void TextServerFallback::_font_set_glyph_texture_idx(const RID &p_font_rid, cons MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); - HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + FontGlyph &fgl = ffsd->glyph_map[p_glyph]; - gl[p_glyph].texture_idx = p_texture_idx; - gl[p_glyph].found = true; + fgl.texture_idx = p_texture_idx; + fgl.found = true; } RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { @@ -2175,27 +2224,28 @@ RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), RID()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), RID()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return RID(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID()); + ERR_FAIL_COND_V(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size(), RID()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph | mod].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { - ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; + if (fgl.texture_idx != -1) { + if (ffsd->textures[fgl.texture_idx].dirty) { + ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref<Image> img = tex.image; if (fd->mipmaps && !img->has_mipmaps()) { img = tex.image->duplicate(); @@ -2208,7 +2258,7 @@ RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid(); + return ffsd->textures[fgl.texture_idx].texture->get_rid(); } } @@ -2222,27 +2272,28 @@ Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, co MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Size2()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Size2()); int mod = 0; if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { mod = (layout << 24); } } - if (!_ensure_glyph(fd, size, p_glyph | mod)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) { return Size2(); // Invalid or non graphicl glyph, do not display errors. } - const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2()); + ERR_FAIL_COND_V(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size(), Size2()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph | mod].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { - ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; + if (fgl.texture_idx != -1) { + if (ffsd->textures[fgl.texture_idx].dirty) { + ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref<Image> img = tex.image; if (fd->mipmaps && !img->has_mipmaps()) { img = tex.image->duplicate(); @@ -2255,7 +2306,7 @@ Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, co } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size(); + return ffsd->textures[fgl.texture_idx].texture->get_size(); } } @@ -2269,7 +2320,8 @@ Dictionary TextServerFallback::_font_get_glyph_contours(const RID &p_font_rid, i MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary()); #ifdef MODULE_FREETYPE_ENABLED PackedVector3Array points; @@ -2277,20 +2329,20 @@ Dictionary TextServerFallback::_font_get_glyph_contours(const RID &p_font_rid, i int32_t index = p_index & 0xffffff; // Remove subpixel shifts. - int error = FT_Load_Glyph(fd->cache[size]->face, FT_Get_Char_Index(fd->cache[size]->face, index), FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + int error = FT_Load_Glyph(ffsd->face, FT_Get_Char_Index(ffsd->face, index), FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); ERR_FAIL_COND_V(error, Dictionary()); if (fd->embolden != 0.f) { FT_Pos strength = fd->embolden * p_size * 4; // 26.6 fractional units (1 / 64). - FT_Outline_Embolden(&fd->cache[size]->face->glyph->outline, strength); + FT_Outline_Embolden(&ffsd->face->glyph->outline, strength); } if (fd->transform != Transform2D()) { FT_Matrix mat = { FT_Fixed(fd->transform[0][0] * 65536), FT_Fixed(fd->transform[0][1] * 65536), FT_Fixed(fd->transform[1][0] * 65536), FT_Fixed(fd->transform[1][1] * 65536) }; // 16.16 fractional units (1 / 65536). - FT_Outline_Transform(&fd->cache[size]->face->glyph->outline, &mat); + FT_Outline_Transform(&ffsd->face->glyph->outline, &mat); } - double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale; + double scale = (1.0 / 64.0) / ffsd->oversampling * ffsd->scale; if (fd->msdf) { scale = scale * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { @@ -2300,13 +2352,13 @@ Dictionary TextServerFallback::_font_get_glyph_contours(const RID &p_font_rid, i scale = scale * Math::round((double)p_size / (double)fd->fixed_size); } } - for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) { - points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i]))); + for (short i = 0; i < ffsd->face->glyph->outline.n_points; i++) { + points.push_back(Vector3(ffsd->face->glyph->outline.points[i].x * scale, -ffsd->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(ffsd->face->glyph->outline.tags[i]))); } - for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) { - contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]); + for (short i = 0; i < ffsd->face->glyph->outline.n_contours; i++) { + contours.push_back(ffsd->face->glyph->outline.contours[i]); } - bool orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); + bool orientation = (FT_Outline_Get_Orientation(&ffsd->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); Dictionary out; out["points"] = points; @@ -2325,10 +2377,11 @@ TypedArray<Vector2i> TextServerFallback::_font_get_kerning_list(const RID &p_fon MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), TypedArray<Vector2i>()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), TypedArray<Vector2i>()); TypedArray<Vector2i> ret; - for (const KeyValue<Vector2i, Vector2> &E : fd->cache[size]->kerning_map) { + for (const KeyValue<Vector2i, Vector2> &E : ffsd->kerning_map) { ret.push_back(E.key); } return ret; @@ -2341,8 +2394,9 @@ void TextServerFallback::_font_clear_kerning_map(const RID &p_font_rid, int64_t MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->kerning_map.clear(); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->kerning_map.clear(); } void TextServerFallback::_font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) { @@ -2352,8 +2406,9 @@ void TextServerFallback::_font_remove_kerning(const RID &p_font_rid, int64_t p_s MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->kerning_map.erase(p_glyph_pair); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->kerning_map.erase(p_glyph_pair); } void TextServerFallback::_font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { @@ -2363,8 +2418,9 @@ void TextServerFallback::_font_set_kerning(const RID &p_font_rid, int64_t p_size MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); - fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning; + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); + ffsd->kerning_map[p_glyph_pair] = p_kerning; } Vector2 TextServerFallback::_font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const { @@ -2374,9 +2430,10 @@ Vector2 TextServerFallback::_font_get_kerning(const RID &p_font_rid, int64_t p_s MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2()); - const HashMap<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map; + const HashMap<Vector2i, Vector2> &kern = ffsd->kerning_map; if (kern.has(p_glyph_pair)) { if (fd->msdf) { @@ -2392,11 +2449,11 @@ Vector2 TextServerFallback::_font_get_kerning(const RID &p_font_rid, int64_t p_s } } else { #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face) { + if (ffsd->face) { FT_Vector delta; - int32_t glyph_a = FT_Get_Char_Index(fd->cache[size]->face, p_glyph_pair.x); - int32_t glyph_b = FT_Get_Char_Index(fd->cache[size]->face, p_glyph_pair.y); - FT_Get_Kerning(fd->cache[size]->face, glyph_a, glyph_b, FT_KERNING_DEFAULT, &delta); + int32_t glyph_a = FT_Get_Char_Index(ffsd->face, p_glyph_pair.x); + int32_t glyph_b = FT_Get_Char_Index(ffsd->face, p_glyph_pair.y); + FT_Get_Kerning(ffsd->face, glyph_a, glyph_b, FT_KERNING_DEFAULT, &delta); if (fd->msdf) { return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->msdf_source_size; } else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { @@ -2431,17 +2488,19 @@ bool TextServerFallback::_font_has_char(const RID &p_font_rid, int64_t p_char) c } MutexLock lock(fd->mutex); + FontForSizeFallback *ffsd = nullptr; if (fd->cache.is_empty()) { - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0), ffsd), false); + } else { + ffsd = fd->cache.begin()->value; } - FontForSizeFallback *at_size = fd->cache.begin()->value; #ifdef MODULE_FREETYPE_ENABLED - if (at_size && at_size->face) { - return FT_Get_Char_Index(at_size->face, p_char) != 0; + if (ffsd->face) { + return FT_Get_Char_Index(ffsd->face, p_char) != 0; } #endif - return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false; + return ffsd->glyph_map.has((int32_t)p_char); } String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) const { @@ -2449,30 +2508,30 @@ String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) cons ERR_FAIL_NULL_V(fd, String()); MutexLock lock(fd->mutex); + FontForSizeFallback *ffsd = nullptr; if (fd->cache.is_empty()) { - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String()); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0), ffsd), String()); + } else { + ffsd = fd->cache.begin()->value; } - FontForSizeFallback *at_size = fd->cache.begin()->value; String chars; #ifdef MODULE_FREETYPE_ENABLED - if (at_size && at_size->face) { + if (ffsd->face) { FT_UInt gindex; - FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + FT_ULong charcode = FT_Get_First_Char(ffsd->face, &gindex); while (gindex != 0) { if (charcode != 0) { chars = chars + String::chr(charcode); } - charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + charcode = FT_Get_Next_Char(ffsd->face, charcode, &gindex); } return chars; } #endif - if (at_size) { - const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map; - for (const KeyValue<int32_t, FontGlyph> &E : gl) { - chars = chars + String::chr(E.key); - } + const HashMap<int32_t, FontGlyph> &gl = ffsd->glyph_map; + for (const KeyValue<int32_t, FontGlyph> &E : gl) { + chars = chars + String::chr(E.key); } return chars; } @@ -2482,10 +2541,12 @@ PackedInt32Array TextServerFallback::_font_get_supported_glyphs(const RID &p_fon ERR_FAIL_NULL_V(fd, PackedInt32Array()); MutexLock lock(fd->mutex); + FontForSizeFallback *at_size = nullptr; if (fd->cache.is_empty()) { - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array()); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0), at_size), PackedInt32Array()); + } else { + at_size = fd->cache.begin()->value; } - FontForSizeFallback *at_size = fd->cache.begin()->value; PackedInt32Array glyphs; #ifdef MODULE_FREETYPE_ENABLED @@ -2516,25 +2577,27 @@ void TextServerFallback::_font_render_range(const RID &p_font_rid, const Vector2 MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); for (int64_t i = p_start; i <= p_end; i++) { #ifdef MODULE_FREETYPE_ENABLED int32_t idx = i; - if (fd->cache[size]->face) { + if (ffsd->face) { + FontGlyph fgl; if (fd->msdf) { - _ensure_glyph(fd, size, (int32_t)idx); + _ensure_glyph(fd, size, (int32_t)idx, fgl); } else { for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24), fgl); } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl); } else { - _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24), fgl); } } } @@ -2549,24 +2612,26 @@ void TextServerFallback::_font_render_glyph(const RID &p_font_rid, const Vector2 MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); #ifdef MODULE_FREETYPE_ENABLED int32_t idx = p_index & 0xffffff; // Remove subpixel shifts. - if (fd->cache[size]->face) { + if (ffsd->face) { + FontGlyph fgl; if (fd->msdf) { - _ensure_glyph(fd, size, (int32_t)idx); + _ensure_glyph(fd, size, (int32_t)idx, fgl); } else { for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24), fgl); } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl); } else { - _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24), fgl); } } } @@ -2583,16 +2648,17 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED - if (!fd->msdf && fd->cache[size]->face) { + if (!fd->msdf && ffsd->face) { // LCD layout, bits 24, 25, 26 if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { lcd_aa = true; index = index | (layout << 24); @@ -2609,24 +2675,24 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca } #endif - if (!_ensure_glyph(fd, size, index)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, index, fgl)) { return; // Invalid or non-graphical glyph, do not display errors, nothing to draw. } - const FontGlyph &gl = fd->cache[size]->glyph_map[index]; - if (gl.found) { - ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + if (fgl.found) { + ERR_FAIL_COND(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size()); - if (gl.texture_idx != -1) { + if (fgl.texture_idx != -1) { Color modulate = p_color; #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face && fd->cache[size]->textures[gl.texture_idx].image.is_valid() && (fd->cache[size]->textures[gl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { + if (ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { modulate.r = modulate.g = modulate.b = 1.0; } #endif if (RenderingServer::get_singleton() != nullptr) { - if (fd->cache[size]->textures[gl.texture_idx].dirty) { - ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; + if (ffsd->textures[fgl.texture_idx].dirty) { + ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref<Image> img = tex.image; if (fd->mipmaps && !img->has_mipmaps()) { img = tex.image->duplicate(); @@ -2639,12 +2705,12 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca } tex.dirty = false; } - RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + RID texture = ffsd->textures[fgl.texture_idx].texture->get_rid(); if (fd->msdf) { Point2 cpos = p_pos; - cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; - Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size; + Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { Point2 cpos = p_pos; double scale = _font_get_scale(p_font_rid, p_size); @@ -2657,8 +2723,8 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca cpos.y = Math::floor(cpos.y); cpos.x = Math::floor(cpos.x); } - Vector2 gpos = gl.rect.position; - Size2 csize = gl.rect.size; + Vector2 gpos = fgl.rect.position; + Size2 csize = fgl.rect.size; if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { double gl_scale = (double)p_size / (double)fd->fixed_size; @@ -2672,9 +2738,9 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca } cpos += gpos; if (lcd_aa) { - RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate); } else { - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false); } } } @@ -2691,16 +2757,17 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size)); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED - if (!fd->msdf && fd->cache[size]->face) { + if (!fd->msdf && ffsd->face) { // LCD layout, bits 24, 25, 26 if (fd->antialiasing == FONT_ANTIALIASING_LCD) { - TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get(); if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { lcd_aa = true; index = index | (layout << 24); @@ -2717,24 +2784,24 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R } #endif - if (!_ensure_glyph(fd, size, index)) { + FontGlyph fgl; + if (!_ensure_glyph(fd, size, index, fgl)) { return; // Invalid or non-graphical glyph, do not display errors, nothing to draw. } - const FontGlyph &gl = fd->cache[size]->glyph_map[index]; - if (gl.found) { - ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + if (fgl.found) { + ERR_FAIL_COND(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size()); - if (gl.texture_idx != -1) { + if (fgl.texture_idx != -1) { Color modulate = p_color; #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face && fd->cache[size]->textures[gl.texture_idx].image.is_valid() && (fd->cache[size]->textures[gl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { + if (ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { modulate.r = modulate.g = modulate.b = 1.0; } #endif if (RenderingServer::get_singleton() != nullptr) { - if (fd->cache[size]->textures[gl.texture_idx].dirty) { - ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; + if (ffsd->textures[fgl.texture_idx].dirty) { + ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref<Image> img = tex.image; if (fd->mipmaps && !img->has_mipmaps()) { img = tex.image->duplicate(); @@ -2747,12 +2814,12 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R } tex.dirty = false; } - RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + RID texture = ffsd->textures[fgl.texture_idx].texture->get_rid(); if (fd->msdf) { Point2 cpos = p_pos; - cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; - Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size; + Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { Point2 cpos = p_pos; double scale = _font_get_scale(p_font_rid, p_size); @@ -2765,8 +2832,8 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R cpos.y = Math::floor(cpos.y); cpos.x = Math::floor(cpos.x); } - Vector2 gpos = gl.rect.position; - Size2 csize = gl.rect.size; + Vector2 gpos = fgl.rect.position; + Size2 csize = fgl.rect.size; if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) { if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) { double gl_scale = (double)p_size / (double)fd->fixed_size; @@ -2780,9 +2847,9 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R } cpos += gpos; if (lcd_aa) { - RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate); } else { - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false); } } } @@ -2872,7 +2939,8 @@ void TextServerFallback::_font_remove_script_support_override(const RID &p_font_ MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->script_support_overrides.erase(p_script); } @@ -2894,7 +2962,8 @@ void TextServerFallback::_font_set_opentype_feature_overrides(const RID &p_font_ MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd)); fd->feature_overrides = p_overrides; } @@ -2916,7 +2985,8 @@ Dictionary TextServerFallback::_font_supported_variation_list(const RID &p_font_ MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, 16); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + FontForSizeFallback *ffsd = nullptr; + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary()); return fd->supported_varaitions; } @@ -2953,7 +3023,7 @@ void TextServerFallback::_font_set_global_oversampling(double p_oversampling) { /*************************************************************************/ void TextServerFallback::invalidate(ShapedTextDataFallback *p_shaped) { - p_shaped->valid = false; + p_shaped->valid.clear(); p_shaped->sort_valid = false; p_shaped->line_breaks_valid = false; p_shaped->justification_ops_valid = false; @@ -3192,7 +3262,7 @@ void TextServerFallback::_shaped_set_span_update_font(const RID &p_shaped, int64 span.font_size = p_size; span.features = p_opentype_features; - sd->valid = false; + sd->valid.clear(); } bool TextServerFallback::_shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { @@ -3288,7 +3358,7 @@ bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const V sd->objects[p_key].rect.size = p_size; sd->objects[p_key].inline_align = p_inline_align; sd->objects[p_key].baseline = p_baseline; - if (sd->valid) { + if (sd->valid.is_set()) { // Recalc string metrics. sd->ascent = 0; sd->descent = 0; @@ -3431,7 +3501,7 @@ RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start if (sd->parent != RID()) { return _shaped_text_substr(sd->parent, p_start, p_length); } - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID()); @@ -3514,7 +3584,7 @@ RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start _realign(new_sd); } - new_sd->valid = true; + new_sd->valid.set(); return shaped_owner.make_rid(new_sd); } @@ -3532,7 +3602,7 @@ double TextServerFallback::_shaped_text_fit_to_width(const RID &p_shaped, double ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } if (!sd->justification_ops_valid) { @@ -3641,7 +3711,7 @@ double TextServerFallback::_shaped_text_tab_align(const RID &p_shaped, const Pac ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } if (!sd->line_breaks_valid) { @@ -3697,7 +3767,7 @@ bool TextServerFallback::_shaped_text_update_breaks(const RID &p_shaped) { ERR_FAIL_NULL_V(sd, false); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { _shaped_text_shape(p_shaped); } @@ -3761,7 +3831,7 @@ bool TextServerFallback::_shaped_text_update_justification_ops(const RID &p_shap ERR_FAIL_NULL_V(sd, false); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { _shaped_text_shape(p_shaped); } if (!sd->line_breaks_valid) { @@ -3940,7 +4010,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ ERR_FAIL_NULL_MSG(sd, "ShapedTextDataFallback invalid."); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { _shaped_text_shape(p_shaped_line); } @@ -3967,7 +4037,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ Vector<ShapedTextDataFallback::Span> &spans = sd->spans; if (sd->parent != RID()) { ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent); - ERR_FAIL_COND(!parent_sd->valid); + ERR_FAIL_COND(!parent_sd->valid.is_set()); spans = parent_sd->spans; } @@ -4161,7 +4231,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) { ERR_FAIL_NULL_V(sd, false); MutexLock lock(sd->mutex); - if (sd->valid) { + if (sd->valid.is_set()) { return true; } @@ -4178,7 +4248,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) { sd->glyphs.clear(); if (sd->text.length() == 0) { - sd->valid = true; + sd->valid.set(); return true; } @@ -4307,16 +4377,15 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) { // Align embedded objects to baseline. _realign(sd); - sd->valid = true; - return sd->valid; + sd->valid.set(); + return sd->valid.is_set(); } bool TextServerFallback::_shaped_text_is_ready(const RID &p_shaped) const { const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL_V(sd, false); - MutexLock lock(sd->mutex); - return sd->valid; + return sd->valid.is_set(); } const Glyph *TextServerFallback::_shaped_text_get_glyphs(const RID &p_shaped) const { @@ -4324,7 +4393,7 @@ const Glyph *TextServerFallback::_shaped_text_get_glyphs(const RID &p_shaped) co ERR_FAIL_NULL_V(sd, nullptr); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } return sd->glyphs.ptr(); @@ -4335,7 +4404,7 @@ int64_t TextServerFallback::_shaped_text_get_glyph_count(const RID &p_shaped) co ERR_FAIL_NULL_V(sd, 0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } return sd->glyphs.size(); @@ -4346,7 +4415,7 @@ const Glyph *TextServerFallback::_shaped_text_sort_logical(const RID &p_shaped) ERR_FAIL_NULL_V(sd, nullptr); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } @@ -4380,7 +4449,7 @@ Rect2 TextServerFallback::_shaped_text_get_object_rect(const RID &p_shaped, cons MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2()); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } return sd->objects[p_key].rect; @@ -4417,7 +4486,7 @@ Size2 TextServerFallback::_shaped_text_get_size(const RID &p_shaped) const { ERR_FAIL_NULL_V(sd, Size2()); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) { @@ -4432,7 +4501,7 @@ double TextServerFallback::_shaped_text_get_ascent(const RID &p_shaped) const { ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } return sd->ascent + sd->extra_spacing[SPACING_TOP]; @@ -4443,7 +4512,7 @@ double TextServerFallback::_shaped_text_get_descent(const RID &p_shaped) const { ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } return sd->descent + sd->extra_spacing[SPACING_BOTTOM]; @@ -4454,7 +4523,7 @@ double TextServerFallback::_shaped_text_get_width(const RID &p_shaped) const { ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } return Math::ceil(sd->width); @@ -4465,7 +4534,7 @@ double TextServerFallback::_shaped_text_get_underline_position(const RID &p_shap ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } @@ -4477,7 +4546,7 @@ double TextServerFallback::_shaped_text_get_underline_thickness(const RID &p_sha ERR_FAIL_NULL_V(sd, 0.0); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } @@ -4489,7 +4558,7 @@ PackedInt32Array TextServerFallback::_shaped_text_get_character_breaks(const RID ERR_FAIL_NULL_V(sd, PackedInt32Array()); MutexLock lock(sd->mutex); - if (!sd->valid) { + if (!sd->valid.is_set()) { const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); } @@ -4626,8 +4695,13 @@ PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_str return ret; } +void TextServerFallback::_update_settings() { + lcd_subpixel_layout.set((TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout")); +} + TextServerFallback::TextServerFallback() { _insert_feature_sets(); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextServerFallback::_update_settings)); }; void TextServerFallback::_cleanup() { diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 1b76c6fa0f..ee1f72401f 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -72,6 +72,7 @@ #include <godot_cpp/templates/hash_map.hpp> #include <godot_cpp/templates/hash_set.hpp> #include <godot_cpp/templates/rid_owner.hpp> +#include <godot_cpp/templates/safe_refcount.hpp> #include <godot_cpp/templates/vector.hpp> using namespace godot; @@ -83,6 +84,7 @@ using namespace godot; #include "core/object/worker_thread_pool.h" #include "core/templates/hash_map.h" #include "core/templates/rid_owner.h" +#include "core/templates/safe_refcount.h" #include "scene/resources/image_texture.h" #include "servers/text/text_server_extension.h" @@ -116,6 +118,9 @@ class TextServerFallback : public TextServerExtension { HashMap<StringName, int32_t> feature_sets; HashMap<int32_t, StringName> feature_sets_inv; + SafeNumeric<TextServer::FontLCDSubpixelLayout> lcd_subpixel_layout{ TextServer::FontLCDSubpixelLayout::FONT_LCD_SUBPIXEL_LAYOUT_NONE }; + void _update_settings(); + void _insert_feature_sets(); _FORCE_INLINE_ void _insert_feature(const StringName &p_name, int32_t p_tag); @@ -278,7 +283,7 @@ class TextServerFallback : public TextServerExtension { int extra_spacing[4] = { 0, 0, 0, 0 }; double baseline_offset = 0.0; - HashMap<Vector2i, FontForSizeFallback *, VariantHasher, VariantComparator> cache; + HashMap<Vector2i, FontForSizeFallback *> cache; bool face_init = false; Dictionary supported_varaitions; @@ -308,8 +313,8 @@ class TextServerFallback : public TextServerExtension { #ifdef MODULE_FREETYPE_ENABLED _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const; #endif - _FORCE_INLINE_ bool _ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; - _FORCE_INLINE_ bool _ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const; + _FORCE_INLINE_ bool _ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph) const; + _FORCE_INLINE_ bool _ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size, FontForSizeFallback *&r_cache_for_size) const; _FORCE_INLINE_ void _font_clear_cache(FontFallback *p_font_data); static void _generateMTSDF_threaded(void *p_td, uint32_t p_y); @@ -432,7 +437,7 @@ class TextServerFallback : public TextServerExtension { /* Shaped data */ TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction. - bool valid = false; // String is shaped. + SafeFlag valid{ false }; // String is shaped. bool line_breaks_valid = false; // Line and word break flags are populated (and virtual zero width spaces inserted). bool justification_ops_valid = false; // Virtual elongation glyphs are added to the string. bool sort_valid = false; diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 31278cf042..f8ac591a78 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -2002,7 +2002,7 @@ String EditorExportPlatformAndroid::get_device_architecture(int p_index) const { return devices[p_index].architecture; } -Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { +Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER); String can_export_error; @@ -2024,11 +2024,11 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, } const bool use_wifi_for_remote_debug = EDITOR_GET("export/android/use_wifi_for_remote_debug"); - const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); + const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT); const bool use_reverse = devices[p_device].api_level >= 21 && !use_wifi_for_remote_debug; if (use_reverse) { - p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; + p_debug_flags.set_flag(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST); } String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().path_join("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); @@ -2107,7 +2107,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, OS::get_singleton()->execute(adb, args, &output, &rv, true); print_verbose(output); - if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { + if (p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG)) { int dbg_port = EDITOR_GET("network/debug/remote_port"); args.clear(); args.push_back("-s"); @@ -2122,7 +2122,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, print_line("Reverse result: " + itos(rv)); } - if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { + if (p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) { int fs_port = EDITOR_GET("filesystem/file_server/port"); args.clear(); @@ -2667,7 +2667,7 @@ Error EditorExportPlatformAndroid::save_apk_expansion_file(const Ref<EditorExpor return err; } -void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) { +void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, Vector<uint8_t> &r_command_line_flags) { String cmdline = p_preset->get("command_line/extra_args"); Vector<String> command_line_strings = cmdline.strip_edges().split(" "); for (int i = 0; i < command_line_strings.size(); i++) { @@ -2677,7 +2677,7 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP } } - gen_export_flags(command_line_strings, p_flags); + command_line_strings.append_array(gen_export_flags(p_flags)); bool apk_expansion = p_preset->get("apk_expansion/enable"); if (apk_expansion) { @@ -3000,13 +3000,13 @@ bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExpor return have_plugins_changed || has_build_dir_changed || first_build; } -Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { int export_format = int(p_preset->get("gradle_build/export_format")); bool should_sign = p_preset->get("package/signed"); return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags); } -Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) { +Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, BitField<EditorExportPlatform::DebugFlags> p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); const String base_dir = p_path.get_base_dir(); @@ -3022,7 +3022,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP bool use_gradle_build = bool(p_preset->get("gradle_build/use_gradle_build")); String gradle_build_directory = use_gradle_build ? ExportTemplateManager::get_android_build_directory(p_preset) : ""; - bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG); + bool p_give_internet = p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT) || p_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG); bool apk_expansion = p_preset->get("apk_expansion/enable"); Vector<ABI> enabled_abis = get_enabled_abis(p_preset); @@ -3127,7 +3127,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP user_data.assets_directory = assets_directory; user_data.libs_directory = gradle_build_directory.path_join("libs"); user_data.debug = p_debug; - if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { + if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) { err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so); } else { err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); @@ -3500,7 +3500,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP } err = OK; - if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { + if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) { APKExportData ed; ed.ep = &ep; ed.apk = unaligned_apk; diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index 97bbd0c7bc..708288fbf4 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -214,7 +214,7 @@ public: virtual String get_device_architecture(int p_index) const override; - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override; virtual Ref<Texture2D> get_run_icon() const override; @@ -242,7 +242,7 @@ public: Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); - void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags); + void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, Vector<uint8_t> &r_command_line_flags); Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep); @@ -253,9 +253,9 @@ public: static String join_list(const List<String> &p_parts, const String &p_separator); static String join_abis(const Vector<ABI> &p_parts, const String &p_separator, bool p_use_arch); - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; - Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags); + Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, BitField<EditorExportPlatform::DebugFlags> p_flags); virtual void get_platform_features(List<String> *r_features) const override; diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle index f9a3e10680..b8b4233636 100644 --- a/platform/android/java/editor/build.gradle +++ b/platform/android/java/editor/build.gradle @@ -37,7 +37,7 @@ ext { // Return the keystore file used for signing the release build. getGodotKeystoreFile = { -> def keyStore = System.getenv("GODOT_ANDROID_SIGN_KEYSTORE") - if (keyStore == null) { + if (keyStore == null || keyStore.isEmpty()) { return null } return file(keyStore) diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index e4b5392c4e..b99e825540 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -2017,11 +2017,11 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> return OK; } -Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { return _export_project_helper(p_preset, p_debug, p_path, p_flags, false, false); } -Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick) { +Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_simulator, bool p_oneclick) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); const String dest_dir = p_path.get_base_dir() + "/"; @@ -2983,7 +2983,7 @@ void EditorExportPlatformIOS::_update_preset_status() { } #endif -Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { +Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { #ifdef MACOS_ENABLED ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER); @@ -3029,11 +3029,11 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int String host = EDITOR_GET("network/debug/remote_host"); int remote_port = (int)EDITOR_GET("network/debug/remote_port"); - if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) { + if (p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST)) { host = "localhost"; } - if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { + if (p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) { int port = EDITOR_GET("filesystem/file_server/port"); String passwd = EDITOR_GET("filesystem/file_server/password"); cmd_args_list.push_back("--remote-fs"); @@ -3044,7 +3044,7 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int } } - if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { + if (p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG)) { cmd_args_list.push_back("--remote-debug"); cmd_args_list.push_back(get_debug_protocol() + host + ":" + String::num(remote_port)); @@ -3066,11 +3066,11 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int } } - if (p_debug_flags & DEBUG_FLAG_VIEW_COLLISIONS) { + if (p_debug_flags.has_flag(DEBUG_FLAG_VIEW_COLLISIONS)) { cmd_args_list.push_back("--debug-collisions"); } - if (p_debug_flags & DEBUG_FLAG_VIEW_NAVIGATION) { + if (p_debug_flags.has_flag(DEBUG_FLAG_VIEW_NAVIGATION)) { cmd_args_list.push_back("--debug-navigation"); } diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h index 1964906c27..db7c0553dd 100644 --- a/platform/ios/export/export_plugin.h +++ b/platform/ios/export/export_plugin.h @@ -146,7 +146,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug); - Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick); + Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_simulator, bool p_oneclick); bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const; @@ -169,7 +169,7 @@ public: virtual Ref<ImageTexture> get_option_icon(int p_index) const override; virtual String get_option_label(int p_index) const override; virtual String get_option_tooltip(int p_index) const override; - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override; virtual bool poll_export() override { bool dc = devices_changed.is_set(); @@ -202,7 +202,7 @@ public: return list; } - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index 0032b898d2..69ba742f72 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -60,7 +60,7 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP return OK; } -Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { String custom_debug = p_preset->get("custom_template/debug"); String custom_release = p_preset->get("custom_template/release"); String arch = p_preset->get("binary_format/architecture"); @@ -458,7 +458,7 @@ void EditorExportPlatformLinuxBSD::cleanup() { cleanup_commands.clear(); } -Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { +Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { cleanup(); if (p_device) { // Stop command, cleanup only. return OK; @@ -512,8 +512,7 @@ Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, String cmd_args; { - Vector<String> cmd_args_list; - gen_debug_flags(cmd_args_list, p_debug_flags); + Vector<String> cmd_args_list = gen_export_flags(p_debug_flags); for (int i = 0; i < cmd_args_list.size(); i++) { if (i != 0) { cmd_args += " "; @@ -522,7 +521,7 @@ Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, } } - const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); + const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT); int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); print_line("Creating temporary directory..."); diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h index bbc55b82ce..1d9ef01d1a 100644 --- a/platform/linuxbsd/export/export_plugin.h +++ b/platform/linuxbsd/export/export_plugin.h @@ -76,7 +76,7 @@ public: virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override; virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; virtual String get_template_file_name(const String &p_target, const String &p_arch) const override; virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override; virtual bool is_executable(const String &p_path) const override; @@ -87,7 +87,7 @@ public: virtual int get_options_count() const override; virtual String get_option_label(int p_index) const override; virtual String get_option_tooltip(int p_index) const override; - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override; virtual void cleanup() override; EditorExportPlatformLinuxBSD(); diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp index 2b98fda0d5..94a748e414 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.cpp +++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp @@ -210,7 +210,15 @@ void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, append_dbus_string(&struct_iter, p_filter_names[i]); dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter); - const String &flt = p_filter_exts[i]; + const String &flt_orig = p_filter_exts[i]; + String flt; + for (int j = 0; j < flt_orig.length(); j++) { + if (is_unicode_letter(flt_orig[j])) { + flt += vformat("[%c%c]", String::char_lowercase(flt_orig[j]), String::char_uppercase(flt_orig[j])); + } else { + flt += flt_orig[j]; + } + } int filter_slice_count = flt.get_slice_count(","); for (int j = 0; j < filter_slice_count; j++) { dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter); diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 290b0082fc..8372600ae9 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -1505,7 +1505,7 @@ Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPres return OK; } -Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); const String base_dir = p_path.get_base_dir(); @@ -2511,7 +2511,7 @@ void EditorExportPlatformMacOS::cleanup() { cleanup_commands.clear(); } -Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { +Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { cleanup(); if (p_device) { // Stop command, cleanup only. return OK; @@ -2573,8 +2573,7 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in String cmd_args; { - Vector<String> cmd_args_list; - gen_debug_flags(cmd_args_list, p_debug_flags); + Vector<String> cmd_args_list = gen_export_flags(p_debug_flags); for (int i = 0; i < cmd_args_list.size(); i++) { if (i != 0) { cmd_args += " "; @@ -2583,7 +2582,7 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in } } - const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); + const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT); int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); print_line("Creating temporary directory..."); diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h index 062a2e5f95..5457c687d3 100644 --- a/platform/macos/export/export_plugin.h +++ b/platform/macos/export/export_plugin.h @@ -147,7 +147,7 @@ public: virtual bool is_executable(const String &p_path) const override; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; @@ -167,7 +167,7 @@ public: virtual int get_options_count() const override; virtual String get_option_label(int p_index) const override; virtual String get_option_tooltip(int p_index) const override; - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override; virtual void cleanup() override; EditorExportPlatformMacOS(); diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index d8c1b6033d..5faab74d7b 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -130,15 +130,14 @@ void EditorExportPlatformWeb::_replace_strings(const HashMap<String, String> &p_ } } -void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) { +void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, BitField<EditorExportPlatform::DebugFlags> p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) { // Engine.js config Dictionary config; Array libs; for (int i = 0; i < p_shared_objects.size(); i++) { libs.push_back(p_shared_objects[i].path.get_file()); } - Vector<String> flags; - gen_export_flags(flags, p_flags & (~DEBUG_FLAG_DUMB_CLIENT)); + Vector<String> flags = gen_export_flags(p_flags & (~DEBUG_FLAG_DUMB_CLIENT)); Array args; for (int i = 0; i < flags.size(); i++) { args.push_back(flags[i]); @@ -450,7 +449,7 @@ List<String> EditorExportPlatformWeb::get_binary_extensions(const Ref<EditorExpo return list; } -Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); const String custom_debug = p_preset->get("custom_template/debug"); @@ -744,7 +743,7 @@ String EditorExportPlatformWeb::get_option_tooltip(int p_index) const { return ""; } -Error EditorExportPlatformWeb::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) { +Error EditorExportPlatformWeb::run(const Ref<EditorExportPreset> &p_preset, int p_option, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { const uint16_t bind_port = EDITOR_GET("export/web/http_port"); // Resolve host if needed. const String bind_host = EDITOR_GET("export/web/http_host"); diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h index 2f67d8107f..3c743e2e74 100644 --- a/platform/web/export/export_plugin.h +++ b/platform/web/export/export_plugin.h @@ -98,7 +98,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform { Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa); void _replace_strings(const HashMap<String, String> &p_replaces, Vector<uint8_t> &r_template); - void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes); + void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, BitField<EditorExportPlatform::DebugFlags> p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes); Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr); Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects); Error _write_or_error(const uint8_t *p_content, int p_len, String p_path); @@ -120,14 +120,14 @@ public: virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; virtual bool poll_export() override; virtual int get_options_count() const override; virtual String get_option_label(int p_index) const override; virtual String get_option_tooltip(int p_index) const override; virtual Ref<ImageTexture> get_option_icon(int p_index) const override; - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override; virtual Ref<Texture2D> get_run_icon() const override; virtual void get_platform_features(List<String> *r_features) const override { diff --git a/platform/windows/crash_handler_windows_seh.cpp b/platform/windows/crash_handler_windows_seh.cpp index 2abe285d31..a6015092e8 100644 --- a/platform/windows/crash_handler_windows_seh.cpp +++ b/platform/windows/crash_handler_windows_seh.cpp @@ -118,7 +118,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { HANDLE process = GetCurrentProcess(); HANDLE hThread = GetCurrentThread(); DWORD offset_from_symbol = 0; - IMAGEHLP_LINE64 line = { 0 }; + IMAGEHLP_LINE64 line = {}; std::vector<module_data> modules; DWORD cbNeeded; std::vector<HMODULE> module_handles(1); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 82a98655c0..d2a9c2315f 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -381,6 +381,15 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): ## Compile/link flags + if env["use_llvm"]: + env["CC"] = "clang-cl" + env["CXX"] = "clang-cl" + env["LINK"] = "lld-link" + env["AR"] = "llvm-lib" + + env.AppendUnique(CPPDEFINES=["R128_STDC_ONLY"]) + env.extra_suffix = ".llvm" + env.extra_suffix + env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable. if env["silence_msvc"] and not env.GetOption("clean"): @@ -465,7 +474,6 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"]) env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding. - env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++ # Once it was thought that only debug builds would be too large, # but this has recently stopped being true. See the mingw function # for notes on why this shouldn't be enabled for gcc @@ -590,6 +598,9 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): if env["target"] in ["editor", "template_debug"]: LIBS += ["psapi", "dbghelp"] + if env["use_llvm"]: + LIBS += [f"clang_rt.builtins-{env['arch']}"] + env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) if vcvars_msvc_config: @@ -605,14 +616,22 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): if env["lto"] != "none": if env["lto"] == "thin": - print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") - sys.exit(255) - env.AppendUnique(CCFLAGS=["/GL"]) - env.AppendUnique(ARFLAGS=["/LTCG"]) - if env["progress"]: - env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"]) + if not env["use_llvm"]: + 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"]) + elif env["use_llvm"]: + env.Append(CCFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-flto"]) else: - env.AppendUnique(LINKFLAGS=["/LTCG"]) + env.AppendUnique(CCFLAGS=["/GL"]) + env.AppendUnique(ARFLAGS=["/LTCG"]) + if env["progress"]: + env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"]) + else: + env.AppendUnique(LINKFLAGS=["/LTCG"]) if vcvars_msvc_config: env.Prepend(CPPPATH=[p for p in str(os.getenv("INCLUDE")).split(";")]) diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index b465bd4ecd..8d3f4bb269 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -167,7 +167,7 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres } } -Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { if (p_preset->get("application/modify_resources")) { _rcedit_add_data(p_preset, p_path, false); String wrapper_path = p_path.get_basename() + ".console.exe"; @@ -178,7 +178,7 @@ Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> return OK; } -Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { +Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { String custom_debug = p_preset->get("custom_template/debug"); String custom_release = p_preset->get("custom_template/release"); String arch = p_preset->get("binary_format/architecture"); @@ -996,7 +996,7 @@ void EditorExportPlatformWindows::cleanup() { cleanup_commands.clear(); } -Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { +Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { cleanup(); if (p_device) { // Stop command, cleanup only. return OK; @@ -1050,8 +1050,7 @@ Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, String cmd_args; { - Vector<String> cmd_args_list; - gen_debug_flags(cmd_args_list, p_debug_flags); + Vector<String> cmd_args_list = gen_export_flags(p_debug_flags); for (int i = 0; i < cmd_args_list.size(); i++) { if (i != 0) { cmd_args += " "; @@ -1060,7 +1059,7 @@ Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, } } - const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); + const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT); int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); print_line("Creating temporary directory..."); diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index 6ccb4a15a7..e86aac83d4 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -76,8 +76,8 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC { String _get_exe_arch(const String &p_path) const; public: - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override; + virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) override; virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) override; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; virtual void get_export_options(List<ExportOption> *r_options) const override; @@ -95,7 +95,7 @@ public: virtual int get_options_count() const override; virtual String get_option_label(int p_index) const override; virtual String get_option_tooltip(int p_index) const override; - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override; virtual void cleanup() override; EditorExportPlatformWindows(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index bc42b234be..47836788e1 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -430,13 +430,13 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha bool has_dll_directory_api = ((add_dll_directory != nullptr) && (remove_dll_directory != nullptr)); DLL_DIRECTORY_COOKIE cookie = nullptr; - String dll_dir = ProjectSettings::get_singleton()->globalize_path(load_path.get_base_dir()); - String wpath = fix_path(dll_dir); + String dll_path = fix_path(load_path); + String dll_dir = fix_path(ProjectSettings::get_singleton()->globalize_path(load_path.get_base_dir())); if (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) { - cookie = add_dll_directory((LPCWSTR)(wpath.get_base_dir().utf16().get_data())); + cookie = add_dll_directory((LPCWSTR)(dll_dir.utf16().get_data())); } - p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(wpath.utf16().get_data()), nullptr, (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); + p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(dll_path.utf16().get_data()), nullptr, (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); if (!p_library_handle) { if (p_data != nullptr && p_data->generate_temp_files) { DirAccess::remove_absolute(load_path); @@ -447,7 +447,7 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha HashSet<String> checked_libs; HashSet<String> missing_libs; - debug_dynamic_library_check_dependencies(wpath, checked_libs, missing_libs); + debug_dynamic_library_check_dependencies(dll_path, checked_libs, missing_libs); if (!missing_libs.is_empty()) { String missing; for (const String &E : missing_libs) { diff --git a/scene/2d/parallax_2d.cpp b/scene/2d/parallax_2d.cpp index 9dd9d4a376..fdb2d2cdd0 100644 --- a/scene/2d/parallax_2d.cpp +++ b/scene/2d/parallax_2d.cpp @@ -47,9 +47,18 @@ void Parallax2D::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PROCESS: { - autoscroll_offset += autoscroll * get_process_delta_time(); - autoscroll_offset = autoscroll_offset.posmodv(repeat_size); + Point2 offset = scroll_offset; + offset += autoscroll * get_process_delta_time(); + if (repeat_size.x) { + offset.x = Math::fposmod(offset.x, repeat_size.x); + } + + if (repeat_size.y) { + offset.y = Math::fposmod(offset.y, repeat_size.y); + } + + scroll_offset = offset; _update_scroll(); } break; @@ -106,14 +115,14 @@ void Parallax2D::_update_scroll() { scroll_ofs *= scroll_scale; if (repeat_size.x) { - real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x - autoscroll_offset.x, repeat_size.x * get_scale().x); + real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x, repeat_size.x * get_scale().x); scroll_ofs.x = screen_offset.x - mod; } else { scroll_ofs.x = screen_offset.x + scroll_offset.x - scroll_ofs.x; } if (repeat_size.y) { - real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y - autoscroll_offset.y, repeat_size.y * get_scale().y); + real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y, repeat_size.y * get_scale().y); scroll_ofs.y = screen_offset.y - mod; } else { scroll_ofs.y = screen_offset.y + scroll_offset.y - scroll_ofs.y; @@ -193,7 +202,6 @@ void Parallax2D::set_autoscroll(const Point2 &p_autoscroll) { } autoscroll = p_autoscroll; - autoscroll_offset = Point2(); _update_process(); _update_scroll(); diff --git a/scene/2d/parallax_2d.h b/scene/2d/parallax_2d.h index 5fbc3a20c8..f15e3fa9ff 100644 --- a/scene/2d/parallax_2d.h +++ b/scene/2d/parallax_2d.h @@ -47,7 +47,6 @@ class Parallax2D : public Node2D { Point2 limit_begin = Point2(-DEFAULT_LIMIT, -DEFAULT_LIMIT); Point2 limit_end = Point2(DEFAULT_LIMIT, DEFAULT_LIMIT); Point2 autoscroll; - Point2 autoscroll_offset; bool follow_viewport = true; bool ignore_camera_scroll = false; diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index b0ad7ec110..ba0958e74b 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -957,7 +957,7 @@ void TileMapLayer::_navigation_update(bool p_force_cleanup) { // Create a dedicated map for each layer. RID new_layer_map = ns->map_create(); // Set the default NavigationPolygon cell_size on the new map as a mismatch causes an error. - ns->map_set_cell_size(new_layer_map, 1.0); + ns->map_set_cell_size(new_layer_map, NavigationDefaults2D::navmesh_cell_size); ns->map_set_active(new_layer_map, true); navigation_map_override = new_layer_map; } diff --git a/scene/3d/physics/vehicle_body_3d.cpp b/scene/3d/physics/vehicle_body_3d.cpp index c23032d3b9..b4c321cf5f 100644 --- a/scene/3d/physics/vehicle_body_3d.cpp +++ b/scene/3d/physics/vehicle_body_3d.cpp @@ -219,6 +219,14 @@ bool VehicleWheel3D::is_in_contact() const { return m_raycastInfo.m_isInContact; } +Vector3 VehicleWheel3D::get_contact_point() const { + return m_raycastInfo.m_contactPointWS; +} + +Vector3 VehicleWheel3D::get_contact_normal() const { + return m_raycastInfo.m_contactNormalWS; +} + Node3D *VehicleWheel3D::get_contact_body() const { return m_raycastInfo.m_groundObject; } @@ -256,6 +264,8 @@ void VehicleWheel3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_in_contact"), &VehicleWheel3D::is_in_contact); ClassDB::bind_method(D_METHOD("get_contact_body"), &VehicleWheel3D::get_contact_body); + ClassDB::bind_method(D_METHOD("get_contact_point"), &VehicleWheel3D::get_contact_point); + ClassDB::bind_method(D_METHOD("get_contact_normal"), &VehicleWheel3D::get_contact_normal); ClassDB::bind_method(D_METHOD("set_roll_influence", "roll_influence"), &VehicleWheel3D::set_roll_influence); ClassDB::bind_method(D_METHOD("get_roll_influence"), &VehicleWheel3D::get_roll_influence); diff --git a/scene/3d/physics/vehicle_body_3d.h b/scene/3d/physics/vehicle_body_3d.h index def9984440..24f120ed26 100644 --- a/scene/3d/physics/vehicle_body_3d.h +++ b/scene/3d/physics/vehicle_body_3d.h @@ -130,6 +130,10 @@ public: bool is_in_contact() const; + Vector3 get_contact_point() const; + + Vector3 get_contact_normal() const; + Node3D *get_contact_body() const; void set_roll_influence(real_t p_value); diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index 6ea1cfdbb0..08d018eee9 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -35,9 +35,8 @@ class Voxelizer { private: - enum { + enum : uint32_t { CHILD_EMPTY = 0xFFFFFFFF - }; struct Cell { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 9bb6130742..0395dffad9 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -817,6 +817,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } int line_count = 0; + // Bottom margin for text clipping. + float v_limit = theme_cache.normal_style->get_margin(SIDE_BOTTOM); Size2 ctrl_size = get_size(); // Draw text. for (int line = 0; line < l.text_buf->get_line_count(); line++) { @@ -824,7 +826,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o off.y += theme_cache.line_separation; } - if (p_ofs.y + off.y >= ctrl_size.height) { + if (p_ofs.y + off.y >= ctrl_size.height - v_limit) { break; } @@ -1890,10 +1892,12 @@ void RichTextLabel::_notification(int p_what) { visible_paragraph_count = 0; visible_line_count = 0; + // Bottom margin for text clipping. + float v_limit = theme_cache.normal_style->get_margin(SIDE_BOTTOM); // New cache draw. Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); int processed_glyphs = 0; - while (ofs.y < size.height && from_line < to_line) { + while (ofs.y < size.height - v_limit && from_line < to_line) { MutexLock lock(main->lines[from_line].text_buf->get_mutex()); visible_paragraph_count++; diff --git a/scene/resources/2d/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h index 86bda47ace..ed2c606c55 100644 --- a/scene/resources/2d/navigation_polygon.h +++ b/scene/resources/2d/navigation_polygon.h @@ -33,6 +33,7 @@ #include "scene/2d/node_2d.h" #include "scene/resources/navigation_mesh.h" +#include "servers/navigation/navigation_globals.h" class NavigationPolygon : public Resource { GDCLASS(NavigationPolygon, Resource); @@ -50,7 +51,7 @@ class NavigationPolygon : public Resource { // Navigation mesh Ref<NavigationMesh> navigation_mesh; - real_t cell_size = 1.0f; // Must match ProjectSettings default 2D cell_size. + real_t cell_size = NavigationDefaults2D::navmesh_cell_size; real_t border_size = 0.0f; Rect2 baking_rect; diff --git a/scene/resources/3d/fog_material.cpp b/scene/resources/3d/fog_material.cpp index 5e4f1970ee..92246b50db 100644 --- a/scene/resources/3d/fog_material.cpp +++ b/scene/resources/3d/fog_material.cpp @@ -138,7 +138,7 @@ void FogMaterial::cleanup_shader() { } void FogMaterial::_update_shader() { - shader_mutex.lock(); + MutexLock shader_lock(shader_mutex); if (shader.is_null()) { shader = RS::get_singleton()->shader_create(); @@ -165,7 +165,6 @@ void fog() { } )"); } - shader_mutex.unlock(); } FogMaterial::FogMaterial() { diff --git a/scene/resources/3d/sky_material.cpp b/scene/resources/3d/sky_material.cpp index 640261d615..c470db5d7f 100644 --- a/scene/resources/3d/sky_material.cpp +++ b/scene/resources/3d/sky_material.cpp @@ -269,7 +269,7 @@ void ProceduralSkyMaterial::cleanup_shader() { } void ProceduralSkyMaterial::_update_shader() { - shader_mutex.lock(); + MutexLock shader_lock(shader_mutex); if (shader_cache[0].is_null()) { for (int i = 0; i < 2; i++) { shader_cache[i] = RS::get_singleton()->shader_create(); @@ -354,7 +354,6 @@ void sky() { i ? "render_mode use_debanding;" : "")); } } - shader_mutex.unlock(); } ProceduralSkyMaterial::ProceduralSkyMaterial() { @@ -463,7 +462,7 @@ void PanoramaSkyMaterial::cleanup_shader() { } void PanoramaSkyMaterial::_update_shader() { - shader_mutex.lock(); + MutexLock shader_lock(shader_mutex); if (shader_cache[0].is_null()) { for (int i = 0; i < 2; i++) { shader_cache[i] = RS::get_singleton()->shader_create(); @@ -484,8 +483,6 @@ void sky() { i ? "filter_linear" : "filter_nearest")); } } - - shader_mutex.unlock(); } PanoramaSkyMaterial::PanoramaSkyMaterial() { @@ -692,7 +689,7 @@ void PhysicalSkyMaterial::cleanup_shader() { } void PhysicalSkyMaterial::_update_shader() { - shader_mutex.lock(); + MutexLock shader_lock(shader_mutex); if (shader_cache[0].is_null()) { for (int i = 0; i < 2; i++) { shader_cache[i] = RS::get_singleton()->shader_create(); @@ -785,8 +782,6 @@ void sky() { i ? "render_mode use_debanding;" : "")); } } - - shader_mutex.unlock(); } PhysicalSkyMaterial::PhysicalSkyMaterial() { diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp index de6a069567..b293a91123 100644 --- a/scene/resources/audio_stream_wav.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -179,17 +179,17 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst, if (pos != p_qoa->cache_pos) { // Prevents triple decoding on lower mix rates. for (int i = 0; i < 2; i++) { // Sign operations prevent triple decoding on backward loops, maxing prevents pop. - uint32_t interp_pos = MIN(pos + (i * sign) + (sign < 0), p_qoa->desc->samples - 1); + uint32_t interp_pos = MIN(pos + (i * sign) + (sign < 0), p_qoa->desc.samples - 1); uint32_t new_data_ofs = 8 + interp_pos / QOA_FRAME_LEN * p_qoa->frame_len; if (p_qoa->data_ofs != new_data_ofs) { p_qoa->data_ofs = new_data_ofs; const uint8_t *src_ptr = (const uint8_t *)base->data; src_ptr += p_qoa->data_ofs + AudioStreamWAV::DATA_PAD; - qoa_decode_frame(src_ptr, p_qoa->frame_len, p_qoa->desc, p_qoa->dec, &p_qoa->dec_len); + qoa_decode_frame(src_ptr, p_qoa->frame_len, &p_qoa->desc, p_qoa->dec, &p_qoa->dec_len); } - uint32_t dec_idx = (interp_pos % QOA_FRAME_LEN) * p_qoa->desc->channels; + uint32_t dec_idx = (interp_pos % QOA_FRAME_LEN) * p_qoa->desc.channels; if ((sign > 0 && i == 0) || (sign < 0 && i == 1)) { final = p_qoa->dec[dec_idx]; @@ -286,7 +286,7 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_ len *= 2; break; case AudioStreamWAV::FORMAT_QOA: - len = qoa.desc->samples * qoa.desc->channels; + len = qoa.desc.samples * qoa.desc.channels; break; } @@ -484,10 +484,6 @@ void AudioStreamPlaybackWAV::set_sample_playback(const Ref<AudioSamplePlayback> AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {} AudioStreamPlaybackWAV::~AudioStreamPlaybackWAV() { - if (qoa.desc) { - memfree(qoa.desc); - } - if (qoa.dec) { memfree(qoa.dec); } @@ -557,7 +553,7 @@ double AudioStreamWAV::get_length() const { len *= 2; break; case AudioStreamWAV::FORMAT_QOA: - qoa_desc desc = { 0, 0, 0, { { { 0 }, { 0 } } } }; + qoa_desc desc = {}; qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, &desc); len = desc.samples * desc.channels; break; @@ -697,12 +693,11 @@ Ref<AudioStreamPlayback> AudioStreamWAV::instantiate_playback() { sample->base = Ref<AudioStreamWAV>(this); if (format == AudioStreamWAV::FORMAT_QOA) { - sample->qoa.desc = (qoa_desc *)memalloc(sizeof(qoa_desc)); - uint32_t ffp = qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, sample->qoa.desc); + uint32_t ffp = qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, &sample->qoa.desc); ERR_FAIL_COND_V(ffp != 8, Ref<AudioStreamPlaybackWAV>()); - sample->qoa.frame_len = qoa_max_frame_size(sample->qoa.desc); - int samples_len = (sample->qoa.desc->samples > QOA_FRAME_LEN ? QOA_FRAME_LEN : sample->qoa.desc->samples); - int alloc_len = sample->qoa.desc->channels * samples_len * sizeof(int16_t); + sample->qoa.frame_len = qoa_max_frame_size(&sample->qoa.desc); + int samples_len = (sample->qoa.desc.samples > QOA_FRAME_LEN ? QOA_FRAME_LEN : sample->qoa.desc.samples); + int alloc_len = sample->qoa.desc.channels * samples_len * sizeof(int16_t); sample->qoa.dec = (int16_t *)memalloc(alloc_len); } diff --git a/scene/resources/audio_stream_wav.h b/scene/resources/audio_stream_wav.h index 806db675b6..47aa10e790 100644 --- a/scene/resources/audio_stream_wav.h +++ b/scene/resources/audio_stream_wav.h @@ -59,7 +59,7 @@ class AudioStreamPlaybackWAV : public AudioStreamPlayback { } ima_adpcm[2]; struct QOA_State { - qoa_desc *desc = nullptr; + qoa_desc desc = {}; uint32_t data_ofs = 0; uint32_t frame_len = 0; int16_t *dec = nullptr; diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 741cea0791..1b3db5bac2 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -33,6 +33,7 @@ #include "core/os/rw_lock.h" #include "scene/resources/mesh.h" +#include "servers/navigation/navigation_globals.h" class NavigationMesh : public Resource { GDCLASS(NavigationMesh, Resource); @@ -77,8 +78,8 @@ public: }; protected: - float cell_size = 0.25f; // Must match ProjectSettings default 3D cell_size and NavigationServer NavMap cell_size. - float cell_height = 0.25f; // Must match ProjectSettings default 3D cell_height and NavigationServer NavMap cell_height. + float cell_size = NavigationDefaults3D::navmesh_cell_size; + float cell_height = NavigationDefaults3D::navmesh_cell_height; float border_size = 0.0f; float agent_height = 1.5f; float agent_radius = 0.5f; diff --git a/servers/navigation/navigation_globals.h b/servers/navigation/navigation_globals.h new file mode 100644 index 0000000000..aa54f95519 --- /dev/null +++ b/servers/navigation/navigation_globals.h @@ -0,0 +1,66 @@ +/**************************************************************************/ +/* navigation_globals.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 NAVIGATION_GLOBALS_H +#define NAVIGATION_GLOBALS_H + +namespace NavigationDefaults3D { + +// Rasterization. + +// To find the polygons edges the vertices are displaced in a grid where +// each cell has the following cell_size and cell_height. +constexpr float navmesh_cell_size{ 0.25f }; // Must match ProjectSettings default 3D cell_size and NavigationMesh cell_size. +constexpr float navmesh_cell_height{ 0.25f }; // Must match ProjectSettings default 3D cell_height and NavigationMesh cell_height. +constexpr auto navmesh_cell_size_hint{ "0.001,100,0.001,or_greater" }; + +// Map. + +constexpr float edge_connection_margin{ 0.25f }; +constexpr float link_connection_radius{ 1.0f }; + +} //namespace NavigationDefaults3D + +namespace NavigationDefaults2D { + +// Rasterization. + +// Same as in 3D but larger since 1px is treated as 1m. +constexpr float navmesh_cell_size{ 1.0f }; // Must match ProjectSettings default 2D cell_size. +constexpr auto navmesh_cell_size_hint{ "0.001,100,0.001,or_greater" }; + +// Map. + +constexpr float edge_connection_margin{ 1.0f }; +constexpr float link_connection_radius{ 4.0f }; + +} //namespace NavigationDefaults2D + +#endif // NAVIGATION_GLOBALS_H diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 398ff1e1f3..f4ffcf5a3e 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "scene/main/node.h" +#include "servers/navigation/navigation_globals.h" NavigationServer3D *NavigationServer3D::singleton = nullptr; @@ -227,18 +228,18 @@ NavigationServer3D::NavigationServer3D() { ERR_FAIL_COND(singleton != nullptr); singleton = this; - GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/2d/default_cell_size", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), 1.0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/2d/default_cell_size", PROPERTY_HINT_RANGE, NavigationDefaults2D::navmesh_cell_size_hint), NavigationDefaults2D::navmesh_cell_size); GLOBAL_DEF("navigation/2d/use_edge_connections", true); - GLOBAL_DEF_BASIC("navigation/2d/default_edge_connection_margin", 1.0); - GLOBAL_DEF_BASIC("navigation/2d/default_link_connection_radius", 4.0); + GLOBAL_DEF_BASIC("navigation/2d/default_edge_connection_margin", NavigationDefaults2D::edge_connection_margin); + GLOBAL_DEF_BASIC("navigation/2d/default_link_connection_radius", NavigationDefaults2D::link_connection_radius); - GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/3d/default_cell_size", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), 0.25); - GLOBAL_DEF_BASIC("navigation/3d/default_cell_height", 0.25); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "navigation/3d/default_cell_size", PROPERTY_HINT_RANGE, NavigationDefaults3D::navmesh_cell_size_hint), NavigationDefaults3D::navmesh_cell_size); + GLOBAL_DEF_BASIC("navigation/3d/default_cell_height", NavigationDefaults3D::navmesh_cell_height); GLOBAL_DEF("navigation/3d/default_up", Vector3(0, 1, 0)); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "navigation/3d/merge_rasterizer_cell_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001,or_greater"), 1.0); GLOBAL_DEF("navigation/3d/use_edge_connections", true); - GLOBAL_DEF_BASIC("navigation/3d/default_edge_connection_margin", 0.25); - GLOBAL_DEF_BASIC("navigation/3d/default_link_connection_radius", 1.0); + GLOBAL_DEF_BASIC("navigation/3d/default_edge_connection_margin", NavigationDefaults3D::edge_connection_margin); + GLOBAL_DEF_BASIC("navigation/3d/default_link_connection_radius", NavigationDefaults3D::link_connection_radius); GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true); GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true); diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index 94ab219dc2..1db58d72f9 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -49,7 +49,7 @@ namespace RendererRD { class LightStorage : public RendererLightStorage { public: - enum ShadowAtlastQuadrant { + enum ShadowAtlastQuadrant : uint32_t { QUADRANT_SHIFT = 27, OMNI_LIGHT_FLAG = 1 << 26, SHADOW_INDEX_MASK = OMNI_LIGHT_FLAG - 1, diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index d8f9e2c31a..1405f585b2 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -377,7 +377,9 @@ public: // used for the render pipelines. struct AttachmentFormat { - enum { UNUSED_ATTACHMENT = 0xFFFFFFFF }; + enum : uint32_t { + UNUSED_ATTACHMENT = 0xFFFFFFFF + }; DataFormat format; TextureSamples samples; uint32_t usage_flags; diff --git a/tests/core/io/test_json_native.h b/tests/core/io/test_json_native.h new file mode 100644 index 0000000000..819078ac57 --- /dev/null +++ b/tests/core/io/test_json_native.h @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* test_json_native.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 TEST_JSON_NATIVE_H +#define TEST_JSON_NATIVE_H + +#include "core/io/json.h" + +namespace TestJSONNative { + +bool compare_variants(Variant variant_1, Variant variant_2, int depth = 0) { + if (depth > 100) { + return false; + } + if (variant_1.get_type() == Variant::RID && variant_2.get_type() == Variant::RID) { + return true; + } + if (variant_1.get_type() == Variant::CALLABLE || variant_2.get_type() == Variant::CALLABLE) { + return true; + } + + List<PropertyInfo> variant_1_properties; + variant_1.get_property_list(&variant_1_properties); + List<PropertyInfo> variant_2_properties; + variant_2.get_property_list(&variant_2_properties); + + if (variant_1_properties.size() != variant_2_properties.size()) { + return false; + } + + for (List<PropertyInfo>::Element *E = variant_1_properties.front(); E; E = E->next()) { + String name = E->get().name; + Variant variant_1_value = variant_1.get(name); + Variant variant_2_value = variant_2.get(name); + + if (!compare_variants(variant_1_value, variant_2_value, depth + 1)) { + return false; + } + } + + return true; +} + +TEST_CASE("[JSON][Native][SceneTree] Conversion between native and JSON formats") { + for (int variant_i = 0; variant_i < Variant::VARIANT_MAX; variant_i++) { + Variant::Type type = static_cast<Variant::Type>(variant_i); + Variant native_data; + Callable::CallError error; + + if (type == Variant::Type::INT || type == Variant::Type::FLOAT) { + Variant value = int64_t(INT64_MAX); + const Variant *args[] = { &value }; + Variant::construct(type, native_data, args, 1, error); + } else if (type == Variant::Type::OBJECT) { + Ref<JSON> json = memnew(JSON); + native_data = json; + } else if (type == Variant::Type::DICTIONARY) { + Dictionary dictionary; + dictionary["key"] = "value"; + native_data = dictionary; + } else if (type == Variant::Type::ARRAY) { + Array array; + array.push_back("element1"); + array.push_back("element2"); + native_data = array; + } else if (type == Variant::Type::PACKED_BYTE_ARRAY) { + PackedByteArray packed_array; + packed_array.push_back(1); + packed_array.push_back(2); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_INT32_ARRAY) { + PackedInt32Array packed_array; + packed_array.push_back(INT32_MIN); + packed_array.push_back(INT32_MAX); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_INT64_ARRAY) { + PackedInt64Array packed_array; + packed_array.push_back(INT64_MIN); + packed_array.push_back(INT64_MAX); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_FLOAT32_ARRAY) { + PackedFloat32Array packed_array; + packed_array.push_back(FLT_MIN); + packed_array.push_back(FLT_MAX); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_FLOAT64_ARRAY) { + PackedFloat64Array packed_array; + packed_array.push_back(DBL_MIN); + packed_array.push_back(DBL_MAX); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_STRING_ARRAY) { + PackedStringArray packed_array; + packed_array.push_back("string1"); + packed_array.push_back("string2"); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_VECTOR2_ARRAY) { + PackedVector2Array packed_array; + Vector2 vector(1.0, 2.0); + packed_array.push_back(vector); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_VECTOR3_ARRAY) { + PackedVector3Array packed_array; + Vector3 vector(1.0, 2.0, 3.0); + packed_array.push_back(vector); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_COLOR_ARRAY) { + PackedColorArray packed_array; + Color color(1.0, 1.0, 1.0); + packed_array.push_back(color); + native_data = packed_array; + } else if (type == Variant::Type::PACKED_VECTOR4_ARRAY) { + PackedVector4Array packed_array; + Vector4 vector(1.0, 2.0, 3.0, 4.0); + packed_array.push_back(vector); + native_data = packed_array; + } else { + Variant::construct(type, native_data, nullptr, 0, error); + } + Variant json_converted_from_native = JSON::from_native(native_data, true, true); + Variant variant_native_converted = JSON::to_native(json_converted_from_native, true, true); + CHECK_MESSAGE(compare_variants(native_data, variant_native_converted), + vformat("Conversion from native to JSON type %s and back successful. \nNative: %s \nNative Converted: %s \nError: %s\nConversion from native to JSON type %s successful: %s", + Variant::get_type_name(type), + native_data, + variant_native_converted, + itos(error.error), + Variant::get_type_name(type), + json_converted_from_native)); + } +} +} // namespace TestJSONNative + +#endif // TEST_JSON_NATIVE_H diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h index 57bc65328a..f1bb62cb70 100644 --- a/tests/core/object/test_object.h +++ b/tests/core/object/test_object.h @@ -174,6 +174,31 @@ TEST_CASE("[Object] Metadata") { CHECK_MESSAGE( meta_list2.size() == 0, "The metadata list should contain 0 items after removing all metadata items."); + + Object other; + object.set_meta("conflicting_meta", "string"); + object.set_meta("not_conflicting_meta", 123); + other.set_meta("conflicting_meta", Color(0, 1, 0)); + other.set_meta("other_meta", "other"); + object.merge_meta_from(&other); + + CHECK_MESSAGE( + Color(object.get_meta("conflicting_meta")).is_equal_approx(Color(0, 1, 0)), + "String meta should be overwritten with Color after merging."); + + CHECK_MESSAGE( + int(object.get_meta("not_conflicting_meta")) == 123, + "Not conflicting meta on destination should be kept intact."); + + CHECK_MESSAGE( + object.get_meta("other_meta", String()) == "other", + "Not conflicting meta name on source should merged."); + + List<StringName> meta_list3; + object.get_meta_list(&meta_list3); + CHECK_MESSAGE( + meta_list3.size() == 3, + "The metadata list should contain 3 items after merging meta from two objects."); } TEST_CASE("[Object] Construction") { diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 46714a2627..7e1c431a3c 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -48,6 +48,7 @@ #include "tests/core/io/test_image.h" #include "tests/core/io/test_ip.h" #include "tests/core/io/test_json.h" +#include "tests/core/io/test_json_native.h" #include "tests/core/io/test_marshalls.h" #include "tests/core/io/test_pck_packer.h" #include "tests/core/io/test_resource.h" diff --git a/thirdparty/libwebp/patches/godot-clang-cl-fix.patch b/thirdparty/libwebp/patches/godot-clang-cl-fix.patch new file mode 100644 index 0000000000..ee4f598951 --- /dev/null +++ b/thirdparty/libwebp/patches/godot-clang-cl-fix.patch @@ -0,0 +1,19 @@ +diff --git a/thirdparty/libwebp/src/dsp/cpu.h b/thirdparty/libwebp/src/dsp/cpu.h +index c86540f280..4dbe607aec 100644 +--- a/thirdparty/libwebp/src/dsp/cpu.h ++++ b/thirdparty/libwebp/src/dsp/cpu.h +@@ -47,12 +47,12 @@ + // x86 defines. + + #if !defined(HAVE_CONFIG_H) +-#if defined(_MSC_VER) && _MSC_VER > 1310 && \ ++#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER > 1310 && \ + (defined(_M_X64) || defined(_M_IX86)) + #define WEBP_MSC_SSE2 // Visual C++ SSE2 targets + #endif + +-#if defined(_MSC_VER) && _MSC_VER >= 1500 && \ ++#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1500 && \ + (defined(_M_X64) || defined(_M_IX86)) + #define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets + #endif diff --git a/thirdparty/libwebp/src/dsp/cpu.h b/thirdparty/libwebp/src/dsp/cpu.h index c86540f280..4dbe607aec 100644 --- a/thirdparty/libwebp/src/dsp/cpu.h +++ b/thirdparty/libwebp/src/dsp/cpu.h @@ -47,12 +47,12 @@ // x86 defines. #if !defined(HAVE_CONFIG_H) -#if defined(_MSC_VER) && _MSC_VER > 1310 && \ +#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER > 1310 && \ (defined(_M_X64) || defined(_M_IX86)) #define WEBP_MSC_SSE2 // Visual C++ SSE2 targets #endif -#if defined(_MSC_VER) && _MSC_VER >= 1500 && \ +#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1500 && \ (defined(_M_X64) || defined(_M_IX86)) #define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets #endif |