diff options
63 files changed, 650 insertions, 626 deletions
diff --git a/SConstruct b/SConstruct index ff1eba681d..dbcd9dfd68 100644 --- a/SConstruct +++ b/SConstruct @@ -121,38 +121,38 @@ elif os.name == "nt" and methods.get_cmdline_bool("use_mingw", False): # We let SCons build its default ENV as it includes OS-specific things which we don't # want to have to pull in manually. # Then we prepend PATH to make it take precedence, while preserving SCons' own entries. -env_base = Environment(tools=custom_tools) -env_base.PrependENVPath("PATH", os.getenv("PATH")) -env_base.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH")) +env = Environment(tools=custom_tools) +env.PrependENVPath("PATH", os.getenv("PATH")) +env.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH")) if "TERM" in os.environ: # Used for colored output. - env_base["ENV"]["TERM"] = os.environ["TERM"] + env["ENV"]["TERM"] = os.environ["TERM"] -env_base.disabled_modules = [] -env_base.module_version_string = "" -env_base.msvc = False +env.disabled_modules = [] +env.module_version_string = "" +env.msvc = False -env_base.__class__.disable_module = methods.disable_module +env.__class__.disable_module = methods.disable_module -env_base.__class__.add_module_version_string = methods.add_module_version_string +env.__class__.add_module_version_string = methods.add_module_version_string -env_base.__class__.add_source_files = methods.add_source_files -env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix +env.__class__.add_source_files = methods.add_source_files +env.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix -env_base.__class__.add_shared_library = methods.add_shared_library -env_base.__class__.add_library = methods.add_library -env_base.__class__.add_program = methods.add_program -env_base.__class__.CommandNoCache = methods.CommandNoCache -env_base.__class__.Run = methods.Run -env_base.__class__.disable_warnings = methods.disable_warnings -env_base.__class__.force_optimization_on_debug = methods.force_optimization_on_debug -env_base.__class__.module_add_dependencies = methods.module_add_dependencies -env_base.__class__.module_check_dependencies = methods.module_check_dependencies +env.__class__.add_shared_library = methods.add_shared_library +env.__class__.add_library = methods.add_library +env.__class__.add_program = methods.add_program +env.__class__.CommandNoCache = methods.CommandNoCache +env.__class__.Run = methods.Run +env.__class__.disable_warnings = methods.disable_warnings +env.__class__.force_optimization_on_debug = methods.force_optimization_on_debug +env.__class__.module_add_dependencies = methods.module_add_dependencies +env.__class__.module_check_dependencies = methods.module_check_dependencies -env_base["x86_libtheora_opt_gcc"] = False -env_base["x86_libtheora_opt_vc"] = False +env["x86_libtheora_opt_gcc"] = False +env["x86_libtheora_opt_vc"] = False # avoid issues when building with different versions of python out of the same directory -env_base.SConsignFile(File("#.sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)).abspath) +env.SConsignFile(File("#.sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)).abspath) # Build options @@ -275,22 +275,22 @@ opts.Add("linkflags", "Custom flags for the linker") # Update the environment to have all above options defined # in following code (especially platform and custom_modules). -opts.Update(env_base) +opts.Update(env) # Copy custom environment variables if set. -if env_base["import_env_vars"]: - for env_var in str(env_base["import_env_vars"]).split(","): +if env["import_env_vars"]: + for env_var in str(env["import_env_vars"]).split(","): if env_var in os.environ: - env_base["ENV"][env_var] = os.environ[env_var] + env["ENV"][env_var] = os.environ[env_var] # Platform selection: validate input, and add options. selected_platform = "" -if env_base["platform"] != "": - selected_platform = env_base["platform"] -elif env_base["p"] != "": - selected_platform = env_base["p"] +if env["platform"] != "": + selected_platform = env["platform"] +elif env["p"] != "": + selected_platform = env["p"] else: # Missing `platform` argument, try to detect platform automatically if ( @@ -343,7 +343,7 @@ if selected_platform not in platform_list: # Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference. # It should always be re-set after calling `opts.Update()` otherwise it uses the original input value. -env_base["platform"] = selected_platform +env["platform"] = selected_platform # Add platform-specific options. if selected_platform in platform_opts: @@ -351,15 +351,15 @@ if selected_platform in platform_opts: opts.Add(opt) # Update the environment to take platform-specific options into account. -opts.Update(env_base) -env_base["platform"] = selected_platform # Must always be re-set after calling opts.Update(). +opts.Update(env) +env["platform"] = selected_platform # Must always be re-set after calling opts.Update(). # Detect modules. modules_detected = OrderedDict() module_search_paths = ["modules"] # Built-in path. -if env_base["custom_modules"]: - paths = env_base["custom_modules"].split(",") +if env["custom_modules"]: + paths = env["custom_modules"].split(",") for p in paths: try: module_search_paths.append(methods.convert_custom_modules_path(p)) @@ -373,13 +373,13 @@ for path in module_search_paths: # so save the time it takes to parse directories. modules = methods.detect_modules(path, recursive=False) else: # Custom. - modules = methods.detect_modules(path, env_base["custom_modules_recursive"]) + modules = methods.detect_modules(path, env["custom_modules_recursive"]) # Provide default include path for both the custom module search `path` # and the base directory containing custom modules, as it may be different # from the built-in "modules" name (e.g. "custom_modules/summator/summator.h"), # so it can be referenced simply as `#include "summator/summator.h"` # independently of where a module is located on user's filesystem. - env_base.Prepend(CPPPATH=[path, os.path.dirname(path)]) + env.Prepend(CPPPATH=[path, os.path.dirname(path)]) # Note: custom modules can override built-in ones. modules_detected.update(modules) @@ -388,7 +388,7 @@ for name, path in modules_detected.items(): sys.path.insert(0, path) import config - if env_base["modules_enabled_by_default"]: + if env["modules_enabled_by_default"]: enabled = True try: enabled = config.is_enabled() @@ -412,17 +412,17 @@ for name, path in modules_detected.items(): methods.write_modules(modules_detected) # Update the environment again after all the module options are added. -opts.Update(env_base) -env_base["platform"] = selected_platform # Must always be re-set after calling opts.Update(). -Help(opts.GenerateHelpText(env_base)) +opts.Update(env) +env["platform"] = selected_platform # Must always be re-set after calling opts.Update(). +Help(opts.GenerateHelpText(env)) # add default include paths -env_base.Prepend(CPPPATH=["#"]) +env.Prepend(CPPPATH=["#"]) # configure ENV for platform -env_base.platform_exporters = platform_exporters -env_base.platform_apis = platform_apis +env.platform_exporters = platform_exporters +env.platform_apis = platform_apis # Configuration of build targets: # - Editor or template @@ -431,71 +431,69 @@ env_base.platform_apis = platform_apis # - Optimization level # - Debug symbols for crash traces / debuggers -env_base.editor_build = env_base["target"] == "editor" -env_base.dev_build = env_base["dev_build"] -env_base.debug_features = env_base["target"] in ["editor", "template_debug"] +env.editor_build = env["target"] == "editor" +env.dev_build = env["dev_build"] +env.debug_features = env["target"] in ["editor", "template_debug"] -if env_base.dev_build: +if env.dev_build: opt_level = "none" -elif env_base.debug_features: +elif env.debug_features: opt_level = "speed_trace" else: # Release opt_level = "speed" -env_base["optimize"] = ARGUMENTS.get("optimize", opt_level) -env_base["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", env_base.dev_build) +env["optimize"] = ARGUMENTS.get("optimize", opt_level) +env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", env.dev_build) -if env_base.editor_build: - env_base.Append(CPPDEFINES=["TOOLS_ENABLED"]) +if env.editor_build: + env.Append(CPPDEFINES=["TOOLS_ENABLED"]) -if env_base.debug_features: +if env.debug_features: # DEBUG_ENABLED enables debugging *features* and debug-only code, which is intended # to give *users* extra debugging information for their game development. - env_base.Append(CPPDEFINES=["DEBUG_ENABLED"]) + env.Append(CPPDEFINES=["DEBUG_ENABLED"]) -if env_base.dev_build: +if env.dev_build: # DEV_ENABLED enables *engine developer* code which should only be compiled for those # working on the engine itself. - env_base.Append(CPPDEFINES=["DEV_ENABLED"]) + env.Append(CPPDEFINES=["DEV_ENABLED"]) else: # Disable assert() for production targets (only used in thirdparty code). - env_base.Append(CPPDEFINES=["NDEBUG"]) + env.Append(CPPDEFINES=["NDEBUG"]) # SCons speed optimization controlled by the `fast_unsafe` option, which provide # more than 10 s speed up for incremental rebuilds. # Unsafe as they reduce the certainty of rebuilding all changed files, so it's # enabled by default for `debug` builds, and can be overridden from command line. # Ref: https://github.com/SCons/scons/wiki/GoFastButton -if methods.get_cmdline_bool("fast_unsafe", env_base.dev_build): +if methods.get_cmdline_bool("fast_unsafe", env.dev_build): # Renamed to `content-timestamp` in SCons >= 4.2, keeping MD5 for compat. - env_base.Decider("MD5-timestamp") - env_base.SetOption("implicit_cache", 1) - env_base.SetOption("max_drift", 60) + env.Decider("MD5-timestamp") + env.SetOption("implicit_cache", 1) + env.SetOption("max_drift", 60) -if env_base["use_precise_math_checks"]: - env_base.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"]) +if env["use_precise_math_checks"]: + env.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"]) -if env_base.editor_build and env_base["engine_update_check"]: - env_base.Append(CPPDEFINES=["ENGINE_UPDATE_CHECK_ENABLED"]) +if env.editor_build and env["engine_update_check"]: + env.Append(CPPDEFINES=["ENGINE_UPDATE_CHECK_ENABLED"]) -if not env_base.File("#main/splash_editor.png").exists(): +if not env.File("#main/splash_editor.png").exists(): # Force disabling editor splash if missing. - env_base["no_editor_splash"] = True -if env_base["no_editor_splash"]: - env_base.Append(CPPDEFINES=["NO_EDITOR_SPLASH"]) + env["no_editor_splash"] = True +if env["no_editor_splash"]: + env.Append(CPPDEFINES=["NO_EDITOR_SPLASH"]) -if not env_base["deprecated"]: - env_base.Append(CPPDEFINES=["DISABLE_DEPRECATED"]) +if not env["deprecated"]: + env.Append(CPPDEFINES=["DISABLE_DEPRECATED"]) -if env_base["precision"] == "double": - env_base.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"]) +if env["precision"] == "double": + env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"]) tmppath = "./platform/" + selected_platform sys.path.insert(0, tmppath) import detect -env = env_base.Clone() - # Default num_jobs to local cpu count if not user specified. # SCons has a peculiarity where user-specified options won't be overridden # by SetOption, so we can rely on this to know if we should use our default. @@ -570,7 +568,7 @@ if env["production"]: # Run SCU file generation script if in a SCU build. if env["scu_build"]: max_includes_per_scu = 8 - if env_base.dev_build == True: + if env.dev_build == True: max_includes_per_scu = 1024 read_scu_limit = int(env["scu_limit"]) @@ -844,7 +842,7 @@ suffix += "." + env["target"] if env.dev_build: suffix += ".dev" -if env_base["precision"] == "double": +if env["precision"] == "double": suffix += ".double" suffix += "." + env["arch"] diff --git a/core/object/object.cpp b/core/object/object.cpp index 06f6e8e9e6..f8d2feb5a8 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1100,6 +1100,20 @@ bool Object::_has_user_signal(const StringName &p_name) const { return signal_map[p_name].user.name.length() > 0; } +void Object::_remove_user_signal(const StringName &p_name) { + SignalData *s = signal_map.getptr(p_name); + ERR_FAIL_NULL_MSG(s, "Provided signal does not exist."); + ERR_FAIL_COND_MSG(!s->removable, "Signal is not removable (not added with add_user_signal)."); + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + Object *target = slot_kv.key.get_object(); + if (likely(target)) { + target->connections.erase(slot_kv.value.cE); + } + } + + signal_map.erase(p_name); +} + Error Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (unlikely(p_argcount < 1)) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -1248,6 +1262,10 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) { } add_user_signal(mi); + + if (signal_map.has(p_name)) { + signal_map.getptr(p_name)->removable = true; + } } TypedArray<Dictionary> Object::_get_signal_list() const { @@ -1661,6 +1679,7 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("add_user_signal", "signal", "arguments"), &Object::_add_user_signal, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("has_user_signal", "signal"), &Object::_has_user_signal); + ClassDB::bind_method(D_METHOD("remove_user_signal", "signal"), &Object::_remove_user_signal); { MethodInfo mi; diff --git a/core/object/object.h b/core/object/object.h index d9551ecd01..915c3a8c25 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -619,6 +619,7 @@ private: MethodInfo user; HashMap<Callable, Slot, HashableHasher<Callable>> slot_map; + bool removable = false; }; HashMap<StringName, SignalData> signal_map; @@ -646,6 +647,7 @@ private: void _add_user_signal(const String &p_name, const Array &p_args = Array()); bool _has_user_signal(const StringName &p_name) const; + void _remove_user_signal(const StringName &p_name); Error _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error); TypedArray<Dictionary> _get_signal_list() const; TypedArray<Dictionary> _get_signal_connection_list(const StringName &p_signal) const; diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp index c10f491a11..e0b8730a67 100644 --- a/core/object/worker_thread_pool.cpp +++ b/core/object/worker_thread_pool.cpp @@ -491,12 +491,10 @@ void WorkerThreadPool::notify_yield_over(TaskID p_task_id) { ERR_FAIL_MSG("Invalid Task ID."); } Task *task = *taskp; - -#ifdef DEBUG_ENABLED - if (task->pool_thread_index == get_thread_index()) { - WARN_PRINT("A worker thread is attempting to notify itself. That makes no sense."); + if (task->completed) { + task_mutex.unlock(); + return; } -#endif ThreadData &td = threads[task->pool_thread_index]; td.yield_is_over = true; diff --git a/doc/classes/AcceptDialog.xml b/doc/classes/AcceptDialog.xml index 7fbdd4272d..392364425b 100644 --- a/doc/classes/AcceptDialog.xml +++ b/doc/classes/AcceptDialog.xml @@ -100,6 +100,12 @@ </signal> </signals> <theme_items> + <theme_item name="buttons_min_height" data_type="constant" type="int" default="0"> + The minimum height of each button in the bottom row (such as OK/Cancel) in pixels. This can be increased to make buttons with short texts easier to click/tap. + </theme_item> + <theme_item name="buttons_min_width" data_type="constant" type="int" default="0"> + The minimum width of each button in the bottom row (such as OK/Cancel) in pixels. This can be increased to make buttons with short texts easier to click/tap. + </theme_item> <theme_item name="buttons_separation" data_type="constant" type="int" default="10"> The size of the vertical space between the dialog's content and the button row. </theme_item> diff --git a/doc/classes/FileSystemDock.xml b/doc/classes/FileSystemDock.xml index 9028dd4b9f..b3dc51ffaa 100644 --- a/doc/classes/FileSystemDock.xml +++ b/doc/classes/FileSystemDock.xml @@ -51,6 +51,11 @@ Emitted when a file is moved from [param old_file] path to [param new_file] path. </description> </signal> + <signal name="folder_color_changed"> + <description> + Emitted when folders change color. + </description> + </signal> <signal name="folder_moved"> <param index="0" name="old_folder" type="String" /> <param index="1" name="new_folder" type="String" /> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index c508591093..961cb2e684 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -357,7 +357,7 @@ <param index="0" name="signal" type="String" /> <param index="1" name="arguments" type="Array" default="[]" /> <description> - Adds a user-defined [param signal]. Optional arguments for the signal can be added as an [Array] of dictionaries, each defining a [code]name[/code] [String] and a [code]type[/code] [int] (see [enum Variant.Type]). See also [method has_user_signal]. + Adds a user-defined [param signal]. Optional arguments for the signal can be added as an [Array] of dictionaries, each defining a [code]name[/code] [String] and a [code]type[/code] [int] (see [enum Variant.Type]). See also [method has_user_signal] and [method remove_user_signal]. [codeblocks] [gdscript] add_user_signal("hurt", [ @@ -797,7 +797,7 @@ <return type="bool" /> <param index="0" name="signal" type="StringName" /> <description> - Returns [code]true[/code] if the given user-defined [param signal] name exists. Only signals added with [method add_user_signal] are included. + Returns [code]true[/code] if the given user-defined [param signal] name exists. Only signals added with [method add_user_signal] are included. See also [method remove_user_signal]. </description> </method> <method name="is_blocking_signals" qualifiers="const"> @@ -905,6 +905,13 @@ [b]Note:[/b] Metadata that has a name starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. </description> </method> + <method name="remove_user_signal" experimental=""> + <return type="void" /> + <param index="0" name="signal" type="StringName" /> + <description> + Removes the given user signal [param signal] from the object. See also [method add_user_signal] and [method has_user_signal]. + </description> + </method> <method name="set"> <return type="void" /> <param index="0" name="property" type="StringName" /> diff --git a/doc/classes/SkeletonIK3D.xml b/doc/classes/SkeletonIK3D.xml index 6de6d9d186..4858a6ce22 100644 --- a/doc/classes/SkeletonIK3D.xml +++ b/doc/classes/SkeletonIK3D.xml @@ -55,6 +55,9 @@ </method> </methods> <members> + <member name="interpolation" type="float" setter="set_interpolation" getter="get_interpolation" deprecated="Use [member SkeletonModifier3D.influence] instead."> + Interpolation value for how much the IK results are applied to the current skeleton bone chain. A value of [code]1.0[/code] will overwrite all skeleton bone transforms completely while a value of [code]0.0[/code] will visually disable the SkeletonIK. + </member> <member name="magnet" type="Vector3" setter="set_magnet_position" getter="get_magnet_position" default="Vector3(0, 0, 0)"> Secondary target position (first is [member target] property or [member target_node]) for the IK chain. Use magnet position (pole target) to control the bending of the IK chain. Only works if the bone chain has more than 2 bones. The middle chain bone position will be linearly interpolated with the magnet position. </member> diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index ecb563214c..bc1af86938 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3112,39 +3112,47 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (pass == 0) { spec_constants |= SceneShaderGLES3::BASE_PASS; - if (inst->omni_light_gl_cache.size() == 0) { - spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI; - } - if (inst->spot_light_gl_cache.size() == 0) { + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT; - } - - if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) { spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; - } + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; + } else { + if (inst->omni_light_gl_cache.size() == 0) { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI; + } - if (inst->reflection_probe_rid_cache.size() == 0) { - // We don't have any probes. - spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE; - } else if (inst->reflection_probe_rid_cache.size() > 1) { - // We have a second probe. - spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE; - } + if (inst->spot_light_gl_cache.size() == 0) { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT; + } - if (inst->lightmap_instance.is_valid()) { - spec_constants |= SceneShaderGLES3::USE_LIGHTMAP; + if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; + } - GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); - GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + if (inst->reflection_probe_rid_cache.size() == 0) { + // We don't have any probes. + spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE; + } else if (inst->reflection_probe_rid_cache.size() > 1) { + // We have a second probe. + spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE; + } - if (lm->uses_spherical_harmonics) { - spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP; + if (inst->lightmap_instance.is_valid()) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP; + + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + if (lm->uses_spherical_harmonics) { + spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP; + } + } else if (inst->lightmap_sh) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE; + } else { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; } - } else if (inst->lightmap_sh) { - spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE; - } else { - spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; } } else { // Only base pass uses the radiance map. diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 4c66068953..f7914d3aaa 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -516,7 +516,14 @@ void CreateDialog::select_type(const String &p_type, bool p_center_on_item) { favorite->set_disabled(false); favorite->set_pressed(favorite_list.has(p_type)); - get_ok_button()->set_disabled(false); + + if (to_select->get_meta("__instantiable", true)) { + get_ok_button()->set_disabled(false); + get_ok_button()->set_tooltip_text(String()); + } else { + get_ok_button()->set_disabled(true); + get_ok_button()->set_tooltip_text(TTR("The selected class can't be instantiated.")); + } } void CreateDialog::select_base() { diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 8ae1752ca7..14190a43a5 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1664,7 +1664,10 @@ void EditorFileSystem::_queue_update_script_class(const String &p_path) { } void EditorFileSystem::_update_scene_groups() { - EditorProgress ep("update_scene_groups", TTR("Update Scene Groups"), update_scene_paths.size()); + EditorProgress *ep = nullptr; + if (update_scene_paths.size() > 1) { + ep = memnew(EditorProgress("update_scene_groups", TTR("Update Scene Groups"), update_scene_paths.size())); + } int step_count = 0; update_scene_mutex.lock(); @@ -1684,9 +1687,12 @@ void EditorFileSystem::_update_scene_groups() { ProjectSettings::get_singleton()->add_scene_groups_cache(path, scene_groups); } - ep.step(TTR("Updating Scene Groups..."), step_count++); + if (ep) { + ep->step(TTR("Updating Scene Groups..."), step_count++); + } } + memdelete_notnull(ep); update_scene_paths.clear(); update_scene_mutex.unlock(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 20fa30ff34..5b24fb1559 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2364,7 +2364,13 @@ static bool overrides_external_editor(Object *p_object) { void EditorNode::_add_to_history(const Object *p_object, const String &p_property, bool p_inspector_only) { ObjectID id = p_object->get_instance_id(); - if (id != editor_history.get_current()) { + ObjectID history_id = editor_history.get_current(); + if (id != history_id) { + const MultiNodeEdit *multi_node_edit = Object::cast_to<const MultiNodeEdit>(p_object); + const MultiNodeEdit *history_multi_node_edit = Object::cast_to<const MultiNodeEdit>(ObjectDB::get_instance(history_id)); + if (multi_node_edit && history_multi_node_edit && multi_node_edit->is_same_selection(history_multi_node_edit)) { + return; + } if (p_inspector_only) { editor_history.add_object(id, String(), true); } else if (p_property.is_empty()) { @@ -2458,6 +2464,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update if (current_node->is_inside_tree()) { NodeDock::get_singleton()->set_node(current_node); SceneTreeDock::get_singleton()->set_selected(current_node); + SceneTreeDock::get_singleton()->set_selection({ current_node }); InspectorDock::get_singleton()->update(current_node); if (!inspector_only && !skip_main_plugin) { skip_main_plugin = stay_in_script_editor_on_node_selected && !ScriptEditor::get_singleton()->is_editor_floating() && ScriptEditor::get_singleton()->is_visible_in_tree(); @@ -2479,13 +2486,13 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update } else { Node *selected_node = nullptr; + Vector<Node *> multi_nodes; if (current_obj->is_class("MultiNodeEdit")) { Node *scene = get_edited_scene(); if (scene) { MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(current_obj); int node_count = multi_node_edit->get_node_count(); if (node_count > 0) { - List<Node *> multi_nodes; for (int node_index = 0; node_index < node_count; ++node_index) { Node *node = scene->get_node(multi_node_edit->get_node(node_index)); if (node) { @@ -2495,7 +2502,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update if (!multi_nodes.is_empty()) { // Pick the top-most node. multi_nodes.sort_custom<Node::Comparator>(); - selected_node = multi_nodes.front()->get(); + selected_node = multi_nodes[0]; } } } @@ -2504,6 +2511,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update InspectorDock::get_inspector_singleton()->edit(current_obj); NodeDock::get_singleton()->set_node(nullptr); SceneTreeDock::get_singleton()->set_selected(selected_node); + SceneTreeDock::get_singleton()->set_selection(multi_nodes); InspectorDock::get_singleton()->update(nullptr); } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 2ed547a970..b811d0f21b 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -234,15 +234,15 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory Color custom_color = has_custom_color ? folder_colors[assigned_folder_colors[lpath]] : Color(); if (has_custom_color) { - subdirectory_item->set_icon_modulate(0, editor_is_dark_theme ? custom_color : custom_color * 1.75); - subdirectory_item->set_custom_bg_color(0, Color(custom_color, editor_is_dark_theme ? 0.1 : 0.15)); + subdirectory_item->set_icon_modulate(0, editor_is_dark_theme ? custom_color : custom_color * ITEM_COLOR_SCALE); + subdirectory_item->set_custom_bg_color(0, Color(custom_color, editor_is_dark_theme ? ITEM_ALPHA_MIN : ITEM_ALPHA_MAX)); } else { TreeItem *parent = subdirectory_item->get_parent(); if (parent) { Color parent_bg_color = parent->get_custom_bg_color(0); if (parent_bg_color != Color()) { bool parent_has_custom_color = assigned_folder_colors.has(parent->get_metadata(0)); - subdirectory_item->set_custom_bg_color(0, parent_has_custom_color ? parent_bg_color.darkened(0.3) : parent_bg_color); + subdirectory_item->set_custom_bg_color(0, parent_has_custom_color ? parent_bg_color.darkened(ITEM_BG_DARK_SCALE) : parent_bg_color); subdirectory_item->set_icon_modulate(0, parent->get_icon_modulate(0)); } else { subdirectory_item->set_icon_modulate(0, get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"))); @@ -328,7 +328,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory file_item->set_icon_max_width(0, icon_size); Color parent_bg_color = subdirectory_item->get_custom_bg_color(0); if (has_custom_color) { - file_item->set_custom_bg_color(0, parent_bg_color.darkened(0.3)); + file_item->set_custom_bg_color(0, parent_bg_color.darkened(ITEM_BG_DARK_SCALE)); } else if (parent_bg_color != Color()) { file_item->set_custom_bg_color(0, parent_bg_color); } @@ -1068,7 +1068,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { files->set_item_metadata(-1, bd); files->set_item_selectable(-1, false); - files->set_item_icon_modulate(-1, editor_is_dark_theme ? inherited_folder_color : inherited_folder_color * 1.75); + files->set_item_icon_modulate(-1, editor_is_dark_theme ? inherited_folder_color : inherited_folder_color * ITEM_COLOR_SCALE); } bool reversed = file_sort == FILE_SORT_NAME_REVERSE; @@ -1082,7 +1082,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { files->add_item(dname, folder_icon, true); files->set_item_metadata(-1, dpath); Color this_folder_color = has_custom_color ? folder_colors[assigned_folder_colors[dpath]] : inherited_folder_color; - files->set_item_icon_modulate(-1, editor_is_dark_theme ? this_folder_color : this_folder_color * 1.75); + files->set_item_icon_modulate(-1, editor_is_dark_theme ? this_folder_color : this_folder_color * ITEM_COLOR_SCALE); if (previous_selection.has(dname)) { files->select(files->get_item_count() - 1, false); @@ -3086,6 +3086,8 @@ void FileSystemDock::_folder_color_index_pressed(int p_index, PopupMenu *p_menu) _update_tree(get_uncollapsed_paths()); _update_file_list(true); + + emit_signal(SNAME("folder_color_changed")); } void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vector<String> &p_paths, bool p_display_path_dependent_options) { @@ -3796,6 +3798,7 @@ void FileSystemDock::_bind_methods() { ADD_SIGNAL(MethodInfo("folder_removed", PropertyInfo(Variant::STRING, "folder"))); ADD_SIGNAL(MethodInfo("files_moved", PropertyInfo(Variant::STRING, "old_file"), PropertyInfo(Variant::STRING, "new_file"))); ADD_SIGNAL(MethodInfo("folder_moved", PropertyInfo(Variant::STRING, "old_folder"), PropertyInfo(Variant::STRING, "new_folder"))); + ADD_SIGNAL(MethodInfo("folder_color_changed")); ADD_SIGNAL(MethodInfo("display_mode_changed")); } diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 8830f31d2d..f514de15e5 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -381,6 +381,11 @@ protected: static void _bind_methods(); public: + static constexpr double ITEM_COLOR_SCALE = 1.75; + static constexpr double ITEM_ALPHA_MIN = 0.1; + static constexpr double ITEM_ALPHA_MAX = 0.15; + static constexpr double ITEM_BG_DARK_SCALE = 0.3; + const HashMap<String, Color> &get_folder_colors() const; Dictionary get_assigned_folder_colors() const; diff --git a/editor/gui/editor_dir_dialog.cpp b/editor/gui/editor_dir_dialog.cpp index 8821a0eeae..9d5464210b 100644 --- a/editor/gui/editor_dir_dialog.cpp +++ b/editor/gui/editor_dir_dialog.cpp @@ -32,27 +32,44 @@ #include "editor/directory_create_dialog.h" #include "editor/editor_file_system.h" +#include "editor/filesystem_dock.h" +#include "editor/themes/editor_theme_manager.h" #include "scene/gui/box_container.h" #include "scene/gui/tree.h" #include "servers/display_server.h" -void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path) { +void EditorDirDialog::_update_dir(const Color &p_default_folder_color, const Dictionary &p_assigned_folder_colors, const HashMap<String, Color> &p_folder_colors, bool p_is_dark_theme, TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path) { updating = true; const String path = p_dir->get_path(); p_item->set_metadata(0, path); p_item->set_icon(0, tree->get_editor_theme_icon(SNAME("Folder"))); - p_item->set_icon_modulate(0, tree->get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"))); if (!p_item->get_parent()) { p_item->set_text(0, "res://"); + p_item->set_icon_modulate(0, p_default_folder_color); } else { if (!opened_paths.has(path) && (p_select_path.is_empty() || !p_select_path.begins_with(path))) { p_item->set_collapsed(true); } p_item->set_text(0, p_dir->get_name()); + + if (p_assigned_folder_colors.has(path)) { + const Color &folder_color = p_folder_colors[p_assigned_folder_colors[path]]; + p_item->set_icon_modulate(0, p_is_dark_theme ? folder_color : folder_color * FileSystemDock::ITEM_COLOR_SCALE); + p_item->set_custom_bg_color(0, Color(folder_color, p_is_dark_theme ? FileSystemDock::ITEM_ALPHA_MIN : FileSystemDock::ITEM_ALPHA_MAX)); + } else { + TreeItem *parent_item = p_item->get_parent(); + Color parent_bg_color = parent_item->get_custom_bg_color(0); + if (parent_bg_color != Color()) { + p_item->set_custom_bg_color(0, p_assigned_folder_colors.has(parent_item->get_metadata(0)) ? parent_bg_color.darkened(FileSystemDock::ITEM_BG_DARK_SCALE) : parent_bg_color); + p_item->set_icon_modulate(0, parent_item->get_icon_modulate(0)); + } else { + p_item->set_icon_modulate(0, p_default_folder_color); + } + } } if (path == new_dir_path || !p_item->get_parent()) { @@ -62,7 +79,7 @@ void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p updating = false; for (int i = 0; i < p_dir->get_subdir_count(); i++) { TreeItem *ti = tree->create_item(p_item); - _update_dir(ti, p_dir->get_subdir(i)); + _update_dir(p_default_folder_color, p_assigned_folder_colors, p_folder_colors, p_is_dark_theme, ti, p_dir->get_subdir(i)); } } @@ -90,7 +107,7 @@ void EditorDirDialog::reload(const String &p_path) { tree->clear(); TreeItem *root = tree->create_item(); - _update_dir(root, EditorFileSystem::get_singleton()->get_filesystem(), p_path); + _update_dir(tree->get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog")), FileSystemDock::get_singleton()->get_assigned_folder_colors(), FileSystemDock::get_singleton()->get_folder_colors(), EditorThemeManager::is_dark_theme(), root, EditorFileSystem::get_singleton()->get_filesystem(), p_path); _item_collapsed(root); new_dir_path.clear(); must_reload = false; @@ -99,6 +116,8 @@ void EditorDirDialog::reload(const String &p_path) { void EditorDirDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { + FileSystemDock::get_singleton()->connect("folder_color_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); + EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); reload(); diff --git a/editor/gui/editor_dir_dialog.h b/editor/gui/editor_dir_dialog.h index 40badec212..b10cc8dd9f 100644 --- a/editor/gui/editor_dir_dialog.h +++ b/editor/gui/editor_dir_dialog.h @@ -53,7 +53,7 @@ class EditorDirDialog : public ConfirmationDialog { void _item_collapsed(Object *p_item); void _item_activated(); - void _update_dir(TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path = String()); + void _update_dir(const Color &p_default_folder_color, const Dictionary &p_assigned_folder_colors, const HashMap<String, Color> &p_folder_colors, bool p_is_dark_theme, TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path = String()); void _make_dir(); void _make_dir_confirm(const String &p_path); diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index 25c8610ff4..a7cdb65145 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -968,8 +968,10 @@ void EditorFileDialog::update_file_list() { } } else if (!dir_access->current_is_hidden()) { String full_path = cdir == "res://" ? item : dir_access->get_current_dir() + "/" + item; - if (dir_access->current_is_dir() && (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path))) { - dirs.push_back(item); + if (dir_access->current_is_dir()) { + if (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path)) { + dirs.push_back(item); + } } else { files.push_back(item); } diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp index 4f0db70681..45786c0ab5 100644 --- a/editor/multi_node_edit.cpp +++ b/editor/multi_node_edit.cpp @@ -244,7 +244,7 @@ int MultiNodeEdit::get_node_count() const { } NodePath MultiNodeEdit::get_node(int p_index) const { - ERR_FAIL_INDEX_V(p_index, nodes.size(), NodePath()); + ERR_FAIL_INDEX_V(p_index, get_node_count(), NodePath()); return nodes[p_index]; } diff --git a/editor/multi_node_edit.h b/editor/multi_node_edit.h index deef88633e..000d41c4c1 100644 --- a/editor/multi_node_edit.h +++ b/editor/multi_node_edit.h @@ -36,7 +36,7 @@ class MultiNodeEdit : public RefCounted { GDCLASS(MultiNodeEdit, RefCounted); - List<NodePath> nodes; + LocalVector<NodePath> nodes; struct PLData { int uses = 0; PropertyInfo info; @@ -67,6 +67,19 @@ public: void set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field); + // If the nodes selected are the same independently of order then return true. + bool is_same_selection(const MultiNodeEdit *p_other) const { + if (get_node_count() != p_other->get_node_count()) { + return false; + } + for (int i = 0; i < get_node_count(); i++) { + if (nodes.find(p_other->get_node(i)) == -1) { + return false; + } + } + + return true; + } MultiNodeEdit(); }; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 05b8b81fa7..0dd845270a 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -953,8 +953,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool bool port_left_used = false; String name_left; if (valid_left) { - name_left = vsnode->get_input_port_name(i); - port_left = vsnode->get_input_port_type(i); + name_left = vsnode->get_input_port_name(j); + port_left = vsnode->get_input_port_type(j); for (const VisualShader::Connection &E : connections) { if (E.to_node == p_id && E.to_port == j) { port_left_used = true; @@ -989,15 +989,15 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool Variant default_value; if (valid_left && !port_left_used) { - default_value = vsnode->get_input_port_default_value(i); + default_value = vsnode->get_input_port_default_value(j); } Button *button = memnew(Button); hb->add_child(button); - register_default_input_button(p_id, i, button); - button->connect("pressed", callable_mp(editor, &VisualShaderEditor::_edit_port_default_input).bind(button, p_id, i)); + register_default_input_button(p_id, j, button); + button->connect("pressed", callable_mp(editor, &VisualShaderEditor::_edit_port_default_input).bind(button, p_id, j)); if (default_value.get_type() != Variant::NIL) { // only a label - set_input_port_default_value(p_type, p_id, i, default_value); + set_input_port_default_value(p_type, p_id, j, default_value); } else { button->hide(); } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 0b489cbb36..e1084d4873 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -3020,6 +3020,13 @@ void SceneTreeDock::set_edited_scene(Node *p_scene) { edited_scene = p_scene; } +void SceneTreeDock::set_selection(const Vector<Node *> &p_nodes) { + editor_selection->clear(); + for (Node *node : p_nodes) { + editor_selection->add_node(node); + } +} + void SceneTreeDock::set_selected(Node *p_node, bool p_emit_selected) { scene_tree->set_selected(p_node, p_emit_selected); } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 86e9ff8a47..21e1b00f93 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -322,6 +322,7 @@ public: void set_edited_scene(Node *p_scene); void instantiate(const String &p_file); void instantiate_scenes(const Vector<String> &p_files, Node *p_parent = nullptr); + void set_selection(const Vector<Node *> &p_nodes); void set_selected(Node *p_node, bool p_emit_selected = false); void fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames); void perform_node_renames(Node *p_base, HashMap<Node *, NodePath> *p_renames, HashMap<Ref<Animation>, HashSet<int>> *r_rem_anims = nullptr); diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index 734fa415a3..f74be7d978 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -356,20 +356,25 @@ EditorThemeManager::ThemeConfiguration EditorThemeManager::_create_theme_config( if (config.spacing_preset != "Custom") { int preset_base_spacing = 0; int preset_extra_spacing = 0; + Size2 preset_dialogs_buttons_min_size; if (config.spacing_preset == "Compact") { preset_base_spacing = 0; preset_extra_spacing = 4; + preset_dialogs_buttons_min_size = Size2(90, 26); } else if (config.spacing_preset == "Spacious") { preset_base_spacing = 6; preset_extra_spacing = 2; + preset_dialogs_buttons_min_size = Size2(112, 36); } else { // Default preset_base_spacing = 4; preset_extra_spacing = 0; + preset_dialogs_buttons_min_size = Size2(105, 34); } config.base_spacing = preset_base_spacing; config.extra_spacing = preset_extra_spacing; + config.dialogs_buttons_min_size = preset_dialogs_buttons_min_size; EditorSettings::get_singleton()->set_initial_value("interface/theme/base_spacing", config.base_spacing); EditorSettings::get_singleton()->set_initial_value("interface/theme/additional_spacing", config.extra_spacing); @@ -1271,6 +1276,9 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the // AcceptDialog. p_theme->set_stylebox("panel", "AcceptDialog", p_config.dialog_style); p_theme->set_constant("buttons_separation", "AcceptDialog", 8 * EDSCALE); + // Make buttons with short texts such as "OK" easier to click/tap. + p_theme->set_constant("buttons_min_width", "AcceptDialog", p_config.dialogs_buttons_min_size.x * EDSCALE); + p_theme->set_constant("buttons_min_height", "AcceptDialog", p_config.dialogs_buttons_min_size.y * EDSCALE); // FileDialog. p_theme->set_icon("folder", "FileDialog", p_theme->get_icon(SNAME("Folder"), EditorStringName(EditorIcons))); diff --git a/editor/themes/editor_theme_manager.h b/editor/themes/editor_theme_manager.h index 3eb1dd5ffd..5e7bd00083 100644 --- a/editor/themes/editor_theme_manager.h +++ b/editor/themes/editor_theme_manager.h @@ -62,6 +62,7 @@ class EditorThemeManager { int base_spacing = 4; int extra_spacing = 0; + Size2 dialogs_buttons_min_size = Size2(105, 34); int border_width = 0; int corner_radius = 3; diff --git a/main/main.cpp b/main/main.cpp index ed74094c9e..801e8934b0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -842,21 +842,26 @@ void Main::test_cleanup() { #endif int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) { -#ifdef TESTS_ENABLED for (int x = 0; x < argc; x++) { if ((strncmp(argv[x], "--test", 6) == 0) && (strlen(argv[x]) == 6)) { tests_need_run = true; +#ifdef TESTS_ENABLED // TODO: need to come up with different test contexts. // Not every test requires high-level functionality like `ClassDB`. test_setup(); int status = test_main(argc, argv); test_cleanup(); return status; +#else + ERR_PRINT( + "`--test` was specified on the command line, but this Godot binary was compiled without support for unit tests. Aborting.\n" + "To be able to run unit tests, use the `tests=yes` SCons option when compiling Godot.\n"); + return EXIT_FAILURE; +#endif } } -#endif tests_need_run = false; - return 0; + return EXIT_SUCCESS; } /* Engine initialization diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index ea97a2150e..43349ae6ef 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -264,9 +264,6 @@ Validate extension JSON: API was removed: classes/VisualShaderNodeComment/proper GH-87888 -------- Validate extension JSON: API was removed: classes/Skeleton3D/properties/animate_physical_bones -Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/get_interpolation -Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/set_interpolation -Validate extension JSON: API was removed: classes/SkeletonIK3D/properties/interpolation These base class is changed to SkeletonModifier3D which is processed by Skeleton3D with the assumption that it is Skeleton3D's child. diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp index bde23289f4..44a929d285 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -1629,6 +1629,9 @@ void FBXDocument::_generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTF active_skeleton = skeleton; current_node = active_skeleton; + if (active_skeleton) { + p_scene_parent = active_skeleton; + } if (requires_extra_node) { current_node = nullptr; @@ -2019,8 +2022,8 @@ Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool GLTFNodeIndex fbx_root = state->root_nodes.write[0]; Node *fbx_root_node = state->get_scene_node(fbx_root); Node *root = fbx_root_node; - if (fbx_root_node && fbx_root_node->get_parent()) { - root = fbx_root_node->get_parent(); + if (root && root->get_owner() && root->get_owner() != root) { + root = root->get_owner(); } ERR_FAIL_NULL_V(root, nullptr); _process_mesh_instances(state, root); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index f8c35ab6d1..8f0f0d219e 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5685,6 +5685,9 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, const GL active_skeleton = skeleton; current_node = active_skeleton; + if (active_skeleton) { + p_scene_parent = active_skeleton; + } if (requires_extra_node) { current_node = nullptr; @@ -7104,6 +7107,9 @@ Node *GLTFDocument::_generate_scene_node_tree(Ref<GLTFState> p_state) { if (p_state->extensions_used.has("GODOT_single_root")) { _generate_scene_node(p_state, 0, nullptr, nullptr); single_root = p_state->scene_nodes[0]; + if (single_root && single_root->get_owner() && single_root->get_owner() != single_root) { + single_root = single_root->get_owner(); + } } else { single_root = memnew(Node3D); for (int32_t root_i = 0; root_i < p_state->root_nodes.size(); root_i++) { diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml index 168e0bf077..b9c69075e1 100644 --- a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml +++ b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRCompositionLayer" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRCompositionLayer" inherits="Node3D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> The parent class of all OpenXR composition layer nodes. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml b/modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml index 2de1977671..dd8a11e2b9 100644 --- a/modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml +++ b/modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRCompositionLayerCylinder" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRCompositionLayerCylinder" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> An OpenXR composition layer that is rendered as an internal slice of a cylinder. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml b/modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml index f6eba7e228..716ea72854 100644 --- a/modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml +++ b/modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRCompositionLayerEquirect" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRCompositionLayerEquirect" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> An OpenXR composition layer that is rendered as an internal slice of a sphere. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml b/modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml index fff592bc4f..6632f90ed2 100644 --- a/modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml +++ b/modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRCompositionLayerQuad" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRCompositionLayerQuad" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> An OpenXR composition layer that is rendered as a quad. </brief_description> diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 84d3a5f7fa..165d4d5a67 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -161,6 +161,10 @@ Vector<int> TileMap::_get_tile_map_data_using_compatibility_format(int p_layer) return tile_data; } +void TileMap::_set_layer_tile_data(int p_layer, const PackedInt32Array &p_data) { + _set_tile_map_data_using_compatibility_format(p_layer, format, p_data); +} + void TileMap::_notification(int p_what) { switch (p_what) { case TileMap::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -740,41 +744,23 @@ Rect2 TileMap::_edit_get_rect() const { #endif bool TileMap::_set(const StringName &p_name, const Variant &p_value) { + int index; + const String sname = p_name; + Vector<String> components = String(p_name).split("/", true, 2); - if (p_name == "format") { + if (sname == "format") { if (p_value.get_type() == Variant::INT) { format = (TileMapDataFormat)(p_value.operator int64_t()); // Set format used for loading. return true; } } #ifndef DISABLE_DEPRECATED - else if (p_name == "tile_data") { // Kept for compatibility reasons. - if (p_value.is_array()) { - if (layers.size() == 0) { - TileMapLayer *new_layer = memnew(TileMapLayer); - add_child(new_layer, false, INTERNAL_MODE_FRONT); - new_layer->set_as_tile_map_internal_node(0); - new_layer->set_name("Layer0"); - new_layer->set_tile_set(tile_set); - new_layer->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileMap::_emit_changed)); - layers.push_back(new_layer); - } - _set_tile_map_data_using_compatibility_format(0, format, p_value); - _emit_changed(); - return true; - } - return false; - } else if (p_name == "cell_quadrant_size") { + else if (sname == "cell_quadrant_size") { set_rendering_quadrant_size(p_value); return true; } #endif // DISABLE_DEPRECATED - else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { - int index = components[0].trim_prefix("layer_").to_int(); - if (index < 0) { - return false; - } - + else if (property_helper.is_property_valid(sname, &index)) { if (index >= (int)layers.size()) { while (index >= (int)layers.size()) { TileMapLayer *new_layer = memnew(TileMapLayer); @@ -791,172 +777,38 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { update_configuration_warnings(); } - if (components[1] == "name") { - set_layer_name(index, p_value); - return true; - } else if (components[1] == "enabled") { - set_layer_enabled(index, p_value); - return true; - } else if (components[1] == "modulate") { - set_layer_modulate(index, p_value); - return true; - } else if (components[1] == "y_sort_enabled") { - set_layer_y_sort_enabled(index, p_value); - return true; - } else if (components[1] == "y_sort_origin") { - set_layer_y_sort_origin(index, p_value); - return true; - } else if (components[1] == "z_index") { - set_layer_z_index(index, p_value); - return true; - } else if (components[1] == "navigation_enabled") { - set_layer_navigation_enabled(index, p_value); - return true; - } else if (components[1] == "tile_data") { - _set_tile_map_data_using_compatibility_format(index, format, p_value); - _emit_changed(); + if (property_helper.property_set_value(sname, p_value)) { + if (components[1] == "tile_data") { + _emit_changed(); + } return true; - } else { - return false; } } return false; } bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { + const String sname = p_name; + Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { r_ret = TileMapDataFormat::TILE_MAP_DATA_FORMAT_MAX - 1; // When saving, always save highest format. return true; } #ifndef DISABLE_DEPRECATED - else if (p_name == "cell_quadrant_size") { // Kept for compatibility reasons. + else if (sname == "cell_quadrant_size") { // Kept for compatibility reasons. r_ret = get_rendering_quadrant_size(); return true; } #endif - else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { - int index = components[0].trim_prefix("layer_").to_int(); - if (index < 0 || index >= (int)layers.size()) { - return false; - } - - if (components[1] == "name") { - r_ret = get_layer_name(index); - return true; - } else if (components[1] == "enabled") { - r_ret = is_layer_enabled(index); - return true; - } else if (components[1] == "modulate") { - r_ret = get_layer_modulate(index); - return true; - } else if (components[1] == "y_sort_enabled") { - r_ret = is_layer_y_sort_enabled(index); - return true; - } else if (components[1] == "y_sort_origin") { - r_ret = get_layer_y_sort_origin(index); - return true; - } else if (components[1] == "z_index") { - r_ret = get_layer_z_index(index); - return true; - } else if (components[1] == "navigation_enabled") { - r_ret = is_layer_navigation_enabled(index); - return true; - } else if (components[1] == "tile_data") { - r_ret = _get_tile_map_data_using_compatibility_format(index); - return true; - } else { - return false; - } + else { + return property_helper.property_get_value(sname, r_ret); } - return false; } void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::NIL, "Layers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - -#define MAKE_LAYER_PROPERTY(m_type, m_name, m_hint) \ - { \ - const String property_name = vformat("layer_%d/" m_name, i); \ - p_list->push_back(PropertyInfo(m_type, property_name, PROPERTY_HINT_NONE, m_hint, (get(property_name) == property_get_revert(property_name)) ? PROPERTY_USAGE_EDITOR : PROPERTY_USAGE_DEFAULT)); \ - } - - for (uint32_t i = 0; i < layers.size(); i++) { - MAKE_LAYER_PROPERTY(Variant::STRING, "name", ""); - MAKE_LAYER_PROPERTY(Variant::BOOL, "enabled", ""); - MAKE_LAYER_PROPERTY(Variant::COLOR, "modulate", ""); - MAKE_LAYER_PROPERTY(Variant::BOOL, "y_sort_enabled", ""); - MAKE_LAYER_PROPERTY(Variant::INT, "y_sort_origin", "suffix:px"); - MAKE_LAYER_PROPERTY(Variant::INT, "z_index", ""); - MAKE_LAYER_PROPERTY(Variant::BOOL, "navigation_enabled", ""); - p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("layer_%d/tile_data", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - } - -#undef MAKE_LAYER_PROPERTY -} - -bool TileMap::_property_can_revert(const StringName &p_name) const { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() == 2 && components[0].begins_with("layer_")) { - int index = components[0].trim_prefix("layer_").to_int(); - if (index <= 0 || index >= (int)layers.size()) { - return false; - } - - if (components[1] == "name") { - return layers[index]->get_name() != default_layer->get_name(); - } else if (components[1] == "enabled") { - return layers[index]->is_enabled() != default_layer->is_enabled(); - } else if (components[1] == "modulate") { - return layers[index]->get_modulate() != default_layer->get_modulate(); - } else if (components[1] == "y_sort_enabled") { - return layers[index]->is_y_sort_enabled() != default_layer->is_y_sort_enabled(); - } else if (components[1] == "y_sort_origin") { - return layers[index]->get_y_sort_origin() != default_layer->get_y_sort_origin(); - } else if (components[1] == "z_index") { - return layers[index]->get_z_index() != default_layer->get_z_index(); - } else if (components[1] == "navigation_enabled") { - return layers[index]->is_navigation_enabled() != default_layer->is_navigation_enabled(); - } - } - - return false; -} - -bool TileMap::_property_get_revert(const StringName &p_name, Variant &r_property) const { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() == 2 && components[0].begins_with("layer_")) { - int index = components[0].trim_prefix("layer_").to_int(); - if (index <= 0 || index >= (int)layers.size()) { - return false; - } - - if (components[1] == "name") { - r_property = default_layer->get_name(); - return true; - } else if (components[1] == "enabled") { - r_property = default_layer->is_enabled(); - return true; - } else if (components[1] == "modulate") { - r_property = default_layer->get_modulate(); - return true; - } else if (components[1] == "y_sort_enabled") { - r_property = default_layer->is_y_sort_enabled(); - return true; - } else if (components[1] == "y_sort_origin") { - r_property = default_layer->get_y_sort_origin(); - return true; - } else if (components[1] == "z_index") { - r_property = default_layer->get_z_index(); - return true; - } else if (components[1] == "navigation_enabled") { - r_property = default_layer->is_navigation_enabled(); - return true; - } - } - - return false; + property_helper.get_property_list(p_list, layers.size()); } Vector2 TileMap::map_to_local(const Vector2i &p_pos) const { @@ -1214,11 +1066,26 @@ TileMap::TileMap() { new_layer->set_tile_set(tile_set); new_layer->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileMap::_emit_changed)); layers.push_back(new_layer); - default_layer = memnew(TileMapLayer); -} -TileMap::~TileMap() { - memdelete(default_layer); + if (!base_property_helper.is_initialized()) { + // Initialize static PropertyListHelper if it wasn't yet. This has to be done here, + // because creating TileMapLayer in a static context is not always safe. + TileMapLayer *defaults = memnew(TileMapLayer); + + base_property_helper.set_prefix("layer_"); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "name"), defaults->get_name(), &TileMap::set_layer_name, &TileMap::get_layer_name); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "enabled"), defaults->is_enabled(), &TileMap::set_layer_enabled, &TileMap::is_layer_enabled); + base_property_helper.register_property(PropertyInfo(Variant::COLOR, "modulate"), defaults->get_modulate(), &TileMap::set_layer_modulate, &TileMap::get_layer_modulate); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "y_sort_enabled"), defaults->is_y_sort_enabled(), &TileMap::set_layer_y_sort_enabled, &TileMap::is_layer_y_sort_enabled); + base_property_helper.register_property(PropertyInfo(Variant::INT, "y_sort_origin", PROPERTY_HINT_NONE, "suffix:px"), defaults->get_y_sort_origin(), &TileMap::set_layer_y_sort_origin, &TileMap::get_layer_y_sort_origin); + base_property_helper.register_property(PropertyInfo(Variant::INT, "z_index"), defaults->get_z_index(), &TileMap::set_layer_z_index, &TileMap::get_layer_z_index); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "navigation_enabled"), defaults->is_navigation_enabled(), &TileMap::set_layer_navigation_enabled, &TileMap::is_layer_navigation_enabled); + base_property_helper.register_property(PropertyInfo(Variant::PACKED_INT32_ARRAY, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Vector<int>(), &TileMap::_set_layer_tile_data, &TileMap::_get_tile_map_data_using_compatibility_format); + + memdelete(defaults); + } + + property_helper.setup_for_instance(base_property_helper, this); } #undef TILEMAP_CALL_FOR_LAYER diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 41068ea978..45604bfb8a 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -32,6 +32,7 @@ #define TILE_MAP_H #include "scene/2d/tile_map_layer.h" +#include "scene/property_list_helper.h" #include "scene/resources/2d/tile_set.h" class Control; @@ -73,7 +74,9 @@ private: // Layers. LocalVector<TileMapLayer *> layers; - TileMapLayer *default_layer; // Dummy layer to fetch default values. + + static inline PropertyListHelper base_property_helper; + PropertyListHelper property_helper; // Transforms for collision_animatable. Transform2D last_valid_transform; @@ -86,13 +89,14 @@ private: // Kept for compatibility with TileMap. With TileMapLayers as individual nodes, the format is stored directly in the array. void _set_tile_map_data_using_compatibility_format(int p_layer, TileMapDataFormat p_format, const Vector<int> &p_data); Vector<int> _get_tile_map_data_using_compatibility_format(int p_layer) const; + void _set_layer_tile_data(int p_layer, const PackedInt32Array &p_data); protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - bool _property_can_revert(const StringName &p_name) const; - bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); } void _notification(int p_what); static void _bind_methods(); @@ -235,7 +239,6 @@ public: PackedStringArray get_configuration_warnings() const override; TileMap(); - ~TileMap(); }; VARIANT_ENUM_CAST(TileMap::VisibilityMode); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index e7b6aa1dba..0dc9834539 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -881,9 +881,9 @@ void CPUParticles3D::_particles_process(double p_delta) { case EMISSION_SHAPE_RING: { real_t ring_random_angle = Math::randf() * Math_TAU; real_t ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius; - Vector3 axis = emission_ring_axis.normalized(); + Vector3 axis = emission_ring_axis == Vector3(0.0, 0.0, 0.0) ? Vector3(0.0, 0.0, 1.0) : emission_ring_axis.normalized(); Vector3 ortho_axis; - if (axis == Vector3(1.0, 0.0, 0.0)) { + if (axis.abs() == Vector3(1.0, 0.0, 0.0)) { ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis); } else { ortho_axis = Vector3(1.0, 0.0, 0.0).cross(axis); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 86ff6d15dd..cc923b6676 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -397,7 +397,10 @@ int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const Loc const BSPSimplex &s = p_simplices[p_simplex]; for (int i = 0; i < 4; i++) { const Vector3 v = p_points[s.vertices[i]]; - if (p_plane.has_point(v)) { + // The tolerance used here comes from experiments on scenes up to + // 1000x1000x100 meters. If it's any smaller, some simplices will + // appear to self-intersect due to a lack of precision in Plane. + if (p_plane.has_point(v, 1.0 / (1 << 13))) { // Coplanar. } else if (p_plane.is_point_over(v)) { over++; @@ -419,7 +422,8 @@ int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const Loc //#define DEBUG_BSP int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const LocalVector<Plane> &p_planes, LocalVector<int32_t> &planes_tested, const LocalVector<BSPSimplex> &p_simplices, const LocalVector<int32_t> &p_simplex_indices, LocalVector<BSPNode> &bsp_nodes) { - //if we reach here, it means there is more than one simplex + ERR_FAIL_COND_V(p_simplex_indices.size() < 2, -1); + int32_t node_index = (int32_t)bsp_nodes.size(); bsp_nodes.push_back(BSPNode()); @@ -477,13 +481,14 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc float score = 0; //by default, score is 0 (worst) if (over_count > 0) { - //give score mainly based on ratio (under / over), this means that this plane is splitting simplices a lot, but its balanced - score = float(under_count) / over_count; + // Simplices that are intersected by the plane are moved into both the over + // and under subtrees which makes the entire tree deeper, so the best plane + // will have the least intersections while separating the simplices evenly. + float balance = float(under_count) / over_count; + float separation = float(over_count + under_count) / p_simplex_indices.size(); + score = balance * separation * separation; } - //adjusting priority over least splits, probably not a great idea - //score *= Math::sqrt(float(over_count + under_count) / p_simplex_indices.size()); //also multiply score - if (score > best_plane_score) { best_plane = plane; best_plane_score = score; @@ -491,6 +496,44 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc } } + // We often end up with two (or on rare occasions, three) simplices that are + // either disjoint or share one vertex and don't have a separating plane + // among their faces. The fallback is to loop through new planes created + // with one vertex of the first simplex and two vertices of the second until + // we find a winner. + if (best_plane_score == 0) { + const BSPSimplex &simplex0 = p_simplices[p_simplex_indices[0]]; + const BSPSimplex &simplex1 = p_simplices[p_simplex_indices[1]]; + + for (uint32_t i = 0; i < 4 && !best_plane_score; i++) { + Vector3 v0 = p_points[simplex0.vertices[i]]; + for (uint32_t j = 0; j < 3 && !best_plane_score; j++) { + if (simplex0.vertices[i] == simplex1.vertices[j]) { + break; + } + Vector3 v1 = p_points[simplex1.vertices[j]]; + for (uint32_t k = j + 1; k < 4; k++) { + if (simplex0.vertices[i] == simplex1.vertices[k]) { + break; + } + Vector3 v2 = p_points[simplex1.vertices[k]]; + + Plane plane = Plane(v0, v1, v2); + if (plane == Plane()) { // When v0, v1, and v2 are collinear, they can't form a plane. + continue; + } + int32_t side0 = _bsp_get_simplex_side(p_points, p_simplices, plane, p_simplex_indices[0]); + int32_t side1 = _bsp_get_simplex_side(p_points, p_simplices, plane, p_simplex_indices[1]); + if ((side0 == 1 && side1 == -1) || (side0 == -1 && side1 == 1)) { + best_plane = plane; + best_plane_score = 1.0; + break; + } + } + } + } + } + LocalVector<int32_t> indices_over; LocalVector<int32_t> indices_under; @@ -515,8 +558,6 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc #endif if (best_plane_score < 0.0 || indices_over.size() == p_simplex_indices.size() || indices_under.size() == p_simplex_indices.size()) { - ERR_FAIL_COND_V(p_simplex_indices.size() <= 1, 0); //should not happen, this is a bug - // Failed to separate the tetrahedrons using planes // this means Delaunay broke at some point. // Luckily, because we are using tetrahedrons, we can resort to diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 5c081a0b47..b11a1d9506 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -293,7 +293,7 @@ Vector3 Node3D::get_global_rotation_degrees() const { void Node3D::set_global_rotation(const Vector3 &p_euler_rad) { ERR_THREAD_GUARD; Transform3D transform = get_global_transform(); - transform.basis = Basis::from_euler(p_euler_rad); + transform.basis = Basis::from_euler(p_euler_rad) * Basis::from_scale(transform.basis.get_scale()); set_global_transform(transform); } diff --git a/scene/3d/physics/ray_cast_3d.cpp b/scene/3d/physics/ray_cast_3d.cpp index 0cb722a77e..a9272388c1 100644 --- a/scene/3d/physics/ray_cast_3d.cpp +++ b/scene/3d/physics/ray_cast_3d.cpp @@ -204,8 +204,10 @@ void RayCast3D::_notification(int p_what) { bool prev_collision_state = collided; _update_raycast_state(); - if (prev_collision_state != collided && get_tree()->is_debugging_collisions_hint()) { - _update_debug_shape_material(true); + if (get_tree()->is_debugging_collisions_hint()) { + if (prev_collision_state != collided) { + _update_debug_shape_material(true); + } if (is_inside_tree() && debug_instance.is_valid()) { RenderingServer::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); } diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 9581ae58d8..78a21ba9e1 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -366,6 +366,12 @@ void SkeletonIK3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_min_distance", "get_min_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); + +#ifndef DISABLE_DEPRECATED + ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK3D::_set_interpolation); + ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK3D::_get_interpolation); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001", PROPERTY_USAGE_NONE), "set_interpolation", "get_interpolation"); +#endif } void SkeletonIK3D::_process_modification() { @@ -415,6 +421,16 @@ StringName SkeletonIK3D::get_tip_bone() const { return tip_bone; } +#ifndef DISABLE_DEPRECATED +void SkeletonIK3D::_set_interpolation(real_t p_interpolation) { + set_influence(p_interpolation); +} + +real_t SkeletonIK3D::_get_interpolation() const { + return get_influence(); +} +#endif + void SkeletonIK3D::set_target_transform(const Transform3D &p_target) { target = p_target; reload_goal(); diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index eff018f2cc..5d6020194e 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -134,6 +134,11 @@ class SkeletonIK3D : public SkeletonModifier3D { Variant target_node_override_ref = Variant(); FabrikInverseKinematic::Task *task = nullptr; +#ifndef DISABLE_DEPRECATED + void _set_interpolation(real_t p_interpolation); + real_t _get_interpolation() const; +#endif // DISABLE_DEPRECATED + protected: void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index c13a2e281a..e481b78715 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -202,7 +202,7 @@ void AcceptDialog::register_text_enter(LineEdit *p_line_edit) { } void AcceptDialog::_update_child_rects() { - Size2 dlg_size = get_size(); + Size2 dlg_size = Vector2(get_size()) / get_content_scale_factor(); float h_margins = theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT); float v_margins = theme_cache.panel_style->get_margin(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_BOTTOM); @@ -210,6 +210,15 @@ void AcceptDialog::_update_child_rects() { bg_panel->set_position(Point2()); bg_panel->set_size(dlg_size); + for (int i = 0; i < buttons_hbox->get_child_count(); i++) { + Button *b = Object::cast_to<Button>(buttons_hbox->get_child(i)); + if (!b) { + continue; + } + + b->set_custom_minimum_size(Size2(theme_cache.buttons_min_width, theme_cache.buttons_min_height)); + } + // Place the buttons from the bottom edge to their minimum required size. Size2 buttons_minsize = buttons_hbox->get_combined_minimum_size(); Size2 buttons_size = Size2(dlg_size.x - h_margins, buttons_minsize.y); @@ -389,6 +398,8 @@ void AcceptDialog::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, AcceptDialog, panel_style, "panel"); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_separation); + BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_min_width); + BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_min_height); } bool AcceptDialog::swap_cancel_ok = false; diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index 6f9f450778..12b48c903a 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -57,6 +57,8 @@ class AcceptDialog : public Window { struct ThemeCache { Ref<StyleBox> panel_style; int buttons_separation = 0; + int buttons_min_width = 0; + int buttons_min_height = 0; } theme_cache; void _custom_action(const String &p_action); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 12b2364ddf..c3a586a1ee 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -1255,52 +1255,6 @@ int FileDialog::get_option_count() const { return options.size(); } -bool FileDialog::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0].begins_with("option_") && components[0].trim_prefix("option_").is_valid_int()) { - int item_index = components[0].trim_prefix("option_").to_int(); - String property = components[1]; - if (property == "name") { - set_option_name(item_index, p_value); - return true; - } else if (property == "values") { - set_option_values(item_index, p_value); - return true; - } else if (property == "default") { - set_option_default(item_index, p_value); - return true; - } - } - return false; -} - -bool FileDialog::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0].begins_with("option_") && components[0].trim_prefix("option_").is_valid_int()) { - int item_index = components[0].trim_prefix("option_").to_int(); - String property = components[1]; - if (property == "name") { - r_ret = get_option_name(item_index); - return true; - } else if (property == "values") { - r_ret = get_option_values(item_index); - return true; - } else if (property == "default") { - r_ret = get_option_default(item_index); - return true; - } - } - return false; -} - -void FileDialog::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < options.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("option_%d/name", i))); - p_list->push_back(PropertyInfo(Variant::PACKED_STRING_ARRAY, vformat("option_%d/values", i))); - p_list->push_back(PropertyInfo(Variant::INT, vformat("option_%d/default", i))); - } -} - void FileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_cancel_pressed"), &FileDialog::_cancel_pressed); @@ -1386,6 +1340,13 @@ void FileDialog::_bind_methods() { BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, FileDialog, icon_hover_color, "font_hover_color", "Button"); BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, FileDialog, icon_focus_color, "font_focus_color", "Button"); BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, FileDialog, icon_pressed_color, "font_pressed_color", "Button"); + + Option defaults; + + base_property_helper.set_prefix("option_"); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "name"), defaults.name, &FileDialog::set_option_name, &FileDialog::get_option_name); + base_property_helper.register_property(PropertyInfo(Variant::PACKED_STRING_ARRAY, "values"), defaults.values, &FileDialog::set_option_values, &FileDialog::get_option_values); + base_property_helper.register_property(PropertyInfo(Variant::INT, "default"), defaults.default_idx, &FileDialog::set_option_default, &FileDialog::get_option_default); } void FileDialog::set_show_hidden_files(bool p_show) { @@ -1563,6 +1524,8 @@ FileDialog::FileDialog() { if (register_func) { register_func(this); } + + property_helper.setup_for_instance(base_property_helper, this); } FileDialog::~FileDialog() { diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 7caae7e216..4236f0a56b 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -37,6 +37,7 @@ #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" #include "scene/gui/tree.h" +#include "scene/property_list_helper.h" class GridContainer; @@ -137,6 +138,10 @@ private: Vector<String> values; int default_idx = 0; }; + + static inline PropertyListHelper base_property_helper; + PropertyListHelper property_helper; + Vector<Option> options; Dictionary selected_options; bool options_dirty = false; @@ -187,9 +192,11 @@ private: protected: void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; + bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); } + bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); } + void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list, options.size()); } + bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); } static void _bind_methods(); public: diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 89c627a7a8..e83d9c7c1b 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -153,54 +153,25 @@ void MenuButton::_notification(int p_what) { } bool MenuButton::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0] == "popup") { + const String sname = p_name; + if (property_helper.is_property_valid(sname)) { bool valid; - popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid); + popup->set(sname.trim_prefix("popup/"), p_value, &valid); return valid; } return false; } bool MenuButton::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0] == "popup") { + const String sname = p_name; + if (property_helper.is_property_valid(sname)) { bool valid; - r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid); + r_ret = popup->get(sname.trim_prefix("popup/"), &valid); return valid; } return false; } -void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < popup->get_item_count(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("popup/item_%d/text", i))); - - PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("popup/item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"); - pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As Checkbox,As Radio Button"); - pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/checked", i)); - pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater"); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i)); - pi.usage &= ~(!popup->is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/separator", i)); - pi.usage &= ~(!popup->is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - } -} - void MenuButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup); ClassDB::bind_method(D_METHOD("show_popup"), &MenuButton::show_popup); @@ -215,6 +186,17 @@ void MenuButton::_bind_methods() { ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_"); ADD_SIGNAL(MethodInfo("about_to_popup")); + + PopupMenu::Item defaults(true); + + base_property_helper.set_prefix("popup/item_"); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "text"), defaults.text); + base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon); + base_property_helper.register_property(PropertyInfo(Variant::INT, "checkable", PROPERTY_HINT_ENUM, "No,As Checkbox,As Radio Button"), defaults.checkable_type); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "checked"), defaults.checked); + base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator); } void MenuButton::set_disable_shortcuts(bool p_disabled) { @@ -235,6 +217,8 @@ MenuButton::MenuButton(const String &p_text) : add_child(popup, false, INTERNAL_MODE_FRONT); popup->connect("about_to_popup", callable_mp(this, &MenuButton::_popup_visibility_changed).bind(true)); popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed).bind(false)); + + property_helper.setup_for_instance(base_property_helper, this); } MenuButton::~MenuButton() { diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index eea6b8e877..2bd577ddd0 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -33,6 +33,7 @@ #include "scene/gui/button.h" #include "scene/gui/popup_menu.h" +#include "scene/property_list_helper.h" class MenuButton : public Button { GDCLASS(MenuButton, Button); @@ -42,13 +43,18 @@ class MenuButton : public Button { bool disable_shortcuts = false; PopupMenu *popup = nullptr; + static inline PropertyListHelper base_property_helper; + PropertyListHelper property_helper; + void _popup_visibility_changed(bool p_visible); protected: void _notification(int p_what); bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; + void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list, popup->get_item_count()); } + bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); } static void _bind_methods(); virtual void shortcut_input(const Ref<InputEvent> &p_event) override; diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 0e10652f07..509c6aca99 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -154,23 +154,20 @@ void OptionButton::_notification(int p_what) { } bool OptionButton::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0] == "popup") { - const String &property = components[2]; - if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") { - return false; - } + int index; + const String sname = p_name; + if (property_helper.is_property_valid(sname, &index)) { bool valid; - popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid); + popup->set(sname.trim_prefix("popup/"), p_value, &valid); - int idx = components[1].get_slice("_", 1).to_int(); - if (idx == current) { + if (index == current) { // Force refreshing currently displayed item. current = NONE_SELECTED; - _select(idx, false); + _select(index, false); } + const String property = sname.get_slice("/", 2); if (property == "text" || property == "icon") { _queue_update_size_cache(); } @@ -180,42 +177,6 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) { return false; } -bool OptionButton::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0] == "popup") { - const String &property = components[2]; - if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") { - return false; - } - - bool valid; - r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid); - return valid; - } - return false; -} - -void OptionButton::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < popup->get_item_count(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("popup/item_%d/text", i))); - - PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("popup/item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"); - pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater"); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i)); - pi.usage &= ~(!popup->is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/separator", i)); - pi.usage &= ~(!popup->is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - } -} - void OptionButton::_focused(int p_which) { emit_signal(SNAME("item_focused"), p_which); } @@ -606,6 +567,15 @@ void OptionButton::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, OptionButton, arrow_icon, "arrow"); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, OptionButton, arrow_margin); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, OptionButton, modulate_arrow); + + PopupMenu::Item defaults(true); + + base_property_helper.set_prefix("popup/item_"); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "text"), defaults.text, &OptionButton::_dummy_setter, &OptionButton::get_item_text); + base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &OptionButton::_dummy_setter, &OptionButton::get_item_icon); + base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id, &OptionButton::_dummy_setter, &OptionButton::get_item_id); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &OptionButton::_dummy_setter, &OptionButton::is_item_disabled); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator, &OptionButton::_dummy_setter, &OptionButton::is_item_separator); } void OptionButton::set_disable_shortcuts(bool p_disabled) { @@ -625,6 +595,8 @@ OptionButton::OptionButton(const String &p_text) : popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected)); popup->connect("id_focused", callable_mp(this, &OptionButton::_focused)); popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed).bind(false)); + + property_helper.setup_for_instance(base_property_helper, this); } OptionButton::~OptionButton() { diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index 9c15b295a9..4b5164161a 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -33,6 +33,7 @@ #include "scene/gui/button.h" #include "scene/gui/popup_menu.h" +#include "scene/property_list_helper.h" class OptionButton : public Button { GDCLASS(OptionButton, Button); @@ -64,11 +65,15 @@ class OptionButton : public Button { int modulate_arrow = 0; } theme_cache; + static inline PropertyListHelper base_property_helper; + PropertyListHelper property_helper; + void _focused(int p_which); void _selected(int p_which); void _select(int p_which, bool p_emit = false); void _select_int(int p_which); void _refresh_size_cache(); + void _dummy_setter() {} // Stub for PropertyListHelper (_set() doesn't use it). virtual void pressed() override; @@ -78,8 +83,10 @@ protected: void _notification(int p_what); bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; + bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); } + void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list, popup->get_item_count()); } + bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); } void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index d90d5da2e9..87383283fd 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -230,7 +230,8 @@ Size2 PopupPanel::_get_contents_minimum_size() const { void PopupPanel::_update_child_rects() { Vector2 cpos(theme_cache.panel_style->get_offset()); - Vector2 csize(get_size() - theme_cache.panel_style->get_minimum_size()); + Vector2 panel_size = Vector2(get_size()) / get_content_scale_factor(); + Vector2 csize = panel_size - theme_cache.panel_style->get_minimum_size(); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -244,7 +245,7 @@ void PopupPanel::_update_child_rects() { if (c == panel) { c->set_position(Vector2()); - c->set_size(get_size()); + c->set_size(panel_size); } else { c->set_position(cpos); c->set_size(csize); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index b6dd3ac0b4..260956a775 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -312,18 +312,17 @@ int PopupMenu::_get_items_total_height() const { } int PopupMenu::_get_mouse_over(const Point2 &p_over) const { - if (p_over.x < 0 || p_over.x >= get_size().width || p_over.y < theme_cache.panel_style->get_margin(Side::SIDE_TOP)) { + float win_scale = get_content_scale_factor(); + if (p_over.x < 0 || p_over.x >= get_size().width * win_scale || p_over.y < theme_cache.panel_style->get_margin(Side::SIDE_TOP) * win_scale) { return -1; } - Point2 ofs; + Point2 ofs = Point2(0, theme_cache.v_separation * 0.5) * win_scale; for (int i = 0; i < items.size(); i++) { - ofs.y += theme_cache.v_separation; - - ofs.y += _get_item_height(i); - - if (p_over.y - control->get_position().y < ofs.y) { + ofs.y += i > 0 ? (float)theme_cache.v_separation * win_scale : (float)theme_cache.v_separation * win_scale * 0.5; + ofs.y += _get_item_height(i) * win_scale; + if (p_over.y - control->get_position().y * win_scale < ofs.y) { return i; } } @@ -341,15 +340,17 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { Rect2 this_rect(this_pos, get_size()); float scroll_offset = control->get_position().y; + float scaled_ofs_cache = items[p_over]._ofs_cache * get_content_scale_factor(); + float scaled_height_cache = items[p_over]._height_cache * get_content_scale_factor(); submenu_popup->reset_size(); // Shrink the popup size to its contents. Size2 submenu_size = submenu_popup->get_size(); Point2 submenu_pos; if (control->is_layout_rtl()) { - submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset - theme_cache.v_separation / 2); + submenu_pos = this_pos + Point2(-submenu_size.width, scaled_ofs_cache + scroll_offset - theme_cache.v_separation / 2); } else { - submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset - theme_cache.v_separation / 2); + submenu_pos = this_pos + Point2(this_rect.size.width, scaled_ofs_cache + scroll_offset - theme_cache.v_separation / 2); } // Fix pos if going outside parent rect. @@ -386,8 +387,8 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { // Set autohide areas. Rect2 safe_area = this_rect; - safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2; - safe_area.size.y = items[p_over]._height_cache + theme_cache.v_separation; + safe_area.position.y += scaled_ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2; + safe_area.size.y = scaled_height_cache + theme_cache.v_separation; Viewport *vp = submenu_popup->get_embedder(); if (vp) { vp->subwindow_set_popup_safe_rect(submenu_popup, safe_area); @@ -400,11 +401,11 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { // Autohide area above the submenu item. submenu_pum->clear_autohide_areas(); - submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2)); + submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, scaled_ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2)); // If there is an area below the submenu item, add an autohide area there. - if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) { - int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + theme_cache.v_separation / 2 + theme_cache.panel_style->get_offset().height; + if (scaled_ofs_cache + scaled_height_cache + scroll_offset <= control->get_size().height) { + int from = scaled_ofs_cache + scaled_height_cache + scroll_offset + theme_cache.v_separation / 2 + theme_cache.panel_style->get_offset().height; submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from)); } } @@ -576,6 +577,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) { } item_clickable_area.size.width -= scroll_container->get_v_scroll_bar()->get_size().width; } + item_clickable_area.size = item_clickable_area.size * get_content_scale_factor(); Ref<InputEventMouseButton> b = p_event; @@ -2827,6 +2829,14 @@ void PopupMenu::popup(const Rect2i &p_bounds) { } else { moved = Vector2(); popup_time_msec = OS::get_singleton()->get_ticks_msec(); + if (!is_embedded()) { + float win_scale = get_parent_visible_window()->get_content_scale_factor(); + set_content_scale_factor(win_scale); + Size2 minsize = get_contents_minimum_size(); + minsize.height += 0.5 * win_scale; // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing. + set_min_size(minsize * win_scale); + set_size(Vector2(0, 0)); // Shrinkwraps to min size. + } Popup::popup(p_bounds); } } diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index f7097fffaf..832c1bcc8b 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -97,6 +97,10 @@ class PopupMenu : public Popup { static inline PropertyListHelper base_property_helper; PropertyListHelper property_helper; + // To make Item available. + friend class OptionButton; + friend class MenuButton; + RID global_menu; RID system_menu; NativeMenu::SystemMenus system_menu_id = NativeMenu::INVALID_MENU_ID; diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index d20fef8164..2d687eb201 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -1720,58 +1720,6 @@ bool TabBar::get_deselect_enabled() const { return deselect_enabled; } -bool TabBar::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0].begins_with("tab_") && components[0].trim_prefix("tab_").is_valid_int()) { - int tab_index = components[0].trim_prefix("tab_").to_int(); - const String &property = components[1]; - if (property == "title") { - set_tab_title(tab_index, p_value); - return true; - } else if (property == "icon") { - set_tab_icon(tab_index, p_value); - return true; - } else if (property == "disabled") { - set_tab_disabled(tab_index, p_value); - return true; - } - } - return false; -} - -bool TabBar::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0].begins_with("tab_") && components[0].trim_prefix("tab_").is_valid_int()) { - int tab_index = components[0].trim_prefix("tab_").to_int(); - const String &property = components[1]; - if (property == "title") { - r_ret = get_tab_title(tab_index); - return true; - } else if (property == "icon") { - r_ret = get_tab_icon(tab_index); - return true; - } else if (property == "disabled") { - r_ret = is_tab_disabled(tab_index); - return true; - } - } - return false; -} - -void TabBar::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < tabs.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("tab_%d/title", i))); - - PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("tab_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"); - pi.usage &= ~(get_tab_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::BOOL, vformat("tab_%d/disabled", i)); - pi.usage &= ~(!is_tab_disabled(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - } -} - void TabBar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tab_count", "count"), &TabBar::set_tab_count); ClassDB::bind_method(D_METHOD("get_tab_count"), &TabBar::get_tab_count); @@ -1890,10 +1838,19 @@ void TabBar::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabBar, close_icon, "close"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, button_pressed_style, "button_pressed"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, button_hl_style, "button_highlight"); + + Tab defaults(true); + + base_property_helper.set_prefix("tab_"); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "title"), defaults.text, &TabBar::set_tab_title, &TabBar::get_tab_title); + base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &TabBar::set_tab_icon, &TabBar::get_tab_icon); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &TabBar::set_tab_disabled, &TabBar::is_tab_disabled); } TabBar::TabBar() { set_size(Size2(get_size().width, get_minimum_size().height)); set_focus_mode(FOCUS_ALL); connect("mouse_exited", callable_mp(this, &TabBar::_on_mouse_exited)); + + property_helper.setup_for_instance(base_property_helper, this); } diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index 65a1d5bd4f..6c09e960f1 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -32,6 +32,7 @@ #define TAB_BAR_H #include "scene/gui/control.h" +#include "scene/property_list_helper.h" #include "scene/resources/text_line.h" class TabBar : public Control { @@ -77,8 +78,13 @@ private: Tab() { text_buf.instantiate(); } + + Tab(bool p_dummy) {} }; + static inline PropertyListHelper base_property_helper; + PropertyListHelper property_helper; + int offset = 0; int max_drawn_tab = 0; int highlight_arrow = -1; @@ -163,9 +169,11 @@ private: protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; + bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); } + bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); } + void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list, tabs.size()); } + bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); } void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 1daf86fe0f..1242d3bd16 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3995,12 +3995,14 @@ bool Tree::edit_selected(bool p_force_edit) { return false; } + float popup_scale = popup_editor->is_embedded() ? 1.0 : popup_editor->get_parent_visible_window()->get_content_scale_factor(); Rect2 rect; if (select_mode == SELECT_ROW) { rect = s->get_meta("__focus_col_" + itos(selected_col)); } else { rect = s->get_meta("__focus_rect"); } + rect.position *= popup_scale; popup_edited_item = s; popup_edited_item_col = col; @@ -4043,7 +4045,7 @@ bool Tree::edit_selected(bool p_force_edit) { popup_rect.size = rect.size; // Account for icon. - Size2 icon_size = _get_cell_icon_size(c); + Size2 icon_size = _get_cell_icon_size(c) * popup_scale; popup_rect.position.x += icon_size.x; popup_rect.size.x -= icon_size.x; @@ -4070,7 +4072,10 @@ bool Tree::edit_selected(bool p_force_edit) { } popup_editor->set_position(popup_rect.position); - popup_editor->set_size(popup_rect.size); + popup_editor->set_size(popup_rect.size * popup_scale); + if (!popup_editor->is_embedded()) { + popup_editor->set_content_scale_factor(popup_scale); + } popup_editor->popup(); popup_editor->child_controls_changed(); @@ -4086,7 +4091,10 @@ bool Tree::edit_selected(bool p_force_edit) { text_editor->show(); popup_editor->set_position(get_screen_position() + rect.position); - popup_editor->set_size(rect.size); + popup_editor->set_size(rect.size * popup_scale); + if (!popup_editor->is_embedded()) { + popup_editor->set_content_scale_factor(popup_scale); + } popup_editor->popup(); popup_editor->child_controls_changed(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 17d7fec230..2d30ea345d 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1461,6 +1461,8 @@ void Viewport::_gui_show_tooltip() { panel->set_flag(Window::FLAG_NO_FOCUS, true); panel->set_flag(Window::FLAG_POPUP, false); panel->set_flag(Window::FLAG_MOUSE_PASSTHROUGH, true); + // A non-embedded tooltip window will only be transparent if per_pixel_transparency is allowed in the main Viewport. + panel->set_flag(Window::FLAG_TRANSPARENT, true); panel->set_wrap_controls(true); panel->add_child(base_tooltip); panel->gui_parent = this; @@ -1469,17 +1471,25 @@ void Viewport::_gui_show_tooltip() { tooltip_owner->add_child(gui.tooltip_popup); + Window *window = Object::cast_to<Window>(gui.tooltip_popup->get_embedder()); + if (!window) { // Not embedded. + window = gui.tooltip_popup->get_parent_visible_window(); + } + float win_scale = window->content_scale_factor; Point2 tooltip_offset = GLOBAL_GET("display/mouse_cursor/tooltip_position_offset"); + if (!gui.tooltip_popup->is_embedded()) { + tooltip_offset *= win_scale; + } Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size()); - r.size = r.size.min(panel->get_max_size()); - - Window *window = gui.tooltip_popup->get_parent_visible_window(); Rect2i vr; if (gui.tooltip_popup->is_embedded()) { vr = gui.tooltip_popup->get_embedder()->get_visible_rect(); } else { + panel->content_scale_factor = win_scale; + r.size *= win_scale; vr = window->get_usable_parent_rect(); } + r.size = r.size.min(panel->get_max_size()); if (r.size.x + r.position.x > vr.size.x + vr.position.x) { // Place it in the opposite direction. If it fails, just hug the border. diff --git a/scene/property_list_helper.cpp b/scene/property_list_helper.cpp index d9a80011b0..b666e4c52d 100644 --- a/scene/property_list_helper.cpp +++ b/scene/property_list_helper.cpp @@ -31,7 +31,7 @@ #include "property_list_helper.h" const PropertyListHelper::Property *PropertyListHelper::_get_property(const String &p_property, int *r_index) const { - const Vector<String> components = p_property.split("/", true, 2); + const Vector<String> components = p_property.rsplit("/", true, 1); if (components.size() < 2 || !components[0].begins_with(prefix)) { return nullptr; } @@ -48,36 +48,73 @@ const PropertyListHelper::Property *PropertyListHelper::_get_property(const Stri } void PropertyListHelper::_call_setter(const MethodBind *p_setter, int p_index, const Variant &p_value) const { + DEV_ASSERT(p_setter); Variant args[] = { p_index, p_value }; const Variant *argptrs[] = { &args[0], &args[1] }; Callable::CallError ce; p_setter->call(object, argptrs, 2, ce); } -Variant PropertyListHelper::_call_getter(const MethodBind *p_getter, int p_index) const { +Variant PropertyListHelper::_call_getter(const Property *p_property, int p_index) const { + if (!p_property->getter) { + return object->get(prefix + itos(p_index) + "/" + p_property->info.name); + } + Callable::CallError ce; Variant args[] = { p_index }; const Variant *argptrs[] = { &args[0] }; - return p_getter->call(object, argptrs, 1, ce); + return p_property->getter->call(object, argptrs, 1, ce); } void PropertyListHelper::set_prefix(const String &p_prefix) { prefix = p_prefix; } +void PropertyListHelper::register_property(const PropertyInfo &p_info, const Variant &p_default) { + Property property; + property.info = p_info; + property.default_value = p_default; + + property_list[p_info.name] = property; +} + +bool PropertyListHelper::is_initialized() const { + return !property_list.is_empty(); +} + void PropertyListHelper::setup_for_instance(const PropertyListHelper &p_base, Object *p_object) { prefix = p_base.prefix; property_list = p_base.property_list; object = p_object; } +bool PropertyListHelper::is_property_valid(const String &p_property, int *r_index) const { + const Vector<String> components = p_property.rsplit("/", true, 1); + if (components.size() < 2 || !components[0].begins_with(prefix)) { + return false; + } + + { + const String index_string = components[0].trim_prefix(prefix); + if (!index_string.is_valid_int()) { + return false; + } + + if (r_index) { + *r_index = index_string.to_int(); + } + } + + return property_list.has(components[1]); +} + void PropertyListHelper::get_property_list(List<PropertyInfo> *p_list, int p_count) const { for (int i = 0; i < p_count; i++) { for (const KeyValue<String, Property> &E : property_list) { const Property &property = E.value; PropertyInfo info = property.info; - if (_call_getter(property.getter, i) == property.default_value) { + if (_call_getter(&property, i) == property.default_value) { info.usage &= (~PROPERTY_USAGE_STORAGE); } @@ -92,7 +129,7 @@ bool PropertyListHelper::property_get_value(const String &p_property, Variant &r const Property *property = _get_property(p_property, &index); if (property) { - r_ret = _call_getter(property->getter, index); + r_ret = _call_getter(property, index); return true; } return false; @@ -110,8 +147,7 @@ bool PropertyListHelper::property_set_value(const String &p_property, const Vari } bool PropertyListHelper::property_can_revert(const String &p_property) const { - int index; - return _get_property(p_property, &index) != nullptr; + return is_property_valid(p_property); } bool PropertyListHelper::property_get_revert(const String &p_property, Variant &r_value) const { @@ -129,8 +165,10 @@ PropertyListHelper::~PropertyListHelper() { // No object = it's the main helper. Do a cleanup. if (!object) { for (const KeyValue<String, Property> &E : property_list) { - memdelete(E.value.setter); - memdelete(E.value.getter); + if (E.value.setter) { + memdelete(E.value.setter); + memdelete(E.value.getter); + } } } } diff --git a/scene/property_list_helper.h b/scene/property_list_helper.h index 6c1ad21a05..eac6b03d47 100644 --- a/scene/property_list_helper.h +++ b/scene/property_list_helper.h @@ -48,10 +48,12 @@ class PropertyListHelper { const Property *_get_property(const String &p_property, int *r_index) const; void _call_setter(const MethodBind *p_setter, int p_index, const Variant &p_value) const; - Variant _call_getter(const MethodBind *p_getter, int p_index) const; + Variant _call_getter(const Property *p_property, int p_index) const; public: void set_prefix(const String &p_prefix); + // Register property without setter/getter. Only use when you don't need PropertyListHelper for _set/_get logic. + void register_property(const PropertyInfo &p_info, const Variant &p_default); template <typename S, typename G> void register_property(const PropertyInfo &p_info, const Variant &p_default, S p_setter, G p_getter) { @@ -64,7 +66,9 @@ public: property_list[p_info.name] = property; } + bool is_initialized() const; void setup_for_instance(const PropertyListHelper &p_base, Object *p_object); + bool is_property_valid(const String &p_property, int *r_index = nullptr) const; void get_property_list(List<PropertyInfo> *p_list, int p_count) const; bool property_get_value(const String &p_property, Variant &r_ret) const; diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 5acb08de14..30b90841e3 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -635,9 +635,9 @@ void ParticleProcessMaterial::_update_shader() { code += " \n"; code += " float ring_spawn_angle = rand_from_seed(alt_seed) * 2.0 * pi;\n"; code += " float ring_random_radius = rand_from_seed(alt_seed) * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;\n"; - code += " vec3 axis = normalize(emission_ring_axis);\n"; + code += " vec3 axis = emission_ring_axis == vec3(0.0) ? vec3(0.0, 0.0, 1.0) : normalize(emission_ring_axis);\n"; code += " vec3 ortho_axis = vec3(0.0);\n"; - code += " if (axis == vec3(1.0, 0.0, 0.0)) {\n"; + code += " if (abs(axis) == vec3(1.0, 0.0, 0.0)) {\n"; code += " ortho_axis = cross(axis, vec3(0.0, 1.0, 0.0));\n"; code += " } else {\n"; code += " ortho_axis = cross(axis, vec3(1.0, 0.0, 0.0));\n"; diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 3e3a7d2381..b5333d91c6 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -676,63 +676,6 @@ bool AudioStreamRandomizer::is_monophonic() const { return false; } -bool AudioStreamRandomizer::_get(const StringName &p_name, Variant &r_ret) const { - if (AudioStream::_get(p_name, r_ret)) { - return true; - } - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() == 2 && components[0].begins_with("stream_") && components[0].trim_prefix("stream_").is_valid_int()) { - int index = components[0].trim_prefix("stream_").to_int(); - if (index < 0 || index >= (int)audio_stream_pool.size()) { - return false; - } - - if (components[1] == "stream") { - r_ret = get_stream(index); - return true; - } else if (components[1] == "weight") { - r_ret = get_stream_probability_weight(index); - return true; - } else { - return false; - } - } - return false; -} - -bool AudioStreamRandomizer::_set(const StringName &p_name, const Variant &p_value) { - if (AudioStream::_set(p_name, p_value)) { - return true; - } - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() == 2 && components[0].begins_with("stream_") && components[0].trim_prefix("stream_").is_valid_int()) { - int index = components[0].trim_prefix("stream_").to_int(); - if (index < 0 || index >= (int)audio_stream_pool.size()) { - return false; - } - - if (components[1] == "stream") { - set_stream(index, p_value); - return true; - } else if (components[1] == "weight") { - set_stream_probability_weight(index, p_value); - return true; - } else { - return false; - } - } - return false; -} - -void AudioStreamRandomizer::_get_property_list(List<PropertyInfo> *p_list) const { - AudioStream::_get_property_list(p_list); // Define the trivial scalar properties. - p_list->push_back(PropertyInfo(Variant::NIL, "Streams", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < audio_stream_pool.size(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("stream_%d/stream", i), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); - p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("stream_%d/weight", i), PROPERTY_HINT_RANGE, "0,100,0.001,or_greater")); - } -} - void AudioStreamRandomizer::_bind_methods() { ClassDB::bind_method(D_METHOD("add_stream", "index", "stream", "weight"), &AudioStreamRandomizer::add_stream, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("move_stream", "index_from", "index_to"), &AudioStreamRandomizer::move_stream); @@ -764,9 +707,17 @@ void AudioStreamRandomizer::_bind_methods() { BIND_ENUM_CONSTANT(PLAYBACK_RANDOM_NO_REPEATS); BIND_ENUM_CONSTANT(PLAYBACK_RANDOM); BIND_ENUM_CONSTANT(PLAYBACK_SEQUENTIAL); + + PoolEntry defaults; + + base_property_helper.set_prefix("stream_"); + base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), defaults.stream, &AudioStreamRandomizer::set_stream, &AudioStreamRandomizer::get_stream); + base_property_helper.register_property(PropertyInfo(Variant::FLOAT, "weight", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), defaults.weight, &AudioStreamRandomizer::set_stream_probability_weight, &AudioStreamRandomizer::get_stream_probability_weight); } -AudioStreamRandomizer::AudioStreamRandomizer() {} +AudioStreamRandomizer::AudioStreamRandomizer() { + property_helper.setup_for_instance(base_property_helper, this); +} void AudioStreamPlaybackRandomizer::start(double p_from_pos) { playing = playback; diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index f8123fbe15..01a4a09942 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -33,6 +33,7 @@ #include "core/io/image.h" #include "core/io/resource.h" +#include "scene/property_list_helper.h" #include "servers/audio/audio_filter_sw.h" #include "servers/audio_server.h" @@ -236,9 +237,12 @@ private: struct PoolEntry { Ref<AudioStream> stream; - float weight; + float weight = 1.0; }; + static inline PropertyListHelper base_property_helper; + PropertyListHelper property_helper; + HashSet<AudioStreamPlaybackRandomizer *> playbacks; Vector<PoolEntry> audio_stream_pool; float random_pitch_scale = 1.0f; @@ -254,9 +258,11 @@ private: protected: static void _bind_methods(); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; + bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); } + bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); } + void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list, audio_stream_pool.size()); } + bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); } public: void add_stream(int p_index, Ref<AudioStream> p_stream, float p_weight = 1.0); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 351c03c158..9600caa214 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -212,7 +212,7 @@ String DisplayServer::global_menu_get_item_submenu(const String &p_menu_root, in ERR_FAIL_NULL_V(nmenu, String()); RID rid = nmenu->get_item_submenu(_get_rid_from_name(nmenu, p_menu_root), p_idx); if (!nmenu->is_system_menu(rid)) { - for (HashMap<String, RID>::Iterator E = menu_names.begin(); E;) { + for (HashMap<String, RID>::Iterator E = menu_names.begin(); E; ++E) { if (E->value == rid) { return E->key; } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index e61bb9eae8..6cb03871c9 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -1157,6 +1157,7 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render render_data.lights = ∅ render_data.reflection_probes = ∅ render_data.voxel_gi_instances = ∅ + render_data.lightmaps = ∅ } if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED || diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h index 827a1bed17..8778ea86a6 100644 --- a/tests/servers/test_navigation_server_3d.h +++ b/tests/servers/test_navigation_server_3d.h @@ -764,6 +764,8 @@ TEST_SUITE("[Navigation]") { navigation_server->process(0.0); // Give server some cycles to commit. } + // FIXME: The race condition mentioned below is actually a problem and fails on CI (GH-90613). + /* TEST_CASE("[NavigationServer3D] Server should be able to bake asynchronously") { NavigationServer3D *navigation_server = NavigationServer3D::get_singleton(); Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh); @@ -781,6 +783,7 @@ TEST_SUITE("[Navigation]") { CHECK_EQ(navigation_mesh->get_polygon_count(), 0); CHECK_EQ(navigation_mesh->get_vertices().size(), 0); } + */ } } //namespace TestNavigationServer3D |