summaryrefslogtreecommitdiffstats
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/animation_track_editor.cpp78
-rw-r--r--editor/animation_track_editor.h2
-rw-r--r--editor/debugger/editor_debugger_node.cpp2
-rw-r--r--editor/debugger/editor_performance_profiler.cpp3
-rw-r--r--editor/debugger/script_editor_debugger.cpp1
-rw-r--r--editor/doc_tools.cpp17
-rw-r--r--editor/editor_inspector.cpp20
-rw-r--r--editor/editor_interface.cpp156
-rw-r--r--editor/editor_interface.h2
-rw-r--r--editor/editor_node.cpp9
-rw-r--r--editor/editor_properties.cpp3
-rw-r--r--editor/editor_resource_preview.cpp23
-rw-r--r--editor/event_listener_line_edit.cpp2
-rw-r--r--editor/export/editor_export_platform.cpp26
-rw-r--r--editor/export/editor_export_platform.h2
-rw-r--r--editor/export/project_export.cpp11
-rw-r--r--editor/gui/editor_spin_slider.cpp2
-rw-r--r--editor/icons/RetargetModifier3D.svg1
-rw-r--r--editor/import/3d/post_import_plugin_skeleton_renamer.cpp2
-rw-r--r--editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp227
-rw-r--r--editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp42
-rw-r--r--editor/import/3d/resource_importer_scene.cpp12
-rw-r--r--editor/import/3d/resource_importer_scene.h1
-rw-r--r--editor/input_event_configuration_dialog.cpp15
-rw-r--r--editor/plugins/animation_library_editor.cpp203
-rw-r--r--editor/plugins/animation_library_editor.h7
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp2
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp3
-rw-r--r--editor/plugins/animation_state_machine_editor.h1
-rw-r--r--editor/plugins/camera_3d_editor_plugin.cpp51
-rw-r--r--editor/plugins/camera_3d_editor_plugin.h27
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp19
-rw-r--r--editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp20
-rw-r--r--editor/plugins/gizmos/camera_3d_gizmo_plugin.h1
-rw-r--r--editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp75
-rw-r--r--editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h2
-rw-r--r--editor/plugins/lightmap_gi_editor_plugin.cpp12
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp19
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp7
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp28
-rw-r--r--editor/plugins/node_3d_editor_plugin.h2
-rw-r--r--editor/plugins/plugin_config_dialog.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_text_editor.cpp1
-rw-r--r--editor/plugins/theme_editor_plugin.cpp2
-rw-r--r--editor/plugins/theme_editor_preview.cpp6
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp6
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp10
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.h4
-rw-r--r--editor/themes/editor_theme_manager.cpp1
50 files changed, 1003 insertions, 169 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 7a1296c411..1a612a3e55 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1400,12 +1400,26 @@ void AnimationTimelineEdit::_anim_loop_pressed() {
undo_redo->add_undo_method(this, "update_values");
undo_redo->commit_action();
} else {
- String base_path = animation->get_path();
- if (FileAccess::exists(base_path + ".import")) {
- EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation instanced from imported scene."));
+ String base = animation->get_path();
+ int srpos = base.find("::");
+ if (srpos != -1) {
+ base = animation->get_path().substr(0, srpos);
+ }
+
+ if (FileAccess::exists(base + ".import")) {
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation instanced from an imported scene.\n\nTo change this animation's loop mode, navigate to the scene's Advanced Import settings and select the animation.\nYou can then change the loop mode from the inspector menu."));
+ } else {
+ EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation instanced from an imported resource."));
+ }
} else {
- EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation embedded in another scene."));
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation embedded in another scene.\n\nYou must open this scene and change the animation's loop mode from there."));
+ } else {
+ EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation embedded in another resource."));
+ }
}
+
update_values();
}
}
@@ -2191,7 +2205,7 @@ void AnimationTrackEdit::_notification(int p_what) {
offset = offset * scale + limit;
Color marker_color = animation->get_marker_color(marker);
marker_color.a = 0.2;
- draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color);
+ draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color, Math::round(EDSCALE));
}
}
}
@@ -3647,7 +3661,7 @@ void AnimationTrackEditGroup::_notification(int p_what) {
offset = offset * scale + limit;
Color marker_color = editor->get_current_animation()->get_marker_color(marker);
marker_color.a = 0.2;
- draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color);
+ draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color, Math::round(EDSCALE));
}
}
}
@@ -6628,6 +6642,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
case EDIT_SCALE_SELECTION:
case EDIT_SCALE_FROM_CURSOR: {
scale_dialog->popup_centered(Size2(200, 100) * EDSCALE);
+ scale->get_line_edit()->grab_focus();
} break;
case EDIT_SCALE_CONFIRM: {
if (selection.is_empty()) {
@@ -7364,7 +7379,6 @@ void AnimationTrackEditor::_update_snap_unit() {
}
if (timeline->is_using_fps()) {
- _clear_selection(true); // Needs to recreate a spinbox of the KeyEdit.
snap_unit = 1.0 / step->get_value();
} else {
if (fps_compat->is_pressed()) {
@@ -7876,10 +7890,13 @@ AnimationTrackEditor::AnimationTrackEditor() {
scale->set_min(-99999);
scale->set_max(99999);
scale->set_step(0.001);
+ scale->set_select_all_on_focus(true);
vbc->add_margin_child(TTR("Scale Ratio:"), scale);
- scale_dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_SCALE_CONFIRM));
+ scale_dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_SCALE_CONFIRM), CONNECT_DEFERRED);
add_child(scale_dialog);
+ scale_dialog->register_text_enter(scale->get_line_edit());
+
//
ease_dialog = memnew(ConfirmationDialog);
ease_dialog->set_title(TTR("Select Transition and Easing"));
@@ -7983,6 +8000,11 @@ AnimationTrackEditor::~AnimationTrackEditor() {
// AnimationTrackKeyEditEditorPlugin.
+void AnimationTrackKeyEditEditor::_time_edit_spun() {
+ _time_edit_entered();
+ _time_edit_exited();
+}
+
void AnimationTrackKeyEditEditor::_time_edit_entered() {
int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX);
if (key == -1) {
@@ -8010,7 +8032,7 @@ void AnimationTrackKeyEditEditor::_time_edit_exited() {
int existing = animation->track_find_key(track, new_time, Animation::FIND_MODE_APPROX);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Animation Change Keyframe Time"), UndoRedo::MERGE_ENDS);
+ undo_redo->create_action(TTR("Animation Change Keyframe Time"));
if (existing != -1) {
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, animation->track_get_key_time(track, existing));
@@ -8057,6 +8079,7 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat
spinner->set_min(0);
spinner->set_allow_greater(true);
spinner->set_allow_lesser(true);
+ add_child(spinner);
if (use_fps) {
spinner->set_step(FPS_DECIMAL);
@@ -8065,14 +8088,13 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat
fps = 1.0 / fps;
}
spinner->set_value(key_ofs * fps);
+ spinner->connect("updown_pressed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_spun), CONNECT_DEFERRED);
} else {
spinner->set_step(SECOND_DECIMAL);
spinner->set_value(key_ofs);
spinner->set_max(animation->get_length());
}
- add_child(spinner);
-
spinner->connect("grabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
spinner->connect("ungrabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
spinner->connect("value_focus_entered", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
@@ -8307,9 +8329,6 @@ void AnimationMarkerEdit::_notification(int p_what) {
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
- int hsep = get_theme_constant(SNAME("h_separation"), SNAME("ItemList"));
- Color linecolor = color;
- linecolor.a = 0.2;
// SECTION PREVIEW //
@@ -8368,31 +8387,21 @@ void AnimationMarkerEdit::_notification(int p_what) {
draw_key(name, scale, int(offset), is_selected, limit, limit_end);
- const int font_size = 16;
+ const int font_size = 12 * EDSCALE;
Size2 string_size = font->get_string_size(name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size);
if (int(offset) <= limit_end && int(offset) >= limit && should_show_all_marker_names) {
float bottom = get_size().height + string_size.y - font->get_descent(font_size);
float extrusion = MAX(0, offset + string_size.x - limit_end); // How much the string would extrude outside limit_end if unadjusted.
Color marker_color = animation->get_marker_color(name);
- draw_string(font, Point2(offset - extrusion, bottom), name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, marker_color);
- draw_string_outline(font, Point2(offset - extrusion, bottom), name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, 1, color);
+ float margin = 4 * EDSCALE;
+ Point2 pos = Point2(offset - extrusion + margin, bottom + margin);
+ draw_string(font, pos, name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, marker_color);
+ draw_string_outline(font, pos, name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, 1, color);
}
}
}
draw_fg(limit, get_size().width - timeline->get_buttons_width());
-
- // BUTTONS //
-
- {
- int ofs = get_size().width - timeline->get_buttons_width();
-
- draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE));
-
- ofs += hsep;
- }
-
- draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE));
} break;
case NOTIFICATION_MOUSE_ENTER:
@@ -9193,9 +9202,6 @@ void AnimationMultiMarkerKeyEdit::_get_property_list(List<PropertyInfo> *p_list)
// AnimationMarkerKeyEditEditorPlugin
-void AnimationMarkerKeyEditEditor::_time_edit_entered() {
-}
-
void AnimationMarkerKeyEditEditor::_time_edit_exited() {
real_t new_time = spinner->get_value();
@@ -9214,7 +9220,7 @@ void AnimationMarkerKeyEditEditor::_time_edit_exited() {
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Animation Change Marker Time"), UndoRedo::MERGE_ENDS);
+ undo_redo->create_action(TTR("Animation Change Marker Time"));
Color color = animation->get_marker_color(marker_name);
undo_redo->add_do_method(animation.ptr(), "add_marker", marker_name, new_time);
@@ -9255,6 +9261,7 @@ AnimationMarkerKeyEditEditor::AnimationMarkerKeyEditEditor(Ref<Animation> p_anim
spinner->set_min(0);
spinner->set_allow_greater(true);
spinner->set_allow_lesser(true);
+ add_child(spinner);
float time = animation->get_marker_time(marker_name);
@@ -9265,17 +9272,14 @@ AnimationMarkerKeyEditEditor::AnimationMarkerKeyEditEditor(Ref<Animation> p_anim
fps = 1.0 / fps;
}
spinner->set_value(time * fps);
+ spinner->connect("updown_pressed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
} else {
spinner->set_step(SECOND_DECIMAL);
spinner->set_value(time);
spinner->set_max(animation->get_length());
}
- add_child(spinner);
-
- spinner->connect("grabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
spinner->connect("ungrabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
- spinner->connect("value_focus_entered", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
spinner->connect("value_focus_exited", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
}
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index e7271f1941..f17386b0c9 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -977,6 +977,7 @@ class AnimationTrackKeyEditEditor : public EditorProperty {
Variant value;
} key_data_cache;
+ void _time_edit_spun();
void _time_edit_entered();
void _time_edit_exited();
@@ -996,7 +997,6 @@ class AnimationMarkerKeyEditEditor : public EditorProperty {
EditorSpinSlider *spinner = nullptr;
- void _time_edit_entered();
void _time_edit_exited();
public:
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 909651da45..f06d9be78f 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -120,7 +120,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
tabs->add_child(node);
- node->set_name("Session " + itos(tabs->get_tab_count()));
+ node->set_name(vformat(TTR("Session %d"), tabs->get_tab_count()));
if (tabs->get_tab_count() > 1) {
node->clear_style();
tabs->set_tabs_visible(true);
diff --git a/editor/debugger/editor_performance_profiler.cpp b/editor/debugger/editor_performance_profiler.cpp
index 457e60b145..baa57b02ce 100644
--- a/editor/debugger/editor_performance_profiler.cpp
+++ b/editor/debugger/editor_performance_profiler.cpp
@@ -81,6 +81,9 @@ void EditorPerformanceProfiler::Monitor::reset() {
String EditorPerformanceProfiler::_create_label(float p_value, Performance::MonitorType p_type) {
switch (p_type) {
+ case Performance::MONITOR_TYPE_QUANTITY: {
+ return TS->format_number(itos(p_value));
+ }
case Performance::MONITOR_TYPE_MEMORY: {
return String::humanize_size(p_value);
}
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 2c077e2d4c..6eb9cd1421 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -1062,6 +1062,7 @@ void ScriptEditorDebugger::_update_buttons_state() {
for (KeyValue<uint64_t, ThreadDebugged> &I : threads_debugged) {
threadss.push_back(&I.value);
}
+ threads->set_disabled(threadss.is_empty());
threadss.sort_custom<ThreadSort>();
threads->clear();
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 79e0c7ebd1..842c4acce0 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -908,6 +908,23 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
c.properties.sort();
+ List<StringName> enums;
+ Variant::get_enums_for_type(Variant::Type(i), &enums);
+
+ for (const StringName &E : enums) {
+ List<StringName> enumerations;
+ Variant::get_enumerations_for_enum(Variant::Type(i), E, &enumerations);
+
+ for (const StringName &F : enumerations) {
+ DocData::ConstantDoc constant;
+ constant.name = F;
+ constant.value = itos(Variant::get_enum_value(Variant::Type(i), E, F));
+ constant.is_value_valid = true;
+ constant.enumeration = E;
+ c.constants.push_back(constant);
+ }
+ }
+
List<StringName> constants;
Variant::get_constants_for_type(Variant::Type(i), &constants);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 6b3c6b462d..1a973d7b77 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3472,6 +3472,14 @@ void EditorInspector::update_tree() {
editors.append_array(late_editors);
+ const Node *node = Object::cast_to<Node>(object);
+
+ Vector<SceneState::PackState> sstack;
+ if (node != nullptr) {
+ const Node *es = EditorNode::get_singleton()->get_edited_scene();
+ sstack = PropertyUtils::get_node_states_stack(node, es);
+ }
+
for (int i = 0; i < editors.size(); i++) {
EditorProperty *ep = Object::cast_to<EditorProperty>(editors[i].property_editor);
const Vector<String> &properties = editors[i].properties;
@@ -3525,7 +3533,15 @@ void EditorInspector::update_tree() {
ep->set_checked(checked);
ep->set_keying(keying);
ep->set_read_only(property_read_only || all_read_only);
- ep->set_deletable(deletable_properties || p.name.begins_with("metadata/"));
+ if (p.name.begins_with("metadata/")) {
+ Variant _default = Variant();
+ if (node != nullptr) {
+ _default = PropertyUtils::get_property_default_value(node, p.name, nullptr, &sstack, false, nullptr, nullptr);
+ }
+ ep->set_deletable(_default == Variant());
+ } else {
+ ep->set_deletable(deletable_properties);
+ }
}
if (ep && ep->is_favoritable() && current_favorites.has(p.name)) {
@@ -3648,8 +3664,6 @@ void EditorInspector::update_tree() {
for (List<EditorInspectorSection *>::Element *I = sections.back(); I; I = I->prev()) {
EditorInspectorSection *section = I->get();
if (section->get_vbox()->get_child_count() == 0) {
- I = I->prev();
-
sections.erase(section);
vbox_per_path[main_vbox].erase(section->get_section());
memdelete(section);
diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp
index e85258df50..3cdcc91ae9 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -31,6 +31,7 @@
#include "editor_interface.h"
#include "editor_interface.compat.inc"
+#include "core/config/project_settings.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_feature_profile.h"
#include "editor/editor_main_screen.h"
@@ -50,6 +51,10 @@
#include "editor/property_selector.h"
#include "editor/themes/editor_scale.h"
#include "main/main.h"
+#include "plugins/editor_preview_plugins.h"
+#include "scene/3d/light_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/world_environment.h"
#include "scene/gui/box_container.h"
#include "scene/gui/control.h"
#include "scene/main/window.h"
@@ -98,6 +103,27 @@ EditorUndoRedoManager *EditorInterface::get_editor_undo_redo() const {
return EditorUndoRedoManager::get_singleton();
}
+AABB EditorInterface::_calculate_aabb_for_scene(Node *p_node, AABB &p_scene_aabb) {
+ MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(p_node);
+ if (mesh_node && mesh_node->get_mesh().is_valid()) {
+ Transform3D accum_xform;
+ Node3D *base = mesh_node;
+ while (base) {
+ accum_xform = base->get_transform() * accum_xform;
+ base = Object::cast_to<Node3D>(base->get_parent());
+ }
+
+ AABB aabb = accum_xform.xform(mesh_node->get_mesh()->get_aabb());
+ p_scene_aabb.merge_with(aabb);
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ p_scene_aabb = _calculate_aabb_for_scene(p_node->get_child(i), p_scene_aabb);
+ }
+
+ return p_scene_aabb;
+}
+
TypedArray<Texture2D> EditorInterface::_make_mesh_previews(const TypedArray<Mesh> &p_meshes, int p_preview_size) {
Vector<Ref<Mesh>> meshes;
@@ -203,6 +229,136 @@ Vector<Ref<Texture2D>> EditorInterface::make_mesh_previews(const Vector<Ref<Mesh
return textures;
}
+void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, int p_preview_size) {
+ ERR_FAIL_NULL_MSG(p_scene, "The provided scene is null.");
+ ERR_FAIL_COND_MSG(p_scene->is_inside_tree(), "The scene must not be inside the tree.");
+ ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be called from the editor.");
+
+ SubViewport *sub_viewport_node = memnew(SubViewport);
+ AABB scene_aabb;
+ scene_aabb = _calculate_aabb_for_scene(p_scene, scene_aabb);
+
+ sub_viewport_node->set_update_mode(SubViewport::UPDATE_ALWAYS);
+ sub_viewport_node->set_size(Vector2i(p_preview_size, p_preview_size));
+ sub_viewport_node->set_transparent_background(false);
+ Ref<World3D> world;
+ world.instantiate();
+ sub_viewport_node->set_world_3d(world);
+
+ EditorNode::get_singleton()->add_child(sub_viewport_node);
+ Ref<Environment> env;
+ env.instantiate();
+ env->set_background(Environment::BG_CLEAR_COLOR);
+
+ Ref<CameraAttributesPractical> camera_attributes;
+ camera_attributes.instantiate();
+
+ Node3D *root = memnew(Node3D);
+ root->set_name("Root");
+ sub_viewport_node->add_child(root);
+
+ Camera3D *camera = memnew(Camera3D);
+ camera->set_environment(env);
+ camera->set_attributes(camera_attributes);
+ camera->set_name("Camera3D");
+ root->add_child(camera);
+ camera->set_current(true);
+
+ camera->set_position(Vector3(0.0, 0.0, 3.0));
+
+ DirectionalLight3D *light = memnew(DirectionalLight3D);
+ light->set_name("Light");
+ DirectionalLight3D *light2 = memnew(DirectionalLight3D);
+ light2->set_name("Light2");
+ light2->set_color(Color(0.7, 0.7, 0.7, 1.0));
+
+ root->add_child(light);
+ root->add_child(light2);
+
+ sub_viewport_node->add_child(p_scene);
+
+ // Calculate the camera and lighting position based on the size of the scene.
+ Vector3 center = scene_aabb.get_center();
+ float camera_size = scene_aabb.get_longest_axis_size();
+
+ const float cam_rot_x = -Math_PI / 4;
+ const float cam_rot_y = -Math_PI / 4;
+
+ camera->set_orthogonal(camera_size * 2.0, 0.0001, camera_size * 2.0);
+
+ Transform3D xf;
+ xf.basis = Basis(Vector3(0, 1, 0), cam_rot_y) * Basis(Vector3(1, 0, 0), cam_rot_x);
+ xf.origin = center;
+ xf.translate_local(0, 0, camera_size);
+
+ camera->set_transform(xf);
+
+ Transform3D xform;
+ xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI / 6);
+ xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI / 6) * xform.basis;
+
+ light->set_transform(xform * Transform3D().looking_at(Vector3(-2, -1, -1), Vector3(0, 1, 0)));
+ light2->set_transform(xform * Transform3D().looking_at(Vector3(+1, -1, -2), Vector3(0, 1, 0)));
+
+ // Update the renderer to get the screenshot.
+ DisplayServer::get_singleton()->process_events();
+ Main::iteration();
+ Main::iteration();
+
+ // Get the texture.
+ Ref<Texture2D> texture = sub_viewport_node->get_texture();
+ ERR_FAIL_COND_MSG(texture.is_null(), "Failed to get texture from sub_viewport_node.");
+
+ // Remove the initial scene node.
+ sub_viewport_node->remove_child(p_scene);
+
+ // Cleanup the viewport.
+ if (sub_viewport_node) {
+ if (sub_viewport_node->get_parent()) {
+ sub_viewport_node->get_parent()->remove_child(sub_viewport_node);
+ }
+ sub_viewport_node->queue_free();
+ sub_viewport_node = nullptr;
+ }
+
+ // Now generate the cache image.
+ Ref<Image> img = texture->get_image();
+ if (img.is_valid() && img->get_width() > 0 && img->get_height() > 0) {
+ img = img->duplicate();
+
+ int preview_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
+ preview_size *= EDSCALE;
+
+ int vp_size = MIN(img->get_width(), img->get_height());
+ int x = (img->get_width() - vp_size) / 2;
+ int y = (img->get_height() - vp_size) / 2;
+
+ if (vp_size < preview_size) {
+ img->crop_from_point(x, y, vp_size, vp_size);
+ } else {
+ int ratio = vp_size / preview_size;
+ int size = preview_size * MAX(1, ratio / 2);
+
+ x = (img->get_width() - size) / 2;
+ y = (img->get_height() - size) / 2;
+
+ img->crop_from_point(x, y, size, size);
+ img->resize(preview_size, preview_size, Image::INTERPOLATE_LANCZOS);
+ }
+ img->convert(Image::FORMAT_RGB8);
+
+ String temp_path = EditorPaths::get_singleton()->get_cache_dir();
+ String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
+ cache_base = temp_path.path_join("resthumb-" + cache_base);
+
+ post_process_preview(img);
+ img->save_png(cache_base + ".png");
+ }
+
+ EditorResourcePreview::get_singleton()->check_for_invalidation(p_path);
+ EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed"));
+}
+
void EditorInterface::set_plugin_enabled(const String &p_plugin, bool p_enabled) {
EditorNode::get_singleton()->set_addon_plugin_enabled(p_plugin, p_enabled, true);
}
diff --git a/editor/editor_interface.h b/editor/editor_interface.h
index 2ae77331b1..92d9812003 100644
--- a/editor/editor_interface.h
+++ b/editor/editor_interface.h
@@ -79,6 +79,7 @@ class EditorInterface : public Object {
// Editor tools.
TypedArray<Texture2D> _make_mesh_previews(const TypedArray<Mesh> &p_meshes, int p_preview_size);
+ AABB _calculate_aabb_for_scene(Node *p_node, AABB &p_scene_aabb);
protected:
static void _bind_methods();
@@ -107,6 +108,7 @@ public:
EditorUndoRedoManager *get_editor_undo_redo() const;
Vector<Ref<Texture2D>> make_mesh_previews(const Vector<Ref<Mesh>> &p_meshes, Vector<Transform3D> *p_transforms, int p_preview_size);
+ void make_scene_preview(const String &p_path, Node *p_scene, int p_preview_size);
void set_plugin_enabled(const String &p_plugin, bool p_enabled);
bool is_plugin_enabled(const String &p_plugin) const;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index f056a477c4..222696a608 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -5190,7 +5190,8 @@ void EditorNode::show_accept(const String &p_text, const String &p_title) {
_close_save_scene_progress();
accept->set_ok_button_text(p_title);
accept->set_text(p_text);
- EditorInterface::get_singleton()->popup_dialog_centered(accept);
+ accept->reset_size();
+ EditorInterface::get_singleton()->popup_dialog_centered_clamped(accept, Size2i(), 0.0);
}
}
@@ -5200,7 +5201,8 @@ void EditorNode::show_save_accept(const String &p_text, const String &p_title) {
_close_save_scene_progress();
save_accept->set_ok_button_text(p_title);
save_accept->set_text(p_text);
- EditorInterface::get_singleton()->popup_dialog_centered(save_accept);
+ save_accept->reset_size();
+ EditorInterface::get_singleton()->popup_dialog_centered_clamped(save_accept, Size2i(), 0.0);
}
}
@@ -5209,7 +5211,8 @@ void EditorNode::show_warning(const String &p_text, const String &p_title) {
_close_save_scene_progress();
warning->set_text(p_text);
warning->set_title(p_title);
- EditorInterface::get_singleton()->popup_dialog_centered(warning);
+ warning->reset_size();
+ EditorInterface::get_singleton()->popup_dialog_centered_clamped(warning, Size2i(), 0.0);
} else {
WARN_PRINT(p_title + " " + p_text);
}
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 2b2b32eb22..b600e49b61 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -52,6 +52,7 @@
#include "scene/3d/fog_volume.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/gui/color_picker.h"
+#include "scene/gui/grid_container.h"
#include "scene/main/window.h"
#include "scene/resources/font.h"
#include "scene/resources/mesh.h"
@@ -2645,7 +2646,7 @@ EditorPropertyColor::EditorPropertyColor() {
add_child(picker);
picker->set_flat(true);
picker->connect("color_changed", callable_mp(this, &EditorPropertyColor::_color_changed));
- picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed));
+ picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed), CONNECT_DEFERRED);
picker->get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(picker->get_picker()));
picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_picker_opening));
}
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 4dca3b33af..32c44133e0 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -147,6 +147,10 @@ void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, con
if (!p_path.begins_with("ID:")) {
modified_time = FileAccess::get_modified_time(p_path);
+ String import_path = p_path + ".import";
+ if (FileAccess::exists(import_path)) {
+ modified_time = MAX(modified_time, FileAccess::get_modified_time(import_path));
+ }
}
Item item;
@@ -237,7 +241,14 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
}
Ref<FileAccess> f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE);
ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + cache_base + ".txt'. Check user write permissions.");
- _write_preview_cache(f, thumbnail_size, has_small_texture, FileAccess::get_modified_time(p_item.path), FileAccess::get_md5(p_item.path), p_metadata);
+
+ uint64_t modtime = FileAccess::get_modified_time(p_item.path);
+ String import_path = p_item.path + ".import";
+ if (FileAccess::exists(import_path)) {
+ modtime = MAX(modtime, FileAccess::get_modified_time(import_path));
+ }
+
+ _write_preview_cache(f, thumbnail_size, has_small_texture, modtime, FileAccess::get_md5(p_item.path), p_metadata);
}
}
@@ -298,6 +309,11 @@ void EditorResourcePreview::_iterate() {
_generate_preview(texture, small_texture, item, cache_base, preview_metadata);
} else {
uint64_t modtime = FileAccess::get_modified_time(item.path);
+ String import_path = item.path + ".import";
+ if (FileAccess::exists(import_path)) {
+ modtime = MAX(modtime, FileAccess::get_modified_time(import_path));
+ }
+
int tsize;
bool has_small_texture;
uint64_t last_modtime;
@@ -513,6 +529,11 @@ void EditorResourcePreview::check_for_invalidation(const String &p_path) {
if (cache.has(p_path)) {
uint64_t modified_time = FileAccess::get_modified_time(p_path);
+ String import_path = p_path + ".import";
+ if (FileAccess::exists(import_path)) {
+ modified_time = MAX(modified_time, FileAccess::get_modified_time(import_path));
+ }
+
if (modified_time != cache[p_path].modified_time) {
cache.erase(p_path);
call_invalidated = true;
diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp
index 8fde728027..a6b30233fc 100644
--- a/editor/event_listener_line_edit.cpp
+++ b/editor/event_listener_line_edit.cpp
@@ -121,7 +121,7 @@ String EventListenerLineEdit::get_event_text(const Ref<InputEvent> &p_event, boo
}
String EventListenerLineEdit::get_device_string(int p_device) {
- if (p_device == InputEvent::DEVICE_ID_ALL_DEVICES) {
+ if (p_device == InputMap::ALL_DEVICES) {
return TTR("All Devices");
}
return TTR("Device") + " " + itos(p_device);
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 91c9ff9807..27216c2399 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -1495,7 +1495,15 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
Vector<String> forced_export = get_forced_export_files();
for (int i = 0; i < forced_export.size(); i++) {
- Vector<uint8_t> array = FileAccess::get_file_as_bytes(forced_export[i]);
+ Vector<uint8_t> array;
+ if (GDExtension::get_extension_list_config_file() == forced_export[i]) {
+ array = _filter_extension_list_config_file(forced_export[i], paths);
+ if (array.size() == 0) {
+ continue;
+ }
+ } else {
+ array = FileAccess::get_file_as_bytes(forced_export[i]);
+ }
err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
@@ -1534,6 +1542,22 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
return OK;
}
+Vector<uint8_t> EditorExportPlatform::_filter_extension_list_config_file(const String &p_config_path, const HashSet<String> &p_paths) {
+ Ref<FileAccess> f = FileAccess::open(p_config_path, FileAccess::READ);
+ if (f.is_null()) {
+ ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_config_path) + "'.");
+ }
+ Vector<uint8_t> data;
+ while (!f->eof_reached()) {
+ String l = f->get_line().strip_edges();
+ if (p_paths.has(l)) {
+ data.append_array(l.to_utf8_buffer());
+ data.append('\n');
+ }
+ }
+ return data;
+}
+
Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const SharedObject &p_so) {
PackData *pack_data = (PackData *)p_userdata;
if (pack_data->so_files) {
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index c7378ffec7..a33bdce72a 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -135,6 +135,8 @@ private:
void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude);
void _edit_filter_list(HashSet<String> &r_list, const String &p_filter, bool exclude);
+ static Vector<uint8_t> _filter_extension_list_config_file(const String &p_config_path, const HashSet<String> &p_paths);
+
struct FileExportCache {
uint64_t source_modified_time = 0;
String source_md5;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index df1fa306c8..f5194eb7a7 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -1206,6 +1206,9 @@ void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
bool export_debug = fd_option.get(TTR("Export With Debug"), true);
bool export_as_patch = fd_option.get(TTR("Export As Patch"), true);
+ EditorSettings::get_singleton()->set_project_metadata("export_options", "export_debug", export_debug);
+ EditorSettings::get_singleton()->set_project_metadata("export_options", "export_as_patch", export_as_patch);
+
if (p_path.ends_with(".zip")) {
if (export_as_patch) {
platform->export_zip_patch(current, export_debug, p_path);
@@ -1305,6 +1308,8 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) {
Dictionary fd_option = export_project->get_selected_options();
bool export_debug = fd_option.get(TTR("Export With Debug"), true);
+ EditorSettings::get_singleton()->set_project_metadata("export_options", "export_debug", export_debug);
+
Error err = platform->export_project(current, export_debug, current->get_export_path(), 0);
result_dialog_log->clear();
if (err != ERR_SKIP) {
@@ -1775,9 +1780,9 @@ ProjectExportDialog::ProjectExportDialog() {
export_project->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path));
export_project->get_line_edit()->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_validate_export_path));
- export_project->add_option(TTR("Export With Debug"), Vector<String>(), true);
- export_pck_zip->add_option(TTR("Export With Debug"), Vector<String>(), true);
- export_pck_zip->add_option(TTR("Export As Patch"), Vector<String>(), true);
+ export_project->add_option(TTR("Export With Debug"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true));
+ export_pck_zip->add_option(TTR("Export With Debug"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true));
+ export_pck_zip->add_option(TTR("Export As Patch"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_as_patch", true));
set_hide_on_ok(false);
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 712e91faca..aa9e9f841d 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -67,6 +67,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
} else {
set_value(get_value() - get_step());
}
+ emit_signal("updown_pressed");
return;
}
_grab_start();
@@ -696,6 +697,7 @@ void EditorSpinSlider::_bind_methods() {
ADD_SIGNAL(MethodInfo("grabbed"));
ADD_SIGNAL(MethodInfo("ungrabbed"));
+ ADD_SIGNAL(MethodInfo("updown_pressed"));
ADD_SIGNAL(MethodInfo("value_focus_entered"));
ADD_SIGNAL(MethodInfo("value_focus_exited"));
diff --git a/editor/icons/RetargetModifier3D.svg b/editor/icons/RetargetModifier3D.svg
new file mode 100644
index 0000000000..2ca7af6c6e
--- /dev/null
+++ b/editor/icons/RetargetModifier3D.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#fc7f7f"><path d="m11.667 4.166h-3.334c-1.841 0-3.333 1.492-3.333 3.334.003 1.188.638 2.283 1.667 2.877v2.956c0 .597.317 1.146.833 1.444.254.146.54.221.833.221v.002h3.334v-.002c.293 0 .579-.075.833-.221.516-.299.833-.851.833-1.444v-2.956c1.028-.594 1.664-1.689 1.667-2.877 0-1.842-1.492-3.334-3.333-3.334zm-2.5 4.166h1.666v.834h-1.666zm-2.5-.832c0-.461.372-.834.833-.834s.833.373.833.834-.372.832-.833.832-.833-.371-.833-.832zm5.833 3.223v2.61h-.833v-.833h-.834v.833h-1.666v-.833h-.834v.833h-.833v-2.608-.725h.833v.832h.834v-.832h1.666v.832h.834v-.832h.833zm0-2.391c-.461 0-.833-.371-.833-.832s.372-.834.833-.834.833.373.833.834-.372.832-.833.832z"/><path d="m4.418 9.334h-.085v.833h-.833v-2.608-.725h.567c.323-2.072 2.104-3.668 4.266-3.668h2.445c-.473-1.263-1.682-2.166-3.111-2.166h-3.334c-1.841 0-3.333 1.492-3.333 3.334.003 1.188.638 2.283 1.667 2.877v2.956c0 .597.317 1.146.833 1.444.254.146.54.221.833.221v.002h1.334v-.929c-.538-.421-.962-.962-1.249-1.571zm-1.751-5c0-.461.372-.834.833-.834s.833.373.833.834-.372.832-.833.832-.833-.371-.833-.832z"/></g></svg> \ No newline at end of file
diff --git a/editor/import/3d/post_import_plugin_skeleton_renamer.cpp b/editor/import/3d/post_import_plugin_skeleton_renamer.cpp
index 3f6bfdcf05..700b2bc719 100644
--- a/editor/import/3d/post_import_plugin_skeleton_renamer.cpp
+++ b/editor/import/3d/post_import_plugin_skeleton_renamer.cpp
@@ -39,7 +39,7 @@
void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
- r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones"), true));
+ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/unique_node/make_unique"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/bone_renamer/unique_node/skeleton_name"), "GeneralSkeleton"));
}
diff --git a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp
index 64bec0532b..82940f9cef 100644
--- a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp
+++ b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp
@@ -33,6 +33,7 @@
#include "editor/import/3d/scene_import_settings.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h"
+#include "scene/3d/retarget_modifier_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/bone_map.h"
@@ -42,8 +43,18 @@ void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImpo
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/apply_node_transforms"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/normalize_position_tracks"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/reset_all_bone_poses_after_import"), true));
- r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/overwrite_axis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
+
+ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "retarget/rest_fixer/retarget_method", PROPERTY_HINT_ENUM, "None,Overwrite Axis,Use Retarget Modifier", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/keep_global_rest_on_leftovers"), true));
+ String skeleton_bones_must_be_renamed_warning = String(
+ "The skeleton modifier option uses SkeletonProfile as a list of bone names and retargets by name matching. Without renaming, retargeting by modifier will not work and the track path of the animation will be broken and it will be not playbacked correctly."); // TODO: translate.
+ r_options->push_back(ResourceImporter::ImportOption(
+ PropertyInfo(
+ Variant::STRING, U"retarget/rest_fixer/\u26A0_validation_warning/skeleton_bones_must_be_renamed",
+ PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),
+ Variant(skeleton_bones_must_be_renamed_warning)));
+ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/use_global_pose"), true));
+ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/rest_fixer/original_skeleton_name"), "OriginalSkeleton"));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/fix_silhouette/enable", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
// TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options).
// get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values.
@@ -63,7 +74,11 @@ Variant PostImportPluginSkeletonRestFixer::get_internal_option_visibility(Intern
}
}
} else if (p_option == "retarget/rest_fixer/keep_global_rest_on_leftovers") {
- return bool(p_options["retarget/rest_fixer/overwrite_axis"]);
+ return int(p_options["retarget/rest_fixer/retarget_method"]) == 1;
+ } else if (p_option == "retarget/rest_fixer/original_skeleton_name" || p_option == "retarget/rest_fixer/use_global_pose") {
+ return int(p_options["retarget/rest_fixer/retarget_method"]) == 2;
+ } else if (p_option.begins_with("retarget/") && p_option.ends_with("skeleton_bones_must_be_renamed")) {
+ return int(p_options["retarget/rest_fixer/retarget_method"]) == 2 && bool(p_options["retarget/bone_renamer/rename_bones"]) == false;
}
}
return true;
@@ -147,7 +162,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
src_skeleton->set_bone_pose_position(src_idx, src_skeleton->get_bone_pose_position(src_idx) * scl);
}
- // Fix animation.
+ // Fix animation by changing node transform.
bones_to_process = src_skeleton->get_parentless_bones();
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
@@ -224,6 +239,10 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
+ if (String(name).contains("/")) {
+ continue; // Avoid animation library which may be created by importer dynamically.
+ }
+
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
@@ -454,8 +473,13 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
}
}
- // Overwrite axis.
- if (bool(p_options["retarget/rest_fixer/overwrite_axis"])) {
+ bool is_using_modifier = int(p_options["retarget/rest_fixer/retarget_method"]) == 2;
+ bool is_using_global_pose = bool(p_options["retarget/rest_fixer/use_global_pose"]);
+ Skeleton3D *orig_skeleton = nullptr;
+ Skeleton3D *profile_skeleton = nullptr;
+
+ // Retarget in some way.
+ if (int(p_options["retarget/rest_fixer/retarget_method"]) > 0) {
LocalVector<Transform3D> old_skeleton_rest;
LocalVector<Transform3D> old_skeleton_global_rest;
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
@@ -463,11 +487,151 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
}
+ // Build structure for modifier.
+ if (is_using_modifier) {
+ orig_skeleton = src_skeleton;
+
+ // Duplicate src_skeleton to modify animation tracks, it will memdelele after that animation track modification.
+ src_skeleton = memnew(Skeleton3D);
+ for (int i = 0; i < orig_skeleton->get_bone_count(); i++) {
+ src_skeleton->add_bone(orig_skeleton->get_bone_name(i));
+ src_skeleton->set_bone_rest(i, orig_skeleton->get_bone_rest(i));
+ src_skeleton->set_bone_pose(i, orig_skeleton->get_bone_pose(i));
+ }
+ for (int i = 0; i < orig_skeleton->get_bone_count(); i++) {
+ src_skeleton->set_bone_parent(i, orig_skeleton->get_bone_parent(i));
+ }
+ src_skeleton->set_motion_scale(orig_skeleton->get_motion_scale());
+
+ // Rename orig_skeleton (previous src_skeleton), since it is not animated by animation track with GeneralSkeleton.
+ String original_skeleton_name = String(p_options["retarget/rest_fixer/original_skeleton_name"]);
+ String skel_name = orig_skeleton->get_name();
+ ERR_FAIL_COND_MSG(original_skeleton_name.is_empty(), "Original skeleton name cannot be empty.");
+ ERR_FAIL_COND_MSG(original_skeleton_name == skel_name, "Original skeleton name must be different from unique skeleton name.");
+
+ // Rename profile skeleton to be general skeleton.
+ profile_skeleton = memnew(Skeleton3D);
+ bool is_unique = orig_skeleton->is_unique_name_in_owner();
+ if (is_unique) {
+ orig_skeleton->set_unique_name_in_owner(false);
+ }
+ orig_skeleton->set_name(original_skeleton_name);
+ profile_skeleton->set_name(skel_name);
+ if (is_unique) {
+ profile_skeleton->set_unique_name_in_owner(true);
+ }
+ // Build profile skeleton bones.
+ int len = profile->get_bone_size();
+ for (int i = 0; i < len; i++) {
+ profile_skeleton->add_bone(profile->get_bone_name(i));
+ profile_skeleton->set_bone_rest(i, profile->get_reference_pose(i));
+ }
+ for (int i = 0; i < len; i++) {
+ int target_parent = profile_skeleton->find_bone(profile->get_bone_parent(i));
+ if (target_parent >= 0) {
+ profile_skeleton->set_bone_parent(i, target_parent);
+ }
+ }
+ for (int i = 0; i < len; i++) {
+ Vector3 origin;
+ int found = orig_skeleton->find_bone(profile->get_bone_name(i));
+ String parent_name = profile->get_bone_parent(i);
+ if (found >= 0) {
+ origin = orig_skeleton->get_bone_global_rest(found).origin;
+ if (profile->get_bone_name(i) != profile->get_root_bone()) {
+ int src_parent = -1;
+ while (src_parent < 0 && !parent_name.is_empty()) {
+ src_parent = orig_skeleton->find_bone(parent_name);
+ parent_name = profile->get_bone_parent(profile->find_bone(parent_name));
+ }
+ if (src_parent >= 0) {
+ Transform3D parent_grest = orig_skeleton->get_bone_global_rest(src_parent);
+ origin = origin - parent_grest.origin;
+ }
+ }
+ }
+ int target_parent = profile_skeleton->find_bone(profile->get_bone_parent(i));
+ if (target_parent >= 0) {
+ origin = profile_skeleton->get_bone_global_rest(target_parent).basis.get_rotation_quaternion().xform_inv(origin);
+ }
+ profile_skeleton->set_bone_rest(i, Transform3D(profile_skeleton->get_bone_rest(i).basis, origin));
+ }
+ profile_skeleton->set_motion_scale(orig_skeleton->get_motion_scale());
+ profile_skeleton->reset_bone_poses();
+ // Make structure with modifier.
+ Node *owner = p_node->get_owner();
+
+ Node *pr = orig_skeleton->get_parent();
+ pr->add_child(profile_skeleton);
+ profile_skeleton->set_owner(owner);
+
+ RetargetModifier3D *mod = memnew(RetargetModifier3D);
+ profile_skeleton->add_child(mod);
+ mod->set_owner(owner);
+ mod->set_name("RetargetModifier3D");
+
+ orig_skeleton->set_owner(nullptr);
+ orig_skeleton->reparent(mod, false);
+ orig_skeleton->set_owner(owner);
+ orig_skeleton->set_unique_name_in_owner(true);
+
+ mod->set_use_global_pose(is_using_global_pose);
+ mod->set_profile(profile);
+
+ // Fix skeleton name in animation.
+ // Mapped skeleton is animated by %GenerarSkeleton:RenamedBoneName.
+ // Unmapped skeleton is animated by %OriginalSkeleton:OriginalBoneName.
+ if (is_using_modifier) {
+ TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
+ String general_skeleton_pathname = UNIQUE_NODE_PREFIX + profile_skeleton->get_name();
+ while (nodes.size()) {
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
+ List<StringName> anims;
+ ap->get_animation_list(&anims);
+ for (const StringName &name : anims) {
+ Ref<Animation> anim = ap->get_animation(name);
+ int track_len = anim->get_track_count();
+ for (int i = 0; i < track_len; i++) {
+ if (anim->track_get_path(i).get_name_count() == 0) {
+ return;
+ }
+ if (anim->track_get_path(i).get_name(0) == general_skeleton_pathname) {
+ bool replace = false;
+ if (anim->track_get_path(i).get_subname_count() > 0) {
+ int found = profile_skeleton->find_bone(anim->track_get_path(i).get_concatenated_subnames());
+ if (found < 0) {
+ replace = true;
+ }
+ } else {
+ replace = true;
+ }
+ if (replace) {
+ String path_string = UNIQUE_NODE_PREFIX + original_skeleton_name;
+ if (anim->track_get_path(i).get_name_count() > 1) {
+ Vector<StringName> names = anim->track_get_path(i).get_names();
+ names.remove_at(0);
+ for (int j = 0; j < names.size(); j++) {
+ path_string += "/" + names[i].operator String();
+ }
+ }
+ if (anim->track_get_path(i).get_subname_count() > 0) {
+ path_string = path_string + String(":") + anim->track_get_path(i).get_concatenated_subnames();
+ }
+ anim->track_set_path(i, path_string);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
bool keep_global_rest_leftovers = bool(p_options["retarget/rest_fixer/keep_global_rest_on_leftovers"]);
// Scan hierarchy and populate a whitelist of unmapped bones without mapped descendants.
+ // When both is_using_modifier and is_using_global_pose are enabled, this array is used for detecting warning.
Vector<int> keep_bone_rest;
- if (keep_global_rest_leftovers) {
+ if (is_using_modifier || keep_global_rest_leftovers) {
Vector<int> bones_to_process = src_skeleton->get_parentless_bones();
while (bones_to_process.size() > 0) {
int src_idx = bones_to_process[0];
@@ -526,12 +690,14 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
if (src_parent_idx >= 0) {
src_pg = src_skeleton->get_bone_global_rest(src_parent_idx).basis;
}
-
int prof_idx = profile->find_bone(src_bone_name);
if (prof_idx >= 0) {
- tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; // Mapped bone uses reference pose.
+ // Mapped bone uses reference pose.
+ // It is fine to change rest here even though is_using_modifier is enabled, since next process is aborted with unmapped bones.
+ tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis;
} else if (keep_global_rest_leftovers && keep_bone_rest.has(src_idx)) {
- tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone without mapped children keeps global rest.
+ // Non-Mapped bones without mapped children keeps global rest.
+ tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis;
}
}
@@ -548,7 +714,8 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
src_skeleton->set_bone_rest(src_idx, Transform3D(tgt_rot, diff.xform(src_skeleton->get_bone_rest(src_idx).origin)));
}
- // Fix animation.
+ // Fix animation by changing rest.
+ bool warning_detected = false;
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
@@ -573,7 +740,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
ERR_CONTINUE(!node);
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
- if (!track_skeleton || track_skeleton != src_skeleton) {
+ if (!track_skeleton ||
+ (is_using_modifier && track_skeleton != profile_skeleton && track_skeleton != orig_skeleton) ||
+ (!is_using_modifier && track_skeleton != src_skeleton)) {
continue;
}
@@ -584,6 +753,16 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
int bone_idx = src_skeleton->find_bone(bn);
+ if (is_using_modifier) {
+ int prof_idx = profile->find_bone(bn);
+ if (prof_idx < 0) {
+ if (keep_bone_rest.has(bone_idx)) {
+ warning_detected = true;
+ }
+ continue; // If is_using_modifier, the original skeleton rest is not changed.
+ }
+ }
+
Transform3D old_rest = old_skeleton_rest[bone_idx];
Transform3D new_rest = src_skeleton->get_bone_rest(bone_idx);
Transform3D old_pg;
@@ -629,6 +808,13 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
}
}
}
+ if (is_using_global_pose && warning_detected) {
+ // TODO:
+ // Theoretically, if A and its conversion are calculated correctly taking into account the difference in the number of bones,
+ // there is no need to disable use_global_pose, but this is probably a fairly niche case.
+ WARN_PRINT_ED("Animated extra bone between mapped bones detected, consider disabling Use Global Pose option to prevent that the pose origin be overridden by the RetargetModifier3D.");
+ }
+
if (p_options.has("retarget/rest_fixer/reset_all_bone_poses_after_import") && !bool(p_options["retarget/rest_fixer/reset_all_bone_poses_after_import"])) {
// If Reset All Bone Poses After Import is disabled, preserve the original bone pose, adjusted for the new bone rolls.
for (int bone_idx = 0; bone_idx < src_skeleton->get_bone_count(); bone_idx++) {
@@ -654,6 +840,11 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
}
}
+ if (is_using_modifier) {
+ memdelete(src_skeleton);
+ src_skeleton = profile_skeleton;
+ }
+
is_rest_changed = true;
}
@@ -681,7 +872,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
ERR_CONTINUE(!node);
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
- if (!track_skeleton || track_skeleton != src_skeleton) {
+ if (!track_skeleton ||
+ (is_using_modifier && track_skeleton != profile_skeleton && track_skeleton != orig_skeleton) ||
+ (!is_using_modifier && track_skeleton != src_skeleton)) {
continue;
}
@@ -696,7 +889,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
}
}
- if (is_rest_changed) {
+ if (!is_using_modifier && is_rest_changed) {
// Fix skin.
{
HashSet<Ref<Skin>> mutated_skins;
@@ -766,6 +959,14 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
src_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion());
src_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale());
}
+ if (orig_skeleton) {
+ for (int i = 0; i < orig_skeleton->get_bone_count(); i++) {
+ Transform3D fixed_rest = orig_skeleton->get_bone_rest(i);
+ orig_skeleton->set_bone_pose_position(i, fixed_rest.origin);
+ orig_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion());
+ orig_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale());
+ }
+ }
}
}
diff --git a/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp b/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp
index 53bcc59fcb..98312d3521 100644
--- a/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp
+++ b/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp
@@ -39,7 +39,7 @@ void PostImportPluginSkeletonTrackOrganizer::get_internal_import_options(Interna
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/except_bone_transform"), false));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/unimportant_positions"), true));
- r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/unmapped_bones"), false));
+ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "retarget/remove_tracks/unmapped_bones", PROPERTY_HINT_ENUM, "None,Remove,Separate Library"), 0));
}
}
@@ -61,9 +61,9 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
}
bool remove_except_bone = bool(p_options["retarget/remove_tracks/except_bone_transform"]);
bool remove_positions = bool(p_options["retarget/remove_tracks/unimportant_positions"]);
- bool remove_unmapped_bones = bool(p_options["retarget/remove_tracks/unmapped_bones"]);
+ int separate_unmapped_bones = int(p_options["retarget/remove_tracks/unmapped_bones"]);
- if (!remove_positions && !remove_unmapped_bones) {
+ if (!remove_positions && separate_unmapped_bones == 0) {
return;
}
@@ -72,10 +72,16 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
List<StringName> anims;
ap->get_animation_list(&anims);
+
+ Ref<AnimationLibrary> unmapped_al;
+ unmapped_al.instantiate();
+
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
Vector<int> remove_indices;
+ Vector<int> mapped_bone_indices;
+ Vector<int> unmapped_bone_indices;
for (int i = 0; i < track_len; i++) {
String track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
@@ -96,16 +102,19 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
StringName bn = anim->track_get_path(i).get_subname(0);
if (bn) {
int prof_idx = profile->find_bone(bone_map->find_profile_bone_name(bn));
- if (remove_unmapped_bones && prof_idx < 0) {
- remove_indices.push_back(i);
+ if (prof_idx < 0) {
+ unmapped_bone_indices.push_back(i);
continue;
}
if (remove_positions && anim->track_get_type(i) == Animation::TYPE_POSITION_3D && prof_idx >= 0) {
StringName prof_bn = profile->get_bone_name(prof_idx);
if (prof_bn == profile->get_root_bone() || prof_bn == profile->get_scale_base_bone()) {
+ mapped_bone_indices.push_back(i);
continue;
}
remove_indices.push_back(i);
+ } else {
+ mapped_bone_indices.push_back(i);
}
}
}
@@ -114,11 +123,34 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
}
}
+ if (separate_unmapped_bones == 2 && !unmapped_bone_indices.is_empty()) {
+ Ref<Animation> unmapped_anim = anim->duplicate();
+ Vector<int> to_delete;
+ to_delete.append_array(mapped_bone_indices);
+ to_delete.append_array(remove_indices);
+ to_delete.sort();
+ to_delete.reverse();
+ for (int E : to_delete) {
+ unmapped_anim->remove_track(E);
+ }
+ unmapped_al->add_animation(name, unmapped_anim);
+ }
+
+ if (separate_unmapped_bones >= 1) {
+ remove_indices.append_array(unmapped_bone_indices);
+ remove_indices.sort();
+ }
remove_indices.reverse();
for (int i = 0; i < remove_indices.size(); i++) {
anim->remove_track(remove_indices[i]);
}
}
+
+ if (unmapped_al->get_animation_list_size() == 0) {
+ unmapped_al.unref();
+ } else if (separate_unmapped_bones == 2) {
+ ap->add_animation_library("unmapped_bones", unmapped_al);
+ }
}
}
}
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 86af9caf26..9ab97abd56 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -34,6 +34,7 @@
#include "core/io/dir_access.h"
#include "core/io/resource_saver.h"
#include "core/object/script_language.h"
+#include "editor/editor_interface.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/import/3d/scene_import_settings.h"
@@ -2312,6 +2313,7 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
}
}
+ // TODO: If there are more than 2 or equal get_internal_option_visibility method, visibility state is broken.
for (int i = 0; i < post_importer_plugins.size(); i++) {
Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), _scene_import_type, p_option, p_options);
if (ret.get_type() == Variant::BOOL) {
@@ -2814,6 +2816,15 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani
}
}
+void ResourceImporterScene::_generate_editor_preview_for_scene(const String &p_path, Node *p_scene) {
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+ ERR_FAIL_COND_MSG(p_path.is_empty(), "Path is empty, cannot generate preview.");
+ ERR_FAIL_NULL_MSG(p_scene, "Scene is null, cannot generate preview.");
+ EditorInterface::get_singleton()->make_scene_preview(p_path, p_scene, 1024);
+}
+
Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options) {
Ref<EditorSceneFormatImporter> importer;
String ext = p_source_file.get_extension().to_lower();
@@ -3164,6 +3175,7 @@ Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p
print_verbose("Saving scene to: " + p_save_path + ".scn");
err = ResourceSaver::save(packer, p_save_path + ".scn", flags); //do not take over, let the changed files reload themselves
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + p_save_path + ".scn'.");
+ _generate_editor_preview_for_scene(p_source_file, scene);
} else {
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown scene import type: " + _scene_import_type);
}
diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h
index b2f5fab0eb..6868baf023 100644
--- a/editor/import/3d/resource_importer_scene.h
+++ b/editor/import/3d/resource_importer_scene.h
@@ -236,6 +236,7 @@ class ResourceImporterScene : public ResourceImporter {
};
void _optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions);
+ void _generate_editor_preview_for_scene(const String &p_path, Node *p_scene);
String _scene_import_type = "PackedScene";
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index a2aeeb11bd..c60197b96b 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -551,18 +551,18 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
}
void InputEventConfigurationDialog::_device_selection_changed(int p_option_button_index) {
- // Option index 0 corresponds to "All Devices" (value of -3).
- // Otherwise subtract 1 as option index 1 corresponds to device 0, etc...
- event->set_device(p_option_button_index == 0 ? InputEvent::DEVICE_ID_ALL_DEVICES : p_option_button_index - 1);
+ // Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)
+ // and option index 1 corresponds to device 0, etc...
+ event->set_device(p_option_button_index - 1);
event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));
}
void InputEventConfigurationDialog::_set_current_device(int p_device) {
- device_id_option->select(p_device == InputEvent::DEVICE_ID_ALL_DEVICES ? 0 : p_device + 1);
+ device_id_option->select(p_device + 1);
}
int InputEventConfigurationDialog::_get_current_device() const {
- return device_id_option->get_selected() == 0 ? InputEvent::DEVICE_ID_ALL_DEVICES : device_id_option->get_selected() - 1;
+ return device_id_option->get_selected() - 1;
}
void InputEventConfigurationDialog::_notification(int p_what) {
@@ -705,12 +705,11 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
device_id_option = memnew(OptionButton);
device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- device_id_option->add_item(EventListenerLineEdit::get_device_string(InputEvent::DEVICE_ID_ALL_DEVICES));
- for (int i = 0; i < 8; i++) {
+ for (int i = -1; i < 8; i++) {
device_id_option->add_item(EventListenerLineEdit::get_device_string(i));
}
device_id_option->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed));
- _set_current_device(InputEvent::DEVICE_ID_ALL_DEVICES);
+ _set_current_device(InputMap::ALL_DEVICES);
device_container->add_child(device_id_option);
device_container->hide();
diff --git a/editor/plugins/animation_library_editor.cpp b/editor/plugins/animation_library_editor.cpp
index 4e8a1bd89b..46410b581e 100644
--- a/editor/plugins/animation_library_editor.cpp
+++ b/editor/plugins/animation_library_editor.cpp
@@ -30,7 +30,12 @@
#include "animation_library_editor.h"
+#include "core/string/print_string.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
@@ -518,6 +523,8 @@ void AnimationLibraryEditor::_item_renamed() {
if (restore_text) {
ti->set_text(0, old_text);
}
+
+ _save_mixer_lib_folding(ti);
}
void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button) {
@@ -670,6 +677,8 @@ void AnimationLibraryEditor::update_tree() {
TreeItem *root = tree->create_item();
List<StringName> libs;
+ Vector<uint64_t> collapsed_lib_ids = _load_mixer_libs_folding();
+
mixer->get_animation_library_list(&libs);
for (const StringName &K : libs) {
@@ -759,12 +768,203 @@ void AnimationLibraryEditor::update_tree() {
anitem->set_text(1, anim_path.get_file());
}
}
+
anitem->add_button(1, get_editor_theme_icon("Save"), ANIM_BUTTON_FILE, animation_library_is_foreign, TTR("Save animation to resource on disk."));
anitem->add_button(1, get_editor_theme_icon("Remove"), ANIM_BUTTON_DELETE, animation_library_is_foreign, TTR("Remove animation from Library."));
+
+ for (const uint64_t &lib_id : collapsed_lib_ids) {
+ Object *lib_obj = ObjectDB::get_instance(ObjectID(lib_id));
+ AnimationLibrary *cur_lib = Object::cast_to<AnimationLibrary>(lib_obj);
+ StringName M = mixer->get_animation_library_name(cur_lib);
+
+ if (M == K) {
+ libitem->set_collapsed_recursive(true);
+ }
+ }
}
}
}
+void AnimationLibraryEditor::_save_mixer_lib_folding(TreeItem *p_item) {
+ //Check if ti is a library or animation
+ if (p_item->get_parent()->get_parent() != nullptr) {
+ return;
+ }
+
+ Ref<ConfigFile> config;
+ config.instantiate();
+
+ String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("lib_folding.cfg");
+ Error err = config->load(path);
+ if (err != OK && err != ERR_FILE_NOT_FOUND) {
+ ERR_PRINT("Error loading lib_folding.cfg: " + itos(err));
+ }
+
+ // Get unique identifier for this scene+mixer combination
+ String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text();
+
+ PackedStringArray collapsed_lib_names;
+ PackedStringArray collapsed_lib_ids;
+
+ if (config->has_section(md)) {
+ collapsed_lib_names = String(config->get_value(md, "folding")).split("\n");
+ collapsed_lib_ids = String(config->get_value(md, "id")).split("\n");
+ }
+
+ String lib_name = p_item->get_text(0);
+
+ // Get library reference and check validity
+ Ref<AnimationLibrary> al;
+ uint64_t lib_id = 0;
+
+ if (mixer->has_animation_library(lib_name)) {
+ al = mixer->get_animation_library(lib_name);
+ ERR_FAIL_COND(al.is_null());
+ lib_id = uint64_t(al->get_instance_id());
+ } else {
+ ERR_PRINT("Library not found: " + lib_name);
+ }
+
+ int at = collapsed_lib_names.find(lib_name);
+ if (p_item->is_collapsed()) {
+ if (at != -1) {
+ //Entry exists and needs updating
+ collapsed_lib_ids.set(at, String::num_int64(lib_id + INT64_MIN));
+ } else {
+ //Check if it's a rename
+ int id_at = collapsed_lib_ids.find(String::num_int64(lib_id + INT64_MIN));
+ if (id_at != -1) {
+ //It's actually a rename
+ collapsed_lib_names.set(id_at, lib_name);
+ } else {
+ //It's a new entry
+ collapsed_lib_names.append(lib_name);
+ collapsed_lib_ids.append(String::num_int64(lib_id + INT64_MIN));
+ }
+ }
+ } else {
+ if (at != -1) {
+ collapsed_lib_names.remove_at(at);
+ collapsed_lib_ids.remove_at(at);
+ }
+ }
+
+ //Runtime IDs
+ config->set_value(md, "root", uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id()));
+ config->set_value(md, "mixer", uint64_t(mixer->get_instance_id()));
+
+ //Plan B recovery mechanism
+ config->set_value(md, "mixer_signature", _get_mixer_signature());
+
+ //Save folding state as text and runtime ID
+ config->set_value(md, "folding", String("\n").join(collapsed_lib_names));
+ config->set_value(md, "id", String("\n").join(collapsed_lib_ids));
+
+ err = config->save(path);
+ if (err != OK) {
+ ERR_PRINT("Error saving lib_folding.cfg: " + itos(err));
+ }
+}
+
+Vector<uint64_t> AnimationLibraryEditor::_load_mixer_libs_folding() {
+ Ref<ConfigFile> config;
+ config.instantiate();
+
+ String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("lib_folding.cfg");
+ Error err = config->load(path);
+ if (err != OK && err != ERR_FILE_NOT_FOUND) {
+ ERR_PRINT("Error loading lib_folding.cfg: " + itos(err));
+ return Vector<uint64_t>();
+ }
+
+ // Get unique identifier for this scene+mixer combination
+ String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text();
+
+ Vector<uint64_t> collapsed_lib_ids;
+
+ if (config->has_section(md)) {
+ _load_config_libs_folding(collapsed_lib_ids, config.ptr(), md);
+
+ } else {
+ //The scene/mixer combination is no longer valid and we'll try to recover
+ uint64_t current_mixer_id = uint64_t(mixer->get_instance_id());
+ String current_mixer_signature = _get_mixer_signature();
+ List<String> sections;
+ config->get_sections(&sections);
+
+ for (const String &section : sections) {
+ Variant mixer_id = config->get_value(section, "mixer");
+ if ((mixer_id.get_type() == Variant::INT && uint64_t(mixer_id) == current_mixer_id) || config->get_value(section, "mixer_signature") == current_mixer_signature) { // Ensure value exists and is correct type
+ // Found the mixer in a different section!
+ _load_config_libs_folding(collapsed_lib_ids, config.ptr(), section);
+
+ //Cleanup old entry and copy fold data into new one!
+ String collapsed_lib_names_str = String(config->get_value(section, "folding"));
+ String collapsed_lib_ids_str = String(config->get_value(section, "id"));
+ config->erase_section(section);
+
+ config->set_value(md, "root", uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id()));
+ config->set_value(md, "mixer", uint64_t(mixer->get_instance_id()));
+ config->set_value(md, "mixer_signature", _get_mixer_signature());
+ config->set_value(md, "folding", collapsed_lib_names_str);
+ config->set_value(md, "id", collapsed_lib_ids_str);
+
+ err = config->save(path);
+ if (err != OK) {
+ ERR_PRINT("Error saving lib_folding.cfg: " + itos(err));
+ }
+ break;
+ }
+ }
+ }
+
+ return collapsed_lib_ids;
+}
+
+void AnimationLibraryEditor::_load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section) {
+ if (uint64_t(p_config->get_value(p_section, "root", 0)) != uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id())) {
+ // Root changed - tries to match by library names
+ PackedStringArray collapsed_lib_names = String(p_config->get_value(p_section, "folding", "")).split("\n");
+ for (const String &lib_name : collapsed_lib_names) {
+ if (mixer->has_animation_library(lib_name)) {
+ p_lib_ids.append(mixer->get_animation_library(lib_name)->get_instance_id());
+ } else {
+ print_line("Can't find ", lib_name, " in mixer");
+ }
+ }
+ } else {
+ // Root same - uses saved instance IDs
+ for (const String &saved_id : String(p_config->get_value(p_section, "id")).split("\n")) {
+ p_lib_ids.append(uint64_t(saved_id.to_int() - INT64_MIN));
+ }
+ }
+}
+
+String AnimationLibraryEditor::_get_mixer_signature() const {
+ String signature = String();
+
+ // Get all libraries sorted for consistency
+ List<StringName> libs;
+ mixer->get_animation_library_list(&libs);
+ libs.sort_custom<StringName::AlphCompare>();
+
+ // Add libraries and their animations to signature
+ for (const StringName &lib_name : libs) {
+ signature += "::" + String(lib_name);
+ Ref<AnimationLibrary> lib = mixer->get_animation_library(lib_name);
+ if (lib.is_valid()) {
+ List<StringName> anims;
+ lib->get_animation_list(&anims);
+ anims.sort_custom<StringName::AlphCompare>();
+ for (const StringName &anim_name : anims) {
+ signature += "," + String(anim_name);
+ }
+ }
+ }
+
+ return signature.md5_text();
+}
+
void AnimationLibraryEditor::show_dialog() {
update_tree();
popup_centered_ratio(0.5);
@@ -855,11 +1055,12 @@ AnimationLibraryEditor::AnimationLibraryEditor() {
tree->set_column_custom_minimum_width(1, EDSCALE * 250);
tree->set_column_expand(1, false);
tree->set_hide_root(true);
- tree->set_hide_folding(true);
+ tree->set_hide_folding(false);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tree->connect("item_edited", callable_mp(this, &AnimationLibraryEditor::_item_renamed));
tree->connect("button_clicked", callable_mp(this, &AnimationLibraryEditor::_button_pressed));
+ tree->connect("item_collapsed", callable_mp(this, &AnimationLibraryEditor::_save_mixer_lib_folding));
file_popup = memnew(PopupMenu);
add_child(file_popup);
diff --git a/editor/plugins/animation_library_editor.h b/editor/plugins/animation_library_editor.h
index beb34c6343..c4ad1684a6 100644
--- a/editor/plugins/animation_library_editor.h
+++ b/editor/plugins/animation_library_editor.h
@@ -31,6 +31,8 @@
#ifndef ANIMATION_LIBRARY_EDITOR_H
#define ANIMATION_LIBRARY_EDITOR_H
+#include "core/io/config_file.h"
+#include "core/templates/vector.h"
#include "editor/animation_track_editor.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_mixer.h"
@@ -103,6 +105,11 @@ class AnimationLibraryEditor : public AcceptDialog {
void _load_file(const String &p_path);
void _load_files(const PackedStringArray &p_paths);
+ void _save_mixer_lib_folding(TreeItem *p_item);
+ Vector<uint64_t> _load_mixer_libs_folding();
+ void _load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section);
+ String _get_mixer_signature() const;
+
void _item_renamed();
void _button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button);
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 4edd021b4d..818ac34105 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -901,7 +901,7 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) {
player->connect(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated), CONNECT_DEFERRED);
}
if (!player->is_connected(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed))) {
- player->connect(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed), CONNECT_DEFERRED);
+ player->connect(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed));
}
}
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index d10daa2bfc..8526150e0c 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -917,7 +917,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
}
if (state_machine_draw->has_focus()) {
- state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), theme_cache.highlight_color, false);
+ state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), theme_cache.focus_color, false);
}
int sep = 3 * EDSCALE;
@@ -1642,6 +1642,7 @@ void AnimationNodeStateMachineEditor::_bind_methods() {
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, transition_icon_disabled_color, "transition_icon_disabled_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, highlight_color, "highlight_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, highlight_disabled_color, "highlight_disabled_color", "GraphStateMachine");
+ BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, focus_color, "focus_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, guideline_color, "guideline_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icons[0], "TransitionImmediateBig", "EditorIcons");
diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h
index eb623a147d..0b6320f0ce 100644
--- a/editor/plugins/animation_state_machine_editor.h
+++ b/editor/plugins/animation_state_machine_editor.h
@@ -117,6 +117,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
Color transition_icon_disabled_color;
Color highlight_color;
Color highlight_disabled_color;
+ Color focus_color;
Color guideline_color;
Ref<Texture2D> transition_icons[6]{};
diff --git a/editor/plugins/camera_3d_editor_plugin.cpp b/editor/plugins/camera_3d_editor_plugin.cpp
index f4116ed364..10ed7d1902 100644
--- a/editor/plugins/camera_3d_editor_plugin.cpp
+++ b/editor/plugins/camera_3d_editor_plugin.cpp
@@ -30,8 +30,11 @@
#include "camera_3d_editor_plugin.h"
+#include "core/config/project_settings.h"
#include "editor/editor_node.h"
#include "node_3d_editor_plugin.h"
+#include "scene/gui/texture_rect.h"
+#include "scene/main/viewport.h"
void Camera3DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
@@ -76,9 +79,35 @@ Camera3DEditor::Camera3DEditor() {
preview->connect(SceneStringName(pressed), callable_mp(this, &Camera3DEditor::_pressed));
}
+void Camera3DPreview::_update_sub_viewport_size() {
+ sub_viewport->set_size(Node3DEditor::get_camera_viewport_size(camera));
+}
+
+Camera3DPreview::Camera3DPreview(Camera3D *p_camera) :
+ TexturePreview(nullptr, false), camera(p_camera), sub_viewport(memnew(SubViewport)) {
+ RenderingServer::get_singleton()->viewport_attach_camera(sub_viewport->get_viewport_rid(), camera->get_camera());
+ add_child(sub_viewport);
+
+ TextureRect *display = get_texture_display();
+ display->set_texture(sub_viewport->get_texture());
+ sub_viewport->connect("size_changed", callable_mp((CanvasItem *)display, &CanvasItem::queue_redraw));
+
+ ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Camera3DPreview::_update_sub_viewport_size));
+ _update_sub_viewport_size();
+}
+
+bool EditorInspectorPluginCamera3DPreview::can_handle(Object *p_object) {
+ return Object::cast_to<Camera3D>(p_object) != nullptr;
+}
+
+void EditorInspectorPluginCamera3DPreview::parse_begin(Object *p_object) {
+ Camera3D *camera = Object::cast_to<Camera3D>(p_object);
+ Camera3DPreview *preview = memnew(Camera3DPreview(camera));
+ add_custom_control(preview);
+}
+
void Camera3DEditorPlugin::edit(Object *p_object) {
Node3DEditor::get_singleton()->set_can_preview(Object::cast_to<Camera3D>(p_object));
- //camera_editor->edit(Object::cast_to<Node>(p_object));
}
bool Camera3DEditorPlugin::handles(Object *p_object) const {
@@ -86,27 +115,15 @@ bool Camera3DEditorPlugin::handles(Object *p_object) const {
}
void Camera3DEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- //Node3DEditor::get_singleton()->set_can_preview(Object::cast_to<Camera3D>(p_object));
- } else {
+ if (!p_visible) {
Node3DEditor::get_singleton()->set_can_preview(nullptr);
}
}
Camera3DEditorPlugin::Camera3DEditorPlugin() {
- /* camera_editor = memnew( CameraEditor );
- EditorNode::get_singleton()->get_main_screen_control()->add_child(camera_editor);
-
- camera_editor->set_anchor(SIDE_LEFT,Control::ANCHOR_END);
- camera_editor->set_anchor(SIDE_RIGHT,Control::ANCHOR_END);
- camera_editor->set_offset(SIDE_LEFT,60);
- camera_editor->set_offset(SIDE_RIGHT,0);
- camera_editor->set_offset(SIDE_TOP,0);
- camera_editor->set_offset(SIDE_BOTTOM,10);
-
-
- camera_editor->hide();
-*/
+ Ref<EditorInspectorPluginCamera3DPreview> plugin;
+ plugin.instantiate();
+ add_inspector_plugin(plugin);
}
Camera3DEditorPlugin::~Camera3DEditorPlugin() {
diff --git a/editor/plugins/camera_3d_editor_plugin.h b/editor/plugins/camera_3d_editor_plugin.h
index 1c6838aa02..98d0bcc40c 100644
--- a/editor/plugins/camera_3d_editor_plugin.h
+++ b/editor/plugins/camera_3d_editor_plugin.h
@@ -32,7 +32,10 @@
#define CAMERA_3D_EDITOR_PLUGIN_H
#include "editor/plugins/editor_plugin.h"
-#include "scene/3d/camera_3d.h"
+#include "editor/plugins/texture_editor_plugin.h"
+
+class Camera3D;
+class SubViewport;
class Camera3DEditor : public Control {
GDCLASS(Camera3DEditor, Control);
@@ -51,11 +54,29 @@ public:
Camera3DEditor();
};
+class Camera3DPreview : public TexturePreview {
+ GDCLASS(Camera3DPreview, TexturePreview);
+
+ Camera3D *camera = nullptr;
+ SubViewport *sub_viewport = nullptr;
+
+ void _update_sub_viewport_size();
+
+public:
+ Camera3DPreview(Camera3D *p_camera);
+};
+
+class EditorInspectorPluginCamera3DPreview : public EditorInspectorPluginTexture {
+ GDCLASS(EditorInspectorPluginCamera3DPreview, EditorInspectorPluginTexture);
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual void parse_begin(Object *p_object) override;
+};
+
class Camera3DEditorPlugin : public EditorPlugin {
GDCLASS(Camera3DEditorPlugin, EditorPlugin);
- //CameraEditor *camera_editor;
-
public:
virtual String get_name() const override { return "Camera3D"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index f91a052a24..f73867575d 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1995,15 +1995,14 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
}
}
+ Transform2D edit_transform;
+ bool using_temp_pivot = !Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y);
+ if (using_temp_pivot) {
+ edit_transform = Transform2D(drag_selection.front()->get()->_edit_get_rotation(), temp_pivot);
+ } else {
+ edit_transform = drag_selection.front()->get()->_edit_get_transform();
+ }
for (CanvasItem *ci : drag_selection) {
- Transform2D edit_transform;
- bool using_temp_pivot = !Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y);
- if (using_temp_pivot) {
- edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot);
- } else {
- edit_transform = ci->_edit_get_transform();
- }
-
Transform2D parent_xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse();
Transform2D unscaled_transform = (transform * parent_xform * edit_transform).orthonormalized();
Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform;
@@ -3612,7 +3611,7 @@ void CanvasItemEditor::_draw_selection() {
// Remove non-movable nodes.
for (CanvasItem *ci : selection) {
- if (!_is_node_movable(ci, true)) {
+ if (!_is_node_movable(ci)) {
selection.erase(ci);
}
}
@@ -3913,7 +3912,7 @@ void CanvasItemEditor::_draw_message() {
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
- Point2 msgpos = Point2(RULER_WIDTH + 5 * EDSCALE, viewport->get_size().y - 20 * EDSCALE);
+ Point2 msgpos = Point2(RULER_WIDTH + 10 * EDSCALE, viewport->get_size().y - 14 * EDSCALE);
viewport->draw_string(font, msgpos + Point2(1, 1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
viewport->draw_string(font, msgpos + Point2(-1, -1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
viewport->draw_string(font, msgpos, message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1));
diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
index 63a48d4165..359bb59098 100644
--- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
@@ -46,24 +46,6 @@ Camera3DGizmoPlugin::Camera3DGizmoPlugin() {
create_handle_material("handles");
}
-Size2i Camera3DGizmoPlugin::_get_viewport_size(Camera3D *p_camera) {
- Viewport *viewport = p_camera->get_viewport();
-
- Window *window = Object::cast_to<Window>(viewport);
- if (window) {
- return window->get_size();
- }
-
- SubViewport *sub_viewport = Object::cast_to<SubViewport>(viewport);
- ERR_FAIL_NULL_V(sub_viewport, Size2i());
-
- if (sub_viewport == EditorNode::get_singleton()->get_scene_root()) {
- return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
- }
-
- return sub_viewport->get_size();
-}
-
bool Camera3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<Camera3D>(p_spatial) != nullptr;
}
@@ -166,7 +148,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Ref<Material> material = get_material("camera_material", p_gizmo);
Ref<Material> icon = get_material("camera_icon", p_gizmo);
- const Size2i viewport_size = _get_viewport_size(camera);
+ const Size2i viewport_size = Node3DEditor::get_camera_viewport_size(camera);
const real_t viewport_aspect = viewport_size.x > 0 && viewport_size.y > 0 ? viewport_size.aspect() : 1.0;
const Size2 size_factor = viewport_aspect > 1.0 ? Size2(1.0, 1.0 / viewport_aspect) : Size2(viewport_aspect, 1.0);
diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.h b/editor/plugins/gizmos/camera_3d_gizmo_plugin.h
index ba65ebb8de..94238704c1 100644
--- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.h
+++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.h
@@ -37,7 +37,6 @@ class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin);
private:
- static Size2i _get_viewport_size(Camera3D *p_camera);
static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform);
public:
diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
index 573c686d57..b92abbcf79 100644
--- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
@@ -49,17 +49,47 @@
CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() {
helper.instantiate();
- const Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color();
- create_material("shape_material", gizmo_color);
- const float gizmo_value = gizmo_color.get_v();
- const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
- create_material("shape_material_disabled", gizmo_color_disabled);
+
+ create_collision_material("shape_material", 2.0);
+ create_collision_material("shape_material_arraymesh", 0.0625);
+
+ create_collision_material("shape_material_disabled", 0.0625);
+ create_collision_material("shape_material_arraymesh_disabled", 0.015625);
+
create_handle_material("handles");
}
CollisionShape3DGizmoPlugin::~CollisionShape3DGizmoPlugin() {
}
+void CollisionShape3DGizmoPlugin::create_collision_material(const String &p_name, float p_alpha) {
+ Vector<Ref<StandardMaterial3D>> mats;
+
+ const Color collision_color(1.0, 1.0, 1.0, p_alpha);
+
+ for (int i = 0; i < 4; i++) {
+ bool instantiated = i < 2;
+
+ Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
+
+ Color color = collision_color;
+ color.a *= instantiated ? 0.25 : 1.0;
+
+ material->set_albedo(color);
+ material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
+ material->set_cull_mode(StandardMaterial3D::CULL_BACK);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
+ material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+
+ mats.push_back(material);
+ }
+
+ materials[p_name] = mats;
+}
+
bool CollisionShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CollisionShape3D>(p_spatial) != nullptr;
}
@@ -311,9 +341,20 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
return;
}
- const Ref<Material> material =
+ const Ref<StandardMaterial3D> material =
get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
- Ref<Material> handles_material = get_material("handles");
+ const Ref<StandardMaterial3D> material_arraymesh =
+ get_material(!cs->is_disabled() ? "shape_material_arraymesh" : "shape_material_arraymesh_disabled", p_gizmo);
+ const Ref<Material> handles_material = get_material("handles");
+
+ const Color collision_color = cs->is_disabled() ? Color(1.0, 1.0, 1.0, 0.75) : cs->get_debug_color();
+
+ if (cs->get_debug_fill_enabled()) {
+ Ref<ArrayMesh> array_mesh = s->get_debug_arraymesh_faces(collision_color);
+ if (array_mesh.is_valid()) {
+ p_gizmo->add_mesh(array_mesh, material_arraymesh);
+ }
+ }
if (Object::cast_to<SphereShape3D>(*s)) {
Ref<SphereShape3D> sp = s;
@@ -351,7 +392,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
collision_segments.push_back(Vector3(b.x, b.y, 0));
}
- p_gizmo->add_lines(points, material);
+ p_gizmo->add_lines(points, material, false, collision_color);
p_gizmo->add_collision_segments(collision_segments);
Vector<Vector3> handles;
handles.push_back(Vector3(r, 0, 0));
@@ -374,7 +415,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
const Vector<Vector3> handles = helper->box_get_handles(bs->get_size());
- p_gizmo->add_lines(lines, material);
+ p_gizmo->add_lines(lines, material, false, collision_color);
p_gizmo->add_collision_segments(lines);
p_gizmo->add_handles(handles, handles_material);
}
@@ -412,7 +453,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
points.push_back(Vector3(b.y, b.x, 0) + dud);
}
- p_gizmo->add_lines(points, material);
+ p_gizmo->add_lines(points, material, false, collision_color);
Vector<Vector3> collision_segments;
@@ -476,7 +517,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
}
- p_gizmo->add_lines(points, material);
+ p_gizmo->add_lines(points, material, false, collision_color);
Vector<Vector3> collision_segments;
@@ -531,7 +572,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p.normal * p.d + p.normal * 3
};
- p_gizmo->add_lines(points, material);
+ p_gizmo->add_lines(points, material, false, collision_color);
p_gizmo->add_collision_segments(points);
}
@@ -549,7 +590,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
lines.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a];
lines.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b];
}
- p_gizmo->add_lines(lines, material);
+ p_gizmo->add_lines(lines, material, false, collision_color);
p_gizmo->add_collision_segments(lines);
}
}
@@ -558,7 +599,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (Object::cast_to<ConcavePolygonShape3D>(*s)) {
Ref<ConcavePolygonShape3D> cs2 = s;
Ref<ArrayMesh> mesh = cs2->get_debug_mesh();
- p_gizmo->add_mesh(mesh, material);
+ p_gizmo->add_lines(cs2->get_debug_mesh_lines(), material, false, collision_color);
p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines());
}
@@ -569,7 +610,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector3(),
Vector3(0, 0, rs->get_length())
};
- p_gizmo->add_lines(points, material);
+ p_gizmo->add_lines(points, material, false, collision_color);
p_gizmo->add_collision_segments(points);
Vector<Vector3> handles;
handles.push_back(Vector3(0, 0, rs->get_length()));
@@ -579,7 +620,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (Object::cast_to<HeightMapShape3D>(*s)) {
Ref<HeightMapShape3D> hms = s;
- Ref<ArrayMesh> mesh = hms->get_debug_mesh();
- p_gizmo->add_mesh(mesh, material);
+ Vector<Vector3> lines = hms->get_debug_mesh_lines();
+ p_gizmo->add_lines(lines, material, false, collision_color);
}
}
diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h
index 464012acf9..09590fba58 100644
--- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h
+++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h
@@ -38,6 +38,8 @@ class Gizmo3DHelper;
class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
+ void create_collision_material(const String &p_name, float p_alpha);
+
Ref<Gizmo3DHelper> helper;
public:
diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp
index 6e5dfd44d4..3f21d5d11c 100644
--- a/editor/plugins/lightmap_gi_editor_plugin.cpp
+++ b/editor/plugins/lightmap_gi_editor_plugin.cpp
@@ -93,10 +93,14 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
file_dialog->popup_file_dialog();
} break;
case LightmapGI::BAKE_ERROR_NO_MESHES: {
- EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."));
+ EditorNode::get_singleton()->show_warning(
+ TTR("No meshes with lightmapping support to bake. Make sure they contain UV2 data and their Global Illumination property is set to Static.") +
+ String::utf8("\n\n• ") + TTR("To import a scene with lightmapping support, set Meshes > Light Baking to Static Lightmaps in the Import dock.") +
+ String::utf8("\n• ") + TTR("To enable lightmapping support on a primitive mesh, edit the PrimitiveMesh resource in the inspector and check Add UV2.") +
+ String::utf8("\n• ") + TTR("To enable lightmapping support on a CSG mesh, select the root CSG node and choose CSG > Bake Mesh Instance at the top of the 3D editor viewport.\nSelect the generated MeshInstance3D node and choose Mesh > Unwrap UV2 for Lightmap/AO at the top of the 3D editor viewport."));
} break;
case LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE: {
- EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable."));
+ EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure the lightmap destination path is writable."));
} break;
case LightmapGI::BAKE_ERROR_NO_SCENE_ROOT: {
EditorNode::get_singleton()->show_warning(TTR("No editor scene root found."));
@@ -108,7 +112,7 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
EditorNode::get_singleton()->show_warning(TTR("Maximum texture size is too small for the lightmap images.\nWhile this can be fixed by increasing the maximum texture size, it is recommended you split the scene into more objects instead."));
} break;
case LightmapGI::BAKE_ERROR_LIGHTMAP_TOO_SMALL: {
- EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes selected to bake have `lightmap_size_hint` value set high enough, and `texel_scale` value of LightmapGI is not too low."));
+ EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes to bake have the Lightmap Size Hint property set high enough, and the LightmapGI's Texel Scale value is not too low."));
} break;
case LightmapGI::BAKE_ERROR_ATLAS_TOO_SMALL: {
EditorNode::get_singleton()->show_warning(TTR("Failed fitting a lightmap image into an atlas. This should never happen and should be reported."));
@@ -148,7 +152,7 @@ EditorProgress *LightmapGIEditorPlugin::tmp_progress = nullptr;
bool LightmapGIEditorPlugin::bake_func_step(float p_progress, const String &p_description, void *, bool p_refresh) {
if (!tmp_progress) {
- tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), 1000, false));
+ tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), 1000, true));
ERR_FAIL_NULL_V(tmp_progress, false);
}
return tmp_progress->step(p_description, p_progress * 1000, p_refresh);
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index 6f79ab0529..39401e2738 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -156,6 +156,25 @@ void MeshLibraryEditor::_import_scene_parse_node(Ref<MeshLibrary> p_library, Has
}
p_library->set_item_mesh(item_id, item_mesh);
+ GeometryInstance3D::ShadowCastingSetting gi3d_cast_shadows_setting = mesh_instance_node->get_cast_shadows_setting();
+ switch (gi3d_cast_shadows_setting) {
+ case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF: {
+ p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF);
+ } break;
+ case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON: {
+ p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON);
+ } break;
+ case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED: {
+ p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED);
+ } break;
+ case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY: {
+ p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY);
+ } break;
+ default: {
+ p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON);
+ } break;
+ }
+
Transform3D item_mesh_transform;
if (p_apply_xforms) {
item_mesh_transform = mesh_instance_node->get_transform();
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 8aff3c9aec..b716e925cb 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -292,14 +292,11 @@ void EditorNode3DGizmo::add_vertices(const Vector<Vector3> &p_vertices, const Re
Vector<Color> color;
color.resize(p_vertices.size());
+ const Color vertex_color = (is_selected() ? Color(1, 1, 1, 0.8) : Color(1, 1, 1, 0.2)) * p_modulate;
{
Color *w = color.ptrw();
for (int i = 0; i < p_vertices.size(); i++) {
- if (is_selected()) {
- w[i] = Color(1, 1, 1, 0.8) * p_modulate;
- } else {
- w[i] = Color(1, 1, 1, 0.2) * p_modulate;
- }
+ w[i] = vertex_color;
}
}
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 810d1674ca..c321ff1db5 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -92,6 +92,7 @@
#include "scene/gui/center_container.h"
#include "scene/gui/color_picker.h"
#include "scene/gui/flow_container.h"
+#include "scene/gui/separator.h"
#include "scene/gui/split_container.h"
#include "scene/gui/subviewport_container.h"
#include "scene/resources/3d/sky_material.h"
@@ -3265,7 +3266,7 @@ void Node3DEditorViewport::_draw() {
if (message_time > 0) {
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
- Point2 msgpos = Point2(5, get_size().y - 20);
+ Point2 msgpos = Point2(10 * EDSCALE, get_size().y - 14 * EDSCALE);
font->draw_string(ci, msgpos + Point2(1, 1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
font->draw_string(ci, msgpos + Point2(-1, -1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
font->draw_string(ci, msgpos, message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1));
@@ -8818,7 +8819,12 @@ Node3DEditor::Node3DEditor() {
ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), Key::O);
ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), Key::F);
ED_SHORTCUT_ARRAY("spatial_editor/align_transform_with_view", TTR("Align Transform with View"),
- { int32_t(KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::KP_0), int32_t(KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::M) });
+ { int32_t(KeyModifierMask::ALT | KeyModifierMask::CTRL | Key::KP_0),
+ int32_t(KeyModifierMask::ALT | KeyModifierMask::CTRL | Key::M),
+ int32_t(KeyModifierMask::ALT | KeyModifierMask::CTRL | Key::G) });
+ ED_SHORTCUT_OVERRIDE_ARRAY("spatial_editor/align_transform_with_view", "macos",
+ { int32_t(KeyModifierMask::ALT | KeyModifierMask::META | Key::KP_0),
+ int32_t(KeyModifierMask::ALT | KeyModifierMask::META | Key::G) });
ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KeyModifierMask::ALT + KeyModifierMask::CMD_OR_CTRL + Key::F);
ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KeyModifierMask::SHIFT + Key::F);
ED_SHORTCUT("spatial_editor/decrease_fov", TTR("Decrease Field of View"), KeyModifierMask::CMD_OR_CTRL + Key::EQUAL); // Usually direct access key for `KEY_PLUS`.
@@ -9302,6 +9308,24 @@ void Node3DEditorPlugin::set_state(const Dictionary &p_state) {
spatial_editor->set_state(p_state);
}
+Size2i Node3DEditor::get_camera_viewport_size(Camera3D *p_camera) {
+ Viewport *viewport = p_camera->get_viewport();
+
+ Window *window = Object::cast_to<Window>(viewport);
+ if (window) {
+ return window->get_size();
+ }
+
+ SubViewport *sub_viewport = Object::cast_to<SubViewport>(viewport);
+ ERR_FAIL_NULL_V(sub_viewport, Size2i());
+
+ if (sub_viewport == EditorNode::get_singleton()->get_scene_root()) {
+ return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
+ }
+
+ return sub_viewport->get_size();
+}
+
Vector3 Node3DEditor::snap_point(Vector3 p_target, Vector3 p_start) const {
if (is_snap_enabled()) {
real_t snap = get_translate_snap();
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index d35fcb7653..27db7f27f3 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -880,6 +880,8 @@ protected:
public:
static Node3DEditor *get_singleton() { return singleton; }
+ static Size2i get_camera_viewport_size(Camera3D *p_camera);
+
Vector3 snap_point(Vector3 p_target, Vector3 p_start = Vector3(0, 0, 0)) const;
float get_znear() const { return settings_znear->get_value(); }
diff --git a/editor/plugins/plugin_config_dialog.cpp b/editor/plugins/plugin_config_dialog.cpp
index af9efda939..c3e87c508e 100644
--- a/editor/plugins/plugin_config_dialog.cpp
+++ b/editor/plugins/plugin_config_dialog.cpp
@@ -305,7 +305,7 @@ PluginConfigDialog::PluginConfigDialog() {
grid->add_child(script_name_label);
script_edit = memnew(LineEdit);
- script_edit->set_tooltip_text(TTR("Optional. The path to the script (relative to the add-on folder). If left empty, will default to \"plugin.gd\"."));
+ script_edit->set_tooltip_text(TTR("Optional. The name of the script file. If left empty, will default to the subfolder name."));
script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd");
script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(script_edit);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index a7c925f74f..0b58369917 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -64,6 +64,8 @@
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
#include "editor/window_wrapper.h"
+#include "scene/gui/separator.h"
+#include "scene/gui/texture_rect.h"
#include "scene/main/node.h"
#include "scene/main/window.h"
#include "script_text_editor.h"
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index cf586c792e..a28d709b15 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -40,6 +40,7 @@
#include "editor/editor_string_names.h"
#include "editor/gui/editor_toaster.h"
#include "editor/themes/editor_scale.h"
+#include "scene/gui/menu_button.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/split_container.h"
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 3ba3c196ad..5e4c861721 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -48,6 +48,8 @@
#include "scene/gui/option_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/scroll_container.h"
+#include "scene/gui/separator.h"
+#include "scene/gui/spin_box.h"
#include "scene/gui/split_container.h"
#include "scene/gui/tab_bar.h"
#include "scene/gui/tab_container.h"
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index ea5e8a8ad7..3790b23f76 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -41,9 +41,15 @@
#include "scene/gui/check_button.h"
#include "scene/gui/color_picker.h"
#include "scene/gui/color_rect.h"
+#include "scene/gui/label.h"
#include "scene/gui/margin_container.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/panel.h"
#include "scene/gui/progress_bar.h"
#include "scene/gui/scroll_container.h"
+#include "scene/gui/separator.h"
+#include "scene/gui/spin_box.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/text_edit.h"
#include "scene/gui/tree.h"
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 55118b7ea7..ca0241b6c1 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -59,6 +59,7 @@
#include "scene/gui/rich_text_label.h"
#include "scene/gui/separator.h"
#include "scene/gui/split_container.h"
+#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
@@ -6880,8 +6881,10 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Grayscale", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), { VisualShaderNodeColorFunc::FUNC_GRAYSCALE }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("HSV2RGB", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeColorFunc::FUNC_HSV2RGB, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("LinearToSRGB", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Converts color from linear to sRGB color space."), { VisualShaderNodeColorFunc::FUNC_LINEAR_TO_SRGB }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("RGB2HSV", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeColorFunc::FUNC_RGB2HSV, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Sepia", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), { VisualShaderNodeColorFunc::FUNC_SEPIA }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("SRGBToLinear", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Converts color from sRGB to linear color space."), { VisualShaderNodeColorFunc::FUNC_SRGB_TO_LINEAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Burn", "Color/Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), { VisualShaderNodeColorOp::OP_BURN }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Darken", "Color/Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), { VisualShaderNodeColorOp::OP_DARKEN }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
@@ -7027,7 +7030,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("NodePositionView", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_view", "NODE_POSITION_VIEW"), { "node_position_view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("NodePositionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_world", "NODE_POSITION_WORLD"), { "node_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("PointCoord", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "point_coord", "POINT_COORD"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("ScreenUV", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ScreenUV", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Tangent", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "tangent", "TANGENT"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Vertex", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex", "VERTEX"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("View", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view", "VIEW"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
@@ -7045,6 +7048,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("LightIsDirectional", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_is_directional", "LIGHT_IS_DIRECTIONAL"), { "light_is_directional" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Metallic", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "metallic", "METALLIC"), { "metallic" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Roughness", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "roughness", "ROUGHNESS"), { "roughness" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ScreenUV", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Specular", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "specular", "SPECULAR_LIGHT"), { "specular" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("View", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view", "VIEW"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index 68fe013c08..527138e060 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -146,15 +146,15 @@ void VoxelGIEditorPlugin::make_visible(bool p_visible) {
EditorProgress *VoxelGIEditorPlugin::tmp_progress = nullptr;
-void VoxelGIEditorPlugin::bake_func_begin(int p_steps) {
+void VoxelGIEditorPlugin::bake_func_begin() {
ERR_FAIL_COND(tmp_progress != nullptr);
- tmp_progress = memnew(EditorProgress("bake_gi", TTR("Bake VoxelGI"), p_steps));
+ tmp_progress = memnew(EditorProgress("bake_gi", TTR("Bake VoxelGI"), 1000, true));
}
-void VoxelGIEditorPlugin::bake_func_step(int p_step, const String &p_description) {
- ERR_FAIL_NULL(tmp_progress);
- tmp_progress->step(p_description, p_step, false);
+bool VoxelGIEditorPlugin::bake_func_step(int p_progress, const String &p_description) {
+ ERR_FAIL_NULL_V(tmp_progress, false);
+ return tmp_progress->step(p_description, p_progress, false);
}
void VoxelGIEditorPlugin::bake_func_end() {
diff --git a/editor/plugins/voxel_gi_editor_plugin.h b/editor/plugins/voxel_gi_editor_plugin.h
index d09822dda6..01a2ab4bd1 100644
--- a/editor/plugins/voxel_gi_editor_plugin.h
+++ b/editor/plugins/voxel_gi_editor_plugin.h
@@ -50,8 +50,8 @@ class VoxelGIEditorPlugin : public EditorPlugin {
EditorFileDialog *probe_file = nullptr;
static EditorProgress *tmp_progress;
- static void bake_func_begin(int p_steps);
- static void bake_func_step(int p_step, const String &p_description);
+ static void bake_func_begin();
+ static bool bake_func_step(int p_progress, const String &p_description);
static void bake_func_end();
void _bake();
diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp
index 617ad24299..3bf3fefa13 100644
--- a/editor/themes/editor_theme_manager.cpp
+++ b/editor/themes/editor_theme_manager.cpp
@@ -2510,6 +2510,7 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
p_theme->set_color("transition_icon_disabled_color", "GraphStateMachine", Color(1, 1, 1, 0.2));
p_theme->set_color("highlight_color", "GraphStateMachine", p_config.accent_color);
p_theme->set_color("highlight_disabled_color", "GraphStateMachine", p_config.accent_color * Color(1, 1, 1, 0.6));
+ p_theme->set_color("focus_color", "GraphStateMachine", p_config.accent_color);
p_theme->set_color("guideline_color", "GraphStateMachine", p_config.font_color * Color(1, 1, 1, 0.3));
p_theme->set_color("playback_color", "GraphStateMachine", p_config.font_color);