summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS2
-rw-r--r--.github/workflows/static_checks.yml5
-rw-r--r--core/extension/gdextension_interface.cpp37
-rw-r--r--core/extension/gdextension_interface.h31
-rw-r--r--core/input/input_event.cpp22
-rw-r--r--core/object/object.cpp6
-rw-r--r--core/object/script_language.h3
-rw-r--r--core/object/script_language_extension.cpp1
-rw-r--r--core/object/script_language_extension.h10
-rw-r--r--doc/classes/Font.xml4
-rw-r--r--doc/classes/FontFile.xml17
-rw-r--r--doc/classes/FontVariation.xml2
-rw-r--r--doc/classes/GPUParticles3D.xml1
-rw-r--r--doc/classes/Object.xml53
-rw-r--r--doc/classes/RenderingDevice.xml20
-rw-r--r--doc/classes/ScriptExtension.xml5
-rw-r--r--doc/classes/TextServer.xml17
-rw-r--r--doc/classes/TextServerExtension.xml15
-rw-r--r--doc/classes/TileSetAtlasSource.xml12
-rw-r--r--doc/classes/XMLParser.xml80
-rw-r--r--drivers/gles3/shader_gles3.cpp2
-rw-r--r--drivers/gles3/shader_gles3.h2
-rw-r--r--drivers/gles3/storage/config.cpp16
-rw-r--r--drivers/gles3/storage/config.h16
-rw-r--r--editor/editor_data.cpp8
-rw-r--r--editor/editor_fonts.cpp1
-rw-r--r--editor/editor_inspector.cpp33
-rw-r--r--editor/editor_inspector.h2
-rw-r--r--editor/editor_node.cpp8
-rw-r--r--editor/editor_themes.cpp2
-rw-r--r--editor/export/editor_export_platform.cpp4
-rw-r--r--editor/gui/editor_file_dialog.cpp13
-rw-r--r--editor/gui/editor_spin_slider.cpp3
-rw-r--r--editor/icons/editor_icons_builders.py2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp5
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp24
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp10
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp200
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h11
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp99
-rw-r--r--editor/plugins/tiles/tile_set_editor.h2
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp24
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.h3
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp16
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h6
-rw-r--r--editor/project_manager.cpp20
-rw-r--r--editor/project_manager.h1
-rw-r--r--misc/extension_api_validation/4.0-stable.expected7
-rw-r--r--misc/scripts/gitignore_check.sh26
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml2
-rw-r--r--modules/gdscript/gdscript.cpp37
-rw-r--r--modules/gdscript/gdscript.h4
-rw-r--r--modules/gdscript/gdscript_compiler.cpp1
-rw-r--r--modules/gdscript/gdscript_parser.cpp17
-rw-r--r--modules/gdscript/gdscript_parser.h1
-rw-r--r--modules/mono/csharp_script.cpp21
-rw-r--r--modules/mono/csharp_script.h4
-rw-r--r--modules/text_server_adv/text_server_adv.cpp45
-rw-r--r--modules/text_server_adv/text_server_adv.h16
-rw-r--r--modules/text_server_fb/text_server_fb.cpp44
-rw-r--r--modules/text_server_fb/text_server_fb.h15
-rw-r--r--platform/android/export/export.cpp3
-rw-r--r--platform/android/export/export_plugin.cpp8
-rw-r--r--platform/android/java/editor/src/.gitignore1
-rw-r--r--platform/windows/os_windows.cpp9
-rw-r--r--scene/3d/label_3d.cpp6
-rw-r--r--scene/gui/label.cpp3
-rw-r--r--scene/gui/line_edit.cpp3
-rw-r--r--scene/gui/range.cpp4
-rw-r--r--scene/gui/rich_text_label.cpp3
-rw-r--r--scene/main/viewport.cpp21
-rw-r--r--scene/register_scene_types.cpp1
-rw-r--r--scene/resources/SCsub2
-rw-r--r--scene/resources/default_theme/SCsub32
-rw-r--r--scene/resources/font.compat.inc41
-rw-r--r--scene/resources/font.cpp68
-rw-r--r--scene/resources/font.h16
-rw-r--r--scene/resources/shader.cpp13
-rw-r--r--scene/resources/shader_include.cpp13
-rw-r--r--scene/resources/tile_set.cpp62
-rw-r--r--scene/resources/tile_set.h6
-rw-r--r--scene/theme/SCsub13
-rw-r--r--scene/theme/default_theme.cpp (renamed from scene/resources/default_theme/default_theme.cpp)0
-rw-r--r--scene/theme/default_theme.h (renamed from scene/resources/default_theme/default_theme.h)0
-rw-r--r--scene/theme/default_theme_builders.py (renamed from scene/resources/default_theme/default_theme_builders.py)0
-rw-r--r--scene/theme/icons/SCsub22
-rw-r--r--scene/theme/icons/add.svg (renamed from scene/resources/default_theme/add.svg)0
-rw-r--r--scene/theme/icons/arrow_down.svg (renamed from scene/resources/default_theme/arrow_down.svg)0
-rw-r--r--scene/theme/icons/arrow_left.svg (renamed from scene/resources/default_theme/arrow_left.svg)0
-rw-r--r--scene/theme/icons/arrow_right.svg (renamed from scene/resources/default_theme/arrow_right.svg)0
-rw-r--r--scene/theme/icons/bookmark.svg (renamed from scene/resources/default_theme/bookmark.svg)0
-rw-r--r--scene/theme/icons/breakpoint.svg (renamed from scene/resources/default_theme/breakpoint.svg)0
-rw-r--r--scene/theme/icons/checked.svg (renamed from scene/resources/default_theme/checked.svg)0
-rw-r--r--scene/theme/icons/checked_disabled.svg (renamed from scene/resources/default_theme/checked_disabled.svg)0
-rw-r--r--scene/theme/icons/close.svg (renamed from scene/resources/default_theme/close.svg)0
-rw-r--r--scene/theme/icons/close_hl.svg (renamed from scene/resources/default_theme/close_hl.svg)0
-rw-r--r--scene/theme/icons/color_picker_bar_arrow.svg (renamed from scene/resources/default_theme/color_picker_bar_arrow.svg)0
-rw-r--r--scene/theme/icons/color_picker_cursor.svg (renamed from scene/resources/default_theme/color_picker_cursor.svg)0
-rw-r--r--scene/theme/icons/color_picker_overbright.svg (renamed from scene/resources/default_theme/color_picker_overbright.svg)0
-rw-r--r--scene/theme/icons/color_picker_pipette.svg (renamed from scene/resources/default_theme/color_picker_pipette.svg)0
-rw-r--r--scene/theme/icons/default_theme_icons_builders.py (renamed from scene/resources/default_theme/default_theme_icons_builders.py)0
-rw-r--r--scene/theme/icons/error_icon.svg (renamed from scene/resources/default_theme/error_icon.svg)0
-rw-r--r--scene/theme/icons/file.svg (renamed from scene/resources/default_theme/file.svg)0
-rw-r--r--scene/theme/icons/folder.svg (renamed from scene/resources/default_theme/folder.svg)0
-rw-r--r--scene/theme/icons/folder_up.svg (renamed from scene/resources/default_theme/folder_up.svg)0
-rw-r--r--scene/theme/icons/graph_port.svg (renamed from scene/resources/default_theme/graph_port.svg)0
-rw-r--r--scene/theme/icons/grid_layout.svg (renamed from scene/resources/default_theme/grid_layout.svg)0
-rw-r--r--scene/theme/icons/grid_minimap.svg (renamed from scene/resources/default_theme/grid_minimap.svg)0
-rw-r--r--scene/theme/icons/grid_snap.svg (renamed from scene/resources/default_theme/grid_snap.svg)0
-rw-r--r--scene/theme/icons/grid_toggle.svg (renamed from scene/resources/default_theme/grid_toggle.svg)0
-rw-r--r--scene/theme/icons/hslider_tick.svg (renamed from scene/resources/default_theme/hslider_tick.svg)0
-rw-r--r--scene/theme/icons/hsplitter.svg (renamed from scene/resources/default_theme/hsplitter.svg)0
-rw-r--r--scene/theme/icons/indeterminate.svg (renamed from scene/resources/default_theme/indeterminate.svg)0
-rw-r--r--scene/theme/icons/line_edit_clear.svg (renamed from scene/resources/default_theme/line_edit_clear.svg)0
-rw-r--r--scene/theme/icons/mini_checkerboard.svg (renamed from scene/resources/default_theme/mini_checkerboard.svg)0
-rw-r--r--scene/theme/icons/option_button_arrow.svg (renamed from scene/resources/default_theme/option_button_arrow.svg)0
-rw-r--r--scene/theme/icons/picker_shape_circle.svg (renamed from scene/resources/default_theme/picker_shape_circle.svg)0
-rw-r--r--scene/theme/icons/picker_shape_rectangle.svg (renamed from scene/resources/default_theme/picker_shape_rectangle.svg)0
-rw-r--r--scene/theme/icons/picker_shape_rectangle_wheel.svg (renamed from scene/resources/default_theme/picker_shape_rectangle_wheel.svg)0
-rw-r--r--scene/theme/icons/popup_menu_arrow_left.svg (renamed from scene/resources/default_theme/popup_menu_arrow_left.svg)0
-rw-r--r--scene/theme/icons/popup_menu_arrow_right.svg (renamed from scene/resources/default_theme/popup_menu_arrow_right.svg)0
-rw-r--r--scene/theme/icons/radio_checked.svg (renamed from scene/resources/default_theme/radio_checked.svg)0
-rw-r--r--scene/theme/icons/radio_checked_disabled.svg (renamed from scene/resources/default_theme/radio_checked_disabled.svg)0
-rw-r--r--scene/theme/icons/radio_unchecked.svg (renamed from scene/resources/default_theme/radio_unchecked.svg)0
-rw-r--r--scene/theme/icons/radio_unchecked_disabled.svg (renamed from scene/resources/default_theme/radio_unchecked_disabled.svg)0
-rw-r--r--scene/theme/icons/reload.svg (renamed from scene/resources/default_theme/reload.svg)0
-rw-r--r--scene/theme/icons/resizer_nw.svg (renamed from scene/resources/default_theme/resizer_nw.svg)0
-rw-r--r--scene/theme/icons/resizer_se.svg (renamed from scene/resources/default_theme/resizer_se.svg)0
-rw-r--r--scene/theme/icons/scroll_button_left.svg (renamed from scene/resources/default_theme/scroll_button_left.svg)0
-rw-r--r--scene/theme/icons/scroll_button_left_hl.svg (renamed from scene/resources/default_theme/scroll_button_left_hl.svg)0
-rw-r--r--scene/theme/icons/scroll_button_right.svg (renamed from scene/resources/default_theme/scroll_button_right.svg)0
-rw-r--r--scene/theme/icons/scroll_button_right_hl.svg (renamed from scene/resources/default_theme/scroll_button_right_hl.svg)0
-rw-r--r--scene/theme/icons/slider_grabber.svg (renamed from scene/resources/default_theme/slider_grabber.svg)0
-rw-r--r--scene/theme/icons/slider_grabber_disabled.svg (renamed from scene/resources/default_theme/slider_grabber_disabled.svg)0
-rw-r--r--scene/theme/icons/slider_grabber_hl.svg (renamed from scene/resources/default_theme/slider_grabber_hl.svg)0
-rw-r--r--scene/theme/icons/tabs_drop_mark.svg (renamed from scene/resources/default_theme/tabs_drop_mark.svg)0
-rw-r--r--scene/theme/icons/tabs_menu.svg (renamed from scene/resources/default_theme/tabs_menu.svg)0
-rw-r--r--scene/theme/icons/tabs_menu_hl.svg (renamed from scene/resources/default_theme/tabs_menu_hl.svg)0
-rw-r--r--scene/theme/icons/text_edit_ellipsis.svg (renamed from scene/resources/default_theme/text_edit_ellipsis.svg)0
-rw-r--r--scene/theme/icons/text_edit_space.svg (renamed from scene/resources/default_theme/text_edit_space.svg)0
-rw-r--r--scene/theme/icons/text_edit_tab.svg (renamed from scene/resources/default_theme/text_edit_tab.svg)0
-rw-r--r--scene/theme/icons/toggle_off.svg (renamed from scene/resources/default_theme/toggle_off.svg)0
-rw-r--r--scene/theme/icons/toggle_off_disabled.svg (renamed from scene/resources/default_theme/toggle_off_disabled.svg)0
-rw-r--r--scene/theme/icons/toggle_off_disabled_mirrored.svg (renamed from scene/resources/default_theme/toggle_off_disabled_mirrored.svg)0
-rw-r--r--scene/theme/icons/toggle_off_mirrored.svg (renamed from scene/resources/default_theme/toggle_off_mirrored.svg)0
-rw-r--r--scene/theme/icons/toggle_on.svg (renamed from scene/resources/default_theme/toggle_on.svg)0
-rw-r--r--scene/theme/icons/toggle_on_disabled.svg (renamed from scene/resources/default_theme/toggle_on_disabled.svg)0
-rw-r--r--scene/theme/icons/toggle_on_disabled_mirrored.svg (renamed from scene/resources/default_theme/toggle_on_disabled_mirrored.svg)0
-rw-r--r--scene/theme/icons/toggle_on_mirrored.svg (renamed from scene/resources/default_theme/toggle_on_mirrored.svg)0
-rw-r--r--scene/theme/icons/unchecked.svg (renamed from scene/resources/default_theme/unchecked.svg)0
-rw-r--r--scene/theme/icons/unchecked_disabled.svg (renamed from scene/resources/default_theme/unchecked_disabled.svg)0
-rw-r--r--scene/theme/icons/updown.svg (renamed from scene/resources/default_theme/updown.svg)0
-rw-r--r--scene/theme/icons/visibility_visible.svg (renamed from scene/resources/default_theme/visibility_visible.svg)0
-rw-r--r--scene/theme/icons/vslider_tick.svg (renamed from scene/resources/default_theme/vslider_tick.svg)0
-rw-r--r--scene/theme/icons/vsplitter.svg (renamed from scene/resources/default_theme/vsplitter.svg)0
-rw-r--r--scene/theme/icons/zoom_less.svg (renamed from scene/resources/default_theme/zoom_less.svg)0
-rw-r--r--scene/theme/icons/zoom_more.svg (renamed from scene/resources/default_theme/zoom_more.svg)0
-rw-r--r--scene/theme/icons/zoom_reset.svg (renamed from scene/resources/default_theme/zoom_reset.svg)0
-rw-r--r--scene/theme/theme_db.cpp2
-rw-r--r--servers/rendering/renderer_rd/effects/debug_effects.cpp36
-rw-r--r--servers/rendering/renderer_rd/effects/debug_effects.h14
-rw-r--r--servers/rendering/renderer_rd/environment/gi.cpp73
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp10
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl80
-rw-r--r--servers/rendering/renderer_rd/shaders/particles_copy.glsl18
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp85
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.h15
-rw-r--r--servers/rendering/renderer_scene_cull.h3
-rw-r--r--servers/rendering/storage/utilities.h1
-rw-r--r--servers/text/text_server_extension.cpp13
-rw-r--r--servers/text/text_server_extension.h5
-rw-r--r--servers/text_server.cpp3
-rw-r--r--servers/text_server.h3
-rw-r--r--tests/core/object/test_object.h2
177 files changed, 1423 insertions, 415 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 031582bc63..06aa5731b4 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -154,11 +154,11 @@ doc_classes/* @godotengine/documentation
/scene/debugger/ @godotengine/debugger
/scene/gui/ @godotengine/gui-nodes
/scene/main/ @godotengine/core
-/scene/resources/default_theme/ @godotengine/gui-nodes
/scene/resources/font.* @godotengine/gui-nodes
/scene/resources/text_line.* @godotengine/gui-nodes
/scene/resources/text_paragraph.* @godotengine/gui-nodes
/scene/resources/visual_shader*.* @godotengine/shaders
+/scene/theme/ @godotengine/gui-nodes
# Servers
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index b47ef135a2..c096c63f9b 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -41,6 +41,11 @@ jobs:
files=$(echo "$files" | grep -v 'thirdparty' | xargs -I {} sh -c 'echo "./{}"' | tr '\n' ' ')
echo "CHANGED_FILES=$files" >> $GITHUB_ENV
+ # This needs to happen before Python and npm execution; it must happen before any extra files are written.
+ - name: .gitignore checks (gitignore_check.sh)
+ run: |
+ bash ./misc/scripts/gitignore_check.sh
+
- name: File formatting checks (file_format.sh)
run: |
bash ./misc/scripts/file_format.sh changed.txt
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index b4541de8fe..a70ad5262f 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -1048,6 +1048,41 @@ static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDE
return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension);
}
+static GDExtensionScriptInstancePtr gdextension_placeholder_script_instance_create(GDExtensionObjectPtr p_language, GDExtensionObjectPtr p_script, GDExtensionObjectPtr p_owner) {
+ ScriptLanguage *language = (ScriptLanguage *)p_language;
+ Ref<Script> script;
+ script.reference_ptr((Script *)p_script);
+ Object *owner = (Object *)p_owner;
+
+ PlaceHolderScriptInstance *placeholder = memnew(PlaceHolderScriptInstance(language, script, owner));
+ return reinterpret_cast<GDExtensionScriptInstancePtr>(placeholder);
+}
+
+static void gdextension_placeholder_script_instance_update(GDExtensionScriptInstancePtr p_placeholder, GDExtensionConstTypePtr p_properties, GDExtensionConstTypePtr p_values) {
+ PlaceHolderScriptInstance *placeholder = dynamic_cast<PlaceHolderScriptInstance *>(reinterpret_cast<ScriptInstance *>(p_placeholder));
+ ERR_FAIL_COND_MSG(!placeholder, "Unable to update placeholder, expected a PlaceHolderScriptInstance but received an invalid type.");
+
+ const Array &properties = *reinterpret_cast<const Array *>(p_properties);
+ const Dictionary &values = *reinterpret_cast<const Dictionary *>(p_values);
+
+ List<PropertyInfo> properties_list;
+ HashMap<StringName, Variant> values_map;
+
+ for (int i = 0; i < properties.size(); i++) {
+ Dictionary d = properties[i];
+ properties_list.push_back(PropertyInfo::from_dict(d));
+ }
+
+ List<Variant> keys;
+ values.get_key_list(&keys);
+
+ for (const Variant &E : keys) {
+ values_map.insert(E, values[E]);
+ }
+
+ placeholder->update(properties_list, values_map);
+}
+
static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) {
if (!p_object || !p_language) {
return nullptr;
@@ -1235,6 +1270,8 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(ref_get_object);
REGISTER_INTERFACE_FUNC(ref_set_object);
REGISTER_INTERFACE_FUNC(script_instance_create);
+ REGISTER_INTERFACE_FUNC(placeholder_script_instance_create);
+ REGISTER_INTERFACE_FUNC(placeholder_script_instance_update);
REGISTER_INTERFACE_FUNC(object_get_script_instance);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index cfc21473d6..a86abf2a7e 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -2127,6 +2127,37 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte
typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
/**
+ * @name placeholder_script_instance_create
+ * @since 4.2
+ *
+ * Creates a placeholder script instance for a given script and instance.
+ *
+ * This interface is optional as a custom placeholder could also be created with script_instance_create().
+ *
+ * @param p_language A pointer to a ScriptLanguage.
+ * @param p_script A pointer to a Script.
+ * @param p_owner A pointer to an Object.
+ *
+ * @return A pointer to a PlaceHolderScriptInstance object.
+ */
+typedef GDExtensionScriptInstancePtr (*GDExtensionInterfacePlaceHolderScriptInstanceCreate)(GDExtensionObjectPtr p_language, GDExtensionObjectPtr p_script, GDExtensionObjectPtr p_owner);
+
+/**
+ * @name placeholder_script_instance_update
+ * @since 4.2
+ *
+ * Updates a placeholder script instance with the given properties and values.
+ *
+ * The passed in placeholder must be an instance of PlaceHolderScriptInstance
+ * such as the one returned by placeholder_script_instance_create().
+ *
+ * @param p_placeholder A pointer to a PlaceHolderScriptInstance.
+ * @param p_properties A pointer to an Array of Dictionary representing PropertyInfo.
+ * @param p_values A pointer to a Dictionary mapping StringName to Variant values.
+ */
+typedef void (*GDExtensionInterfacePlaceHolderScriptInstanceUpdate)(GDExtensionScriptInstancePtr p_placeholder, GDExtensionConstTypePtr p_properties, GDExtensionConstTypePtr p_values);
+
+/**
* @name object_get_script_instance
* @since 4.2
*
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index e37886cbe9..9e341b2157 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -1722,7 +1722,27 @@ String InputEventMIDI::as_text() const {
}
String InputEventMIDI::to_string() {
- return vformat("InputEventMIDI: channel=%d, message=%d, pitch=%d, velocity=%d, pressure=%d, controller_number=%d, controller_value=%d", channel, message, pitch, velocity, pressure, controller_number, controller_value);
+ String ret;
+ switch (message) {
+ case MIDIMessage::NOTE_ON:
+ ret = vformat("Note On: channel=%d, pitch=%d, velocity=%d", channel, pitch, velocity);
+ break;
+ case MIDIMessage::NOTE_OFF:
+ ret = vformat("Note Off: channel=%d, pitch=%d, velocity=%d", channel, pitch, velocity);
+ break;
+ case MIDIMessage::PITCH_BEND:
+ ret = vformat("Pitch Bend: channel=%d, pitch=%d", channel, pitch);
+ break;
+ case MIDIMessage::CHANNEL_PRESSURE:
+ ret = vformat("Channel Pressure: channel=%d, pressure=%d", channel, pressure);
+ break;
+ case MIDIMessage::CONTROL_CHANGE:
+ ret = vformat("Control Change: channel=%d, controller_number=%d, controller_value=%d", channel, controller_number, controller_value);
+ break;
+ default:
+ ret = vformat("channel=%d, message=%d, pitch=%d, velocity=%d, pressure=%d, controller_number=%d, controller_value=%d, instrument=%d", channel, message, pitch, velocity, pressure, controller_number, controller_value, instrument);
+ }
+ return "InputEventMIDI: " + ret;
}
void InputEventMIDI::_bind_methods() {
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 4ae0ecdefd..b803d57cd9 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -526,6 +526,10 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
void Object::validate_property(PropertyInfo &p_property) const {
_validate_propertyv(p_property);
+
+ if (script_instance) { // Call it last to allow user altering already validated properties.
+ script_instance->validate_property(p_property);
+ }
}
bool Object::property_can_revert(const StringName &p_name) const {
@@ -1604,6 +1608,8 @@ void Object::_bind_methods() {
plget.return_val.hint_string = "Dictionary";
BIND_OBJ_CORE_METHOD(plget);
+ BIND_OBJ_CORE_METHOD(MethodInfo(Variant::NIL, "_validate_property", PropertyInfo(Variant::DICTIONARY, "property")));
+
BIND_OBJ_CORE_METHOD(MethodInfo(Variant::BOOL, "_property_can_revert", PropertyInfo(Variant::STRING_NAME, "property")));
MethodInfo mipgr("_property_get_revert", PropertyInfo(Variant::STRING_NAME, "property"));
mipgr.return_val.name = "Variant";
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 3ea6a6e4c3..9d83a12bbd 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -141,6 +141,7 @@ public:
#ifdef TOOLS_ENABLED
virtual Vector<DocData::ClassDoc> get_documentation() const = 0;
+ virtual String get_class_icon_path() const = 0;
virtual PropertyInfo get_class_category() const;
#endif // TOOLS_ENABLED
@@ -179,6 +180,7 @@ public:
virtual bool get(const StringName &p_name, Variant &r_ret) const = 0;
virtual void get_property_list(List<PropertyInfo> *p_properties) const = 0;
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const = 0;
+ virtual void validate_property(PropertyInfo &p_property) const = 0;
virtual bool property_can_revert(const StringName &p_name) const = 0;
virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const = 0;
@@ -462,6 +464,7 @@ public:
virtual bool get(const StringName &p_name, Variant &r_ret) const override;
virtual void get_property_list(List<PropertyInfo> *p_properties) const override;
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override;
+ virtual void validate_property(PropertyInfo &p_property) const override {}
virtual bool property_can_revert(const StringName &p_name) const override { return false; };
virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { return false; };
diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp
index 0df9d58334..bf8bac476a 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -52,6 +52,7 @@ void ScriptExtension::_bind_methods() {
GDVIRTUAL_BIND(_reload, "keep_state");
GDVIRTUAL_BIND(_get_documentation);
+ GDVIRTUAL_BIND(_get_class_icon_path);
GDVIRTUAL_BIND(_has_method, "method");
GDVIRTUAL_BIND(_get_method_info, "method");
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 1a0ec29479..6edbcdaeee 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -77,6 +77,7 @@ public:
EXBIND1R(Error, reload, bool)
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_documentation)
+ GDVIRTUAL0RC(String, _get_class_icon_path)
#ifdef TOOLS_ENABLED
virtual Vector<DocData::ClassDoc> get_documentation() const override {
TypedArray<Dictionary> doc;
@@ -89,6 +90,12 @@ public:
return class_doc;
}
+
+ virtual String get_class_icon_path() const override {
+ String ret;
+ GDVIRTUAL_CALL(_get_class_icon_path, ret);
+ return ret;
+ }
#endif // TOOLS_ENABLED
EXBIND1RC(bool, has_method, const StringName &)
@@ -675,6 +682,9 @@ public:
}
return Variant::NIL;
}
+ virtual void validate_property(PropertyInfo &p_property) const override {
+ // TODO
+ }
virtual bool property_can_revert(const StringName &p_name) const override {
if (native_info->property_can_revert_func) {
diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml
index e87712825d..63e7d80737 100644
--- a/doc/classes/Font.xml
+++ b/doc/classes/Font.xml
@@ -114,6 +114,10 @@
<param index="1" name="face_index" type="int" default="0" />
<param index="2" name="strength" type="float" default="0.0" />
<param index="3" name="transform" type="Transform2D" default="Transform2D(1, 0, 0, 1, 0, 0)" />
+ <param index="4" name="spacing_top" type="int" default="0" />
+ <param index="5" name="spacing_bottom" type="int" default="0" />
+ <param index="6" name="spacing_space" type="int" default="0" />
+ <param index="7" name="spacing_glyph" type="int" default="0" />
<description>
Returns [TextServer] RID of the font cache for specific variation.
</description>
diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml
index 86d1cb552a..1628f8819f 100644
--- a/doc/classes/FontFile.xml
+++ b/doc/classes/FontFile.xml
@@ -130,6 +130,14 @@
Returns embolden strength, if is not equal to zero, emboldens the font outlines. Negative values reduce the outline thickness.
</description>
</method>
+ <method name="get_extra_spacing" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="cache_index" type="int" />
+ <param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
+ <description>
+ Returns spacing for [param spacing] (see [enum TextServer.SpacingType]) in pixels (not relative to the font size).
+ </description>
+ </method>
<method name="get_face_index" qualifiers="const">
<return type="int" />
<param index="0" name="cache_index" type="int" />
@@ -436,6 +444,15 @@
Sets embolden strength, if is not equal to zero, emboldens the font outlines. Negative values reduce the outline thickness.
</description>
</method>
+ <method name="set_extra_spacing">
+ <return type="void" />
+ <param index="0" name="cache_index" type="int" />
+ <param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
+ <param index="2" name="value" type="int" />
+ <description>
+ Sets the spacing for [param spacing] (see [enum TextServer.SpacingType]) to [param value] in pixels (not relative to the font size).
+ </description>
+ </method>
<method name="set_face_index">
<return type="void" />
<param index="0" name="cache_index" type="int" />
diff --git a/doc/classes/FontVariation.xml b/doc/classes/FontVariation.xml
index f02fda31b9..128f3b5a85 100644
--- a/doc/classes/FontVariation.xml
+++ b/doc/classes/FontVariation.xml
@@ -38,7 +38,7 @@
<param index="0" name="spacing" type="int" enum="TextServer.SpacingType" />
<param index="1" name="value" type="int" />
<description>
- Sets the spacing for [code]type[/code] (see [enum TextServer.SpacingType]) to [param value] in pixels (not relative to the font size).
+ Sets the spacing for [param spacing] (see [enum TextServer.SpacingType]) to [param value] in pixels (not relative to the font size).
</description>
</method>
</methods>
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index 9ba64feebc..e42aca1ada 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -67,6 +67,7 @@
</member>
<member name="draw_order" type="int" setter="set_draw_order" getter="get_draw_order" enum="GPUParticles3D.DrawOrder" default="0">
Particle draw order. Uses [enum DrawOrder] values.
+ [b]Note:[/b] [constant DRAW_ORDER_INDEX] is the only option that supports motion vectors for effects like TAA. It is suggested to use this draw order if the particles are opaque to fix ghosting artifacts.
</member>
<member name="draw_pass_1" type="Mesh" setter="set_draw_pass_mesh" getter="get_draw_pass_mesh">
[Mesh] that is drawn for the first draw pass.
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index 5fb1d7ab8f..bb958a8189 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -269,6 +269,59 @@
[/codeblock]
</description>
</method>
+ <method name="_validate_property" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="property" type="Dictionary" />
+ <description>
+ Override this method to customize existing properties. Every property info goes through this method. The dictionary contents is the same as in [method _get_property_list].
+ [codeblocks]
+ [gdscript]
+ @tool
+ extends Node
+
+ @export var is_number_editable: bool:
+ set(value):
+ is_number_editable = value
+ notify_property_list_changed()
+ @export var number: int
+
+ func _validate_property(property: Dictionary):
+ if property.name == "number" and not is_number_editable:
+ property.usage |= PROPERTY_USAGE_READ_ONLY
+ [/gdscript]
+ [csharp]
+ [Tool]
+ public partial class MyNode : Node
+ {
+ private bool _isNumberEditable;
+
+ [Export]
+ public bool IsNumberEditable
+ {
+ get =&gt; _isNumberEditable;
+ set
+ {
+ _isNumberEditable = value;
+ NotifyPropertyListChanged();
+ }
+ }
+
+ [Export]
+ public int Number { get; set; }
+
+ public override void _ValidateProperty(Godot.Collections.Dictionary property)
+ {
+ if (property["name"].AsStringName() == PropertyName.Number &amp;&amp; IsNumberEditable)
+ {
+ var usage = property["usage"].As&gt;PropertyUsageFlags&lt;() | PropertyUsageFlags.ReadOnly;
+ property["usage"] = (int)usage;
+ }
+ }
+ }
+ [/csharp]
+ [/codeblocks]
+ </description>
+ </method>
<method name="add_user_signal">
<return type="void" />
<param index="0" name="signal" type="String" />
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index 5c81914511..8984c54253 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -29,6 +29,12 @@
<param index="2" name="size_bytes" type="int" />
<param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
+ Clears the contents of the [param buffer], clearing [param size_bytes] bytes, starting at [param offset]. Always raises a memory barrier.
+ Prints an error if:
+ - the size isn't a multiple of four
+ - the region specified by [param offset] + [param size_bytes] exceeds the buffer
+ - a draw list is currently active (created by [method draw_list_begin])
+ - a compute list is currently active (created by [method compute_list_begin])
</description>
</method>
<method name="buffer_get_data">
@@ -48,6 +54,11 @@
<param index="3" name="data" type="PackedByteArray" />
<param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
+ Updates a region of [param size_bytes] bytes, starting at [param offset], in the buffer, with the specified [param data]. Raises a memory barrier except when [param post_barrier] is set to [constant BARRIER_MASK_NO_BARRIER].
+ Prints an error if:
+ - the region specified by [param offset] + [param size_bytes] exceeds the buffer
+ - a draw list is currently active (created by [method draw_list_begin])
+ - a compute list is currently active (created by [method compute_list_begin])
</description>
</method>
<method name="capture_timestamp">
@@ -61,6 +72,7 @@
<return type="void" />
<param index="0" name="compute_list" type="int" />
<description>
+ Raises a Vulkan compute barrier in the specified [param compute_list].
</description>
</method>
<method name="compute_list_begin">
@@ -92,6 +104,7 @@
<param index="0" name="compute_list" type="int" />
<param index="1" name="compute_pipeline" type="RID" />
<description>
+ Tells the GPU what compute pipeline to use when processing the compute list. If the shader has changed since the last time this function was called, Godot will unbind all descriptor sets and will re-bind them inside [method compute_list_dispatch].
</description>
</method>
<method name="compute_list_bind_uniform_set">
@@ -100,6 +113,7 @@
<param index="1" name="uniform_set" type="RID" />
<param index="2" name="set_index" type="int" />
<description>
+ Binds the [param uniform_set] to this [param compute_list]. Godot ensures that all textures in the uniform set have the correct Vulkan access masks. If Godot had to change access masks of textures, it will raise a Vulkan image memory barrier.
</description>
</method>
<method name="compute_list_dispatch">
@@ -644,6 +658,7 @@
<return type="int" />
<param index="0" name="shader" type="RID" />
<description>
+ Returns the internal vertex input mask. Internally, the vertex input mask is an unsigned integer consisting of the locations (specified in GLSL via. [code]layout(location = ...)[/code]) of the input variables (specified in GLSL by the [code]in[/code] keyword).
</description>
</method>
<method name="storage_buffer_create">
@@ -830,7 +845,7 @@
<param index="0" name="size_bytes" type="int" />
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
<description>
- It can be accessed with the RID that is returned.
+ Creates a new uniform buffer. It can be accessed with the RID that is returned.
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
</description>
</method>
@@ -840,7 +855,7 @@
<param index="1" name="shader" type="RID" />
<param index="2" name="shader_set" type="int" />
<description>
- It can be accessed with the RID that is returned.
+ Creates a new uniform set. It can be accessed with the RID that is returned.
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
</description>
</method>
@@ -848,6 +863,7 @@
<return type="bool" />
<param index="0" name="uniform_set" type="RID" />
<description>
+ Checks if the [param uniform_set] is valid, i.e. is owned.
</description>
</method>
<method name="vertex_array_create">
diff --git a/doc/classes/ScriptExtension.xml b/doc/classes/ScriptExtension.xml
index 934cbf5a26..f7c8ecb3fb 100644
--- a/doc/classes/ScriptExtension.xml
+++ b/doc/classes/ScriptExtension.xml
@@ -22,6 +22,11 @@
<description>
</description>
</method>
+ <method name="_get_class_icon_path" qualifiers="virtual const">
+ <return type="String" />
+ <description>
+ </description>
+ </method>
<method name="_get_constants" qualifiers="virtual const">
<return type="Dictionary" />
<description>
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 0646a70e73..3893ef8cbc 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -378,6 +378,14 @@
Returns list of the font sizes in the cache. Each size is [code]Vector2i[/code] with font size and outline size.
</description>
</method>
+ <method name="font_get_spacing" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="font_rid" type="RID" />
+ <param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
+ <description>
+ Returns the spacing for [param spacing] (see [enum TextServer.SpacingType]) in pixels (not relative to the font size).
+ </description>
+ </method>
<method name="font_get_stretch" qualifiers="const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
@@ -828,6 +836,15 @@
Adds override for [method font_is_script_supported].
</description>
</method>
+ <method name="font_set_spacing">
+ <return type="void" />
+ <param index="0" name="font_rid" type="RID" />
+ <param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
+ <param index="2" name="value" type="int" />
+ <description>
+ Sets the spacing for [param spacing] (see [enum TextServer.SpacingType]) to [param value] in pixels (not relative to the font size).
+ </description>
+ </method>
<method name="font_set_stretch">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index c58e3b705f..82162e77be 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -325,6 +325,13 @@
<description>
</description>
</method>
+ <method name="_font_get_spacing" qualifiers="virtual const">
+ <return type="int" />
+ <param index="0" name="font_rid" type="RID" />
+ <param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
+ <description>
+ </description>
+ </method>
<method name="_font_get_stretch" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
@@ -724,6 +731,14 @@
<description>
</description>
</method>
+ <method name="_font_set_spacing" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="font_rid" type="RID" />
+ <param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
+ <param index="2" name="value" type="int" />
+ <description>
+ </description>
+ </method>
<method name="_font_set_stretch" qualifiers="virtual">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml
index 7daabcbfee..1623cd87ee 100644
--- a/doc/classes/TileSetAtlasSource.xml
+++ b/doc/classes/TileSetAtlasSource.xml
@@ -13,6 +13,12 @@
<tutorials>
</tutorials>
<methods>
+ <method name="clear_tiles_outside_texture">
+ <return type="void" />
+ <description>
+ Removes all tiles that don't fit the available texture area. This method iterates over all the source's tiles, so it's advised to use [method has_tiles_outside_texture] beforehand.
+ </description>
+ </method>
<method name="create_alternative_tile">
<return type="int" />
<param index="0" name="atlas_coords" type="Vector2i" />
@@ -160,6 +166,12 @@
Returns whether there is enough room in an atlas to create/modify a tile with the given properties. If [param ignored_tile] is provided, act as is the given tile was not present in the atlas. This may be used when you want to modify a tile's properties.
</description>
</method>
+ <method name="has_tiles_outside_texture" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Checks if the source has any tiles that don't fit the texture area (either partially or completely).
+ </description>
+ </method>
<method name="move_tile_in_atlas">
<return type="void" />
<param index="0" name="atlas_coords" type="Vector2i" />
diff --git a/doc/classes/XMLParser.xml b/doc/classes/XMLParser.xml
index 9b960f66fe..36795852e0 100644
--- a/doc/classes/XMLParser.xml
+++ b/doc/classes/XMLParser.xml
@@ -2,10 +2,41 @@
<class name="XMLParser" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Provides a low-level interface for creating parsers for XML files.
- Low-level class for creating parsers for [url=https://en.wikipedia.org/wiki/XML]XML[/url] files.
</brief_description>
<description>
Provides a low-level interface for creating parsers for [url=https://en.wikipedia.org/wiki/XML]XML[/url] files. This class can serve as base to make custom XML parsers.
+ To parse XML, you must open a file with the [method open] method or a buffer with the [method open_buffer] method. Then, the [method read] method must be called to parse the next nodes. Most of the methods take into consideration the currently parsed node.
+ Here is an example of using [XMLParser] to parse a SVG file (which is based on XML), printing each element and its attributes as a dictionary:
+ [codeblocks]
+ [gdscript]
+ var parser = XMLParser.new()
+ parser.open("path/to/file.svg")
+ while parser.read() != ERR_FILE_EOF:
+ if parser.get_node_type() == XMLParser.NODE_ELEMENT:
+ var node_name = parser.get_node_name()
+ var attributes_dict = {}
+ for idx in range(parser.get_attribute_count()):
+ attributes_dict[parser.get_attribute_name(idx)] = parser.get_attribute_value(idx)
+ print("The ", node_name, " element has the following attributes: ", attributes_dict)
+ [/gdscript]
+ [csharp]
+ var parser = new XmlParser();
+ parser.Open("path/to/file.svg");
+ while (parser.Read() != Error.FileEof)
+ {
+ if (parser.GetNodeType() == XmlParser.NodeType.Element)
+ {
+ var nodeName = parser.GetNodeName();
+ var attributesDict = new Godot.Collections.Dictionary();
+ for (int idx = 0; idx &lt; parser.GetAttributeCount(); idx++)
+ {
+ attributesDict[parser.GetAttributeName(idx)] = parser.GetAttributeValue(idx);
+ }
+ GD.Print($"The {nodeName} element has the following attributes: {attributesDict}");
+ }
+ }
+ [/csharp]
+ [/codeblocks]
</description>
<tutorials>
</tutorials>
@@ -13,111 +44,112 @@
<method name="get_attribute_count" qualifiers="const">
<return type="int" />
<description>
- Gets the number of attributes in the current element.
+ Returns the number of attributes in the currently parsed element.
+ [b]Note:[/b] If this method is used while the currently parsed node is not [constant NODE_ELEMENT] or [constant NODE_ELEMENT_END], this count will not be updated and will still reflect the last element.
</description>
</method>
<method name="get_attribute_name" qualifiers="const">
<return type="String" />
<param index="0" name="idx" type="int" />
<description>
- Gets the name of the attribute specified by the [param idx] index.
+ Returns the name of an attribute of the currently parsed element, specified by the [param idx] index.
</description>
</method>
<method name="get_attribute_value" qualifiers="const">
<return type="String" />
<param index="0" name="idx" type="int" />
<description>
- Gets the value of the attribute specified by the [param idx] index.
+ Returns the value of an attribute of the currently parsed element, specified by the [param idx] index.
</description>
</method>
<method name="get_current_line" qualifiers="const">
<return type="int" />
<description>
- Gets the current line in the parsed file, counting from 0.
+ Returns the current line in the parsed file, counting from 0.
</description>
</method>
<method name="get_named_attribute_value" qualifiers="const">
<return type="String" />
<param index="0" name="name" type="String" />
<description>
- Gets the value of a certain attribute of the current element by [param name]. This will raise an error if the element has no such attribute.
+ Returns the value of an attribute of the currently parsed element, specified by its [param name]. This method will raise an error if the element has no such attribute.
</description>
</method>
<method name="get_named_attribute_value_safe" qualifiers="const">
<return type="String" />
<param index="0" name="name" type="String" />
<description>
- Gets the value of a certain attribute of the current element by [param name]. This will return an empty [String] if the attribute is not found.
+ Returns the value of an attribute of the currently parsed element, specified by its [param name]. This method will return an empty string if the element has no such attribute.
</description>
</method>
<method name="get_node_data" qualifiers="const">
<return type="String" />
<description>
- Gets the contents of a text node. This will raise an error in any other type of node.
+ Returns the contents of a text node. This method will raise an error if the current parsed node is of any other type.
</description>
</method>
<method name="get_node_name" qualifiers="const">
<return type="String" />
<description>
- Gets the name of the current element node. This will raise an error if the current node type is neither [constant NODE_ELEMENT] nor [constant NODE_ELEMENT_END].
+ Returns the name of an element node. This method will raise an error if the currently parsed node is not of [constant NODE_ELEMENT] or [constant NODE_ELEMENT_END] type.
</description>
</method>
<method name="get_node_offset" qualifiers="const">
<return type="int" />
<description>
- Gets the byte offset of the current node since the beginning of the file or buffer.
+ Returns the byte offset of the currently parsed node since the beginning of the file or buffer. This is usually equivalent to the number of characters before the read position.
</description>
</method>
<method name="get_node_type">
<return type="int" enum="XMLParser.NodeType" />
<description>
- Gets the type of the current node. Compare with [enum NodeType] constants.
+ Returns the type of the current node. Compare with [enum NodeType] constants.
</description>
</method>
<method name="has_attribute" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="String" />
<description>
- Check whether the current element has a certain attribute.
+ Returns [code]true[/code] if the currently parsed element has an attribute with the [param name].
</description>
</method>
<method name="is_empty" qualifiers="const">
<return type="bool" />
<description>
- Check whether the current element is empty (this only works for completely empty tags, e.g. [code]&lt;element /&gt;[/code]).
+ Returns [code]true[/code] if the currently parsed element is empty, e.g. [code]&lt;element /&gt;[/code].
</description>
</method>
<method name="open">
<return type="int" enum="Error" />
<param index="0" name="file" type="String" />
<description>
- Opens an XML [param file] for parsing. This returns an error code.
+ Opens an XML [param file] for parsing. This method returns an error code.
</description>
</method>
<method name="open_buffer">
<return type="int" enum="Error" />
<param index="0" name="buffer" type="PackedByteArray" />
<description>
- Opens an XML raw [param buffer] for parsing. This returns an error code.
+ Opens an XML raw [param buffer] for parsing. This method returns an error code.
</description>
</method>
<method name="read">
<return type="int" enum="Error" />
<description>
- Reads the next node of the file. This returns an error code.
+ Parses the next node in the file. This method returns an error code.
</description>
</method>
<method name="seek">
<return type="int" enum="Error" />
<param index="0" name="position" type="int" />
<description>
- Moves the buffer cursor to a certain offset (since the beginning) and read the next node there. This returns an error code.
+ Moves the buffer cursor to a certain offset (since the beginning) and reads the next node there. This method returns an error code.
</description>
</method>
<method name="skip_section">
<return type="void" />
<description>
- Skips the current section. If the node contains other elements, they will be ignored and the cursor will go to the closing of the current element.
+ Skips the current section. If the currently parsed node contains more inner nodes, they will be ignored and the cursor will go to the closing of the current element.
</description>
</method>
</methods>
@@ -126,22 +158,22 @@
There's no node (no file or buffer opened).
</constant>
<constant name="NODE_ELEMENT" value="1" enum="NodeType">
- Element (tag).
+ An element node type, also known as a tag, e.g. [code]&lt;title&gt;[/code].
</constant>
<constant name="NODE_ELEMENT_END" value="2" enum="NodeType">
- End of element.
+ An end of element node type, e.g. [code]&lt;/title&gt;[/code].
</constant>
<constant name="NODE_TEXT" value="3" enum="NodeType">
- Text node.
+ A text node type, i.e. text that is not inside an element. This includes whitespace.
</constant>
<constant name="NODE_COMMENT" value="4" enum="NodeType">
- Comment node.
+ A comment node type, e.g. [code]&lt;!--A comment--&gt;[/code].
</constant>
<constant name="NODE_CDATA" value="5" enum="NodeType">
- CDATA content.
+ A node type for CDATA (Character Data) sections, e.g. [code]&lt;![CDATA[CDATA section]]&gt;[/code].
</constant>
<constant name="NODE_UNKNOWN" value="6" enum="NodeType">
- Unknown node.
+ An unknown node type.
</constant>
</constants>
</class>
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index fdc787b4a1..b963224c21 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -771,7 +771,7 @@ void ShaderGLES3::initialize(const String &p_general_defines, int p_base_texture
print_verbose("Shader '" + name + "' SHA256: " + base_sha256);
}
- glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_image_units);
+ glGetInteger64v(GL_MAX_TEXTURE_IMAGE_UNITS, &max_image_units);
}
void ShaderGLES3::set_shader_cache_dir(const String &p_dir) {
diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h
index c095c41e79..7265202ced 100644
--- a/drivers/gles3/shader_gles3.h
+++ b/drivers/gles3/shader_gles3.h
@@ -154,7 +154,7 @@ private:
static bool shader_cache_save_debug;
bool shader_cache_dir_valid = false;
- GLint max_image_units = 0;
+ int64_t max_image_units = 0;
enum StageType {
STAGE_TYPE_VERTEX,
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
index 77a4553c76..03dde88d4b 100644
--- a/drivers/gles3/storage/config.cpp
+++ b/drivers/gles3/storage/config.cpp
@@ -53,9 +53,9 @@ Config::Config() {
singleton = this;
{
- GLint max_extensions = 0;
- glGetIntegerv(GL_NUM_EXTENSIONS, &max_extensions);
- for (int i = 0; i < max_extensions; i++) {
+ int64_t max_extensions = 0;
+ glGetInteger64v(GL_NUM_EXTENSIONS, &max_extensions);
+ for (int64_t i = 0; i < max_extensions; i++) {
const GLubyte *s = glGetStringi(GL_EXTENSIONS, i);
if (!s) {
break;
@@ -88,11 +88,11 @@ Config::Config() {
rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc");
#endif
- glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units);
- glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units);
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
- glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
+ glGetInteger64v(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units);
+ glGetInteger64v(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units);
+ glGetInteger64v(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+ glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
+ glGetInteger64v(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
support_anisotropic_filter = extensions.has("GL_EXT_texture_filter_anisotropic");
if (support_anisotropic_filter) {
diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h
index 623f0442bd..6b90c8c359 100644
--- a/drivers/gles3/storage/config.h
+++ b/drivers/gles3/storage/config.h
@@ -58,14 +58,14 @@ public:
bool use_nearest_mip_filter = false;
bool use_depth_prepass = true;
- int max_vertex_texture_image_units = 0;
- int max_texture_image_units = 0;
- int max_texture_size = 0;
- int max_viewport_size[2] = { 0, 0 };
- int max_uniform_buffer_size = 0;
- int max_renderable_elements = 0;
- int max_renderable_lights = 0;
- int max_lights_per_object = 0;
+ int64_t max_vertex_texture_image_units = 0;
+ int64_t max_texture_image_units = 0;
+ int64_t max_texture_size = 0;
+ int64_t max_viewport_size[2] = { 0, 0 };
+ int64_t max_uniform_buffer_size = 0;
+ int64_t max_renderable_elements = 0;
+ int64_t max_renderable_lights = 0;
+ int64_t max_lights_per_object = 0;
// TODO implement wireframe in OpenGL
// bool generate_wireframes;
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 5ea5553cd3..d8749aa290 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -1122,8 +1122,14 @@ Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
Ref<Script> base_scr = p_script;
while (base_scr.is_valid()) {
// Check for scripted classes.
+ String icon_path;
StringName class_name = script_class_get_name(base_scr->get_path());
- String icon_path = script_class_get_icon_path(class_name);
+ if (base_scr->is_built_in() || class_name == StringName()) {
+ icon_path = base_scr->get_class_icon_path();
+ } else {
+ icon_path = script_class_get_icon_path(class_name);
+ }
+
Ref<Texture2D> icon = _load_script_icon(icon_path);
if (icon.is_valid()) {
_script_icon_cache[p_script] = icon;
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index 74616bc0ce..bc8bcb1134 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -34,7 +34,6 @@
#include "core/io/dir_access.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
-#include "scene/resources/default_theme/default_theme.h"
#include "scene/resources/font.h"
Ref<FontFile> load_external_font(const String &p_path, TextServer::Hinting p_hinting, TextServer::FontAntialiasing p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning, bool p_msdf = false, TypedArray<Font> *r_fallbacks = nullptr) {
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index ae3cd35fca..db61700c00 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -1852,6 +1852,10 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {
if (page == max_page && (MAX(0, count - 1) / page_length != max_page)) {
emit_signal(SNAME("page_change_request"), max_page - 1);
}
+ } else if (p_to_pos == begin_array_index - 1) {
+ emit_signal(SNAME("page_change_request"), page - 1);
+ } else if (p_to_pos > end_array_index) {
+ emit_signal(SNAME("page_change_request"), page + 1);
}
begin_array_index = page * page_length;
end_array_index = MIN(count, (page + 1) * page_length);
@@ -2112,6 +2116,19 @@ void EditorInspectorArray::_setup() {
// Move button.
if (movable) {
+ int element_position = begin_array_index + i;
+ VBoxContainer *move_vbox = memnew(VBoxContainer);
+ move_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
+ move_vbox->set_alignment(BoxContainer::ALIGNMENT_CENTER);
+ ae.hbox->add_child(move_vbox);
+
+ if (element_position > 0) {
+ ae.move_up = memnew(Button);
+ ae.move_up->set_icon(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")));
+ ae.move_up->connect("pressed", callable_mp(this, &EditorInspectorArray::_move_element).bind(element_position, element_position - 1));
+ move_vbox->add_child(ae.move_up);
+ }
+
ae.move_texture_rect = memnew(TextureRect);
ae.move_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
ae.move_texture_rect->set_default_cursor_shape(Control::CURSOR_MOVE);
@@ -2119,7 +2136,14 @@ void EditorInspectorArray::_setup() {
if (is_inside_tree()) {
ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons")));
}
- ae.hbox->add_child(ae.move_texture_rect);
+ move_vbox->add_child(ae.move_texture_rect);
+
+ if (element_position < _get_array_count() - 1) {
+ ae.move_down = memnew(Button);
+ ae.move_down->set_icon(get_theme_icon(SNAME("MoveDown"), SNAME("EditorIcons")));
+ ae.move_down->connect("pressed", callable_mp(this, &EditorInspectorArray::_move_element).bind(element_position, element_position + 2));
+ move_vbox->add_child(ae.move_down);
+ }
}
if (numbered) {
@@ -2220,7 +2244,12 @@ void EditorInspectorArray::_notification(int p_what) {
if (ae.move_texture_rect) {
ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons")));
}
-
+ if (ae.move_up) {
+ ae.move_up->set_icon(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")));
+ }
+ if (ae.move_down) {
+ ae.move_down->set_icon(get_theme_icon(SNAME("MoveDown"), SNAME("EditorIcons")));
+ }
Size2 min_size = get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"))->get_minimum_size();
ae.margin->add_theme_constant_override("margin_left", min_size.x / 2);
ae.margin->add_theme_constant_override("margin_top", min_size.y / 2);
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index e41f18e350..63d3db9b89 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -361,7 +361,9 @@ class EditorInspectorArray : public EditorInspectorSection {
PanelContainer *panel = nullptr;
MarginContainer *margin = nullptr;
HBoxContainer *hbox = nullptr;
+ Button *move_up = nullptr;
TextureRect *move_texture_rect = nullptr;
+ Button *move_down = nullptr;
Label *number = nullptr;
VBoxContainer *vbox = nullptr;
Button *erase = nullptr;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index d6dea2dc79..645d6dd0f9 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -3352,7 +3352,7 @@ void EditorNode::_remove_edited_scene(bool p_change_tab) {
void EditorNode::_remove_scene(int index, bool p_change_tab) {
// Clear icon cache in case some scripts are no longer needed.
- // FIXME: Perfectly the cache should never be cleared and only updated on per-script basis, when an icon changes.
+ // FIXME: Ideally the cache should never be cleared and only updated on per-script basis, when an icon changes.
editor_data.clear_script_icon_cache();
if (editor_data.get_edited_scene() == index) {
@@ -4025,11 +4025,7 @@ void EditorNode::_quick_opened() {
bool open_scene_dialog = quick_open->get_base_type() == "PackedScene";
for (int i = 0; i < files.size(); i++) {
String res_path = files[i];
-
- List<String> scene_extensions;
- ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions);
-
- if (open_scene_dialog || scene_extensions.find(files[i].get_extension().to_lower())) {
+ if (open_scene_dialog || ClassDB::is_parent_class(ResourceLoader::get_resource_type(res_path), "PackedScene")) {
open_request(res_path);
} else {
load_resource(res_path);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index b8a112859e..7446a5b3c7 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -245,7 +245,7 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1,
return style;
}
-// See also `generate_icon()` in `scene/resources/default_theme.cpp`.
+// See also `generate_icon()` in `scene/theme/default_theme.cpp`.
static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float p_saturation, const HashMap<Color, Color> &p_convert_colors = HashMap<Color, Color>()) {
Ref<Image> img = memnew(Image);
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 058cc33c05..1ea5fbc759 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -141,7 +141,9 @@ void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags)
String host = EDITOR_GET("network/debug/remote_host");
int remote_port = (int)EDITOR_GET("network/debug/remote_port");
- if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) {
+ if (EditorSettings::get_singleton()->has_setting("export/android/use_wifi_for_remote_debug") && EDITOR_GET("export/android/use_wifi_for_remote_debug")) {
+ host = EDITOR_GET("export/android/wifi_remote_debug_host");
+ } else if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) {
host = "localhost";
}
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 72933517d7..cf701f1e63 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -264,6 +264,8 @@ void EditorFileDialog::update_dir() {
}
dir->set_text(dir_access->get_current_dir(false));
+ file->set_text("");
+
// Disable "Open" button only when selecting file(s) mode.
get_ok_button()->set_disabled(_is_open_should_be_disabled());
switch (mode) {
@@ -271,10 +273,10 @@ void EditorFileDialog::update_dir() {
case FILE_MODE_OPEN_FILES:
set_ok_button_text(TTR("Open"));
break;
+ case FILE_MODE_OPEN_ANY:
case FILE_MODE_OPEN_DIR:
set_ok_button_text(TTR("Select Current Folder"));
break;
- case FILE_MODE_OPEN_ANY:
case FILE_MODE_SAVE_FILE:
// FIXME: Implement, or refactor to avoid duplication with set_mode
break;
@@ -522,7 +524,11 @@ void EditorFileDialog::_item_selected(int p_item) {
if (!d["dir"]) {
file->set_text(d["name"]);
_request_single_thumbnail(get_current_dir().path_join(get_current_file()));
- } else if (mode == FILE_MODE_OPEN_DIR) {
+
+ // FILE_MODE_OPEN_ANY can alternate this text depending on what's selected.
+ set_ok_button_text(TTR("Open"));
+ } else if (mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY) {
+ file->set_text("");
set_ok_button_text(TTR("Select This Folder"));
}
@@ -560,12 +566,13 @@ void EditorFileDialog::_items_clear_selection(const Vector2 &p_pos, MouseButton
get_ok_button()->set_disabled(!item_list->is_anything_selected());
break;
+ case FILE_MODE_OPEN_ANY:
case FILE_MODE_OPEN_DIR:
+ file->set_text("");
get_ok_button()->set_disabled(false);
set_ok_button_text(TTR("Select Current Folder"));
break;
- case FILE_MODE_OPEN_ANY:
case FILE_MODE_SAVE_FILE:
// FIXME: Implement, or refactor to avoid duplication with set_mode
break;
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 451827da09..fa94a107f0 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -336,9 +336,6 @@ void EditorSpinSlider::_draw_spin_slider() {
int suffix_start = numstr.length();
RID num_rid = TS->create_shaped_text();
TS->shaped_text_add_string(num_rid, numstr + U"\u2009" + suffix, font->get_rids(), font_size, font->get_opentype_features());
- for (int i = 0; i < TextServer::SPACING_MAX; i++) {
- TS->shaped_text_set_spacing(num_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
- }
float text_start = rtl ? Math::round(sb->get_offset().x) : Math::round(sb->get_offset().x + label_width + sep);
Vector2 text_ofs = rtl ? Vector2(text_start + (number_width - TS->shaped_text_get_width(num_rid)), vofs) : Vector2(text_start, vofs);
diff --git a/editor/icons/editor_icons_builders.py b/editor/icons/editor_icons_builders.py
index ae25072a9e..359245b6d7 100644
--- a/editor/icons/editor_icons_builders.py
+++ b/editor/icons/editor_icons_builders.py
@@ -9,7 +9,7 @@ from io import StringIO
from platform_methods import subprocess_main
-# See also `scene/resources/default_theme/default_theme_icons_builders.py`.
+# See also `scene/theme/icons/default_theme_icons_builders.py`.
def make_editor_icons_action(target, source, env):
dst = target[0]
svg_icons = source
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index d38890eb36..4284fd8ae2 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2887,8 +2887,9 @@ void Node3DEditorViewport::_notification(int p_what) {
fps_label->set_visible(show_fps);
RS::get_singleton()->viewport_set_measure_render_time(viewport->get_viewport_rid(), show_fps);
for (int i = 0; i < FRAME_TIME_HISTORY; i++) {
- cpu_time_history[i] = 0;
- gpu_time_history[i] = 0;
+ // Initialize to 120 FPS, so that the initial estimation until we get enough data is always reasonable.
+ cpu_time_history[i] = 8.333333;
+ gpu_time_history[i] = 8.333333;
}
cpu_time_history_index = 0;
gpu_time_history_index = 0;
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index 937480eb50..b91f32775f 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -61,6 +61,10 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
int line_height = 0;
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
Ref<TileSetAtlasSource> atlas_source = p_atlas_sources[source_index];
+ Ref<Image> input_image = atlas_source->get_texture()->get_image();
+ if (input_image->get_format() != Image::FORMAT_RGBA8) {
+ input_image->convert(Image::FORMAT_RGBA8);
+ }
merged_mapping.push_back(HashMap<Vector2i, Vector2i>());
// Layout the tiles.
@@ -70,20 +74,25 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
Vector2i tile_id = atlas_source->get_tile_id(tile_index);
atlas_size = atlas_size.max(tile_id + atlas_source->get_tile_size_in_atlas(tile_id));
- Rect2i new_tile_rect_in_altas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id));
+ Rect2i new_tile_rect_in_atlas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id));
// Copy the texture.
for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame);
- Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size);
+ Vector2i new_position = new_tile_rect_in_atlas.position * new_texture_region_size;
+ if (frame > 0) {
+ new_position += src_rect.size * Vector2i(frame, 0);
+ atlas_size.x = MAX(frame + 1, atlas_size.x);
+ }
+ Rect2 dst_rect_wide = Rect2i(new_position, new_tile_rect_in_atlas.size * new_texture_region_size);
if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
}
- output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
+ output_image->blit_rect(input_image, src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
}
// Add to the mapping.
- merged_mapping[source_index][tile_id] = new_tile_rect_in_altas.position;
+ merged_mapping[source_index][tile_id] = new_tile_rect_in_atlas.position;
}
// Compute the atlas offset.
@@ -110,6 +119,13 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
int changed_id = -1;
if (alternative_id == 0) {
merged->create_tile(tile_mapping.value, atlas_source->get_tile_size_in_atlas(tile_mapping.key));
+ int count = atlas_source->get_tile_animation_frames_count(tile_mapping.key);
+ merged->set_tile_animation_frames_count(tile_mapping.value, count);
+ for (int i = 0; i < count; i++) {
+ merged->set_tile_animation_frame_duration(tile_mapping.value, i, atlas_source->get_tile_animation_frame_duration(tile_mapping.key, i));
+ }
+ merged->set_tile_animation_speed(tile_mapping.value, atlas_source->get_tile_animation_speed(tile_mapping.key));
+ merged->set_tile_animation_mode(tile_mapping.value, atlas_source->get_tile_animation_mode(tile_mapping.key));
} else {
changed_id = merged->create_alternative_tile(tile_mapping.value, alternative_index);
}
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 216dd745cc..fd74e6f8ca 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -632,7 +632,7 @@ TileAtlasView::TileAtlasView() {
panel->add_child(center_container);
missing_source_label = memnew(Label);
- missing_source_label->set_text(TTR("No atlas source with a valid texture selected."));
+ missing_source_label->set_text(TTR("The selected atlas source has no valid texture. Assign a texture in the TileSet bottom tab."));
center_container->add_child(missing_source_label);
margin_container = memnew(MarginContainer);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 6e1841090b..b181835f6f 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -177,7 +177,11 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
if (scene_collection_source) {
texture = tiles_bottom_panel->get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
if (item_text.is_empty()) {
- item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
+ if (scene_collection_source->get_scene_tiles_count() > 0) {
+ item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
+ } else {
+ item_text = vformat(TTR("Empty Scene Collection Source (ID: %d)"), source_id);
+ }
}
}
@@ -398,6 +402,10 @@ void TileMapEditorTilesPlugin::_update_scenes_collection_view() {
scene_tiles_list->select(item_index, false);
}
}
+ if (scene_tiles_list->get_item_count() == 0) {
+ scene_tiles_list->add_item(TTR("The selected scene collection source has no scenes. Add scenes in the TileSet bottom tab."));
+ scene_tiles_list->set_item_disabled(-1, true);
+ }
// Icon size update.
int int_size = int(EDITOR_GET("filesystem/file_dialog/thumbnail_size")) * EDSCALE;
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 8836eac936..a9a1cd76a3 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_toaster.h"
#include "editor/plugins/tiles/tile_set_editor.h"
#include "editor/progress_dialog.h"
@@ -1672,11 +1673,16 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) {
_update_tile_id_label();
} break;
case ADVANCED_AUTO_CREATE_TILES: {
+ atlases_to_auto_create_tiles.clear();
+ atlases_to_auto_create_tiles.append(tile_set_atlas_source);
_auto_create_tiles();
} break;
case ADVANCED_AUTO_REMOVE_TILES: {
_auto_remove_tiles();
} break;
+ case ADVANCED_CLEANUP_TILES: {
+ _cleanup_outside_tiles();
+ } break;
}
}
@@ -2096,6 +2102,8 @@ void TileSetAtlasSourceEditor::_tile_proxy_object_changed(String p_what) {
void TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed(String p_what) {
if (p_what == "texture" && !atlas_source_proxy_object->get("texture").is_null()) {
+ atlases_to_auto_create_tiles.clear();
+ atlases_to_auto_create_tiles.append(tile_set_atlas_source);
confirm_auto_create_tiles->popup_centered();
} else if (p_what == "id") {
emit_signal(SNAME("source_id_changed"), atlas_source_proxy_object->get_id());
@@ -2137,44 +2145,6 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
}
internal_undo_redo->end_force_keep_in_merge_ends();
}
-
- TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited);
- if (atlas_source_proxy) {
- Ref<TileSetAtlasSource> atlas_source = atlas_source_proxy->get_edited();
- ERR_FAIL_COND(!atlas_source.is_valid());
-
- UndoRedo *internal_undo_redo = undo_redo_man->get_history_for_object(atlas_source_proxy).undo_redo;
- internal_undo_redo->start_force_keep_in_merge_ends();
-
- PackedVector2Array arr;
- if (p_property == "texture") {
- arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size());
- } else if (p_property == "margins") {
- arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), p_new_value, atlas_source->get_separation(), atlas_source->get_texture_region_size());
- } else if (p_property == "separation") {
- arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), p_new_value, atlas_source->get_texture_region_size());
- } else if (p_property == "texture_region_size") {
- arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), atlas_source->get_separation(), p_new_value);
- }
-
- if (!arr.is_empty()) {
- // Get all properties assigned to a tile.
- List<PropertyInfo> properties;
- atlas_source->get_property_list(&properties);
-
- for (int i = 0; i < arr.size(); i++) {
- Vector2i coords = arr[i];
- String prefix = vformat("%d:%d/", coords.x, coords.y);
- for (PropertyInfo pi : properties) {
- if (pi.name.begins_with(prefix)) {
- ADD_UNDO(atlas_source_proxy, pi.name);
- }
- }
- }
- }
- internal_undo_redo->end_force_keep_in_merge_ends();
- }
-
#undef ADD_UNDO
}
@@ -2204,6 +2174,14 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
+ if (tile_set_atlas_source) {
+ tile_set_atlas_source->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_update_source_texture));
+ if (atlas_source_texture.is_valid()) {
+ atlas_source_texture->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_check_outside_tiles));
+ atlas_source_texture = Ref<Texture2D>();
+ }
+ }
+
// Clear the selection.
selection.clear();
@@ -2219,6 +2197,11 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
tile_set->connect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
+ if (tile_set_atlas_source) {
+ tile_set_atlas_source->connect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_update_source_texture));
+ _update_source_texture();
+ }
+
if (read_only && tools_button_group->get_pressed_button() == tool_paint_button) {
tool_paint_button->set_pressed(false);
tool_setup_atlas_source_button->set_pressed(true);
@@ -2242,54 +2225,116 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
_update_current_tile_data_editor();
}
-void TileSetAtlasSourceEditor::init_source() {
+void TileSetAtlasSourceEditor::init_new_atlases(const Vector<Ref<TileSetAtlasSource>> &p_atlases) {
tool_setup_atlas_source_button->set_pressed(true);
+ atlases_to_auto_create_tiles = p_atlases;
confirm_auto_create_tiles->popup_centered();
}
-void TileSetAtlasSourceEditor::_auto_create_tiles() {
- if (!tile_set_atlas_source) {
+void TileSetAtlasSourceEditor::_update_source_texture() {
+ if (tile_set_atlas_source && tile_set_atlas_source->get_texture() == atlas_source_texture) {
return;
}
- Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
- if (texture.is_valid()) {
- Vector2i margins = tile_set_atlas_source->get_margins();
- Vector2i separation = tile_set_atlas_source->get_separation();
- Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
- Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Create tiles in non-transparent texture regions"));
- for (int y = 0; y < grid_size.y; y++) {
- for (int x = 0; x < grid_size.x; x++) {
- // Check if we have a tile at the coord
- Vector2i coords = Vector2i(x, y);
- if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
- // Check if the texture is empty at the given coords.
- Rect2i region = Rect2i(margins + (coords * (texture_region_size + separation)), texture_region_size);
- bool is_opaque = false;
- for (int region_x = region.get_position().x; region_x < region.get_end().x; region_x++) {
- for (int region_y = region.get_position().y; region_y < region.get_end().y; region_y++) {
- if (texture->is_pixel_opaque(region_x, region_y)) {
- is_opaque = true;
- break;
+ if (atlas_source_texture.is_valid()) {
+ atlas_source_texture->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_check_outside_tiles));
+ atlas_source_texture = Ref<Texture2D>();
+ }
+
+ if (!tile_set_atlas_source || tile_set_atlas_source->get_texture().is_null()) {
+ return;
+ }
+ atlas_source_texture = tile_set_atlas_source->get_texture();
+ atlas_source_texture->connect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_check_outside_tiles), CONNECT_DEFERRED);
+ _check_outside_tiles();
+}
+
+void TileSetAtlasSourceEditor::_check_outside_tiles() {
+ ERR_FAIL_NULL(tile_set_atlas_source);
+ outside_tiles_warning->set_visible(!read_only && tile_set_atlas_source->has_tiles_outside_texture());
+ tool_advanced_menu_button->get_popup()->set_item_disabled(tool_advanced_menu_button->get_popup()->get_item_index(ADVANCED_CLEANUP_TILES), !tile_set_atlas_source->has_tiles_outside_texture());
+}
+
+void TileSetAtlasSourceEditor::_cleanup_outside_tiles() {
+ ERR_FAIL_NULL(tile_set_atlas_source);
+
+ List<PropertyInfo> list;
+ tile_set_atlas_source->get_property_list(&list);
+ HashMap<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source);
+ Vector<Vector2i> tiles_outside = tile_set_atlas_source->get_tiles_outside_texture();
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Remove Tiles Outside the Texture"));
+
+ undo_redo->add_do_method(tile_set_atlas_source, "clear_tiles_outside_texture");
+ for (const Vector2i &coords : tiles_outside) {
+ undo_redo->add_undo_method(tile_set_atlas_source, "create_tile", coords);
+ if (per_tile.has(coords)) {
+ for (List<const PropertyInfo *>::Element *E_property = per_tile[coords].front(); E_property; E_property = E_property->next()) {
+ String property = E_property->get()->name;
+ Variant value = tile_set_atlas_source->get(property);
+ if (value.get_type() != Variant::NIL) {
+ undo_redo->add_undo_method(tile_set_atlas_source, "set", E_property->get()->name, value);
+ }
+ }
+ }
+ }
+
+ undo_redo->add_do_method(this, "_check_outside_tiles");
+ undo_redo->add_undo_method(this, "_check_outside_tiles");
+ undo_redo->commit_action();
+ outside_tiles_warning->hide();
+}
+
+void TileSetAtlasSourceEditor::_auto_create_tiles() {
+ for (Ref<TileSetAtlasSource> &atlas_source : atlases_to_auto_create_tiles) {
+ if (atlas_source.is_valid()) {
+ Ref<Texture2D> texture = atlas_source->get_texture();
+ if (texture.is_valid()) {
+ Vector2i margins = atlas_source->get_margins();
+ Vector2i separation = atlas_source->get_separation();
+ Vector2i texture_region_size = atlas_source->get_texture_region_size();
+ Size2i grid_size = atlas_source->get_atlas_grid_size();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Create tiles in non-transparent texture regions"));
+ for (int y = 0; y < grid_size.y; y++) {
+ for (int x = 0; x < grid_size.x; x++) {
+ // Check if we have a tile at the coord.
+ Vector2i coords = Vector2i(x, y);
+ if (atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
+ // Check if the texture is empty at the given coords.
+ Rect2i region = Rect2i(margins + (coords * (texture_region_size + separation)), texture_region_size);
+ bool is_opaque = false;
+ for (int region_x = region.get_position().x; region_x < region.get_end().x; region_x++) {
+ for (int region_y = region.get_position().y; region_y < region.get_end().y; region_y++) {
+ if (texture->is_pixel_opaque(region_x, region_y)) {
+ is_opaque = true;
+ break;
+ }
+ }
+ if (is_opaque) {
+ break;
+ }
}
- }
- if (is_opaque) {
- break;
- }
- }
- // If we do have opaque pixels, create a tile.
- if (is_opaque) {
- undo_redo->add_do_method(tile_set_atlas_source, "create_tile", coords);
- undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", coords);
+ // If we do have opaque pixels, create a tile.
+ if (is_opaque) {
+ undo_redo->add_do_method(*atlas_source, "create_tile", coords);
+ undo_redo->add_undo_method(*atlas_source, "remove_tile", coords);
+ }
+ }
}
}
+ undo_redo->commit_action();
}
}
- undo_redo->commit_action();
}
+
+ _cancel_auto_create_tiles();
+}
+
+void TileSetAtlasSourceEditor::_cancel_auto_create_tiles() {
+ atlases_to_auto_create_tiles.clear();
}
void TileSetAtlasSourceEditor::_auto_remove_tiles() {
@@ -2363,8 +2408,8 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
tool_paint_button->set_icon(get_theme_icon(SNAME("CanvasItem"), SNAME("EditorIcons")));
tools_settings_erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
-
tool_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
+ outside_tiles_warning->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
resize_handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons"));
resize_handle_disabled = get_theme_icon(SNAME("EditorHandleDisabled"), SNAME("EditorIcons"));
@@ -2413,6 +2458,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
void TileSetAtlasSourceEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_selection_from_array"), &TileSetAtlasSourceEditor::_set_selection_from_array);
+ ClassDB::bind_method(D_METHOD("_check_outside_tiles"), &TileSetAtlasSourceEditor::_check_outside_tiles);
ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id")));
}
@@ -2555,9 +2601,16 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tool_advanced_menu_button->set_flat(true);
tool_advanced_menu_button->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES);
tool_advanced_menu_button->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES);
+ tool_advanced_menu_button->get_popup()->add_item(TTR("Remove Tiles Outside the Texture"), ADVANCED_CLEANUP_TILES);
tool_advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
tool_settings->add_child(tool_advanced_menu_button);
+ outside_tiles_warning = memnew(TextureRect);
+ outside_tiles_warning->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
+ outside_tiles_warning->set_tooltip_text(vformat(TTR("The current atlas source has tiles outside the texture.\nYou can clear it using \"%s\" option in the 3 dots menu."), TTR("Remove Tiles Outside the Texture")));
+ outside_tiles_warning->hide();
+ tool_settings->add_child(outside_tiles_warning);
+
_update_toolbar();
// Right side of toolbar.
@@ -2644,6 +2697,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
confirm_auto_create_tiles->set_ok_button_text(TTR("Yes"));
confirm_auto_create_tiles->add_cancel_button()->set_text(TTR("No"));
confirm_auto_create_tiles->connect("confirmed", callable_mp(this, &TileSetAtlasSourceEditor::_auto_create_tiles));
+ confirm_auto_create_tiles->connect("canceled", callable_mp(this, &TileSetAtlasSourceEditor::_cancel_auto_create_tiles));
add_child(confirm_auto_create_tiles);
// Inspector plugin.
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index ff928ab2eb..7f6bab804d 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -127,6 +127,7 @@ private:
Ref<TileSet> tile_set;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
int tile_set_atlas_source_id = TileSet::INVALID_SOURCE;
+ Ref<Texture2D> atlas_source_texture;
bool tile_set_changed_needs_update = false;
@@ -205,6 +206,7 @@ private:
ADVANCED_AUTO_CREATE_TILES,
ADVANCED_AUTO_REMOVE_TILES,
+ ADVANCED_CLEANUP_TILES,
};
Vector2i menu_option_coords;
int menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
@@ -222,6 +224,7 @@ private:
HBoxContainer *tool_settings_tile_data_toolbar_container = nullptr;
Button *tools_settings_erase_button = nullptr;
MenuButton *tool_advanced_menu_button = nullptr;
+ TextureRect *outside_tiles_warning = nullptr;
// Selection.
RBSet<TileSelection> selection;
@@ -270,9 +273,15 @@ private:
// -- Misc --
void _auto_create_tiles();
void _auto_remove_tiles();
+ void _cancel_auto_create_tiles();
AcceptDialog *confirm_auto_create_tiles = nullptr;
+ Vector<Ref<TileSetAtlasSource>> atlases_to_auto_create_tiles;
Vector2i _get_drag_offset_tile_coords(const Vector2i &p_offset) const;
+ void _update_source_texture();
+ void _check_outside_tiles();
+ void _cleanup_outside_tiles();
+
void _tile_set_changed();
void _tile_proxy_object_changed(String p_what);
void _atlas_source_proxy_object_changed(String p_what);
@@ -288,7 +297,7 @@ protected:
public:
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
- void init_source();
+ void init_new_atlases(const Vector<Ref<TileSetAtlasSource>> &p_atlases);
TileSetAtlasSourceEditor();
~TileSetAtlasSourceEditor();
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 3aace96b5e..3a21bff947 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -57,35 +57,9 @@ void TileSetEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data,
if (p_from == sources_list) {
// Handle dropping a texture in the list of atlas resources.
- int source_id = TileSet::INVALID_SOURCE;
- int added = 0;
Dictionary d = p_data;
Vector<String> files = d["files"];
- for (int i = 0; i < files.size(); i++) {
- Ref<Texture2D> resource = ResourceLoader::load(files[i]);
- if (resource.is_valid()) {
- // Retrieve the id for the next created source.
- source_id = tile_set->get_next_source_id();
-
- // Actually create the new source.
- Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
- atlas_source->set_texture(resource);
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Add a new atlas source"));
- undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
- undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
- undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
- undo_redo->commit_action();
- added += 1;
- }
- }
-
- if (added == 1) {
- tile_set_atlas_source_editor->init_source();
- }
-
- // Update the selected source (thus triggering an update).
- _update_sources_list(source_id);
+ _load_texture_files(files);
}
}
@@ -126,6 +100,43 @@ bool TileSetEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_da
return false;
}
+void TileSetEditor::_load_texture_files(const Vector<String> &p_paths) {
+ int source_id = TileSet::INVALID_SOURCE;
+ Vector<Ref<TileSetAtlasSource>> atlases;
+
+ for (const String &p_path : p_paths) {
+ Ref<Texture2D> texture = ResourceLoader::load(p_path);
+
+ if (texture.is_null()) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid texture selected."));
+ continue;
+ }
+
+ // Retrieve the id for the next created source.
+ source_id = tile_set->get_next_source_id();
+
+ // Actually create the new source.
+ Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
+ atlas_source->set_texture(texture);
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Add a new atlas source"));
+ undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
+ undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
+ undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
+ undo_redo->commit_action();
+
+ atlases.append(atlas_source);
+ }
+
+ if (!atlases.is_empty()) {
+ tile_set_atlas_source_editor->init_new_atlases(atlases);
+ }
+
+ // Update the selected source (thus triggering an update).
+ _update_sources_list(source_id);
+}
+
void TileSetEditor::_update_sources_list(int force_selected_id) {
if (tile_set.is_null()) {
return;
@@ -181,7 +192,11 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
if (scene_collection_source) {
texture = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
if (item_text.is_empty()) {
- item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
+ if (scene_collection_source->get_scene_tiles_count() > 0) {
+ item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
+ } else {
+ item_text = vformat(TTR("Empty Scene Collection Source (ID: %d)"), source_id);
+ }
}
}
@@ -226,30 +241,6 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
TilesEditorUtils::get_singleton()->set_sources_lists_current(sources_list->get_current());
}
-void TileSetEditor::_texture_file_selected(const String &p_path) {
- Ref<Texture2D> texture = ResourceLoader::load(p_path);
- if (texture.is_null()) {
- EditorNode::get_singleton()->show_warning(TTR("Invalid texture selected."));
- return;
- }
-
- int source_id = tile_set->get_next_source_id();
-
- Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
- atlas_source->set_texture(texture);
-
- // Add a new source.
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Add atlas source"));
- undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
- undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
- undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
- undo_redo->commit_action();
-
- _update_sources_list(source_id);
- tile_set_atlas_source_editor->init_source();
-}
-
void TileSetEditor::_source_selected(int p_source_index) {
ERR_FAIL_COND(!tile_set.is_valid());
@@ -308,8 +299,8 @@ void TileSetEditor::_source_add_id_pressed(int p_id_pressed) {
if (!texture_file_dialog) {
texture_file_dialog = memnew(EditorFileDialog);
add_child(texture_file_dialog);
- texture_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
- texture_file_dialog->connect("file_selected", callable_mp(this, &TileSetEditor::_texture_file_selected));
+ texture_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES);
+ texture_file_dialog->connect("files_selected", callable_mp(this, &TileSetEditor::_load_texture_files));
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Texture2D", &extensions);
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index 86cd70d19e..755b940db2 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -68,6 +68,7 @@ private:
void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+ void _load_texture_files(const Vector<String> &p_paths);
void _update_sources_list(int force_selected_id = -1);
@@ -78,7 +79,6 @@ private:
MenuButton *sources_advanced_menu_button = nullptr;
ItemList *sources_list = nullptr;
Ref<Texture2D> missing_texture_texture;
- void _texture_file_selected(const String &p_path);
void _source_selected(int p_source_index);
void _source_delete_pressed();
void _source_add_id_pressed(int p_id_pressed);
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
index 13270f3821..e357e262c2 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_file_dialog.h"
#include "editor/plugins/tiles/tile_set_editor.h"
#include "scene/gui/button.h"
@@ -239,10 +240,26 @@ void TileSetScenesCollectionSourceEditor::_scenes_list_item_activated(int p_inde
}
void TileSetScenesCollectionSourceEditor::_source_add_pressed() {
+ if (!scene_select_dialog) {
+ scene_select_dialog = memnew(EditorFileDialog);
+ add_child(scene_select_dialog);
+ scene_select_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
+ scene_select_dialog->connect("file_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scene_file_selected));
+
+ for (const String &E : Vector<String>{ "tscn", "scn" }) {
+ scene_select_dialog->add_filter("*." + E, E.to_upper());
+ }
+ }
+ scene_select_dialog->popup_file_dialog();
+}
+
+void TileSetScenesCollectionSourceEditor::_scene_file_selected(const String &p_path) {
+ Ref<PackedScene> scene = ResourceLoader::load(p_path);
+
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add a Scene Tile"));
- undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", Ref<PackedScene>(), scene_id);
+ undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", scene, scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
undo_redo->commit_action();
_update_scenes_list();
@@ -323,6 +340,10 @@ void TileSetScenesCollectionSourceEditor::_update_scenes_list() {
to_reselect = i;
}
}
+ if (scene_tiles_list->get_item_count() == 0) {
+ scene_tiles_list->add_item(TTR("Drag and drop scenes here or use the Add button."));
+ scene_tiles_list->set_item_disabled(-1, true);
+ }
// Reselect if needed.
if (to_reselect >= 0) {
@@ -336,7 +357,6 @@ void TileSetScenesCollectionSourceEditor::_update_scenes_list() {
void TileSetScenesCollectionSourceEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
scene_tile_add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
scene_tile_delete_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
index dd9ec3228d..82de063a07 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
@@ -38,6 +38,7 @@
class Button;
class ItemList;
class Label;
+class EditorFileDialog;
class TileSetScenesCollectionSourceEditor : public HBoxContainer {
GDCLASS(TileSetScenesCollectionSourceEditor, HBoxContainer);
@@ -114,6 +115,7 @@ private:
ItemList *scene_tiles_list = nullptr;
Button *scene_tile_add_button = nullptr;
Button *scene_tile_delete_button = nullptr;
+ EditorFileDialog *scene_select_dialog = nullptr;
void _tile_set_scenes_collection_source_changed();
void _scenes_collection_source_proxy_object_changed(String p_what);
@@ -121,6 +123,7 @@ private:
void _scenes_list_item_activated(int p_index);
void _source_add_pressed();
+ void _scene_file_selected(const String &p_path);
void _source_delete_pressed();
// Update methods.
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index e432704702..7c1cf52925 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -369,6 +369,13 @@ void TileMapEditorPlugin::edit(Object *p_object) {
tile_set_plugin_singleton->make_visible(true);
edited_tileset = tile_map->get_tileset()->get_instance_id();
}
+ } else if (edited_tileset.is_valid()) {
+ // Hide the TileSet editor, unless another TileSet is being edited.
+ if (tile_set_plugin_singleton->get_edited_tileset() == edited_tileset) {
+ tile_set_plugin_singleton->edit(nullptr);
+ tile_set_plugin_singleton->make_visible(false);
+ }
+ edited_tileset = ObjectID();
}
}
@@ -426,6 +433,11 @@ TileMapEditorPlugin::~TileMapEditorPlugin() {
void TileSetEditorPlugin::edit(Object *p_object) {
editor->edit(Ref<TileSet>(p_object));
+ if (p_object) {
+ edited_tileset = p_object->get_instance_id();
+ } else {
+ edited_tileset = ObjectID();
+ }
}
bool TileSetEditorPlugin::handles(Object *p_object) const {
@@ -446,6 +458,10 @@ void TileSetEditorPlugin::make_visible(bool p_visible) {
}
}
+ObjectID TileSetEditorPlugin::get_edited_tileset() const {
+ return edited_tileset;
+}
+
TileSetEditorPlugin::TileSetEditorPlugin() {
DEV_ASSERT(tile_map_plugin_singleton);
tile_set_plugin_singleton = this;
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index 81cb48eb00..6ca115454a 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -118,7 +118,7 @@ class TileMapEditorPlugin : public EditorPlugin {
ObjectID tile_map_id;
bool tile_map_changed_needs_update = false;
- ObjectID edited_tileset;
+ ObjectID edited_tileset; // The TileSet associated with the TileMap.
void _tile_map_changed();
void _update_tile_map();
@@ -147,11 +147,15 @@ class TileSetEditorPlugin : public EditorPlugin {
TileSetEditor *editor = nullptr;
Button *button = nullptr;
+ ObjectID edited_tileset;
+
public:
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
+ ObjectID get_edited_tileset() const;
+
TileSetEditorPlugin();
~TileSetEditorPlugin();
};
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 7d2950f6f6..6736bbc668 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -208,7 +208,7 @@ String ProjectDialog::_test_path() {
}
} else {
- _set_message(TTR("Please choose a \"project.godot\" or \".zip\" file."), MESSAGE_ERROR);
+ _set_message(TTR("Please choose a \"project.godot\", a directory with it, or a \".zip\" file."), MESSAGE_ERROR);
install_path_container->hide();
get_ok_button()->set_disabled(true);
return "";
@@ -283,6 +283,9 @@ void ProjectDialog::_path_text_changed(const String &p_path) {
}
void ProjectDialog::_file_selected(const String &p_path) {
+ // If not already shown.
+ show_dialog();
+
String p = p_path;
if (mode == MODE_IMPORT) {
if (p.ends_with("project.godot")) {
@@ -311,6 +314,9 @@ void ProjectDialog::_file_selected(const String &p_path) {
}
void ProjectDialog::_path_selected(const String &p_path) {
+ // If not already shown.
+ show_dialog();
+
String sp = p_path.simplify_path();
project_path->set_text(sp);
_path_text_changed(sp);
@@ -328,7 +334,7 @@ void ProjectDialog::_browse_path() {
fdialog->set_current_dir(project_path->get_text());
if (mode == MODE_IMPORT) {
- fdialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
+ fdialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_ANY);
fdialog->clear_filters();
fdialog->add_filter("project.godot", vformat("%s %s", VERSION_NAME, TTR("Project")));
fdialog->add_filter("*.zip", TTR("ZIP File"));
@@ -666,6 +672,14 @@ void ProjectDialog::set_project_path(const String &p_path) {
project_path->set_text(p_path);
}
+void ProjectDialog::ask_for_path_and_show() {
+ // Workaround: for the file selection dialog content to be rendered we need to show its parent dialog.
+ show_dialog();
+ _set_message("");
+
+ _browse_path();
+}
+
void ProjectDialog::show_dialog() {
if (mode == MODE_RENAME) {
project_path->set_editable(false);
@@ -2448,7 +2462,7 @@ void ProjectManager::_new_project() {
void ProjectManager::_import_project() {
npdialog->set_mode(ProjectDialog::MODE_IMPORT);
- npdialog->show_dialog();
+ npdialog->ask_for_path_and_show();
}
void ProjectManager::_rename_project() {
diff --git a/editor/project_manager.h b/editor/project_manager.h
index 46c465f24d..554fdbd007 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -131,6 +131,7 @@ public:
void set_mode(Mode p_mode);
void set_project_path(const String &p_path);
+ void ask_for_path_and_show();
void show_dialog();
ProjectDialog();
diff --git a/misc/extension_api_validation/4.0-stable.expected b/misc/extension_api_validation/4.0-stable.expected
index 5f982cbff6..89dbe8ebfa 100644
--- a/misc/extension_api_validation/4.0-stable.expected
+++ b/misc/extension_api_validation/4.0-stable.expected
@@ -451,3 +451,10 @@ Validate extension JSON: Error: Field 'classes/PopupMenu/methods/add_icon_shortc
Validate extension JSON: Error: Field 'classes/PopupMenu/methods/add_shortcut/arguments': size changed value in new API, from 3 to 4.
Added optional argument. Compatibility methods registered.
+
+
+GH-80954
+--------
+Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 4 to 8.
+
+Added optional arguments. Compatibility method registered.
diff --git a/misc/scripts/gitignore_check.sh b/misc/scripts/gitignore_check.sh
new file mode 100644
index 0000000000..f162e25391
--- /dev/null
+++ b/misc/scripts/gitignore_check.sh
@@ -0,0 +1,26 @@
+set -uo pipefail
+shopt -s globstar
+
+echo -e ".gitignore validation..."
+
+# Get a list of files that exist in the repo but are ignored.
+
+# The --verbose flag also includes files un-ignored via ! prefixes.
+# We filter those out with a somewhat awkward `awk` directive.
+ # (Explanation: Split each line by : delimiters,
+ # see if the actual gitignore line shown in the third field starts with !,
+ # if it doesn't, print it.)
+
+# ignorecase for the sake of Windows users.
+
+output=$(git -c core.ignorecase=true check-ignore --verbose --no-index **/* | \
+ awk -F ':' '{ if ($3 !~ /^!/) print $0 }')
+
+# Then we take this result and return success if it's empty.
+if [ -z "$output" ]; then
+ exit 0
+else
+ # And print the result if it isn't.
+ echo "$output"
+ exit 1
+fi
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index dd6b668c45..4f1a256ec9 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -595,7 +595,7 @@
<return type="void" />
<param index="0" name="icon_path" type="String" />
<description>
- Add a custom icon to the current script. The script must be registered as a global class using the [code]class_name[/code] keyword for this to have a visible effect. The icon specified at [param icon_path] is displayed in the Scene dock for every node of that class, as well as in various editor dialogs.
+ Add a custom icon to the current script. The icon specified at [param icon_path] is displayed in the Scene dock for every node of that class, as well as in various editor dialogs.
[codeblock]
@icon("res://path/to/class/icon.svg")
[/codeblock]
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 42b08f8a68..b304bc6ca7 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -482,6 +482,10 @@ void GDScript::_clear_doc() {
docs.clear();
doc = DocData::ClassDoc();
}
+
+String GDScript::get_class_icon_path() const {
+ return simplified_icon_path;
+}
#endif
bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) {
@@ -1728,6 +1732,25 @@ Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool
return Variant::NIL;
}
+void GDScriptInstance::validate_property(PropertyInfo &p_property) const {
+ Variant property = (Dictionary)p_property;
+ const Variant *args[1] = { &property };
+
+ const GDScript *sptr = script.ptr();
+ while (sptr) {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._validate_property);
+ if (E) {
+ Callable::CallError err;
+ Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err);
+ if (err.error == Callable::CallError::CALL_OK) {
+ p_property = PropertyInfo::from_dict(property);
+ return;
+ }
+ }
+ sptr = sptr->_base;
+ }
+}
+
void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
// exported members, not done yet!
@@ -1793,7 +1816,8 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
p_properties->push_back(sptr->get_class_category());
#endif // TOOLS_ENABLED
- for (const PropertyInfo &prop : props) {
+ for (PropertyInfo &prop : props) {
+ validate_property(prop);
p_properties->push_back(prop);
}
@@ -2527,13 +2551,6 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
* Before changing this function, please ask the current maintainer of EditorFileSystem.
*/
- if (r_icon_path) {
- if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
- *r_icon_path = c->icon_path.simplify_path();
- } else if (c->icon_path.is_relative_path()) {
- *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path();
- }
- }
if (r_base_type) {
const GDScriptParser::ClassNode *subclass = c;
String path = p_path;
@@ -2601,6 +2618,9 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
}
}
}
+ if (r_icon_path) {
+ *r_icon_path = c->simplified_icon_path;
+ }
return c->identifier != nullptr ? String(c->identifier->name) : String();
}
@@ -2616,6 +2636,7 @@ GDScriptLanguage::GDScriptLanguage() {
strings._set = StaticCString::create("_set");
strings._get = StaticCString::create("_get");
strings._get_property_list = StaticCString::create("_get_property_list");
+ strings._validate_property = StaticCString::create("_validate_property");
strings._property_can_revert = StaticCString::create("_property_can_revert");
strings._property_get_revert = StaticCString::create("_property_get_revert");
strings._script_source = StaticCString::create("script/source");
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index c41b1a0def..ba21a90ce5 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -144,6 +144,7 @@ class GDScript : public Script {
String path;
String name;
String fully_qualified_name;
+ String simplified_icon_path;
SelfList<GDScript> script_list;
SelfList<GDScriptFunctionState>::List pending_func_states;
@@ -250,6 +251,7 @@ public:
virtual Vector<DocData::ClassDoc> get_documentation() const override {
return docs;
}
+ virtual String get_class_icon_path() const override;
#endif // TOOLS_ENABLED
virtual Error reload(bool p_keep_state = false) override;
@@ -319,6 +321,7 @@ public:
virtual bool get(const StringName &p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const;
+ virtual void validate_property(PropertyInfo &p_property) const;
virtual bool property_can_revert(const StringName &p_name) const;
virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const;
@@ -466,6 +469,7 @@ public:
StringName _set;
StringName _get;
StringName _get_property_list;
+ StringName _validate_property;
StringName _property_can_revert;
StringName _property_get_revert;
StringName _script_source;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index f964db231a..985eb97b29 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -2928,6 +2928,7 @@ void GDScriptCompiler::convert_to_initializer_type(Variant &p_variant, const GDS
void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
p_script->fully_qualified_name = p_class->fqcn;
p_script->name = p_class->identifier ? p_class->identifier->name : "";
+ p_script->simplified_icon_path = p_class->simplified_icon_path;
HashMap<StringName, Ref<GDScript>> old_subclasses;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 0f7166c101..039d46f678 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3840,18 +3840,31 @@ bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p
bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p_node) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)");
ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
+
ClassNode *p_class = static_cast<ClassNode *>(p_node);
+ String path = p_annotation->resolved_arguments[0];
+
#ifdef DEBUG_ENABLED
if (!p_class->icon_path.is_empty()) {
push_error(R"("@icon" annotation can only be used once.)", p_annotation);
return false;
}
- if (String(p_annotation->resolved_arguments[0]).is_empty()) {
+ if (path.is_empty()) {
push_error(R"("@icon" annotation argument must contain the path to the icon.)", p_annotation->arguments[0]);
return false;
}
#endif // DEBUG_ENABLED
- p_class->icon_path = p_annotation->resolved_arguments[0];
+
+ p_class->icon_path = path;
+
+ if (path.is_empty() || path.is_absolute_path()) {
+ p_class->simplified_icon_path = path.simplify_path();
+ } else if (path.is_relative_path()) {
+ p_class->simplified_icon_path = script_path.get_base_dir().path_join(path).simplify_path();
+ } else {
+ p_class->simplified_icon_path = path;
+ }
+
return true;
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index d1ab5f92cc..05c5bc2f11 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -724,6 +724,7 @@ public:
IdentifierNode *identifier = nullptr;
String icon_path;
+ String simplified_icon_path;
Vector<Member> members;
HashMap<StringName, int> members_indices;
ClassNode *outer = nullptr;
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 2971706c75..1fcb2791d9 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1656,7 +1656,8 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
}
}
- for (const PropertyInfo &prop : props) {
+ for (PropertyInfo &prop : props) {
+ validate_property(prop);
p_properties->push_back(prop);
}
}
@@ -1694,6 +1695,24 @@ bool CSharpInstance::property_can_revert(const StringName &p_name) const {
return (bool)ret;
}
+void CSharpInstance::validate_property(PropertyInfo &p_property) const {
+ ERR_FAIL_COND(!script.is_valid());
+
+ Variant property_arg = (Dictionary)p_property;
+ const Variant *args[1] = { &property_arg };
+
+ Variant ret;
+ Callable::CallError call_error;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &SNAME("_validate_property"), args, 1, &call_error, &ret);
+
+ if (call_error.error != Callable::CallError::CALL_OK) {
+ return;
+ }
+
+ p_property = PropertyInfo::from_dict(property_arg);
+}
+
bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const {
ERR_FAIL_COND_V(!script.is_valid(), false);
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 9802067b46..e28c91da2c 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -164,6 +164,9 @@ public:
Vector<DocData::ClassDoc> docs;
return docs;
}
+ virtual String get_class_icon_path() const override {
+ return icon_path;
+ }
#endif // TOOLS_ENABLED
Error reload(bool p_keep_state = false) override;
@@ -255,6 +258,7 @@ public:
bool get(const StringName &p_name, Variant &r_ret) const override;
void get_property_list(List<PropertyInfo> *p_properties) const override;
Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override;
+ virtual void validate_property(PropertyInfo &p_property) const override;
bool property_can_revert(const StringName &p_name) const override;
bool property_get_revert(const StringName &p_name, Variant &r_ret) const override;
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 804a16423d..cbe48db494 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -2336,6 +2336,29 @@ double TextServerAdvanced::_font_get_embolden(const RID &p_font_rid) const {
return fd->embolden;
}
+void TextServerAdvanced::_font_set_spacing(const RID &p_font_rid, SpacingType p_spacing, int64_t p_value) {
+ ERR_FAIL_INDEX((int)p_spacing, 4);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->extra_spacing[p_spacing] != p_value) {
+ _font_clear_cache(fd);
+ fd->extra_spacing[p_spacing] = p_value;
+ }
+}
+
+int64_t TextServerAdvanced::_font_get_spacing(const RID &p_font_rid, SpacingType p_spacing) const {
+ ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);
+
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+
+ return fd->extra_spacing[p_spacing];
+}
+
void TextServerAdvanced::_font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -4129,8 +4152,8 @@ bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const V
} else {
if (gl.font_rid.is_valid()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- sd->ascent = MAX(sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
- sd->descent = MAX(sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
+ sd->ascent = MAX(sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP), -gl.y_off));
+ sd->descent = MAX(sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM), gl.y_off));
} else {
sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
@@ -4384,8 +4407,8 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
} else {
if (gl.font_rid.is_valid()) {
if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) {
- p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
- p_new_sd->descent = MAX(p_new_sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
+ p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP), -gl.y_off));
+ p_new_sd->descent = MAX(p_new_sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM), gl.y_off));
} else {
p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
p_new_sd->descent = MAX(p_new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
@@ -4707,7 +4730,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
int ellipsis_width = 0;
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
- ellipsis_width = 3 * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + (cut_per_word ? whitespace_adv.x : 0);
+ ellipsis_width = 3 * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + _font_get_spacing(dot_gl_font_rid, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
}
int ell_min_characters = 6;
@@ -5559,6 +5582,10 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
_font_set_oversampling(sysf.rid, key.oversampling);
_font_set_embolden(sysf.rid, key.embolden);
_font_set_transform(sysf.rid, key.transform);
+ _font_set_spacing(sysf.rid, SPACING_TOP, key.extra_spacing[SPACING_TOP]);
+ _font_set_spacing(sysf.rid, SPACING_BOTTOM, key.extra_spacing[SPACING_BOTTOM]);
+ _font_set_spacing(sysf.rid, SPACING_SPACE, key.extra_spacing[SPACING_SPACE]);
+ _font_set_spacing(sysf.rid, SPACING_GLYPH, key.extra_spacing[SPACING_GLYPH]);
if (system_fonts.has(key)) {
system_fonts[key].var.push_back(sysf);
@@ -5613,8 +5640,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
Vector2i fss = _get_size(fd, fs);
hb_font_t *hb_font = _font_get_hb_handle(f, fs);
double scale = _font_get_scale(f, fs);
- double sp_sp = p_sd->extra_spacing[SPACING_SPACE];
- double sp_gl = p_sd->extra_spacing[SPACING_GLYPH];
+ double sp_sp = p_sd->extra_spacing[SPACING_SPACE] + _font_get_spacing(f, SPACING_SPACE);
+ double sp_gl = p_sd->extra_spacing[SPACING_GLYPH] + _font_get_spacing(f, SPACING_GLYPH);
bool last_run = (p_sd->end == p_end);
double ea = _get_extra_advance(f, fs);
bool subpos = (scale != 1.0) || (_font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_HALF) || (_font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (_font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_AUTO && fs <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
@@ -5799,8 +5826,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
if (failed_subrun_start != p_end + 1) {
_shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end);
}
- p_sd->ascent = MAX(p_sd->ascent, _font_get_ascent(f, fs));
- p_sd->descent = MAX(p_sd->descent, _font_get_descent(f, fs));
+ p_sd->ascent = MAX(p_sd->ascent, _font_get_ascent(f, fs) + _font_get_spacing(f, SPACING_TOP));
+ p_sd->descent = MAX(p_sd->descent, _font_get_descent(f, fs) + _font_get_spacing(f, SPACING_BOTTOM));
p_sd->upos = MAX(p_sd->upos, _font_get_underline_position(f, fs));
p_sd->uthk = MAX(p_sd->uthk, _font_get_underline_thickness(f, fs));
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index f27fa1dee9..7445becfae 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -316,6 +316,7 @@ class TextServerAdvanced : public TextServerExtension {
String style_name;
int weight = 400;
int stretch = 100;
+ int extra_spacing[4] = { 0, 0, 0, 0 };
HashMap<Vector2i, FontForSizeAdvanced *, VariantHasher, VariantComparator> cache;
@@ -554,9 +555,10 @@ class TextServerAdvanced : public TextServerExtension {
double oversampling = 0.0;
double embolden = 0.0;
Transform2D transform;
+ int extra_spacing[4] = { 0, 0, 0, 0 };
bool operator==(const SystemFontKey &p_b) const {
- return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform);
+ return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform) && (extra_spacing[SPACING_TOP] == p_b.extra_spacing[SPACING_TOP]) && (extra_spacing[SPACING_BOTTOM] == p_b.extra_spacing[SPACING_BOTTOM]) && (extra_spacing[SPACING_SPACE] == p_b.extra_spacing[SPACING_SPACE]) && (extra_spacing[SPACING_GLYPH] == p_b.extra_spacing[SPACING_GLYPH]);
}
SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerAdvanced *p_fb) {
@@ -577,6 +579,10 @@ class TextServerAdvanced : public TextServerExtension {
oversampling = p_fb->_font_get_oversampling(p_font);
embolden = p_fb->_font_get_embolden(p_font);
transform = p_fb->_font_get_transform(p_font);
+ extra_spacing[SPACING_TOP] = p_fb->_font_get_spacing(p_font, SPACING_TOP);
+ extra_spacing[SPACING_BOTTOM] = p_fb->_font_get_spacing(p_font, SPACING_BOTTOM);
+ extra_spacing[SPACING_SPACE] = p_fb->_font_get_spacing(p_font, SPACING_SPACE);
+ extra_spacing[SPACING_GLYPH] = p_fb->_font_get_spacing(p_font, SPACING_GLYPH);
}
};
@@ -605,6 +611,11 @@ class TextServerAdvanced : public TextServerExtension {
hash = hash_murmur3_one_real(p_a.transform[0].y, hash);
hash = hash_murmur3_one_real(p_a.transform[1].x, hash);
hash = hash_murmur3_one_real(p_a.transform[1].y, hash);
+ hash = hash_murmur3_one_32(p_a.extra_spacing[SPACING_TOP], hash);
+ hash = hash_murmur3_one_32(p_a.extra_spacing[SPACING_BOTTOM], hash);
+ hash = hash_murmur3_one_32(p_a.extra_spacing[SPACING_SPACE], hash);
+ hash = hash_murmur3_one_32(p_a.extra_spacing[SPACING_GLYPH], hash);
+
return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash));
}
};
@@ -748,6 +759,9 @@ public:
MODBIND2(font_set_embolden, const RID &, double);
MODBIND1RC(double, font_get_embolden, const RID &);
+ MODBIND3(font_set_spacing, const RID &, SpacingType, int64_t);
+ MODBIND2RC(int64_t, font_get_spacing, const RID &, SpacingType);
+
MODBIND2(font_set_transform, const RID &, const Transform2D &);
MODBIND1RC(Transform2D, font_get_transform, const RID &);
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 4976c70b3b..d0587bf6c0 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -1330,6 +1330,28 @@ double TextServerFallback::_font_get_embolden(const RID &p_font_rid) const {
return fd->embolden;
}
+void TextServerFallback::_font_set_spacing(const RID &p_font_rid, SpacingType p_spacing, int64_t p_value) {
+ ERR_FAIL_INDEX((int)p_spacing, 4);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->extra_spacing[p_spacing] != p_value) {
+ _font_clear_cache(fd);
+ fd->extra_spacing[p_spacing] = p_value;
+ }
+}
+
+int64_t TextServerFallback::_font_get_spacing(const RID &p_font_rid, SpacingType p_spacing) const {
+ ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);
+
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ return fd->extra_spacing[p_spacing];
+}
+
void TextServerFallback::_font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2999,8 +3021,8 @@ bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const V
} else {
if (gl.font_rid.is_valid()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size));
- sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size));
+ sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));
+ sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));
} else {
sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
@@ -3165,8 +3187,8 @@ RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start
} else {
if (gl.font_rid.is_valid()) {
if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
- new_sd->ascent = MAX(new_sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size));
- new_sd->descent = MAX(new_sd->descent, _font_get_descent(gl.font_rid, gl.font_size));
+ new_sd->ascent = MAX(new_sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));
+ new_sd->descent = MAX(new_sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));
} else {
new_sd->ascent = MAX(new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
new_sd->descent = MAX(new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
@@ -3510,7 +3532,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
int ellipsis_width = 0;
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
- ellipsis_width = 3 * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + (cut_per_word ? whitespace_adv.x : 0);
+ ellipsis_width = 3 * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + _font_get_spacing(dot_gl_font_rid, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
}
int ell_min_characters = 6;
@@ -3849,6 +3871,10 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
_font_set_oversampling(sysf.rid, key.oversampling);
_font_set_embolden(sysf.rid, key.embolden);
_font_set_transform(sysf.rid, key.transform);
+ _font_set_spacing(sysf.rid, SPACING_TOP, key.extra_spacing[SPACING_TOP]);
+ _font_set_spacing(sysf.rid, SPACING_BOTTOM, key.extra_spacing[SPACING_BOTTOM]);
+ _font_set_spacing(sysf.rid, SPACING_SPACE, key.extra_spacing[SPACING_SPACE]);
+ _font_set_spacing(sysf.rid, SPACING_GLYPH, key.extra_spacing[SPACING_GLYPH]);
if (system_fonts.has(key)) {
system_fonts[key].var.push_back(sysf);
@@ -3873,8 +3899,8 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
gl.advance = _font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x;
gl.x_off = 0;
gl.y_off = 0;
- sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size));
- sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size));
+ sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));
+ sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));
} else {
gl.advance = _font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y;
gl.x_off = -Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5);
@@ -3886,9 +3912,9 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
if (j < sd->end - 1) {
// Do not add extra spacing to the last glyph of the string.
if (is_whitespace(sd->text[j - sd->start])) {
- gl.advance += sd->extra_spacing[SPACING_SPACE];
+ gl.advance += sd->extra_spacing[SPACING_SPACE] + _font_get_spacing(gl.font_rid, SPACING_SPACE);
} else {
- gl.advance += sd->extra_spacing[SPACING_GLYPH];
+ gl.advance += sd->extra_spacing[SPACING_GLYPH] + _font_get_spacing(gl.font_rid, SPACING_GLYPH);
}
}
sd->upos = MAX(sd->upos, _font_get_underline_position(gl.font_rid, gl.font_size));
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 457573ecb3..c44b45fc27 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -268,6 +268,7 @@ class TextServerFallback : public TextServerExtension {
String style_name;
int weight = 400;
int stretch = 100;
+ int extra_spacing[4] = { 0, 0, 0, 0 };
HashMap<Vector2i, FontForSizeFallback *, VariantHasher, VariantComparator> cache;
@@ -471,9 +472,10 @@ class TextServerFallback : public TextServerExtension {
double oversampling = 0.0;
double embolden = 0.0;
Transform2D transform;
+ int extra_spacing[4] = { 0, 0, 0, 0 };
bool operator==(const SystemFontKey &p_b) const {
- return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform);
+ return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform) && (extra_spacing[SPACING_TOP] == p_b.extra_spacing[SPACING_TOP]) && (extra_spacing[SPACING_BOTTOM] == p_b.extra_spacing[SPACING_BOTTOM]) && (extra_spacing[SPACING_SPACE] == p_b.extra_spacing[SPACING_SPACE]) && (extra_spacing[SPACING_GLYPH] == p_b.extra_spacing[SPACING_GLYPH]);
}
SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerFallback *p_fb) {
@@ -494,6 +496,10 @@ class TextServerFallback : public TextServerExtension {
oversampling = p_fb->_font_get_oversampling(p_font);
embolden = p_fb->_font_get_embolden(p_font);
transform = p_fb->_font_get_transform(p_font);
+ extra_spacing[SPACING_TOP] = p_fb->_font_get_spacing(p_font, SPACING_TOP);
+ extra_spacing[SPACING_BOTTOM] = p_fb->_font_get_spacing(p_font, SPACING_BOTTOM);
+ extra_spacing[SPACING_SPACE] = p_fb->_font_get_spacing(p_font, SPACING_SPACE);
+ extra_spacing[SPACING_GLYPH] = p_fb->_font_get_spacing(p_font, SPACING_GLYPH);
}
};
@@ -522,6 +528,10 @@ class TextServerFallback : public TextServerExtension {
hash = hash_murmur3_one_real(p_a.transform[0].y, hash);
hash = hash_murmur3_one_real(p_a.transform[1].x, hash);
hash = hash_murmur3_one_real(p_a.transform[1].y, hash);
+ hash = hash_murmur3_one_32(p_a.extra_spacing[SPACING_TOP], hash);
+ hash = hash_murmur3_one_32(p_a.extra_spacing[SPACING_BOTTOM], hash);
+ hash = hash_murmur3_one_32(p_a.extra_spacing[SPACING_SPACE], hash);
+ hash = hash_murmur3_one_32(p_a.extra_spacing[SPACING_GLYPH], hash);
return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash));
}
};
@@ -613,6 +623,9 @@ public:
MODBIND2(font_set_embolden, const RID &, double);
MODBIND1RC(double, font_get_embolden, const RID &);
+ MODBIND3(font_set_spacing, const RID &, SpacingType, int64_t);
+ MODBIND2RC(int64_t, font_get_spacing, const RID &, SpacingType);
+
MODBIND2(font_set_transform, const RID &, const Transform2D &);
MODBIND1RC(Transform2D, font_get_transform, const RID &);
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 32b46271fd..682603e46b 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -53,6 +53,9 @@ void register_android_exporter() {
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
EDITOR_DEF("export/android/one_click_deploy_clear_previous_install", false);
+
+ EDITOR_DEF("export/android/use_wifi_for_remote_debug", false);
+ EDITOR_DEF("export/android/wifi_remote_debug_host", "localhost");
#endif
Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 21de46f4fc..15ded5c61e 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1954,8 +1954,9 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
return ERR_SKIP;
}
+ const bool use_wifi_for_remote_debug = EDITOR_GET("export/android/use_wifi_for_remote_debug");
const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
- const bool use_reverse = devices[p_device].api_level >= 21;
+ const bool use_reverse = devices[p_device].api_level >= 21 && !use_wifi_for_remote_debug;
if (use_reverse) {
p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
@@ -2068,7 +2069,10 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
print_line("Reverse result2: " + itos(rv));
}
} else {
- static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---";
+ static const char *const api_version_msg = "--- Device API < 21; debugging over Wi-Fi ---";
+ static const char *const manual_override_msg = "--- Wi-Fi remote debug enabled in project settings; debugging over Wi-Fi ---";
+
+ const char *const msg = use_wifi_for_remote_debug ? manual_override_msg : api_version_msg;
EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR);
print_line(String(msg).to_upper());
}
diff --git a/platform/android/java/editor/src/.gitignore b/platform/android/java/editor/src/.gitignore
new file mode 100644
index 0000000000..c081ec3425
--- /dev/null
+++ b/platform/android/java/editor/src/.gitignore
@@ -0,0 +1 @@
+!/debug
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 7c94e38e14..b661222dfe 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1356,18 +1356,13 @@ Error OS_Windows::shell_open(String p_uri) {
}
Error OS_Windows::shell_show_in_file_manager(String p_path, bool p_open_folder) {
- p_path = p_path.trim_suffix("file://");
-
bool open_folder = false;
if (DirAccess::dir_exists_absolute(p_path) && p_open_folder) {
open_folder = true;
}
- if (p_path.begins_with("\"")) {
- p_path = String("\"") + p_path;
- }
- if (p_path.ends_with("\"")) {
- p_path = p_path + String("\"");
+ if (!p_path.is_quoted()) {
+ p_path = p_path.quote();
}
p_path = p_path.replace("/", "\\");
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index b0e7c73253..3d304de1df 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -473,9 +473,6 @@ void Label3D::_shape() {
String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), language);
- for (int i = 0; i < TextServer::SPACING_MAX; i++) {
- TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
- }
TypedArray<Vector3i> stt;
if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
@@ -493,9 +490,6 @@ void Label3D::_shape() {
for (int i = 0; i < spans; i++) {
TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features());
}
- for (int i = 0; i < TextServer::SPACING_MAX; i++) {
- TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
- }
dirty_font = false;
dirty_lines = true;
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index aec9722fec..cf0627c6f1 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -127,9 +127,6 @@ void Label::_shape() {
TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features());
}
}
- for (int i = 0; i < TextServer::SPACING_MAX; i++) {
- TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
- }
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, txt));
if (!tab_stops.is_empty()) {
TS->shaped_text_tab_align(text_rid, tab_stops);
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 956e93466c..bb727ff62c 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -2314,9 +2314,6 @@ void LineEdit::_shape() {
TS->shaped_text_set_preserve_control(text_rid, draw_control_chars);
TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language);
- for (int i = 0; i < TextServer::SPACING_MAX; i++) {
- TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
- }
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t));
full_width = TS->shaped_text_get_size(text_rid).x;
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index 8db4fe42ef..236dfcc864 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -94,6 +94,10 @@ void Range::set_value(double p_val) {
}
void Range::_set_value_no_signal(double p_val) {
+ if (!Math::is_finite(p_val)) {
+ return;
+ }
+
if (shared->step > 0) {
p_val = Math::round((p_val - shared->min) / shared->step) * shared->step + shared->min;
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 9ebcf18ff6..acf6f9d5aa 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -243,9 +243,6 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
font_size = font_size_it->font_size;
}
TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features());
- for (int j = 0; j < TextServer::SPACING_MAX; j++) {
- TS->shaped_text_set_spacing(t, TextServer::SpacingType(j), font->get_spacing(TextServer::SpacingType(j)));
- }
}
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index e862a76b92..5f8f5225c9 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2524,6 +2524,7 @@ void Viewport::_drop_physics_mouseover(bool p_paused_only) {
void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference) {
List<ObjectID> to_erase;
+ List<ObjectID> to_mouse_exit;
for (const KeyValue<ObjectID, uint64_t> &E : physics_2d_mouseover) {
if (!p_clean_all_frames && E.value == p_frame_reference) {
@@ -2537,7 +2538,7 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
if (p_clean_all_frames && p_paused_only && co->can_process()) {
continue;
}
- co->_mouse_exit();
+ to_mouse_exit.push_back(E.key);
}
}
to_erase.push_back(E.key);
@@ -2550,6 +2551,7 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
// Per-shape.
List<Pair<ObjectID, int>> shapes_to_erase;
+ List<Pair<ObjectID, int>> shapes_to_mouse_exit;
for (KeyValue<Pair<ObjectID, int>, uint64_t> &E : physics_2d_shape_mouseover) {
if (!p_clean_all_frames && E.value == p_frame_reference) {
@@ -2563,7 +2565,7 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
if (p_clean_all_frames && p_paused_only && co->can_process()) {
continue;
}
- co->_mouse_shape_exit(E.key.second);
+ shapes_to_mouse_exit.push_back(E.key);
}
}
shapes_to_erase.push_back(E.key);
@@ -2573,6 +2575,21 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
physics_2d_shape_mouseover.erase(shapes_to_erase.front()->get());
shapes_to_erase.pop_front();
}
+
+ while (to_mouse_exit.size()) {
+ Object *o = ObjectDB::get_instance(to_mouse_exit.front()->get());
+ CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
+ co->_mouse_exit();
+ to_mouse_exit.pop_front();
+ }
+
+ while (shapes_to_mouse_exit.size()) {
+ Pair<ObjectID, int> e = shapes_to_mouse_exit.front()->get();
+ Object *o = ObjectDB::get_instance(e.first);
+ CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
+ co->_mouse_shape_exit(e.second);
+ shapes_to_mouse_exit.pop_front();
+ }
}
void Viewport::_gui_grab_click_focus(Control *p_control) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 4d5d287fbe..5a29e5ff37 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -159,7 +159,6 @@
#include "scene/resources/convex_polygon_shape_3d.h"
#include "scene/resources/curve_texture.h"
#include "scene/resources/cylinder_shape_3d.h"
-#include "scene/resources/default_theme/default_theme.h"
#include "scene/resources/environment.h"
#include "scene/resources/font.h"
#include "scene/resources/gradient.h"
diff --git a/scene/resources/SCsub b/scene/resources/SCsub
index f4dc7a46fb..9e21c627f9 100644
--- a/scene/resources/SCsub
+++ b/scene/resources/SCsub
@@ -22,5 +22,3 @@ env.scene_sources += scene_obj
# Needed to force rebuilding the scene files when the thirdparty code is updated.
env.Depends(scene_obj, thirdparty_obj)
-
-SConscript("default_theme/SCsub")
diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub
deleted file mode 100644
index 5bef7e5a6c..0000000000
--- a/scene/resources/default_theme/SCsub
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-
-from platform_methods import run_in_subprocess
-import default_theme_builders
-import default_theme_icons_builders
-
-env.add_source_files(env.scene_sources, "*.cpp")
-
-env.Depends("#scene/resources/default_theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.woff2")
-env.CommandNoCache(
- "#scene/resources/default_theme/default_font.gen.h",
- "#thirdparty/fonts/OpenSans_SemiBold.woff2",
- run_in_subprocess(default_theme_builders.make_fonts_header),
-)
-
-env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder(
- action=env.Run(
- default_theme_icons_builders.make_default_theme_icons_action, "Generating default project theme icons header."
- ),
- suffix=".h",
- src_suffix=".svg",
-)
-
-# Default theme icons
-icon_sources = Glob("*.svg")
-
-env.Alias(
- "default_theme_icons",
- [env.MakeDefaultThemeIconsBuilder("#scene/resources/default_theme/default_theme_icons.gen.h", icon_sources)],
-)
diff --git a/scene/resources/font.compat.inc b/scene/resources/font.compat.inc
new file mode 100644
index 0000000000..bf50432556
--- /dev/null
+++ b/scene/resources/font.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* font.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+RID Font::_find_variation_compat_80954(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
+ return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, 0, 0, 0, 0);
+}
+
+void Font::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform"), &Font::_find_variation_compat_80954, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()));
+}
+
+#endif
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index df0bd8bc2e..f5edc8d5e9 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "font.h"
+#include "font.compat.inc"
#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
@@ -50,7 +51,7 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_fallbacks"), &Font::get_fallbacks);
// Output.
- ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()));
+ ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids);
// Font metrics.
@@ -916,6 +917,9 @@ void FontFile::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transform", "cache_index", "transform"), &FontFile::set_transform);
ClassDB::bind_method(D_METHOD("get_transform", "cache_index"), &FontFile::get_transform);
+ ClassDB::bind_method(D_METHOD("set_extra_spacing", "cache_index", "spacing", "value"), &FontFile::set_extra_spacing);
+ ClassDB::bind_method(D_METHOD("get_extra_spacing", "cache_index", "spacing"), &FontFile::get_extra_spacing);
+
ClassDB::bind_method(D_METHOD("set_face_index", "cache_index", "face_index"), &FontFile::set_face_index);
ClassDB::bind_method(D_METHOD("get_face_index", "cache_index"), &FontFile::get_face_index);
@@ -1126,6 +1130,18 @@ bool FontFile::_set(const StringName &p_name, const Variant &p_value) {
} else if (tokens.size() == 3 && tokens[2] == "transform") {
set_transform(cache_index, p_value);
return true;
+ } else if (tokens.size() == 3 && tokens[2] == "spacing_top") {
+ set_extra_spacing(cache_index, TextServer::SPACING_TOP, p_value);
+ return true;
+ } else if (tokens.size() == 3 && tokens[2] == "spacing_bottom") {
+ set_extra_spacing(cache_index, TextServer::SPACING_BOTTOM, p_value);
+ return true;
+ } else if (tokens.size() == 3 && tokens[2] == "spacing_space") {
+ set_extra_spacing(cache_index, TextServer::SPACING_SPACE, p_value);
+ return true;
+ } else if (tokens.size() == 3 && tokens[2] == "spacing_glyph") {
+ set_extra_spacing(cache_index, TextServer::SPACING_GLYPH, p_value);
+ return true;
}
if (tokens.size() >= 5) {
Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int());
@@ -1205,6 +1221,18 @@ bool FontFile::_get(const StringName &p_name, Variant &r_ret) const {
} else if (tokens.size() == 3 && tokens[2] == "transform") {
r_ret = get_transform(cache_index);
return true;
+ } else if (tokens.size() == 3 && tokens[2] == "spacing_top") {
+ r_ret = get_extra_spacing(cache_index, TextServer::SPACING_TOP);
+ return true;
+ } else if (tokens.size() == 3 && tokens[2] == "spacing_bottom") {
+ r_ret = get_extra_spacing(cache_index, TextServer::SPACING_BOTTOM);
+ return true;
+ } else if (tokens.size() == 3 && tokens[2] == "spacing_space") {
+ r_ret = get_extra_spacing(cache_index, TextServer::SPACING_SPACE);
+ return true;
+ } else if (tokens.size() == 3 && tokens[2] == "spacing_glyph") {
+ r_ret = get_extra_spacing(cache_index, TextServer::SPACING_GLYPH);
+ return true;
}
if (tokens.size() >= 5) {
Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int());
@@ -1276,6 +1304,10 @@ void FontFile::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, prefix + "face_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + "embolden", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::TRANSFORM2D, prefix + "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, prefix + "spacing_top", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, prefix + "spacing_bottom", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, prefix + "spacing_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, prefix + "spacing_glyph", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
for (int j = 0; j < sizes.size(); j++) {
Vector2i sz = sizes[j];
@@ -2183,7 +2215,7 @@ real_t FontFile::get_oversampling() const {
return oversampling;
}
-RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
+RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph) const {
// Find existing variation cache.
const Dictionary &supported_coords = get_supported_variation_list();
for (int i = 0; i < cache.size(); i++) {
@@ -2193,6 +2225,10 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa
match = match && (TS->font_get_face_index(cache[i]) == p_face_index);
match = match && (TS->font_get_embolden(cache[i]) == p_strength);
match = match && (TS->font_get_transform(cache[i]) == p_transform);
+ match = match && (TS->font_get_spacing(cache[i], TextServer::SPACING_TOP) == p_spacing_top);
+ match = match && (TS->font_get_spacing(cache[i], TextServer::SPACING_BOTTOM) == p_spacing_bottom);
+ match = match && (TS->font_get_spacing(cache[i], TextServer::SPACING_SPACE) == p_spacing_space);
+ match = match && (TS->font_get_spacing(cache[i], TextServer::SPACING_GLYPH) == p_spacing_glyph);
for (const Variant *V = supported_coords.next(nullptr); V && match; V = supported_coords.next(V)) {
const Vector3 &def = supported_coords[*V];
@@ -2231,6 +2267,10 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa
TS->font_set_face_index(cache[idx], p_face_index);
TS->font_set_embolden(cache[idx], p_strength);
TS->font_set_transform(cache[idx], p_transform);
+ TS->font_set_spacing(cache[idx], TextServer::SPACING_TOP, p_spacing_top);
+ TS->font_set_spacing(cache[idx], TextServer::SPACING_BOTTOM, p_spacing_bottom);
+ TS->font_set_spacing(cache[idx], TextServer::SPACING_SPACE, p_spacing_space);
+ TS->font_set_spacing(cache[idx], TextServer::SPACING_GLYPH, p_spacing_glyph);
return cache[idx];
}
@@ -2312,6 +2352,18 @@ Transform2D FontFile::get_transform(int p_cache_index) const {
return TS->font_get_transform(cache[p_cache_index]);
}
+void FontFile::set_extra_spacing(int p_cache_index, TextServer::SpacingType p_spacing, int64_t p_value) {
+ ERR_FAIL_COND(p_cache_index < 0);
+ _ensure_rid(p_cache_index);
+ TS->font_set_spacing(cache[p_cache_index], p_spacing, p_value);
+}
+
+int64_t FontFile::get_extra_spacing(int p_cache_index, TextServer::SpacingType p_spacing) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0);
+ _ensure_rid(p_cache_index);
+ return TS->font_get_spacing(cache[p_cache_index], p_spacing);
+}
+
void FontFile::set_face_index(int p_cache_index, int64_t p_index) {
ERR_FAIL_COND(p_cache_index < 0);
ERR_FAIL_COND(p_index < 0);
@@ -2848,10 +2900,10 @@ int FontVariation::get_spacing(TextServer::SpacingType p_spacing) const {
return extra_spacing[p_spacing];
}
-RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
+RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph) const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
- return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform);
+ return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph);
}
return RID();
}
@@ -2859,7 +2911,7 @@ RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int
RID FontVariation::_get_rid() const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
- return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform);
+ return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform, extra_spacing[TextServer::SPACING_TOP], extra_spacing[TextServer::SPACING_BOTTOM], extra_spacing[TextServer::SPACING_SPACE], extra_spacing[TextServer::SPACING_GLYPH]);
}
return RID();
}
@@ -3328,7 +3380,7 @@ int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const {
}
}
-RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
+RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph) const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
Dictionary var = p_variation_coordinates;
@@ -3344,9 +3396,9 @@ RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_
if (!face_indeces.is_empty()) {
int face_index = CLAMP(p_face_index, 0, face_indeces.size() - 1);
- return f->find_variation(var, face_indeces[face_index], p_strength, p_transform);
+ return f->find_variation(var, face_indeces[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph);
} else {
- return f->find_variation(var, 0, p_strength, p_transform);
+ return f->find_variation(var, 0, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph);
}
}
return RID();
diff --git a/scene/resources/font.h b/scene/resources/font.h
index d8374c4447..30ae11a235 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -103,6 +103,11 @@ protected:
virtual void reset_state() override;
+#ifndef DISABLE_DEPRECATED
+ RID _find_variation_compat_80954(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const;
+ static void _bind_compatibility_methods();
+#endif
+
public:
virtual void _invalidate_rids();
@@ -113,7 +118,7 @@ public:
virtual TypedArray<Font> get_fallbacks() const;
// Output.
- virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const { return RID(); };
+ virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0) const { return RID(); };
virtual RID _get_rid() const { return RID(); };
virtual TypedArray<RID> get_rids() const;
@@ -268,7 +273,7 @@ public:
virtual real_t get_oversampling() const;
// Cache.
- virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override;
+ virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0) const override;
virtual RID _get_rid() const override;
virtual int get_cache_count() const;
@@ -288,6 +293,9 @@ public:
virtual void set_transform(int p_cache_index, Transform2D p_transform);
virtual Transform2D get_transform(int p_cache_index) const;
+ virtual void set_extra_spacing(int p_cache_index, TextServer::SpacingType p_spacing, int64_t p_value);
+ virtual int64_t get_extra_spacing(int p_cache_index, TextServer::SpacingType p_spacing) const;
+
virtual void set_face_index(int p_cache_index, int64_t p_index);
virtual int64_t get_face_index(int p_cache_index) const;
@@ -420,7 +428,7 @@ public:
virtual int get_spacing(TextServer::SpacingType p_spacing) const override;
// Output.
- virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override;
+ virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0) const override;
virtual RID _get_rid() const override;
FontVariation();
@@ -513,7 +521,7 @@ public:
virtual int get_spacing(TextServer::SpacingType p_spacing) const override;
- virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override;
+ virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0) const override;
virtual RID _get_rid() const override;
int64_t get_face_count() const override;
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index f919386980..7120b21190 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -234,13 +234,16 @@ Ref<Resource> ResourceFormatLoaderShader::load(const String &p_path, const Strin
*r_error = ERR_FILE_CANT_OPEN;
}
- Ref<Shader> shader;
- shader.instantiate();
-
- Vector<uint8_t> buffer = FileAccess::get_file_as_bytes(p_path);
+ Error error = OK;
+ Vector<uint8_t> buffer = FileAccess::get_file_as_bytes(p_path, &error);
+ ERR_FAIL_COND_V_MSG(error, nullptr, "Cannot load shader: " + p_path);
String str;
- str.parse_utf8((const char *)buffer.ptr(), buffer.size());
+ error = str.parse_utf8((const char *)buffer.ptr(), buffer.size());
+ ERR_FAIL_COND_V_MSG(error, nullptr, "Cannot parse shader: " + p_path);
+
+ Ref<Shader> shader;
+ shader.instantiate();
shader->set_include_path(p_path);
shader->set_code(str);
diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp
index aead484cc1..323be6e491 100644
--- a/scene/resources/shader_include.cpp
+++ b/scene/resources/shader_include.cpp
@@ -88,13 +88,16 @@ Ref<Resource> ResourceFormatLoaderShaderInclude::load(const String &p_path, cons
*r_error = ERR_FILE_CANT_OPEN;
}
- Ref<ShaderInclude> shader_inc;
- shader_inc.instantiate();
-
- Vector<uint8_t> buffer = FileAccess::get_file_as_bytes(p_path);
+ Error error = OK;
+ Vector<uint8_t> buffer = FileAccess::get_file_as_bytes(p_path, &error);
+ ERR_FAIL_COND_V_MSG(error, nullptr, "Cannot load shader include: " + p_path);
String str;
- str.parse_utf8((const char *)buffer.ptr(), buffer.size());
+ error = str.parse_utf8((const char *)buffer.ptr(), buffer.size());
+ ERR_FAIL_COND_V_MSG(error, nullptr, "Cannot parse shader include: " + p_path);
+
+ Ref<ShaderInclude> shader_inc;
+ shader_inc.instantiate();
shader_inc->set_include_path(p_path);
shader_inc->set_code(str);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index f340573c6a..da3a18e71b 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -3823,7 +3823,6 @@ void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
texture->connect_changed(callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
}
- _clear_tiles_outside_texture();
_queue_update_padded_texture();
emit_changed();
}
@@ -3840,7 +3839,6 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) {
margins = p_margins;
}
- _clear_tiles_outside_texture();
_queue_update_padded_texture();
emit_changed();
}
@@ -3857,7 +3855,6 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) {
separation = p_separation;
}
- _clear_tiles_outside_texture();
_queue_update_padded_texture();
emit_changed();
}
@@ -3874,7 +3871,6 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
texture_region_size = p_tile_size;
}
- _clear_tiles_outside_texture();
_queue_update_padded_texture();
emit_changed();
}
@@ -4360,6 +4356,9 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
if (p_size.x <= 0 || p_size.y <= 0) {
return false;
}
+ if (p_frames_count <= 0) {
+ return false;
+ }
Size2i atlas_grid_size = get_atlas_grid_size();
for (int frame = 0; frame < p_frames_count; frame++) {
Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0));
@@ -4378,6 +4377,40 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
return true;
}
+bool TileSetAtlasSource::has_tiles_outside_texture() const {
+ for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
+ if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+Vector<Vector2i> TileSetAtlasSource::get_tiles_outside_texture() const {
+ Vector<Vector2i> to_return;
+
+ for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
+ if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) {
+ to_return.push_back(E.key);
+ }
+ }
+ return to_return;
+}
+
+void TileSetAtlasSource::clear_tiles_outside_texture() {
+ LocalVector<Vector2i> to_remove;
+
+ for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
+ if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) {
+ to_remove.push_back(E.key);
+ }
+ }
+
+ for (const Vector2i &v : to_remove) {
+ remove_tile(v);
+ }
+}
+
PackedVector2Array TileSetAtlasSource::get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size) {
ERR_FAIL_COND_V(p_margins.x < 0 || p_margins.y < 0, PackedVector2Array());
ERR_FAIL_COND_V(p_separation.x < 0 || p_separation.y < 0, PackedVector2Array());
@@ -4598,6 +4631,9 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tiles_to_be_removed_on_change", "texture", "margins", "separation", "texture_region_size"), &TileSetAtlasSource::get_tiles_to_be_removed_on_change);
ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords);
+ ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture);
+ ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture);
+
ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns);
ClassDB::bind_method(D_METHOD("get_tile_animation_columns", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_columns);
ClassDB::bind_method(D_METHOD("set_tile_animation_separation", "atlas_coords", "separation"), &TileSetAtlasSource::set_tile_animation_separation);
@@ -4704,20 +4740,6 @@ void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) {
}
}
-void TileSetAtlasSource::_clear_tiles_outside_texture() {
- LocalVector<Vector2i> to_remove;
-
- for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
- if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) {
- to_remove.push_back(E.key);
- }
- }
-
- for (const Vector2i &v : to_remove) {
- remove_tile(v);
- }
-}
-
void TileSetAtlasSource::_queue_update_padded_texture() {
padded_texture_needs_update = true;
call_deferred(SNAME("_update_padded_texture"));
@@ -4867,9 +4889,9 @@ void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref<PackedSce
type = scene_state->get_node_type(0);
scene_state = scene_state->get_base_scene_state();
}
- ERR_FAIL_COND_MSG(type.is_empty(), vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Could not get the type of the root node.", p_packed_scene->get_path()));
+ ERR_FAIL_COND_EDMSG(type.is_empty(), vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Could not get the type of the root node.", p_packed_scene->get_path()));
bool extends_correct_class = ClassDB::is_parent_class(type, "CanvasItem");
- ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend CanvasItem. Found %s instead.", p_packed_scene->get_path(), type));
+ ERR_FAIL_COND_EDMSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend CanvasItem. Found %s instead.", p_packed_scene->get_path(), type));
scenes[p_id].scene = p_packed_scene;
} else {
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 4150da53db..4f238f7334 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -634,8 +634,6 @@ private:
void _clear_coords_mapping_cache(Vector2i p_atlas_coords);
void _create_coords_mapping_cache(Vector2i p_atlas_coords);
- void _clear_tiles_outside_texture();
-
bool use_texture_padding = true;
Ref<ImageTexture> padded_texture;
bool padded_texture_needs_update = false;
@@ -702,6 +700,10 @@ public:
PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size);
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
+ bool has_tiles_outside_texture() const;
+ Vector<Vector2i> get_tiles_outside_texture() const;
+ void clear_tiles_outside_texture();
+
// Animation.
void set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns);
int get_tile_animation_columns(const Vector2i p_atlas_coords) const;
diff --git a/scene/theme/SCsub b/scene/theme/SCsub
index fc61250247..34f60b449d 100644
--- a/scene/theme/SCsub
+++ b/scene/theme/SCsub
@@ -2,4 +2,17 @@
Import("env")
+from platform_methods import run_in_subprocess
+import default_theme_builders
+
+
env.add_source_files(env.scene_sources, "*.cpp")
+
+SConscript("icons/SCsub")
+
+env.Depends("#scene/theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.woff2")
+env.CommandNoCache(
+ "#scene/theme/default_font.gen.h",
+ "#thirdparty/fonts/OpenSans_SemiBold.woff2",
+ run_in_subprocess(default_theme_builders.make_fonts_header),
+)
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/theme/default_theme.cpp
index eef46a6798..eef46a6798 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
diff --git a/scene/resources/default_theme/default_theme.h b/scene/theme/default_theme.h
index 55fc32a3d3..55fc32a3d3 100644
--- a/scene/resources/default_theme/default_theme.h
+++ b/scene/theme/default_theme.h
diff --git a/scene/resources/default_theme/default_theme_builders.py b/scene/theme/default_theme_builders.py
index 0455d6d246..0455d6d246 100644
--- a/scene/resources/default_theme/default_theme_builders.py
+++ b/scene/theme/default_theme_builders.py
diff --git a/scene/theme/icons/SCsub b/scene/theme/icons/SCsub
new file mode 100644
index 0000000000..fa9f3cb1ce
--- /dev/null
+++ b/scene/theme/icons/SCsub
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+Import("env")
+
+import default_theme_icons_builders
+
+
+env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder(
+ action=env.Run(
+ default_theme_icons_builders.make_default_theme_icons_action, "Generating default project theme icons header."
+ ),
+ suffix=".h",
+ src_suffix=".svg",
+)
+
+# Default theme icons
+icon_sources = Glob("*.svg")
+
+env.Alias(
+ "default_theme_icons",
+ [env.MakeDefaultThemeIconsBuilder("#scene/theme/default_theme_icons.gen.h", icon_sources)],
+)
diff --git a/scene/resources/default_theme/add.svg b/scene/theme/icons/add.svg
index 818f8353ec..818f8353ec 100644
--- a/scene/resources/default_theme/add.svg
+++ b/scene/theme/icons/add.svg
diff --git a/scene/resources/default_theme/arrow_down.svg b/scene/theme/icons/arrow_down.svg
index 4dfb4a1559..4dfb4a1559 100644
--- a/scene/resources/default_theme/arrow_down.svg
+++ b/scene/theme/icons/arrow_down.svg
diff --git a/scene/resources/default_theme/arrow_left.svg b/scene/theme/icons/arrow_left.svg
index 6fb49d505f..6fb49d505f 100644
--- a/scene/resources/default_theme/arrow_left.svg
+++ b/scene/theme/icons/arrow_left.svg
diff --git a/scene/resources/default_theme/arrow_right.svg b/scene/theme/icons/arrow_right.svg
index 9af063d900..9af063d900 100644
--- a/scene/resources/default_theme/arrow_right.svg
+++ b/scene/theme/icons/arrow_right.svg
diff --git a/scene/resources/default_theme/bookmark.svg b/scene/theme/icons/bookmark.svg
index 15a2e50a3a..15a2e50a3a 100644
--- a/scene/resources/default_theme/bookmark.svg
+++ b/scene/theme/icons/bookmark.svg
diff --git a/scene/resources/default_theme/breakpoint.svg b/scene/theme/icons/breakpoint.svg
index 475e83572c..475e83572c 100644
--- a/scene/resources/default_theme/breakpoint.svg
+++ b/scene/theme/icons/breakpoint.svg
diff --git a/scene/resources/default_theme/checked.svg b/scene/theme/icons/checked.svg
index 49549e4fa4..49549e4fa4 100644
--- a/scene/resources/default_theme/checked.svg
+++ b/scene/theme/icons/checked.svg
diff --git a/scene/resources/default_theme/checked_disabled.svg b/scene/theme/icons/checked_disabled.svg
index 3a3b20793e..3a3b20793e 100644
--- a/scene/resources/default_theme/checked_disabled.svg
+++ b/scene/theme/icons/checked_disabled.svg
diff --git a/scene/resources/default_theme/close.svg b/scene/theme/icons/close.svg
index 6618aa2e14..6618aa2e14 100644
--- a/scene/resources/default_theme/close.svg
+++ b/scene/theme/icons/close.svg
diff --git a/scene/resources/default_theme/close_hl.svg b/scene/theme/icons/close_hl.svg
index 3d74ba54c3..3d74ba54c3 100644
--- a/scene/resources/default_theme/close_hl.svg
+++ b/scene/theme/icons/close_hl.svg
diff --git a/scene/resources/default_theme/color_picker_bar_arrow.svg b/scene/theme/icons/color_picker_bar_arrow.svg
index cb00f648f5..cb00f648f5 100644
--- a/scene/resources/default_theme/color_picker_bar_arrow.svg
+++ b/scene/theme/icons/color_picker_bar_arrow.svg
diff --git a/scene/resources/default_theme/color_picker_cursor.svg b/scene/theme/icons/color_picker_cursor.svg
index 8a7e56b559..8a7e56b559 100644
--- a/scene/resources/default_theme/color_picker_cursor.svg
+++ b/scene/theme/icons/color_picker_cursor.svg
diff --git a/scene/resources/default_theme/color_picker_overbright.svg b/scene/theme/icons/color_picker_overbright.svg
index 86ade4485d..86ade4485d 100644
--- a/scene/resources/default_theme/color_picker_overbright.svg
+++ b/scene/theme/icons/color_picker_overbright.svg
diff --git a/scene/resources/default_theme/color_picker_pipette.svg b/scene/theme/icons/color_picker_pipette.svg
index d167f58e6f..d167f58e6f 100644
--- a/scene/resources/default_theme/color_picker_pipette.svg
+++ b/scene/theme/icons/color_picker_pipette.svg
diff --git a/scene/resources/default_theme/default_theme_icons_builders.py b/scene/theme/icons/default_theme_icons_builders.py
index c4d132294c..c4d132294c 100644
--- a/scene/resources/default_theme/default_theme_icons_builders.py
+++ b/scene/theme/icons/default_theme_icons_builders.py
diff --git a/scene/resources/default_theme/error_icon.svg b/scene/theme/icons/error_icon.svg
index fcfa5d5da2..fcfa5d5da2 100644
--- a/scene/resources/default_theme/error_icon.svg
+++ b/scene/theme/icons/error_icon.svg
diff --git a/scene/resources/default_theme/file.svg b/scene/theme/icons/file.svg
index 6687b53a74..6687b53a74 100644
--- a/scene/resources/default_theme/file.svg
+++ b/scene/theme/icons/file.svg
diff --git a/scene/resources/default_theme/folder.svg b/scene/theme/icons/folder.svg
index bbb47d979d..bbb47d979d 100644
--- a/scene/resources/default_theme/folder.svg
+++ b/scene/theme/icons/folder.svg
diff --git a/scene/resources/default_theme/folder_up.svg b/scene/theme/icons/folder_up.svg
index 54645c1c4b..54645c1c4b 100644
--- a/scene/resources/default_theme/folder_up.svg
+++ b/scene/theme/icons/folder_up.svg
diff --git a/scene/resources/default_theme/graph_port.svg b/scene/theme/icons/graph_port.svg
index 309a21b106..309a21b106 100644
--- a/scene/resources/default_theme/graph_port.svg
+++ b/scene/theme/icons/graph_port.svg
diff --git a/scene/resources/default_theme/grid_layout.svg b/scene/theme/icons/grid_layout.svg
index cd901cd7b1..cd901cd7b1 100644
--- a/scene/resources/default_theme/grid_layout.svg
+++ b/scene/theme/icons/grid_layout.svg
diff --git a/scene/resources/default_theme/grid_minimap.svg b/scene/theme/icons/grid_minimap.svg
index 0c9fca73d2..0c9fca73d2 100644
--- a/scene/resources/default_theme/grid_minimap.svg
+++ b/scene/theme/icons/grid_minimap.svg
diff --git a/scene/resources/default_theme/grid_snap.svg b/scene/theme/icons/grid_snap.svg
index 82b0eae465..82b0eae465 100644
--- a/scene/resources/default_theme/grid_snap.svg
+++ b/scene/theme/icons/grid_snap.svg
diff --git a/scene/resources/default_theme/grid_toggle.svg b/scene/theme/icons/grid_toggle.svg
index b0721db518..b0721db518 100644
--- a/scene/resources/default_theme/grid_toggle.svg
+++ b/scene/theme/icons/grid_toggle.svg
diff --git a/scene/resources/default_theme/hslider_tick.svg b/scene/theme/icons/hslider_tick.svg
index 7110748cbc..7110748cbc 100644
--- a/scene/resources/default_theme/hslider_tick.svg
+++ b/scene/theme/icons/hslider_tick.svg
diff --git a/scene/resources/default_theme/hsplitter.svg b/scene/theme/icons/hsplitter.svg
index 89e2ef4172..89e2ef4172 100644
--- a/scene/resources/default_theme/hsplitter.svg
+++ b/scene/theme/icons/hsplitter.svg
diff --git a/scene/resources/default_theme/indeterminate.svg b/scene/theme/icons/indeterminate.svg
index 2a742e1475..2a742e1475 100644
--- a/scene/resources/default_theme/indeterminate.svg
+++ b/scene/theme/icons/indeterminate.svg
diff --git a/scene/resources/default_theme/line_edit_clear.svg b/scene/theme/icons/line_edit_clear.svg
index 6618aa2e14..6618aa2e14 100644
--- a/scene/resources/default_theme/line_edit_clear.svg
+++ b/scene/theme/icons/line_edit_clear.svg
diff --git a/scene/resources/default_theme/mini_checkerboard.svg b/scene/theme/icons/mini_checkerboard.svg
index 40e6aa1dd0..40e6aa1dd0 100644
--- a/scene/resources/default_theme/mini_checkerboard.svg
+++ b/scene/theme/icons/mini_checkerboard.svg
diff --git a/scene/resources/default_theme/option_button_arrow.svg b/scene/theme/icons/option_button_arrow.svg
index 6a551e7663..6a551e7663 100644
--- a/scene/resources/default_theme/option_button_arrow.svg
+++ b/scene/theme/icons/option_button_arrow.svg
diff --git a/scene/resources/default_theme/picker_shape_circle.svg b/scene/theme/icons/picker_shape_circle.svg
index 1f45259fc5..1f45259fc5 100644
--- a/scene/resources/default_theme/picker_shape_circle.svg
+++ b/scene/theme/icons/picker_shape_circle.svg
diff --git a/scene/resources/default_theme/picker_shape_rectangle.svg b/scene/theme/icons/picker_shape_rectangle.svg
index 4eb335758a..4eb335758a 100644
--- a/scene/resources/default_theme/picker_shape_rectangle.svg
+++ b/scene/theme/icons/picker_shape_rectangle.svg
diff --git a/scene/resources/default_theme/picker_shape_rectangle_wheel.svg b/scene/theme/icons/picker_shape_rectangle_wheel.svg
index 3b70538f1e..3b70538f1e 100644
--- a/scene/resources/default_theme/picker_shape_rectangle_wheel.svg
+++ b/scene/theme/icons/picker_shape_rectangle_wheel.svg
diff --git a/scene/resources/default_theme/popup_menu_arrow_left.svg b/scene/theme/icons/popup_menu_arrow_left.svg
index 642fecf3c7..642fecf3c7 100644
--- a/scene/resources/default_theme/popup_menu_arrow_left.svg
+++ b/scene/theme/icons/popup_menu_arrow_left.svg
diff --git a/scene/resources/default_theme/popup_menu_arrow_right.svg b/scene/theme/icons/popup_menu_arrow_right.svg
index b295df0764..b295df0764 100644
--- a/scene/resources/default_theme/popup_menu_arrow_right.svg
+++ b/scene/theme/icons/popup_menu_arrow_right.svg
diff --git a/scene/resources/default_theme/radio_checked.svg b/scene/theme/icons/radio_checked.svg
index 2d39431bff..2d39431bff 100644
--- a/scene/resources/default_theme/radio_checked.svg
+++ b/scene/theme/icons/radio_checked.svg
diff --git a/scene/resources/default_theme/radio_checked_disabled.svg b/scene/theme/icons/radio_checked_disabled.svg
index 9bcac0fd0f..9bcac0fd0f 100644
--- a/scene/resources/default_theme/radio_checked_disabled.svg
+++ b/scene/theme/icons/radio_checked_disabled.svg
diff --git a/scene/resources/default_theme/radio_unchecked.svg b/scene/theme/icons/radio_unchecked.svg
index abcceabd40..abcceabd40 100644
--- a/scene/resources/default_theme/radio_unchecked.svg
+++ b/scene/theme/icons/radio_unchecked.svg
diff --git a/scene/resources/default_theme/radio_unchecked_disabled.svg b/scene/theme/icons/radio_unchecked_disabled.svg
index 5a1856b5b1..5a1856b5b1 100644
--- a/scene/resources/default_theme/radio_unchecked_disabled.svg
+++ b/scene/theme/icons/radio_unchecked_disabled.svg
diff --git a/scene/resources/default_theme/reload.svg b/scene/theme/icons/reload.svg
index 52f22a943d..52f22a943d 100644
--- a/scene/resources/default_theme/reload.svg
+++ b/scene/theme/icons/reload.svg
diff --git a/scene/resources/default_theme/resizer_nw.svg b/scene/theme/icons/resizer_nw.svg
index c207147dd4..c207147dd4 100644
--- a/scene/resources/default_theme/resizer_nw.svg
+++ b/scene/theme/icons/resizer_nw.svg
diff --git a/scene/resources/default_theme/resizer_se.svg b/scene/theme/icons/resizer_se.svg
index a0383ac4df..a0383ac4df 100644
--- a/scene/resources/default_theme/resizer_se.svg
+++ b/scene/theme/icons/resizer_se.svg
diff --git a/scene/resources/default_theme/scroll_button_left.svg b/scene/theme/icons/scroll_button_left.svg
index 1cf907e044..1cf907e044 100644
--- a/scene/resources/default_theme/scroll_button_left.svg
+++ b/scene/theme/icons/scroll_button_left.svg
diff --git a/scene/resources/default_theme/scroll_button_left_hl.svg b/scene/theme/icons/scroll_button_left_hl.svg
index 2ca045ca24..2ca045ca24 100644
--- a/scene/resources/default_theme/scroll_button_left_hl.svg
+++ b/scene/theme/icons/scroll_button_left_hl.svg
diff --git a/scene/resources/default_theme/scroll_button_right.svg b/scene/theme/icons/scroll_button_right.svg
index 0d17f95ec0..0d17f95ec0 100644
--- a/scene/resources/default_theme/scroll_button_right.svg
+++ b/scene/theme/icons/scroll_button_right.svg
diff --git a/scene/resources/default_theme/scroll_button_right_hl.svg b/scene/theme/icons/scroll_button_right_hl.svg
index 1d29169c37..1d29169c37 100644
--- a/scene/resources/default_theme/scroll_button_right_hl.svg
+++ b/scene/theme/icons/scroll_button_right_hl.svg
diff --git a/scene/resources/default_theme/slider_grabber.svg b/scene/theme/icons/slider_grabber.svg
index 420f5d779b..420f5d779b 100644
--- a/scene/resources/default_theme/slider_grabber.svg
+++ b/scene/theme/icons/slider_grabber.svg
diff --git a/scene/resources/default_theme/slider_grabber_disabled.svg b/scene/theme/icons/slider_grabber_disabled.svg
index 74b1e2214d..74b1e2214d 100644
--- a/scene/resources/default_theme/slider_grabber_disabled.svg
+++ b/scene/theme/icons/slider_grabber_disabled.svg
diff --git a/scene/resources/default_theme/slider_grabber_hl.svg b/scene/theme/icons/slider_grabber_hl.svg
index 126c75f5f7..126c75f5f7 100644
--- a/scene/resources/default_theme/slider_grabber_hl.svg
+++ b/scene/theme/icons/slider_grabber_hl.svg
diff --git a/scene/resources/default_theme/tabs_drop_mark.svg b/scene/theme/icons/tabs_drop_mark.svg
index b1415bec45..b1415bec45 100644
--- a/scene/resources/default_theme/tabs_drop_mark.svg
+++ b/scene/theme/icons/tabs_drop_mark.svg
diff --git a/scene/resources/default_theme/tabs_menu.svg b/scene/theme/icons/tabs_menu.svg
index f132bf8fa2..f132bf8fa2 100644
--- a/scene/resources/default_theme/tabs_menu.svg
+++ b/scene/theme/icons/tabs_menu.svg
diff --git a/scene/resources/default_theme/tabs_menu_hl.svg b/scene/theme/icons/tabs_menu_hl.svg
index fce133f2d9..fce133f2d9 100644
--- a/scene/resources/default_theme/tabs_menu_hl.svg
+++ b/scene/theme/icons/tabs_menu_hl.svg
diff --git a/scene/resources/default_theme/text_edit_ellipsis.svg b/scene/theme/icons/text_edit_ellipsis.svg
index 320d740cee..320d740cee 100644
--- a/scene/resources/default_theme/text_edit_ellipsis.svg
+++ b/scene/theme/icons/text_edit_ellipsis.svg
diff --git a/scene/resources/default_theme/text_edit_space.svg b/scene/theme/icons/text_edit_space.svg
index 5c549918ed..5c549918ed 100644
--- a/scene/resources/default_theme/text_edit_space.svg
+++ b/scene/theme/icons/text_edit_space.svg
diff --git a/scene/resources/default_theme/text_edit_tab.svg b/scene/theme/icons/text_edit_tab.svg
index 3412e283f4..3412e283f4 100644
--- a/scene/resources/default_theme/text_edit_tab.svg
+++ b/scene/theme/icons/text_edit_tab.svg
diff --git a/scene/resources/default_theme/toggle_off.svg b/scene/theme/icons/toggle_off.svg
index aa2805d2cb..aa2805d2cb 100644
--- a/scene/resources/default_theme/toggle_off.svg
+++ b/scene/theme/icons/toggle_off.svg
diff --git a/scene/resources/default_theme/toggle_off_disabled.svg b/scene/theme/icons/toggle_off_disabled.svg
index e82eab7b31..e82eab7b31 100644
--- a/scene/resources/default_theme/toggle_off_disabled.svg
+++ b/scene/theme/icons/toggle_off_disabled.svg
diff --git a/scene/resources/default_theme/toggle_off_disabled_mirrored.svg b/scene/theme/icons/toggle_off_disabled_mirrored.svg
index 6a1ab0433d..6a1ab0433d 100644
--- a/scene/resources/default_theme/toggle_off_disabled_mirrored.svg
+++ b/scene/theme/icons/toggle_off_disabled_mirrored.svg
diff --git a/scene/resources/default_theme/toggle_off_mirrored.svg b/scene/theme/icons/toggle_off_mirrored.svg
index ec568b5c96..ec568b5c96 100644
--- a/scene/resources/default_theme/toggle_off_mirrored.svg
+++ b/scene/theme/icons/toggle_off_mirrored.svg
diff --git a/scene/resources/default_theme/toggle_on.svg b/scene/theme/icons/toggle_on.svg
index 8306d73973..8306d73973 100644
--- a/scene/resources/default_theme/toggle_on.svg
+++ b/scene/theme/icons/toggle_on.svg
diff --git a/scene/resources/default_theme/toggle_on_disabled.svg b/scene/theme/icons/toggle_on_disabled.svg
index 09bd172f66..09bd172f66 100644
--- a/scene/resources/default_theme/toggle_on_disabled.svg
+++ b/scene/theme/icons/toggle_on_disabled.svg
diff --git a/scene/resources/default_theme/toggle_on_disabled_mirrored.svg b/scene/theme/icons/toggle_on_disabled_mirrored.svg
index 2f1e0de7a9..2f1e0de7a9 100644
--- a/scene/resources/default_theme/toggle_on_disabled_mirrored.svg
+++ b/scene/theme/icons/toggle_on_disabled_mirrored.svg
diff --git a/scene/resources/default_theme/toggle_on_mirrored.svg b/scene/theme/icons/toggle_on_mirrored.svg
index 2236d1e23e..2236d1e23e 100644
--- a/scene/resources/default_theme/toggle_on_mirrored.svg
+++ b/scene/theme/icons/toggle_on_mirrored.svg
diff --git a/scene/resources/default_theme/unchecked.svg b/scene/theme/icons/unchecked.svg
index 1a2afa4e3b..1a2afa4e3b 100644
--- a/scene/resources/default_theme/unchecked.svg
+++ b/scene/theme/icons/unchecked.svg
diff --git a/scene/resources/default_theme/unchecked_disabled.svg b/scene/theme/icons/unchecked_disabled.svg
index a319f790e9..a319f790e9 100644
--- a/scene/resources/default_theme/unchecked_disabled.svg
+++ b/scene/theme/icons/unchecked_disabled.svg
diff --git a/scene/resources/default_theme/updown.svg b/scene/theme/icons/updown.svg
index 2258f3b5d1..2258f3b5d1 100644
--- a/scene/resources/default_theme/updown.svg
+++ b/scene/theme/icons/updown.svg
diff --git a/scene/resources/default_theme/visibility_visible.svg b/scene/theme/icons/visibility_visible.svg
index 33e8718941..33e8718941 100644
--- a/scene/resources/default_theme/visibility_visible.svg
+++ b/scene/theme/icons/visibility_visible.svg
diff --git a/scene/resources/default_theme/vslider_tick.svg b/scene/theme/icons/vslider_tick.svg
index a96a8e48bb..a96a8e48bb 100644
--- a/scene/resources/default_theme/vslider_tick.svg
+++ b/scene/theme/icons/vslider_tick.svg
diff --git a/scene/resources/default_theme/vsplitter.svg b/scene/theme/icons/vsplitter.svg
index 37b0aa470e..37b0aa470e 100644
--- a/scene/resources/default_theme/vsplitter.svg
+++ b/scene/theme/icons/vsplitter.svg
diff --git a/scene/resources/default_theme/zoom_less.svg b/scene/theme/icons/zoom_less.svg
index 29bd1708ca..29bd1708ca 100644
--- a/scene/resources/default_theme/zoom_less.svg
+++ b/scene/theme/icons/zoom_less.svg
diff --git a/scene/resources/default_theme/zoom_more.svg b/scene/theme/icons/zoom_more.svg
index 7a844b1418..7a844b1418 100644
--- a/scene/resources/default_theme/zoom_more.svg
+++ b/scene/theme/icons/zoom_more.svg
diff --git a/scene/resources/default_theme/zoom_reset.svg b/scene/theme/icons/zoom_reset.svg
index c12d9c3bf2..c12d9c3bf2 100644
--- a/scene/resources/default_theme/zoom_reset.svg
+++ b/scene/theme/icons/zoom_reset.svg
diff --git a/scene/theme/theme_db.cpp b/scene/theme/theme_db.cpp
index 9b85a62c6e..39a4f078b5 100644
--- a/scene/theme/theme_db.cpp
+++ b/scene/theme/theme_db.cpp
@@ -32,11 +32,11 @@
#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
-#include "scene/resources/default_theme/default_theme.h"
#include "scene/resources/font.h"
#include "scene/resources/style_box.h"
#include "scene/resources/texture.h"
#include "scene/resources/theme.h"
+#include "scene/theme/default_theme.h"
#include "servers/text_server.h"
// Default engine theme creation and configuration.
diff --git a/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp
index 8cd3c22483..3d26a9a8df 100644
--- a/servers/rendering/renderer_rd/effects/debug_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp
@@ -51,6 +51,17 @@ DebugEffects::DebugEffects() {
raster_state.wireframe = true;
shadow_frustum.pipelines[SFP_WIREFRAME].setup(shadow_frustum.shader.version_get_shader(shadow_frustum.shader_version, 0), RD::RENDER_PRIMITIVE_LINES, raster_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
}
+
+ {
+ // Motion Vectors debug shader.
+ Vector<String> modes;
+ modes.push_back("");
+
+ motion_vectors.shader.initialize(modes);
+ motion_vectors.shader_version = motion_vectors.shader.version_create();
+
+ motion_vectors.pipeline.setup(motion_vectors.shader.version_get_shader(motion_vectors.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0);
+ }
}
void DebugEffects::_create_frustum_arrays() {
@@ -163,6 +174,8 @@ DebugEffects::~DebugEffects() {
if (frustum.lines_buffer.is_valid()) {
RD::get_singleton()->free(frustum.lines_buffer); // Array gets freed as dependency.
}
+
+ motion_vectors.shader.version_free(motion_vectors.shader_version);
}
void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect) {
@@ -326,3 +339,26 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj
}
}
}
+
+void DebugEffects::draw_motion_vectors(RID p_velocity, RID p_dest_fb, Size2i p_velocity_size) {
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ RD::Uniform u_source_velocity(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_velocity }));
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, motion_vectors.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_fb), false, RD::get_singleton()->draw_list_get_current_pass()));
+
+ motion_vectors.push_constant.velocity_resolution[0] = p_velocity_size.width;
+ motion_vectors.push_constant.velocity_resolution[1] = p_velocity_size.height;
+
+ RID shader = motion_vectors.shader.version_get_shader(motion_vectors.shader_version, 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_velocity), 0);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &motion_vectors.push_constant, sizeof(MotionVectorsPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
+ RD::get_singleton()->draw_list_end();
+}
diff --git a/servers/rendering/renderer_rd/effects/debug_effects.h b/servers/rendering/renderer_rd/effects/debug_effects.h
index 21b7b03f84..ae32d94912 100644
--- a/servers/rendering/renderer_rd/effects/debug_effects.h
+++ b/servers/rendering/renderer_rd/effects/debug_effects.h
@@ -32,6 +32,7 @@
#define DEBUG_EFFECTS_RD_H
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl.gen.h"
#include "servers/rendering/renderer_scene_render.h"
@@ -70,6 +71,18 @@ private:
PipelineCacheRD pipelines[SFP_MAX];
} shadow_frustum;
+ struct MotionVectorsPushConstant {
+ float velocity_resolution[2];
+ float pad[2];
+ };
+
+ struct {
+ MotionVectorsShaderRD shader;
+ RID shader_version;
+ PipelineCacheRD pipeline;
+ MotionVectorsPushConstant push_constant;
+ } motion_vectors;
+
void _create_frustum_arrays();
protected:
@@ -78,6 +91,7 @@ public:
~DebugEffects();
void draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect);
+ void draw_motion_vectors(RID p_velocity, RID p_dest_fb, Size2i p_velocity_size);
};
} // namespace RendererRD
diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp
index 991ccf984e..d623553273 100644
--- a/servers/rendering/renderer_rd/environment/gi.cpp
+++ b/servers/rendering/renderer_rd/environment/gi.cpp
@@ -395,6 +395,16 @@ Dependency *GI::voxel_gi_get_dependency(RID p_voxel_gi) const {
////////////////////////////////////////////////////////////////////////////////
// SDFGI
+static RID create_clear_texture(const RD::TextureFormat &p_format, const String &p_name) {
+ RID texture = RD::get_singleton()->texture_create(p_format, RD::TextureView());
+ ERR_FAIL_COND_V_MSG(texture.is_null(), RID(), String("Cannot create texture: ") + p_name);
+
+ RD::get_singleton()->set_resource_name(texture, p_name);
+ RD::get_singleton()->texture_clear(texture, Color(0, 0, 0, 0), 0, p_format.mipmaps, 0, p_format.array_layers);
+
+ return texture;
+}
+
void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
@@ -424,39 +434,31 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re
{
RD::TextureFormat tf_render = tf_sdf;
tf_render.format = RD::DATA_FORMAT_R16_UINT;
- render_albedo = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_albedo, "VoxelGI Render Albedo");
+ render_albedo = create_clear_texture(tf_render, "SDFGI Render Albedo");
+
tf_render.format = RD::DATA_FORMAT_R32_UINT;
- render_emission = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_emission, "VoxelGI Render Emission");
- render_emission_aniso = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_emission_aniso, "VoxelGI Render Emission Aniso");
+ render_emission = create_clear_texture(tf_render, "SDFGI Render Emission");
+ render_emission_aniso = create_clear_texture(tf_render, "SDFGI Render Emission Aniso");
tf_render.format = RD::DATA_FORMAT_R8_UNORM; //at least its easy to visualize
for (int i = 0; i < 8; i++) {
- render_occlusion[i] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_occlusion[i], String("VoxelGI Render Occlusion ") + itos(i));
+ render_occlusion[i] = create_clear_texture(tf_render, String("SDFGI Render Occlusion ") + itos(i));
}
tf_render.format = RD::DATA_FORMAT_R32_UINT;
- render_geom_facing = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_geom_facing, "VoxelGI Render Geometry Facing");
+ render_geom_facing = create_clear_texture(tf_render, "SDFGI Render Geometry Facing");
tf_render.format = RD::DATA_FORMAT_R8G8B8A8_UINT;
- render_sdf[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_sdf[0], "VoxelGI Render SDF 0");
- render_sdf[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_sdf[1], "VoxelGI Render SDF 1");
+ render_sdf[0] = create_clear_texture(tf_render, "SDFGI Render SDF 0");
+ render_sdf[1] = create_clear_texture(tf_render, "SDFGI Render SDF 1");
tf_render.width /= 2;
tf_render.height /= 2;
tf_render.depth /= 2;
- render_sdf_half[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_sdf_half[0], "VoxelGI Render SDF Half 0");
- render_sdf_half[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- RD::get_singleton()->set_resource_name(render_sdf_half[1], "VoxelGI Render SDF Half 1");
+ render_sdf_half[0] = create_clear_texture(tf_render, "SDFGI Render SDF Half 0");
+ render_sdf_half[1] = create_clear_texture(tf_render, "SDFGI Render SDF Half 1");
}
RD::TextureFormat tf_occlusion = tf_sdf;
@@ -496,10 +498,8 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re
tf_probe_average.format = RD::DATA_FORMAT_R32G32B32A32_SINT; //signed integer because SH are signed
tf_probe_average.texture_type = RD::TEXTURE_TYPE_2D;
- lightprobe_history_scroll = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView());
- RD::get_singleton()->set_resource_name(lightprobe_history_scroll, "VoxelGI LightProbe History Scroll");
- lightprobe_average_scroll = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView());
- RD::get_singleton()->set_resource_name(lightprobe_average_scroll, "VoxelGI LightProbe Average Scroll");
+ lightprobe_history_scroll = create_clear_texture(tf_probe_history, "SDFGI LightProbe History Scroll");
+ lightprobe_average_scroll = create_clear_texture(tf_probe_average, "SDFGI LightProbe Average Scroll");
{
//octahedral lightprobes
@@ -512,8 +512,7 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re
tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32);
//lightprobe texture is an octahedral texture
- lightprobe_data = RD::get_singleton()->texture_create(tf_octprobes, RD::TextureView());
- RD::get_singleton()->set_resource_name(lightprobe_data, "VoxelGI LightProbe Data");
+ lightprobe_data = create_clear_texture(tf_octprobes, "SDFGI LightProbe Data");
RD::TextureView tv;
tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
lightprobe_texture = RD::get_singleton()->texture_create_shared(tv, lightprobe_data);
@@ -526,14 +525,12 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re
tf_ambient.height = probe_axis_count;
tf_ambient.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
//lightprobe texture is an octahedral texture
- ambient_texture = RD::get_singleton()->texture_create(tf_ambient, RD::TextureView());
- RD::get_singleton()->set_resource_name(ambient_texture, "VoxelGI Ambient Texture");
+ ambient_texture = create_clear_texture(tf_ambient, "SDFGI Ambient Texture");
}
cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES);
- occlusion_data = RD::get_singleton()->texture_create(tf_occlusion, RD::TextureView());
- RD::get_singleton()->set_resource_name(occlusion_data, "VoxelGI Occlusion Data");
+ occlusion_data = create_clear_texture(tf_occlusion, "SDFGI Occlusion Data");
{
RD::TextureView tv;
tv.format_override = RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16;
@@ -543,25 +540,17 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re
for (SDFGI::Cascade &cascade : cascades) {
/* 3D Textures */
- cascade.sdf_tex = RD::get_singleton()->texture_create(tf_sdf, RD::TextureView());
- RD::get_singleton()->set_resource_name(cascade.sdf_tex, "VoxelGI Cascade SDF Texture");
+ cascade.sdf_tex = create_clear_texture(tf_sdf, "SDFGI Cascade SDF Texture");
- cascade.light_data = RD::get_singleton()->texture_create(tf_light, RD::TextureView());
- RD::get_singleton()->set_resource_name(cascade.light_data, "VoxelGI Cascade Light Data");
+ cascade.light_data = create_clear_texture(tf_light, "SDFGI Cascade Light Data");
- cascade.light_aniso_0_tex = RD::get_singleton()->texture_create(tf_aniso0, RD::TextureView());
- RD::get_singleton()->set_resource_name(cascade.light_aniso_0_tex, "VoxelGI Cascade Light Aniso 0 Texture");
- cascade.light_aniso_1_tex = RD::get_singleton()->texture_create(tf_aniso1, RD::TextureView());
- RD::get_singleton()->set_resource_name(cascade.light_aniso_1_tex, "VoxelGI Cascade Light Aniso 1 Texture");
+ cascade.light_aniso_0_tex = create_clear_texture(tf_aniso0, "SDFGI Cascade Light Aniso 0 Texture");
+ cascade.light_aniso_1_tex = create_clear_texture(tf_aniso1, "SDFGI Cascade Light Aniso 1 Texture");
{
RD::TextureView tv;
tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
cascade.light_tex = RD::get_singleton()->texture_create_shared(tv, cascade.light_data);
-
- RD::get_singleton()->texture_clear(cascade.light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(cascade.light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(cascade.light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
}
cascade.cell_size = base_cell_size;
@@ -579,11 +568,11 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re
/* Probe History */
cascade.lightprobe_history_tex = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView());
- RD::get_singleton()->set_resource_name(cascade.lightprobe_history_tex, "VoxelGI Cascade LightProbe History Texture");
+ RD::get_singleton()->set_resource_name(cascade.lightprobe_history_tex, "SDFGI Cascade LightProbe History Texture");
RD::get_singleton()->texture_clear(cascade.lightprobe_history_tex, Color(0, 0, 0, 0), 0, 1, 0, tf_probe_history.array_layers); //needs to be cleared for average to work
cascade.lightprobe_average_tex = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView());
- RD::get_singleton()->set_resource_name(cascade.lightprobe_average_tex, "VoxelGI Cascade LightProbe Average Texture");
+ RD::get_singleton()->set_resource_name(cascade.lightprobe_average_tex, "SDFGI Cascade LightProbe Average Texture");
RD::get_singleton()->texture_clear(cascade.lightprobe_average_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); //needs to be cleared for average to work
/* Buffers */
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index c98752fb39..2e64946fd2 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -252,6 +252,7 @@ bool RenderForwardClustered::free(RID p_rid) {
template <RenderForwardClustered::PassMode p_pass_mode, uint32_t p_color_pass_flags>
void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element) {
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+ RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton();
RD::DrawListID draw_list = p_draw_list;
RD::FramebufferFormatID framebuffer_format = p_framebuffer_Format;
@@ -477,7 +478,9 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
prev_material_uniform_set = material_uniform_set;
}
- if ((surf->owner->base_flags & (INSTANCE_DATA_FLAG_MULTIMESH | INSTANCE_DATA_FLAG_PARTICLES)) == INSTANCE_DATA_FLAG_MULTIMESH) {
+ if (surf->owner->base_flags & INSTANCE_DATA_FLAG_PARTICLES) {
+ particles_storage->particles_get_instance_buffer_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset);
+ } else if (surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH) {
mesh_storage->_multimesh_get_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset);
} else {
push_constant.multimesh_motion_vectors_current_offset = 0;
@@ -3716,6 +3719,10 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p
// Particles haven't been cleared or updated, update once now to ensure they are ready to render.
particles_storage->update_particles();
}
+
+ if (ginstance->data->dirty_dependencies) {
+ particles_storage->particles_update_dependency(ginstance->data->base, &ginstance->data->dependency_tracker);
+ }
} else if (ginstance->data->base_type == RS::INSTANCE_MESH) {
if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) {
ginstance->transforms_uniform_set = mesh_storage->skeleton_get_3d_uniform_set(ginstance->data->skeleton, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET);
@@ -3755,6 +3762,7 @@ void RenderForwardClustered::_geometry_instance_dependency_changed(Dependency::D
case Dependency::DEPENDENCY_CHANGED_MATERIAL:
case Dependency::DEPENDENCY_CHANGED_MESH:
case Dependency::DEPENDENCY_CHANGED_PARTICLES:
+ case Dependency::DEPENDENCY_CHANGED_PARTICLES_INSTANCES:
case Dependency::DEPENDENCY_CHANGED_MULTIMESH:
case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: {
static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty();
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 90d770399e..63d20cf8e1 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -2614,6 +2614,10 @@ void RenderForwardMobile::_geometry_instance_update(RenderGeometryInstance *p_ge
// Particles haven't been cleared or updated, update once now to ensure they are ready to render.
particles_storage->update_particles();
}
+
+ if (ginstance->data->dirty_dependencies) {
+ particles_storage->particles_update_dependency(ginstance->data->base, &ginstance->data->dependency_tracker);
+ }
} else if (ginstance->data->base_type == RS::INSTANCE_MESH) {
if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) {
ginstance->transforms_uniform_set = mesh_storage->skeleton_get_3d_uniform_set(ginstance->data->skeleton, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET);
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 9d4d266a7a..20e24dba0e 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -745,8 +745,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_ren
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS && _render_buffers_get_velocity_texture(rb).is_valid()) {
- Size2i rtsize = texture_storage->render_target_get_size(render_target);
- copy_effects->copy_to_fb_rect(_render_buffers_get_velocity_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false);
+ debug_effects->draw_motion_vectors(_render_buffers_get_velocity_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), rb->get_internal_size());
}
}
diff --git a/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl b/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl
new file mode 100644
index 0000000000..80e4f51565
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl
@@ -0,0 +1,80 @@
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+layout(location = 0) out vec2 uv_interp;
+
+void main() {
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
+}
+
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+layout(location = 0) in vec2 uv_interp;
+
+layout(set = 0, binding = 0) uniform sampler2D source_velocity;
+
+layout(location = 0) out vec4 frag_color;
+
+layout(push_constant, std430) uniform Params {
+ vec2 resolution;
+}
+params;
+
+// Based on distance to line segment from https://www.shadertoy.com/view/3tdSDj
+
+float line_segment(in vec2 p, in vec2 a, in vec2 b) {
+ vec2 aspect = vec2(params.resolution.x / params.resolution.y, 1.0f);
+ vec2 ba = (b - a) * aspect;
+ vec2 pa = (p - a) * aspect;
+ float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0f, 1.0f);
+ return length(pa - h * ba) * (params.resolution.y / 2.0f);
+}
+
+void main() {
+ // Retrieve motion vector data.
+ float cell_size = 32.0f;
+ float circle_radius = 2.0f;
+ vec3 nan_color = vec3(1.0f, 0.0f, 0.0f);
+ vec3 active_color = vec3(1.0f, 0.8f, 0.1f);
+ vec3 inactive_color = vec3(0.5f, 0.5f, 0.5f);
+ vec2 pos_pixel = uv_interp * params.resolution;
+ vec2 cell_pos_pixel = floor(pos_pixel / cell_size) * cell_size + (cell_size * 0.5f);
+ vec2 cell_pos_uv = cell_pos_pixel / params.resolution;
+ vec2 cell_pos_previous_uv = cell_pos_uv + textureLod(source_velocity, cell_pos_uv, 0.0f).xy;
+
+ // Draw the shapes.
+ float epsilon = 1e-6f;
+ vec2 cell_pos_delta_uv = cell_pos_uv - cell_pos_previous_uv;
+ bool motion_active = length(cell_pos_delta_uv) > epsilon;
+ vec3 color;
+ if (any(isnan(cell_pos_delta_uv))) {
+ color = nan_color;
+ } else if (motion_active) {
+ color = active_color;
+ } else {
+ color = inactive_color;
+ }
+
+ float alpha;
+ if (length(cell_pos_pixel - pos_pixel) <= circle_radius) {
+ // Circle center.
+ alpha = 1.0f;
+ } else if (motion_active) {
+ // Motion vector line.
+ alpha = 1.0f - line_segment(uv_interp, cell_pos_uv, cell_pos_previous_uv);
+ } else {
+ // Ignore pixel.
+ alpha = 0.0f;
+ }
+
+ frag_color = vec4(color, alpha);
+}
diff --git a/servers/rendering/renderer_rd/shaders/particles_copy.glsl b/servers/rendering/renderer_rd/shaders/particles_copy.glsl
index 67a894b86f..6a06979c2c 100644
--- a/servers/rendering/renderer_rd/shaders/particles_copy.glsl
+++ b/servers/rendering/renderer_rd/shaders/particles_copy.glsl
@@ -45,6 +45,9 @@ layout(set = 2, binding = 0, std430) restrict readonly buffer TrailBindPoses {
}
trail_bind_poses;
+#define PARAMS_FLAG_ORDER_BY_LIFETIME 1
+#define PARAMS_FLAG_COPY_MODE_2D 2
+
layout(push_constant, std430) uniform Params {
vec3 sort_direction;
uint total_particles;
@@ -57,10 +60,10 @@ layout(push_constant, std430) uniform Params {
vec3 align_up;
uint align_mode;
- bool order_by_lifetime;
uint lifetime_split;
bool lifetime_reverse;
- bool copy_mode_2d;
+ uint motion_vectors_current_offset;
+ uint flags;
mat4 inv_emission_transform;
}
@@ -103,7 +106,7 @@ void main() {
particle = uint(sort_buffer.data[particle].y); //use index from sort buffer
}
#else
- if (params.order_by_lifetime) {
+ if (bool(params.flags & PARAMS_FLAG_ORDER_BY_LIFETIME)) {
if (params.trail_size > 1) {
uint limit = (params.total_particles / params.trail_size) - params.lifetime_split;
@@ -201,7 +204,7 @@ void main() {
txform = txform * trail_bind_poses.data[part_ofs];
}
- if (params.copy_mode_2d) {
+ if (bool(params.flags & PARAMS_FLAG_COPY_MODE_2D)) {
// In global mode, bring 2D particles to local coordinates
// as they will be drawn with the node position as origin.
txform = params.inv_emission_transform * txform;
@@ -213,15 +216,16 @@ void main() {
}
txform = transpose(txform);
- if (params.copy_mode_2d) {
- uint write_offset = gl_GlobalInvocationID.x * (2 + 1 + 1); //xform + color + custom
+ uint instance_index = gl_GlobalInvocationID.x + params.motion_vectors_current_offset;
+ if (bool(params.flags & PARAMS_FLAG_COPY_MODE_2D)) {
+ uint write_offset = instance_index * (2 + 1 + 1); //xform + color + custom
instances.data[write_offset + 0] = txform[0];
instances.data[write_offset + 1] = txform[1];
instances.data[write_offset + 2] = particles.data[particle].color;
instances.data[write_offset + 3] = particles.data[particle].custom;
} else {
- uint write_offset = gl_GlobalInvocationID.x * (3 + 1 + 1); //xform + color + custom
+ uint write_offset = instance_index * (3 + 1 + 1); //xform + color + custom
instances.data[write_offset + 0] = txform[0];
instances.data[write_offset + 1] = txform[1];
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index 0a91672544..3a415e97e0 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -666,6 +666,19 @@ RID ParticlesStorage::particles_get_draw_pass_mesh(RID p_particles, int p_pass)
return particles->draw_passes[p_pass];
}
+void ParticlesStorage::particles_update_dependency(RID p_particles, DependencyTracker *p_instance) {
+ Particles *particles = particles_owner.get_or_null(p_particles);
+ ERR_FAIL_COND(!particles);
+ p_instance->update_dependency(&particles->dependency);
+}
+
+void ParticlesStorage::particles_get_instance_buffer_motion_vectors_offsets(RID p_particles, uint32_t &r_current_offset, uint32_t &r_prev_offset) {
+ Particles *particles = particles_owner.get_or_null(p_particles);
+ ERR_FAIL_COND(!particles);
+ r_current_offset = particles->instance_motion_vectors_current_offset;
+ r_prev_offset = particles->instance_motion_vectors_previous_offset;
+}
+
void ParticlesStorage::particles_add_collision(RID p_particles, RID p_particles_collision_instance) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_COND(!particles);
@@ -1185,6 +1198,7 @@ void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p
copy_push_constant.order_by_lifetime = (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME);
copy_push_constant.lifetime_split = (MIN(int(particles->amount * particles->phase), particles->amount - 1) + 1) % particles->amount;
copy_push_constant.lifetime_reverse = particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME;
+ copy_push_constant.motion_vectors_current_offset = particles->instance_motion_vectors_current_offset;
copy_push_constant.frame_remainder = particles->interpolate ? particles->frame_remainder : 0.0;
copy_push_constant.total_particles = particles->amount;
@@ -1252,28 +1266,50 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) {
userdata_count = particle_shader_data->userdata_count;
}
+ bool uses_motion_vectors = RSG::viewport->get_num_viewports_with_motion_vectors() > 0;
+ bool index_draw_order = particles->draw_order == RS::ParticlesDrawOrder::PARTICLES_DRAW_ORDER_INDEX;
+ bool enable_motion_vectors = uses_motion_vectors && index_draw_order && !particles->instance_motion_vectors_enabled;
+ bool only_instances_changed = false;
+
if (userdata_count != particles->userdata_count) {
- // Mismatch userdata, re-create buffers.
+ // Mismatch userdata, re-create all buffers.
_particles_free_data(particles);
+ } else if (enable_motion_vectors) {
+ // Only motion vectors are required, release the transforms buffer and uniform set.
+ if (particles->particle_instance_buffer.is_valid()) {
+ RD::get_singleton()->free(particles->particle_instance_buffer);
+ particles->particle_instance_buffer = RID();
+ }
+
+ particles->particles_transforms_buffer_uniform_set = RID();
+ only_instances_changed = true;
+ } else if (!particles->particle_buffer.is_null()) {
+ // No operation is required because a buffer already exists, return early.
+ return;
}
- if (particles->amount > 0 && particles->particle_buffer.is_null()) {
+ if (particles->amount > 0) {
int total_amount = particles->amount;
if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) {
total_amount *= particles->trail_bind_poses.size();
}
uint32_t xform_size = particles->mode == RS::PARTICLES_MODE_2D ? 2 : 3;
-
- particles->particle_buffer = RD::get_singleton()->storage_buffer_create((sizeof(ParticleData) + userdata_count * sizeof(float) * 4) * total_amount);
-
- particles->userdata_count = userdata_count;
+ if (particles->particle_buffer.is_null()) {
+ particles->particle_buffer = RD::get_singleton()->storage_buffer_create((sizeof(ParticleData) + userdata_count * sizeof(float) * 4) * total_amount);
+ particles->userdata_count = userdata_count;
+ }
PackedByteArray data;
- data.resize_zeroed(sizeof(float) * 4 * (xform_size + 1 + 1) * total_amount);
+ uint32_t particle_instance_buffer_size = total_amount * (xform_size + 1 + 1) * sizeof(float) * 4;
+ if (uses_motion_vectors) {
+ particle_instance_buffer_size *= 2;
+ particles->instance_motion_vectors_enabled = true;
+ }
- particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (xform_size + 1 + 1) * total_amount, data);
- //needs to clear it
+ data.resize_zeroed(particle_instance_buffer_size);
+
+ particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(particle_instance_buffer_size, data);
{
Vector<RD::Uniform> uniforms;
@@ -1295,9 +1331,20 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) {
particles->particles_copy_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, 0), 0);
}
+
+ particles->instance_motion_vectors_current_offset = 0;
+ particles->instance_motion_vectors_previous_offset = 0;
+ particles->instance_motion_vectors_last_change = -1;
+
+ if (only_instances_changed) {
+ // Notify the renderer the instances uniform must be retrieved again, as it's the only element that has been changed because motion vectors were enabled.
+ particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES_INSTANCES);
+ }
}
}
void ParticlesStorage::update_particles() {
+ uint32_t frame = RSG::rasterizer->get_frame_number();
+ bool uses_motion_vectors = RSG::viewport->get_num_viewports_with_motion_vectors() > 0;
while (particle_update_list) {
//use transform feedback to process particles
@@ -1461,16 +1508,25 @@ void ParticlesStorage::update_particles() {
// Ensure that memory is initialized (the code above should ensure that _particles_process is always called at least once upon clearing).
DEV_ASSERT(!particles->clear);
+ int total_amount = particles->amount;
+ if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) {
+ total_amount *= particles->trail_bind_poses.size();
+ }
+
+ // Swap offsets for motion vectors. Motion vectors can only be used when the draw order keeps the indices consistent across frames.
+ bool index_draw_order = particles->draw_order == RS::ParticlesDrawOrder::PARTICLES_DRAW_ORDER_INDEX;
+ particles->instance_motion_vectors_previous_offset = particles->instance_motion_vectors_current_offset;
+ if (uses_motion_vectors && index_draw_order && particles->instance_motion_vectors_enabled && (frame - particles->instance_motion_vectors_last_change) == 1) {
+ particles->instance_motion_vectors_current_offset = total_amount - particles->instance_motion_vectors_current_offset;
+ }
+
+ particles->instance_motion_vectors_last_change = frame;
+
// Copy particles to instance buffer.
if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
//does not need view dependent operation, do copy here
ParticlesShader::CopyPushConstant copy_push_constant;
- int total_amount = particles->amount;
- if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) {
- total_amount *= particles->trail_bind_poses.size();
- }
-
// Affect 2D only.
if (particles->use_local_coords) {
// In local mode, particle positions are calculated locally (relative to the node position)
@@ -1506,6 +1562,7 @@ void ParticlesStorage::update_particles() {
copy_push_constant.order_by_lifetime = (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME);
copy_push_constant.lifetime_split = (MIN(int(particles->amount * particles->phase), particles->amount - 1) + 1) % particles->amount;
copy_push_constant.lifetime_reverse = particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME;
+ copy_push_constant.motion_vectors_current_offset = particles->instance_motion_vectors_current_offset;
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
copy_push_constant.copy_mode_2d = particles->mode == RS::PARTICLES_MODE_2D ? 1 : 0;
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h
index 612420a36c..7cea706a8c 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h
@@ -225,6 +225,11 @@ private:
double frame_remainder = 0;
real_t collision_base_size = 0.01;
+ uint32_t instance_motion_vectors_current_offset = 0;
+ uint32_t instance_motion_vectors_previous_offset = 0;
+ uint64_t instance_motion_vectors_last_change = -1;
+ bool instance_motion_vectors_enabled = false;
+
bool clear = true;
bool force_sub_emit = false;
@@ -288,10 +293,13 @@ private:
float align_up[3];
uint32_t align_mode;
- uint32_t order_by_lifetime;
uint32_t lifetime_split;
uint32_t lifetime_reverse;
- uint32_t copy_mode_2d;
+ uint32_t motion_vectors_current_offset;
+ struct {
+ uint32_t order_by_lifetime : 1;
+ uint32_t copy_mode_2d : 1;
+ };
float inv_emission_transform[16];
};
@@ -521,12 +529,15 @@ public:
return particles->particles_transforms_buffer_uniform_set;
}
+ void particles_get_instance_buffer_motion_vectors_offsets(RID p_particles, uint32_t &r_current_offset, uint32_t &r_prev_offset);
+
virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance) override;
virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance) override;
void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture);
virtual void update_particles() override;
+ void particles_update_dependency(RID p_particles, DependencyTracker *p_instance);
Dependency *particles_get_dependency(RID p_particles) const;
/* Particles Collision */
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 4745c3d1d3..29e5ea29fd 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -506,6 +506,9 @@ public:
}
} break;
+ default: {
+ // Ignored notifications.
+ } break;
}
}
diff --git a/servers/rendering/storage/utilities.h b/servers/rendering/storage/utilities.h
index 8fb4907a43..387f71f78a 100644
--- a/servers/rendering/storage/utilities.h
+++ b/servers/rendering/storage/utilities.h
@@ -44,6 +44,7 @@ public:
DEPENDENCY_CHANGED_MULTIMESH,
DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES,
DEPENDENCY_CHANGED_PARTICLES,
+ DEPENDENCY_CHANGED_PARTICLES_INSTANCES,
DEPENDENCY_CHANGED_DECAL,
DEPENDENCY_CHANGED_SKELETON_DATA,
DEPENDENCY_CHANGED_SKELETON_BONES,
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index 6fc13867cc..91a2c9797e 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -109,6 +109,9 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_font_set_embolden, "font_rid", "strength");
GDVIRTUAL_BIND(_font_get_embolden, "font_rid");
+ GDVIRTUAL_BIND(_font_set_spacing, "font_rid", "spacing", "value");
+ GDVIRTUAL_BIND(_font_get_spacing, "font_rid", "spacing");
+
GDVIRTUAL_BIND(_font_set_transform, "font_rid", "transform");
GDVIRTUAL_BIND(_font_get_transform, "font_rid");
@@ -599,6 +602,16 @@ double TextServerExtension::font_get_embolden(const RID &p_font_rid) const {
return ret;
}
+void TextServerExtension::font_set_spacing(const RID &p_font_rid, SpacingType p_spacing, int64_t p_value) {
+ GDVIRTUAL_CALL(_font_set_spacing, p_font_rid, p_spacing, p_value);
+}
+
+int64_t TextServerExtension::font_get_spacing(const RID &p_font_rid, SpacingType p_spacing) const {
+ int64_t ret = 0;
+ GDVIRTUAL_CALL(_font_get_spacing, p_font_rid, p_spacing, ret);
+ return ret;
+}
+
void TextServerExtension::font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
GDVIRTUAL_CALL(_font_set_transform, p_font_rid, p_transform);
}
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index a0c47a70d6..1d475f13d4 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -161,6 +161,11 @@ public:
GDVIRTUAL2(_font_set_embolden, RID, double);
GDVIRTUAL1RC(double, _font_get_embolden, RID);
+ virtual void font_set_spacing(const RID &p_font_rid, SpacingType p_spacing, int64_t p_value) override;
+ virtual int64_t font_get_spacing(const RID &p_font_rid, SpacingType p_spacing) const override;
+ GDVIRTUAL3(_font_set_spacing, const RID &, SpacingType, int64_t);
+ GDVIRTUAL2RC(int64_t, _font_get_spacing, const RID &, SpacingType);
+
virtual void font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) override;
virtual Transform2D font_get_transform(const RID &p_font_rid) const override;
GDVIRTUAL2(_font_set_transform, RID, Transform2D);
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index 28c6b20f7b..3c901f573e 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -263,6 +263,9 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("font_set_embolden", "font_rid", "strength"), &TextServer::font_set_embolden);
ClassDB::bind_method(D_METHOD("font_get_embolden", "font_rid"), &TextServer::font_get_embolden);
+ ClassDB::bind_method(D_METHOD("font_set_spacing", "font_rid", "spacing", "value"), &TextServer::font_set_spacing);
+ ClassDB::bind_method(D_METHOD("font_get_spacing", "font_rid", "spacing"), &TextServer::font_get_spacing);
+
ClassDB::bind_method(D_METHOD("font_set_transform", "font_rid", "transform"), &TextServer::font_set_transform);
ClassDB::bind_method(D_METHOD("font_get_transform", "font_rid"), &TextServer::font_get_transform);
diff --git a/servers/text_server.h b/servers/text_server.h
index 7bc353cb8c..d0cfd87b64 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -295,6 +295,9 @@ public:
virtual void font_set_embolden(const RID &p_font_rid, double p_strength) = 0;
virtual double font_get_embolden(const RID &p_font_rid) const = 0;
+ virtual void font_set_spacing(const RID &p_font_rid, SpacingType p_spacing, int64_t p_value) = 0;
+ virtual int64_t font_get_spacing(const RID &p_font_rid, SpacingType p_spacing) const = 0;
+
virtual void font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) = 0;
virtual Transform2D font_get_transform(const RID &p_font_rid) const = 0;
diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h
index 8ab6221a1c..a3d6b38ca4 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -82,6 +82,8 @@ public:
Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override {
return Variant::PACKED_FLOAT32_ARRAY;
}
+ virtual void validate_property(PropertyInfo &p_property) const override {
+ }
bool property_can_revert(const StringName &p_name) const override {
return false;
};