diff options
48 files changed, 518 insertions, 203 deletions
diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 578cd91c52..812fbc774e 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -31,7 +31,6 @@ #include "translation_loader_po.h" #include "core/io/file_access.h" -#include "core/string/translation.h" #include "core/string/translation_po.h" Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_error) { @@ -361,7 +360,7 @@ void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions) } bool TranslationLoaderPO::handles_type(const String &p_type) const { - return (p_type == "Translation"); + return (p_type == "Translation") || (p_type == "TranslationPO"); } String TranslationLoaderPO::get_resource_type(const String &p_path) const { diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 55d00b6cf9..66f15f7494 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -6,7 +6,7 @@ <description> A list of global scope enumerated constants and built-in functions. This is all that resides in the globals, constants regarding error codes, keycodes, property hints, etc. Singletons are also documented here, since they can be accessed from anywhere. - For the entries related to GDScript which can be accessed in any script see [@GDScript]. + For the entries that can only be accessed from scripts written in GDScript, see [@GDScript]. </description> <tutorials> <link title="Random number generation">$DOCS_URL/tutorials/math/random_number_generation.html</link> diff --git a/doc/classes/AtlasTexture.xml b/doc/classes/AtlasTexture.xml index 45877f4003..287f2cc19a 100644 --- a/doc/classes/AtlasTexture.xml +++ b/doc/classes/AtlasTexture.xml @@ -6,7 +6,7 @@ <description> [Texture2D] resource that draws only part of its [member atlas] texture, as defined by the [member region]. An additional [member margin] can also be set, which is useful for small adjustments. Multiple [AtlasTexture] resources can be cropped from the same [member atlas]. Packing many smaller textures into a singular large texture helps to optimize video memory costs and render calls. - [b]Note:[/b] [AtlasTexture] cannot be used in an [AnimatedTexture], and may not tile properly in nodes such as [TextureRect], when inside other [AtlasTexture] resources. + [b]Note:[/b] [AtlasTexture] cannot be used in an [AnimatedTexture], and will not tile properly in nodes such as [TextureRect] or [Sprite2D]. To tile an [AtlasTexture], modify its [member region] instead. </description> <tutorials> </tutorials> diff --git a/doc/classes/AudioEffectFilter.xml b/doc/classes/AudioEffectFilter.xml index e5c1f4ccf4..18540de736 100644 --- a/doc/classes/AudioEffectFilter.xml +++ b/doc/classes/AudioEffectFilter.xml @@ -14,6 +14,7 @@ Threshold frequency for the filter, in Hz. </member> <member name="db" type="int" setter="set_db" getter="get_db" enum="AudioEffectFilter.FilterDB" default="0"> + Steepness of the cutoff curve in dB per octave, also known as the order of the filter. Higher orders have a more aggressive cutoff. </member> <member name="gain" type="float" setter="set_gain" getter="get_gain" default="1.0"> Gain amount of the frequencies after the filter. @@ -24,12 +25,16 @@ </members> <constants> <constant name="FILTER_6DB" value="0" enum="FilterDB"> + Cutting off at 6dB per octave. </constant> <constant name="FILTER_12DB" value="1" enum="FilterDB"> + Cutting off at 12dB per octave. </constant> <constant name="FILTER_18DB" value="2" enum="FilterDB"> + Cutting off at 18dB per octave. </constant> <constant name="FILTER_24DB" value="3" enum="FilterDB"> + Cutting off at 24dB per octave. </constant> </constants> </class> diff --git a/doc/classes/AudioEffectStereoEnhance.xml b/doc/classes/AudioEffectStereoEnhance.xml index f009bec5bb..459ae3ebc6 100644 --- a/doc/classes/AudioEffectStereoEnhance.xml +++ b/doc/classes/AudioEffectStereoEnhance.xml @@ -11,11 +11,13 @@ </tutorials> <members> <member name="pan_pullout" type="float" setter="set_pan_pullout" getter="get_pan_pullout" default="1.0"> - Values greater than 1.0 increase intensity of any panning on audio passing through this effect, whereas values less than 1.0 will decrease the panning intensity. A value of 0.0 will downmix audio to mono. + Amplifies the difference between stereo channels, increasing or decreasing existing panning. A value of 0.0 will downmix stereo to mono. Does not affect a mono signal. </member> <member name="surround" type="float" setter="set_surround" getter="get_surround" default="0.0"> + Widens sound stage through phase shifting in conjunction with [member time_pullout_ms]. Just pans sound to the left channel if [member time_pullout_ms] is 0. </member> <member name="time_pullout_ms" type="float" setter="set_time_pullout" getter="get_time_pullout" default="0.0"> + Widens sound stage through phase shifting in conjunction with [member surround]. Just delays the right channel if [member surround] is 0. </member> </members> </class> diff --git a/doc/classes/AudioStreamPlayback.xml b/doc/classes/AudioStreamPlayback.xml index 02f3407f79..f01406d0f1 100644 --- a/doc/classes/AudioStreamPlayback.xml +++ b/doc/classes/AudioStreamPlayback.xml @@ -79,12 +79,47 @@ Overridable method. Called whenever the audio stream is mixed if the playback is active and [method AudioServer.set_enable_tagging_used_audio_streams] has been set to [code]true[/code]. Editor plugins may use this method to "tag" the current position along the audio stream and display it in a preview. </description> </method> + <method name="get_loop_count" qualifiers="const"> + <return type="int" /> + <description> + Returns the number of times the stream has looped. + </description> + </method> + <method name="get_playback_position" qualifiers="const"> + <return type="float" /> + <description> + Returns the current position in the stream, in seconds. + </description> + </method> <method name="get_sample_playback" qualifiers="const" experimental=""> <return type="AudioSamplePlayback" /> <description> Returns the [AudioSamplePlayback] associated with this [AudioStreamPlayback] for playing back the audio sample of this stream. </description> </method> + <method name="is_playing" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the stream is playing. + </description> + </method> + <method name="mix_audio"> + <return type="PackedVector2Array" /> + <param index="0" name="rate_scale" type="float" /> + <param index="1" name="frames" type="int" /> + <description> + Mixes up to [param frames] of audio from the stream from the current position, at a rate of [param rate_scale], advancing the stream. + Returns a [PackedVector2Array] where each element holds the left and right channel volume levels of each frame. + [b]Note:[/b] Can return fewer frames than requested, make sure to use the size of the return value. + </description> + </method> + <method name="seek"> + <return type="void" /> + <param index="0" name="time" type="float" default="0.0" /> + <description> + Seeks the stream at the given [param time], in seconds. + </description> + </method> <method name="set_sample_playback" experimental=""> <return type="void" /> <param index="0" name="playback_sample" type="AudioSamplePlayback" /> @@ -92,5 +127,18 @@ Associates [AudioSamplePlayback] to this [AudioStreamPlayback] for playing back the audio sample of this stream. </description> </method> + <method name="start"> + <return type="void" /> + <param index="0" name="from_pos" type="float" default="0.0" /> + <description> + Starts the stream from the given [param from_pos], in seconds. + </description> + </method> + <method name="stop"> + <return type="void" /> + <description> + Stops the stream. + </description> + </method> </methods> </class> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index bc17c8b008..78e9c392db 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -533,9 +533,13 @@ </method> <method name="make_canvas_position_local" qualifiers="const"> <return type="Vector2" /> - <param index="0" name="screen_point" type="Vector2" /> + <param index="0" name="viewport_point" type="Vector2" /> <description> - Assigns [param screen_point] as this node's new local transform. + Transforms [param viewport_point] from the viewport's coordinates to this node's local coordinates. + For the opposite operation, use [method get_global_transform_with_canvas]. + [codeblock] + var viewport_point = get_global_transform_with_canvas() * local_point + [/codeblock] </description> </method> <method name="make_input_local" qualifiers="const"> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 3e0c328dcb..91c9072f73 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -33,6 +33,7 @@ - [kbd]Cmd + E[/kbd]: Same as [kbd]End[/kbd], move the caret to the end of the line - [kbd]Cmd + Left Arrow[/kbd]: Same as [kbd]Home[/kbd], move the caret to the beginning of the line - [kbd]Cmd + Right Arrow[/kbd]: Same as [kbd]End[/kbd], move the caret to the end of the line + [b]Note:[/b] Caret movement shortcuts listed above are not affected by [member shortcut_keys_enabled]. </description> <tutorials> </tutorials> @@ -334,7 +335,7 @@ If [code]false[/code], it's impossible to select the text using mouse nor keyboard. </member> <member name="shortcut_keys_enabled" type="bool" setter="set_shortcut_keys_enabled" getter="is_shortcut_keys_enabled" default="true"> - If [code]false[/code], using shortcuts will be disabled. + If [code]true[/code], shortcut keys for context menu items are enabled, even if the context menu is disabled. </member> <member name="structured_text_bidi_override" type="int" setter="set_structured_text_bidi_override" getter="get_structured_text_bidi_override" enum="TextServer.StructuredTextParser" default="0"> Set BiDi algorithm override for the structured text. diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 40f08dafe6..588d0c73f9 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -6,6 +6,7 @@ <description> This is the built-in string Variant type (and the one used by GDScript). Strings may contain any number of Unicode characters, and expose methods useful for manipulating and generating strings. Strings are reference-counted and use a copy-on-write approach (every modification to a string returns a new [String]), so passing them around is cheap in resources. Some string methods have corresponding variations. Variations suffixed with [code]n[/code] ([method countn], [method findn], [method replacen], etc.) are [b]case-insensitive[/b] (they make no distinction between uppercase and lowercase letters). Method variations prefixed with [code]r[/code] ([method rfind], [method rsplit], etc.) are reversed, and start from the end of the string, instead of the beginning. + To convert any Variant to or from a string, see [method @GlobalScope.str], [method @GlobalScope.str_to_var], and [method @GlobalScope.var_to_str]. [b]Note:[/b] In a boolean context, a string will evaluate to [code]false[/code] if it is empty ([code]""[/code]). Otherwise, a string will always evaluate to [code]true[/code]. </description> <tutorials> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 4158fbe710..756716433e 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -251,14 +251,14 @@ </member> <member name="y" type="Vector2" setter="" getter="" default="Vector2(0, 1)"> The transform basis's Y axis, and the column [code]1[/code] of the matrix. Combined with [member x], this represents the transform's rotation, scale, and skew. - On the identity transform, this vector points up ([constant Vector2.UP]). + On the identity transform, this vector points down ([constant Vector2.DOWN]). </member> </members> <constants> <constant name="IDENTITY" value="Transform2D(1, 0, 0, 1, 0, 0)"> The identity [Transform2D]. A transform with no translation, no rotation, and its scale being [code]1[/code]. When multiplied by another [Variant] such as [Rect2] or another [Transform2D], no transformation occurs. This means that: - The [member x] points right ([constant Vector2.RIGHT]); - - The [member y] points up ([constant Vector2.UP]). + - The [member y] points down ([constant Vector2.DOWN]). [codeblock] var transform = Transform2D.IDENTITY print("| X | Y | Origin") diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index ee16c61c89..bb02172b1a 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -547,6 +547,7 @@ Variant EditorData::instantiate_custom_type(const String &p_type, const String & if (n) { n->set_name(p_type); } + n->set_meta(SceneStringName(_custom_type_script), script); ((Object *)ob)->set_script(script); return ob; } @@ -1008,6 +1009,7 @@ Variant EditorData::script_class_instance(const String &p_class) { // Store in a variant to initialize the refcount if needed. Variant obj = ClassDB::instantiate(script->get_instance_base_type()); if (obj) { + Object::cast_to<Object>(obj)->set_meta(SceneStringName(_custom_type_script), script); obj.operator Object *()->set_script(script); } return obj; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f328b9fc91..c69f443a22 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3379,6 +3379,8 @@ void EditorNode::unload_editor_addons() { remove_editor_plugin(E.value, false); memdelete(E.value); } + + addon_name_to_plugin.clear(); } void EditorNode::_discard_changes(const String &p_str) { @@ -4673,6 +4675,11 @@ void EditorNode::stop_child_process(OS::ProcessID p_pid) { Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const { ERR_FAIL_NULL_V(p_object, nullptr); + const Node *node = Object::cast_to<const Node>(p_object); + if (node && node->has_meta(SceneStringName(_custom_type_script))) { + return node->get_meta(SceneStringName(_custom_type_script)); + } + Ref<Script> scr = p_object->get_script(); if (scr.is_valid()) { diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index c5a35e466c..2e2a18b9d6 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -3221,6 +3221,7 @@ void EditorPropertyResource::setup(Object *p_object, const String &p_path, const } resource_picker->set_base_type(p_base_type); + resource_picker->set_resource_owner(p_object); resource_picker->set_editable(true); resource_picker->set_h_size_flags(SIZE_EXPAND_FILL); add_child(resource_picker); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 0f0287718c..025d019f45 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -224,7 +224,9 @@ void EditorResourcePicker::_update_menu_items() { } if (is_editable()) { - edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR); + if (!_is_custom_type_script()) { + edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR); + } edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE); // Check whether the resource has subresources. @@ -694,6 +696,16 @@ bool EditorResourcePicker::_is_type_valid(const String &p_type_name, const HashS return false; } +bool EditorResourcePicker::_is_custom_type_script() const { + Ref<Script> resource_as_script = edited_resource; + + if (resource_as_script.is_valid() && resource_owner && resource_owner->has_meta(SceneStringName(_custom_type_script))) { + return true; + } + + return false; +} + Variant EditorResourcePicker::get_drag_data_fw(const Point2 &p_point, Control *p_from) { if (edited_resource.is_valid()) { Dictionary drag_data = EditorNode::get_singleton()->drag_resource(edited_resource, p_from); @@ -953,6 +965,10 @@ bool EditorResourcePicker::is_toggle_pressed() const { return assign_button->is_pressed(); } +void EditorResourcePicker::set_resource_owner(Object *p_object) { + resource_owner = p_object; +} + void EditorResourcePicker::set_editable(bool p_editable) { editable = p_editable; assign_button->set_disabled(!editable && !edited_resource.is_valid()); @@ -1098,7 +1114,10 @@ void EditorScriptPicker::set_create_options(Object *p_menu_node) { return; } - menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT); + if (!(script_owner && script_owner->has_meta(SceneStringName(_custom_type_script)))) { + menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT); + } + if (script_owner) { Ref<Script> scr = script_owner->get_script(); if (scr.is_valid()) { diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 0a32dea3ed..8fb774a2cb 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -81,6 +81,8 @@ class EditorResourcePicker : public HBoxContainer { CONVERT_BASE_ID = 1000, }; + Object *resource_owner = nullptr; + PopupMenu *edit_menu = nullptr; void _update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj); @@ -102,6 +104,7 @@ class EditorResourcePicker : public HBoxContainer { void _ensure_allowed_types() const; bool _is_drop_valid(const Dictionary &p_drag_data) const; bool _is_type_valid(const String &p_type_name, const HashSet<StringName> &p_allowed_types) const; + bool _is_custom_type_script() const; Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; @@ -137,6 +140,8 @@ public: void set_toggle_pressed(bool p_pressed); bool is_toggle_pressed() const; + void set_resource_owner(Object *p_object); + void set_editable(bool p_editable); bool is_editable() const; diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp index cd13deb3e9..24943af60f 100644 --- a/editor/plugins/control_editor_plugin.cpp +++ b/editor/plugins/control_editor_plugin.cpp @@ -536,7 +536,6 @@ ControlEditorPopupButton::ControlEditorPopupButton() { set_focus_mode(FOCUS_NONE); popup_panel = memnew(PopupPanel); - popup_panel->set_theme_type_variation("ControlEditorPopupPanel"); add_child(popup_panel); popup_panel->connect("about_to_popup", callable_mp(this, &ControlEditorPopupButton::_popup_visibility_changed).bind(true)); popup_panel->connect("popup_hide", callable_mp(this, &ControlEditorPopupButton::_popup_visibility_changed).bind(false)); diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index c7db243662..5333e10b56 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -593,12 +593,12 @@ Sprite2DEditor::Sprite2DEditor() { add_child(err_dialog); debug_uv_dialog = memnew(ConfirmationDialog); + debug_uv_dialog->set_size(Size2(960, 540) * EDSCALE); VBoxContainer *vb = memnew(VBoxContainer); debug_uv_dialog->add_child(vb); debug_uv = memnew(Panel); debug_uv->connect(SceneStringName(gui_input), callable_mp(this, &Sprite2DEditor::_debug_uv_input)); debug_uv->connect(SceneStringName(draw), callable_mp(this, &Sprite2DEditor::_debug_uv_draw)); - debug_uv->set_custom_minimum_size(Size2(800, 500) * EDSCALE); debug_uv->set_clip_contents(true); vb->add_margin_child(TTR("Preview:"), debug_uv, true); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index bcab0c2883..11aa282833 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1667,6 +1667,7 @@ void SceneTreeDock::_notification(int p_what) { button_instance->set_icon(get_editor_theme_icon(SNAME("Instance"))); button_create_script->set_icon(get_editor_theme_icon(SNAME("ScriptCreate"))); button_detach_script->set_icon(get_editor_theme_icon(SNAME("ScriptRemove"))); + button_extend_script->set_icon(get_editor_theme_icon(SNAME("ScriptExtend"))); button_tree_menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); filter->set_right_icon(get_editor_theme_icon(SNAME("Search"))); @@ -2784,33 +2785,49 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { } void SceneTreeDock::_update_script_button() { - if (!profile_allow_script_editing) { - button_create_script->hide(); - button_detach_script->hide(); - } else if (editor_selection->get_selection().size() == 0) { - button_create_script->hide(); - button_detach_script->hide(); - } else if (editor_selection->get_selection().size() == 1) { - Node *n = editor_selection->get_selected_node_list().front()->get(); - if (n->get_script().is_null()) { - button_create_script->show(); - button_detach_script->hide(); - } else { - button_create_script->hide(); - button_detach_script->show(); - } - } else { - button_create_script->hide(); + bool can_create_script = false; + bool can_detach_script = false; + bool can_extend_script = false; + + if (profile_allow_script_editing) { Array selection = editor_selection->get_selected_nodes(); + for (int i = 0; i < selection.size(); i++) { Node *n = Object::cast_to<Node>(selection[i]); - if (!n->get_script().is_null()) { - button_detach_script->show(); - return; + Ref<Script> s = n->get_script(); + Ref<Script> cts; + + if (n->has_meta(SceneStringName(_custom_type_script))) { + cts = n->get_meta(SceneStringName(_custom_type_script)); + } + + if (selection.size() == 1) { + if (s.is_valid()) { + if (cts.is_valid() && s == cts) { + can_extend_script = true; + } + } else { + can_create_script = true; + } + } + + if (s.is_valid()) { + if (cts.is_valid()) { + if (s != cts) { + can_detach_script = true; + break; + } + } else { + can_detach_script = true; + break; + } } } - button_detach_script->hide(); } + + button_create_script->set_visible(can_create_script); + button_detach_script->set_visible(can_detach_script); + button_extend_script->set_visible(can_extend_script); } void SceneTreeDock::_selection_changed() { @@ -3057,7 +3074,28 @@ void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_pro Node *newnode = p_by_node; if (p_keep_properties) { - Node *default_oldnode = Object::cast_to<Node>(ClassDB::instantiate(oldnode->get_class())); + Node *default_oldnode = nullptr; + + // If we're dealing with a custom node type, we need to create a default instance of the custom type instead of the native type for property comparison. + if (oldnode->has_meta(SceneStringName(_custom_type_script))) { + Ref<Script> cts = oldnode->get_meta(SceneStringName(_custom_type_script)); + default_oldnode = Object::cast_to<Node>(get_editor_data()->script_class_instance(cts->get_global_name())); + if (default_oldnode) { + default_oldnode->set_name(cts->get_global_name()); + get_editor_data()->instantiate_object_properties(default_oldnode); + } else { + // Legacy custom type, registered with "add_custom_type()". + // TODO: Should probably be deprecated in 4.x. + const EditorData::CustomType *custom_type = get_editor_data()->get_custom_type_by_path(cts->get_path()); + if (custom_type) { + default_oldnode = Object::cast_to<Node>(get_editor_data()->instantiate_custom_type(custom_type->name, cts->get_instance_base_type())); + } + } + } + + if (!default_oldnode) { + default_oldnode = Object::cast_to<Node>(ClassDB::instantiate(oldnode->get_class())); + } List<PropertyInfo> pinfo; oldnode->get_property_list(&pinfo); @@ -3542,6 +3580,27 @@ void SceneTreeDock::_script_dropped(const String &p_file, NodePath p_to) { undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(n)).path_join(new_node->get_name()))); undo_redo->commit_action(); } else { + // Check if dropped script is compatible. + if (n->has_meta(SceneStringName(_custom_type_script))) { + Ref<Script> ct_scr = n->get_meta(SceneStringName(_custom_type_script)); + if (!scr->inherits_script(ct_scr)) { + String custom_type_name = ct_scr->get_global_name(); + + // Legacy custom type, registered with "add_custom_type()". + if (custom_type_name.is_empty()) { + const EditorData::CustomType *custom_type = get_editor_data()->get_custom_type_by_path(ct_scr->get_path()); + if (custom_type) { + custom_type_name = custom_type->name; + } else { + custom_type_name = TTR("<unknown>"); + } + } + + WARN_PRINT_ED(vformat("Script does not extend type: '%s'.", custom_type_name)); + return; + } + } + undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, n); undo_redo->add_do_method(InspectorDock::get_singleton(), "store_script_properties", n); undo_redo->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", n); @@ -3649,6 +3708,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { Ref<Script> existing_script; bool existing_script_removable = true; + bool allow_attach_new_script = true; if (selection.size() == 1) { Node *selected = selection.front()->get(); @@ -3672,6 +3732,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (EditorNode::get_singleton()->get_object_custom_type_base(selected) == existing_script) { existing_script_removable = false; } + + if (selected->has_meta(SceneStringName(_custom_type_script))) { + allow_attach_new_script = false; + } } if (profile_allow_editing) { @@ -3692,7 +3756,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (full_selection.size() == 1) { add_separator = true; - menu->add_icon_shortcut(get_editor_theme_icon(SNAME("ScriptCreate")), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + if (allow_attach_new_script) { + menu->add_icon_shortcut(get_editor_theme_icon(SNAME("ScriptCreate")), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + } + if (existing_script.is_valid()) { menu->add_icon_shortcut(get_editor_theme_icon(SNAME("ScriptExtend")), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_EXTEND_SCRIPT); } @@ -4601,6 +4668,14 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec filter_hbc->add_child(button_detach_script); button_detach_script->hide(); + button_extend_script = memnew(Button); + button_extend_script->set_flat(true); + button_extend_script->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_EXTEND_SCRIPT, false)); + button_extend_script->set_tooltip_text(TTR("Extend the script of the selected node.")); + button_extend_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/extend_script")); + filter_hbc->add_child(button_extend_script); + button_extend_script->hide(); + button_tree_menu = memnew(MenuButton); button_tree_menu->set_flat(false); button_tree_menu->set_theme_type_variation("FlatMenuButton"); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 05ad0f36e4..8cee2870f6 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -115,6 +115,7 @@ class SceneTreeDock : public VBoxContainer { Button *button_instance = nullptr; Button *button_create_script = nullptr; Button *button_detach_script = nullptr; + Button *button_extend_script = nullptr; MenuButton *button_tree_menu = nullptr; Button *node_shortcuts_toggle = nullptr; diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index cdc4087142..4db43f0703 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -633,6 +633,16 @@ void EditorThemeManager::_create_shared_styles(const Ref<EditorTheme> &p_theme, // in 4.0, and even if it was, it may not always work in practice (e.g. running with compositing disabled). p_config.popup_style->set_corner_radius_all(0); + p_config.popup_border_style = p_config.popup_style->duplicate(); + p_config.popup_border_style->set_content_margin_all(MAX(Math::round(EDSCALE), p_config.border_width) + 2 + (p_config.base_margin * 1.5) * EDSCALE); + // Always display a border for popups like PopupMenus so they can be distinguished from their background. + p_config.popup_border_style->set_border_width_all(MAX(Math::round(EDSCALE), p_config.border_width)); + if (p_config.draw_extra_borders) { + p_config.popup_border_style->set_border_color(p_config.extra_border_color_2); + } else { + p_config.popup_border_style->set_border_color(p_config.dark_color_2); + } + p_config.window_style = p_config.popup_style->duplicate(); p_config.window_style->set_border_color(p_config.base_color); p_config.window_style->set_border_width(SIDE_TOP, 24 * EDSCALE); @@ -707,7 +717,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the } // PopupPanel - p_theme->set_stylebox(SceneStringName(panel), "PopupPanel", p_config.popup_style); + p_theme->set_stylebox(SceneStringName(panel), "PopupPanel", p_config.popup_border_style); } // Buttons. @@ -1314,18 +1324,11 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the // PopupMenu. { - Ref<StyleBoxFlat> style_popup_menu = p_config.popup_style->duplicate(); + Ref<StyleBoxFlat> style_popup_menu = p_config.popup_border_style->duplicate(); // Use 1 pixel for the sides, since if 0 is used, the highlight of hovered items is drawn // on top of the popup border. This causes a 'gap' in the panel border when an item is highlighted, // and it looks weird. 1px solves this. - style_popup_menu->set_content_margin_individual(EDSCALE, 2 * EDSCALE, EDSCALE, 2 * EDSCALE); - // Always display a border for PopupMenus so they can be distinguished from their background. - style_popup_menu->set_border_width_all(EDSCALE); - if (p_config.draw_extra_borders) { - style_popup_menu->set_border_color(p_config.extra_border_color_2); - } else { - style_popup_menu->set_border_color(p_config.dark_color_2); - } + style_popup_menu->set_content_margin_individual(Math::round(EDSCALE), 2 * EDSCALE, Math::round(EDSCALE), 2 * EDSCALE); p_theme->set_stylebox(SceneStringName(panel), "PopupMenu", style_popup_menu); Ref<StyleBoxFlat> style_menu_hover = p_config.button_style_hover->duplicate(); @@ -1335,17 +1338,17 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the Ref<StyleBoxLine> style_popup_separator(memnew(StyleBoxLine)); style_popup_separator->set_color(p_config.separator_color); - style_popup_separator->set_grow_begin(p_config.popup_margin - MAX(Math::round(EDSCALE), p_config.border_width)); - style_popup_separator->set_grow_end(p_config.popup_margin - MAX(Math::round(EDSCALE), p_config.border_width)); + style_popup_separator->set_grow_begin(Math::round(EDSCALE) - MAX(Math::round(EDSCALE), p_config.border_width)); + style_popup_separator->set_grow_end(Math::round(EDSCALE) - MAX(Math::round(EDSCALE), p_config.border_width)); style_popup_separator->set_thickness(MAX(Math::round(EDSCALE), p_config.border_width)); Ref<StyleBoxLine> style_popup_labeled_separator_left(memnew(StyleBoxLine)); - style_popup_labeled_separator_left->set_grow_begin(p_config.popup_margin - MAX(Math::round(EDSCALE), p_config.border_width)); + style_popup_labeled_separator_left->set_grow_begin(Math::round(EDSCALE) - MAX(Math::round(EDSCALE), p_config.border_width)); style_popup_labeled_separator_left->set_color(p_config.separator_color); style_popup_labeled_separator_left->set_thickness(MAX(Math::round(EDSCALE), p_config.border_width)); Ref<StyleBoxLine> style_popup_labeled_separator_right(memnew(StyleBoxLine)); - style_popup_labeled_separator_right->set_grow_end(p_config.popup_margin - MAX(Math::round(EDSCALE), p_config.border_width)); + style_popup_labeled_separator_right->set_grow_end(Math::round(EDSCALE) - MAX(Math::round(EDSCALE), p_config.border_width)); style_popup_labeled_separator_right->set_color(p_config.separator_color); style_popup_labeled_separator_right->set_thickness(MAX(Math::round(EDSCALE), p_config.border_width)); @@ -2123,21 +2126,6 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme // EditorValidationPanel. p_theme->set_stylebox(SceneStringName(panel), "EditorValidationPanel", p_config.tree_panel_style); - - // ControlEditor. - { - p_theme->set_type_variation("ControlEditorPopupPanel", "PopupPanel"); - - Ref<StyleBoxFlat> control_editor_popup_style = p_config.popup_style->duplicate(); - control_editor_popup_style->set_shadow_size(0); - control_editor_popup_style->set_content_margin(SIDE_LEFT, p_config.base_margin * EDSCALE); - control_editor_popup_style->set_content_margin(SIDE_TOP, p_config.base_margin * EDSCALE); - control_editor_popup_style->set_content_margin(SIDE_RIGHT, p_config.base_margin * EDSCALE); - control_editor_popup_style->set_content_margin(SIDE_BOTTOM, p_config.base_margin * EDSCALE); - control_editor_popup_style->set_border_width_all(0); - - p_theme->set_stylebox(SceneStringName(panel), "ControlEditorPopupPanel", control_editor_popup_style); - } } // Editor inspector. diff --git a/editor/themes/editor_theme_manager.h b/editor/themes/editor_theme_manager.h index 5e7bd00083..ca5e1a4e2d 100644 --- a/editor/themes/editor_theme_manager.h +++ b/editor/themes/editor_theme_manager.h @@ -135,6 +135,7 @@ class EditorThemeManager { Ref<StyleBoxFlat> button_style_hover; Ref<StyleBoxFlat> popup_style; + Ref<StyleBoxFlat> popup_border_style; Ref<StyleBoxFlat> window_style; Ref<StyleBoxFlat> dialog_style; Ref<StyleBoxFlat> panel_container_style; diff --git a/editor/window_wrapper.cpp b/editor/window_wrapper.cpp index 9496ba016c..7f61623980 100644 --- a/editor/window_wrapper.cpp +++ b/editor/window_wrapper.cpp @@ -391,7 +391,6 @@ void ScreenSelect::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { set_icon(get_editor_theme_icon("MakeFloating")); - popup_background->add_theme_style_override(SceneStringName(panel), get_theme_stylebox("PanelForeground", EditorStringName(EditorStyles))); const real_t popup_height = real_t(get_theme_font_size(SceneStringName(font_size))) * 2.0; popup->set_min_size(Size2(0, popup_height * 3)); @@ -454,14 +453,10 @@ ScreenSelect::ScreenSelect() { // Create the popup. const Size2 borders = Size2(4, 4) * EDSCALE; - popup = memnew(Popup); + popup = memnew(PopupPanel); popup->connect("popup_hide", callable_mp(static_cast<BaseButton *>(this), &ScreenSelect::set_pressed).bind(false)); add_child(popup); - popup_background = memnew(Panel); - popup_background->set_anchors_and_offsets_preset(PRESET_FULL_RECT); - popup->add_child(popup_background); - MarginContainer *popup_root = memnew(MarginContainer); popup_root->add_theme_constant_override("margin_right", borders.width); popup_root->add_theme_constant_override("margin_top", borders.height); diff --git a/editor/window_wrapper.h b/editor/window_wrapper.h index a07e95f09e..3597276de9 100644 --- a/editor/window_wrapper.h +++ b/editor/window_wrapper.h @@ -88,7 +88,6 @@ class ScreenSelect : public Button { GDCLASS(ScreenSelect, Button); Popup *popup = nullptr; - Panel *popup_background = nullptr; HBoxContainer *screen_list = nullptr; void _build_advanced_menu(); diff --git a/modules/betsy/image_compress_betsy.cpp b/modules/betsy/image_compress_betsy.cpp index 6bfe01f65c..1ad9bed721 100644 --- a/modules/betsy/image_compress_betsy.cpp +++ b/modules/betsy/image_compress_betsy.cpp @@ -37,11 +37,16 @@ #include "bc1.glsl.gen.h" #include "bc4.glsl.gen.h" #include "bc6h.glsl.gen.h" +#include "servers/display_server.h" static Mutex betsy_mutex; static BetsyCompressor *betsy = nullptr; void BetsyCompressor::_init() { + if (!DisplayServer::can_create_rendering_device()) { + return; + } + // Create local RD. RenderingContextDriver *rcd = nullptr; RenderingDevice *rd = RenderingServer::get_singleton()->create_local_rendering_device(); @@ -182,6 +187,11 @@ static String get_shader_name(BetsyFormat p_format) { Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) { uint64_t start_time = OS::get_singleton()->get_ticks_msec(); + // Return an error so that the compression can fall back to cpu compression + if (compress_rd == nullptr) { + return ERR_CANT_CREATE; + } + if (r_img->is_compressed()) { return ERR_INVALID_DATA; } diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 5fe47d69df..21365da7cc 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -4,8 +4,8 @@ Built-in GDScript constants, functions, and annotations. </brief_description> <description> - A list of GDScript-specific utility functions and annotations accessible from any script. - For the list of the global functions and constants see [@GlobalScope]. + A list of utility functions and annotations accessible from any script written in GDScript. + For the list of global functions and constants that can be accessed in any scripting language, see [@GlobalScope]. </description> <tutorials> <link title="GDScript exports">$DOCS_URL/tutorials/scripting/gdscript/gdscript_exports.html</link> diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index d903137195..b9d493b844 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -104,51 +104,33 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const ui picture->size(width, height); std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen(); - // Note: memalloc here, be sure to memfree before any return. - uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * width * height); + Vector<uint8_t> buffer; + buffer.resize(sizeof(uint32_t) * width * height); - tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888S); + tvg::Result res = sw_canvas->target((uint32_t *)buffer.ptrw(), width, width, height, tvg::SwCanvas::ABGR8888S); if (res != tvg::Result::Success) { - memfree(buffer); ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't set target on ThorVG canvas."); } res = sw_canvas->push(std::move(picture)); if (res != tvg::Result::Success) { - memfree(buffer); ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't insert ThorVG picture on canvas."); } res = sw_canvas->draw(); if (res != tvg::Result::Success) { - memfree(buffer); ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't draw ThorVG pictures on canvas."); } res = sw_canvas->sync(); if (res != tvg::Result::Success) { - memfree(buffer); ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't sync ThorVG canvas."); } - Vector<uint8_t> image; - image.resize(width * height * sizeof(uint32_t)); - - for (uint32_t y = 0; y < height; y++) { - for (uint32_t x = 0; x < width; x++) { - uint32_t n = buffer[y * width + x]; - const size_t offset = sizeof(uint32_t) * width * y + sizeof(uint32_t) * x; - image.write[offset + 0] = (n >> 16) & 0xff; - image.write[offset + 1] = (n >> 8) & 0xff; - image.write[offset + 2] = n & 0xff; - image.write[offset + 3] = (n >> 24) & 0xff; - } - } + p_image->set_data(width, height, false, Image::FORMAT_RGBA8, buffer); res = sw_canvas->clear(true); - memfree(buffer); - p_image->set_data(width, height, false, Image::FORMAT_RGBA8, image); return OK; } diff --git a/platform/android/detect.py b/platform/android/detect.py index 0a10754e24..233e74364f 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -171,9 +171,7 @@ def configure(env: "SConsEnvironment"): env["AS"] = compiler_path + "/clang" env.Append( - CCFLAGS=( - "-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split() - ) + CCFLAGS=("-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden".split()) ) if get_min_sdk_version(env["ndk_platform"]) >= 24: diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 20a3a996bc..317dbd3f4a 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -134,7 +134,7 @@ def configure(env: "SConsEnvironment"): elif env["arch"] == "arm64": env.Append( CCFLAGS=( - "-fobjc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing" + "-fobjc-arc -arch arm64 -fmessage-length=0" " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits" " -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies" " -isysroot $IOS_SDK_PATH".split() diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 0ee52a09a7..4b1fde99cf 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -483,9 +483,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): else: print_warning("Missing environment variable: WindowsSdkDir") - if int(env["target_win_version"], 16) < 0x0601: - print_error("`target_win_version` should be 0x0601 or higher (Windows 7).") - sys.exit(255) + validate_win_version(env) env.AppendUnique( CPPDEFINES=[ @@ -549,15 +547,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): LIBS += ["vulkan"] if env["d3d12"]: - # Check whether we have d3d12 dependencies installed. - if not os.path.exists(env["mesa_libs"]): - print_error( - "The Direct3D 12 rendering driver requires dependencies to be installed.\n" - "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n" - "See the documentation for more information:\n\t" - "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html" - ) - sys.exit(255) + check_d3d12_installed(env) env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"]) LIBS += ["dxgi", "dxguid"] @@ -820,9 +810,7 @@ def configure_mingw(env: "SConsEnvironment"): ## Compile flags - if int(env["target_win_version"], 16) < 0x0601: - print_error("`target_win_version` should be 0x0601 or higher (Windows 7).") - sys.exit(255) + validate_win_version(env) if not env["use_llvm"]: env.Append(CCFLAGS=["-mwindows"]) @@ -900,15 +888,7 @@ def configure_mingw(env: "SConsEnvironment"): env.Append(LIBS=["vulkan"]) if env["d3d12"]: - # Check whether we have d3d12 dependencies installed. - if not os.path.exists(env["mesa_libs"]): - print_error( - "The Direct3D 12 rendering driver requires dependencies to be installed.\n" - "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n" - "See the documentation for more information:\n\t" - "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html" - ) - sys.exit(255) + check_d3d12_installed(env) env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"]) env.Append(LIBS=["dxgi", "dxguid"]) @@ -984,3 +964,20 @@ def configure(env: "SConsEnvironment"): else: # MinGW configure_mingw(env) + + +def check_d3d12_installed(env): + if not os.path.exists(env["mesa_libs"]): + print_error( + "The Direct3D 12 rendering driver requires dependencies to be installed.\n" + "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n" + "See the documentation for more information:\n\t" + "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html" + ) + sys.exit(255) + + +def validate_win_version(env): + if int(env["target_win_version"], 16) < 0x0601: + print_error("`target_win_version` should be 0x0601 or higher (Windows 7).") + sys.exit(255) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 21b318ee4d..c427596829 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -6400,7 +6400,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID); - ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); + if (main_window == INVALID_WINDOW_ID) { + r_error = ERR_UNAVAILABLE; + ERR_FAIL_MSG("Failed to create main window."); + } joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index c0386b056f..7c8bf9c809 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -1253,7 +1253,7 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("force_update_transform"), &CanvasItem::force_update_transform); - ClassDB::bind_method(D_METHOD("make_canvas_position_local", "screen_point"), &CanvasItem::make_canvas_position_local); + ClassDB::bind_method(D_METHOD("make_canvas_position_local", "viewport_point"), &CanvasItem::make_canvas_position_local); ClassDB::bind_method(D_METHOD("make_input_local", "event"), &CanvasItem::make_input_local); ClassDB::bind_method(D_METHOD("set_visibility_layer", "layer"), &CanvasItem::set_visibility_layer); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 0cdb23618f..8755d5f51e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1921,7 +1921,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(mpos)); tooltip = tooltip.strip_edges(); - if (tooltip.is_empty() || tooltip != gui.tooltip_text) { + if (tooltip != gui.tooltip_text) { _gui_cancel_tooltip(); } else { is_tooltip_shown = true; diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp index 94a037bd9b..f068e34beb 100644 --- a/scene/property_utils.cpp +++ b/scene/property_utils.cpp @@ -89,6 +89,16 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const *r_is_valid = false; } + // Handle special case "script" property, where the default value is either null or the custom type script. + // Do this only if there's no states stack cache to trace for default values. + if (!p_states_stack_cache && p_property == CoreStringName(script) && p_object->has_meta(SceneStringName(_custom_type_script))) { + Ref<Script> ct_scr = p_object->get_meta(SceneStringName(_custom_type_script)); + if (r_is_valid) { + *r_is_valid = true; + } + return ct_scr; + } + Ref<Script> topmost_script; if (const Node *node = Object::cast_to<Node>(p_object)) { diff --git a/scene/resources/external_texture.cpp b/scene/resources/external_texture.cpp index 0552bbd081..c8b714372a 100644 --- a/scene/resources/external_texture.cpp +++ b/scene/resources/external_texture.cpp @@ -39,12 +39,14 @@ void ExternalTexture::_bind_methods() { } uint64_t ExternalTexture::get_external_texture_id() const { + _ensure_created(); return RenderingServer::get_singleton()->texture_get_native_handle(texture); } void ExternalTexture::set_size(const Size2 &p_size) { if (p_size.width > 0 && p_size.height > 0 && p_size != size) { size = p_size; + _ensure_created(); RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer); emit_changed(); } @@ -57,6 +59,7 @@ Size2 ExternalTexture::get_size() const { void ExternalTexture::set_external_buffer_id(uint64_t p_external_buffer) { if (p_external_buffer != external_buffer) { external_buffer = p_external_buffer; + _ensure_created(); RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer); } } @@ -74,11 +77,29 @@ bool ExternalTexture::has_alpha() const { } RID ExternalTexture::get_rid() const { + if (!texture.is_valid()) { + texture = RenderingServer::get_singleton()->texture_2d_placeholder_create(); + using_placeholder = true; + } return texture; } +void ExternalTexture::_ensure_created() const { + if (texture.is_valid() && !using_placeholder) { + return; + } + + RID new_texture = RenderingServer::get_singleton()->texture_external_create(size.width, size.height); + if (using_placeholder) { + DEV_ASSERT(texture.is_valid()); + RenderingServer::get_singleton()->texture_replace(texture, new_texture); + using_placeholder = false; + } else { + texture = new_texture; + } +} + ExternalTexture::ExternalTexture() { - texture = RenderingServer::get_singleton()->texture_external_create(size.width, size.height); } ExternalTexture::~ExternalTexture() { diff --git a/scene/resources/external_texture.h b/scene/resources/external_texture.h index 96bcd8d0fe..cd60bcc030 100644 --- a/scene/resources/external_texture.h +++ b/scene/resources/external_texture.h @@ -38,10 +38,13 @@ class ExternalTexture : public Texture2D { GDCLASS(ExternalTexture, Texture2D); private: - RID texture; + mutable RID texture; + mutable bool using_placeholder = false; Size2 size = Size2(256, 256); uint64_t external_buffer = 0; + void _ensure_created() const; + protected: static void _bind_methods(); diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 140e588291..31daeb3ae3 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -130,6 +130,8 @@ SceneStringNames::SceneStringNames() { shader_overrides_group = StaticCString::create("_shader_overrides_group_"); shader_overrides_group_active = StaticCString::create("_shader_overrides_group_active_"); + _custom_type_script = StaticCString::create("_custom_type_script"); + pressed = StaticCString::create("pressed"); id_pressed = StaticCString::create("id_pressed"); toggled = StaticCString::create("toggled"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index fc22be33b2..0a2ebeda7a 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -143,6 +143,8 @@ public: StringName shader_overrides_group; StringName shader_overrides_group_active; + StringName _custom_type_script; + StringName pressed; StringName id_pressed; StringName toggled; diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 1886ebe1ac..d400b5790f 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -76,6 +76,42 @@ int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_fra return ret; } +PackedVector2Array AudioStreamPlayback::_mix_audio_bind(float p_rate_scale, int p_frames) { + Vector<AudioFrame> frames = mix_audio(p_rate_scale, p_frames); + + PackedVector2Array res; + res.resize(frames.size()); + + Vector2 *res_ptrw = res.ptrw(); + for (int i = 0; i < frames.size(); i++) { + res_ptrw[i] = Vector2(frames[i].left, frames[i].right); + } + + return res; +} + +Vector<AudioFrame> AudioStreamPlayback::mix_audio(float p_rate_scale, int p_frames) { + Vector<AudioFrame> res; + res.resize(p_frames); + + int frames = mix(res.ptrw(), p_rate_scale, p_frames); + res.resize(frames); + + return res; +} + +void AudioStreamPlayback::start_playback(double p_from_pos) { + start(p_from_pos); +} + +void AudioStreamPlayback::stop_playback() { + stop(); +} + +void AudioStreamPlayback::seek_playback(double p_time) { + seek(p_time); +} + void AudioStreamPlayback::tag_used_streams() { GDVIRTUAL_CALL(_tag_used_streams); } @@ -108,6 +144,13 @@ void AudioStreamPlayback::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sample_playback", "playback_sample"), &AudioStreamPlayback::set_sample_playback); ClassDB::bind_method(D_METHOD("get_sample_playback"), &AudioStreamPlayback::get_sample_playback); + ClassDB::bind_method(D_METHOD("mix_audio", "rate_scale", "frames"), &AudioStreamPlayback::_mix_audio_bind); + ClassDB::bind_method(D_METHOD("start", "from_pos"), &AudioStreamPlayback::start_playback, DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("seek", "time"), &AudioStreamPlayback::seek_playback, DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayback::stop_playback); + ClassDB::bind_method(D_METHOD("get_loop_count"), &AudioStreamPlayback::get_loop_count); + ClassDB::bind_method(D_METHOD("get_playback_position"), &AudioStreamPlayback::get_playback_position); + ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayback::is_playing); } AudioStreamPlayback::AudioStreamPlayback() {} diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index 3feaa53630..65efccdc28 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -83,6 +83,7 @@ class AudioStreamPlayback : public RefCounted { protected: static void _bind_methods(); + PackedVector2Array _mix_audio_bind(float p_rate_scale, int p_frames); GDVIRTUAL1(_start, double) GDVIRTUAL0(_stop) GDVIRTUAL0RC(bool, _is_playing) @@ -118,6 +119,11 @@ public: AudioStreamPlayback(); ~AudioStreamPlayback(); + + Vector<AudioFrame> mix_audio(float p_rate_scale, int p_frames); + void start_playback(double p_from_pos = 0.0); + void stop_playback(); + void seek_playback(double p_time); }; class AudioStreamPlaybackResampled : public AudioStreamPlayback { diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 8bfe95aa76..428fb77c74 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -1230,6 +1230,10 @@ void DisplayServer::_input_set_custom_mouse_cursor_func(const Ref<Resource> &p_i } bool DisplayServer::can_create_rendering_device() { + if (get_singleton()->get_name() == "headless") { + return false; + } + #if defined(RD_ENABLED) RenderingDevice *device = RenderingDevice::get_singleton(); if (device) { diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index 2dfcd67411..903d73ff2b 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -622,6 +622,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); bool any_uses_time = false; + Vector3 cam_position = p_cam_transform.get_origin(); for (int i = 0; i < (int)p_fog_volumes.size(); i++) { FogVolumeInstance *fog_volume_instance = fog_volume_instance_owner.get_or_null(p_fog_volumes[i]); @@ -652,41 +653,68 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P any_uses_time |= shader_data->uses_time; - Vector3i min; - Vector3i max; + Vector3i froxel_min; + Vector3i froxel_max; Vector3i kernel_size; - Vector3 position = fog_volume_instance->transform.get_origin(); + Vector3 fog_position = fog_volume_instance->transform.get_origin(); RS::FogVolumeShape volume_type = RendererRD::Fog::get_singleton()->fog_volume_get_shape(fog_volume); Vector3 extents = RendererRD::Fog::get_singleton()->fog_volume_get_size(fog_volume) / 2; if (volume_type != RS::FOG_VOLUME_SHAPE_WORLD) { // Local fog volume. - Vector3i points[8]; Vector3 fog_size = Vector3(fog->width, fog->height, fog->depth); float volumetric_fog_detail_spread = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_detail_spread(p_settings.env); - points[0] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(extents.x, extents.y, extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - points[1] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(-extents.x, extents.y, extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - points[2] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(extents.x, -extents.y, extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - points[3] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(-extents.x, -extents.y, extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - points[4] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(extents.x, extents.y, -extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - points[5] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(-extents.x, extents.y, -extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - points[6] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(extents.x, -extents.y, -extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - points[7] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(-extents.x, -extents.y, -extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - - min = Vector3i(int32_t(fog->width) - 1, int32_t(fog->height) - 1, int32_t(fog->depth) - 1); - max = Vector3i(1, 1, 1); - + Vector3 corners[8]{ + fog_volume_instance->transform.xform(Vector3(extents.x, extents.y, extents.z)), + fog_volume_instance->transform.xform(Vector3(-extents.x, extents.y, extents.z)), + fog_volume_instance->transform.xform(Vector3(extents.x, -extents.y, extents.z)), + fog_volume_instance->transform.xform(Vector3(-extents.x, -extents.y, extents.z)), + fog_volume_instance->transform.xform(Vector3(extents.x, extents.y, -extents.z)), + fog_volume_instance->transform.xform(Vector3(-extents.x, extents.y, -extents.z)), + fog_volume_instance->transform.xform(Vector3(extents.x, -extents.y, -extents.z)), + fog_volume_instance->transform.xform(Vector3(-extents.x, -extents.y, -extents.z)) + }; + Vector3i froxels[8]; + Vector3 corner_min = corners[0]; + Vector3 corner_max = corners[0]; for (int j = 0; j < 8; j++) { - min = min.min(points[j]); - max = max.max(points[j]); + froxels[j] = _point_get_position_in_froxel_volume(corners[j], fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); + corner_min = corner_min.min(corners[j]); + corner_max = corner_max.max(corners[j]); + } + + froxel_min = Vector3i(int32_t(fog->width) - 1, int32_t(fog->height) - 1, int32_t(fog->depth) - 1); + froxel_max = Vector3i(1, 1, 1); + + // Tracking just the corners of the fog volume can result in missing some fog: + // when the camera's near plane is inside the fog, we must always consider the entire screen + Vector3 near_plane_corner(frustum_near_size.x, frustum_near_size.y, z_near); + float expand = near_plane_corner.length(); + if (cam_position.x > (corner_min.x - expand) && cam_position.x < (corner_max.x + expand) && + cam_position.y > (corner_min.y - expand) && cam_position.y < (corner_max.y + expand) && + cam_position.z > (corner_min.z - expand) && cam_position.z < (corner_max.z + expand)) { + froxel_min.x = 0; + froxel_min.y = 0; + froxel_min.z = 0; + froxel_max.x = int32_t(fog->width); + froxel_max.y = int32_t(fog->height); + for (int j = 0; j < 8; j++) { + froxel_max.z = MAX(froxel_max.z, froxels[j].z); + } + } else { + // Camera is guaranteed to be outside the fog volume + for (int j = 0; j < 8; j++) { + froxel_min = froxel_min.min(froxels[j]); + froxel_max = froxel_max.max(froxels[j]); + } } - kernel_size = max - min; + kernel_size = froxel_max - froxel_min; } else { // Volume type global runs on all cells extents = Vector3(fog->width, fog->height, fog->depth); - min = Vector3i(0, 0, 0); + froxel_min = Vector3i(0, 0, 0); kernel_size = Vector3i(int32_t(fog->width), int32_t(fog->height), int32_t(fog->depth)); } @@ -695,15 +723,15 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P } VolumetricFogShader::FogPushConstant push_constant; - push_constant.position[0] = position.x; - push_constant.position[1] = position.y; - push_constant.position[2] = position.z; + push_constant.position[0] = fog_position.x; + push_constant.position[1] = fog_position.y; + push_constant.position[2] = fog_position.z; push_constant.size[0] = extents.x * 2; push_constant.size[1] = extents.y * 2; push_constant.size[2] = extents.z * 2; - push_constant.corner[0] = min.x; - push_constant.corner[1] = min.y; - push_constant.corner[2] = min.z; + push_constant.corner[0] = froxel_min.x; + push_constant.corner[1] = froxel_min.y; + push_constant.corner[2] = froxel_min.z; push_constant.shape = uint32_t(RendererRD::Fog::get_singleton()->fog_volume_get_shape(fog_volume)); RendererRD::MaterialStorage::store_transform(fog_volume_instance->transform.affine_inverse(), push_constant.transform); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 942d5631e4..6eb1386749 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -7260,6 +7260,10 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(DEBUG_PASS); } +void RenderingDevice::make_current() { + render_thread_id = Thread::get_caller_id(); +} + RenderingDevice::~RenderingDevice() { finalize(); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 71ffbfbd88..c440e11cd4 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1496,6 +1496,8 @@ public: static RenderingDevice *get_singleton(); + void make_current(); + RenderingDevice(); ~RenderingDevice(); diff --git a/servers/rendering/rendering_device_binds.cpp b/servers/rendering/rendering_device_binds.cpp index d9ca286b15..e41a56b0a3 100644 --- a/servers/rendering/rendering_device_binds.cpp +++ b/servers/rendering/rendering_device_binds.cpp @@ -31,7 +31,10 @@ #include "rendering_device_binds.h" Error RDShaderFile::parse_versions_from_text(const String &p_text, const String p_defines, OpenIncludeFunction p_include_func, void *p_include_func_userdata) { - ERR_FAIL_NULL_V(RenderingDevice::get_singleton(), ERR_UNAVAILABLE); + ERR_FAIL_NULL_V_MSG( + RenderingDevice::get_singleton(), + ERR_UNAVAILABLE, + "Cannot import custom .glsl shaders when running without a RenderingDevice. This can happen if you are using the headless more or the Compatibility backend."); Vector<String> lines = p_text.split("\n"); diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 20f1f9ad6f..2ec693cbbf 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -370,6 +370,8 @@ Size2i RenderingServerDefault::get_maximum_viewport_size() const { void RenderingServerDefault::_assign_mt_ids(WorkerThreadPool::TaskID p_pump_task_id) { server_thread = Thread::get_caller_id(); server_task_id = p_pump_task_id; + // This is needed because the main RD is created on the main thread. + RenderingDevice::get_singleton()->make_current(); } void RenderingServerDefault::_thread_exit() { diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index b6770c773c..7c4128b0e3 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -356,7 +356,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_CF_BREAK, "break", CF_BLOCK, {}, {} }, { TK_CF_CONTINUE, "continue", CF_BLOCK, {}, {} }, { TK_CF_RETURN, "return", CF_BLOCK, {}, {} }, - { TK_CF_DISCARD, "discard", CF_BLOCK, { "particles", "sky", "fog" }, { "fragment" } }, + { TK_CF_DISCARD, "discard", CF_BLOCK, { "particles", "sky", "fog" }, { "vertex" } }, // function specifier keywords @@ -3565,28 +3565,33 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI int argcount = args.size(); - if (p_function_info.stage_functions.has(name)) { - //stage based function - const StageFunctionInfo &sf = p_function_info.stage_functions[name]; - if (argcount != sf.arguments.size()) { - _set_error(vformat(RTR("Invalid number of arguments when calling stage function '%s', which expects %d arguments."), String(name), sf.arguments.size())); - return false; - } - //validate arguments - for (int i = 0; i < argcount; i++) { - if (args[i] != sf.arguments[i].type) { - _set_error(vformat(RTR("Invalid argument type when calling stage function '%s', type expected is '%s'."), String(name), get_datatype_name(sf.arguments[i].type))); - return false; - } - } + if (stages) { + // Stage functions can be used in custom functions as well, that why need to check them all. + for (const KeyValue<StringName, FunctionInfo> &E : *stages) { + if (E.value.stage_functions.has(name)) { + // Stage-based function. + const StageFunctionInfo &sf = E.value.stage_functions[name]; + if (argcount != sf.arguments.size()) { + _set_error(vformat(RTR("Invalid number of arguments when calling stage function '%s', which expects %d arguments."), String(name), sf.arguments.size())); + return false; + } + // Validate arguments. + for (int i = 0; i < argcount; i++) { + if (args[i] != sf.arguments[i].type) { + _set_error(vformat(RTR("Invalid argument type when calling stage function '%s', type expected is '%s'."), String(name), get_datatype_name(sf.arguments[i].type))); + return false; + } + } - if (r_ret_type) { - *r_ret_type = sf.return_type; - } - if (r_ret_type_str) { - *r_ret_type_str = ""; + if (r_ret_type) { + *r_ret_type = sf.return_type; + } + if (r_ret_type_str) { + *r_ret_type_str = ""; + } + return true; + } } - return true; } bool failed_builtin = false; @@ -5937,22 +5942,35 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons calls_info[current_function].calls.push_back(&calls_info[name]); } - int idx = 0; bool is_builtin = false; - while (frag_only_func_defs[idx].name) { - if (frag_only_func_defs[idx].name == name) { - // If a built-in function not found for the current shader type, then it shouldn't be parsed further. - if (!is_supported_frag_only_funcs) { - _set_error(vformat(RTR("Built-in function '%s' is not supported for the '%s' shader type."), name, shader_type_identifier)); - return nullptr; + if (is_supported_frag_only_funcs && stages) { + for (const KeyValue<StringName, FunctionInfo> &E : *stages) { + if (E.value.stage_functions.has(name)) { + // Register usage of the restricted stage function. + calls_info[current_function].uses_restricted_items.push_back(Pair<StringName, CallInfo::Item>(name, CallInfo::Item(CallInfo::Item::ITEM_TYPE_BUILTIN, _get_tkpos()))); + is_builtin = true; + break; } - // Register usage of the restricted function. - calls_info[current_function].uses_restricted_items.push_back(Pair<StringName, CallInfo::Item>(name, CallInfo::Item(CallInfo::Item::ITEM_TYPE_BUILTIN, _get_tkpos()))); - is_builtin = true; - break; } - idx++; + } + + if (!is_builtin) { + int idx = 0; + while (frag_only_func_defs[idx].name) { + if (frag_only_func_defs[idx].name == name) { + // If a built-in function not found for the current shader type, then it shouldn't be parsed further. + if (!is_supported_frag_only_funcs) { + _set_error(vformat(RTR("Built-in function '%s' is not supported for the '%s' shader type."), name, shader_type_identifier)); + return nullptr; + } + // Register usage of the restricted function. + calls_info[current_function].uses_restricted_items.push_back(Pair<StringName, CallInfo::Item>(name, CallInfo::Item(CallInfo::Item::ITEM_TYPE_BUILTIN, _get_tkpos()))); + is_builtin = true; + break; + } + idx++; + } } // Recursively checks for the restricted function call. @@ -8581,6 +8599,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun block = block->parent_block; } } else if (tk.type == TK_CF_DISCARD) { + if (!is_discard_supported) { + _set_error(vformat(RTR("Use of '%s' is not supported for the '%s' shader type."), "discard", shader_type_identifier)); + return ERR_PARSE_ERROR; + } + //check return type BlockNode *b = p_block; while (b && !b->parent_function) { @@ -8592,7 +8615,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } if (!b->parent_function->can_discard) { - _set_error(vformat(RTR("Use of '%s' is not allowed here."), "discard")); + _set_error(vformat(RTR("'%s' cannot be used within the '%s' processor function."), "discard", b->parent_function->name)); return ERR_PARSE_ERROR; } @@ -8601,6 +8624,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun pos = _get_tkpos(); tk = _get_token(); + + calls_info[b->parent_function->name].uses_restricted_items.push_back(Pair<StringName, CallInfo::Item>("discard", CallInfo::Item(CallInfo::Item::ITEM_TYPE_BUILTIN, pos))); + if (tk.type != TK_SEMICOLON) { _set_expected_after_error(";", "discard"); return ERR_PARSE_ERROR; @@ -8838,7 +8864,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; stages = &p_functions; - is_supported_frag_only_funcs = shader_type_identifier == "canvas_item" || shader_type_identifier == "spatial" || shader_type_identifier == "sky"; + + is_discard_supported = shader_type_identifier == "canvas_item" || shader_type_identifier == "spatial"; + is_supported_frag_only_funcs = is_discard_supported || shader_type_identifier == "sky"; const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo(); @@ -10332,6 +10360,8 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f if (p_functions.has(name)) { func_node->can_discard = p_functions[name].can_discard; + } else { + func_node->can_discard = is_discard_supported; // Allow use it for custom functions (in supported shader types). } if (!function_overload_count.has(name)) { @@ -10922,10 +10952,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ break; // Ignore hint keywords (parsed below). } if (keyword_list[i].flags & keyword_completion_context) { - if (keyword_list[i].excluded_shader_types.has(shader_type_identifier)) { - continue; - } - if (!keyword_list[i].functions.is_empty() && !keyword_list[i].functions.has(current_function)) { + if (keyword_list[i].excluded_shader_types.has(shader_type_identifier) || keyword_list[i].excluded_functions.has(current_function)) { continue; } ScriptLanguage::CodeCompletionOption option(keyword_list[i].text, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); @@ -11160,9 +11187,15 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ int idx = 0; bool low_end = RenderingServer::get_singleton()->is_low_end(); - if (stages && stages->has(skip_function)) { - for (const KeyValue<StringName, StageFunctionInfo> &E : (*stages)[skip_function].stage_functions) { - matches.insert(String(E.key), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); + if (stages) { + // Stage functions can be used in custom functions as well, that why need to check them all. + for (const KeyValue<StringName, FunctionInfo> &E : *stages) { + for (const KeyValue<StringName, StageFunctionInfo> &F : E.value.stage_functions) { + if (F.value.skip_function == skip_function && stages->has(skip_function)) { + continue; + } + matches.insert(String(F.key), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); + } } } @@ -11292,9 +11325,15 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ return OK; } - if (stages && stages->has(block_function)) { - for (const KeyValue<StringName, StageFunctionInfo> &E : (*stages)[block_function].stage_functions) { - if (completion_function == E.key) { + if (stages) { + // Stage functions can be used in custom functions as well, that why need to check them all. + for (const KeyValue<StringName, FunctionInfo> &S : *stages) { + for (const KeyValue<StringName, StageFunctionInfo> &E : S.value.stage_functions) { + // No need to check for the skip function here. + if (completion_function != E.key) { + continue; + } + calltip += get_datatype_name(E.value.return_type); calltip += " "; calltip += E.key; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index fb0a526230..ddd4c41059 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -859,6 +859,7 @@ public: Vector<Argument> arguments; DataType return_type = TYPE_VOID; + String skip_function; }; struct ModeInfo { @@ -934,7 +935,7 @@ private: const char *text; uint32_t flags; const Vector<String> excluded_shader_types; - const Vector<String> functions; + const Vector<String> excluded_functions; }; static const KeyWord keyword_list[]; @@ -1150,6 +1151,7 @@ private: const HashMap<StringName, FunctionInfo> *stages = nullptr; bool is_supported_frag_only_funcs = false; + bool is_discard_supported = false; bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier); static const BuiltinFuncDef builtin_func_defs[]; diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index f498c0bf93..9ccfe2f9d7 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -284,6 +284,7 @@ ShaderTypes::ShaderTypes() { { ShaderLanguage::StageFunctionInfo func; + func.skip_function = "vertex"; func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("sdf_pos", ShaderLanguage::TYPE_VEC2)); func.return_type = ShaderLanguage::TYPE_FLOAT; //whether it could emit shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].stage_functions["texture_sdf"] = func; @@ -297,6 +298,7 @@ ShaderTypes::ShaderTypes() { { ShaderLanguage::StageFunctionInfo func; + func.skip_function = "vertex"; func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("uv", ShaderLanguage::TYPE_VEC2)); func.return_type = ShaderLanguage::TYPE_VEC2; //whether it could emit shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].stage_functions["screen_uv_to_sdf"] = func; |