diff options
82 files changed, 2010 insertions, 570 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 00e74a7cce..ba9c6e4c60 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1137,8 +1137,8 @@ Array ProjectSettings::get_global_class_list() { Ref<ConfigFile> cf; cf.instantiate(); - if (cf->load(get_project_data_path().path_join("global_script_class_cache.cfg")) == OK) { - script_classes = cf->get_value("", "list"); + if (cf->load(get_global_class_list_path()) == OK) { + script_classes = cf->get_value("", "list", Array()); } else { #ifndef TOOLS_ENABLED // Script classes can't be recreated in exported project, so print an error. @@ -1148,11 +1148,15 @@ Array ProjectSettings::get_global_class_list() { return script_classes; } +String ProjectSettings::get_global_class_list_path() const { + return get_project_data_path().path_join("global_script_class_cache.cfg"); +} + void ProjectSettings::store_global_class_list(const Array &p_classes) { Ref<ConfigFile> cf; cf.instantiate(); cf->set_value("", "list", p_classes); - cf->save(get_project_data_path().path_join("global_script_class_cache.cfg")); + cf->save(get_global_class_list_path()); } bool ProjectSettings::has_custom_feature(const String &p_feature) const { diff --git a/core/config/project_settings.h b/core/config/project_settings.h index d1704a7c31..70f697741f 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -143,6 +143,7 @@ public: Variant get_setting(const String &p_setting, const Variant &p_default_value = Variant()) const; Array get_global_class_list(); void store_global_class_list(const Array &p_classes); + String get_global_class_list_path() const; bool has_setting(String p_var) const; String localize_path(const String &p_path) const; diff --git a/core/input/input.cpp b/core/input/input.cpp index 3cf83fd64b..aa89facdd7 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -891,6 +891,31 @@ void Input::parse_input_event(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); +#ifdef DEBUG_ENABLED + uint64_t curr_frame = Engine::get_singleton()->get_process_frames(); + if (curr_frame != last_parsed_frame) { + frame_parsed_events.clear(); + last_parsed_frame = curr_frame; + frame_parsed_events.insert(p_event); + } else if (frame_parsed_events.has(p_event)) { + // It would be technically safe to send the same event in cases such as: + // - After an explicit flush. + // - In platforms using buffering when agile flushing is enabled, after one of the mid-frame flushes. + // - If platform doesn't use buffering and event accumulation is disabled. + // - If platform doesn't use buffering and the event type is not accumulable. + // However, it wouldn't be reasonable to ask users to remember the full ruleset and be aware at all times + // of the possibilites of the target platform, project settings and engine internals, which may change + // without prior notice. + // Therefore, the guideline is, "don't send the same event object more than once per frame". + WARN_PRINT_ONCE( + "An input event object is being parsed more than once in the same frame, which is unsafe.\n" + "If you are generating events in a script, you have to instantiate a new event instead of sending the same one more than once, unless the original one was sent on an earlier frame.\n" + "You can call duplicate() on the event to get a new instance with identical values."); + } else { + frame_parsed_events.insert(p_event); + } +#endif + if (use_accumulated_input) { if (buffered_events.is_empty() || !buffered_events.back()->get()->accumulate(p_event)) { buffered_events.push_back(p_event); diff --git a/core/input/input.h b/core/input/input.h index f2de56b6b9..c254650ef8 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -223,6 +223,10 @@ private: void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated); List<Ref<InputEvent>> buffered_events; +#ifdef DEBUG_ENABLED + HashSet<Ref<InputEvent>> frame_parsed_events; + uint64_t last_parsed_frame = UINT64_MAX; +#endif friend class DisplayServer; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index df5486512d..61f5cfc22a 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -369,6 +369,14 @@ void ScriptServer::save_global_classes() { ProjectSettings::get_singleton()->store_global_class_list(gcarr); } +bool ScriptServer::has_global_classes() { + return !global_classes.is_empty(); +} + +String ScriptServer::get_global_class_cache_file_path() { + return ProjectSettings::get_singleton()->get_global_class_list_path(); +} + //////////////////// Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { diff --git a/core/object/script_language.h b/core/object/script_language.h index f82b58439f..b1393c7196 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -91,6 +91,8 @@ public: static void get_global_class_list(List<StringName> *r_global_classes); static void get_inheriters_list(const StringName &p_base_type, List<StringName> *r_classes); static void save_global_classes(); + static bool has_global_classes(); + static String get_global_class_cache_file_path(); static void init_languages(); static void finish_languages(); diff --git a/doc/classes/AnimationNodeBlendSpace1D.xml b/doc/classes/AnimationNodeBlendSpace1D.xml index 0f1ce127cd..9a7872f50e 100644 --- a/doc/classes/AnimationNodeBlendSpace1D.xml +++ b/doc/classes/AnimationNodeBlendSpace1D.xml @@ -67,6 +67,9 @@ </method> </methods> <members> + <member name="blend_mode" type="int" setter="set_blend_mode" getter="get_blend_mode" enum="AnimationNodeBlendSpace1D.BlendMode" default="0"> + Controls the interpolation between animations. See [enum BlendMode] constants. + </member> <member name="max_space" type="float" setter="set_max_space" getter="get_max_space" default="1.0"> The blend space's axis's upper limit for the points' position. See [method add_blend_point]. </member> @@ -84,4 +87,15 @@ Label of the virtual axis of the blend space. </member> </members> + <constants> + <constant name="BLEND_MODE_INTERPOLATED" value="0" enum="BlendMode"> + The interpolation between animations is linear. + </constant> + <constant name="BLEND_MODE_DISCRETE" value="1" enum="BlendMode"> + The blend space plays the animation of the node the blending position is closest to. Useful for frame-by-frame 2D animations. + </constant> + <constant name="BLEND_MODE_DISCRETE_CARRY" value="2" enum="BlendMode"> + Similar to [constant BLEND_MODE_DISCRETE], but starts the new animation at the last animation's playback position. + </constant> + </constants> </class> diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 36f12dd5c4..ae331f8491 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -29,13 +29,6 @@ Adds an [AudioEffect] effect to the bus [param bus_idx] at [param at_position]. </description> </method> - <method name="capture_get_device_list"> - <return type="PackedStringArray" /> - <description> - Returns the names of all audio input devices detected on the system. - [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. - </description> - </method> <method name="generate_bus_layout" qualifiers="const"> <return type="AudioBusLayout" /> <description> @@ -117,10 +110,11 @@ Returns the volume of the bus at index [param bus_idx] in dB. </description> </method> - <method name="get_device_list"> + <method name="get_input_device_list"> <return type="PackedStringArray" /> <description> - Returns the names of all audio devices detected on the system. + Returns the names of all audio input devices detected on the system. + [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. </description> </method> <method name="get_mix_rate" qualifiers="const"> @@ -129,6 +123,12 @@ Returns the sample rate at the output of the [AudioServer]. </description> </method> + <method name="get_output_device_list"> + <return type="PackedStringArray" /> + <description> + Returns the names of all audio output devices detected on the system. + </description> + </method> <method name="get_output_latency" qualifiers="const"> <return type="float" /> <description> @@ -302,12 +302,12 @@ <member name="bus_count" type="int" setter="set_bus_count" getter="get_bus_count" default="1"> Number of available audio buses. </member> - <member name="capture_device" type="String" setter="capture_set_device" getter="capture_get_device" default=""Default""> - Name of the current device for audio input (see [method capture_get_device_list]). On systems with multiple audio inputs (such as analog, USB and HDMI audio), this can be used to select the audio input device. The value [code]"Default"[/code] will record audio on the system-wide default audio input. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code]. + <member name="input_device" type="String" setter="set_input_device" getter="get_input_device" default=""Default""> + Name of the current device for audio input (see [method get_input_device_list]). On systems with multiple audio inputs (such as analog, USB and HDMI audio), this can be used to select the audio input device. The value [code]"Default"[/code] will record audio on the system-wide default audio input. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code]. [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. </member> - <member name="device" type="String" setter="set_device" getter="get_device" default=""Default""> - Name of the current device for audio output (see [method get_device_list]). On systems with multiple audio outputs (such as analog, USB and HDMI audio), this can be used to select the audio output device. The value [code]"Default"[/code] will play audio on the system-wide default audio output. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code]. + <member name="output_device" type="String" setter="set_output_device" getter="get_output_device" default=""Default""> + Name of the current device for audio output (see [method get_output_device_list]). On systems with multiple audio outputs (such as analog, USB and HDMI audio), this can be used to select the audio output device. The value [code]"Default"[/code] will play audio on the system-wide default audio output. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code]. </member> <member name="playback_speed_scale" type="float" setter="set_playback_speed_scale" getter="get_playback_speed_scale" default="1.0"> Scales the rate at which audio is played (i.e. setting it to [code]0.5[/code] will make the audio be played at half its speed). diff --git a/doc/classes/Camera2D.xml b/doc/classes/Camera2D.xml index 9315a85e1f..4156c9451a 100644 --- a/doc/classes/Camera2D.xml +++ b/doc/classes/Camera2D.xml @@ -55,6 +55,18 @@ [b]Note:[/b] The returned value is not the same as [member Node2D.global_position], as it is affected by the drag properties. It is also not the same as the current position if [member position_smoothing_enabled] is [code]true[/code] (see [method get_screen_center_position]). </description> </method> + <method name="is_current" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if this [Camera2D] is the active camera (see [method Viewport.get_camera_2d]). + </description> + </method> + <method name="make_current"> + <return type="void" /> + <description> + Forces this [Camera2D] to become the current active one. [member enabled] must be [code]true[/code]. + </description> + </method> <method name="reset_smoothing"> <return type="void" /> <description> @@ -83,9 +95,6 @@ <member name="anchor_mode" type="int" setter="set_anchor_mode" getter="get_anchor_mode" enum="Camera2D.AnchorMode" default="1"> The Camera2D's anchor point. See [enum AnchorMode] constants. </member> - <member name="current" type="bool" setter="set_current" getter="is_current" default="false"> - If [code]true[/code], the camera acts as the active camera for its [Viewport] ancestor. Only one camera can be current in a given viewport, so setting a different camera in the same viewport [code]current[/code] will disable whatever camera was already active in that viewport. - </member> <member name="custom_viewport" type="Node" setter="set_custom_viewport" getter="get_custom_viewport"> The custom [Viewport] node attached to the [Camera2D]. If [code]null[/code] or not a [Viewport], uses the default viewport instead. </member> @@ -124,6 +133,10 @@ <member name="editor_draw_screen" type="bool" setter="set_screen_drawing_enabled" getter="is_screen_drawing_enabled" default="true"> If [code]true[/code], draws the camera's screen rectangle in the editor. </member> + <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> + Controls whether the camera can be active or not. If [code]true[/code], the [Camera2D] will become the main camera when it enters the scene tree and there is no active camera currently (see [method Viewport.get_camera_2d]). + When the camera is currently active and [member enabled] is set to [code]false[/code], the next enabled [Camera2D] in the scene tree will become active. + </member> <member name="ignore_rotation" type="bool" setter="set_ignore_rotation" getter="is_ignoring_rotation" default="true"> If [code]true[/code], the camera's rendered view is not affected by its [member Node2D.rotation] and [member Node2D.global_rotation]. </member> diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index 6ae4dc4177..92fd8bcc6a 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -111,6 +111,21 @@ <member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false"> If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer2D]. When [method NavigationAgent2D.set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it. </member> + <member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false"> + If [code]true[/code] shows debug visuals for this agent. + </member> + <member name="debug_path_custom_color" type="Color" setter="set_debug_path_custom_color" getter="get_debug_path_custom_color" default="Color(1, 1, 1, 1)"> + If [member debug_use_custom] is [code]true[/code] uses this color for this agent instead of global color. + </member> + <member name="debug_path_custom_line_width" type="float" setter="set_debug_path_custom_line_width" getter="get_debug_path_custom_line_width" default="1.0"> + If [member debug_use_custom] is [code]true[/code] uses this line width for rendering paths for this agent instead of global line width. + </member> + <member name="debug_path_custom_point_size" type="float" setter="set_debug_path_custom_point_size" getter="get_debug_path_custom_point_size" default="4.0"> + If [member debug_use_custom] is [code]true[/code] uses this rasterized point size for rendering path points for this agent instead of global point size. + </member> + <member name="debug_use_custom" type="bool" setter="set_debug_use_custom" getter="get_debug_use_custom" default="false"> + If [code]true[/code] uses the defined [member debug_path_custom_color] for this agent instead of global color. + </member> <member name="max_neighbors" type="int" setter="set_max_neighbors" getter="get_max_neighbors" default="10"> The maximum number of neighbors for the agent to consider. </member> diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml index a22cd6dd46..0ed11bc477 100644 --- a/doc/classes/NavigationAgent3D.xml +++ b/doc/classes/NavigationAgent3D.xml @@ -114,6 +114,18 @@ <member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false"> If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer3D]. When [method NavigationAgent3D.set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it. </member> + <member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false"> + If [code]true[/code] shows debug visuals for this agent. + </member> + <member name="debug_path_custom_color" type="Color" setter="set_debug_path_custom_color" getter="get_debug_path_custom_color" default="Color(1, 1, 1, 1)"> + If [member debug_use_custom] is [code]true[/code] uses this color for this agent instead of global color. + </member> + <member name="debug_path_custom_point_size" type="float" setter="set_debug_path_custom_point_size" getter="get_debug_path_custom_point_size" default="4.0"> + If [member debug_use_custom] is [code]true[/code] uses this rasterized point size for rendering path points for this agent instead of global point size. + </member> + <member name="debug_use_custom" type="bool" setter="set_debug_use_custom" getter="get_debug_use_custom" default="false"> + If [code]true[/code] uses the defined [member debug_path_custom_color] for this agent instead of global color. + </member> <member name="ignore_y" type="bool" setter="set_ignore_y" getter="get_ignore_y" default="true"> Ignores collisions on the Y axis. Must be true to move on a horizontal plane. </member> diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 16f6de5238..7270a19b4d 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -528,5 +528,10 @@ Emitted when a navigation map is updated, when a region moves or is modified. </description> </signal> + <signal name="navigation_debug_changed"> + <description> + Emitted when navigation debug settings are changed. Only available in debug builds. + </description> + </signal> </signals> </class> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index f1ada58e0d..f2ef93c6d2 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -426,6 +426,9 @@ <member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await. </member> + <member name="debug/gdscript/warnings/renamed_in_godot_4_hint" type="bool" setter="" getter="" default="1"> + When enabled, using a property, enum, or function that was renamed since Godot 3 will produce a hint if an error occurs. + </member> <member name="debug/gdscript/warnings/return_value_discarded" type="int" setter="" getter="" default="0"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a function without using its return value (by assigning it to a variable or using it as a function argument). Such return values are sometimes used to denote possible errors using the [enum Error] enum. </member> @@ -474,6 +477,9 @@ <member name="debug/gdscript/warnings/unsafe_property_access" type="int" setter="" getter="" default="0"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when accessing a property whose presence is not guaranteed at compile-time in the class. </member> + <member name="debug/gdscript/warnings/unsafe_void_return" type="int" setter="" getter="" default="0"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when returning a call from a [code]void[/code] function when such call cannot be guaranteed to be also [code]void[/code]. + </member> <member name="debug/gdscript/warnings/unused_local_constant" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local constant is never used. </member> @@ -522,9 +528,21 @@ <member name="debug/shapes/collision/shape_color" type="Color" setter="" getter="" default="Color(0, 0.6, 0.7, 0.42)"> Color of the collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu. </member> + <member name="debug/shapes/navigation/agent_path_color" type="Color" setter="" getter="" default="Color(1, 0, 0, 1)"> + Color to display enabled navigation agent paths when an agent has debug enabled. + </member> + <member name="debug/shapes/navigation/agent_path_point_size" type="float" setter="" getter="" default="4.0"> + Rasterized size (pixel) used to render navigation agent path points when an agent has debug enabled. + </member> <member name="debug/shapes/navigation/edge_connection_color" type="Color" setter="" getter="" default="Color(1, 0, 1, 1)"> Color to display edge connections between navigation regions, visible when "Visible Navigation" is enabled in the Debug menu. </member> + <member name="debug/shapes/navigation/enable_agent_paths" type="bool" setter="" getter="" default="true"> + If enabled, displays navigation agent paths when an agent has debug enabled. + </member> + <member name="debug/shapes/navigation/enable_agent_paths_xray" type="bool" setter="" getter="" default="true"> + If enabled, displays navigation agent paths through geometry when an agent has debug enabled. + </member> <member name="debug/shapes/navigation/enable_edge_connections" type="bool" setter="" getter="" default="true"> If enabled, displays edge connections between navigation regions when "Visible Navigation" is enabled in the Debug menu. </member> diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index e9eb11e2e3..c454da8e23 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -44,10 +44,10 @@ OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, void *inClientData) { AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData); - // If our selected device is the Default call set_device to update the + // If our selected input device is the Default, call set_input_device to update the // kAudioOutputUnitProperty_CurrentDevice property - if (driver->capture_device_name == "Default") { - driver->capture_set_device("Default"); + if (driver->input_device_name == "Default") { + driver->set_input_device("Default"); } return noErr; @@ -58,10 +58,10 @@ OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID void *inClientData) { AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData); - // If our selected device is the Default call set_device to update the + // If our selected output device is the Default call set_output_device to update the // kAudioOutputUnitProperty_CurrentDevice property - if (driver->device_name == "Default") { - driver->set_device("Default"); + if (driver->output_device_name == "Default") { + driver->set_output_device("Default"); } return noErr; @@ -495,7 +495,7 @@ Error AudioDriverCoreAudio::capture_stop() { #ifdef MACOS_ENABLED -PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) { +PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) { PackedStringArray list; list.push_back("Default"); @@ -514,7 +514,7 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount; i++) { - prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + prop.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size); @@ -555,10 +555,10 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) { return list; } -void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { +void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) { AudioDeviceID deviceId; bool found = false; - if (device != "Default") { + if (output_device != "Default") { AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDevices; @@ -573,7 +573,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount && !found; i++) { - prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + prop.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size); @@ -602,7 +602,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { ERR_FAIL_NULL_MSG(buffer, "Out of memory."); if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")"; - if (name == device) { + if (name == output_device) { deviceId = audioDevices[i]; found = true; } @@ -618,7 +618,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { if (!found) { // If we haven't found the desired device get the system default one UInt32 size = sizeof(AudioDeviceID); - UInt32 elem = capture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; + UInt32 elem = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId); @@ -628,10 +628,10 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { } if (found) { - OSStatus result = AudioUnitSetProperty(capture ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); + OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); ERR_FAIL_COND(result != noErr); - if (capture) { + if (input) { // Reset audio input to keep synchronization. input_position = 0; input_size = 0; @@ -639,34 +639,34 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { } } -PackedStringArray AudioDriverCoreAudio::get_device_list() { +PackedStringArray AudioDriverCoreAudio::get_output_device_list() { return _get_device_list(); } -String AudioDriverCoreAudio::get_device() { - return device_name; +String AudioDriverCoreAudio::get_output_device() { + return output_device_name; } -void AudioDriverCoreAudio::set_device(String device) { - device_name = device; +void AudioDriverCoreAudio::set_output_device(String output_device) { + output_device_name = output_device; if (active) { - _set_device(device_name); + _set_device(output_device_name); } } -void AudioDriverCoreAudio::capture_set_device(const String &p_name) { - capture_device_name = p_name; +void AudioDriverCoreAudio::set_input_device(const String &p_name) { + input_device_name = p_name; if (active) { - _set_device(capture_device_name, true); + _set_device(input_device_name, true); } } -PackedStringArray AudioDriverCoreAudio::capture_get_device_list() { +PackedStringArray AudioDriverCoreAudio::get_input_device_list() { return _get_device_list(true); } -String AudioDriverCoreAudio::capture_get_device() { - return capture_device_name; +String AudioDriverCoreAudio::get_input_device() { + return input_device_name; } #endif diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index 675265757b..2b192e630e 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -47,8 +47,8 @@ class AudioDriverCoreAudio : public AudioDriver { bool active = false; Mutex mutex; - String device_name = "Default"; - String capture_device_name = "Default"; + String output_device_name = "Default"; + String input_device_name = "Default"; int mix_rate = 0; unsigned int channels = 2; @@ -60,7 +60,7 @@ class AudioDriverCoreAudio : public AudioDriver { #ifdef MACOS_ENABLED PackedStringArray _get_device_list(bool capture = false); - void _set_device(const String &device, bool capture = false); + void _set_device(const String &output_device, bool capture = false); static OSStatus input_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, @@ -107,13 +107,13 @@ public: void stop(); #ifdef MACOS_ENABLED - virtual PackedStringArray get_device_list(); - virtual String get_device(); - virtual void set_device(String device); + virtual PackedStringArray get_output_device_list(); + virtual String get_output_device(); + virtual void set_output_device(String output_device); - virtual PackedStringArray capture_get_device_list(); - virtual void capture_set_device(const String &p_name); - virtual String capture_get_device(); + virtual PackedStringArray get_input_device_list(); + virtual void set_input_device(const String &p_name); + virtual String get_input_device(); #endif AudioDriverCoreAudio(); diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index e14c3c7f7a..f96247a473 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -106,15 +106,15 @@ void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_inf ERR_FAIL_COND_MSG(!i, "PulseAudio server info is null."); AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); - ad->capture_default_device = i->default_source_name; - ad->default_device = i->default_sink_name; + ad->default_input_device = i->default_source_name; + ad->default_output_device = i->default_sink_name; ad->pa_status++; } -Error AudioDriverPulseAudio::detect_channels(bool capture) { - pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map); +Error AudioDriverPulseAudio::detect_channels(bool input) { + pa_channel_map_init_stereo(input ? &pa_rec_map : &pa_map); - String device = capture ? capture_device_name : device_name; + String device = input ? input_device_name : output_device_name; if (device == "Default") { // Get the default output device name pa_status = 0; @@ -136,7 +136,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { char dev[1024]; if (device == "Default") { - strcpy(dev, capture ? capture_default_device.utf8().get_data() : default_device.utf8().get_data()); + strcpy(dev, input ? default_input_device.utf8().get_data() : default_output_device.utf8().get_data()); } else { strcpy(dev, device.utf8().get_data()); } @@ -145,7 +145,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { // Now using the device name get the amount of channels pa_status = 0; pa_operation *pa_op; - if (capture) { + if (input) { pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this); } else { pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); @@ -165,7 +165,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { return FAILED; } } else { - if (capture) { + if (input) { ERR_PRINT("pa_context_get_source_info_by_name error"); } else { ERR_PRINT("pa_context_get_sink_info_by_name error"); @@ -175,13 +175,13 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { return OK; } -Error AudioDriverPulseAudio::init_device() { - // If there is a specified device check that it is really present - if (device_name != "Default") { - PackedStringArray list = get_device_list(); - if (list.find(device_name) == -1) { - device_name = "Default"; - new_device = "Default"; +Error AudioDriverPulseAudio::init_output_device() { + // If there is a specified output device, check that it is really present + if (output_device_name != "Default") { + PackedStringArray list = get_output_device_list(); + if (list.find(output_device_name) == -1) { + output_device_name = "Default"; + new_output_device = "Default"; } } @@ -192,7 +192,7 @@ Error AudioDriverPulseAudio::init_device() { Error err = detect_channels(); if (err != OK) { // This most likely means there are no sinks. - ERR_PRINT("PulseAudio: init device failed to detect number of output channels"); + ERR_PRINT("PulseAudio: init_output_device failed to detect number of output channels"); return err; } @@ -256,7 +256,7 @@ Error AudioDriverPulseAudio::init_device() { attr.maxlength = (uint32_t)-1; attr.minreq = (uint32_t)-1; - const char *dev = device_name == "Default" ? nullptr : device_name.utf8().get_data(); + const char *dev = output_device_name == "Default" ? nullptr : output_device_name.utf8().get_data(); pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, nullptr, nullptr); ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN); @@ -346,7 +346,7 @@ Error AudioDriverPulseAudio::init() { return ERR_CANT_OPEN; } - init_device(); + init_output_device(); thread.start(AudioDriverPulseAudio::thread_func, this); return OK; @@ -448,18 +448,18 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { } } - // User selected a new device, finish the current one so we'll init the new device - if (ad->device_name != ad->new_device) { - ad->device_name = ad->new_device; - ad->finish_device(); + // User selected a new output device, finish the current one so we'll init the new output device + if (ad->output_device_name != ad->new_output_device) { + ad->output_device_name = ad->new_output_device; + ad->finish_output_device(); - Error err = ad->init_device(); + Error err = ad->init_output_device(); if (err != OK) { - ERR_PRINT("PulseAudio: init_device error"); - ad->device_name = "Default"; - ad->new_device = "Default"; + ERR_PRINT("PulseAudio: init_output_device error"); + ad->output_device_name = "Default"; + ad->new_output_device = "Default"; - err = ad->init_device(); + err = ad->init_output_device(); if (err != OK) { ad->active.clear(); ad->exit_thread.set(); @@ -471,11 +471,11 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { write_ofs = 0; } - // If we're using the default device check that the current device is still the default - if (ad->device_name == "Default") { + // If we're using the default output device, check that the current output device is still the default + if (ad->output_device_name == "Default") { uint64_t msec = OS::get_singleton()->get_ticks_msec(); if (msec > (default_device_msec + 1000)) { - String old_default_device = ad->default_device; + String old_default_device = ad->default_output_device; default_device_msec = msec; @@ -494,12 +494,12 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(ad->pa_ctx)))); } - if (old_default_device != ad->default_device) { - ad->finish_device(); + if (old_default_device != ad->default_output_device) { + ad->finish_output_device(); - Error err = ad->init_device(); + Error err = ad->init_output_device(); if (err != OK) { - ERR_PRINT("PulseAudio: init_device error"); + ERR_PRINT("PulseAudio: init_output_device error"); ad->active.clear(); ad->exit_thread.set(); break; @@ -541,18 +541,18 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { } } - // User selected a new device, finish the current one so we'll init the new device - if (ad->capture_device_name != ad->capture_new_device) { - ad->capture_device_name = ad->capture_new_device; - ad->capture_finish_device(); + // User selected a new input device, finish the current one so we'll init the new input device + if (ad->input_device_name != ad->new_input_device) { + ad->input_device_name = ad->new_input_device; + ad->finish_input_device(); - Error err = ad->capture_init_device(); + Error err = ad->init_input_device(); if (err != OK) { - ERR_PRINT("PulseAudio: capture_init_device error"); - ad->capture_device_name = "Default"; - ad->capture_new_device = "Default"; + ERR_PRINT("PulseAudio: init_input_device error"); + ad->input_device_name = "Default"; + ad->new_input_device = "Default"; - err = ad->capture_init_device(); + err = ad->init_input_device(); if (err != OK) { ad->active.clear(); ad->exit_thread.set(); @@ -596,7 +596,7 @@ void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, ad->pa_status++; } -PackedStringArray AudioDriverPulseAudio::get_device_list() { +PackedStringArray AudioDriverPulseAudio::get_output_device_list() { pa_devices.clear(); pa_devices.push_back("Default"); @@ -606,7 +606,7 @@ PackedStringArray AudioDriverPulseAudio::get_device_list() { lock(); - // Get the device list + // Get the output device list pa_status = 0; pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this); if (pa_op) { @@ -627,13 +627,13 @@ PackedStringArray AudioDriverPulseAudio::get_device_list() { return pa_devices; } -String AudioDriverPulseAudio::get_device() { - return device_name; +String AudioDriverPulseAudio::get_output_device() { + return output_device_name; } -void AudioDriverPulseAudio::set_device(String device) { +void AudioDriverPulseAudio::set_output_device(String output_device) { lock(); - new_device = device; + new_output_device = output_device; unlock(); } @@ -645,7 +645,7 @@ void AudioDriverPulseAudio::unlock() { mutex.unlock(); } -void AudioDriverPulseAudio::finish_device() { +void AudioDriverPulseAudio::finish_output_device() { if (pa_str) { pa_stream_disconnect(pa_str); pa_stream_unref(pa_str); @@ -661,7 +661,7 @@ void AudioDriverPulseAudio::finish() { exit_thread.set(); thread.wait_to_finish(); - finish_device(); + finish_output_device(); if (pa_ctx) { pa_context_disconnect(pa_ctx); @@ -675,13 +675,13 @@ void AudioDriverPulseAudio::finish() { } } -Error AudioDriverPulseAudio::capture_init_device() { - // If there is a specified device check that it is really present - if (capture_device_name != "Default") { - PackedStringArray list = capture_get_device_list(); - if (list.find(capture_device_name) == -1) { - capture_device_name = "Default"; - capture_new_device = "Default"; +Error AudioDriverPulseAudio::init_input_device() { + // If there is a specified input device, check that it is really present + if (input_device_name != "Default") { + PackedStringArray list = get_input_device_list(); + if (list.find(input_device_name) == -1) { + input_device_name = "Default"; + new_input_device = "Default"; } } @@ -718,7 +718,7 @@ Error AudioDriverPulseAudio::capture_init_device() { ERR_FAIL_V(ERR_CANT_OPEN); } - const char *dev = capture_device_name == "Default" ? nullptr : capture_device_name.utf8().get_data(); + const char *dev = input_device_name == "Default" ? nullptr : input_device_name.utf8().get_data(); pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); int error_code = pa_stream_connect_record(pa_rec_str, dev, &attr, flags); if (error_code < 0) { @@ -734,7 +734,7 @@ Error AudioDriverPulseAudio::capture_init_device() { return OK; } -void AudioDriverPulseAudio::capture_finish_device() { +void AudioDriverPulseAudio::finish_input_device() { if (pa_rec_str) { int ret = pa_stream_disconnect(pa_rec_str); if (ret != 0) { @@ -745,25 +745,25 @@ void AudioDriverPulseAudio::capture_finish_device() { } } -Error AudioDriverPulseAudio::capture_start() { +Error AudioDriverPulseAudio::input_start() { lock(); - Error err = capture_init_device(); + Error err = init_input_device(); unlock(); return err; } -Error AudioDriverPulseAudio::capture_stop() { +Error AudioDriverPulseAudio::input_stop() { lock(); - capture_finish_device(); + finish_input_device(); unlock(); return OK; } -void AudioDriverPulseAudio::capture_set_device(const String &p_name) { +void AudioDriverPulseAudio::set_input_device(const String &p_name) { lock(); - capture_new_device = p_name; + new_input_device = p_name; unlock(); } @@ -782,7 +782,7 @@ void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info ad->pa_status++; } -PackedStringArray AudioDriverPulseAudio::capture_get_device_list() { +PackedStringArray AudioDriverPulseAudio::get_input_device_list() { pa_rec_devices.clear(); pa_rec_devices.push_back("Default"); @@ -813,9 +813,9 @@ PackedStringArray AudioDriverPulseAudio::capture_get_device_list() { return pa_rec_devices; } -String AudioDriverPulseAudio::capture_get_device() { +String AudioDriverPulseAudio::get_input_device() { lock(); - String name = capture_device_name; + String name = input_device_name; unlock(); return name; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index 16398e67eb..68df03cb60 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -51,13 +51,13 @@ class AudioDriverPulseAudio : public AudioDriver { pa_channel_map pa_map = {}; pa_channel_map pa_rec_map = {}; - String device_name = "Default"; - String new_device = "Default"; - String default_device; + String output_device_name = "Default"; + String new_output_device = "Default"; + String default_output_device; - String capture_device_name; - String capture_new_device; - String capture_default_device; + String input_device_name; + String new_input_device; + String default_input_device; Vector<int32_t> samples_in; Vector<int16_t> samples_out; @@ -83,11 +83,11 @@ class AudioDriverPulseAudio : public AudioDriver { static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); - Error init_device(); - void finish_device(); + Error init_output_device(); + void finish_output_device(); - Error capture_init_device(); - void capture_finish_device(); + Error init_input_device(); + void finish_input_device(); Error detect_channels(bool capture = false); @@ -103,13 +103,13 @@ public: virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; - virtual PackedStringArray get_device_list(); - virtual String get_device(); - virtual void set_device(String device); + virtual PackedStringArray get_output_device_list(); + virtual String get_output_device(); + virtual void set_output_device(String output_device); - virtual PackedStringArray capture_get_device_list(); - virtual void capture_set_device(const String &p_name); - virtual String capture_get_device(); + virtual PackedStringArray get_input_device_list(); + virtual void set_input_device(const String &p_name); + virtual String get_input_device(); virtual void lock(); virtual void unlock(); @@ -117,8 +117,8 @@ public: virtual float get_latency(); - virtual Error capture_start(); - virtual Error capture_stop(); + virtual Error input_start(); + virtual Error input_stop(); AudioDriverPulseAudio(); ~AudioDriverPulseAudio() {} diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index b2745b1e86..42a2b85200 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -118,8 +118,8 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); #define CAPTURE_BUFFER_CHANNELS 2 -static bool default_render_device_changed = false; -static bool default_capture_device_changed = false; +static bool default_output_device_changed = false; +static bool default_input_device_changed = false; // Silence warning due to a COM API weirdness (GH-35194). #if defined(__GNUC__) && !defined(__clang__) @@ -181,9 +181,9 @@ public: HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { if (role == eConsole) { if (flow == eRender) { - default_render_device_changed = true; + default_output_device_changed = true; } else if (flow == eCapture) { - default_capture_device_changed = true; + default_input_device_changed = true; } } @@ -201,10 +201,10 @@ public: static CMMNotificationClient notif_client; -Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool p_reinit, bool p_no_audio_client_3) { +Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = nullptr; - IMMDevice *device = nullptr; + IMMDevice *output_device = nullptr; CoInitialize(nullptr); @@ -212,11 +212,11 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); if (p_device->device_name == "Default") { - hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); + hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device); } else { IMMDeviceCollection *devices = nullptr; - hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); + hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); LPWSTR strId = nullptr; @@ -255,20 +255,20 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } if (found) { - hr = enumerator->GetDevice(strId, &device); + hr = enumerator->GetDevice(strId, &output_device); } if (strId) { CoTaskMemFree(strId); } - if (device == nullptr) { - hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); + if (output_device == nullptr) { + hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device); } } if (p_reinit) { - // In case we're trying to re-initialize the device prevent throwing this error on the console, + // In case we're trying to re-initialize the device, prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. if (hr != S_OK) { return ERR_CANT_OPEN; @@ -284,28 +284,28 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } - using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) + using_audio_client_3 = !p_input; // IID_IAudioClient3 is only used for adjustable output latency (not input) if (p_no_audio_client_3) { using_audio_client_3 = false; } if (using_audio_client_3) { - hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + hr = output_device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); if (hr != S_OK) { // IID_IAudioClient3 will never activate on OS versions before Windows 10. // Older Windows versions should fall back gracefully. using_audio_client_3 = false; - print_verbose("WASAPI: Couldn't activate device with IAudioClient3 interface, falling back to IAudioClient interface"); + print_verbose("WASAPI: Couldn't activate output_device with IAudioClient3 interface, falling back to IAudioClient interface"); } else { - print_verbose("WASAPI: Activated device using IAudioClient3 interface"); + print_verbose("WASAPI: Activated output_device using IAudioClient3 interface"); } } if (!using_audio_client_3) { - hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + hr = output_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); } - SAFE_RELEASE(device) + SAFE_RELEASE(output_device) if (p_reinit) { if (hr != S_OK) { @@ -339,7 +339,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c WAVEFORMATEX *closest = nullptr; hr = p_device->audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, pwfex, &closest); if (hr == S_FALSE) { - WARN_PRINT("WASAPI: Mix format is not supported by the Device"); + WARN_PRINT("WASAPI: Mix format is not supported by the output_device"); if (closest) { print_verbose("WASAPI: closest->wFormatTag = " + itos(closest->wFormatTag)); print_verbose("WASAPI: closest->nChannels = " + itos(closest->nChannels)); @@ -385,14 +385,14 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c pwfex->nSamplesPerSec = mix_rate; pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); } - hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr); + hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_input ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr); ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); UINT32 max_frames; hr = p_device->audio_client->GetBufferSize(&max_frames); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Due to WASAPI Shared Mode we have no control of the buffer size - if (!p_capture) { + if (!p_input) { buffer_frames = max_frames; int64_t latency = 0; @@ -421,8 +421,8 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c if (hr != S_OK) { print_verbose("WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(device) - return audio_device_init(p_device, p_capture, p_reinit, true); + SAFE_RELEASE(output_device) + return audio_device_init(p_device, p_input, p_reinit, true); } // Period frames must be an integral multiple of fundamental_period_frames or IAudioClient3 initialization will fail, @@ -443,8 +443,8 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c if (hr != S_OK) { print_verbose("WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(device); - return audio_device_init(p_device, p_capture, p_reinit, true); + SAFE_RELEASE(output_device); + return audio_device_init(p_device, p_input, p_reinit, true); } else { uint32_t output_latency_in_frames; WAVEFORMATEX *current_pwfex; @@ -455,13 +455,13 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } else { print_verbose("WASAPI: GetCurrentSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(device); - return audio_device_init(p_device, p_capture, p_reinit, true); + SAFE_RELEASE(output_device); + return audio_device_init(p_device, p_input, p_reinit, true); } } } - if (p_capture) { + if (p_input) { hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client); } else { hr = p_device->audio_client->GetService(IID_IAudioRenderClient, (void **)&p_device->render_client); @@ -470,12 +470,12 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c // Free memory CoTaskMemFree(pwfex); - SAFE_RELEASE(device) + SAFE_RELEASE(output_device) return OK; } -Error AudioDriverWASAPI::init_render_device(bool p_reinit) { +Error AudioDriverWASAPI::init_output_device(bool p_reinit) { Error err = audio_device_init(&audio_output, false, p_reinit); if (err != OK) { return err; @@ -507,7 +507,7 @@ Error AudioDriverWASAPI::init_render_device(bool p_reinit) { return OK; } -Error AudioDriverWASAPI::init_capture_device(bool p_reinit) { +Error AudioDriverWASAPI::init_input_device(bool p_reinit) { Error err = audio_device_init(&audio_input, true, p_reinit); if (err != OK) { return err; @@ -538,11 +538,11 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { return OK; } -Error AudioDriverWASAPI::finish_render_device() { +Error AudioDriverWASAPI::finish_output_device() { return audio_device_finish(&audio_output); } -Error AudioDriverWASAPI::finish_capture_device() { +Error AudioDriverWASAPI::finish_input_device() { return audio_device_finish(&audio_input); } @@ -551,9 +551,9 @@ Error AudioDriverWASAPI::init() { target_latency_ms = GLOBAL_GET("audio/driver/output_latency"); - Error err = init_render_device(); + Error err = init_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: init_render_device error"); + ERR_PRINT("WASAPI: init_output_device error"); } exit_thread.clear(); @@ -575,7 +575,7 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } -PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { +PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) { PackedStringArray list; IMMDeviceCollection *devices = nullptr; IMMDeviceEnumerator *enumerator = nullptr; @@ -587,7 +587,7 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); - hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); + hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); UINT count = 0; @@ -595,13 +595,13 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); for (ULONG i = 0; i < count; i++) { - IMMDevice *device = nullptr; + IMMDevice *output_device = nullptr; - hr = devices->Item(i, &device); + hr = devices->Item(i, &output_device); ERR_BREAK(hr != S_OK); IPropertyStore *props = nullptr; - hr = device->OpenPropertyStore(STGM_READ, &props); + hr = output_device->OpenPropertyStore(STGM_READ, &props); ERR_BREAK(hr != S_OK); PROPVARIANT propvar; @@ -614,7 +614,7 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { PropVariantClear(&propvar); props->Release(); - device->Release(); + output_device->Release(); } devices->Release(); @@ -622,11 +622,11 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { return list; } -PackedStringArray AudioDriverWASAPI::get_device_list() { +PackedStringArray AudioDriverWASAPI::get_output_device_list() { return audio_device_get_list(false); } -String AudioDriverWASAPI::get_device() { +String AudioDriverWASAPI::get_output_device() { lock(); String name = audio_output.device_name; unlock(); @@ -634,9 +634,9 @@ String AudioDriverWASAPI::get_device() { return name; } -void AudioDriverWASAPI::set_device(String device) { +void AudioDriverWASAPI::set_output_device(String output_device) { lock(); - audio_output.new_device = device; + audio_output.new_device = output_device; unlock(); } @@ -769,13 +769,13 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { avail_frames -= write_frames; written_frames += write_frames; } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { - // Device is not valid anymore, reopen it + // output_device is not valid anymore, reopen it - Error err = ad->finish_render_device(); + Error err = ad->finish_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_render_device error"); + ERR_PRINT("WASAPI: finish_output_device error"); } else { - // We reopened the device and samples_in may have resized, so invalidate the current avail_frames + // We reopened the output device and samples_in may have resized, so invalidate the current avail_frames avail_frames = 0; } } else { @@ -790,37 +790,37 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } if (invalidated) { - // Device is not valid anymore - WARN_PRINT("WASAPI: Current device invalidated, closing device"); + // output_device is not valid anymore + WARN_PRINT("WASAPI: Current output_device invalidated, closing output_device"); - Error err = ad->finish_render_device(); + Error err = ad->finish_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_render_device error"); + ERR_PRINT("WASAPI: finish_output_device error"); } } } - // If we're using the Default device and it changed finish it so we'll re-init the device - if (ad->audio_output.device_name == "Default" && default_render_device_changed) { - Error err = ad->finish_render_device(); + // If we're using the Default output device and it changed finish it so we'll re-init the output device + if (ad->audio_output.device_name == "Default" && default_output_device_changed) { + Error err = ad->finish_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_render_device error"); + ERR_PRINT("WASAPI: finish_output_device error"); } - default_render_device_changed = false; + default_output_device_changed = false; } - // User selected a new device, finish the current one so we'll init the new device + // User selected a new output device, finish the current one so we'll init the new output device if (ad->audio_output.device_name != ad->audio_output.new_device) { ad->audio_output.device_name = ad->audio_output.new_device; - Error err = ad->finish_render_device(); + Error err = ad->finish_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_render_device error"); + ERR_PRINT("WASAPI: finish_output_device error"); } } if (!ad->audio_output.audio_client) { - Error err = ad->init_render_device(true); + Error err = ad->init_output_device(true); if (err == OK) { ad->start(); } @@ -873,29 +873,29 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } } - // If we're using the Default device and it changed finish it so we'll re-init the device - if (ad->audio_input.device_name == "Default" && default_capture_device_changed) { - Error err = ad->finish_capture_device(); + // If we're using the Default output device and it changed finish it so we'll re-init the output device + if (ad->audio_input.device_name == "Default" && default_input_device_changed) { + Error err = ad->finish_input_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_capture_device error"); + ERR_PRINT("WASAPI: finish_input_device error"); } - default_capture_device_changed = false; + default_input_device_changed = false; } - // User selected a new device, finish the current one so we'll init the new device + // User selected a new input device, finish the current one so we'll init the new input device if (ad->audio_input.device_name != ad->audio_input.new_device) { ad->audio_input.device_name = ad->audio_input.new_device; - Error err = ad->finish_capture_device(); + Error err = ad->finish_input_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_capture_device error"); + ERR_PRINT("WASAPI: finish_input_device error"); } } if (!ad->audio_input.audio_client) { - Error err = ad->init_capture_device(true); + Error err = ad->init_input_device(true); if (err == OK) { - ad->capture_start(); + ad->input_start(); } } } @@ -933,14 +933,14 @@ void AudioDriverWASAPI::finish() { exit_thread.set(); thread.wait_to_finish(); - finish_capture_device(); - finish_render_device(); + finish_input_device(); + finish_output_device(); } -Error AudioDriverWASAPI::capture_start() { - Error err = init_capture_device(); +Error AudioDriverWASAPI::input_start() { + Error err = init_input_device(); if (err != OK) { - ERR_PRINT("WASAPI: init_capture_device error"); + ERR_PRINT("WASAPI: init_input_device error"); return err; } @@ -953,7 +953,7 @@ Error AudioDriverWASAPI::capture_start() { return OK; } -Error AudioDriverWASAPI::capture_stop() { +Error AudioDriverWASAPI::input_stop() { if (audio_input.active.is_set()) { audio_input.audio_client->Stop(); audio_input.active.clear(); @@ -964,17 +964,17 @@ Error AudioDriverWASAPI::capture_stop() { return FAILED; } -void AudioDriverWASAPI::capture_set_device(const String &p_name) { +void AudioDriverWASAPI::set_input_device(const String &p_name) { lock(); audio_input.new_device = p_name; unlock(); } -PackedStringArray AudioDriverWASAPI::capture_get_device_list() { +PackedStringArray AudioDriverWASAPI::get_input_device_list() { return audio_device_get_list(true); } -String AudioDriverWASAPI::capture_get_device() { +String AudioDriverWASAPI::get_input_device() { lock(); String name = audio_input.device_name; unlock(); diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index 6eea24f78a..bf18ba8c99 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -47,8 +47,8 @@ class AudioDriverWASAPI : public AudioDriver { class AudioDeviceWASAPI { public: IAudioClient *audio_client = nullptr; - IAudioRenderClient *render_client = nullptr; - IAudioCaptureClient *capture_client = nullptr; + IAudioRenderClient *render_client = nullptr; // Output + IAudioCaptureClient *capture_client = nullptr; // Input SafeFlag active; WORD format_tag = 0; @@ -56,8 +56,8 @@ class AudioDriverWASAPI : public AudioDriver { unsigned int channels = 0; unsigned int frame_size = 0; - String device_name = "Default"; - String new_device = "Default"; + String device_name = "Default"; // Output OR Input + String new_device = "Default"; // Output OR Input AudioDeviceWASAPI() {} }; @@ -83,15 +83,15 @@ class AudioDriverWASAPI : public AudioDriver { static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i); static void thread_func(void *p_udata); - Error init_render_device(bool p_reinit = false); - Error init_capture_device(bool p_reinit = false); + Error init_output_device(bool p_reinit = false); + Error init_input_device(bool p_reinit = false); - Error finish_render_device(); - Error finish_capture_device(); + Error finish_output_device(); + Error finish_input_device(); - Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool p_reinit, bool p_no_audio_client_3 = false); + Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3 = false); Error audio_device_finish(AudioDeviceWASAPI *p_device); - PackedStringArray audio_device_get_list(bool p_capture); + PackedStringArray audio_device_get_list(bool p_input); public: virtual const char *get_name() const { @@ -103,18 +103,18 @@ public: virtual int get_mix_rate() const; virtual float get_latency(); virtual SpeakerMode get_speaker_mode() const; - virtual PackedStringArray get_device_list(); - virtual String get_device(); - virtual void set_device(String device); + virtual PackedStringArray get_output_device_list(); + virtual String get_output_device(); + virtual void set_output_device(String output_device); virtual void lock(); virtual void unlock(); virtual void finish(); - virtual Error capture_start(); - virtual Error capture_stop(); - virtual PackedStringArray capture_get_device_list(); - virtual void capture_set_device(const String &p_name); - virtual String capture_get_device(); + virtual Error input_start(); + virtual Error input_stop(); + virtual PackedStringArray get_input_device_list(); + virtual void set_input_device(const String &p_name); + virtual String get_input_device(); AudioDriverWASAPI(); }; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index ef33b82390..644c32e8a4 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1591,6 +1591,11 @@ void EditorFileSystem::_update_script_classes() { void EditorFileSystem::_update_pending_script_classes() { if (!update_script_paths.is_empty()) { _update_script_classes(); + } else { + // In case the class cache file was removed somehow, regenerate it. + if (!FileAccess::exists(ScriptServer::get_global_class_cache_file_path())) { + ScriptServer::save_global_classes(); + } } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f3f2f771af..0405749147 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -581,6 +581,7 @@ void EditorNode::_notification(int p_what) { ResourceImporterTexture::get_singleton()->update_imports(); + bottom_panel_updating = false; } break; case NOTIFICATION_ENTER_TREE: { @@ -5601,12 +5602,16 @@ void EditorNode::remove_bottom_panel_item(Control *p_item) { } void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) { + if (bottom_panel_updating) { + return; + } ERR_FAIL_INDEX(p_idx, bottom_panel_items.size()); if (bottom_panel_items[p_idx].control->is_visible() == p_enable) { return; } + bottom_panel_updating = true; if (p_enable) { for (int i = 0; i < bottom_panel_items.size(); i++) { bottom_panel_items[i].button->set_pressed(i == p_idx); diff --git a/editor/editor_node.h b/editor/editor_node.h index bb10abb589..3967f64c6b 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -460,6 +460,7 @@ private: EditorToaster *editor_toaster = nullptr; LinkButton *version_btn = nullptr; Button *bottom_panel_raise = nullptr; + bool bottom_panel_updating = false; Tree *disk_changed_list = nullptr; ConfirmationDialog *disk_changed = nullptr; diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index fe67813d65..a46484bb0e 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -814,7 +814,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & HashSet<String> paths; Vector<String> path_remaps; - paths.insert(ProjectSettings::get_singleton()->get_project_data_path().path_join("global_script_class_cache.cfg")); + paths.insert(ProjectSettings::get_singleton()->get_global_class_list_path()); if (p_preset->get_export_filter() == EditorExportPreset::EXPORT_ALL_RESOURCES) { //find stuff _export_find_resources(EditorFileSystem::get_singleton()->get_filesystem(), paths); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index aa5f9ff29a..6c6c89bcc0 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1085,10 +1085,10 @@ Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, co return p_node; } -Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) { +Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale) { // children first for (int i = 0; i < p_node->get_child_count(); i++) { - Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps); + Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps, p_applied_root_scale); if (!r) { i--; //was erased } @@ -1231,7 +1231,8 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< } else { shapes = get_collision_shapes( m->get_mesh(), - node_settings); + node_settings, + p_applied_root_scale); } if (shapes.size()) { @@ -1242,6 +1243,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< p_node->add_child(col, true); col->set_owner(p_node->get_owner()); col->set_transform(get_collision_shapes_transform(node_settings)); + col->set_position(p_applied_root_scale * col->get_position()); base = col; } break; case MESH_PHYSICS_RIGID_BODY_AND_MESH: { @@ -1249,6 +1251,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< rigid_body->set_name(p_node->get_name()); p_node->replace_by(rigid_body); rigid_body->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings)); + rigid_body->set_position(p_applied_root_scale * rigid_body->get_position()); p_node = rigid_body; mi->set_transform(Transform3D()); rigid_body->add_child(mi, true); @@ -1258,6 +1261,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< case MESH_PHYSICS_STATIC_COLLIDER_ONLY: { StaticBody3D *col = memnew(StaticBody3D); col->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings)); + col->set_position(p_applied_root_scale * col->get_position()); col->set_name(p_node->get_name()); p_node->replace_by(col); memdelete(p_node); @@ -1267,6 +1271,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< case MESH_PHYSICS_AREA_ONLY: { Area3D *area = memnew(Area3D); area->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings)); + area->set_position(p_applied_root_scale * area->get_position()); area->set_name(p_node->get_name()); p_node->replace_by(area); memdelete(p_node); @@ -2398,7 +2403,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p fps = (float)p_options[SNAME("animation/fps")]; } _pre_fix_animations(scene, scene, node_data, animation_data, fps); - _post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps); + _post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps, apply_root ? root_scale : 1.0); _post_fix_animations(scene, scene, node_data, animation_data, fps); String root_type = p_options["nodes/root_type"]; diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 2d08d4df50..aa057d3404 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -279,7 +279,7 @@ public: Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames); Node *_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps); - Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps); + Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale); Node *_post_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps); Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks); @@ -298,7 +298,7 @@ public: ResourceImporterScene(bool p_animation_import = false); template <class M> - static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options); + static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options, float p_applied_root_scale); template <class M> static Transform3D get_collision_shapes_transform(const M &p_options); @@ -314,7 +314,7 @@ public: }; template <class M> -Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options) { +Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options, float p_applied_root_scale) { ShapeType generate_shape_type = SHAPE_TYPE_DECOMPOSE_CONVEX; if (p_options.has(SNAME("physics/shape_type"))) { generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int(); @@ -409,7 +409,7 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> Ref<BoxShape3D> box; box.instantiate(); if (p_options.has(SNAME("primitive/size"))) { - box->set_size(p_options[SNAME("primitive/size")]); + box->set_size(p_options[SNAME("primitive/size")].operator Vector3() * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -420,7 +420,7 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> Ref<SphereShape3D> sphere; sphere.instantiate(); if (p_options.has(SNAME("primitive/radius"))) { - sphere->set_radius(p_options[SNAME("primitive/radius")]); + sphere->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -430,10 +430,10 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> Ref<CylinderShape3D> cylinder; cylinder.instantiate(); if (p_options.has(SNAME("primitive/height"))) { - cylinder->set_height(p_options[SNAME("primitive/height")]); + cylinder->set_height(p_options[SNAME("primitive/height")].operator float() * p_applied_root_scale); } if (p_options.has(SNAME("primitive/radius"))) { - cylinder->set_radius(p_options[SNAME("primitive/radius")]); + cylinder->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -443,10 +443,10 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> Ref<CapsuleShape3D> capsule; capsule.instantiate(); if (p_options.has(SNAME("primitive/height"))) { - capsule->set_height(p_options[SNAME("primitive/height")]); + capsule->set_height(p_options[SNAME("primitive/height")].operator float() * p_applied_root_scale); } if (p_options.has(SNAME("primitive/radius"))) { - capsule->set_radius(p_options[SNAME("primitive/radius")]); + capsule->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 044f7475c2..8d26feebf4 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -441,7 +441,7 @@ void SceneImportSettings::_update_view_gizmos() { // This collider_view doesn't have a mesh so we need to generate a new one. // Generate the mesh collider. - Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh_node->get_mesh(), e.value.settings); + Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh_node->get_mesh(), e.value.settings, 1.0); const Transform3D transform = ResourceImporterScene::get_collision_shapes_transform(e.value.settings); Ref<ArrayMesh> collider_view_mesh; diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 33aebe5883..df94815105 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -38,6 +38,7 @@ #include "editor/editor_undo_redo_manager.h" #include "scene/animation/animation_blend_tree.h" #include "scene/gui/check_box.h" +#include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { @@ -335,6 +336,7 @@ void AnimationNodeBlendSpace1DEditor::_update_space() { min_value->set_value(blend_space->get_min_space()); sync->set_pressed(blend_space->is_using_sync()); + interpolation->select(blend_space->get_blend_mode()); label_value->set_text(blend_space->get_value_label()); @@ -361,6 +363,8 @@ void AnimationNodeBlendSpace1DEditor::_config_changed(double) { undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap()); undo_redo->add_do_method(blend_space.ptr(), "set_use_sync", sync->is_pressed()); undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync", blend_space->is_using_sync()); + undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode", interpolation->get_selected()); + undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode", blend_space->get_blend_mode()); undo_redo->add_do_method(this, "_update_space"); undo_redo->add_undo_method(this, "_update_space"); undo_redo->commit_action(); @@ -579,6 +583,10 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); snap->set_icon(get_theme_icon(SNAME("SnapGrid"), SNAME("EditorIcons"))); open_editor->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + interpolation->clear(); + interpolation->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), "", 0); + interpolation->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), "", 1); + interpolation->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), "", 2); } break; case NOTIFICATION_PROCESS: { @@ -639,6 +647,7 @@ void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) { min_value->set_editable(!read_only); max_value->set_editable(!read_only); sync->set_disabled(read_only); + interpolation->set_disabled(read_only); } AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr; @@ -707,6 +716,13 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { top_hb->add_child(sync); sync->connect("toggled", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); + top_hb->add_child(memnew(VSeparator)); + + top_hb->add_child(memnew(Label(TTR("Blend:")))); + interpolation = memnew(OptionButton); + top_hb->add_child(interpolation); + interpolation->connect("item_selected", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); + edit_hb = memnew(HBoxContainer); top_hb->add_child(edit_hb); edit_hb->add_child(memnew(VSeparator)); diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index e71a4bd1b9..90104fc310 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -41,6 +41,7 @@ #include "scene/gui/tree.h" class CheckBox; +class OptionButton; class PanelContainer; class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { @@ -66,6 +67,7 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { SpinBox *min_value = nullptr; CheckBox *sync = nullptr; + OptionButton *interpolation = nullptr; HBoxContainer *edit_hb = nullptr; SpinBox *edit_value = nullptr; diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 9df2f0a9d1..14b5f7cefb 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -1523,8 +1523,25 @@ SpriteFramesEditor::SpriteFramesEditor() { delete_anim->set_disabled(true); delete_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_remove)); + autoplay_container = memnew(HBoxContainer); + hbc_animlist->add_child(autoplay_container); + + autoplay_container->add_child(memnew(VSeparator)); + + autoplay = memnew(Button); + autoplay->set_flat(true); + autoplay->set_tooltip_text(TTR("Autoplay on Load")); + autoplay_container->add_child(autoplay); + hbc_animlist->add_child(memnew(VSeparator)); + anim_loop = memnew(Button); + anim_loop->set_toggle_mode(true); + anim_loop->set_flat(true); + anim_loop->set_tooltip_text(TTR("Animation Looping")); + anim_loop->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_loop_changed)); + hbc_animlist->add_child(anim_loop); + anim_speed = memnew(SpinBox); anim_speed->set_suffix(TTR("FPS")); anim_speed->set_min(0); @@ -1535,23 +1552,6 @@ SpriteFramesEditor::SpriteFramesEditor() { anim_speed->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_animation_speed_changed)); hbc_animlist->add_child(anim_speed); - anim_loop = memnew(Button); - anim_loop->set_toggle_mode(true); - anim_loop->set_flat(true); - anim_loop->set_tooltip_text(TTR("Animation Looping")); - anim_loop->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_loop_changed)); - hbc_animlist->add_child(anim_loop); - - autoplay_container = memnew(HBoxContainer); - hbc_animlist->add_child(autoplay_container); - - autoplay_container->add_child(memnew(VSeparator)); - - autoplay = memnew(Button); - autoplay->set_flat(true); - autoplay->set_tooltip_text(TTR("Autoplay on Load")); - autoplay_container->add_child(autoplay); - anim_search_box = memnew(LineEdit); sub_vb->add_child(anim_search_box); anim_search_box->set_h_size_flags(SIZE_EXPAND_FILL); @@ -1953,9 +1953,7 @@ void SpriteFramesEditorPlugin::make_visible(bool p_visible) { EditorNode::get_singleton()->make_bottom_panel_item_visible(frames_editor); } else { button->hide(); - if (frames_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - } + frames_editor->edit(Ref<SpriteFrames>()); } } diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 319da02d7a..d3e16211f7 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -32,10 +32,11 @@ #include "modules/modules_enabled.gen.h" -const int ERROR_CODE = 77; - +#ifndef DISABLE_DEPRECATED #ifdef MODULE_REGEX_ENABLED +const int ERROR_CODE = 77; + #include "modules/regex/regex.h" #include "core/io/dir_access.h" @@ -44,7 +45,7 @@ const int ERROR_CODE = 77; #include "core/templates/list.h" #include "core/templates/local_vector.h" -static const char *enum_renames[][2] = { +const char *ProjectConverter3To4::enum_renames[][2] = { //// constants { "TYPE_COLOR_ARRAY", "TYPE_PACKED_COLOR_ARRAY" }, { "TYPE_FLOAT64_ARRAY", "TYPE_PACKED_FLOAT64_ARRAY" }, @@ -164,7 +165,7 @@ static const char *enum_renames[][2] = { { nullptr, nullptr }, }; -static const char *gdscript_function_renames[][2] = { +const char *ProjectConverter3To4::gdscript_function_renames[][2] = { // { "_set_name", "get_tracker_name"}, // XRPositionalTracker - CameraFeed use this // { "_unhandled_input", "_unhandled_key_input"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer // { "create_gizmo", "_create_gizmo"}, // EditorNode3DGizmoPlugin - may be used @@ -240,6 +241,9 @@ static const char *gdscript_function_renames[][2] = { { "can_generate_small_preview", "_can_generate_small_preview" }, // EditorResourcePreviewGenerator { "can_instance", "can_instantiate" }, // PackedScene, Script { "canvas_light_set_scale", "canvas_light_set_texture_scale" }, // RenderingServer + { "capture_get_device", "get_input_device" }, // AudioServer + { "capture_get_device_list", "get_input_device_list" }, // AudioServer + { "capture_set_device", "set_input_device" }, // AudioServer { "center_viewport_to_cursor", "center_viewport_to_caret" }, // TextEdit { "change_scene", "change_scene_to_file" }, // SceneTree { "change_scene_to", "change_scene_to_packed" }, // SceneTree @@ -300,6 +304,8 @@ static const char *gdscript_function_renames[][2] = { { "get_cursor_position", "get_caret_column" }, // LineEdit { "get_d", "get_distance" }, // LineShape2D { "get_depth_bias_enable", "get_depth_bias_enabled" }, // RDPipelineRasterizationState + { "get_device", "get_output_device" }, // AudioServer + { "get_device_list", "get_output_device_list" }, // AudioServer { "get_drag_data", "_get_drag_data" }, // Control { "get_editor_viewport", "get_editor_main_screen" }, // EditorPlugin { "get_enabled_focus_mode", "get_focus_mode" }, // BaseButton @@ -498,6 +504,7 @@ static const char *gdscript_function_renames[][2] = { { "set_cursor_position", "set_caret_column" }, // LineEdit { "set_d", "set_distance" }, // WorldMarginShape2D { "set_depth_bias_enable", "set_depth_bias_enabled" }, // RDPipelineRasterizationState + { "set_device", "set_output_device" }, // AudioServer { "set_doubleclick", "set_double_click" }, // InputEventMouseButton { "set_draw_red", "set_draw_warning" }, // EditorProperty { "set_enable_follow_smoothing", "set_position_smoothing_enabled" }, // Camera2D @@ -619,7 +626,7 @@ static const char *gdscript_function_renames[][2] = { }; // gdscript_function_renames clone with CamelCase -static const char *csharp_function_renames[][2] = { +const char *ProjectConverter3To4::csharp_function_renames[][2] = { // { "_SetName", "GetTrackerName"}, // XRPositionalTracker - CameraFeed use this // { "_UnhandledInput", "_UnhandledKeyInput"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer // { "CreateGizmo", "_CreateGizmo"}, // EditorNode3DGizmoPlugin - may be used @@ -696,6 +703,9 @@ static const char *csharp_function_renames[][2] = { { "CanGenerateSmallPreview", "_CanGenerateSmallPreview" }, // EditorResourcePreviewGenerator { "CanInstance", "CanInstantiate" }, // PackedScene, Script { "CanvasLightSetScale", "CanvasLightSetTextureScale" }, // RenderingServer + { "CaptureGetDevice", "GetInputDevice" }, // AudioServer + { "CaptureGetDeviceList", "GetInputDeviceList" }, // AudioServer + { "CaptureSetDevice", "SetInputDevice" }, // AudioServer { "CenterViewportToCursor", "CenterViewportToCaret" }, // TextEdit { "ChangeScene", "ChangeSceneToFile" }, // SceneTree { "ChangeSceneTo", "ChangeSceneToPacked" }, // SceneTree @@ -753,6 +763,8 @@ static const char *csharp_function_renames[][2] = { { "GetCursorPosition", "GetCaretColumn" }, // LineEdit { "GetD", "GetDistance" }, // LineShape2D { "GetDepthBiasEnable", "GetDepthBiasEnabled" }, // RDPipelineRasterizationState + { "GetDevice", "GetOutputDevice" }, // AudioServer + { "GetDeviceList", "GetOutputDeviceList" }, // AudioServer { "GetDragDataFw", "_GetDragDataFw" }, // ScriptEditor { "GetEditorViewport", "GetViewport" }, // EditorPlugin { "GetEnabledFocusMode", "GetFocusMode" }, // BaseButton @@ -941,6 +953,7 @@ static const char *csharp_function_renames[][2] = { { "SetCursorPosition", "SetCaretColumn" }, // LineEdit { "SetD", "SetDistance" }, // WorldMarginShape2D { "SetDepthBiasEnable", "SetDepthBiasEnabled" }, // RDPipelineRasterizationState + { "SetDevice", "SetOutputDevice" }, // AudioServer { "SetDoubleclick", "SetDoubleClick" }, // InputEventMouseButton { "SetEnableFollowSmoothing", "SetFollowSmoothingEnabled" }, // Camera2D { "SetEnabledFocusMode", "SetFocusMode" }, // BaseButton @@ -1056,7 +1069,7 @@ static const char *csharp_function_renames[][2] = { }; // Some needs to be disabled, because users can use this names as variables -static const char *gdscript_properties_renames[][2] = { +const char *ProjectConverter3To4::gdscript_properties_renames[][2] = { // // { "d", "distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą` // // {"alt","alt_pressed"}, // This may broke a lot of comments and user variables // // {"command","command_pressed"},// This may broke a lot of comments and user variables @@ -1069,6 +1082,7 @@ static const char *gdscript_properties_renames[][2] = { // // {"shift","shift_pressed"},// This may broke a lot of comments and user variables // { "autowrap", "autowrap_mode" }, // Label // { "cast_to", "target_position" }, // RayCast2D, RayCast3D + // { "device", "output_device"}, // AudioServer - Too vague, most likely breaks comments & variables // { "doubleclick", "double_click" }, // InputEventMouseButton // { "group", "button_group" }, // BaseButton // { "process_mode", "process_callback" }, // AnimationTree, Camera2D @@ -1084,6 +1098,7 @@ static const char *gdscript_properties_renames[][2] = { { "bbcode_text", "text" }, // RichTextLabel { "bg", "panel" }, // Theme { "bg_focus", "focus" }, // Theme + { "capture_device", "input_device" }, // AudioServer { "caret_blink_speed", "caret_blink_interval" }, // TextEdit, LineEdit { "caret_moving_by_right_click", "caret_move_on_right_click" }, // TextEdit { "caret_position", "caret_column" }, // LineEdit @@ -1173,7 +1188,7 @@ static const char *gdscript_properties_renames[][2] = { }; // Some needs to be disabled, because users can use this names as variables -static const char *csharp_properties_renames[][2] = { +const char *ProjectConverter3To4::csharp_properties_renames[][2] = { // // { "D", "Distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą` // // {"Alt","AltPressed"}, // This may broke a lot of comments and user variables // // {"Command","CommandPressed"},// This may broke a lot of comments and user variables @@ -1278,7 +1293,7 @@ static const char *csharp_properties_renames[][2] = { { nullptr, nullptr }, }; -static const char *gdscript_signals_renames[][2] = { +const char *ProjectConverter3To4::gdscript_signals_renames[][2] = { // {"instantiate","instance"}, // FileSystemDock // { "hide", "hidden" }, // CanvasItem - function with same name exists // { "tween_all_completed","loop_finished"}, // Tween - TODO, not sure @@ -1303,7 +1318,7 @@ static const char *gdscript_signals_renames[][2] = { { nullptr, nullptr }, }; -static const char *csharp_signals_renames[][2] = { +const char *ProjectConverter3To4::csharp_signals_renames[][2] = { // {"Instantiate","Instance"}, // FileSystemDock // { "Hide", "Hidden" }, // CanvasItem - function with same name exists // { "TweenAllCompleted","LoopFinished"}, // Tween - TODO, not sure @@ -1327,7 +1342,7 @@ static const char *csharp_signals_renames[][2] = { }; -static const char *project_settings_renames[][2] = { +const char *ProjectConverter3To4::project_settings_renames[][2] = { { "audio/channel_disable_threshold_db", "audio/buses/channel_disable_threshold_db" }, { "audio/channel_disable_time", "audio/buses/channel_disable_time" }, { "audio/default_bus_layout", "audio/buses/default_bus_layout" }, @@ -1372,7 +1387,7 @@ static const char *project_settings_renames[][2] = { { nullptr, nullptr }, }; -static const char *input_map_renames[][2] = { +const char *ProjectConverter3To4::input_map_renames[][2] = { { ",\"alt\":", ",\"alt_pressed\":" }, { ",\"shift\":", ",\"shift_pressed\":" }, { ",\"control\":", ",\"ctrl_pressed\":" }, @@ -1384,7 +1399,7 @@ static const char *input_map_renames[][2] = { { nullptr, nullptr }, }; -static const char *builtin_types_renames[][2] = { +const char *ProjectConverter3To4::builtin_types_renames[][2] = { { "PoolByteArray", "PackedByteArray" }, { "PoolColorArray", "PackedColorArray" }, { "PoolIntArray", "PackedInt32Array" }, @@ -1398,7 +1413,7 @@ static const char *builtin_types_renames[][2] = { { nullptr, nullptr }, }; -static const char *shaders_renames[][2] = { +const char *ProjectConverter3To4::shaders_renames[][2] = { { "ALPHA_SCISSOR", "ALPHA_SCISSOR_THRESHOLD" }, { "CAMERA_MATRIX", "INV_VIEW_MATRIX" }, { "INV_CAMERA_MATRIX", "VIEW_MATRIX" }, @@ -1416,7 +1431,7 @@ static const char *shaders_renames[][2] = { { nullptr, nullptr }, }; -static const char *class_renames[][2] = { +const char *ProjectConverter3To4::class_renames[][2] = { // { "BulletPhysicsDirectBodyState", "BulletPhysicsDirectBodyState3D" }, // Class is not visible in ClassDB // { "BulletPhysicsServer", "BulletPhysicsServer3D" }, // Class is not visible in ClassDB // { "GDScriptFunctionState", "Node3D" }, // TODO - not sure to which should be changed @@ -1641,7 +1656,7 @@ static const char *class_renames[][2] = { { nullptr, nullptr }, }; -static const char *color_renames[][2] = { +const char *ProjectConverter3To4::ProjectConverter3To4::color_renames[][2] = { { "aliceblue", "ALICE_BLUE" }, { "antiquewhite", "ANTIQUE_WHITE" }, { "aqua", "AQUA" }, @@ -4350,3 +4365,4 @@ int ProjectConverter3To4::validate_conversion() { } #endif // MODULE_REGEX_ENABLED +#endif // DISABLE_DEPRECATED diff --git a/editor/project_converter_3_to_4.h b/editor/project_converter_3_to_4.h index b3aa52f1e3..6ec2dd188d 100644 --- a/editor/project_converter_3_to_4.h +++ b/editor/project_converter_3_to_4.h @@ -29,6 +29,7 @@ /**************************************************************************/ #ifndef PROJECT_CONVERTER_3_TO_4_H +#ifndef DISABLE_DEPRECATED #define PROJECT_CONVERTER_3_TO_4_H #include "core/io/file_access.h" @@ -41,6 +42,19 @@ class RegEx; class ProjectConverter3To4 { public: class RegExContainer; + static const char *enum_renames[][2]; + static const char *gdscript_function_renames[][2]; + static const char *csharp_function_renames[][2]; + static const char *gdscript_properties_renames[][2]; + static const char *csharp_properties_renames[][2]; + static const char *gdscript_signals_renames[][2]; + static const char *csharp_signals_renames[][2]; + static const char *project_settings_renames[][2]; + static const char *input_map_renames[][2]; + static const char *builtin_types_renames[][2]; + static const char *shaders_renames[][2]; + static const char *class_renames[][2]; + static const char *color_renames[][2]; private: uint64_t maximum_file_size; @@ -97,4 +111,6 @@ public: int convert(); }; +#endif // DISABLE_DEPRECATED + #endif // PROJECT_CONVERTER_3_TO_4_H diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 7f9d4ae253..cd837b3a91 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -563,6 +563,7 @@ [/codeblock] [b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported. [b]Note:[/b] As annotations describe their subject, the [code]@icon[/code] annotation must be placed before the class definition and inheritance. + [b]Note:[/b] Unlike other annotations, the argument of the [code]@icon[/code] annotation must be a string literal (constant expressions are not supported). </description> </annotation> <annotation name="@onready"> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index a8667e9cec..8324cb0fe0 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2437,21 +2437,25 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b GDScriptParser parser; err = parser.parse(source, p_path, false); - if (err) { - return String(); - } - - GDScriptAnalyzer analyzer(&parser); - err = analyzer.resolve_inheritance(); - if (err) { - return String(); - } const GDScriptParser::ClassNode *c = parser.get_tree(); - - if (r_base_type) { - *r_base_type = c->get_datatype().native_type; - } + if (!c) { + return String(); // No class parsed. + } + + /* **WARNING** + * + * This function is written with the goal to be *extremely* error tolerant, as such + * it should meet the following requirements: + * + * - It must not rely on the analyzer (in fact, the analyzer must not be used here), + * because at the time global classes are parsed, the dependencies may not be present + * yet, hence the function will fail (which is unintended). + * - It must not fail even if the parsing fails, because even if the file is broken, + * it should attempt its best to retrieve the inheritance metadata. + * + * Before changing this function, please ask the current maintainer of EditorFileSystem. + */ if (r_icon_path) { if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) { @@ -2460,7 +2464,73 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path(); } } + if (r_base_type) { + const GDScriptParser::ClassNode *subclass = c; + String path = p_path; + GDScriptParser subparser; + while (subclass) { + if (subclass->extends_used) { + if (!subclass->extends_path.is_empty()) { + if (subclass->extends.size() == 0) { + get_global_class_name(subclass->extends_path, r_base_type); + subclass = nullptr; + break; + } else { + Vector<StringName> extend_classes = subclass->extends; + + Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); + if (subfile.is_null()) { + break; + } + String subsource = subfile->get_as_utf8_string(); + + if (subsource.is_empty()) { + break; + } + String subpath = subclass->extends_path; + if (subpath.is_relative_path()) { + subpath = path.get_base_dir().path_join(subpath).simplify_path(); + } + if (OK != subparser.parse(subsource, subpath, false)) { + break; + } + path = subpath; + subclass = subparser.get_tree(); + + while (extend_classes.size() > 0) { + bool found = false; + for (int i = 0; i < subclass->members.size(); i++) { + if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) { + continue; + } + + const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class; + if (inner_class->identifier->name == extend_classes[0]) { + extend_classes.remove_at(0); + found = true; + subclass = inner_class; + break; + } + } + if (!found) { + subclass = nullptr; + break; + } + } + } + } else if (subclass->extends.size() == 1) { + *r_base_type = subclass->extends[0]; + subclass = nullptr; + } else { + break; + } + } else { + *r_base_type = "RefCounted"; + subclass = nullptr; + } + } + } return c->identifier != nullptr ? String(c->identifier->name) : String(); } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index f788236af3..1c2b743909 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1972,17 +1972,40 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { } if (p_return->return_value != nullptr) { - reduce_expression(p_return->return_value); - if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) { - update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type()); - } - if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) { - push_error("A void function cannot return a value.", p_return); - } - if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) { - update_const_expression_builtin_type(p_return->return_value, expected_type, "return"); + bool is_void_function = has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL; + bool is_call = p_return->return_value->type == GDScriptParser::Node::CALL; + if (is_void_function && is_call) { + // Pretend the call is a root expression to allow those that are "void". + reduce_call(static_cast<GDScriptParser::CallNode *>(p_return->return_value), false, true); + } else { + reduce_expression(p_return->return_value); + } + if (is_void_function) { + p_return->void_return = true; + const GDScriptParser::DataType &return_type = p_return->return_value->datatype; + if (is_call && !return_type.is_hard_type()) { + String function_name = parser->current_function->identifier ? parser->current_function->identifier->name.operator String() : String("<anonymous function>"); + String called_function_name = static_cast<GDScriptParser::CallNode *>(p_return->return_value)->function_name.operator String(); +#ifdef DEBUG_ENABLED + parser->push_warning(p_return, GDScriptWarning::UNSAFE_VOID_RETURN, function_name, called_function_name); +#endif + mark_node_unsafe(p_return); + } else if (!is_call) { + push_error("A void function cannot return a value.", p_return); + } + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::NIL; + result.is_constant = true; + } else { + if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type()); + } + if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) { + update_const_expression_builtin_type(p_return->return_value, expected_type, "return"); + } + result = p_return->return_value->get_datatype(); } - result = p_return->return_value->get_datatype(); } else { // Return type is null by default. result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -2464,6 +2487,62 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o p_binary_op->set_datatype(result); } +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED +const char *GDScriptAnalyzer::get_rename_from_map(const char *map[][2], String key) { + for (int index = 0; map[index][0]; index++) { + if (map[index][0] == key) { + return map[index][1]; + } + } + return nullptr; +} + +// Checks if an identifier/function name has been renamed in Godot 4, uses ProjectConverter3To4 for rename map. +// Returns the new name if found, nullptr otherwise. +const char *GDScriptAnalyzer::check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type) { + switch (type) { + case GDScriptParser::Node::IDENTIFIER: { + // Check properties + const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_properties_renames, identifier); + if (result) { + return result; + } + // Check enum values + result = get_rename_from_map(ProjectConverter3To4::enum_renames, identifier); + if (result) { + return result; + } + // Check color constants + result = get_rename_from_map(ProjectConverter3To4::color_renames, identifier); + if (result) { + return result; + } + // Check type names + result = get_rename_from_map(ProjectConverter3To4::class_renames, identifier); + if (result) { + return result; + } + return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier); + } + case GDScriptParser::Node::CALL: { + const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_function_renames, identifier); + if (result) { + return result; + } + // Built-in Types are mistaken for function calls when the built-in type is not found. + // Check built-in types if function rename not found + return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier); + } + // Signal references don't get parsed through the GDScriptAnalyzer. No support for signal rename hints. + default: + // No rename found, return null + return nullptr; + } +} +#endif // DISABLE_DEPRECATED +#endif // TOOLS_ENABLED + void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) { bool all_is_constant = true; HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing. @@ -2881,7 +2960,22 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) { String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string(); +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + String rename_hint = String(); + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type); + if (renamed_function_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", String(renamed_function_name) + "()"); + } + } + push_error(vformat(R"*(Function "%s()" not found in base %s.%s)*", p_call->function_name, base_name, rename_hint), p_call->is_super ? p_call : p_call->callee); +#else // !DISABLE_DEPRECATED push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); +#endif // DISABLE_DEPRECATED +#else + push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); +#endif } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) { push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call); } @@ -3071,7 +3165,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->reduced_value = result; p_identifier->set_datatype(type_from_variant(result, p_identifier)); } else if (base.is_hard_type()) { - push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier); +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + String rename_hint = String(); + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); + if (renamed_identifier_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); + } + } + push_error(vformat(R"(Cannot find constant "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier); +#else // !DISABLE_DEPRECATED + push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier); +#endif // DISABLE_DEPRECATED +#else + push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier); +#endif } } else { switch (base.builtin_type) { @@ -3100,7 +3209,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } } if (base.is_hard_type()) { +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + String rename_hint = String(); + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); + if (renamed_identifier_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); + } + } + push_error(vformat(R"(Cannot find property "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier); +#else // !DISABLE_DEPRECATED + push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); +#endif // DISABLE_DEPRECATED +#else push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); +#endif } } } @@ -3439,7 +3563,22 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident if (GDScriptUtilityFunctions::function_exists(name)) { push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier); } else { +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + String rename_hint = String(); + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); + if (renamed_identifier_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); + } + } + push_error(vformat(R"(Identifier "%s" not declared in the current scope.%s)", name, rename_hint), p_identifier); +#else // !DISABLE_DEPRECATED + push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier); +#endif // DISABLE_DEPRECATED +#else push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier); +#endif } GDScriptParser::DataType dummy; dummy.kind = GDScriptParser::DataType::VARIANT; @@ -4708,11 +4847,6 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) { } Error GDScriptAnalyzer::resolve_inheritance() { - // Apply annotations. - for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) { - resolve_annotation(E); - E->apply(parser, parser->head); - } return resolve_class_inheritance(parser->head, true); } @@ -4746,6 +4880,12 @@ Error GDScriptAnalyzer::analyze() { return err; } + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) { + resolve_annotation(E); + E->apply(parser, parser->head); + } + resolve_interface(); resolve_body(); if (!parser->errors.is_empty()) { diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 613399fb40..b51564fb0a 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -37,6 +37,10 @@ #include "gdscript_cache.h" #include "gdscript_parser.h" +#ifdef TOOLS_ENABLED +#include "editor/project_converter_3_to_4.h" +#endif + class GDScriptAnalyzer { GDScriptParser *parser = nullptr; HashMap<String, Ref<GDScriptParserRef>> depended_parsers; @@ -133,6 +137,13 @@ class GDScriptAnalyzer { bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context); #endif +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + const char *get_rename_from_map(const char *map[][2], String key); + const char *check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type); +#endif // DISABLE_DEPRECATED +#endif // TOOLS_ENABLED + public: Error resolve_inheritance(); Error resolve_interface(); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 373d8a3efd..46cd4b0d55 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1859,7 +1859,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } } - gen->write_return(return_value); + if (return_n->void_return) { + // Always return "null", even if the expression is a call to a void function. + gen->write_return(codegen.add_constant(Variant())); + } else { + gen->write_return(return_value); + } if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index bb08b017c9..713ad3ed17 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -491,7 +491,12 @@ void GDScriptParser::parse_program() { AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); if (annotation != nullptr) { if (annotation->applies_to(AnnotationInfo::SCRIPT)) { - head->annotations.push_back(annotation); + // `@icon` needs to be applied in the parser. See GH-72444. + if (annotation->name == SNAME("@icon")) { + annotation->apply(this, head); + } else { + head->annotations.push_back(annotation); + } } else { annotation_stack.push_back(annotation); // This annotation must appear after script-level annotations @@ -809,7 +814,7 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) { if (previous.type != GDScriptTokenizer::Token::NEWLINE) { push_error(R"(Expected newline after a standalone annotation.)"); } - if (annotation->name == "@export_category" || annotation->name == "@export_group" || annotation->name == "@export_subgroup") { + if (annotation->name == SNAME("@export_category") || annotation->name == SNAME("@export_group") || annotation->name == SNAME("@export_subgroup")) { current_class->add_member_group(annotation); } else { // For potential non-group standalone annotations. @@ -1436,7 +1441,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional. if (valid) { - valid = validate_annotation_argument_count(annotation); + valid = validate_annotation_arguments(annotation); } return valid ? annotation : nullptr; @@ -3551,7 +3556,7 @@ bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const { return (info->target_kind & p_target_kinds) > 0; } -bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annotation) { +bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) { ERR_FAIL_COND_V_MSG(!valid_annotations.has(p_annotation->name), false, vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name)); const MethodInfo &info = valid_annotations[p_annotation->name].info; @@ -3566,6 +3571,27 @@ bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annota return false; } + // `@icon`'s argument needs to be resolved in the parser. See GH-72444. + if (p_annotation->name == SNAME("@icon")) { + ExpressionNode *argument = p_annotation->arguments[0]; + + if (argument->type != Node::LITERAL) { + push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument); + return false; + } + + Variant value = static_cast<LiteralNode *>(argument)->value; + + if (value.get_type() != Variant::STRING) { + push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument); + return false; + } + + p_annotation->resolved_arguments.push_back(value); + } + + // For other annotations, see `GDScriptAnalyzer::resolve_annotation()`. + return true; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index bb4549c15c..07dac25ec5 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -970,6 +970,7 @@ public: struct ReturnNode : public Node { ExpressionNode *return_value = nullptr; + bool void_return = false; ReturnNode() { type = RETURN; @@ -1401,7 +1402,7 @@ private: // Annotations AnnotationNode *parse_annotation(uint32_t p_valid_targets); bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false); - bool validate_annotation_argument_count(AnnotationNode *p_annotation); + bool validate_annotation_arguments(AnnotationNode *p_annotation); void clear_unused_annotations(); bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target); bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target); diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 024fed8517..9436146bed 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -125,6 +125,10 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(4); return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; } break; + case UNSAFE_VOID_RETURN: { + CHECK_SYMBOLS(2); + return "The method '" + symbols[0] + "()' returns 'void' but it's trying to return a call to '" + symbols[1] + "()' that can't be ensured to also be 'void'."; + } break; case DEPRECATED_KEYWORD: { CHECK_SYMBOLS(2); return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; @@ -163,6 +167,9 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(1); return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]); } + case RENAMED_IN_GD4_HINT: { + break; // Renamed identifier hint is taken care of by the GDScriptAnalyzer. No message needed here. + } case WARNING_MAX: break; // Can't happen, but silences warning } @@ -184,6 +191,9 @@ int GDScriptWarning::get_default_value(Code p_code) { PropertyInfo GDScriptWarning::get_property_info(Code p_code) { // Making this a separate function in case a warning needs different PropertyInfo in the future. + if (p_code == Code::RENAMED_IN_GD4_HINT) { + return PropertyInfo(Variant::BOOL, get_settings_path_from_code(p_code)); + } return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error"); } @@ -218,6 +228,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNSAFE_METHOD_ACCESS", "UNSAFE_CAST", "UNSAFE_CALL_ARGUMENT", + "UNSAFE_VOID_RETURN", "DEPRECATED_KEYWORD", "STANDALONE_TERNARY", "ASSERT_ALWAYS_TRUE", @@ -229,6 +240,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "INT_AS_ENUM_WITHOUT_MATCH", "STATIC_CALLED_ON_INSTANCE", "CONFUSABLE_IDENTIFIER", + "RENAMED_IN_GODOT_4_HINT" }; static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names."); diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 7492972c1a..fa2907cdae 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -69,6 +69,7 @@ public: UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes). UNSAFE_CAST, // Cast used in an unknown type. UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument. + UNSAFE_VOID_RETURN, // Function returns void but returned a call to a function that can't be type checked. DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced. STANDALONE_TERNARY, // Return value of ternary expression is discarded. ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true. @@ -80,6 +81,7 @@ public: INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member. STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e"). + RENAMED_IN_GD4_HINT, // A variable or function that could not be found has been renamed in Godot 4 WARNING_MAX, }; diff --git a/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.gd b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.gd new file mode 100644 index 0000000000..df89137f40 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.gd @@ -0,0 +1,20 @@ +func test(): + return_call() + return_nothing() + return_side_effect() + var r = return_side_effect.call() # Untyped call to check return value. + prints(r, typeof(r) == TYPE_NIL) + print("end") + +func side_effect(v): + print("effect") + return v + +func return_call() -> void: + return print("hello") + +func return_nothing() -> void: + return + +func return_side_effect() -> void: + return side_effect("x") diff --git a/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out new file mode 100644 index 0000000000..7c0416371f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out @@ -0,0 +1,10 @@ +GDTEST_OK +>> WARNING +>> Line: 20 +>> UNSAFE_VOID_RETURN +>> The method 'return_side_effect()' returns 'void' but it's trying to return a call to 'side_effect()' that can't be ensured to also be 'void'. +hello +effect +effect +<null> true +end diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd index 0085b3f367..2470fe978e 100644 --- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd @@ -1,5 +1,5 @@ # Error here. Annotations should be used before `class_name`, not after. -class_name HelloWorld +class_name WrongAnnotationPlace @icon("res://path/to/optional/icon.svg") func test(): diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 0b519bd6a3..5950ad33b5 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3065,6 +3065,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p // Ref: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#images const Array &images = p_state->json["images"]; + HashSet<String> used_names; for (int i = 0; i < images.size(); i++) { const Dictionary &d = images[i]; @@ -3092,11 +3093,21 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p int data_size = 0; String image_name; + if (d.has("name")) { + image_name = d["name"]; + image_name = image_name.get_file().get_basename().validate_filename(); + } + if (image_name.is_empty()) { + image_name = itos(i); + } + while (used_names.has(image_name)) { + image_name += "_" + itos(i); + } + used_names.insert(image_name); if (d.has("uri")) { // Handles the first two bullet points from the spec (embedded data, or external file). String uri = d["uri"]; - image_name = uri; if (uri.begins_with("data:")) { // Embedded data using base64. // Validate data MIME types and throw a warning if it's one we don't know/support. @@ -3158,7 +3169,6 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p vformat("glTF: Image index '%d' specifies 'bufferView' but no 'mimeType', which is invalid.", i)); const GLTFBufferViewIndex bvi = d["bufferView"]; - image_name = itos(bvi); ERR_FAIL_INDEX_V(bvi, p_state->buffer_views.size(), ERR_PARAMETER_RANGE_ERROR); @@ -3206,13 +3216,12 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p p_state->source_images.push_back(Ref<Image>()); continue; } + img->set_name(image_name); if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); continue; } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) { - String extracted_image_name = image_name.get_file().get_basename().validate_filename(); - img->set_name(extracted_image_name); if (p_state->base_path.is_empty()) { p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs index 150eb98fc7..350626389b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs @@ -6,13 +6,17 @@ using Godot.NativeInterop; namespace Godot { /// <summary> - /// The Rid type is used to access the unique integer ID of a resource. - /// They are opaque, which means they do not grant access to the associated - /// resource by themselves. They are used by and with the low-level Server - /// classes such as <see cref="RenderingServer"/>. + /// The RID type is used to access a low-level resource by its unique ID. + /// RIDs are opaque, which means they do not grant access to the resource + /// by themselves. They are used by the low-level server classes, such as + /// <see cref="DisplayServer"/>, <see cref="RenderingServer"/>, + /// <see cref="TextServer"/>, etc. + /// + /// A low-level resource may correspond to a high-level <see cref="Resource"/>, + /// such as <see cref="Texture"/> or <see cref="Mesh"/> /// </summary> [StructLayout(LayoutKind.Sequential)] - public readonly struct Rid + public readonly struct Rid : IEquatable<Rid> { private readonly ulong _id; // Default is 0 @@ -28,15 +32,73 @@ namespace Godot => _id = from is Resource res ? res.GetRid()._id : default; /// <summary> - /// Returns the ID of the referenced resource. + /// Returns the ID of the referenced low-level resource. /// </summary> /// <returns>The ID of the referenced resource.</returns> public ulong Id => _id; /// <summary> + /// Returns <see langword="true"/> if the <see cref="Rid"/> is not <c>0</c>. + /// </summary> + /// <returns>Whether or not the ID is valid.</returns> + public bool IsValid => _id != 0; + + /// <summary> + /// Returns <see langword="true"/> if both <see cref="Rid"/>s are equal, + /// which means they both refer to the same low-level resource. + /// </summary> + /// <param name="left">The left RID.</param> + /// <param name="right">The right RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public static bool operator ==(Rid left, Rid right) + { + return left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if the <see cref="Rid"/>s are not equal. + /// </summary> + /// <param name="left">The left RID.</param> + /// <param name="right">The right RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public static bool operator !=(Rid left, Rid right) + { + return !left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if this RID and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the color and the other object are equal.</returns> + public override readonly bool Equals(object obj) + { + return obj is Rid other && Equals(other); + } + + /// <summary> + /// Returns <see langword="true"/> if the RIDs are equal. + /// </summary> + /// <param name="other">The other RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public readonly bool Equals(Rid other) + { + return _id == other.Id; + } + + /// <summary> + /// Serves as the hash function for <see cref="Rid"/>. + /// </summary> + /// <returns>A hash code for this RID.</returns> + public override readonly int GetHashCode() + { + return HashCode.Combine(_id); + } + + /// <summary> /// Converts this <see cref="Rid"/> to a string. /// </summary> /// <returns>A string representation of this Rid.</returns> - public override string ToString() => $"Rid({Id})"; + public override string ToString() => $"RID({Id})"; } } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index ebd0733c55..cc230575c8 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -991,15 +991,6 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3); window_set_position(wpos, p_window); } - - // Don't let the mouse leave the window when resizing to a smaller resolution. - if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { - RECT crect; - GetClientRect(wd.hWnd, &crect); - ClientToScreen(wd.hWnd, (POINT *)&crect.left); - ClientToScreen(wd.hWnd, (POINT *)&crect.right); - ClipCursor(&crect); - } } Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { @@ -1077,15 +1068,6 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window AdjustWindowRectEx(&rc, style, false, exStyle); MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); - // Don't let the mouse leave the window when moved. - if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { - RECT rect; - GetClientRect(wd.hWnd, &rect); - ClientToScreen(wd.hWnd, (POINT *)&rect.left); - ClientToScreen(wd.hWnd, (POINT *)&rect.right); - ClipCursor(&rect); - } - wd.last_pos = p_position; _update_real_mouse_position(p_window); } @@ -1227,15 +1209,6 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo } MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE); - - // Don't let the mouse leave the window when resizing to a smaller resolution. - if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { - RECT crect; - GetClientRect(wd.hWnd, &crect); - ClientToScreen(wd.hWnd, (POINT *)&crect.left); - ClientToScreen(wd.hWnd, (POINT *)&crect.right); - ClipCursor(&crect); - } } Size2i DisplayServerWindows::window_get_size(WindowID p_window) const { @@ -1423,15 +1396,6 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, 0, 0); } } - - // Don't let the mouse leave the window when resizing to a smaller resolution. - if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { - RECT crect; - GetClientRect(wd.hWnd, &crect); - ClientToScreen(wd.hWnd, (POINT *)&crect.left); - ClientToScreen(wd.hWnd, (POINT *)&crect.right); - ClipCursor(&crect); - } } DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const { @@ -3381,6 +3345,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Callable::CallError ce; window.rect_changed_callback.callp(args, 1, ret, ce); } + + // Update cursor clip region after window rect has changed. + if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + RECT crect; + GetClientRect(window.hWnd, &crect); + ClientToScreen(window.hWnd, (POINT *)&crect.left); + ClientToScreen(window.hWnd, (POINT *)&crect.right); + ClipCursor(&crect); + } } // Return here to prevent WM_MOVE and WM_SIZE from being sent diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 71b8fdb539..49c5501e77 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -50,7 +50,7 @@ void Camera2D::_update_scroll() { return; } - if (current) { + if (is_current()) { ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)); Transform2D xform = get_camera_transform(); @@ -241,10 +241,6 @@ void Camera2D::_notification(int p_what) { viewport = get_viewport(); } - if (is_current()) { - viewport->_camera_2d_set(this); - } - canvas = get_canvas(); RID vp = viewport->get_viewport_rid(); @@ -254,6 +250,10 @@ void Camera2D::_notification(int p_what) { add_to_group(group_name); add_to_group(canvas_group_name); + if (enabled && !viewport->get_camera_2d()) { + make_current(); + } + _update_process_callback(); first = true; _update_scroll(); @@ -261,11 +261,7 @@ void Camera2D::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { if (is_current()) { - if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { - viewport->set_canvas_transform(Transform2D()); - clear_current(); - current = true; - } + clear_current(); } remove_from_group(group_name); remove_from_group(canvas_group_name); @@ -397,19 +393,31 @@ void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) { _update_process_callback(); } +void Camera2D::set_enabled(bool p_enabled) { + enabled = p_enabled; + + if (enabled && is_inside_tree() && !viewport->get_camera_2d()) { + make_current(); + } else if (!enabled && is_current()) { + clear_current(); + } +} + +bool Camera2D::is_enabled() const { + return enabled; +} + Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const { return process_callback; } void Camera2D::_make_current(Object *p_which) { if (p_which == this) { - current = true; if (is_inside_tree()) { get_viewport()->_camera_2d_set(this); queue_redraw(); } } else { - current = false; if (is_inside_tree()) { if (get_viewport()->get_camera_2d() == this) { get_viewport()->_camera_2d_set(nullptr); @@ -419,43 +427,32 @@ void Camera2D::_make_current(Object *p_which) { } } -void Camera2D::set_current(bool p_current) { - if (p_current) { - make_current(); - } else { - if (current) { - clear_current(); - } - } -} - void Camera2D::_update_process_internal_for_smoothing() { bool is_not_in_scene_or_editor = !(is_inside_tree() && Engine::get_singleton()->is_editor_hint()); bool is_any_smoothing_valid = position_smoothing_speed > 0 || rotation_smoothing_speed > 0; - bool enabled = is_any_smoothing_valid && is_not_in_scene_or_editor; - set_process_internal(enabled); -} - -bool Camera2D::is_current() const { - return current; + bool enable = is_any_smoothing_valid && is_not_in_scene_or_editor; + set_process_internal(enable); } void Camera2D::make_current() { - if (is_inside_tree()) { - get_tree()->call_group(group_name, "_make_current", this); - } else { - current = true; - } + ERR_FAIL_COND(!enabled || !is_inside_tree()); + get_tree()->call_group(group_name, "_make_current", this); _update_scroll(); } void Camera2D::clear_current() { - if (is_inside_tree()) { - get_tree()->call_group(group_name, "_make_current", (Object *)nullptr); - } else { - current = false; + ERR_FAIL_COND(!is_current()); + if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + viewport->assign_next_enabled_camera_2d(group_name); + } +} + +bool Camera2D::is_current() const { + if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + return viewport->get_camera_2d() == this; } + return false; } void Camera2D::set_limit(Side p_side, int p_limit) { @@ -715,7 +712,10 @@ void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &Camera2D::set_process_callback); ClassDB::bind_method(D_METHOD("get_process_callback"), &Camera2D::get_process_callback); - ClassDB::bind_method(D_METHOD("set_current", "current"), &Camera2D::set_current); + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &Camera2D::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &Camera2D::is_enabled); + + ClassDB::bind_method(D_METHOD("make_current"), &Camera2D::make_current); ClassDB::bind_method(D_METHOD("is_current"), &Camera2D::is_current); ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current); @@ -779,7 +779,7 @@ void Camera2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_rotation"), "set_ignore_rotation", "is_ignoring_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom", PROPERTY_HINT_LINK), "set_zoom", "get_zoom"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback"); diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 304b4ceaa6..7a77266db8 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -64,7 +64,7 @@ protected: Vector2 zoom_scale = Vector2(1, 1); AnchorMode anchor_mode = ANCHOR_MODE_DRAG_CENTER; bool ignore_rotation = true; - bool current = false; + bool enabled = true; real_t position_smoothing_speed = 5.0; bool follow_smoothing_enabled = false; @@ -88,7 +88,6 @@ protected: void _update_scroll(); void _make_current(Object *p_which); - void set_current(bool p_current); void _set_old_smoothing(real_t p_enable); @@ -155,6 +154,9 @@ public: void set_process_callback(Camera2DProcessCallback p_mode); Camera2DProcessCallback get_process_callback() const; + void set_enabled(bool p_enabled); + bool is_enabled() const; + void make_current(); void clear_current(); bool is_current() const; diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index e73b6e7e23..380a684c9b 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -108,6 +108,26 @@ void NavigationAgent2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10,0.01,suffix:s"), "set_time_horizon", "get_time_horizon"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:px/s"), "set_max_speed", "get_max_speed"); +#ifdef DEBUG_ENABLED + ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent2D::set_debug_enabled); + ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationAgent2D::get_debug_enabled); + ClassDB::bind_method(D_METHOD("set_debug_use_custom", "enabled"), &NavigationAgent2D::set_debug_use_custom); + ClassDB::bind_method(D_METHOD("get_debug_use_custom"), &NavigationAgent2D::get_debug_use_custom); + ClassDB::bind_method(D_METHOD("set_debug_path_custom_color", "color"), &NavigationAgent2D::set_debug_path_custom_color); + ClassDB::bind_method(D_METHOD("get_debug_path_custom_color"), &NavigationAgent2D::get_debug_path_custom_color); + ClassDB::bind_method(D_METHOD("set_debug_path_custom_point_size", "point_size"), &NavigationAgent2D::set_debug_path_custom_point_size); + ClassDB::bind_method(D_METHOD("get_debug_path_custom_point_size"), &NavigationAgent2D::get_debug_path_custom_point_size); + ClassDB::bind_method(D_METHOD("set_debug_path_custom_line_width", "line_width"), &NavigationAgent2D::set_debug_path_custom_line_width); + ClassDB::bind_method(D_METHOD("get_debug_path_custom_line_width"), &NavigationAgent2D::get_debug_path_custom_line_width); + + ADD_GROUP("Debug", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_enabled"), "set_debug_enabled", "get_debug_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_use_custom"), "set_debug_use_custom", "get_debug_use_custom"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_path_custom_color"), "set_debug_path_custom_color", "get_debug_path_custom_color"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "debug_path_custom_point_size", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_point_size", "get_debug_path_custom_point_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "debug_path_custom_line_width", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_line_width", "get_debug_path_custom_line_width"); +#endif // DEBUG_ENABLED + ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("target_reached")); ADD_SIGNAL(MethodInfo("waypoint_reached", PropertyInfo(Variant::DICTIONARY, "details"))); @@ -123,6 +143,12 @@ void NavigationAgent2D::_notification(int p_what) { // cannot use READY as ready does not get called if Node is readded to SceneTree set_agent_parent(get_parent()); set_physics_process_internal(true); + +#ifdef DEBUG_ENABLED + if (NavigationServer2D::get_singleton()->get_debug_enabled()) { + debug_path_dirty = true; + } +#endif // DEBUG_ENABLED } break; case NOTIFICATION_PARENTED: { @@ -165,6 +191,12 @@ void NavigationAgent2D::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { agent_parent = nullptr; set_physics_process_internal(false); + +#ifdef DEBUG_ENABLED + if (debug_path_instance.is_valid()) { + RenderingServer::get_singleton()->canvas_item_set_visible(debug_path_instance, false); + } +#endif // DEBUG_ENABLED } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -176,6 +208,12 @@ void NavigationAgent2D::_notification(int p_what) { } _check_distance_to_target(); } + +#ifdef DEBUG_ENABLED + if (debug_path_dirty) { + _update_debug_path(); + } +#endif // DEBUG_ENABLED } break; } } @@ -194,12 +232,25 @@ NavigationAgent2D::NavigationAgent2D() { navigation_result = Ref<NavigationPathQueryResult2D>(); navigation_result.instantiate(); + +#ifdef DEBUG_ENABLED + NavigationServer2D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent2D::_navigation_debug_changed)); +#endif // DEBUG_ENABLED } NavigationAgent2D::~NavigationAgent2D() { ERR_FAIL_NULL(NavigationServer2D::get_singleton()); NavigationServer2D::get_singleton()->free(agent); agent = RID(); // Pointless + +#ifdef DEBUG_ENABLED + NavigationServer2D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent2D::_navigation_debug_changed)); + + ERR_FAIL_NULL(RenderingServer::get_singleton()); + if (debug_path_instance.is_valid()) { + RenderingServer::get_singleton()->free(debug_path_instance); + } +#endif // DEBUG_ENABLED } void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) { @@ -463,6 +514,9 @@ void NavigationAgent2D::update_navigation() { } NavigationServer2D::get_singleton()->query_path(navigation_query, navigation_result); +#ifdef DEBUG_ENABLED + debug_path_dirty = true; +#endif // DEBUG_ENABLED navigation_finished = false; navigation_path_index = 0; emit_signal(SNAME("path_changed")); @@ -549,3 +603,111 @@ void NavigationAgent2D::_check_distance_to_target() { } } } + +////////DEBUG//////////////////////////////////////////////////////////// + +#ifdef DEBUG_ENABLED +void NavigationAgent2D::set_debug_enabled(bool p_enabled) { + debug_enabled = p_enabled; + debug_path_dirty = true; +} + +bool NavigationAgent2D::get_debug_enabled() const { + return debug_enabled; +} + +void NavigationAgent2D::set_debug_use_custom(bool p_enabled) { + debug_use_custom = p_enabled; + debug_path_dirty = true; +} + +bool NavigationAgent2D::get_debug_use_custom() const { + return debug_use_custom; +} + +void NavigationAgent2D::set_debug_path_custom_color(Color p_color) { + debug_path_custom_color = p_color; + debug_path_dirty = true; +} + +Color NavigationAgent2D::get_debug_path_custom_color() const { + return debug_path_custom_color; +} + +void NavigationAgent2D::set_debug_path_custom_point_size(float p_point_size) { + debug_path_custom_point_size = MAX(0.1, p_point_size); + debug_path_dirty = true; +} + +float NavigationAgent2D::get_debug_path_custom_point_size() const { + return debug_path_custom_point_size; +} + +void NavigationAgent2D::set_debug_path_custom_line_width(float p_line_width) { + debug_path_custom_line_width = p_line_width; + debug_path_dirty = true; +} + +float NavigationAgent2D::get_debug_path_custom_line_width() const { + return debug_path_custom_line_width; +} + +void NavigationAgent2D::_navigation_debug_changed() { + debug_path_dirty = true; +} + +void NavigationAgent2D::_update_debug_path() { + if (!debug_path_dirty) { + return; + } + debug_path_dirty = false; + + if (!debug_path_instance.is_valid()) { + debug_path_instance = RenderingServer::get_singleton()->canvas_item_create(); + } + + RenderingServer::get_singleton()->canvas_item_clear(debug_path_instance); + + if (!(debug_enabled && NavigationServer2D::get_singleton()->get_debug_navigation_enable_agent_paths())) { + return; + } + + if (!(agent_parent && agent_parent->is_inside_tree())) { + return; + } + + RenderingServer::get_singleton()->canvas_item_set_parent(debug_path_instance, agent_parent->get_canvas()); + RenderingServer::get_singleton()->canvas_item_set_visible(debug_path_instance, agent_parent->is_visible_in_tree()); + + const Vector<Vector2> &navigation_path = navigation_result->get_path(); + + if (navigation_path.size() <= 1) { + return; + } + + Color debug_path_color = NavigationServer2D::get_singleton()->get_debug_navigation_agent_path_color(); + if (debug_use_custom) { + debug_path_color = debug_path_custom_color; + } + + Vector<Color> debug_path_colors; + debug_path_colors.resize(navigation_path.size()); + debug_path_colors.fill(debug_path_color); + + RenderingServer::get_singleton()->canvas_item_add_polyline(debug_path_instance, navigation_path, debug_path_colors, debug_path_custom_line_width, false); + + float point_size = NavigationServer2D::get_singleton()->get_debug_navigation_agent_path_point_size(); + float half_point_size = point_size * 0.5; + + if (debug_use_custom) { + point_size = debug_path_custom_point_size; + half_point_size = debug_path_custom_point_size * 0.5; + } + + for (int i = 0; i < navigation_path.size(); i++) { + const Vector2 &vert = navigation_path[i]; + Rect2 path_point_rect = Rect2(vert.x - half_point_size, vert.y - half_point_size, point_size, point_size); + RenderingServer::get_singleton()->canvas_item_add_rect(debug_path_instance, path_point_rect, debug_path_color); + } +} +#endif // DEBUG_ENABLED diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 9787bb1bdb..8f4a373327 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -74,6 +74,20 @@ class NavigationAgent2D : public Node { // No initialized on purpose uint32_t update_frame_id = 0; +#ifdef DEBUG_ENABLED + bool debug_enabled = false; + bool debug_path_dirty = true; + RID debug_path_instance; + float debug_path_custom_point_size = 4.0; + float debug_path_custom_line_width = 1.0; + bool debug_use_custom = false; + Color debug_path_custom_color = Color(1.0, 1.0, 1.0, 1.0); + +private: + void _navigation_debug_changed(); + void _update_debug_path(); +#endif // DEBUG_ENABLED + protected: static void _bind_methods(); void _notification(int p_what); @@ -169,6 +183,23 @@ public: PackedStringArray get_configuration_warnings() const override; +#ifdef DEBUG_ENABLED + void set_debug_enabled(bool p_enabled); + bool get_debug_enabled() const; + + void set_debug_use_custom(bool p_enabled); + bool get_debug_use_custom() const; + + void set_debug_path_custom_color(Color p_color); + Color get_debug_path_custom_color() const; + + void set_debug_path_custom_point_size(float p_point_size); + float get_debug_path_custom_point_size() const; + + void set_debug_path_custom_line_width(float p_line_width); + float get_debug_path_custom_line_width() const; +#endif // DEBUG_ENABLED + private: void update_navigation(); void _request_repath(); diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index fe7f6837f0..ba5ff02862 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -81,11 +81,6 @@ bool BoneAttachment3D::_get(const StringName &p_path, Variant &r_ret) const { } void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::BOOL, "override_pose", PROPERTY_HINT_NONE, "")); - if (override_pose) { - p_list->push_back(PropertyInfo(Variant::INT, "override_mode", PROPERTY_HINT_ENUM, "Global Pose Override,Local Pose Override,Custom Pose")); - } - p_list->push_back(PropertyInfo(Variant::BOOL, "use_external_skeleton", PROPERTY_HINT_NONE, "")); if (use_external_skeleton) { p_list->push_back(PropertyInfo(Variant::NODE_PATH, "external_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D")); diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 4aa6e61ec5..5db8611d72 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -120,6 +120,23 @@ void NavigationAgent3D::_bind_methods() { ADD_SIGNAL(MethodInfo("link_reached", PropertyInfo(Variant::DICTIONARY, "details"))); ADD_SIGNAL(MethodInfo("navigation_finished")); ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR3, "safe_velocity"))); + +#ifdef DEBUG_ENABLED + ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent3D::set_debug_enabled); + ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationAgent3D::get_debug_enabled); + ClassDB::bind_method(D_METHOD("set_debug_use_custom", "enabled"), &NavigationAgent3D::set_debug_use_custom); + ClassDB::bind_method(D_METHOD("get_debug_use_custom"), &NavigationAgent3D::get_debug_use_custom); + ClassDB::bind_method(D_METHOD("set_debug_path_custom_color", "color"), &NavigationAgent3D::set_debug_path_custom_color); + ClassDB::bind_method(D_METHOD("get_debug_path_custom_color"), &NavigationAgent3D::get_debug_path_custom_color); + ClassDB::bind_method(D_METHOD("set_debug_path_custom_point_size", "point_size"), &NavigationAgent3D::set_debug_path_custom_point_size); + ClassDB::bind_method(D_METHOD("get_debug_path_custom_point_size"), &NavigationAgent3D::get_debug_path_custom_point_size); + + ADD_GROUP("Debug", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_enabled"), "set_debug_enabled", "get_debug_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_use_custom"), "set_debug_use_custom", "get_debug_use_custom"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_path_custom_color"), "set_debug_path_custom_color", "get_debug_path_custom_color"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "debug_path_custom_point_size", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_point_size", "get_debug_path_custom_point_size"); +#endif // DEBUG_ENABLED } void NavigationAgent3D::_notification(int p_what) { @@ -129,6 +146,12 @@ void NavigationAgent3D::_notification(int p_what) { // cannot use READY as ready does not get called if Node is readded to SceneTree set_agent_parent(get_parent()); set_physics_process_internal(true); + +#ifdef DEBUG_ENABLED + if (NavigationServer3D::get_singleton()->get_debug_enabled()) { + debug_path_dirty = true; + } +#endif // DEBUG_ENABLED } break; case NOTIFICATION_PARENTED: { @@ -151,6 +174,12 @@ void NavigationAgent3D::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { set_agent_parent(nullptr); set_physics_process_internal(false); + +#ifdef DEBUG_ENABLED + if (debug_path_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_path_instance, false); + } +#endif // DEBUG_ENABLED } break; case NOTIFICATION_PAUSED: { @@ -182,6 +211,11 @@ void NavigationAgent3D::_notification(int p_what) { } _check_distance_to_target(); } +#ifdef DEBUG_ENABLED + if (debug_path_dirty) { + _update_debug_path(); + } +#endif // DEBUG_ENABLED } break; } } @@ -201,12 +235,28 @@ NavigationAgent3D::NavigationAgent3D() { navigation_result = Ref<NavigationPathQueryResult3D>(); navigation_result.instantiate(); + +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent3D::_navigation_debug_changed)); +#endif // DEBUG_ENABLED } NavigationAgent3D::~NavigationAgent3D() { ERR_FAIL_NULL(NavigationServer3D::get_singleton()); NavigationServer3D::get_singleton()->free(agent); agent = RID(); // Pointless + +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent3D::_navigation_debug_changed)); + + ERR_FAIL_NULL(RenderingServer::get_singleton()); + if (debug_path_instance.is_valid()) { + RenderingServer::get_singleton()->free(debug_path_instance); + } + if (debug_path_mesh.is_valid()) { + RenderingServer::get_singleton()->free(debug_path_mesh->get_rid()); + } +#endif // DEBUG_ENABLED } void NavigationAgent3D::set_avoidance_enabled(bool p_enabled) { @@ -480,6 +530,9 @@ void NavigationAgent3D::update_navigation() { } NavigationServer3D::get_singleton()->query_path(navigation_query, navigation_result); +#ifdef DEBUG_ENABLED + debug_path_dirty = true; +#endif // DEBUG_ENABLED navigation_finished = false; navigation_path_index = 0; emit_signal(SNAME("path_changed")); @@ -566,3 +619,130 @@ void NavigationAgent3D::_check_distance_to_target() { } } } + +////////DEBUG//////////////////////////////////////////////////////////// + +#ifdef DEBUG_ENABLED +void NavigationAgent3D::set_debug_enabled(bool p_enabled) { + debug_enabled = p_enabled; + debug_path_dirty = true; +} + +bool NavigationAgent3D::get_debug_enabled() const { + return debug_enabled; +} + +void NavigationAgent3D::set_debug_use_custom(bool p_enabled) { + debug_use_custom = p_enabled; + debug_path_dirty = true; +} + +bool NavigationAgent3D::get_debug_use_custom() const { + return debug_use_custom; +} + +void NavigationAgent3D::set_debug_path_custom_color(Color p_color) { + debug_path_custom_color = p_color; + debug_path_dirty = true; +} + +Color NavigationAgent3D::get_debug_path_custom_color() const { + return debug_path_custom_color; +} + +void NavigationAgent3D::set_debug_path_custom_point_size(float p_point_size) { + debug_path_custom_point_size = p_point_size; + debug_path_dirty = true; +} + +float NavigationAgent3D::get_debug_path_custom_point_size() const { + return debug_path_custom_point_size; +} + +void NavigationAgent3D::_navigation_debug_changed() { + debug_path_dirty = true; +} + +void NavigationAgent3D::_update_debug_path() { + if (!debug_path_dirty) { + return; + } + debug_path_dirty = false; + + if (!debug_path_instance.is_valid()) { + debug_path_instance = RenderingServer::get_singleton()->instance_create(); + } + + if (!debug_path_mesh.is_valid()) { + debug_path_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + debug_path_mesh->clear_surfaces(); + + if (!(debug_enabled && NavigationServer3D::get_singleton()->get_debug_navigation_enable_agent_paths())) { + return; + } + + if (!(agent_parent && agent_parent->is_inside_tree())) { + return; + } + + const Vector<Vector3> &navigation_path = navigation_result->get_path(); + + if (navigation_path.size() <= 1) { + return; + } + + Vector<Vector3> debug_path_lines_vertex_array; + + for (int i = 0; i < navigation_path.size() - 1; i++) { + debug_path_lines_vertex_array.push_back(navigation_path[i]); + debug_path_lines_vertex_array.push_back(navigation_path[i + 1]); + } + + Array debug_path_lines_mesh_array; + debug_path_lines_mesh_array.resize(Mesh::ARRAY_MAX); + debug_path_lines_mesh_array[Mesh::ARRAY_VERTEX] = debug_path_lines_vertex_array; + + debug_path_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, debug_path_lines_mesh_array); + + Ref<StandardMaterial3D> debug_agent_path_line_material = NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_line_material(); + if (debug_use_custom) { + if (!debug_agent_path_line_custom_material.is_valid()) { + debug_agent_path_line_custom_material = debug_agent_path_line_material->duplicate(); + } + debug_agent_path_line_custom_material->set_albedo(debug_path_custom_color); + debug_path_mesh->surface_set_material(0, debug_agent_path_line_custom_material); + } else { + debug_path_mesh->surface_set_material(0, debug_agent_path_line_material); + } + + Vector<Vector3> debug_path_points_vertex_array; + + for (int i = 0; i < navigation_path.size(); i++) { + debug_path_points_vertex_array.push_back(navigation_path[i]); + } + + Array debug_path_points_mesh_array; + debug_path_points_mesh_array.resize(Mesh::ARRAY_MAX); + debug_path_points_mesh_array[Mesh::ARRAY_VERTEX] = debug_path_lines_vertex_array; + + debug_path_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, debug_path_points_mesh_array); + + Ref<StandardMaterial3D> debug_agent_path_point_material = NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_point_material(); + if (debug_use_custom) { + if (!debug_agent_path_point_custom_material.is_valid()) { + debug_agent_path_point_custom_material = debug_agent_path_point_material->duplicate(); + } + debug_agent_path_point_custom_material->set_albedo(debug_path_custom_color); + debug_agent_path_point_custom_material->set_point_size(debug_path_custom_point_size); + debug_path_mesh->surface_set_material(1, debug_agent_path_point_custom_material); + } else { + debug_path_mesh->surface_set_material(1, debug_agent_path_point_material); + } + + RS::get_singleton()->instance_set_base(debug_path_instance, debug_path_mesh->get_rid()); + RS::get_singleton()->instance_set_scenario(debug_path_instance, agent_parent->get_world_3d()->get_scenario()); + RS::get_singleton()->instance_set_visible(debug_path_instance, agent_parent->is_visible_in_tree()); +} +#endif // DEBUG_ENABLED diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 12f83ce6a8..98bf395d7c 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -76,6 +76,22 @@ class NavigationAgent3D : public Node { // No initialized on purpose uint32_t update_frame_id = 0; +#ifdef DEBUG_ENABLED + bool debug_enabled = false; + bool debug_path_dirty = true; + RID debug_path_instance; + Ref<ArrayMesh> debug_path_mesh; + float debug_path_custom_point_size = 4.0; + bool debug_use_custom = false; + Color debug_path_custom_color = Color(1.0, 1.0, 1.0, 1.0); + Ref<StandardMaterial3D> debug_agent_path_line_custom_material; + Ref<StandardMaterial3D> debug_agent_path_point_custom_material; + +private: + void _navigation_debug_changed(); + void _update_debug_path(); +#endif // DEBUG_ENABLED + protected: static void _bind_methods(); void _notification(int p_what); @@ -181,6 +197,20 @@ public: PackedStringArray get_configuration_warnings() const override; +#ifdef DEBUG_ENABLED + void set_debug_enabled(bool p_enabled); + bool get_debug_enabled() const; + + void set_debug_use_custom(bool p_enabled); + bool get_debug_use_custom() const; + + void set_debug_path_custom_color(Color p_color); + Color get_debug_path_custom_color() const; + + void set_debug_path_custom_point_size(float p_point_size); + float get_debug_path_custom_point_size() const; +#endif // DEBUG_ENABLED + private: void update_navigation(); void _request_repath(); diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index a2028b8de8..d28a6fcc04 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -30,12 +30,20 @@ #include "animation_blend_space_1d.h" +#include "animation_blend_tree.h" + void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const { r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position)); + r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const { - return 0; + if (p_parameter == closest) { + return -1; + } else { + return 0; + } } Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) { @@ -77,6 +85,9 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label); ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label); + ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &AnimationNodeBlendSpace1D::set_blend_mode); + ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace1D::get_blend_mode); + ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace1D::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync); @@ -91,7 +102,12 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync"); + + BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED); + BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE); + BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY); } void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) { @@ -214,6 +230,14 @@ String AnimationNodeBlendSpace1D::get_value_label() const { return value_label; } +void AnimationNodeBlendSpace1D::set_blend_mode(BlendMode p_blend_mode) { + blend_mode = p_blend_mode; +} + +AnimationNodeBlendSpace1D::BlendMode AnimationNodeBlendSpace1D::get_blend_mode() const { + return blend_mode; +} + void AnimationNodeBlendSpace1D::set_use_sync(bool p_sync) { sync = p_sync; } @@ -241,79 +265,125 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_is_ } double blend_pos = get_parameter(blend_position); + int cur_closest = get_parameter(closest); + double cur_length_internal = get_parameter(length_internal); + double max_time_remaining = 0.0; - float weights[MAX_BLEND_POINTS] = {}; + if (blend_mode == BLEND_MODE_INTERPOLATED) { + float weights[MAX_BLEND_POINTS] = {}; + + int point_lower = -1; + float pos_lower = 0.0; + int point_higher = -1; + float pos_higher = 0.0; + + // find the closest two points to blend between + for (int i = 0; i < blend_points_used; i++) { + float pos = blend_points[i].position; + + if (pos <= blend_pos) { + if (point_lower == -1) { + point_lower = i; + pos_lower = pos; + } else if ((blend_pos - pos) < (blend_pos - pos_lower)) { + point_lower = i; + pos_lower = pos; + } + } else { + if (point_higher == -1) { + point_higher = i; + pos_higher = pos; + } else if ((pos - blend_pos) < (pos_higher - blend_pos)) { + point_higher = i; + pos_higher = pos; + } + } + } - int point_lower = -1; - float pos_lower = 0.0; - int point_higher = -1; - float pos_higher = 0.0; + // fill in weights - // find the closest two points to blend between - for (int i = 0; i < blend_points_used; i++) { - float pos = blend_points[i].position; - - if (pos <= blend_pos) { - if (point_lower == -1) { - point_lower = i; - pos_lower = pos; - } else if ((blend_pos - pos) < (blend_pos - pos_lower)) { - point_lower = i; - pos_lower = pos; - } + if (point_lower == -1 && point_higher != -1) { + // we are on the left side, no other point to the left + // we just play the next point. + + weights[point_higher] = 1.0; + } else if (point_higher == -1) { + // we are on the right side, no other point to the right + // we just play the previous point + + weights[point_lower] = 1.0; } else { - if (point_higher == -1) { - point_higher = i; - pos_higher = pos; - } else if ((pos - blend_pos) < (pos_higher - blend_pos)) { - point_higher = i; - pos_higher = pos; - } - } - } + // we are between two points. + // figure out weights, then blend the animations - // fill in weights + float distance_between_points = pos_higher - pos_lower; - if (point_lower == -1 && point_higher != -1) { - // we are on the left side, no other point to the left - // we just play the next point. + float current_pos_inbetween = blend_pos - pos_lower; - weights[point_higher] = 1.0; - } else if (point_higher == -1) { - // we are on the right side, no other point to the right - // we just play the previous point + float blend_percentage = current_pos_inbetween / distance_between_points; - weights[point_lower] = 1.0; - } else { - // we are between two points. - // figure out weights, then blend the animations + float blend_lower = 1.0 - blend_percentage; + float blend_higher = blend_percentage; - float distance_between_points = pos_higher - pos_lower; + weights[point_lower] = blend_lower; + weights[point_higher] = blend_higher; + } - float current_pos_inbetween = blend_pos - pos_lower; + // actually blend the animations now - float blend_percentage = current_pos_inbetween / distance_between_points; + for (int i = 0; i < blend_points_used; i++) { + if (i == point_lower || i == point_higher) { + double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true); + max_time_remaining = MAX(max_time_remaining, remaining); + } else if (sync) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); + } + } + } else { + int new_closest = -1; + double new_closest_dist = 1e20; + + for (int i = 0; i < blend_points_used; i++) { + double d = abs(blend_points[i].position - blend_pos); + if (d < new_closest_dist) { + new_closest = i; + new_closest_dist = d; + } + } - float blend_lower = 1.0 - blend_percentage; - float blend_higher = blend_percentage; + if (new_closest != cur_closest && new_closest != -1) { + double from = 0.0; + if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) { + //for ping-pong loop + Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node); + Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node); + if (!na_c.is_null() && !na_n.is_null()) { + na_n->set_backward(na_c->is_backward()); + } + //see how much animation remains + from = cur_length_internal - blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, false, p_is_external_seeking, 0.0, FILTER_IGNORE, true); + } - weights[point_lower] = blend_lower; - weights[point_higher] = blend_higher; - } + max_time_remaining = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true); + cur_length_internal = from + max_time_remaining; - // actually blend the animations now + cur_closest = new_closest; - double max_time_remaining = 0.0; + } else { + max_time_remaining = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); + } - for (int i = 0; i < blend_points_used; i++) { - if (i == point_lower || i == point_higher) { - double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true); - max_time_remaining = MAX(max_time_remaining, remaining); - } else if (sync) { - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); + if (sync) { + for (int i = 0; i < blend_points_used; i++) { + if (i != cur_closest) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); + } + } } } + set_parameter(this->closest, cur_closest); + set_parameter(this->length_internal, cur_length_internal); return max_time_remaining; } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index af93783c0d..a1e9a7a764 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -36,6 +36,14 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode); +public: + enum BlendMode { + BLEND_MODE_INTERPOLATED, + BLEND_MODE_DISCRETE, + BLEND_MODE_DISCRETE_CARRY, + }; + +protected: enum { MAX_BLEND_POINTS = 64 }; @@ -61,6 +69,10 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { void _tree_changed(); StringName blend_position = "blend_position"; + StringName closest = "closest"; + StringName length_internal = "length_internal"; + + BlendMode blend_mode = BLEND_MODE_INTERPOLATED; protected: bool sync = false; @@ -95,6 +107,9 @@ public: void set_value_label(const String &p_label); String get_value_label() const; + void set_blend_mode(BlendMode p_blend_mode); + BlendMode get_blend_mode() const; + void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -107,4 +122,6 @@ public: ~AnimationNodeBlendSpace1D(); }; +VARIANT_ENUM_CAST(AnimationNodeBlendSpace1D::BlendMode) + #endif // ANIMATION_BLEND_SPACE_1D_H diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 1c1f94c986..fa72bbc593 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -1861,6 +1861,8 @@ void AnimationTree::_setup_animation_player() { return; } + cache_valid = false; + AnimationPlayer *new_player = nullptr; if (!animation_player.is_empty()) { new_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player)); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index f7c056316d..6f5e2cf058 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -692,6 +692,12 @@ Transform2D Control::get_transform() const { return xform; } +void Control::_toplevel_changed_on_parent() { + // Update root control status. + _notification(NOTIFICATION_EXIT_CANVAS); + _notification(NOTIFICATION_ENTER_CANVAS); +} + /// Anchors and offsets. void Control::_set_anchor(Side p_side, real_t p_anchor) { diff --git a/scene/gui/control.h b/scene/gui/control.h index a93a88e5b4..5977f4dbea 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -292,6 +292,9 @@ private: void _update_minimum_size(); void _size_changed(); + void _toplevel_changed() override{}; // Controls don't need to do anything, only other CanvasItems. + void _toplevel_changed_on_parent() override; + void _clear_size_warning(); // Input events. diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 35176f0edd..cde3503bdf 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -400,11 +400,28 @@ void CanvasItem::set_as_top_level(bool p_top_level) { _exit_canvas(); top_level = p_top_level; + _toplevel_changed(); _enter_canvas(); _notify_transform(); } +void CanvasItem::_toplevel_changed() { + // Inform children that toplevel status has changed on a parent. + int childs = get_child_count(); + for (int i = 0; i < childs; i++) { + CanvasItem *child = Object::cast_to<CanvasItem>(get_child(i)); + if (child) { + child->_toplevel_changed_on_parent(); + } + } +} + +void CanvasItem::_toplevel_changed_on_parent() { + // Inform children that toplevel status has changed on a parent. + _toplevel_changed(); +} + bool CanvasItem::is_set_as_top_level() const { return top_level; } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 1c84ea338a..1ddfaa288c 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -125,6 +125,9 @@ private: void _propagate_visibility_changed(bool p_parent_visible_in_tree); void _handle_visibility_change(bool p_visible); + virtual void _toplevel_changed(); + virtual void _toplevel_changed_on_parent(); + void _redraw_callback(); void _enter_canvas(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 48cff5aa8e..b6bcbe04a3 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1045,6 +1045,25 @@ Transform2D Viewport::get_final_transform() const { return _get_input_pre_xform().affine_inverse() * stretch_transform * global_canvas_transform; } +void Viewport::assign_next_enabled_camera_2d(const StringName &p_camera_group) { + List<Node *> camera_list; + get_tree()->get_nodes_in_group(p_camera_group, &camera_list); + + Camera2D *new_camera = nullptr; + for (const Node *E : camera_list) { + const Camera2D *cam = Object::cast_to<Camera2D>(E); + if (cam->is_enabled()) { + new_camera = const_cast<Camera2D *>(cam); + break; + } + } + + _camera_2d_set(new_camera); + if (!camera_2d) { + set_canvas_transform(Transform2D()); + } +} + void Viewport::_update_canvas_items(Node *p_node) { if (p_node != this) { Window *w = Object::cast_to<Window>(p_node); @@ -1110,14 +1129,11 @@ Viewport::PositionalShadowAtlasQuadrantSubdiv Viewport::get_positional_shadow_at } Transform2D Viewport::_get_input_pre_xform() const { - Transform2D pre_xf; - - if (to_screen_rect.size.x != 0 && to_screen_rect.size.y != 0) { - pre_xf.columns[2] = -to_screen_rect.position; - pre_xf.scale(Vector2(size) / to_screen_rect.size); + const Window *this_window = Object::cast_to<Window>(this); + if (this_window) { + return this_window->window_transform.affine_inverse(); } - - return pre_xf; + return Transform2D(); } Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index d5d5201e9a..e081013fde 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -511,6 +511,7 @@ public: Transform2D get_global_canvas_transform() const; Transform2D get_final_transform() const; + void assign_next_enabled_camera_2d(const StringName &p_camera_group); void gui_set_root_order_dirty(); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 869d12b4df..b1f2bc65dc 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -907,6 +907,7 @@ void Window::_update_viewport_size() { Rect2i attach_to_screen_rect(Point2i(), size); Transform2D stretch_transform_new; float font_oversampling = 1.0; + window_transform = Transform2D(); if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) { font_oversampling = content_scale_factor; @@ -993,11 +994,18 @@ void Window::_update_viewport_size() { Size2 scale = Vector2(screen_size) / Vector2(final_size_override); stretch_transform_new.scale(scale); + window_transform.translate_local(margin); } break; case CONTENT_SCALE_MODE_VIEWPORT: { final_size = (viewport_size / content_scale_factor).floor(); attach_to_screen_rect = Rect2(margin, screen_size); + window_transform.translate_local(margin); + if (final_size.x != 0 && final_size.y != 0) { + Transform2D scale_transform; + scale_transform.scale(Vector2(attach_to_screen_rect.size) / Vector2(final_size)); + window_transform *= scale_transform; + } } break; } } @@ -2127,13 +2135,13 @@ Transform2D Window::get_popup_base_transform() const { if (is_embedding_subwindows()) { return Transform2D(); } - Transform2D window_transform; - window_transform.set_origin(get_position()); - window_transform *= Viewport::get_screen_transform(); + Transform2D popup_base_transform; + popup_base_transform.set_origin(get_position()); + popup_base_transform *= Viewport::get_screen_transform(); if (_get_embedder()) { - return _get_embedder()->get_popup_base_transform() * window_transform; + return _get_embedder()->get_popup_base_transform() * popup_base_transform; } - return window_transform; + return popup_base_transform; } void Window::_bind_methods() { diff --git a/scene/main/window.h b/scene/main/window.h index 182caf5f0c..1730de0b33 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -173,6 +173,8 @@ private: Viewport *embedder = nullptr; + Transform2D window_transform; + friend class Viewport; //friend back, can call the methods below void _window_input(const Ref<InputEvent> &p_ev); diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 2a70139bcb..c7304da358 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -43,6 +43,14 @@ RID World2D::get_canvas() const { } RID World2D::get_space() const { + if (space.is_null()) { + space = PhysicsServer2D::get_singleton()->space_create(); + PhysicsServer2D::get_singleton()->space_set_active(space, true); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_GET("physics/2d/default_gravity")); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_GET("physics/2d/default_gravity_vector")); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_GET("physics/2d/default_linear_damp")); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_GET("physics/2d/default_angular_damp")); + } return space; } @@ -71,19 +79,11 @@ void World2D::_bind_methods() { } PhysicsDirectSpaceState2D *World2D::get_direct_space_state() { - return PhysicsServer2D::get_singleton()->space_get_direct_state(space); + return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space()); } World2D::World2D() { canvas = RenderingServer::get_singleton()->canvas_create(); - - // Create and configure space2D to be more friendly with pixels than meters - space = PhysicsServer2D::get_singleton()->space_create(); - PhysicsServer2D::get_singleton()->space_set_active(space, true); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF_BASIC("physics/2d/default_gravity", 980.0)); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/2d/default_gravity_vector", Vector2(0, 1))); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 0.1)); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 1.0)); } World2D::~World2D() { @@ -91,7 +91,9 @@ World2D::~World2D() { ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); ERR_FAIL_NULL(NavigationServer2D::get_singleton()); RenderingServer::get_singleton()->free(canvas); - PhysicsServer2D::get_singleton()->free(space); + if (space.is_valid()) { + PhysicsServer2D::get_singleton()->free(space); + } if (navigation_map.is_valid()) { NavigationServer2D::get_singleton()->free(navigation_map); } diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index f02dddd2fe..0b3b9df7dc 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -43,7 +43,7 @@ class World2D : public Resource { GDCLASS(World2D, Resource); RID canvas; - RID space; + mutable RID space; mutable RID navigation_map; HashSet<Viewport *> viewports; diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index cc4d261c0d..82c056d5ee 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -51,6 +51,14 @@ void World3D::_remove_camera(Camera3D *p_camera) { } RID World3D::get_space() const { + if (space.is_null()) { + space = PhysicsServer3D::get_singleton()->space_create(); + PhysicsServer3D::get_singleton()->space_set_active(space, true); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_GET("physics/3d/default_gravity")); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_GET("physics/3d/default_gravity_vector")); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_GET("physics/3d/default_linear_damp")); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_GET("physics/3d/default_angular_damp")); + } return space; } @@ -121,7 +129,7 @@ Ref<CameraAttributes> World3D::get_camera_attributes() const { } PhysicsDirectSpaceState3D *World3D::get_direct_space_state() { - return PhysicsServer3D::get_singleton()->space_get_direct_state(space); + return PhysicsServer3D::get_singleton()->space_get_direct_state(get_space()); } void World3D::_bind_methods() { @@ -145,22 +153,18 @@ void World3D::_bind_methods() { } World3D::World3D() { - space = PhysicsServer3D::get_singleton()->space_create(); scenario = RenderingServer::get_singleton()->scenario_create(); - - PhysicsServer3D::get_singleton()->space_set_active(space, true); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_DEF_BASIC("physics/3d/default_gravity", 9.8)); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1)); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1)); } World3D::~World3D() { - ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); ERR_FAIL_NULL(RenderingServer::get_singleton()); + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); ERR_FAIL_NULL(NavigationServer3D::get_singleton()); - PhysicsServer3D::get_singleton()->free(space); + RenderingServer::get_singleton()->free(scenario); + if (space.is_valid()) { + PhysicsServer3D::get_singleton()->free(space); + } if (navigation_map.is_valid()) { NavigationServer3D::get_singleton()->free(navigation_map); } diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index ad17daf466..518fff64e2 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -45,8 +45,8 @@ class World3D : public Resource { GDCLASS(World3D, Resource); private: - RID space; RID scenario; + mutable RID space; mutable RID navigation_map; Ref<Environment> environment; diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 6b4435a991..8c877e4eed 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -144,7 +144,7 @@ int AudioDriver::get_total_channels_by_speaker_mode(AudioDriver::SpeakerMode p_m ERR_FAIL_V(2); } -PackedStringArray AudioDriver::get_device_list() { +PackedStringArray AudioDriver::get_output_device_list() { PackedStringArray list; list.push_back("Default"); @@ -152,11 +152,11 @@ PackedStringArray AudioDriver::get_device_list() { return list; } -String AudioDriver::get_device() { +String AudioDriver::get_output_device() { return "Default"; } -PackedStringArray AudioDriver::capture_get_device_list() { +PackedStringArray AudioDriver::get_input_device_list() { PackedStringArray list; list.push_back("Default"); @@ -238,7 +238,7 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) { #endif if (channel_count != get_channel_count()) { - // Amount of channels changed due to a device change + // Amount of channels changed due to a output_device change // reinitialize the buses channels and buffers init_channels_and_buffers(); } @@ -1632,28 +1632,28 @@ Ref<AudioBusLayout> AudioServer::generate_bus_layout() const { return state; } -PackedStringArray AudioServer::get_device_list() { - return AudioDriver::get_singleton()->get_device_list(); +PackedStringArray AudioServer::get_output_device_list() { + return AudioDriver::get_singleton()->get_output_device_list(); } -String AudioServer::get_device() { - return AudioDriver::get_singleton()->get_device(); +String AudioServer::get_output_device() { + return AudioDriver::get_singleton()->get_output_device(); } -void AudioServer::set_device(String device) { - AudioDriver::get_singleton()->set_device(device); +void AudioServer::set_output_device(String output_device) { + AudioDriver::get_singleton()->set_output_device(output_device); } -PackedStringArray AudioServer::capture_get_device_list() { - return AudioDriver::get_singleton()->capture_get_device_list(); +PackedStringArray AudioServer::get_input_device_list() { + return AudioDriver::get_singleton()->get_input_device_list(); } -String AudioServer::capture_get_device() { - return AudioDriver::get_singleton()->capture_get_device(); +String AudioServer::get_input_device() { + return AudioDriver::get_singleton()->get_input_device(); } -void AudioServer::capture_set_device(const String &p_name) { - AudioDriver::get_singleton()->capture_set_device(p_name); +void AudioServer::set_input_device(const String &p_name) { + AudioDriver::get_singleton()->set_input_device(p_name); } void AudioServer::set_enable_tagging_used_audio_streams(bool p_enable) { @@ -1711,17 +1711,17 @@ void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode); ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate); - ClassDB::bind_method(D_METHOD("get_device_list"), &AudioServer::get_device_list); - ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device); - ClassDB::bind_method(D_METHOD("set_device", "device"), &AudioServer::set_device); + ClassDB::bind_method(D_METHOD("get_output_device_list"), &AudioServer::get_output_device_list); + ClassDB::bind_method(D_METHOD("get_output_device"), &AudioServer::get_output_device); + ClassDB::bind_method(D_METHOD("set_output_device", "output_device"), &AudioServer::set_output_device); ClassDB::bind_method(D_METHOD("get_time_to_next_mix"), &AudioServer::get_time_to_next_mix); ClassDB::bind_method(D_METHOD("get_time_since_last_mix"), &AudioServer::get_time_since_last_mix); ClassDB::bind_method(D_METHOD("get_output_latency"), &AudioServer::get_output_latency); - ClassDB::bind_method(D_METHOD("capture_get_device_list"), &AudioServer::capture_get_device_list); - ClassDB::bind_method(D_METHOD("capture_get_device"), &AudioServer::capture_get_device); - ClassDB::bind_method(D_METHOD("capture_set_device", "name"), &AudioServer::capture_set_device); + ClassDB::bind_method(D_METHOD("get_input_device_list"), &AudioServer::get_input_device_list); + ClassDB::bind_method(D_METHOD("get_input_device"), &AudioServer::get_input_device); + ClassDB::bind_method(D_METHOD("set_input_device", "name"), &AudioServer::set_input_device); ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout); ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout); @@ -1729,11 +1729,11 @@ void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enable_tagging_used_audio_streams", "enable"), &AudioServer::set_enable_tagging_used_audio_streams); ADD_PROPERTY(PropertyInfo(Variant::INT, "bus_count"), "set_bus_count", "get_bus_count"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "device"), "set_device", "get_device"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "capture_device"), "capture_set_device", "capture_get_device"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "output_device"), "set_output_device", "get_output_device"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_device"), "set_input_device", "get_input_device"); // The default value may be set to an empty string by the platform-specific audio driver. // Override for class reference generation purposes. - ADD_PROPERTY_DEFAULT("capture_device", "Default"); + ADD_PROPERTY_DEFAULT("input_device", "Default"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed_scale"), "set_playback_speed_scale", "get_playback_speed_scale"); ADD_SIGNAL(MethodInfo("bus_layout_changed")); diff --git a/servers/audio_server.h b/servers/audio_server.h index a369efedf7..d3d87a8400 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -94,18 +94,18 @@ public: virtual void start() = 0; virtual int get_mix_rate() const = 0; virtual SpeakerMode get_speaker_mode() const = 0; - virtual PackedStringArray get_device_list(); - virtual String get_device(); - virtual void set_device(String device) {} + virtual PackedStringArray get_output_device_list(); + virtual String get_output_device(); + virtual void set_output_device(String output_device) {} virtual void lock() = 0; virtual void unlock() = 0; virtual void finish() = 0; virtual Error capture_start() { return FAILED; } virtual Error capture_stop() { return FAILED; } - virtual void capture_set_device(const String &p_name) {} - virtual String capture_get_device() { return "Default"; } - virtual PackedStringArray capture_get_device_list(); + virtual void set_input_device(const String &p_name) {} + virtual String get_input_device() { return "Default"; } + virtual PackedStringArray get_input_device_list(); virtual float get_latency() { return 0; } @@ -419,13 +419,13 @@ public: void set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout); Ref<AudioBusLayout> generate_bus_layout() const; - PackedStringArray get_device_list(); - String get_device(); - void set_device(String device); + PackedStringArray get_output_device_list(); + String get_output_device(); + void set_output_device(String output_device); - PackedStringArray capture_get_device_list(); - String capture_get_device(); - void capture_set_device(const String &p_name); + PackedStringArray get_input_device_list(); + String get_input_device(); + void set_input_device(const String &p_name); void set_enable_tagging_used_audio_streams(bool p_enable); diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index 2e4087c1de..85ba8ed431 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -207,6 +207,30 @@ void NavigationServer2D::set_debug_navigation_enable_edge_connections(const bool bool NavigationServer2D::get_debug_navigation_enable_edge_connections() const { return NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_connections(); } + +void NavigationServer2D::set_debug_navigation_agent_path_color(const Color &p_color) { + NavigationServer3D::get_singleton()->set_debug_navigation_agent_path_color(p_color); +} + +Color NavigationServer2D::get_debug_navigation_agent_path_color() const { + return NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_color(); +} + +void NavigationServer2D::set_debug_navigation_enable_agent_paths(const bool p_value) { + NavigationServer3D::get_singleton()->set_debug_navigation_enable_agent_paths(p_value); +} + +bool NavigationServer2D::get_debug_navigation_enable_agent_paths() const { + return NavigationServer3D::get_singleton()->get_debug_navigation_enable_agent_paths(); +} + +void NavigationServer2D::set_debug_navigation_agent_path_point_size(float p_point_size) { + NavigationServer3D::get_singleton()->set_debug_navigation_agent_path_point_size(p_point_size); +} + +float NavigationServer2D::get_debug_navigation_agent_path_point_size() const { + return NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_point_size(); +} #endif // DEBUG_ENABLED void NavigationServer2D::_bind_methods() { @@ -286,14 +310,26 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer2D::free); ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map"))); + + ADD_SIGNAL(MethodInfo("navigation_debug_changed")); } NavigationServer2D::NavigationServer2D() { singleton = this; ERR_FAIL_COND_MSG(!NavigationServer3D::get_singleton(), "The Navigation3D singleton should be initialized before the 2D one."); NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationServer2D::_emit_map_changed)); + +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationServer2D::_emit_navigation_debug_changed_signal)); +#endif // DEBUG_ENABLED } +#ifdef DEBUG_ENABLED +void NavigationServer2D::_emit_navigation_debug_changed_signal() { + emit_signal(SNAME("navigation_debug_changed")); +} +#endif // DEBUG_ENABLED + NavigationServer2D::~NavigationServer2D() { singleton = nullptr; } diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index cd18476182..746389404b 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -254,6 +254,20 @@ public: void set_debug_navigation_enable_edge_connections(const bool p_value); bool get_debug_navigation_enable_edge_connections() const; + + void set_debug_navigation_agent_path_color(const Color &p_color); + Color get_debug_navigation_agent_path_color() const; + + void set_debug_navigation_enable_agent_paths(const bool p_value); + bool get_debug_navigation_enable_agent_paths() const; + + void set_debug_navigation_agent_path_point_size(float p_point_size); + float get_debug_navigation_agent_path_point_size() const; +#endif // DEBUG_ENABLED + +#ifdef DEBUG_ENABLED +private: + void _emit_navigation_debug_changed_signal(); #endif // DEBUG_ENABLED }; diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 8f2cff0e04..70897ae75c 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -159,6 +159,7 @@ NavigationServer3D::NavigationServer3D() { debug_navigation_geometry_face_disabled_color = GLOBAL_DEF("debug/shapes/navigation/geometry_face_disabled_color", Color(0.5, 0.5, 0.5, 0.4)); debug_navigation_link_connection_color = GLOBAL_DEF("debug/shapes/navigation/link_connection_color", Color(1.0, 0.5, 1.0, 1.0)); debug_navigation_link_connection_disabled_color = GLOBAL_DEF("debug/shapes/navigation/link_connection_disabled_color", Color(0.5, 0.5, 0.5, 1.0)); + debug_navigation_agent_path_color = GLOBAL_DEF("debug/shapes/navigation/agent_path_color", Color(1.0, 0.0, 0.0, 1.0)); debug_navigation_enable_edge_connections = GLOBAL_DEF("debug/shapes/navigation/enable_edge_connections", true); debug_navigation_enable_edge_connections_xray = GLOBAL_DEF("debug/shapes/navigation/enable_edge_connections_xray", true); @@ -168,6 +169,11 @@ NavigationServer3D::NavigationServer3D() { debug_navigation_enable_link_connections = GLOBAL_DEF("debug/shapes/navigation/enable_link_connections", true); debug_navigation_enable_link_connections_xray = GLOBAL_DEF("debug/shapes/navigation/enable_link_connections_xray", true); + debug_navigation_enable_agent_paths = GLOBAL_DEF("debug/shapes/navigation/enable_agent_paths", true); + debug_navigation_enable_agent_paths_xray = GLOBAL_DEF("debug/shapes/navigation/enable_agent_paths_xray", true); + + debug_navigation_agent_path_point_size = GLOBAL_DEF("debug/shapes/navigation/agent_path_point_size", 4.0); + if (Engine::get_singleton()->is_editor_hint()) { // enable NavigationServer3D when in Editor or else navigation mesh edge connections are invisible // on runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this @@ -329,6 +335,42 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_link_connection return debug_navigation_link_connections_disabled_material; } +Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_line_material() { + if (debug_navigation_agent_path_line_material.is_valid()) { + return debug_navigation_agent_path_line_material; + } + + Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_albedo(debug_navigation_agent_path_color); + if (debug_navigation_enable_agent_paths_xray) { + material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); + } + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2); + + debug_navigation_agent_path_line_material = material; + return debug_navigation_agent_path_line_material; +} + +Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_point_material() { + if (debug_navigation_agent_path_point_material.is_valid()) { + return debug_navigation_agent_path_point_material; + } + + Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_albedo(debug_navigation_agent_path_color); + material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); + material->set_point_size(debug_navigation_agent_path_point_size); + if (debug_navigation_enable_agent_paths_xray) { + material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); + } + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2); + + debug_navigation_agent_path_point_material = material; + return debug_navigation_agent_path_point_material; +} + void NavigationServer3D::set_debug_navigation_edge_connection_color(const Color &p_color) { debug_navigation_edge_connection_color = p_color; if (debug_navigation_edge_connections_material.is_valid()) { @@ -406,6 +448,31 @@ Color NavigationServer3D::get_debug_navigation_link_connection_disabled_color() return debug_navigation_link_connection_disabled_color; } +void NavigationServer3D::set_debug_navigation_agent_path_point_size(float p_point_size) { + debug_navigation_agent_path_point_size = MAX(0.1, p_point_size); + if (debug_navigation_agent_path_point_material.is_valid()) { + debug_navigation_agent_path_point_material->set_point_size(debug_navigation_agent_path_point_size); + } +} + +float NavigationServer3D::get_debug_navigation_agent_path_point_size() const { + return debug_navigation_agent_path_point_size; +} + +void NavigationServer3D::set_debug_navigation_agent_path_color(const Color &p_color) { + debug_navigation_agent_path_color = p_color; + if (debug_navigation_agent_path_line_material.is_valid()) { + debug_navigation_agent_path_line_material->set_albedo(debug_navigation_agent_path_color); + } + if (debug_navigation_agent_path_point_material.is_valid()) { + debug_navigation_agent_path_point_material->set_albedo(debug_navigation_agent_path_color); + } +} + +Color NavigationServer3D::get_debug_navigation_agent_path_color() const { + return debug_navigation_agent_path_color; +} + void NavigationServer3D::set_debug_navigation_enable_edge_connections(const bool p_value) { debug_navigation_enable_edge_connections = p_value; debug_dirty = true; @@ -494,6 +561,37 @@ void NavigationServer3D::set_debug_enabled(bool p_enabled) { bool NavigationServer3D::get_debug_enabled() const { return debug_enabled; } + +void NavigationServer3D::set_debug_navigation_enable_agent_paths(const bool p_value) { + if (debug_navigation_enable_agent_paths != p_value) { + debug_dirty = true; + } + + debug_navigation_enable_agent_paths = p_value; + + if (debug_dirty) { + call_deferred("_emit_navigation_debug_changed_signal"); + } +} + +bool NavigationServer3D::get_debug_navigation_enable_agent_paths() const { + return debug_navigation_enable_agent_paths; +} + +void NavigationServer3D::set_debug_navigation_enable_agent_paths_xray(const bool p_value) { + debug_navigation_enable_agent_paths_xray = p_value; + if (debug_navigation_agent_path_line_material.is_valid()) { + debug_navigation_agent_path_line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, debug_navigation_enable_agent_paths_xray); + } + if (debug_navigation_agent_path_point_material.is_valid()) { + debug_navigation_agent_path_point_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, debug_navigation_enable_agent_paths_xray); + } +} + +bool NavigationServer3D::get_debug_navigation_enable_agent_paths_xray() const { + return debug_navigation_enable_agent_paths_xray; +} + #endif // DEBUG_ENABLED /////////////////////////////////////////////////////// diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 9c468d1b10..bc4bdf2a30 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -287,6 +287,9 @@ private: Color debug_navigation_geometry_face_disabled_color = Color(0.5, 0.5, 0.5, 0.4); Color debug_navigation_link_connection_color = Color(1.0, 0.5, 1.0, 1.0); Color debug_navigation_link_connection_disabled_color = Color(0.5, 0.5, 0.5, 1.0); + Color debug_navigation_agent_path_color = Color(1.0, 0.0, 0.0, 1.0); + + float debug_navigation_agent_path_point_size = 4.0; bool debug_navigation_enable_edge_connections = true; bool debug_navigation_enable_edge_connections_xray = true; @@ -295,6 +298,8 @@ private: bool debug_navigation_enable_geometry_face_random_color = true; bool debug_navigation_enable_link_connections = true; bool debug_navigation_enable_link_connections_xray = true; + bool debug_navigation_enable_agent_paths = true; + bool debug_navigation_enable_agent_paths_xray = true; Ref<StandardMaterial3D> debug_navigation_geometry_edge_material; Ref<StandardMaterial3D> debug_navigation_geometry_face_material; @@ -304,6 +309,9 @@ private: Ref<StandardMaterial3D> debug_navigation_link_connections_material; Ref<StandardMaterial3D> debug_navigation_link_connections_disabled_material; + Ref<StandardMaterial3D> debug_navigation_agent_path_line_material; + Ref<StandardMaterial3D> debug_navigation_agent_path_point_material; + public: void set_debug_enabled(bool p_enabled); bool get_debug_enabled() const; @@ -329,6 +337,9 @@ public: void set_debug_navigation_link_connection_disabled_color(const Color &p_color); Color get_debug_navigation_link_connection_disabled_color() const; + void set_debug_navigation_agent_path_color(const Color &p_color); + Color get_debug_navigation_agent_path_color() const; + void set_debug_navigation_enable_edge_connections(const bool p_value); bool get_debug_navigation_enable_edge_connections() const; @@ -350,6 +361,15 @@ public: void set_debug_navigation_enable_link_connections_xray(const bool p_value); bool get_debug_navigation_enable_link_connections_xray() const; + void set_debug_navigation_enable_agent_paths(const bool p_value); + bool get_debug_navigation_enable_agent_paths() const; + + void set_debug_navigation_enable_agent_paths_xray(const bool p_value); + bool get_debug_navigation_enable_agent_paths_xray() const; + + void set_debug_navigation_agent_path_point_size(float p_point_size); + float get_debug_navigation_agent_path_point_size() const; + Ref<StandardMaterial3D> get_debug_navigation_geometry_face_material(); Ref<StandardMaterial3D> get_debug_navigation_geometry_edge_material(); Ref<StandardMaterial3D> get_debug_navigation_geometry_face_disabled_material(); @@ -357,6 +377,9 @@ public: Ref<StandardMaterial3D> get_debug_navigation_edge_connections_material(); Ref<StandardMaterial3D> get_debug_navigation_link_connections_material(); Ref<StandardMaterial3D> get_debug_navigation_link_connections_disabled_material(); + + Ref<StandardMaterial3D> get_debug_navigation_agent_path_line_material(); + Ref<StandardMaterial3D> get_debug_navigation_agent_path_point_material(); #endif // DEBUG_ENABLED }; diff --git a/servers/physics_2d/godot_space_2d.cpp b/servers/physics_2d/godot_space_2d.cpp index 0a5a7c93cc..1d1f1ba054 100644 --- a/servers/physics_2d/godot_space_2d.cpp +++ b/servers/physics_2d/godot_space_2d.cpp @@ -1216,15 +1216,15 @@ GodotPhysicsDirectSpaceState2D *GodotSpace2D::get_direct_state() { } GodotSpace2D::GodotSpace2D() { - body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0); - body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_angular", Math::deg_to_rad(8.0)); - body_time_to_sleep = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); - solver_iterations = GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/2d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); - contact_recycle_radius = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.0); - contact_max_separation = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.5); - contact_max_allowed_penetration = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 0.3); - contact_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); - constraint_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_constraint_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.2); + body_linear_velocity_sleep_threshold = GLOBAL_GET("physics/2d/sleep_threshold_linear"); + body_angular_velocity_sleep_threshold = GLOBAL_GET("physics/2d/sleep_threshold_angular"); + body_time_to_sleep = GLOBAL_GET("physics/2d/time_before_sleep"); + solver_iterations = GLOBAL_GET("physics/2d/solver/solver_iterations"); + contact_recycle_radius = GLOBAL_GET("physics/2d/solver/contact_recycle_radius"); + contact_max_separation = GLOBAL_GET("physics/2d/solver/contact_max_separation"); + contact_max_allowed_penetration = GLOBAL_GET("physics/2d/solver/contact_max_allowed_penetration"); + contact_bias = GLOBAL_GET("physics/2d/solver/default_contact_bias"); + constraint_bias = GLOBAL_GET("physics/2d/solver/default_constraint_bias"); broadphase = GodotBroadPhase2D::create_func(); broadphase->set_pair_callback(_broadphase_pair, this); diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp index 93572965d2..35f6fa023d 100644 --- a/servers/physics_3d/godot_space_3d.cpp +++ b/servers/physics_3d/godot_space_3d.cpp @@ -1250,14 +1250,14 @@ GodotPhysicsDirectSpaceState3D *GodotSpace3D::get_direct_state() { } GodotSpace3D::GodotSpace3D() { - body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1); - body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_angular", Math::deg_to_rad(8.0)); - body_time_to_sleep = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); - solver_iterations = GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/3d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); - contact_recycle_radius = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); - contact_max_separation = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.05); - contact_max_allowed_penetration = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); - contact_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); + body_linear_velocity_sleep_threshold = GLOBAL_GET("physics/3d/sleep_threshold_linear"); + body_angular_velocity_sleep_threshold = GLOBAL_GET("physics/3d/sleep_threshold_angular"); + body_time_to_sleep = GLOBAL_GET("physics/3d/time_before_sleep"); + solver_iterations = GLOBAL_GET("physics/3d/solver/solver_iterations"); + contact_recycle_radius = GLOBAL_GET("physics/3d/solver/contact_recycle_radius"); + contact_max_separation = GLOBAL_GET("physics/3d/solver/contact_max_separation"); + contact_max_allowed_penetration = GLOBAL_GET("physics/3d/solver/contact_max_allowed_penetration"); + contact_bias = GLOBAL_GET("physics/3d/solver/default_contact_bias"); broadphase = GodotBroadPhase3D::create_func(); broadphase->set_pair_callback(_broadphase_pair, this); diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 15c2749484..f9a8d5f156 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -879,6 +879,23 @@ void PhysicsServer2D::_bind_methods() { PhysicsServer2D::PhysicsServer2D() { singleton = this; + + // World2D physics space + GLOBAL_DEF_BASIC("physics/2d/default_gravity", 980.0); + GLOBAL_DEF_BASIC("physics/2d/default_gravity_vector", Vector2(0, 1)); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 0.1); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 1.0); + + // PhysicsServer2D + GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0); + GLOBAL_DEF("physics/2d/sleep_threshold_angular", Math::deg_to_rad(8.0)); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); + GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/2d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.0); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.5); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 0.3); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_constraint_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.2); } PhysicsServer2D::~PhysicsServer2D() { diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index 864774374b..6a163c86d2 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -1047,6 +1047,22 @@ void PhysicsServer3D::_bind_methods() { PhysicsServer3D::PhysicsServer3D() { singleton = this; + + // World3D physics space + GLOBAL_DEF_BASIC("physics/3d/default_gravity", 9.8); + GLOBAL_DEF_BASIC("physics/3d/default_gravity_vector", Vector3(0, -1, 0)); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1); + + // PhysicsServer3D + GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1); + GLOBAL_DEF("physics/3d/sleep_threshold_angular", Math::deg_to_rad(8.0)); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); + GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/3d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.05); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); } PhysicsServer3D::~PhysicsServer3D() { diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h new file mode 100644 index 0000000000..1736f2c452 --- /dev/null +++ b/tests/display_server_mock.h @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* display_server_mock.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISPLAY_SERVER_MOCK_H +#define DISPLAY_SERVER_MOCK_H + +#include "servers/display_server_headless.h" + +#include "servers/rendering/dummy/rasterizer_dummy.h" + +// Specialized DisplayServer for unittests based on DisplayServerHeadless, that +// additionally supports rudimentary InputEvent handling and mouse position. +class DisplayServerMock : public DisplayServerHeadless { +private: + friend class DisplayServer; + + Point2i mouse_position = Point2i(-1, -1); // Outside of Window. + bool window_over = false; + Callable event_callback; + Callable input_event_callback; + + static Vector<String> get_rendering_drivers_func() { + Vector<String> drivers; + drivers.push_back("dummy"); + return drivers; + } + + static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { + r_error = OK; + RasterizerDummy::make_current(); + return memnew(DisplayServerMock()); + } + + static void _dispatch_input_events(const Ref<InputEvent> &p_event) { + static_cast<DisplayServerMock *>(get_singleton())->_dispatch_input_event(p_event); + } + + void _dispatch_input_event(const Ref<InputEvent> &p_event) { + Variant ev = p_event; + Variant *evp = &ev; + Variant ret; + Callable::CallError ce; + + if (input_event_callback.is_valid()) { + input_event_callback.callp((const Variant **)&evp, 1, ret, ce); + } + } + + void _set_mouse_position(const Point2i &p_position) { + if (mouse_position == p_position) { + return; + } + mouse_position = p_position; + _set_window_over(Rect2i(Point2i(0, 0), window_get_size()).has_point(p_position)); + } + + void _set_window_over(bool p_over) { + if (p_over == window_over) { + return; + } + window_over = p_over; + _send_window_event(p_over ? WINDOW_EVENT_MOUSE_ENTER : WINDOW_EVENT_MOUSE_EXIT); + } + + void _send_window_event(WindowEvent p_event) { + if (!event_callback.is_null()) { + Variant event = int(p_event); + Variant *eventp = &event; + Variant ret; + Callable::CallError ce; + event_callback.callp((const Variant **)&eventp, 1, ret, ce); + } + } + +public: + bool has_feature(Feature p_feature) const override { + switch (p_feature) { + case FEATURE_MOUSE: + return true; + default: { + } + } + return false; + } + + String get_name() const override { return "mock"; } + + // You can simulate DisplayServer-events by calling this function. + // The events will be deliverd to Godot's Input-system. + // Mouse-events (Button & Motion) will additionally update the DisplayServer's mouse position. + void simulate_event(Ref<InputEvent> p_event) { + Ref<InputEventMouse> me = p_event; + if (me.is_valid()) { + _set_mouse_position(me->get_position()); + } + Input::get_singleton()->parse_input_event(p_event); + } + + virtual Point2i mouse_get_position() const override { return mouse_position; } + + virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override { + return Size2i(1920, 1080); + } + + virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + event_callback = p_callable; + } + + virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + input_event_callback = p_callable; + } + + static void register_mock_driver() { + register_create_function("mock", create_func, get_rendering_drivers_func); + } + + DisplayServerMock() { + Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); + } + ~DisplayServerMock() {} +}; + +#endif // DISPLAY_SERVER_MOCK_H diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 944f9cb9f6..f2663b037d 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -3271,6 +3271,7 @@ TEST_CASE("[SceneTree][TextEdit] mouse") { TEST_CASE("[SceneTree][TextEdit] caret") { TextEdit *text_edit = memnew(TextEdit); + text_edit->set_context_menu_enabled(false); // Prohibit sending InputEvents to the context menu. SceneTree::get_singleton()->get_root()->add_child(text_edit); text_edit->set_size(Size2(800, 200)); diff --git a/tests/test_macros.h b/tests/test_macros.h index 3074c1abf5..80a93c8327 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -31,6 +31,8 @@ #ifndef TEST_MACROS_H #define TEST_MACROS_H +#include "display_server_mock.h" + #include "core/core_globals.h" #include "core/input/input_map.h" #include "core/object/message_queue.h" @@ -139,13 +141,15 @@ int register_test_command(String p_command, TestFunc p_function); // SEND_GUI_MOUSE_MOTION_EVENT - takes an object, position, mouse mask and modifiers e.g SEND_GUI_MOUSE_MOTION_EVENT(code_edit, Vector2(50, 50), MouseButtonMask::LEFT, KeyModifierMask::META); // SEND_GUI_DOUBLE_CLICK - takes an object, position and modifiers. e.g SEND_GUI_DOUBLE_CLICK(code_edit, Vector2(50, 50), KeyModifierMask::META); +#define _SEND_DISPLAYSERVER_EVENT(m_event) ((DisplayServerMock *)(DisplayServer::get_singleton()))->simulate_event(m_event); + #define SEND_GUI_ACTION(m_object, m_action) \ { \ const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(m_action); \ const List<Ref<InputEvent>>::Element *first_event = events->front(); \ Ref<InputEventKey> event = first_event->get(); \ event->set_pressed(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -153,7 +157,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ Ref<InputEventKey> event = InputEventKey::create_reference(m_input); \ event->set_pressed(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -176,7 +180,7 @@ int register_test_command(String p_command, TestFunc p_function); #define SEND_GUI_MOUSE_BUTTON_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers) \ { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -184,7 +188,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \ event->set_pressed(false); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -192,7 +196,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, MouseButton::LEFT, 0, m_modifers); \ event->set_double_click(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -207,7 +211,7 @@ int register_test_command(String p_command, TestFunc p_function); event->set_button_mask(m_mask); \ event->set_relative(Vector2(10, 10)); \ _UPDATE_EVENT_MODIFERS(event, m_modifers); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ CoreGlobals::print_error_enabled = errors_enabled; \ } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index c7f2d4cbfb..ea6058f707 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -107,6 +107,7 @@ #include "modules/modules_tests.gen.h" +#include "tests/display_server_mock.h" #include "tests/test_macros.h" #include "scene/theme/theme_db.h" @@ -126,6 +127,7 @@ int test_main(int argc, char *argv[]) { args.push_back(String::utf8(argv[i])); } OS::get_singleton()->set_cmdline("", args, List<String>()); + DisplayServerMock::register_mock_driver(); // Run custom test tools. if (test_commands) { @@ -200,11 +202,12 @@ struct GodotTestCaseListener : public doctest::IReporter { memnew(MessageQueue); memnew(Input); + Input::get_singleton()->set_use_accumulated_input(false); Error err = OK; OS::get_singleton()->set_has_server_feature_callback(nullptr); for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { - if (String("headless") == DisplayServer::get_create_function_name(i)) { + if (String("mock") == DisplayServer::get_create_function_name(i)) { DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, err); break; } |