summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/editor_audio_buses.cpp41
-rw-r--r--editor/editor_feature_profile.cpp12
-rw-r--r--editor/editor_inspector.cpp127
-rw-r--r--editor/editor_inspector.h2
-rw-r--r--editor/editor_themes.cpp5
-rw-r--r--editor/import/editor_importer_bake_reset.cpp223
-rw-r--r--editor/import/editor_importer_bake_reset.h54
-rw-r--r--editor/import/resource_importer_scene.cpp7
-rw-r--r--editor/localization_editor.cpp9
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp10
-rw-r--r--editor/plugins/theme_editor_plugin.cpp4
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp2
-rw-r--r--editor/plugins/tiles/tile_proxies_manager_dialog.cpp12
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp2
15 files changed, 384 insertions, 128 deletions
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 4a19f007d4..5209ee06c6 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -62,7 +62,8 @@ void EditorAudioBus::_update_visible_channels() {
void EditorAudioBus::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
for (int i = 0; i < CHANNELS_MAX; i++) {
channel[i].vu_l->set_under_texture(get_theme_icon(SNAME("BusVuEmpty"), SNAME("EditorIcons")));
channel[i].vu_l->set_progress_texture(get_theme_icon(SNAME("BusVuFull"), SNAME("EditorIcons")));
@@ -86,6 +87,12 @@ void EditorAudioBus::_notification(int p_what) {
bus_options->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
+ audio_value_preview_label->add_theme_color_override("font_color", get_theme_color(SNAME("font_color"), SNAME("TooltipLabel")));
+ audio_value_preview_label->add_theme_color_override("font_shadow_color", get_theme_color(SNAME("font_shadow_color"), SNAME("TooltipLabel")));
+ audio_value_preview_box->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")));
+ } break;
+
+ case NOTIFICATION_READY: {
update_bus();
set_process(true);
} break;
@@ -157,26 +164,7 @@ void EditorAudioBus::_notification(int p_what) {
set_process(is_visible_in_tree());
} break;
- case NOTIFICATION_THEME_CHANGED: {
- for (int i = 0; i < CHANNELS_MAX; i++) {
- channel[i].vu_l->set_under_texture(get_theme_icon(SNAME("BusVuEmpty"), SNAME("EditorIcons")));
- channel[i].vu_l->set_progress_texture(get_theme_icon(SNAME("BusVuFull"), SNAME("EditorIcons")));
- channel[i].vu_r->set_under_texture(get_theme_icon(SNAME("BusVuEmpty"), SNAME("EditorIcons")));
- channel[i].vu_r->set_progress_texture(get_theme_icon(SNAME("BusVuFull"), SNAME("EditorIcons")));
- channel[i].prev_active = true;
- }
-
- disabled_vu = get_theme_icon(SNAME("BusVuFrozen"), SNAME("EditorIcons"));
-
- solo->set_icon(get_theme_icon(SNAME("AudioBusSolo"), SNAME("EditorIcons")));
- mute->set_icon(get_theme_icon(SNAME("AudioBusMute"), SNAME("EditorIcons")));
- bypass->set_icon(get_theme_icon(SNAME("AudioBusBypass"), SNAME("EditorIcons")));
-
- bus_options->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
- audio_value_preview_box->add_theme_color_override("font_color", get_theme_color(SNAME("font_color"), SNAME("TooltipLabel")));
- audio_value_preview_box->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")));
- } break;
case NOTIFICATION_MOUSE_EXIT:
case NOTIFICATION_DRAG_END: {
if (hovering_drop) {
@@ -833,6 +821,11 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
slider->set_clip_contents(false);
audio_value_preview_box = memnew(Panel);
+ slider->add_child(audio_value_preview_box);
+ audio_value_preview_box->set_as_top_level(true);
+ audio_value_preview_box->set_mouse_filter(MOUSE_FILTER_PASS);
+ audio_value_preview_box->hide();
+
HBoxContainer *audioprev_hbc = memnew(HBoxContainer);
audioprev_hbc->set_v_size_flags(SIZE_EXPAND_FILL);
audioprev_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -842,16 +835,8 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
audio_value_preview_label->set_v_size_flags(SIZE_EXPAND_FILL);
audio_value_preview_label->set_h_size_flags(SIZE_EXPAND_FILL);
audio_value_preview_label->set_mouse_filter(MOUSE_FILTER_PASS);
- audio_value_preview_box->add_theme_color_override("font_color", get_theme_color(SNAME("font_color"), SNAME("TooltipLabel")));
-
audioprev_hbc->add_child(audio_value_preview_label);
- slider->add_child(audio_value_preview_box);
- audio_value_preview_box->set_as_top_level(true);
- audio_value_preview_box->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")));
- audio_value_preview_box->set_mouse_filter(MOUSE_FILTER_PASS);
- audio_value_preview_box->hide();
-
preview_timer = memnew(Timer);
preview_timer->set_wait_time(0.8f);
preview_timer->set_one_shot(true);
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index 22f44a54bb..72a0c353e8 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -740,7 +740,7 @@ void EditorFeatureProfileManager::_update_selected_profile() {
TreeItem *features = class_list->create_item(root);
TreeItem *last_feature;
- features->set_text(0, TTR("Main Features") + ":");
+ features->set_text(0, TTR("Main Features:"));
for (int i = 0; i < EditorFeatureProfile::FEATURE_MAX; i++) {
TreeItem *feature;
if (i == EditorFeatureProfile::FEATURE_IMPORT_DOCK) {
@@ -764,7 +764,7 @@ void EditorFeatureProfileManager::_update_selected_profile() {
}
TreeItem *classes = class_list->create_item(root);
- classes->set_text(0, TTR("Nodes and Classes") + ":");
+ classes->set_text(0, TTR("Nodes and Classes:"));
_fill_classes_from(classes, "Node", class_selected);
_fill_classes_from(classes, "Resource", class_selected);
@@ -917,7 +917,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
class_list_vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
class_list = memnew(Tree);
- class_list_vbc->add_margin_child(TTR("Configure Selected Profile") + ":", class_list, true);
+ class_list_vbc->add_margin_child(TTR("Configure Selected Profile:"), class_list, true);
class_list->set_hide_root(true);
class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
class_list->connect("cell_selected", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_selected));
@@ -931,11 +931,11 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
property_list_vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
description_bit = memnew(EditorHelpBit);
- property_list_vbc->add_margin_child(TTR("Description") + ":", description_bit, false);
+ property_list_vbc->add_margin_child(TTR("Description:"), description_bit, false);
description_bit->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
property_list = memnew(Tree);
- property_list_vbc->add_margin_child(TTR("Extra Options") + ":", property_list, true);
+ property_list_vbc->add_margin_child(TTR("Extra Options:"), property_list, true);
property_list->set_hide_root(true);
property_list->set_hide_folding(true);
property_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
@@ -957,7 +957,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
VBoxContainer *new_profile_vb = memnew(VBoxContainer);
new_profile_dialog->add_child(new_profile_vb);
Label *new_profile_label = memnew(Label);
- new_profile_label->set_text(TTR("New profile name") + ":");
+ new_profile_label->set_text(TTR("New profile name:"));
new_profile_vb->add_child(new_profile_label);
new_profile_name = memnew(LineEdit);
new_profile_vb->add_child(new_profile_name);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 3db03c0276..4a3413d9ef 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -493,6 +493,7 @@ bool EditorPropertyRevert::is_node_property_different(Node *p_node, const Varian
if (ss.is_valid()) {
found_state = true;
+ break;
}
if (node == edited_scene) {
//just in case
@@ -506,59 +507,71 @@ bool EditorPropertyRevert::is_node_property_different(Node *p_node, const Varian
}
}
- if (p_current.get_type() == Variant::FLOAT && p_orig.get_type() == Variant::FLOAT) {
- float a = p_current;
- float b = p_orig;
+ return is_property_value_different(p_current, p_orig);
+}
- return !Math::is_equal_approx(a, b); //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
+bool EditorPropertyRevert::is_property_value_different(const Variant &p_a, const Variant &p_b) {
+ if (p_a.get_type() == Variant::FLOAT && p_b.get_type() == Variant::FLOAT) {
+ //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
+ return !Math::is_equal_approx((float)p_a, (float)p_b);
+ } else {
+ return p_a != p_b;
}
-
- return bool(Variant::evaluate(Variant::OP_NOT_EQUAL, p_current, p_orig));
}
-bool EditorPropertyRevert::can_property_revert(Object *p_object, const StringName &p_property) {
- bool has_revert = false;
+Variant EditorPropertyRevert::get_property_revert_value(Object *p_object, const StringName &p_property) {
+ // If the object implements property_can_revert, rely on that completely
+ // (i.e. don't then try to revert to default value - the property_get_revert implementation
+ // can do that if so desired)
+ if (p_object->has_method("property_can_revert") && p_object->call("property_can_revert", p_property)) {
+ return p_object->call("property_get_revert", p_property);
+ }
+ Ref<Script> scr = p_object->get_script();
Node *node = Object::cast_to<Node>(p_object);
-
if (node && EditorPropertyRevert::may_node_be_in_instance(node)) {
- //check for difference including instantiation
- Variant vorig;
- if (EditorPropertyRevert::get_instantiated_node_original_property(node, p_property, vorig)) {
- Variant v = p_object->get(p_property);
-
- if (EditorPropertyRevert::is_node_property_different(node, v, vorig)) {
- has_revert = true;
+ //if this node is an instance or inherits, but it has a script attached which is unrelated
+ //to the one set for the parent and also has a default value for the property, consider that
+ //has precedence over the value from the parent, because that is an explicit source of defaults
+ //closer in the tree to the current node
+ bool ignore_parent = false;
+ if (scr.is_valid()) {
+ Variant sorig;
+ if (EditorPropertyRevert::get_instantiated_node_original_property(node, "script", sorig) && !scr->inherits_script(sorig)) {
+ Variant dummy;
+ if (scr->get_property_default_value(p_property, dummy)) {
+ ignore_parent = true;
+ }
}
}
- } else {
- //check for difference against default class value instead
- Variant default_value = ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property);
- if (default_value != Variant() && default_value != p_object->get(p_property)) {
- has_revert = true;
+
+ if (!ignore_parent) {
+ //check for difference including instantiation
+ Variant vorig;
+ if (EditorPropertyRevert::get_instantiated_node_original_property(node, p_property, vorig)) {
+ return vorig;
+ }
}
}
- // If the object implements property_can_revert, rely on that completely
- // (i.e. don't then try to revert to default value - the property_get_revert implementation
- // can do that if so desired)
- if (p_object->has_method("property_can_revert")) {
- has_revert = p_object->call("property_can_revert", p_property).operator bool();
- } else {
- if (!has_revert && !p_object->get_script().is_null()) {
- Ref<Script> scr = p_object->get_script();
- if (scr.is_valid()) {
- Variant orig_value;
- if (scr->get_property_default_value(p_property, orig_value)) {
- if (orig_value != p_object->get(p_property)) {
- has_revert = true;
- }
- }
- }
+ if (scr.is_valid()) {
+ Variant orig_value;
+ if (scr->get_property_default_value(p_property, orig_value)) {
+ return orig_value;
}
}
- return has_revert;
+ //report default class value instead
+ return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property);
+}
+
+bool EditorPropertyRevert::can_property_revert(Object *p_object, const StringName &p_property) {
+ Variant revert_value = EditorPropertyRevert::get_property_revert_value(p_object, p_property);
+ if (revert_value.get_type() == Variant::NIL) {
+ return false;
+ }
+ Variant current_value = p_object->get(p_property);
+ return EditorPropertyRevert::is_property_value_different(current_value, revert_value);
}
void EditorProperty::update_reload_status() {
@@ -761,41 +774,11 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) {
}
if (revert_rect.has_point(mpos)) {
- Variant vorig;
-
- Node *node = Object::cast_to<Node>(object);
- if (node && EditorPropertyRevert::may_node_be_in_instance(node) && EditorPropertyRevert::get_instantiated_node_original_property(node, property, vorig)) {
- emit_changed(property, vorig.duplicate(true));
- update_property();
- return;
- }
-
- if (object->call("property_can_revert", property).operator bool()) {
- Variant rev = object->call("property_get_revert", property);
- emit_changed(property, rev);
- update_property();
- return;
- }
-
- if (!object->get_script().is_null()) {
- Ref<Script> scr = object->get_script();
- if (scr.is_valid()) {
- Variant orig_value;
- if (scr->get_property_default_value(property, orig_value)) {
- emit_changed(property, orig_value);
- update_property();
- return;
- }
- }
- }
-
- Variant default_value = ClassDB::class_get_default_property_value(object->get_class_name(), property);
- if (default_value != Variant()) {
- emit_changed(property, default_value);
- update_property();
- return;
- }
+ Variant revert_value = EditorPropertyRevert::get_property_revert_value(object, property);
+ emit_changed(property, revert_value);
+ update_property();
}
+
if (check_rect.has_point(mpos)) {
checked = !checked;
update();
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index ee70bd4397..2bb856dc81 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -42,6 +42,8 @@ public:
static bool may_node_be_in_instance(Node *p_node);
static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value);
static bool is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig);
+ static bool is_property_value_different(const Variant &p_a, const Variant &p_b);
+ static Variant get_property_revert_value(Object *p_object, const StringName &p_property);
static bool can_property_revert(Object *p_object, const StringName &p_property);
};
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index c230bdcb73..f0cc1530ab 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -380,6 +380,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color font_color = mono_color.lerp(base_color, 0.25);
const Color font_hover_color = mono_color.lerp(base_color, 0.125);
const Color font_disabled_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.3);
+ const Color font_readonly_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.65);
const Color selection_color = accent_color * Color(1, 1, 1, 0.4);
const Color disabled_color = mono_color.inverted().lerp(base_color, 0.7);
const Color disabled_bg_color = mono_color.inverted().lerp(base_color, 0.9);
@@ -1028,9 +1029,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("read_only", "LineEdit", style_line_edit_disabled);
theme->set_icon("clear", "LineEdit", theme->get_icon("GuiClose", "EditorIcons"));
theme->set_color("read_only", "LineEdit", font_disabled_color);
- theme->set_color("font_uneditable_color", "LineEdit", font_disabled_color);
theme->set_color("font_color", "LineEdit", font_color);
theme->set_color("font_selected_color", "LineEdit", mono_color);
+ theme->set_color("font_uneditable_color", "LineEdit", font_readonly_color);
theme->set_color("caret_color", "LineEdit", font_color);
theme->set_color("selection_color", "LineEdit", selection_color);
theme->set_color("clear_button_color", "LineEdit", font_color);
@@ -1196,7 +1197,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_tooltip->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE * 0.5);
style_tooltip->set_default_margin(SIDE_RIGHT, default_margin_size * EDSCALE);
style_tooltip->set_default_margin(SIDE_BOTTOM, default_margin_size * EDSCALE * 0.5);
- style_tooltip->set_bg_color(mono_color.inverted());
+ style_tooltip->set_bg_color(mono_color.inverted() * Color(1, 1, 1, 0.9));
style_tooltip->set_border_width_all(0);
theme->set_color("font_color", "TooltipLabel", font_hover_color);
theme->set_color("font_color_shadow", "TooltipLabel", Color(0, 0, 0, 0));
diff --git a/editor/import/editor_importer_bake_reset.cpp b/editor/import/editor_importer_bake_reset.cpp
new file mode 100644
index 0000000000..939c47faa4
--- /dev/null
+++ b/editor/import/editor_importer_bake_reset.cpp
@@ -0,0 +1,223 @@
+/*************************************************************************/
+/* editor_importer_bake_reset.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor/import/editor_importer_bake_reset.h"
+
+#include "core/error/error_macros.h"
+#include "core/math/transform_3d.h"
+#include "editor/import/scene_importer_mesh_node_3d.h"
+#include "resource_importer_scene.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/animation/animation_player.h"
+
+// Given that an engineering team has made a reference character, one wants ten animators to create animations.
+// Currently, a tech artist needs to combine the ten files into one exported gltf2 to import into Godot Engine.
+// We bake the RESET animation and then set it to identity,
+// so that rigs with corresponding RESET animation can have their animations transferred with ease.
+//
+// The original algorithm for the code was used to change skeleton bone rolls to be parent to child.
+//
+// Reference https://github.com/godotengine/godot-proposals/issues/2961
+void BakeReset::_bake_animation_pose(Node *scene, const String &p_bake_anim) {
+ Map<StringName, BakeResetRestBone> r_rest_bones;
+ Vector<Node3D *> r_meshes;
+ List<Node *> queue;
+ queue.push_back(scene);
+ while (!queue.is_empty()) {
+ List<Node *>::Element *E = queue.front();
+ Node *node = E->get();
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
+ // Step 1: import scene with animations into the rest bones data structure.
+ _fetch_reset_animation(ap, r_rest_bones, p_bake_anim);
+
+ int child_count = node->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ queue.push_back(node->get_child(i));
+ }
+ queue.pop_front();
+ }
+
+ queue.push_back(scene);
+ while (!queue.is_empty()) {
+ List<Node *>::Element *E = queue.front();
+ Node *node = E->get();
+ EditorSceneImporterMeshNode3D *editor_mesh_3d = scene->cast_to<EditorSceneImporterMeshNode3D>(node);
+ MeshInstance3D *mesh_3d = scene->cast_to<MeshInstance3D>(node);
+ if (scene->cast_to<Skeleton3D>(node)) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+
+ // Step 2: Bake the RESET animation from the RestBone to the skeleton.
+ _fix_skeleton(skeleton, r_rest_bones);
+ }
+ if (editor_mesh_3d) {
+ NodePath path = editor_mesh_3d->get_skeleton_path();
+ if (!path.is_empty() && editor_mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(editor_mesh_3d->get_node_or_null(path))) {
+ r_meshes.push_back(editor_mesh_3d);
+ }
+ } else if (mesh_3d) {
+ NodePath path = mesh_3d->get_skeleton_path();
+ if (!path.is_empty() && mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(mesh_3d->get_node_or_null(path))) {
+ r_meshes.push_back(mesh_3d);
+ }
+ }
+ int child_count = node->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ queue.push_back(node->get_child(i));
+ }
+ queue.pop_front();
+ }
+
+ queue.push_back(scene);
+ while (!queue.is_empty()) {
+ List<Node *>::Element *E = queue.front();
+ Node *node = E->get();
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
+ if (ap) {
+ // Step 3: Key all RESET animation frames to identity.
+ _align_animations(ap, r_rest_bones);
+ }
+
+ int child_count = node->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ queue.push_back(node->get_child(i));
+ }
+ queue.pop_front();
+ }
+}
+
+void BakeReset::_align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones) {
+ ERR_FAIL_NULL(p_ap);
+ List<StringName> anim_names;
+ p_ap->get_animation_list(&anim_names);
+ for (List<StringName>::Element *anim_i = anim_names.front(); anim_i; anim_i = anim_i->next()) {
+ Ref<Animation> a = p_ap->get_animation(anim_i->get());
+ ERR_CONTINUE(a.is_null());
+ for (Map<StringName, BakeResetRestBone>::Element *rest_bone_i = r_rest_bones.front(); rest_bone_i; rest_bone_i = rest_bone_i->next()) {
+ int track = a->find_track(NodePath(rest_bone_i->key()));
+ if (track == -1) {
+ continue;
+ }
+ int new_track = a->add_track(Animation::TYPE_TRANSFORM3D);
+ NodePath new_path = NodePath(rest_bone_i->key());
+ BakeResetRestBone rest_bone = rest_bone_i->get();
+ a->track_set_path(new_track, new_path);
+ for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
+ Vector3 loc;
+ Quaternion rot;
+ Vector3 scale;
+ Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
+ ERR_CONTINUE(err);
+ real_t time = a->track_get_key_time(track, key_i);
+ rot.normalize();
+ loc = loc - rest_bone.loc;
+ rot = rest_bone.rest_delta.get_rotation_quaternion().inverse() * rot;
+ rot.normalize();
+ scale = Vector3(1, 1, 1) - (rest_bone.rest_delta.get_scale() - scale);
+ // Apply the reverse of the rest changes to make the key be close to identity transform.
+ a->transform_track_insert_key(new_track, time, loc, rot, scale);
+ }
+ a->remove_track(track);
+ }
+ }
+}
+
+void BakeReset::_fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim) {
+ ERR_FAIL_NULL(p_ap);
+ List<StringName> anim_names;
+ p_ap->get_animation_list(&anim_names);
+ Node *root = p_ap->get_owner();
+ ERR_FAIL_NULL(root);
+ Ref<Animation> a = p_ap->get_animation(p_bake_anim);
+ ERR_FAIL_NULL(a);
+ for (int32_t track = 0; track < a->get_track_count(); track++) {
+ NodePath path = a->track_get_path(track);
+ String string_path = path;
+ Skeleton3D *skeleton = root->cast_to<Skeleton3D>(root->get_node(string_path.get_slice(":", 0)));
+ if (!skeleton) {
+ continue;
+ }
+ String bone_name = string_path.get_slice(":", 1);
+ for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
+ Vector3 loc;
+ Quaternion rot;
+ Vector3 scale;
+ Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
+ ERR_CONTINUE(err);
+ rot.normalize();
+ Basis rot_basis = Basis(rot, scale);
+ BakeResetRestBone rest_bone;
+ rest_bone.rest_delta = rot_basis;
+ rest_bone.loc = loc;
+ // Store the animation into the RestBone.
+ r_rest_bones[StringName(String(skeleton->get_owner()->get_path_to(skeleton)) + ":" + bone_name)] = rest_bone;
+ break;
+ }
+ }
+}
+
+void BakeReset::_fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones) {
+ int bone_count = p_skeleton->get_bone_count();
+
+ // First iterate through all the bones and update the RestBone.
+ for (int j = 0; j < bone_count; j++) {
+ StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(j);
+ BakeResetRestBone &rest_bone = r_rest_bones[final_path];
+ rest_bone.rest_local = p_skeleton->get_bone_rest(j);
+ }
+ for (int i = 0; i < bone_count; i++) {
+ int parent_bone = p_skeleton->get_bone_parent(i);
+ String path = p_skeleton->get_owner()->get_path_to(p_skeleton);
+ StringName final_path = String(path) + String(":") + p_skeleton->get_bone_name(parent_bone);
+ if (parent_bone >= 0) {
+ r_rest_bones[path].children.push_back(i);
+ }
+ }
+
+ // When we apply transform to a bone, we also have to move all of its children in the opposite direction.
+ for (int i = 0; i < bone_count; i++) {
+ StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
+ r_rest_bones[final_path].rest_local = r_rest_bones[final_path].rest_local * Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc);
+ // Iterate through the children and move in the opposite direction.
+ for (int j = 0; j < r_rest_bones[final_path].children.size(); j++) {
+ int child_index = r_rest_bones[final_path].children[j];
+ StringName children_path = String(p_skeleton->get_name()) + String(":") + p_skeleton->get_bone_name(child_index);
+ r_rest_bones[children_path].rest_local = Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc).affine_inverse() * r_rest_bones[children_path].rest_local;
+ }
+ }
+
+ for (int i = 0; i < bone_count; i++) {
+ StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
+ ERR_CONTINUE(!r_rest_bones.has(final_path));
+ Transform3D rest_transform = r_rest_bones[final_path].rest_local;
+ p_skeleton->set_bone_rest(i, rest_transform);
+ }
+}
diff --git a/editor/import/editor_importer_bake_reset.h b/editor/import/editor_importer_bake_reset.h
new file mode 100644
index 0000000000..e36ae86181
--- /dev/null
+++ b/editor/import/editor_importer_bake_reset.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* editor_importer_bake_reset.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_BAKE_RESET_H
+#define RESOURCE_IMPORTER_BAKE_RESET_H
+
+#include "scene/main/node.h"
+
+class Skeleton3D;
+class AnimationPlayer;
+class BakeReset {
+ struct BakeResetRestBone {
+ Transform3D rest_local;
+ Basis rest_delta;
+ Vector3 loc;
+ Vector<int> children;
+ };
+
+public:
+ void _bake_animation_pose(Node *scene, const String &p_bake_anim);
+
+private:
+ void _fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones);
+ void _align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones);
+ void _fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim);
+};
+#endif
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index e9160f0414..1e642462dc 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -32,6 +32,7 @@
#include "core/io/resource_saver.h"
#include "editor/editor_node.h"
+#include "editor/import/editor_importer_bake_reset.h"
#include "editor/import/scene_import_settings.h"
#include "editor/import/scene_importer_mesh_node_3d.h"
#include "scene/3d/area_3d.h"
@@ -1060,6 +1061,7 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/bake_reset_animation"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
@@ -1410,6 +1412,11 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
_pre_fix_node(scene, scene, collision_map);
_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
+ bool use_bake_reset_animation = p_options["animation/bake_reset_animation"];
+ if (use_bake_reset_animation) {
+ BakeReset bake_reset;
+ bake_reset._bake_animation_pose(scene, "RESET");
+ }
String root_type = p_options["nodes/root_type"];
root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index b32ea67d4d..3fe1aa557d 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -371,15 +371,10 @@ void LocalizationEditor::_translation_filter_mode_changed(int p_mode) {
void LocalizationEditor::_pot_add(const PackedStringArray &p_paths) {
PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files");
-
for (int i = 0; i < p_paths.size(); i++) {
- for (int j = 0; j < pot_translations.size(); j++) {
- if (pot_translations[j] == p_paths[i]) {
- continue; //exists
- }
+ if (!pot_translations.has(p_paths[i])) {
+ pot_translations.push_back(p_paths[i]);
}
-
- pot_translations.push_back(p_paths[i]);
}
undo_redo->create_action(vformat(TTR("Add %d file(s) for POT generation"), p_paths.size()));
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index c94014944c..69206daea8 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -89,7 +89,7 @@ Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const {
void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_tree();
updating = true;
- undo_redo->create_action(TTR("Parameter Changed") + ": " + String(p_property), UndoRedo::MERGE_ENDS);
+ undo_redo->create_action(TTR("Parameter Changed:") + " " + String(p_property), UndoRedo::MERGE_ENDS);
undo_redo->add_do_property(tree, p_property, p_value);
undo_redo->add_undo_property(tree, p_property, tree->get(p_property));
undo_redo->add_do_method(this, "_update_graph");
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index b1f4baac13..af04c9566a 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1596,8 +1596,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
se->gizmo->commit_subgizmos(ids, restore, false);
spatial_editor->update_transform_gizmo();
} else {
- static const char *_transform_name[4] = { "None", "Rotate", "Translate", "Scale" };
- undo_redo->create_action(_transform_name[_edit.mode]);
+ static const char *_transform_name[4] = {
+ TTRC("None"),
+ TTRC("Rotate"),
+ // TRANSLATORS: This refers to the movement that changes the position of an object.
+ TTRC("Translate"),
+ TTRC("Scale"),
+ };
+ undo_redo->create_action(TTRGET(_transform_name[_edit.mode]));
List<Node *> &selection = editor_selection->get_selected_node_list();
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 6bcc65e30c..0929a629f8 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -3112,7 +3112,7 @@ void ThemeEditor::edit(const Ref<Theme> &p_theme) {
preview_tab->set_preview_theme(p_theme);
}
- theme_name->set_text(TTR("Theme") + ": " + theme->get_path().get_file());
+ theme_name->set_text(TTR("Theme:") + " " + theme->get_path().get_file());
}
Ref<Theme> ThemeEditor::get_edited_theme() {
@@ -3230,7 +3230,7 @@ ThemeEditor::ThemeEditor() {
add_child(top_menu);
theme_name = memnew(Label);
- theme_name->set_text(TTR("Theme") + ": ");
+ theme_name->set_text(TTR("Theme:"));
theme_name->set_theme_type_variation("HeaderSmall");
top_menu->add_child(theme_name);
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index bbafc7802b..d54906c98c 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -154,7 +154,7 @@ void AtlasMergingDialog::_merge_confirmed(String p_path) {
Ref<Texture2D> new_texture_resource = ResourceLoader::load(p_path, "Texture2D");
merged->set_texture(new_texture_resource);
- undo_redo->create_action("Merge TileSetAtlasSource");
+ undo_redo->create_action(TTR("Merge TileSetAtlasSource"));
int next_id = tile_set->get_next_source_id();
undo_redo->add_do_method(*tile_set, "add_source", merged, next_id);
undo_redo->add_undo_method(*tile_set, "remove_source", next_id);
diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
index e5e7efa531..9e47a44b34 100644
--- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
+++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
@@ -47,7 +47,7 @@ void TileProxiesManagerDialog::_menu_id_pressed(int p_id) {
}
void TileProxiesManagerDialog::_delete_selected_bindings() {
- undo_redo->create_action("Remove Tile Proxies");
+ undo_redo->create_action(TTR("Remove Tile Proxies"));
Vector<int> source_level_selected = source_level_list->get_selected_items();
for (int i = 0; i < source_level_selected.size(); i++) {
@@ -151,7 +151,7 @@ void TileProxiesManagerDialog::_add_button_pressed() {
Vector2i to_coords = to.get_atlas_coords();
if (from_coords.x >= 0 && from_coords.y >= 0 && to_coords.x >= 0 && to_coords.y >= 0) {
if (from.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE && to.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE) {
- undo_redo->create_action("Create Alternative-level Tile Proxy");
+ undo_redo->create_action(TTR("Create Alternative-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile, to.source_id, to.get_atlas_coords(), to.alternative_tile);
if (tile_set->has_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile)) {
Array a = tile_set->get_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile);
@@ -160,7 +160,7 @@ void TileProxiesManagerDialog::_add_button_pressed() {
undo_redo->add_undo_method(*tile_set, "remove_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile);
}
} else {
- undo_redo->create_action("Create Coords-level Tile Proxy");
+ undo_redo->create_action(TTR("Create Coords-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", from.source_id, from.get_atlas_coords(), to.source_id, to.get_atlas_coords());
if (tile_set->has_coords_level_tile_proxy(from.source_id, from.get_atlas_coords())) {
Array a = tile_set->get_coords_level_tile_proxy(from.source_id, from.get_atlas_coords());
@@ -170,7 +170,7 @@ void TileProxiesManagerDialog::_add_button_pressed() {
}
}
} else {
- undo_redo->create_action("Create source-level Tile Proxy");
+ undo_redo->create_action(TTR("Create source-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_source_level_tile_proxy", from.source_id, to.source_id);
if (tile_set->has_source_level_tile_proxy(from.source_id)) {
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", to.source_id, tile_set->get_source_level_tile_proxy(from.source_id));
@@ -186,7 +186,7 @@ void TileProxiesManagerDialog::_add_button_pressed() {
}
void TileProxiesManagerDialog::_clear_invalid_button_pressed() {
- undo_redo->create_action("Delete All Invalid Tile Proxies");
+ undo_redo->create_action(TTR("Delete All Invalid Tile Proxies"));
undo_redo->add_do_method(*tile_set, "cleanup_invalid_tile_proxies");
@@ -213,7 +213,7 @@ void TileProxiesManagerDialog::_clear_invalid_button_pressed() {
}
void TileProxiesManagerDialog::_clear_all_button_pressed() {
- undo_redo->create_action("Delete All Tile Proxies");
+ undo_redo->create_action(TTR("Delete All Tile Proxies"));
undo_redo->add_do_method(*tile_set, "clear_tile_proxies");
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 46818afe45..add75d47fd 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -4697,7 +4697,7 @@ public:
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
updating = true;
- undo_redo->create_action(TTR("Edit Visual Property") + ": " + p_property, UndoRedo::MERGE_ENDS);
+ undo_redo->create_action(TTR("Edit Visual Property:") + " " + p_property, UndoRedo::MERGE_ENDS);
undo_redo->add_do_property(node.ptr(), p_property, p_value);
undo_redo->add_undo_property(node.ptr(), p_property, node->get(p_property));