summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/io/translation_loader_po.cpp3
-rw-r--r--doc/classes/@GlobalScope.xml2
-rw-r--r--doc/classes/AtlasTexture.xml2
-rw-r--r--doc/classes/AudioEffectFilter.xml5
-rw-r--r--doc/classes/AudioEffectStereoEnhance.xml4
-rw-r--r--doc/classes/AudioStreamPlayback.xml48
-rw-r--r--doc/classes/CanvasItem.xml8
-rw-r--r--doc/classes/LineEdit.xml3
-rw-r--r--doc/classes/String.xml1
-rw-r--r--doc/classes/Transform2D.xml4
-rw-r--r--editor/editor_data.cpp2
-rw-r--r--editor/editor_node.cpp7
-rw-r--r--editor/editor_properties.cpp1
-rw-r--r--editor/editor_resource_picker.cpp23
-rw-r--r--editor/editor_resource_picker.h5
-rw-r--r--editor/plugins/control_editor_plugin.cpp1
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp2
-rw-r--r--editor/scene_tree_dock.cpp121
-rw-r--r--editor/scene_tree_dock.h1
-rw-r--r--editor/themes/editor_theme_manager.cpp46
-rw-r--r--editor/themes/editor_theme_manager.h1
-rw-r--r--editor/window_wrapper.cpp7
-rw-r--r--editor/window_wrapper.h1
-rw-r--r--modules/betsy/image_compress_betsy.cpp10
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml4
-rw-r--r--modules/svg/image_loader_svg.cpp26
-rw-r--r--platform/android/detect.py4
-rw-r--r--platform/ios/detect.py2
-rw-r--r--platform/windows/detect.py45
-rw-r--r--platform/windows/display_server_windows.cpp5
-rw-r--r--scene/main/canvas_item.cpp2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/property_utils.cpp10
-rw-r--r--scene/resources/external_texture.cpp23
-rw-r--r--scene/resources/external_texture.h5
-rw-r--r--scene/scene_string_names.cpp2
-rw-r--r--scene/scene_string_names.h2
-rw-r--r--servers/audio/audio_stream.cpp43
-rw-r--r--servers/audio/audio_stream.h6
-rw-r--r--servers/display_server.cpp4
-rw-r--r--servers/rendering/renderer_rd/environment/fog.cpp80
-rw-r--r--servers/rendering/rendering_device.cpp4
-rw-r--r--servers/rendering/rendering_device.h2
-rw-r--r--servers/rendering/rendering_device_binds.cpp5
-rw-r--r--servers/rendering/rendering_server_default.cpp2
-rw-r--r--servers/rendering/shader_language.cpp129
-rw-r--r--servers/rendering/shader_language.h4
-rw-r--r--servers/rendering/shader_types.cpp2
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;