summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/SCsub1
-rw-r--r--editor/animation_track_editor.cpp332
-rw-r--r--editor/animation_track_editor.h2
-rw-r--r--editor/debugger/editor_visual_profiler.cpp4
-rw-r--r--editor/editor_file_system.cpp2
-rw-r--r--editor/editor_node.cpp25
-rw-r--r--editor/editor_paths.cpp14
-rw-r--r--editor/editor_plugin.cpp17
-rw-r--r--editor/editor_plugin.h7
-rw-r--r--editor/editor_settings.cpp33
-rw-r--r--editor/find_in_files.cpp2
-rw-r--r--editor/icons/KeyBlendShape.svg44
-rw-r--r--editor/icons/KeyTrackBlendShape.svg45
-rw-r--r--editor/icons/KeyTrackPosition.svg47
-rw-r--r--editor/icons/KeyTrackRotation.svg47
-rw-r--r--editor/icons/KeyTrackScale.svg47
-rw-r--r--editor/icons/KeyXPosition.svg43
-rw-r--r--editor/icons/KeyXRotation.svg44
-rw-r--r--editor/icons/KeyXScale.svg44
-rw-r--r--editor/import/editor_import_collada.cpp166
-rw-r--r--editor/import/editor_import_collada.h6
-rw-r--r--editor/import/editor_importer_bake_reset.cpp234
-rw-r--r--editor/import/editor_importer_bake_reset.h54
-rw-r--r--editor/import/resource_importer_obj.h4
-rw-r--r--editor/import/resource_importer_scene.cpp517
-rw-r--r--editor/import/resource_importer_scene.h110
-rw-r--r--editor/plugin_config_dialog.cpp8
-rw-r--r--editor/plugin_config_dialog.h1
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp24
-rw-r--r--editor/plugins/collision_polygon_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp4
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp76
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp12
-rw-r--r--editor/plugins/script_editor_plugin.cpp67
-rw-r--r--editor/plugins/script_editor_plugin.h3
-rw-r--r--editor/plugins/script_text_editor.cpp2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp434
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h62
-rw-r--r--editor/plugins/text_editor.cpp4
-rw-r--r--editor/plugins/text_editor.h2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp94
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h4
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp20
-rw-r--r--editor/plugins/tiles/tile_data_editors.h4
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp88
-rw-r--r--editor/plugins/tiles/tile_map_editor.h3
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp92
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h6
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp32
-rw-r--r--editor/settings_config_dialog.cpp2
50 files changed, 1808 insertions, 1129 deletions
diff --git a/editor/SCsub b/editor/SCsub
index d149cc6273..e8d417319a 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -101,6 +101,7 @@ if env["tools"]:
)
env.add_source_files(env.editor_sources, "*.cpp")
+ env.add_source_files(env.editor_sources, "register_exporters.gen.cpp")
SConscript("debugger/SCsub")
SConscript("fileserver/SCsub")
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 14ca35a664..7f118532c9 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -165,21 +165,40 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d_old = animation->track_get_key_value(track, key);
- Dictionary d_new = d_old.duplicate();
- d_new[p_name] = p_value;
- setting = true;
- undo_redo->create_action(TTR("Anim Change Transform"));
- undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
- undo_redo->add_do_method(this, "_update_obj", animation);
- undo_redo->add_undo_method(this, "_update_obj", animation);
- undo_redo->commit_action();
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ if (name == "position" || name == "rotation" || name == "scale") {
+ Variant old = animation->track_get_key_value(track, key);
+ setting = true;
+ String chan;
+ switch (animation->track_get_type(track)) {
+ case Animation::TYPE_POSITION_3D:
+ chan = "Position3D";
+ break;
+ case Animation::TYPE_ROTATION_3D:
+ chan = "Rotation3D";
+ break;
+ case Animation::TYPE_SCALE_3D:
+ chan = "Scale3D";
+ break;
+ default: {
+ }
+ }
+
+ undo_redo->create_action(vformat(TTR("Anim Change %s"), chan));
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+
+ setting = false;
+ return true;
+ }
- setting = false;
- return true;
} break;
+ case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
if (name == "value") {
Variant value = p_value;
@@ -412,13 +431,15 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d = animation->track_get_key_value(track, key);
- ERR_FAIL_COND_V(!d.has(name), false);
- r_ret = d[p_name];
- return true;
-
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ if (name == "position" || name == "rotation" || name == "scale") {
+ r_ret = animation->track_get_key_value(track, key);
+ return true;
+ }
} break;
+ case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
if (name == "value") {
r_ret = animation->track_get_key_value(track, key);
@@ -523,11 +544,17 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
- p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
+ case Animation::TYPE_POSITION_3D: {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "rotation"));
+ } break;
+ case Animation::TYPE_SCALE_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
-
+ } break;
+ case Animation::TYPE_BLEND_SHAPE: {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "value"));
} break;
case Animation::TYPE_VALUE: {
Variant v = animation->track_get_key_value(track, key);
@@ -779,19 +806,34 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d_old = animation->track_get_key_value(track, key);
- Dictionary d_new = d_old.duplicate();
- d_new[p_name] = p_value;
-
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ Variant old = animation->track_get_key_value(track, key);
if (!setting) {
+ String chan;
+ switch (animation->track_get_type(track)) {
+ case Animation::TYPE_POSITION_3D:
+ chan = "Position3D";
+ break;
+ case Animation::TYPE_ROTATION_3D:
+ chan = "Rotation3D";
+ break;
+ case Animation::TYPE_SCALE_3D:
+ chan = "Scale3D";
+ break;
+ default: {
+ }
+ }
+
setting = true;
- undo_redo->create_action(TTR("Anim Multi Change Transform"));
+ undo_redo->create_action(vformat(TTR("Anim Multi Change %s"), chan));
}
- undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
update_obj = true;
} break;
+ case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
if (name == "value") {
Variant value = p_value;
@@ -1009,13 +1051,16 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d = animation->track_get_key_value(track, key);
- ERR_FAIL_COND_V(!d.has(name), false);
- r_ret = d[p_name];
- return true;
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ if (name == "position" || name == "rotation" || name == "scale") {
+ r_ret = animation->track_get_key_value(track, key);
+ return true;
+ }
} break;
+ case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
if (name == "value") {
r_ret = animation->track_get_key_value(track, key);
@@ -1159,11 +1204,18 @@ public:
if (same_track_type) {
switch (animation->track_get_type(first_track)) {
- case Animation::TYPE_TRANSFORM3D: {
- p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
- p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
+ case Animation::TYPE_POSITION_3D: {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale"));
+ } break;
+ case Animation::TYPE_SCALE_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
} break;
+ case Animation::TYPE_BLEND_SHAPE: {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "value"));
+ } break;
case Animation::TYPE_VALUE: {
if (same_key_type) {
Variant v = animation->track_get_key_value(first_track, first_key);
@@ -1359,7 +1411,10 @@ void AnimationTimelineEdit::_notification(int p_what) {
add_track->get_popup()->clear();
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), TTR("Property Track"));
- add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")), TTR("3D Transform Track"));
+ add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track"));
+ add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track"));
+ add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track"));
+ add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBlendShape"), SNAME("EditorIcons")), TTR("Blend Shape Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track"));
@@ -1828,9 +1883,12 @@ void AnimationTrackEdit::_notification(int p_what) {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
Color color = get_theme_color(SNAME("font_color"), SNAME("Label"));
- Ref<Texture2D> type_icons[6] = {
+ Ref<Texture2D> type_icons[9] = {
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
- get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyTrackPosition"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyTrackRotation"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyTrackScale"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyTrackBlendShape"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
@@ -2021,7 +2079,7 @@ void AnimationTrackEdit::_notification(int p_what) {
interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
interp_mode_rect.size = icon->get_size();
- if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(icon, interp_mode_rect.position);
}
// Make it easier to click.
@@ -2031,7 +2089,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep;
interp_mode_rect.size.x += hsep;
- if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
interp_mode_rect.size.x += down_icon->get_width();
} else {
@@ -2054,7 +2112,7 @@ void AnimationTrackEdit::_notification(int p_what) {
loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
loop_mode_rect.size = icon->get_size();
- if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(icon, loop_mode_rect.position);
}
@@ -2064,7 +2122,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep;
loop_mode_rect.size.x += hsep;
- if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
loop_mode_rect.size.x += down_icon->get_width();
} else {
@@ -2284,9 +2342,12 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati
track = p_track;
update();
- Ref<Texture2D> type_icons[6] = {
+ Ref<Texture2D> type_icons[9] = {
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
- get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyBlendShape"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
@@ -2456,17 +2517,21 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
if (key_idx != -1) {
String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n";
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d = animation->track_get_key_value(track, key_idx);
- if (d.has("location")) {
- text += "Pos: " + String(d["location"]) + "\n";
- }
- if (d.has("rotation")) {
- text += "Rot: " + String(d["rotation"]) + "\n";
- }
- if (d.has("scale")) {
- text += "Scale: " + String(d["scale"]) + "\n";
- }
+ case Animation::TYPE_POSITION_3D: {
+ Vector3 t = animation->track_get_key_value(track, key_idx);
+ text += "Position: " + String(t) + "\n";
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ Quaternion t = animation->track_get_key_value(track, key_idx);
+ text += "Rotation: " + String(t) + "\n";
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ Vector3 t = animation->track_get_key_value(track, key_idx);
+ text += "Scale: " + String(t) + "\n";
+ } break;
+ case Animation::TYPE_BLEND_SHAPE: {
+ float t = animation->track_get_key_value(track, key_idx);
+ text += "Blend Shape: " + itos(t) + "\n";
} break;
case Animation::TYPE_VALUE: {
const Variant &v = animation->track_get_key_value(track, key_idx);
@@ -3321,9 +3386,15 @@ static bool track_type_is_resettable(Animation::TrackType p_type) {
switch (p_type) {
case Animation::TYPE_VALUE:
[[fallthrough]];
+ case Animation::TYPE_BLEND_SHAPE:
+ [[fallthrough]];
case Animation::TYPE_BEZIER:
[[fallthrough]];
- case Animation::TYPE_TRANSFORM3D:
+ case Animation::TYPE_POSITION_3D:
+ [[fallthrough]];
+ case Animation::TYPE_ROTATION_3D:
+ [[fallthrough]];
+ case Animation::TYPE_SCALE_3D:
return true;
default:
return false;
@@ -3483,33 +3554,50 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
NodePath np = path;
- int track_idx = -1;
+ int position_idx = -1;
+ int rotation_idx = -1;
+ int scale_idx = -1;
for (int i = 0; i < animation->get_track_count(); i++) {
- if (animation->track_get_type(i) != Animation::TYPE_TRANSFORM3D) {
- continue;
- }
if (animation->track_get_path(i) != np) {
continue;
}
- track_idx = i;
- break;
+ if (animation->track_get_type(i) == Animation::TYPE_POSITION_3D) {
+ position_idx = i;
+ }
+ if (animation->track_get_type(i) == Animation::TYPE_ROTATION_3D) {
+ rotation_idx = i;
+ }
+ if (animation->track_get_type(i) == Animation::TYPE_SCALE_3D) {
+ scale_idx = i;
+ }
}
InsertData id;
- Dictionary val;
-
id.path = np;
- id.track_idx = track_idx;
- id.value = p_xform;
- id.type = Animation::TYPE_TRANSFORM3D;
// TRANSLATORS: This describes the target of new animation track, will be inserted into another string.
id.query = vformat(TTR("node '%s'"), p_node->get_name());
id.advance = false;
- // Dialog insert.
- _query_insert(id);
+ {
+ id.track_idx = position_idx;
+ id.value = p_xform.origin;
+ id.type = Animation::TYPE_POSITION_3D;
+ _query_insert(id);
+ }
+ {
+ id.track_idx = rotation_idx;
+ id.value = p_xform.basis.get_rotation_quaternion();
+ id.type = Animation::TYPE_ROTATION_3D;
+ _query_insert(id);
+ }
+ {
+ id.track_idx = scale_idx;
+ id.value = p_xform.basis.get_scale();
+ id.type = Animation::TYPE_SCALE_3D;
+ _query_insert(id);
+ }
}
bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_sub) {
@@ -3530,7 +3618,7 @@ bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_s
}
int track_id = animation->find_track(path);
if (track_id >= 0) {
- if (animation->track_get_type(track_id) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track_id) == Animation::TYPE_POSITION_3D || animation->track_get_type(track_id) == Animation::TYPE_ROTATION_3D || animation->track_get_type(track_id) == Animation::TYPE_SCALE_3D) {
return true;
}
}
@@ -3977,18 +4065,14 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
Variant value;
switch (p_id.type) {
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D:
+ case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
value = p_id.value;
} break;
- case Animation::TYPE_TRANSFORM3D: {
- Transform3D tr = p_id.value;
- Dictionary d;
- d["location"] = tr.origin;
- d["scale"] = tr.basis.get_scale();
- d["rotation"] = Quaternion(tr.basis);
- value = d;
- } break;
case Animation::TYPE_BEZIER: {
Array array;
array.resize(5);
@@ -4404,8 +4488,13 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
ERR_FAIL_COND(!node);
NodePath path_to = root->get_path_to(node);
- if (adding_track_type == Animation::TYPE_TRANSFORM3D && !node->is_class("Node3D")) {
- EditorNode::get_singleton()->show_warning(TTR("Transform3D tracks only apply to 3D-based nodes."));
+ if (adding_track_type == Animation::TYPE_BLEND_SHAPE && !node->is_class("MeshInstance3D")) {
+ EditorNode::get_singleton()->show_warning(TTR("Blend Shape tracks only apply to MeshInstance3D nodes."));
+ return;
+ }
+
+ if ((adding_track_type == Animation::TYPE_POSITION_3D || adding_track_type == Animation::TYPE_ROTATION_3D || adding_track_type == Animation::TYPE_SCALE_3D) && !node->is_class("Node3D")) {
+ EditorNode::get_singleton()->show_warning(TTR("Position/Rotation/Scale 3D tracks only apply to 3D-based nodes."));
return;
}
@@ -4415,7 +4504,16 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
prop_selector->set_type_filter(Vector<Variant::Type>());
prop_selector->select_property_from_instance(node);
} break;
- case Animation::TYPE_TRANSFORM3D:
+ case Animation::TYPE_BLEND_SHAPE: {
+ adding_track_path = path_to;
+ Vector<Variant::Type> filter;
+ filter.push_back(Variant::FLOAT);
+ prop_selector->set_type_filter(filter);
+ prop_selector->select_property_from_instance(node);
+ } break;
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D:
case Animation::TYPE_METHOD: {
undo_redo->create_action(TTR("Add Track"));
undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
@@ -4584,7 +4682,27 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
}
switch (animation->track_get_type(p_track)) {
- case Animation::TYPE_TRANSFORM3D: {
+ case Animation::TYPE_POSITION_3D: {
+ if (!root->has_node(animation->track_get_path(p_track))) {
+ EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
+ return;
+ }
+ Node3D *base = Object::cast_to<Node3D>(root->get_node(animation->track_get_path(p_track)));
+
+ if (!base) {
+ EditorNode::get_singleton()->show_warning(TTR("Track is not of type Node3D, can't insert key"));
+ return;
+ }
+
+ Vector3 pos = base->get_position();
+
+ undo_redo->create_action(TTR("Add Position Key"));
+ undo_redo->add_do_method(animation.ptr(), "position_track_insert_key", p_track, p_ofs, pos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
if (!root->has_node(animation->track_get_path(p_track))) {
EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
return;
@@ -4596,18 +4714,35 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
return;
}
- Transform3D xf = base->get_transform();
+ Quaternion rot = base->get_transform().basis.operator Quaternion();
+
+ undo_redo->create_action(TTR("Add Rotation Key"));
+ undo_redo->add_do_method(animation.ptr(), "rotation_track_insert_key", p_track, p_ofs, rot);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ if (!root->has_node(animation->track_get_path(p_track))) {
+ EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
+ return;
+ }
+ Node3D *base = Object::cast_to<Node3D>(root->get_node(animation->track_get_path(p_track)));
+
+ if (!base) {
+ EditorNode::get_singleton()->show_warning(TTR("Track is not of type Node3D, can't insert key"));
+ return;
+ }
- Vector3 loc = xf.get_origin();
- Vector3 scale = xf.basis.get_scale_local();
- Quaternion rot = xf.basis;
+ Vector3 scale = base->get_scale();
- undo_redo->create_action(TTR("Add Transform Track Key"));
- undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale);
+ undo_redo->create_action(TTR("Add Scale Key"));
+ undo_redo->add_do_method(animation.ptr(), "scale_track_insert_key", p_track, p_ofs, scale);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
+ case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
NodePath bp;
Variant value;
@@ -5277,8 +5412,17 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}
switch (animation->track_get_type(i)) {
- case Animation::TYPE_TRANSFORM3D:
- text += " (Transform)";
+ case Animation::TYPE_POSITION_3D:
+ text += " (Position)";
+ break;
+ case Animation::TYPE_ROTATION_3D:
+ text += " (Rotation)";
+ break;
+ case Animation::TYPE_SCALE_3D:
+ text += " (Scale)";
+ break;
+ case Animation::TYPE_BLEND_SHAPE:
+ text += " (BlendShape)";
break;
case Animation::TYPE_METHOD:
text += " (Methods)";
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 576edef0c8..2555901557 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -35,7 +35,7 @@
#include "editor/editor_spin_slider.h"
#include "editor/property_editor.h"
#include "editor/property_selector.h"
-#include "scene/animation/animation_cache.h"
+
#include "scene/gui/control.h"
#include "scene/gui/file_dialog.h"
#include "scene/gui/menu_button.h"
diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp
index f17ad0d36c..f25f18b7e4 100644
--- a/editor/debugger/editor_visual_profiler.cpp
+++ b/editor/debugger/editor_visual_profiler.cpp
@@ -370,8 +370,8 @@ void EditorVisualProfiler::_update_frame(bool p_focus_selected) {
float total_gpu = E->get_metadata(2);
total_cpu += cpu_time;
total_gpu += gpu_time;
- E->set_metadata(1, cpu_time);
- E->set_metadata(2, gpu_time);
+ E->set_metadata(1, total_cpu);
+ E->set_metadata(2, total_gpu);
}
category->set_icon(0, track_icon);
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 6fef65c92d..767856f939 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -2163,7 +2163,7 @@ Error EditorFileSystem::_resource_import(const String &p_path) {
}
bool EditorFileSystem::_should_skip_directory(const String &p_path) {
- if (p_path == ProjectSettings::get_singleton()->get_project_data_path()) {
+ if (p_path.begins_with(ProjectSettings::get_singleton()->get_project_data_path())) {
return true;
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 5fd0a41788..2f92f60d5e 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -1776,19 +1776,25 @@ void EditorNode::restart_editor() {
}
void EditorNode::_save_all_scenes() {
+ bool all_saved = true;
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
Node *scene = editor_data.get_edited_scene_root(i);
- if (scene && scene->get_scene_file_path() != "" && DirAccess::exists(scene->get_scene_file_path().get_base_dir())) {
- if (i != editor_data.get_edited_scene()) {
- _save_scene(scene->get_scene_file_path(), i);
+ if (scene) {
+ if (scene->get_scene_file_path() != "" && DirAccess::exists(scene->get_scene_file_path().get_base_dir())) {
+ if (i != editor_data.get_edited_scene()) {
+ _save_scene(scene->get_scene_file_path(), i);
+ } else {
+ _save_scene_with_preview(scene->get_scene_file_path());
+ }
} else {
- _save_scene_with_preview(scene->get_scene_file_path());
+ all_saved = false;
}
- } else {
- show_warning(TTR("Could not save one or more scenes!"), TTR("Save All Scenes"));
}
}
+ if (!all_saved) {
+ show_warning(TTR("Could not save one or more scenes!"), TTR("Save All Scenes"));
+ }
_save_default_environment();
}
@@ -3842,7 +3848,8 @@ void EditorNode::register_editor_types() {
GDREGISTER_VIRTUAL_CLASS(EditorInterface);
GDREGISTER_CLASS(EditorExportPlugin);
GDREGISTER_CLASS(EditorResourceConversionPlugin);
- GDREGISTER_CLASS(EditorSceneImporter);
+ GDREGISTER_CLASS(EditorSceneFormatImporter);
+ GDREGISTER_CLASS(EditorScenePostImportPlugin);
GDREGISTER_CLASS(EditorInspector);
GDREGISTER_CLASS(EditorInspectorPlugin);
GDREGISTER_CLASS(EditorProperty);
@@ -5929,7 +5936,7 @@ EditorNode::EditorNode() {
ResourceFormatImporter::get_singleton()->add_importer(import_scene);
{
- Ref<EditorSceneImporterCollada> import_collada;
+ Ref<EditorSceneFormatImporterCollada> import_collada;
import_collada.instantiate();
import_scene->add_importer(import_collada);
@@ -5937,7 +5944,7 @@ EditorNode::EditorNode() {
import_obj2.instantiate();
import_scene->add_importer(import_obj2);
- Ref<EditorSceneImporterESCN> import_escn;
+ Ref<EditorSceneFormatImporterESCN> import_escn;
import_escn.instantiate();
import_scene->add_importer(import_escn);
}
diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp
index 71f13c0c2f..5b48cc2638 100644
--- a/editor/editor_paths.cpp
+++ b/editor/editor_paths.cpp
@@ -200,6 +200,20 @@ EditorPaths::EditorPaths() {
paths_valid = false;
}
}
+
+ // Check that the project data directory '.gdignore' file exists
+ String project_data_gdignore_file_path = project_data_dir.plus_file(".gdignore");
+ if (!FileAccess::exists(project_data_gdignore_file_path)) {
+ // Add an empty .gdignore file to avoid scan.
+ FileAccessRef f = FileAccess::open(project_data_gdignore_file_path, FileAccess::WRITE);
+ if (f) {
+ f->store_line("");
+ f->close();
+ } else {
+ ERR_PRINT("Failed to create file " + project_data_gdignore_file_path);
+ }
+ }
+
Engine::get_singleton()->set_shader_cache_path(project_data_dir);
// Editor metadata dir.
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index aee8322a97..61c01993ae 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -762,16 +762,23 @@ void EditorPlugin::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_p
EditorInspector::remove_inspector_plugin(p_plugin);
}
-void EditorPlugin::add_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer) {
+void EditorPlugin::add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer) {
ERR_FAIL_COND(!p_importer.is_valid());
ResourceImporterScene::get_singleton()->add_importer(p_importer);
}
-void EditorPlugin::remove_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer) {
+void EditorPlugin::remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer) {
ERR_FAIL_COND(!p_importer.is_valid());
ResourceImporterScene::get_singleton()->remove_importer(p_importer);
}
+void EditorPlugin::add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {
+ ResourceImporterScene::get_singleton()->add_post_importer_plugin(p_plugin);
+}
+void EditorPlugin::remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {
+ ResourceImporterScene::get_singleton()->remove_post_importer_plugin(p_plugin);
+}
+
int find(const PackedStringArray &a, const String &v) {
const String *r = a.ptr();
for (int j = 0; j < a.size(); ++j) {
@@ -879,8 +886,10 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_translation_parser_plugin", "parser"), &EditorPlugin::remove_translation_parser_plugin);
ClassDB::bind_method(D_METHOD("add_import_plugin", "importer"), &EditorPlugin::add_import_plugin);
ClassDB::bind_method(D_METHOD("remove_import_plugin", "importer"), &EditorPlugin::remove_import_plugin);
- ClassDB::bind_method(D_METHOD("add_scene_import_plugin", "scene_importer"), &EditorPlugin::add_scene_import_plugin);
- ClassDB::bind_method(D_METHOD("remove_scene_import_plugin", "scene_importer"), &EditorPlugin::remove_scene_import_plugin);
+ ClassDB::bind_method(D_METHOD("add_scene_format_importer_plugin", "scene_format_importer"), &EditorPlugin::add_scene_format_importer_plugin);
+ ClassDB::bind_method(D_METHOD("remove_scene_format_importer_plugin", "scene_format_importer"), &EditorPlugin::remove_scene_format_importer_plugin);
+ ClassDB::bind_method(D_METHOD("add_scene_post_import_plugin", "scene_import_plugin"), &EditorPlugin::add_scene_post_import_plugin);
+ ClassDB::bind_method(D_METHOD("remove_scene_post_import_plugin", "scene_import_plugin"), &EditorPlugin::remove_scene_post_import_plugin);
ClassDB::bind_method(D_METHOD("add_export_plugin", "plugin"), &EditorPlugin::add_export_plugin);
ClassDB::bind_method(D_METHOD("remove_export_plugin", "plugin"), &EditorPlugin::remove_export_plugin);
ClassDB::bind_method(D_METHOD("add_spatial_gizmo_plugin", "plugin"), &EditorPlugin::add_spatial_gizmo_plugin);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 57830df327..278059f8c4 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -288,8 +288,11 @@ public:
void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
- void add_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer);
- void remove_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer);
+ void add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer);
+ void remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer);
+
+ void add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer);
+ void remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer);
void add_autoload_singleton(const String &p_name, const String &p_path);
void remove_autoload_singleton(const String &p_name);
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 43d458c58e..223cc6650c 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -140,32 +140,37 @@ bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "shortcuts") {
Array save_array;
+ const OrderedHashMap<String, List<Ref<InputEvent>>> &builtin_list = InputMap::get_singleton()->get_builtins();
for (const KeyValue<String, Ref<Shortcut>> &shortcut_definition : shortcuts) {
Ref<Shortcut> sc = shortcut_definition.value;
- if (builtin_action_overrides.has(shortcut_definition.key)) {
+ if (builtin_list.has(shortcut_definition.key)) {
// This shortcut was auto-generated from built in actions: don't save.
+ // If the builtin is overriden, it will be saved in the "builtin_action_overrides" section below.
continue;
}
- if (optimize_save) {
- if (!sc->has_meta("original")) {
- continue; //this came from settings but is not any longer used
- }
+ Array shortcut_events = sc->get_events();
+
+ Dictionary dict;
+ dict["name"] = shortcut_definition.key;
+ dict["shortcuts"] = shortcut_events;
+
+ if (!sc->has_meta("original")) {
+ // Getting the meta when it doesn't exist will return an empty array. If the 'shortcut_events' have been cleared,
+ // we still want save the shortcut in this case so that shortcuts that the user has customised are not reset,
+ // even if the 'original' has not been populated yet. This can happen when calling save() from the Project Manager.
+ save_array.push_back(dict);
+ continue;
}
Array original_events = sc->get_meta("original");
- Array shortcut_events = sc->get_events();
bool is_same = Shortcut::is_event_array_equal(original_events, shortcut_events);
if (is_same) {
continue; // Not changed from default; don't save.
}
- Dictionary dict;
- dict["name"] = shortcut_definition.key;
- dict["shortcuts"] = shortcut_events;
-
save_array.push_back(dict);
}
r_ret = save_array;
@@ -1511,7 +1516,7 @@ void ED_SHORTCUT_OVERRIDE_ARRAY(const String &p_path, const String &p_feature, c
// Directly override the existing shortcut.
sc->set_events(events);
- sc->set_meta("original", events);
+ sc->set_meta("original", events.duplicate(true));
}
Ref<Shortcut> ED_SHORTCUT(const String &p_path, const String &p_name, Key p_keycode) {
@@ -1545,21 +1550,21 @@ Ref<Shortcut> ED_SHORTCUT_ARRAY(const String &p_path, const String &p_name, cons
sc.instantiate();
sc->set_name(p_name);
sc->set_events(events);
- sc->set_meta("original", events);
+ sc->set_meta("original", events.duplicate(true));
return sc;
}
Ref<Shortcut> sc = EditorSettings::get_singleton()->get_shortcut(p_path);
if (sc.is_valid()) {
sc->set_name(p_name); //keep name (the ones that come from disk have no name)
- sc->set_meta("original", events); //to compare against changes
+ sc->set_meta("original", events.duplicate(true)); //to compare against changes
return sc;
}
sc.instantiate();
sc->set_name(p_name);
sc->set_events(events);
- sc->set_meta("original", events); //to compare against changes
+ sc->set_meta("original", events.duplicate(true)); //to compare against changes
EditorSettings::get_singleton()->add_shortcut(p_path, sc);
return sc;
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
index 283496c0f1..b61f6e12eb 100644
--- a/editor/find_in_files.cpp
+++ b/editor/find_in_files.cpp
@@ -235,7 +235,7 @@ void FindInFiles::_scan_dir(String path, PackedStringArray &out_folders) {
// Ignore special dirs (such as .git and project data directory)
String project_data_dir_name = ProjectSettings::get_singleton()->get_project_data_dir_name();
- if (file.begins_with(".") || file == project_data_dir_name) {
+ if (file.begins_with(".") || file.begins_with(project_data_dir_name)) {
continue;
}
if (dir->current_is_hidden()) {
diff --git a/editor/icons/KeyBlendShape.svg b/editor/icons/KeyBlendShape.svg
new file mode 100644
index 0000000000..42f7341942
--- /dev/null
+++ b/editor/icons/KeyBlendShape.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg80"
+ sodipodi:docname="KeyBlendShape.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs84" />
+ <sodipodi:namedview
+ id="namedview82"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="84.4"
+ inkscape:cx="2.6599526"
+ inkscape:cy="5.0059242"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg80" />
+ <rect
+ fill="#3cf34e"
+ height="6.1027"
+ ry=".76286"
+ transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)"
+ width="6.1027"
+ x="-740.13947"
+ y="741.10779"
+ id="rect78"
+ style="fill:#5ad5c4;fill-opacity:1" />
+</svg>
diff --git a/editor/icons/KeyTrackBlendShape.svg b/editor/icons/KeyTrackBlendShape.svg
new file mode 100644
index 0000000000..e82f0d6a6f
--- /dev/null
+++ b/editor/icons/KeyTrackBlendShape.svg
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg12"
+ sodipodi:docname="KeyTrackBlendShape.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs16" />
+ <sodipodi:namedview
+ id="namedview14"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="29.839906"
+ inkscape:cx="-3.5690461"
+ inkscape:cy="9.0985541"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg12" />
+ <ellipse
+ style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+ id="path921"
+ cx="-0.88986981"
+ cy="6.0959954"
+ rx="1.2495773"
+ ry="1.0867468" />
+ <path
+ id="path1910"
+ style="color:#000000;fill:#5ad5c4;stroke-linejoin:round;-inkscape-stroke:none"
+ d="m 4.5230825,1.1341567 c -2.1310744,0.017055 -3.86718737,1.7635044 -3.86718737,3.8984375 0,1.8778511 1.34348597,3.4523891 3.11718737,3.8164061 L 3.95277,7.5794693 C 2.7929991,7.3095662 1.9351921,6.2780435 1.9351921,5.0325942 c 0,-1.4262775 1.123493,-2.5732858 2.5390622,-2.6152344 v 0.017578 h 0.2011719 l 0.1796875,-1.28125 H 4.5230825 v -0.011719 c 0,-0.00263 -2.64e-5,-0.00518 0,-0.00781 z m 1.6816406,0.019531 -0.1796875,1.28125 h 1.3085937 c 0.078866,0 0.1230469,0.044181 0.1230469,0.1230469 v 4.9882815 c 0,0.07887 -0.044181,0.121093 -0.1230469,0.121094 H 5.2887075 L 5.10902,8.9486103 h 2.2246093 c 0.7663818,0 1.4042969,-0.635962 1.4042969,-1.402344 V 2.5579848 c 0,-0.7663818 -0.637915,-1.4042969 -1.4042969,-1.4042969 z" />
+</svg>
diff --git a/editor/icons/KeyTrackPosition.svg b/editor/icons/KeyTrackPosition.svg
new file mode 100644
index 0000000000..05c6e88841
--- /dev/null
+++ b/editor/icons/KeyTrackPosition.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg12"
+ sodipodi:docname="KeyTrackPosition.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs16" />
+ <sodipodi:namedview
+ id="namedview14"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="42.2"
+ inkscape:cx="12.78436"
+ inkscape:cy="6.1729858"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg12" />
+ <ellipse
+ style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+ id="path921"
+ cx="-0.88986981"
+ cy="6.0959954"
+ rx="1.2495773"
+ ry="1.0867468" />
+ <path
+ d="M 4.949661,0.60426977 A 0.6444116,0.6444116 0 0 0 4.504153,0.79178767 L 3.215459,2.0804819 4.12663,2.9916532 4.95977,2.1585124 5.792911,2.9916532 6.704083,2.0804819 5.415388,0.79178767 A 0.6444116,0.6444116 0 0 0 4.949744,0.60426977 Z M 1.926771,3.3691634 0.638077,4.6578577 a 0.6444116,0.6444116 0 0 0 0,0.9111713 L 1.926771,6.8577233 2.837942,5.946552 2.004801,5.1134111 2.837942,4.2802702 1.926771,3.3690989 Z m 6.065948,0 -0.911171,0.9111713 0.83314,0.8331409 -0.83314,0.8331408 0.911171,0.9111714 1.288694,-1.2886944 a 0.6444116,0.6444116 0 0 0 0,-0.9111713 L 7.992719,3.3692278 Z M 4.959777,3.8247361 A 1.2886943,1.2886943 0 0 0 3.671083,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,6.4021248 1.2886943,1.2886943 0 0 0 6.248471,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,3.8247361 Z m -0.83314,3.4105296 -0.911172,0.9111715 1.288694,1.288694 a 0.6444116,0.6444116 0 0 0 0.911171,0 L 6.704025,8.1464372 5.792853,7.2352657 4.959712,8.0684062 4.126572,7.2352657 Z"
+ fill="#e0e0e0"
+ fill-opacity="0.99608"
+ id="path1400"
+ style="fill:#ea7940;fill-opacity:1;stroke-width:0.644347" />
+</svg>
diff --git a/editor/icons/KeyTrackRotation.svg b/editor/icons/KeyTrackRotation.svg
new file mode 100644
index 0000000000..d05e381eb2
--- /dev/null
+++ b/editor/icons/KeyTrackRotation.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg12"
+ sodipodi:docname="KeyTrackRotation.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs16" />
+ <sodipodi:namedview
+ id="namedview14"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="42.2"
+ inkscape:cx="1.9075829"
+ inkscape:cy="5.8175355"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg12" />
+ <ellipse
+ style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+ id="path921"
+ cx="-0.88986981"
+ cy="6.0959954"
+ rx="1.2495773"
+ ry="1.0867468" />
+ <path
+ d="m 5.0711986,0.88214062 a 4.1086779,4.1086779 0 0 0 -0.178839,0.00115 4.1086779,4.1086779 0 0 0 -0.409265,0.033245 A 4.1086779,4.1086779 0 0 0 0.99001362,4.1883346 4.1086779,4.1086779 0 0 0 2.1467236,7.9244144 h -0.648877 v 1.1739077 h 2.347816 a 0.58701268,0.58701268 0 0 0 0.569756,-0.729119 l -0.586953,-2.3478164 -1.139514,0.2854534 0.165082,0.6580347 a 2.9347699,2.9347699 0 0 1 -0.769204,-1.9752178 2.9347699,2.9347699 0 0 1 2.93477,-2.93477 2.9347699,2.9347699 0 0 1 2.93477,2.93477 2.9347699,2.9347699 0 0 1 -0.860944,2.0738257 l 0.831127,0.8311266 A 4.1086779,4.1086779 0 0 0 8.7040866,3.1726236 4.1086779,4.1086779 0 0 0 5.0711336,0.88215292 Z m -0.05159,2.93359608 a 1.173908,1.173908 0 0 0 -1.173907,1.173908 1.173908,1.173908 0 0 0 1.173907,1.173908 1.173908,1.173908 0 0 0 1.173909,-1.173908 1.173908,1.173908 0 0 0 -1.173909,-1.173908 z"
+ fill="#e0e0e0"
+ fill-opacity="0.99608"
+ id="path1165"
+ style="fill:#ff2b88;fill-opacity:1;stroke-width:0.586954" />
+</svg>
diff --git a/editor/icons/KeyTrackScale.svg b/editor/icons/KeyTrackScale.svg
new file mode 100644
index 0000000000..9269ccbca2
--- /dev/null
+++ b/editor/icons/KeyTrackScale.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg12"
+ sodipodi:docname="KeyTrackScale.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs16" />
+ <sodipodi:namedview
+ id="namedview14"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="42.2"
+ inkscape:cx="1.9075829"
+ inkscape:cy="5.8175355"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg12" />
+ <ellipse
+ style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+ id="path921"
+ cx="-0.88986981"
+ cy="6.0959954"
+ rx="1.2495773"
+ ry="1.0867468" />
+ <path
+ d="M 5.5742494,0.94786723 A 0.58774585,0.58774585 0 0 0 4.9865036,1.535613 0.58774585,0.58774585 0 0 0 5.5742494,2.1233589 h 1.519852 L 6.334146,2.8833142 7.1652774,3.7144456 7.9252328,2.9544903 V 4.4743422 A 0.58774585,0.58774585 0 0 0 8.5129787,5.0620881 0.58774585,0.58774585 0 0 0 9.1007245,4.4743422 V 1.535613 A 0.58780462,0.58780462 0 0 0 8.5129787,0.94786723 Z M 4.9865036,3.8865964 A 1.1754917,1.1754917 0 0 0 3.8110119,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,6.2375798 1.1754917,1.1754917 0 0 0 6.1619953,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,3.8865964 Z M 1.4600285,5.0620881 A 0.58774585,0.58774585 0 0 0 0.8722826,5.6498339 V 8.5885638 A 0.58780462,0.58780462 0 0 0 1.4600285,9.1763092 H 4.3987577 A 0.58774585,0.58774585 0 0 0 4.9865036,8.5885638 0.58774585,0.58774585 0 0 0 4.3987577,8.0008173 H 2.8789057 L 3.6388611,7.2408619 2.8077297,6.4097305 2.0477743,7.1696859 V 5.6498339 A 0.58774585,0.58774585 0 0 0 1.4600285,5.0620881 Z"
+ fill="#e0e0e0"
+ fill-opacity="0.99608"
+ id="path7"
+ style="fill:#eac840;fill-opacity:1;stroke-width:0.587745" />
+</svg>
diff --git a/editor/icons/KeyXPosition.svg b/editor/icons/KeyXPosition.svg
new file mode 100644
index 0000000000..5816a241c9
--- /dev/null
+++ b/editor/icons/KeyXPosition.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg1678"
+ sodipodi:docname="KeyXPosition.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1682" />
+ <sodipodi:namedview
+ id="namedview1680"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="84.4"
+ inkscape:cx="4.9940758"
+ inkscape:cy="5.0059242"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1678" />
+ <rect
+ fill="#ea7940"
+ height="6.1027"
+ ry=".76286"
+ transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)"
+ width="6.1027"
+ x="-740.13947"
+ y="741.10779"
+ id="rect1676" />
+</svg>
diff --git a/editor/icons/KeyXRotation.svg b/editor/icons/KeyXRotation.svg
new file mode 100644
index 0000000000..6cd5a67ac0
--- /dev/null
+++ b/editor/icons/KeyXRotation.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg1678"
+ sodipodi:docname="KeyXRotation.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1682" />
+ <sodipodi:namedview
+ id="namedview1680"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="84.4"
+ inkscape:cx="0.32582938"
+ inkscape:cy="5.0059242"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1678" />
+ <rect
+ fill="#ea7940"
+ height="6.1027002"
+ ry="0.76286"
+ transform="rotate(-45,-1258.2881,-521.2)"
+ width="6.1030002"
+ x="-740.13947"
+ y="741.10779"
+ id="rect1676"
+ style="fill:#ee3c94;fill-opacity:1" />
+</svg>
diff --git a/editor/icons/KeyXScale.svg b/editor/icons/KeyXScale.svg
new file mode 100644
index 0000000000..588fa5d2f3
--- /dev/null
+++ b/editor/icons/KeyXScale.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg1678"
+ sodipodi:docname="KeyXScale.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1682" />
+ <sodipodi:namedview
+ id="namedview1680"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="84.4"
+ inkscape:cx="2.6658768"
+ inkscape:cy="5.0059242"
+ inkscape:window-width="1473"
+ inkscape:window-height="752"
+ inkscape:window-x="122"
+ inkscape:window-y="114"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg1678" />
+ <rect
+ fill="#ea7940"
+ height="6.1027002"
+ ry="0.76286"
+ transform="rotate(-45,-1258.2881,-521.2)"
+ width="6.1030002"
+ x="-740.13947"
+ y="741.10779"
+ id="rect1676"
+ style="fill:#dbbf4f;fill-opacity:1" />
+</svg>
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 4b01595028..ac8adedf2f 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -91,8 +91,8 @@ struct ColladaImport {
Error _create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ImporterMesh>> p_morph_meshes = Vector<Ref<ImporterMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false);
Error load(const String &p_path, int p_flags, bool p_force_make_tangents = false, bool p_use_compression = false);
void _fix_param_animation_tracks();
- void create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
- void create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
+ void create_animation(int p_clip, bool p_import_value_tracks);
+ void create_animations(bool p_import_value_tracks);
Set<String> tracks_in_clips;
Vector<String> missing_textures;
@@ -120,6 +120,15 @@ Error ColladaImport::_populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p
skeleton_bone_map[p_skeleton][joint->sid] = r_bone;
+ {
+ Transform3D xform = joint->compute_transform(collada);
+ xform = collada.fix_transform(xform) * joint->post_transform;
+
+ p_skeleton->set_bone_pose_position(r_bone, xform.origin);
+ p_skeleton->set_bone_pose_rotation(r_bone, xform.basis.get_rotation_quaternion());
+ p_skeleton->set_bone_pose_scale(r_bone, xform.basis.get_scale());
+ }
+
if (collada.state.bone_rest_map.has(joint->sid)) {
p_skeleton->set_bone_rest(r_bone, collada.fix_transform(collada.state.bone_rest_map[joint->sid]));
//should map this bone to something for animation?
@@ -1384,7 +1393,7 @@ void ColladaImport::_fix_param_animation_tracks() {
}
}
-void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
+void ColladaImport::create_animations(bool p_import_value_tracks) {
_fix_param_animation_tracks();
for (int i = 0; i < collada.state.animation_clips.size(); i++) {
for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) {
@@ -1417,13 +1426,13 @@ void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_im
}
}
- create_animation(-1, p_make_tracks_in_all_bones, p_import_value_tracks);
+ create_animation(-1, p_import_value_tracks);
for (int i = 0; i < collada.state.animation_clips.size(); i++) {
- create_animation(i, p_make_tracks_in_all_bones, p_import_value_tracks);
+ create_animation(i, p_import_value_tracks);
}
}
-void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
+void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
Ref<Animation> animation = Ref<Animation>(memnew(Animation));
if (p_clip == -1) {
@@ -1522,10 +1531,55 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
continue;
}
- animation->add_track(Animation::TYPE_TRANSFORM3D);
- int track = animation->get_track_count() - 1;
- animation->track_set_path(track, path);
- animation->track_set_imported(track, true); //helps merging later
+ bool has_position = false;
+ bool has_rotation = false;
+ bool has_scale = false;
+
+ for (int i = 0; cn->xform_list.size(); i++) {
+ switch (cn->xform_list[i].op) {
+ case Collada::Node::XForm::OP_ROTATE: {
+ has_rotation = true;
+ } break;
+ case Collada::Node::XForm::OP_SCALE: {
+ has_scale = true;
+ } break;
+ case Collada::Node::XForm::OP_TRANSLATE: {
+ has_position = true;
+ } break;
+ case Collada::Node::XForm::OP_MATRIX: {
+ has_position = true;
+ has_rotation = true;
+ has_scale = true;
+ } break;
+ case Collada::Node::XForm::OP_VISIBILITY: {
+ } break;
+ }
+ }
+
+ int base_track = animation->get_track_count();
+ int position_idx = -1;
+ if (has_position) {
+ position_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_POSITION_3D);
+ animation->track_set_path(position_idx, path);
+ animation->track_set_imported(position_idx, true); //helps merging later
+ }
+
+ int rotation_idx = -1;
+ if (has_rotation) {
+ rotation_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_ROTATION_3D);
+ animation->track_set_path(rotation_idx, path);
+ animation->track_set_imported(rotation_idx, true); //helps merging later
+ }
+
+ int scale_idx = -1;
+ if (has_scale) {
+ scale_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_SCALE_3D);
+ animation->track_set_path(scale_idx, path);
+ animation->track_set_imported(scale_idx, true); //helps merging later
+ }
Vector<real_t> snapshots = base_snapshots;
@@ -1594,22 +1648,21 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
Transform3D xform = cn->compute_transform(collada);
xform = collada.fix_transform(xform) * cn->post_transform;
- if (nm.bone >= 0) {
- //make bone transform relative to rest (in case of skeleton)
- Skeleton3D *sk = Object::cast_to<Skeleton3D>(nm.node);
- if (sk) {
- xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
- } else {
- ERR_PRINT("Collada: Invalid skeleton");
- }
- }
-
Vector3 s = xform.basis.get_scale();
bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
- Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
+ Quaternion q = singular_matrix ? Quaternion() :
+ xform.basis.get_rotation_quaternion();
Vector3 l = xform.origin;
- animation->transform_track_insert_key(track, snapshots[i], l, q, s);
+ if (position_idx >= 0) {
+ animation->position_track_insert_key(position_idx, snapshots[i], l);
+ }
+ if (rotation_idx >= 0) {
+ animation->rotation_track_insert_key(rotation_idx, snapshots[i], q);
+ }
+ if (scale_idx >= 0) {
+ animation->scale_track_insert_key(scale_idx, snapshots[i], s);
+ }
}
if (nm.bone >= 0) {
@@ -1621,48 +1674,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
if (found_anim) {
tracks_found = true;
} else {
- animation->remove_track(track);
- }
- }
-
- if (p_make_tracks_in_all_bones) {
- //some bones may lack animation, but since we don't store pose as a property, we must add keyframes!
- for (const KeyValue<String, bool> &E : bones_with_animation) {
- if (E.value) {
- continue;
+ if (position_idx >= 0) {
+ animation->remove_track(base_track);
}
-
- NodeMap &nm = node_map[E.key];
- String path = scene->get_path_to(nm.node);
- ERR_CONTINUE(nm.bone < 0);
- Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node);
- String name = sk->get_bone_name(nm.bone);
- path = path + ":" + name;
-
- Collada::Node *cn = collada.state.scene_map[E.key];
- if (cn->ignore_anim) {
- WARN_PRINT("Collada: Ignoring animation on node: " + path);
- continue;
+ if (rotation_idx >= 0) {
+ animation->remove_track(base_track);
+ }
+ if (scale_idx >= 0) {
+ animation->remove_track(base_track);
}
-
- animation->add_track(Animation::TYPE_TRANSFORM3D);
- int track = animation->get_track_count() - 1;
- animation->track_set_path(track, path);
- animation->track_set_imported(track, true); //helps merging later
-
- Transform3D xform = cn->compute_transform(collada);
- xform = collada.fix_transform(xform) * cn->post_transform;
-
- xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
-
- Vector3 s = xform.basis.get_scale();
- bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
- Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
- Vector3 l = xform.origin;
-
- animation->transform_track_insert_key(track, 0, l, q, s);
-
- tracks_found = true;
}
}
@@ -1690,7 +1710,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
NodeMap &nm = node_map[at.target];
String path = scene->get_path_to(nm.node);
- animation->add_track(Animation::TYPE_VALUE);
+ animation->add_track(Animation::TYPE_BLEND_SHAPE);
int track = animation->get_track_count() - 1;
path = path + ":" + at.param;
@@ -1712,7 +1732,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
WARN_PRINT("Collada: Unexpected amount of value keys: " + itos(data.size()));
}
- animation->track_insert_key(track, time, value);
+ animation->blend_shape_track_insert_key(track, time, value);
}
tracks_found = true;
@@ -1728,15 +1748,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
/*************************************** SCENE ***********************************/
/*********************************************************************************/
-uint32_t EditorSceneImporterCollada::get_import_flags() const {
+uint32_t EditorSceneFormatImporterCollada::get_import_flags() const {
return IMPORT_SCENE | IMPORT_ANIMATION;
}
-void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) const {
+void EditorSceneFormatImporterCollada::get_extensions(List<String> *r_extensions) const {
r_extensions->push_back("dae");
}
-Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
+Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
if (r_err) {
*r_err = OK;
}
@@ -1749,7 +1769,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
state.use_mesh_builtin_materials = true;
state.bake_fps = p_bake_fps;
- Error err = state.load(p_path, flags, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, false);
+ Error err = state.load(p_path, flags, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, false);
if (r_err) {
*r_err = err;
@@ -1773,7 +1793,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
}
if (p_flags & IMPORT_ANIMATION) {
- state.create_animations(true, true);
+ state.create_animations(true);
AnimationPlayer *ap = memnew(AnimationPlayer);
for (int i = 0; i < state.animations.size(); i++) {
String name;
@@ -1792,15 +1812,15 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
return state.scene;
}
-Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
+Ref<Animation> EditorSceneFormatImporterCollada::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
ColladaImport state;
state.use_mesh_builtin_materials = false;
- Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
+ Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
- state.create_animations(true, true);
+ state.create_animations(true);
if (state.scene) {
memdelete(state.scene);
}
@@ -1813,5 +1833,5 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
return anim;
}
-EditorSceneImporterCollada::EditorSceneImporterCollada() {
+EditorSceneFormatImporterCollada::EditorSceneFormatImporterCollada() {
}
diff --git a/editor/import/editor_import_collada.h b/editor/import/editor_import_collada.h
index bf45322765..055a6fe178 100644
--- a/editor/import/editor_import_collada.h
+++ b/editor/import/editor_import_collada.h
@@ -33,8 +33,8 @@
#include "editor/import/resource_importer_scene.h"
-class EditorSceneImporterCollada : public EditorSceneImporter {
- GDCLASS(EditorSceneImporterCollada, EditorSceneImporter);
+class EditorSceneFormatImporterCollada : public EditorSceneFormatImporter {
+ GDCLASS(EditorSceneFormatImporterCollada, EditorSceneFormatImporter);
public:
virtual uint32_t get_import_flags() const override;
@@ -42,7 +42,7 @@ public:
virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr) override;
virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) override;
- EditorSceneImporterCollada();
+ EditorSceneFormatImporterCollada();
};
#endif
diff --git a/editor/import/editor_importer_bake_reset.cpp b/editor/import/editor_importer_bake_reset.cpp
deleted file mode 100644
index 451a07351c..0000000000
--- a/editor/import/editor_importer_bake_reset.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/*************************************************************************/
-/* 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_list.h"
-#include "core/error/error_macros.h"
-#include "core/math/transform_3d.h"
-#include "resource_importer_scene.h"
-#include "scene/3d/importer_mesh_instance_3d.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();
- ImporterMeshInstance3D *editor_mesh_3d = scene->cast_to<ImporterMeshInstance3D>(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 (const KeyValue<StringName, BakeResetRestBone> &rest_bone_i : r_rest_bones) {
- 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);
- const BakeResetRestBone rest_bone = rest_bone_i.value;
- 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) {
- if (!p_ap) {
- return;
- }
- List<StringName> anim_names;
- p_ap->get_animation_list(&anim_names);
- Node *root = p_ap->get_owner();
- ERR_FAIL_NULL(root);
- if (!p_ap->has_animation(p_bake_anim)) {
- return;
- }
- Ref<Animation> a = p_ap->get_animation(p_bake_anim);
- if (a.is_null()) {
- return;
- }
- 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);
- if (err != OK) {
- ERR_PRINT_ONCE("Reset animation baker can't get key.");
- continue;
- }
- 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
deleted file mode 100644
index e36ae86181..0000000000
--- a/editor/import/editor_importer_bake_reset.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*************************************************************************/
-/* 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_obj.h b/editor/import/resource_importer_obj.h
index 1bb5ef33ce..d9f2f79903 100644
--- a/editor/import/resource_importer_obj.h
+++ b/editor/import/resource_importer_obj.h
@@ -33,8 +33,8 @@
#include "resource_importer_scene.h"
-class EditorOBJImporter : public EditorSceneImporter {
- GDCLASS(EditorOBJImporter, EditorSceneImporter);
+class EditorOBJImporter : public EditorSceneFormatImporter {
+ GDCLASS(EditorOBJImporter, EditorSceneFormatImporter);
public:
virtual uint32_t get_import_flags() const override;
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 1e93113488..319e5ee25f 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -32,7 +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 "scene/3d/area_3d.h"
#include "scene/3d/collision_shape_3d.h"
@@ -52,7 +52,7 @@
#include "scene/resources/surface_tool.h"
#include "scene/resources/world_boundary_shape_3d.h"
-uint32_t EditorSceneImporter::get_import_flags() const {
+uint32_t EditorSceneFormatImporter::get_import_flags() const {
int ret;
if (GDVIRTUAL_CALL(_get_import_flags, ret)) {
return ret;
@@ -61,7 +61,7 @@ uint32_t EditorSceneImporter::get_import_flags() const {
ERR_FAIL_V(0);
}
-void EditorSceneImporter::get_extensions(List<String> *r_extensions) const {
+void EditorSceneFormatImporter::get_extensions(List<String> *r_extensions) const {
Vector<String> arr;
if (GDVIRTUAL_CALL(_get_extensions, arr)) {
for (int i = 0; i < arr.size(); i++) {
@@ -73,7 +73,7 @@ void EditorSceneImporter::get_extensions(List<String> *r_extensions) const {
ERR_FAIL();
}
-Node *EditorSceneImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
+Node *EditorSceneFormatImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
Object *ret;
if (GDVIRTUAL_CALL(_import_scene, p_path, p_flags, p_bake_fps, ret)) {
return Object::cast_to<Node>(ret);
@@ -82,7 +82,7 @@ Node *EditorSceneImporter::import_scene(const String &p_path, uint32_t p_flags,
ERR_FAIL_V(nullptr);
}
-Ref<Animation> EditorSceneImporter::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
+Ref<Animation> EditorSceneFormatImporter::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
Ref<Animation> ret;
if (GDVIRTUAL_CALL(_import_animation, p_path, p_flags, p_bake_fps, ret)) {
return ret;
@@ -94,17 +94,17 @@ Ref<Animation> EditorSceneImporter::import_animation(const String &p_path, uint3
//for documenters, these functions are useful when an importer calls an external conversion helper (like, fbx2gltf),
//and you want to load the resulting file
-Node *EditorSceneImporter::import_scene_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) {
+Node *EditorSceneFormatImporter::import_scene_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) {
return ResourceImporterScene::get_singleton()->import_scene_from_other_importer(this, p_path, p_flags, p_bake_fps);
}
-Ref<Animation> EditorSceneImporter::import_animation_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) {
+Ref<Animation> EditorSceneFormatImporter::import_animation_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) {
return ResourceImporterScene::get_singleton()->import_animation_from_other_importer(this, p_path, p_flags, p_bake_fps);
}
-void EditorSceneImporter::_bind_methods() {
- ClassDB::bind_method(D_METHOD("import_scene_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneImporter::import_scene_from_other_importer);
- ClassDB::bind_method(D_METHOD("import_animation_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneImporter::import_animation_from_other_importer);
+void EditorSceneFormatImporter::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("import_scene_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneFormatImporter::import_scene_from_other_importer);
+ ClassDB::bind_method(D_METHOD("import_animation_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneFormatImporter::import_animation_from_other_importer);
GDVIRTUAL_BIND(_get_import_flags);
GDVIRTUAL_BIND(_get_extensions);
@@ -144,6 +144,105 @@ void EditorScenePostImport::init(const String &p_source_file) {
EditorScenePostImport::EditorScenePostImport() {
}
+///////////////////////////////////////////////////////
+
+Variant EditorScenePostImportPlugin::get_option_value(const StringName &p_name) const {
+ ERR_FAIL_COND_V_MSG(current_options == nullptr && current_options_dict == nullptr, Variant(), "get_option_value called from a function where option values are not available.");
+ ERR_FAIL_COND_V_MSG(current_options && !current_options->has(p_name), Variant(), "get_option_value called with unexisting option argument: " + String(p_name));
+ ERR_FAIL_COND_V_MSG(current_options_dict && !current_options_dict->has(p_name), Variant(), "get_option_value called with unexisting option argument: " + String(p_name));
+ if (current_options) {
+ (*current_options)[p_name];
+ }
+ if (current_options_dict) {
+ (*current_options_dict)[p_name];
+ }
+ return Variant();
+}
+void EditorScenePostImportPlugin::add_import_option(const String &p_name, Variant p_default_value) {
+ ERR_FAIL_COND_MSG(current_option_list == nullptr, "add_import_option() can only be called from get_import_options()");
+ add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);
+}
+void EditorScenePostImportPlugin::add_import_option_advanced(Variant::Type p_type, const String &p_name, Variant p_default_value, PropertyHint p_hint, const String &p_hint_string, int p_usage_flags) {
+ ERR_FAIL_COND_MSG(current_option_list == nullptr, "add_import_option_advanced() can only be called from get_import_options()");
+ current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));
+}
+
+void EditorScenePostImportPlugin::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
+ current_option_list = r_options;
+ GDVIRTUAL_CALL(_get_internal_import_options, p_category);
+ current_option_list = nullptr;
+}
+Variant EditorScenePostImportPlugin::get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const {
+ current_options = &p_options;
+ Variant ret;
+ GDVIRTUAL_CALL(_get_internal_option_visibility, p_category, p_option, ret);
+ current_options = nullptr;
+ return ret;
+}
+Variant EditorScenePostImportPlugin::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const {
+ current_options = &p_options;
+ Variant ret;
+ GDVIRTUAL_CALL(_get_internal_option_update_view_required, p_category, p_option, ret);
+ current_options = nullptr;
+ return ret;
+}
+
+void EditorScenePostImportPlugin::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, RES p_resource, const Dictionary &p_options) {
+ current_options_dict = &p_options;
+ GDVIRTUAL_CALL(_internal_process, p_category, p_base_scene, p_node, p_resource);
+ current_options_dict = nullptr;
+}
+
+void EditorScenePostImportPlugin::get_import_options(List<ResourceImporter::ImportOption> *r_options) {
+ current_option_list = r_options;
+ GDVIRTUAL_CALL(_get_import_options);
+ current_option_list = nullptr;
+}
+Variant EditorScenePostImportPlugin::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ current_options = &p_options;
+ Variant ret;
+ GDVIRTUAL_CALL(_get_option_visibility, p_option, ret);
+ current_options = nullptr;
+ return ret;
+}
+
+void EditorScenePostImportPlugin::pre_process(Node *p_scene, const Map<StringName, Variant> &p_options) {
+ current_options = &p_options;
+ GDVIRTUAL_CALL(_pre_process, p_scene);
+ current_options = nullptr;
+}
+void EditorScenePostImportPlugin::post_process(Node *p_scene, const Map<StringName, Variant> &p_options) {
+ current_options = &p_options;
+ GDVIRTUAL_CALL(_post_process, p_scene);
+ current_options = nullptr;
+}
+
+void EditorScenePostImportPlugin::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_option_value", "name"), &EditorScenePostImportPlugin::get_option_value);
+
+ ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorScenePostImportPlugin::add_import_option);
+ ClassDB::bind_method(D_METHOD("add_import_option_advanced", "type", "name", "default_value", "hint", "hint_string", "usage_flags"), &EditorScenePostImportPlugin::add_import_option_advanced, DEFVAL(PROPERTY_HINT_NONE), DEFVAL(""), DEFVAL(PROPERTY_USAGE_DEFAULT));
+
+ GDVIRTUAL_BIND(_get_internal_import_options, "category");
+ GDVIRTUAL_BIND(_get_internal_option_visibility, "category", "option");
+ GDVIRTUAL_BIND(_get_internal_option_update_view_required, "category", "option");
+ GDVIRTUAL_BIND(_internal_process, "category", "base_node", "node", "resource");
+ GDVIRTUAL_BIND(_get_import_options);
+ GDVIRTUAL_BIND(_get_option_visibility, "option");
+ GDVIRTUAL_BIND(_pre_process, "scene");
+ GDVIRTUAL_BIND(_post_process, "scene");
+
+ BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_NODE);
+ BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE);
+ BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MESH);
+ BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MATERIAL);
+ BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION);
+ BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE);
+ BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MAX);
+}
+
+/////////////////////////////////////////////////////////
+
String ResourceImporterScene::get_importer_name() const {
return "scene";
}
@@ -153,7 +252,7 @@ String ResourceImporterScene::get_visible_name() const {
}
void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions) const {
- for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) {
+ for (Set<Ref<EditorSceneFormatImporter>>::Element *E = importers.front(); E; E = E->next()) {
E->get()->get_extensions(p_extensions);
}
}
@@ -181,6 +280,13 @@ bool ResourceImporterScene::get_option_visibility(const String &p_option, const
return false;
}
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_option, p_options);
+ if (ret.get_type() == Variant::BOOL) {
+ return ret;
+ }
+ }
+
return true;
}
@@ -547,6 +653,26 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
return nullptr;
}
+ {
+ ObjectID node_id = p_node->get_instance_id();
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_NODE, p_root, p_node, RES(), node_settings);
+ if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue
+ break;
+ }
+ }
+ }
+
+ if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
+ ObjectID node_id = p_node->get_instance_id();
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, p_root, p_node, RES(), node_settings);
+ if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue
+ break;
+ }
+ }
+ }
+
if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
@@ -566,6 +692,11 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
if (mat_id != String() && p_material_data.has(mat_id)) {
Dictionary matdata = p_material_data[mat_id];
+
+ for (int j = 0; j < post_importer_plugins.size(); j++) {
+ post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata);
+ }
+
if (matdata.has("use_external/enabled") && bool(matdata["use_external/enabled"]) && matdata.has("use_external/path")) {
String path = matdata["use_external/path"];
Ref<Material> external_mat = ResourceLoader::load(path);
@@ -715,6 +846,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
}
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, p_root, p_node, RES(), node_settings);
+ }
+
bool use_optimizer = node_settings["optimizer/enabled"];
float anim_optimizer_linerr = node_settings["optimizer/max_linear_error"];
float anim_optimizer_angerr = node_settings["optimizer/max_angular_error"];
@@ -779,6 +914,41 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
}
}
+
+ AnimationImportTracks import_tracks_mode[TRACK_CHANNEL_MAX] = {
+ AnimationImportTracks(int(node_settings["import_tracks/position"])),
+ AnimationImportTracks(int(node_settings["import_tracks/rotation"])),
+ AnimationImportTracks(int(node_settings["import_tracks/scale"]))
+ };
+
+ if (anims.size() > 1 && (import_tracks_mode[0] != ANIMATION_IMPORT_TRACKS_IF_PRESENT || import_tracks_mode[1] != ANIMATION_IMPORT_TRACKS_IF_PRESENT || import_tracks_mode[2] != ANIMATION_IMPORT_TRACKS_IF_PRESENT)) {
+ _optimize_track_usage(ap, import_tracks_mode);
+ }
+ }
+
+ if (post_importer_plugins.size()) {
+ List<StringName> anims;
+ ap->get_animation_list(&anims);
+ for (const StringName &name : anims) {
+ if (p_animation_data.has(name)) {
+ Ref<Animation> anim = ap->get_animation(name);
+ Dictionary anim_settings = p_animation_data[name];
+ {
+ //fill with default values
+ List<ImportOption> iopts;
+ get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);
+ for (const ImportOption &F : iopts) {
+ if (!anim_settings.has(F.option.name)) {
+ anim_settings[F.option.name] = F.default_value;
+ }
+ }
+ }
+
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION, p_root, p_node, anim, node_settings);
+ }
+ }
+ }
}
}
@@ -851,44 +1021,71 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
if (kt > (from + 0.01) && k > 0) {
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
- Quaternion q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Vector3 p;
+ default_anim->position_track_interpolate(j, from, &p);
+ new_anim->position_track_insert_key(dtrack, 0, p);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+ Quaternion r;
+ default_anim->rotation_track_interpolate(j, from, &r);
+ new_anim->rotation_track_insert_key(dtrack, 0, r);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
Vector3 s;
- default_anim->transform_track_interpolate(j, from, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
- }
- if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+ default_anim->scale_track_interpolate(j, from, &s);
+ new_anim->scale_track_insert_key(dtrack, 0, s);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, from);
new_anim->track_insert_key(dtrack, 0, var);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
+ float interp;
+ default_anim->blend_shape_track_interpolate(j, from, &interp);
+ new_anim->blend_shape_track_insert_key(dtrack, 0, interp);
}
}
}
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
- Quaternion q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Vector3 p;
+ default_anim->position_track_get_key(j, k, &p);
+ new_anim->position_track_insert_key(dtrack, kt - from, p);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+ Quaternion r;
+ default_anim->rotation_track_get_key(j, k, &r);
+ new_anim->rotation_track_insert_key(dtrack, kt - from, r);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
Vector3 s;
- default_anim->transform_track_get_key(j, k, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, kt - from, p, q, s);
- }
- if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+ default_anim->scale_track_get_key(j, k, &s);
+ new_anim->scale_track_insert_key(dtrack, kt - from, s);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->track_get_key_value(j, k);
new_anim->track_insert_key(dtrack, kt - from, var);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
+ float interp;
+ default_anim->blend_shape_track_get_key(j, k, &interp);
+ new_anim->blend_shape_track_insert_key(dtrack, kt - from, interp);
}
}
if (dtrack != -1 && kt >= to) {
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
- Quaternion q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Vector3 p;
+ default_anim->position_track_interpolate(j, to, &p);
+ new_anim->position_track_insert_key(dtrack, to - from, p);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+ Quaternion r;
+ default_anim->rotation_track_interpolate(j, to, &r);
+ new_anim->rotation_track_insert_key(dtrack, to - from, r);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
Vector3 s;
- default_anim->transform_track_interpolate(j, to, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
- }
- if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+ default_anim->scale_track_interpolate(j, to, &s);
+ new_anim->scale_track_insert_key(dtrack, to - from, s);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, to);
new_anim->track_insert_key(dtrack, to - from, var);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
+ float interp;
+ default_anim->blend_shape_track_interpolate(j, to, &interp);
+ new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);
}
}
}
@@ -897,20 +1094,35 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
new_anim->add_track(default_anim->track_get_type(j));
dtrack = new_anim->get_track_count() - 1;
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
- Quaternion q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Vector3 p;
+ default_anim->position_track_interpolate(j, from, &p);
+ new_anim->position_track_insert_key(dtrack, 0, p);
+ default_anim->position_track_interpolate(j, to, &p);
+ new_anim->position_track_insert_key(dtrack, to - from, p);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+ Quaternion r;
+ default_anim->rotation_track_interpolate(j, from, &r);
+ new_anim->rotation_track_insert_key(dtrack, 0, r);
+ default_anim->rotation_track_interpolate(j, to, &r);
+ new_anim->rotation_track_insert_key(dtrack, to - from, r);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
Vector3 s;
- default_anim->transform_track_interpolate(j, from, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
- default_anim->transform_track_interpolate(j, to, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
- }
- if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+ default_anim->scale_track_interpolate(j, from, &s);
+ new_anim->scale_track_insert_key(dtrack, 0, s);
+ default_anim->scale_track_interpolate(j, to, &s);
+ new_anim->scale_track_insert_key(dtrack, to - from, s);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, from);
new_anim->track_insert_key(dtrack, 0, var);
Variant to_var = default_anim->value_track_interpolate(j, to);
new_anim->track_insert_key(dtrack, to - from, to_var);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
+ float interp;
+ default_anim->blend_shape_track_interpolate(j, from, &interp);
+ new_anim->blend_shape_track_insert_key(dtrack, 0, interp);
+ default_anim->blend_shape_track_interpolate(j, to, &interp);
+ new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);
}
}
}
@@ -1000,6 +1212,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
for (int i = 0; i < 256; i++) {
@@ -1015,6 +1230,10 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
default: {
}
}
+
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->get_internal_import_options(EditorScenePostImportPlugin::InternalImportCategory(p_category), r_options);
+ }
}
bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const {
@@ -1117,6 +1336,13 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
}
}
+ 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), p_option, p_options);
+ if (ret.get_type() == Variant::BOOL) {
+ return ret;
+ }
+ }
+
return true;
}
@@ -1144,6 +1370,14 @@ bool ResourceImporterScene::get_internal_option_update_view_required(InternalImp
default: {
}
}
+
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ Variant ret = post_importer_plugins.write[i]->get_internal_option_update_view_required(EditorScenePostImportPlugin::InternalImportCategory(p_category), p_option, p_options);
+ if (ret.get_type() == Variant::BOOL) {
+ return ret;
+ }
+ }
+
return false;
}
@@ -1171,11 +1405,14 @@ 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), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), Dictionary()));
+
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->get_import_options(r_options);
+ }
}
void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner) {
@@ -1189,11 +1426,11 @@ void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_
}
}
-Node *ResourceImporterScene::import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps) {
- Ref<EditorSceneImporter> importer;
+Node *ResourceImporterScene::import_scene_from_other_importer(EditorSceneFormatImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps) {
+ Ref<EditorSceneFormatImporter> importer;
String ext = p_path.get_extension().to_lower();
- for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) {
+ for (Set<Ref<EditorSceneFormatImporter>>::Element *E = importers.front(); E; E = E->next()) {
if (E->get().ptr() == p_exception) {
continue;
}
@@ -1219,11 +1456,11 @@ Node *ResourceImporterScene::import_scene_from_other_importer(EditorSceneImporte
return importer->import_scene(p_path, p_flags, p_bake_fps, &missing, &err);
}
-Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps) {
- Ref<EditorSceneImporter> importer;
+Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(EditorSceneFormatImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps) {
+ Ref<EditorSceneFormatImporter> importer;
String ext = p_path.get_extension().to_lower();
- for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) {
+ for (Set<Ref<EditorSceneFormatImporter>>::Element *E = importers.front(); E; E = E->next()) {
if (E->get().ptr() == p_exception) {
continue;
}
@@ -1320,6 +1557,10 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
save_to_file = "";
}
}
+
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH, nullptr, src_mesh_node, src_mesh_node->get_mesh(), mesh_settings);
+ }
}
if (generate_lods) {
@@ -1422,14 +1663,164 @@ void ResourceImporterScene::_add_shapes(Node *p_node, const Vector<Ref<Shape3D>>
}
}
+void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions) {
+ List<StringName> anims;
+ p_player->get_animation_list(&anims);
+ Node *parent = p_player->get_parent();
+ ERR_FAIL_COND(parent == nullptr);
+ OrderedHashMap<NodePath, uint32_t> used_tracks[TRACK_CHANNEL_MAX];
+ bool tracks_to_add = false;
+ static const Animation::TrackType track_types[TRACK_CHANNEL_MAX] = { Animation::TYPE_POSITION_3D, Animation::TYPE_ROTATION_3D, Animation::TYPE_SCALE_3D, Animation::TYPE_BLEND_SHAPE };
+ for (const StringName &I : anims) {
+ Ref<Animation> anim = p_player->get_animation(I);
+ for (int i = 0; i < anim->get_track_count(); i++) {
+ for (int j = 0; j < TRACK_CHANNEL_MAX; j++) {
+ if (anim->track_get_type(i) != track_types[j]) {
+ continue;
+ }
+ switch (p_track_actions[j]) {
+ case ANIMATION_IMPORT_TRACKS_IF_PRESENT: {
+ // Do Nothing.
+ } break;
+ case ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL: {
+ used_tracks[j].insert(anim->track_get_path(i), 0);
+ tracks_to_add = true;
+ } break;
+ case ANIMATION_IMPORT_TRACKS_NEVER: {
+ anim->remove_track(i);
+ i--;
+ } break;
+ }
+ }
+ }
+ }
+
+ if (!tracks_to_add) {
+ return;
+ }
+
+ uint32_t pass = 0;
+ for (const StringName &I : anims) {
+ Ref<Animation> anim = p_player->get_animation(I);
+ for (int j = 0; j < TRACK_CHANNEL_MAX; j++) {
+ if (p_track_actions[j] != ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL) {
+ continue;
+ }
+
+ pass++;
+
+ for (int i = 0; i < anim->get_track_count(); i++) {
+ if (anim->track_get_type(i) != track_types[j]) {
+ continue;
+ }
+
+ NodePath path = anim->track_get_path(i);
+
+ ERR_CONTINUE(!used_tracks[j].has(path)); // Should never happen.
+
+ used_tracks[j][path] = pass;
+ }
+
+ for (OrderedHashMap<NodePath, uint32_t>::Element J = used_tracks[j].front(); J; J = J.next()) {
+ if (J.get() == pass) {
+ continue;
+ }
+
+ NodePath path = J.key();
+ Node *n = parent->get_node(path);
+
+ if (j == TRACK_CHANNEL_BLEND_SHAPE) {
+ MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(n);
+ if (mi && path.get_subname_count() > 0) {
+ StringName bs = path.get_subname(0);
+ bool valid;
+ float value = mi->get(bs, &valid);
+ if (valid) {
+ int track_idx = anim->add_track(track_types[j]);
+ anim->track_set_path(track_idx, path);
+ anim->track_set_imported(track_idx, true);
+ anim->blend_shape_track_insert_key(track_idx, 0, value);
+ }
+ }
+
+ } else {
+ Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);
+ Node3D *n3d = Object::cast_to<Node3D>(n);
+ Vector3 loc;
+ Quaternion rot;
+ Vector3 scale;
+ if (skel && path.get_subname_count() > 0) {
+ StringName bone = path.get_subname(0);
+ int bone_idx = skel->find_bone(bone);
+ if (bone_idx == -1) {
+ continue;
+ }
+ skel->get_bone_pose(bone_idx);
+ loc = skel->get_bone_pose_position(bone_idx);
+ rot = skel->get_bone_pose_rotation(bone_idx);
+ scale = skel->get_bone_pose_scale(bone_idx);
+ } else if (n3d) {
+ loc = n3d->get_position();
+ rot = n3d->get_transform().basis.get_rotation_quaternion();
+ scale = n3d->get_scale();
+ } else {
+ continue;
+ }
+
+ // Ensure insertion keeps tracks together and ordered by type (loc/rot/scale)
+ int insert_at_pos = -1;
+ for (int k = 0; k < anim->get_track_count(); k++) {
+ NodePath tpath = anim->track_get_path(k);
+
+ if (path == tpath) {
+ Animation::TrackType ttype = anim->track_get_type(k);
+ if (insert_at_pos == -1) {
+ // First insert, determine whether replacing or kicking back
+ if (track_types[j] < ttype) {
+ insert_at_pos = k;
+ break; // No point in continuing.
+ } else {
+ insert_at_pos = k + 1;
+ }
+ } else if (ttype < track_types[j]) {
+ // Kick back.
+ insert_at_pos = k + 1;
+ }
+ } else if (insert_at_pos >= 0) {
+ break;
+ }
+ }
+ int track_idx = anim->add_track(track_types[j], insert_at_pos);
+
+ anim->track_set_path(track_idx, path);
+ anim->track_set_imported(track_idx, true);
+ switch (j) {
+ case TRACK_CHANNEL_POSITION: {
+ anim->position_track_insert_key(track_idx, 0, loc);
+ } break;
+ case TRACK_CHANNEL_ROTATION: {
+ anim->rotation_track_insert_key(track_idx, 0, rot);
+ } break;
+ case TRACK_CHANNEL_SCALE: {
+ anim->scale_track_insert_key(track_idx, 0, scale);
+ } break;
+ default: {
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
Node *ResourceImporterScene::pre_import(const String &p_source_file) {
- Ref<EditorSceneImporter> importer;
+ Ref<EditorSceneFormatImporter> importer;
String ext = p_source_file.get_extension().to_lower();
EditorProgress progress("pre-import", TTR("Pre-Import Scene"), 0);
progress.step(TTR("Importing Scene..."), 0);
- for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) {
+ for (Set<Ref<EditorSceneFormatImporter>>::Element *E = importers.front(); E; E = E->next()) {
List<String> extensions;
E->get()->get_extensions(&extensions);
@@ -1448,7 +1839,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
ERR_FAIL_COND_V(!importer.is_valid(), nullptr);
Error err = OK;
- Node *scene = importer->import_scene(p_source_file, EditorSceneImporter::IMPORT_ANIMATION | EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, 15, nullptr, &err);
+ Node *scene = importer->import_scene(p_source_file, EditorSceneFormatImporter::IMPORT_ANIMATION | EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, 15, nullptr, &err);
if (!scene || err != OK) {
return nullptr;
}
@@ -1463,13 +1854,13 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
Error ResourceImporterScene::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
const String &src_path = p_source_file;
- Ref<EditorSceneImporter> importer;
+ Ref<EditorSceneFormatImporter> importer;
String ext = src_path.get_extension().to_lower();
EditorProgress progress("import", TTR("Import Scene"), 104);
progress.step(TTR("Importing Scene..."), 0);
- for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) {
+ for (Set<Ref<EditorSceneFormatImporter>>::Element *E = importers.front(); E; E = E->next()) {
List<String> extensions;
E->get()->get_extensions(&extensions);
@@ -1492,16 +1883,16 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
int import_flags = 0;
if (bool(p_options["animation/import"])) {
- import_flags |= EditorSceneImporter::IMPORT_ANIMATION;
+ import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;
}
if (bool(p_options["skins/use_named_skins"])) {
- import_flags |= EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS;
+ import_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
}
bool ensure_tangents = p_options["meshes/ensure_tangents"];
if (ensure_tangents) {
- import_flags |= EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS;
+ import_flags |= EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS;
}
Error err = OK;
@@ -1532,13 +1923,13 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
_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");
+
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->pre_process(scene, p_options);
}
+ _post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
+
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.
@@ -1640,6 +2031,10 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
}
}
+ for (int i = 0; i < post_importer_plugins.size(); i++) {
+ post_importer_plugins.write[i]->post_process(scene, p_options);
+ }
+
progress.step(TTR("Saving..."), 104);
Ref<PackedScene> packer = memnew(PackedScene);
@@ -1671,15 +2066,15 @@ ResourceImporterScene::ResourceImporterScene() {
///////////////////////////////////////
-uint32_t EditorSceneImporterESCN::get_import_flags() const {
+uint32_t EditorSceneFormatImporterESCN::get_import_flags() const {
return IMPORT_SCENE;
}
-void EditorSceneImporterESCN::get_extensions(List<String> *r_extensions) const {
+void EditorSceneFormatImporterESCN::get_extensions(List<String> *r_extensions) const {
r_extensions->push_back("escn");
}
-Node *EditorSceneImporterESCN::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
+Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
Error error;
Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error);
ERR_FAIL_COND_V_MSG(!ps.is_valid(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");
@@ -1690,6 +2085,6 @@ Node *EditorSceneImporterESCN::import_scene(const String &p_path, uint32_t p_fla
return scene;
}
-Ref<Animation> EditorSceneImporterESCN::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
+Ref<Animation> EditorSceneFormatImporterESCN::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
ERR_FAIL_V(Ref<Animation>());
}
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 2a67fa9aae..e1e7046be5 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -42,8 +42,8 @@ class Material;
class AnimationPlayer;
class ImporterMesh;
-class EditorSceneImporter : public RefCounted {
- GDCLASS(EditorSceneImporter, RefCounted);
+class EditorSceneFormatImporter : public RefCounted {
+ GDCLASS(EditorSceneFormatImporter, RefCounted);
protected:
static void _bind_methods();
@@ -70,7 +70,7 @@ public:
virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr);
virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps);
- EditorSceneImporter() {}
+ EditorSceneFormatImporter() {}
};
class EditorScenePostImport : public RefCounted {
@@ -90,10 +90,64 @@ public:
EditorScenePostImport();
};
+class EditorScenePostImportPlugin : public RefCounted {
+ GDCLASS(EditorScenePostImportPlugin, RefCounted);
+
+public:
+ enum InternalImportCategory {
+ INTERNAL_IMPORT_CATEGORY_NODE,
+ INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE,
+ INTERNAL_IMPORT_CATEGORY_MESH,
+ INTERNAL_IMPORT_CATEGORY_MATERIAL,
+ INTERNAL_IMPORT_CATEGORY_ANIMATION,
+ INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE,
+ INTERNAL_IMPORT_CATEGORY_MAX
+ };
+
+private:
+ mutable const Map<StringName, Variant> *current_options = nullptr;
+ mutable const Dictionary *current_options_dict = nullptr;
+ List<ResourceImporter::ImportOption> *current_option_list = nullptr;
+ InternalImportCategory current_category = INTERNAL_IMPORT_CATEGORY_MAX;
+
+protected:
+ GDVIRTUAL1(_get_internal_import_options, int)
+ GDVIRTUAL2RC(Variant, _get_internal_option_visibility, int, String)
+ GDVIRTUAL2RC(Variant, _get_internal_option_update_view_required, int, String)
+ GDVIRTUAL4(_internal_process, int, Node *, Node *, RES)
+ GDVIRTUAL0(_get_import_options)
+ GDVIRTUAL1RC(Variant, _get_option_visibility, String)
+ GDVIRTUAL1(_pre_process, Node *)
+ GDVIRTUAL1(_post_process, Node *)
+
+ static void _bind_methods();
+
+public:
+ Variant get_option_value(const StringName &p_name) const;
+ void add_import_option(const String &p_name, Variant p_default_value);
+ void add_import_option_advanced(Variant::Type p_type, const String &p_name, Variant p_default_value, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(), int p_usage_flags = PROPERTY_USAGE_DEFAULT);
+
+ virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options);
+ virtual Variant get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const;
+ virtual Variant get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const;
+
+ virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, RES p_resource, const Dictionary &p_options);
+
+ virtual void get_import_options(List<ResourceImporter::ImportOption> *r_options);
+ virtual Variant get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
+
+ virtual void pre_process(Node *p_scene, const Map<StringName, Variant> &p_options);
+ virtual void post_process(Node *p_scene, const Map<StringName, Variant> &p_options);
+
+ EditorScenePostImportPlugin() {}
+};
+
+VARIANT_ENUM_CAST(EditorScenePostImportPlugin::InternalImportCategory)
+
class ResourceImporterScene : public ResourceImporter {
GDCLASS(ResourceImporterScene, ResourceImporter);
- Set<Ref<EditorSceneImporter>> importers;
+ Set<Ref<EditorSceneFormatImporter>> importers;
static ResourceImporterScene *singleton;
@@ -144,13 +198,33 @@ class ResourceImporterScene : public ResourceImporter {
void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches);
void _add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes);
+ enum AnimationImportTracks {
+ ANIMATION_IMPORT_TRACKS_IF_PRESENT,
+ ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL,
+ ANIMATION_IMPORT_TRACKS_NEVER,
+ };
+ enum TrackChannel {
+ TRACK_CHANNEL_POSITION,
+ TRACK_CHANNEL_ROTATION,
+ TRACK_CHANNEL_SCALE,
+ TRACK_CHANNEL_BLEND_SHAPE,
+ TRACK_CHANNEL_MAX
+ };
+
+ void _optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions);
+
+ mutable Vector<Ref<EditorScenePostImportPlugin>> post_importer_plugins;
+
public:
static ResourceImporterScene *get_singleton() { return singleton; }
- const Set<Ref<EditorSceneImporter>> &get_importers() const { return importers; }
+ void add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) { post_importer_plugins.push_back(p_plugin); }
+ void remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) { post_importer_plugins.erase(p_plugin); }
+
+ const Set<Ref<EditorSceneFormatImporter>> &get_importers() const { return importers; }
- void add_importer(Ref<EditorSceneImporter> p_importer) { importers.insert(p_importer); }
- void remove_importer(Ref<EditorSceneImporter> p_importer) { importers.erase(p_importer); }
+ void add_importer(Ref<EditorSceneFormatImporter> p_importer) { importers.insert(p_importer); }
+ void remove_importer(Ref<EditorSceneFormatImporter> p_importer) { importers.erase(p_importer); }
virtual String get_importer_name() const override;
virtual String get_visible_name() const override;
@@ -163,13 +237,13 @@ public:
virtual String get_preset_name(int p_idx) const override;
enum InternalImportCategory {
- INTERNAL_IMPORT_CATEGORY_NODE,
- INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE,
- INTERNAL_IMPORT_CATEGORY_MESH,
- INTERNAL_IMPORT_CATEGORY_MATERIAL,
- INTERNAL_IMPORT_CATEGORY_ANIMATION,
- INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE,
- INTERNAL_IMPORT_CATEGORY_MAX
+ INTERNAL_IMPORT_CATEGORY_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_NODE,
+ INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE,
+ INTERNAL_IMPORT_CATEGORY_MESH = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH,
+ INTERNAL_IMPORT_CATEGORY_MATERIAL = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL,
+ INTERNAL_IMPORT_CATEGORY_ANIMATION = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION,
+ INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE,
+ INTERNAL_IMPORT_CATEGORY_MAX = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MAX
};
void get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const;
@@ -191,8 +265,8 @@ public:
Node *pre_import(const String &p_source_file);
virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
- Node *import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
- Ref<Animation> import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
+ Node *import_scene_from_other_importer(EditorSceneFormatImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
+ Ref<Animation> import_animation_from_other_importer(EditorSceneFormatImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
virtual bool has_advanced_options() const override;
virtual void show_advanced_options(const String &p_path) override;
@@ -208,8 +282,8 @@ public:
static Transform3D get_collision_shapes_transform(const M &p_options);
};
-class EditorSceneImporterESCN : public EditorSceneImporter {
- GDCLASS(EditorSceneImporterESCN, EditorSceneImporter);
+class EditorSceneFormatImporterESCN : public EditorSceneFormatImporter {
+ GDCLASS(EditorSceneFormatImporterESCN, EditorSceneFormatImporter);
public:
virtual uint32_t get_import_flags() const override;
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index a3ff312497..91ca1465df 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -124,6 +124,10 @@ void PluginConfigDialog::_on_cancelled() {
_clear_fields();
}
+void PluginConfigDialog::_on_language_changed(const int) {
+ _on_required_text_changed(String());
+}
+
void PluginConfigDialog::_on_required_text_changed(const String &) {
int lang_idx = script_option_edit->get_selected();
String ext = ScriptServer::get_language(lang_idx)->get_extension();
@@ -161,6 +165,9 @@ void PluginConfigDialog::_on_required_text_changed(const String &) {
is_valid = false;
subfolder_validation->set_texture(invalid_icon);
subfolder_validation->set_tooltip(TTR("Subfolder cannot be blank."));
+ } else if (!subfolder_edit->get_text().is_valid_filename()) {
+ subfolder_validation->set_texture(invalid_icon);
+ subfolder_validation->set_tooltip(TTR("Subfolder name is not a valid folder name."));
} else {
DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_RESOURCES);
String path = "res://addons/" + subfolder_edit->get_text();
@@ -330,6 +337,7 @@ PluginConfigDialog::PluginConfigDialog() {
}
script_option_edit->select(default_lang);
grid->add_child(script_option_edit);
+ script_option_edit->connect("item_selected", callable_mp(this, &PluginConfigDialog::_on_language_changed));
// Plugin Script Name
Label *script_lb = memnew(Label);
diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h
index ad5b96735f..45fcdb6b6e 100644
--- a/editor/plugin_config_dialog.h
+++ b/editor/plugin_config_dialog.h
@@ -59,6 +59,7 @@ class PluginConfigDialog : public ConfirmationDialog {
void _clear_fields();
void _on_confirmed();
void _on_cancelled();
+ void _on_language_changed(const int p_language);
void _on_required_text_changed(const String &p_text);
static String _to_absolute_plugin_path(const String &p_plugin_name);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index ef872bcead..8935f715e6 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1174,7 +1174,6 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
if (!panning) {
if (b->is_pressed() &&
(b->get_button_index() == MOUSE_BUTTON_MIDDLE ||
- b->get_button_index() == MOUSE_BUTTON_RIGHT ||
(b->get_button_index() == MOUSE_BUTTON_LEFT && tool == TOOL_PAN) ||
(b->get_button_index() == MOUSE_BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_panning") && pan_pressed))) {
// Pan the viewport
@@ -1232,8 +1231,9 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
}
}
- if (is_pan_key) {
+ if (is_pan_key && pan_pressed != k->is_pressed()) {
pan_pressed = k->is_pressed();
+ _update_cursor();
}
}
@@ -2615,7 +2615,19 @@ void CanvasItemEditor::_update_cursor() {
c = CURSOR_HSIZE;
}
- viewport->set_default_cursor_shape(c);
+ if (pan_pressed) {
+ c = CURSOR_DRAG;
+ }
+
+ if (c != viewport->get_default_cursor_shape()) {
+ viewport->set_default_cursor_shape(c);
+
+ // Force refresh cursor if it's over the viewport.
+ if (viewport->get_global_rect().has_point(get_global_mouse_position())) {
+ DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)viewport->get_default_cursor_shape();
+ DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
+ }
+ }
}
void CanvasItemEditor::_draw_text_at_position(Point2 p_position, String p_string, Side p_side) {
@@ -4254,10 +4266,6 @@ void CanvasItemEditor::_button_tool_select(int p_index) {
viewport->update();
_update_cursor();
-
- // Request immediate refresh of cursor when using hot-keys to switch between tools
- DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)viewport->get_default_cursor_shape();
- DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
}
void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing) {
@@ -5384,7 +5392,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
pan_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_PAN));
pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), KEY_G));
pan_button->set_shortcut_context(this);
- pan_button->set_tooltip(TTR("Pan Mode"));
+ pan_button->set_tooltip(TTR("You can also use Pan View shortcut (Space by default) to pan in any mode."));
ruler_button = memnew(Button);
ruler_button->set_flat(true);
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/collision_polygon_3d_editor_plugin.cpp
index 1ee834a974..53314db1e9 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_3d_editor_plugin.cpp
@@ -112,7 +112,7 @@ EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(
Transform3D gi = gt.affine_inverse();
float depth = _get_depth() * 0.5;
Vector3 n = gt.basis.get_axis(2).normalized();
- Plane p(gt.origin + n * depth, n);
+ Plane p(n, gt.origin + n * depth);
Ref<InputEventMouseButton> mb = p_event;
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 4d2fc29fe0..b28f3e134c 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -682,7 +682,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point,
}
if (collision_segments.size()) {
- Plane camp(p_camera->get_transform().origin, (-p_camera->get_transform().basis.get_axis(2)).normalized());
+ Plane camp(-p_camera->get_transform().basis.get_axis(2).normalized(), p_camera->get_transform().origin);
int vc = collision_segments.size();
const Vector3 *vptr = collision_segments.ptr();
@@ -1313,7 +1313,7 @@ void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id,
light->set_param(Light3D::PARAM_RANGE, d);
} else if (Object::cast_to<OmniLight3D>(light)) {
- Plane cp = Plane(gt.origin, p_camera->get_transform().basis.get_axis(2));
+ Plane cp = Plane(p_camera->get_transform().basis.get_axis(2), gt.origin);
Vector3 inters;
if (cp.intersects_ray(ray_from, ray_dir, &inters)) {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index ea6ef8ab84..32a8bbf406 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -651,13 +651,13 @@ void Node3DEditorViewport::_select_region() {
Vector3 a = _get_screen_to_space(box[i]);
Vector3 b = _get_screen_to_space(box[(i + 1) % 4]);
if (orthogonal) {
- frustum.push_back(Plane(a, (a - b).normalized()));
+ frustum.push_back(Plane((a - b).normalized(), a));
} else {
frustum.push_back(Plane(a, b, cam_pos));
}
}
- Plane near(cam_pos, -_get_camera_normal());
+ Plane near(-_get_camera_normal(), cam_pos);
near.d -= get_znear();
frustum.push_back(near);
@@ -972,7 +972,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gizmo_scale * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667);
Vector3 r;
- Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+ Plane plane(gt.basis.get_axis(i).normalized(), gt.origin);
if (plane.intersects_ray(ray_pos, ray, &r)) {
const real_t dist = r.distance_to(grabber_pos);
@@ -1010,7 +1010,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
float col_d = 1e20;
for (int i = 0; i < 3; i++) {
- Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+ Plane plane(gt.basis.get_axis(i).normalized(), gt.origin);
Vector3 r;
if (!plane.intersects_ray(ray_pos, ray, &r)) {
continue;
@@ -1076,7 +1076,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gizmo_scale * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667);
Vector3 r;
- Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+ Plane plane(gt.basis.get_axis(i).normalized(), gt.origin);
if (plane.intersects_ray(ray_pos, ray, &r)) {
const real_t dist = r.distance_to(grabber_pos);
@@ -1761,33 +1761,33 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
motion_mask = Vector3(0, 0, 0);
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_get_camera_normal(), _edit.center);
break;
case TRANSFORM_X_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_Y_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_Z_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_YZ:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
plane_mv = true;
break;
case TRANSFORM_XZ:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(0);
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
plane_mv = true;
break;
case TRANSFORM_XY:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
plane_mv = true;
break;
}
@@ -1882,30 +1882,30 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_get_camera_normal(), _edit.center);
break;
case TRANSFORM_X_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_Y_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_Z_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_YZ:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
plane_mv = true;
break;
case TRANSFORM_XZ:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
plane_mv = true;
break;
case TRANSFORM_XY:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
plane_mv = true;
break;
}
@@ -1978,18 +1978,18 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_get_camera_normal(), _edit.center);
break;
case TRANSFORM_X_AXIS:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
axis = Vector3(1, 0, 0);
break;
case TRANSFORM_Y_AXIS:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
axis = Vector3(0, 1, 0);
break;
case TRANSFORM_Z_AXIS:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
axis = Vector3(0, 0, 1);
break;
case TRANSFORM_YZ:
@@ -2226,6 +2226,21 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (ED_IS_SHORTCUT("spatial_editor/right_view", p_event)) {
_menu_option(VIEW_RIGHT);
}
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_down", p_event)) {
+ cursor.x_rot -= Math_PI / 12.0;
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_up", p_event)) {
+ cursor.x_rot += Math_PI / 12.0;
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_right", p_event)) {
+ cursor.y_rot -= Math_PI / 12.0;
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_left", p_event)) {
+ cursor.y_rot += Math_PI / 12.0;
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_180", p_event)) {
+ cursor.y_rot += Math_PI;
+ }
if (ED_IS_SHORTCUT("spatial_editor/focus_origin", p_event)) {
_menu_option(VIEW_CENTER_TO_ORIGIN);
}
@@ -3588,7 +3603,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
const Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized();
const Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized();
- const Plane p(camera_xform.origin, camz);
+ const Plane p = Plane(camz, camera_xform.origin);
const real_t gizmo_d = MAX(Math::abs(p.distance_to(xform.origin)), CMP_EPSILON);
const real_t d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y;
const real_t d1 = camera->unproject_position(camera_xform.origin + camz * gizmo_d + camy).y;
@@ -3863,7 +3878,7 @@ void Node3DEditorViewport::assign_pending_data_pointers(Node3D *p_preview_node,
}
Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const {
- const float MAX_DISTANCE = 10;
+ const float MAX_DISTANCE = 50.0;
Vector3 world_ray = _get_ray(p_pos);
Vector3 world_pos = _get_ray_pos(p_pos);
@@ -6171,7 +6186,7 @@ void Node3DEditor::_init_grid() {
if (orthogonal) {
camera_distance = camera->get_size() / 2.0;
Vector3 camera_direction = -camera->get_global_transform().get_basis().get_axis(2);
- Plane grid_plane = Plane(Vector3(), normal);
+ Plane grid_plane = Plane(normal);
Vector3 intersection;
if (grid_plane.intersects_ray(camera_position, camera_direction, &intersection)) {
camera_position = intersection;
@@ -6469,7 +6484,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
// We add a bit of margin to the from position to avoid it from snapping
// when the spatial is already on a floor and there's another floor under
// it
- from = from + Vector3(0.0, 0.2, 0.0);
+ from = from + Vector3(0.0, 1, 0.0);
Dictionary d;
@@ -6485,7 +6500,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
Array keys = snap_data.keys();
// The maximum height an object can travel to be snapped
- const float max_snap_height = 20.0;
+ const float max_snap_height = 500.0;
// Will be set to `true` if at least one node from the selection was successfully snapped
bool snapped_to_floor = false;
@@ -7306,6 +7321,11 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), KEY_KP_1);
ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT + KEY_KP_3);
ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3);
+ ED_SHORTCUT("spatial_editor/orbit_view_down", TTR("Orbit View Down"), KEY_KP_2);
+ ED_SHORTCUT("spatial_editor/orbit_view_left", TTR("Orbit View Left"), KEY_KP_4);
+ ED_SHORTCUT("spatial_editor/orbit_view_right", TTR("Orbit View Right"), KEY_KP_6);
+ ED_SHORTCUT("spatial_editor/orbit_view_up", TTR("Orbit View Up"), KEY_KP_8);
+ ED_SHORTCUT("spatial_editor/orbit_view_180", TTR("Orbit View 180"), KEY_KP_9);
ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal View"), KEY_KP_5);
ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K);
ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O);
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 0268b6e5ea..e2902feba1 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -101,7 +101,7 @@ void Path3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point
// Setting curve point positions
if (p_id < c->get_point_count()) {
- Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2));
+ const Plane p = Plane(p_camera->get_transform().basis.get_axis(2), gt.xform(original));
Vector3 inters;
@@ -125,7 +125,7 @@ void Path3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point
Vector3 base = c->get_point_position(idx);
- Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2));
+ Plane p(p_camera->get_transform().basis.get_axis(2), gt.xform(original));
Vector3 inters;
@@ -389,13 +389,13 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
- Vector3 org;
+ Vector3 origin;
if (c->get_point_count() == 0) {
- org = path->get_transform().get_origin();
+ origin = path->get_transform().get_origin();
} else {
- org = gt.xform(c->get_point_position(c->get_point_count() - 1));
+ origin = gt.xform(c->get_point_position(c->get_point_count() - 1));
}
- Plane p(org, p_camera->get_transform().basis.get_axis(2));
+ Plane p(p_camera->get_transform().basis.get_axis(2), origin);
Vector3 ray_from = p_camera->project_ray_origin(mbpos);
Vector3 ray_dir = p_camera->project_ray_normal(mbpos);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 5df6743f4c..677a5f1f2c 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -214,6 +214,7 @@ Ref<EditorSyntaxHighlighter> EditorPlainTextSyntaxHighlighter::_create() const {
void ScriptEditorBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_base_editor"), &ScriptEditorBase::get_base_editor);
+ ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &ScriptEditorBase::add_syntax_highlighter);
ADD_SIGNAL(MethodInfo("name_changed"));
ADD_SIGNAL(MethodInfo("edited_script_changed"));
@@ -762,20 +763,24 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tselected);
if (current) {
- Ref<Script> script = current->get_edited_resource();
- if (p_save && script.is_valid()) {
+ RES file = current->get_edited_resource();
+ if (p_save && file.is_valid()) {
// Do not try to save internal scripts, but prompt to save in-memory
// scripts which are not saved to disk yet (have empty path).
- if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
+ if (file->get_path().find("local://") == -1 && file->get_path().find("::") == -1) {
save_current_script();
}
}
- if (script.is_valid()) {
- if (!script->get_path().is_empty()) {
+ if (file.is_valid()) {
+ if (!file->get_path().is_empty()) {
// Only saved scripts can be restored.
- previous_scripts.push_back(script->get_path());
+ previous_scripts.push_back(file->get_path());
+ }
+
+ Ref<Script> script = file;
+ if (script.is_valid()) {
+ notify_script_close(script);
}
- notify_script_close(script);
}
}
@@ -1079,7 +1084,6 @@ void ScriptEditor::_file_dialog_action(String p_file) {
memdelete(file);
if (EditorFileSystem::get_singleton()) {
- const Vector<String> textfile_extensions = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
if (textfile_extensions.has(p_file.get_extension())) {
EditorFileSystem::get_singleton()->update_file(p_file);
}
@@ -1167,9 +1171,8 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog_option = FILE_NEW_TEXTFILE;
file_dialog->clear_filters();
- const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
- for (int i = 0; i < textfile_ext.size(); i++) {
- file_dialog->add_filter("*." + textfile_ext[i] + " ; " + textfile_ext[i].to_upper());
+ for (const String &E : textfile_extensions) {
+ file_dialog->add_filter("*." + E + " ; " + E.to_upper());
}
file_dialog->popup_file_dialog();
file_dialog->set_title(TTR("New Text File..."));
@@ -1187,9 +1190,8 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
}
- const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
- for (int i = 0; i < textfile_ext.size(); i++) {
- file_dialog->add_filter("*." + textfile_ext[i] + " ; " + textfile_ext[i].to_upper());
+ for (const String &E : textfile_extensions) {
+ file_dialog->add_filter("*." + E + " ; " + E.to_upper());
}
file_dialog->popup_file_dialog();
@@ -1542,6 +1544,7 @@ void ScriptEditor::_notification(int p_what) {
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ScriptEditor::_editor_settings_changed));
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &ScriptEditor::_filesystem_changed));
+ _editor_settings_changed();
[[fallthrough]];
}
case NOTIFICATION_TRANSLATION_CHANGED:
@@ -2117,7 +2120,7 @@ void ScriptEditor::_update_script_connections() {
}
}
-Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) {
+Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) const {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -2601,6 +2604,12 @@ void ScriptEditor::_save_layout() {
}
void ScriptEditor::_editor_settings_changed() {
+ textfile_extensions.clear();
+ const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
+ for (const String &E : textfile_ext) {
+ textfile_extensions.insert(E);
+ }
+
trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/trim_trailing_whitespace_on_save");
convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/convert_indent_on_save");
use_space_indentation = EditorSettings::get_singleton()->get("text_editor/behavior/indent/type");
@@ -2807,12 +2816,22 @@ bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data
if (file == "" || !FileAccess::exists(file)) {
continue;
}
- Ref<Script> scr = ResourceLoader::load(file);
- if (scr.is_valid()) {
- return true;
+ if (ResourceLoader::exists(file, "Script")) {
+ Ref<Script> scr = ResourceLoader::load(file);
+ if (scr.is_valid()) {
+ return true;
+ }
+ }
+
+ if (textfile_extensions.has(file.get_extension())) {
+ Error err;
+ Ref<TextFile> text_file = _load_text_file(file, &err);
+ if (text_file.is_valid() && err == OK) {
+ return true;
+ }
}
}
- return true;
+ return false;
}
return false;
@@ -2877,9 +2896,13 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
if (file == "" || !FileAccess::exists(file)) {
continue;
}
- Ref<Script> scr = ResourceLoader::load(file);
- if (scr.is_valid()) {
- edit(scr);
+
+ if (!ResourceLoader::exists(file, "Script") && !textfile_extensions.has(file.get_extension())) {
+ continue;
+ }
+
+ RES res = open_file(file);
+ if (res.is_valid()) {
if (tab_container->get_child_count() > num_tabs_before) {
tab_container->move_child(tab_container->get_child(tab_container->get_child_count() - 1), new_index);
num_tabs_before = tab_container->get_child_count();
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index ce26699280..8caebc1c8c 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -453,7 +453,8 @@ class ScriptEditor : public PanelContainer {
Ref<Script> _get_current_script();
Array _get_open_scripts() const;
- Ref<TextFile> _load_text_file(const String &p_path, Error *r_error);
+ Set<String> textfile_extensions;
+ Ref<TextFile> _load_text_file(const String &p_path, Error *r_error) const;
Error _save_text_file(Ref<TextFile> p_text_file, const String &p_path);
void _on_find_in_files_requested(String text);
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 1c251075de..2c02389db2 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1341,8 +1341,6 @@ void ScriptTextEditor::_bind_methods() {
ClassDB::bind_method("_get_drag_data_fw", &ScriptTextEditor::get_drag_data_fw);
ClassDB::bind_method("_can_drop_data_fw", &ScriptTextEditor::can_drop_data_fw);
ClassDB::bind_method("_drop_data_fw", &ScriptTextEditor::drop_data_fw);
-
- ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &ScriptTextEditor::add_syntax_highlighter);
}
Control *ScriptTextEditor::get_edit_menu() {
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 531ffc6a73..708eaf2c46 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -49,296 +49,128 @@ void BoneTransformEditor::create_editors() {
section = memnew(EditorInspectorSection);
section->setup("trf_properties", label, this, section_color, true);
+ section->unfold();
add_child(section);
- enabled_checkbox = memnew(CheckBox(TTR("Pose Enabled")));
- enabled_checkbox->set_flat(true);
- enabled_checkbox->set_visible(toggle_enabled);
+ enabled_checkbox = memnew(EditorPropertyCheck());
+ enabled_checkbox->set_label("Pose Enabled");
+ enabled_checkbox->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
section->get_vbox()->add_child(enabled_checkbox);
- key_button = memnew(Button);
- key_button->set_text(TTR("Key Transform"));
- key_button->set_visible(keyable);
- key_button->set_icon(get_theme_icon(SNAME("Key"), SNAME("EditorIcons")));
- key_button->set_flat(true);
- section->get_vbox()->add_child(key_button);
-
- // Translation property.
- translation_property = memnew(EditorPropertyVector3());
- translation_property->setup(-10000, 10000, 0.001f, true);
- translation_property->set_label("Translation");
- translation_property->set_use_folding(true);
- translation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
- section->get_vbox()->add_child(translation_property);
+ // Position property.
+ position_property = memnew(EditorPropertyVector3());
+ position_property->setup(-10000, 10000, 0.001f, true);
+ position_property->set_label("Position");
+ position_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+ section->get_vbox()->add_child(position_property);
// Rotation property.
- rotation_property = memnew(EditorPropertyVector3());
+ rotation_property = memnew(EditorPropertyQuaternion());
rotation_property->setup(-10000, 10000, 0.001f, true);
- rotation_property->set_label("Rotation Degrees");
- rotation_property->set_use_folding(true);
- rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+ rotation_property->set_label("Rotation");
+ rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
section->get_vbox()->add_child(rotation_property);
// Scale property.
scale_property = memnew(EditorPropertyVector3());
scale_property->setup(-10000, 10000, 0.001f, true);
scale_property->set_label("Scale");
- scale_property->set_use_folding(true);
- scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+ scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
section->get_vbox()->add_child(scale_property);
// Transform/Matrix section.
- transform_section = memnew(EditorInspectorSection);
- transform_section->setup("trf_properties_transform", "Matrix", this, section_color, true);
- section->get_vbox()->add_child(transform_section);
+ rest_section = memnew(EditorInspectorSection);
+ rest_section->setup("trf_properties_transform", "Rest", this, section_color, true);
+ section->get_vbox()->add_child(rest_section);
// Transform/Matrix property.
- transform_property = memnew(EditorPropertyTransform3D());
- transform_property->setup(-10000, 10000, 0.001f, true);
- transform_property->set_label("Transform");
- transform_property->set_use_folding(true);
- transform_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_transform));
- transform_section->get_vbox()->add_child(transform_property);
+ rest_matrix = memnew(EditorPropertyTransform3D());
+ rest_matrix->setup(-10000, 10000, 0.001f, true);
+ rest_matrix->set_label("Transform");
+ rest_section->get_vbox()->add_child(rest_matrix);
}
void BoneTransformEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
create_editors();
- key_button->connect("pressed", callable_mp(this, &BoneTransformEditor::_key_button_pressed));
- enabled_checkbox->connect("pressed", callable_mp(this, &BoneTransformEditor::_checkbox_pressed));
- [[fallthrough]];
- }
- case NOTIFICATION_SORT_CHILDREN: {
- const Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree"));
- int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree"));
-
- Point2 buffer;
- buffer.x += get_theme_constant(SNAME("inspector_margin"), SNAME("Editor"));
- buffer.y += font->get_height(font_size);
- buffer.y += get_theme_constant(SNAME("vseparation"), SNAME("Tree"));
-
- const float vector_height = translation_property->get_size().y;
- const float transform_height = transform_property->get_size().y;
- const float button_height = key_button->get_size().y;
-
- const float width = get_size().x - get_theme_constant(SNAME("inspector_margin"), SNAME("Editor"));
- Vector<Rect2> input_rects;
- if (keyable && section->get_vbox()->is_visible()) {
- input_rects.push_back(Rect2(key_button->get_position() + buffer, Size2(width, button_height)));
- } else {
- input_rects.push_back(Rect2(0, 0, 0, 0));
- }
-
- if (section->get_vbox()->is_visible()) {
- input_rects.push_back(Rect2(translation_property->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(rotation_property->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(scale_property->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(transform_property->get_position() + buffer, Size2(width, transform_height)));
- } else {
- const int32_t start = input_rects.size();
- const int32_t empty_input_rect_elements = 4;
- const int32_t end = start + empty_input_rect_elements;
- for (int i = start; i < end; ++i) {
- input_rects.push_back(Rect2(0, 0, 0, 0));
- }
- }
-
- for (int32_t i = 0; i < input_rects.size(); i++) {
- background_rects[i] = input_rects[i];
- }
-
- update();
- break;
- }
- case NOTIFICATION_DRAW: {
- const Color dark_color = get_theme_color(SNAME("dark_color_2"), SNAME("Editor"));
-
- for (int i = 0; i < 5; ++i) {
- draw_rect(background_rects[i], dark_color);
- }
-
break;
}
}
}
-void BoneTransformEditor::_value_changed(const double p_value) {
+void BoneTransformEditor::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
if (updating) {
return;
}
-
- Transform3D tform = compute_transform_from_vector3s();
- _change_transform(tform);
-}
-
-void BoneTransformEditor::_value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean) {
- if (updating) {
- return;
- }
- Transform3D tform = compute_transform_from_vector3s();
- _change_transform(tform);
-}
-
-Transform3D BoneTransformEditor::compute_transform_from_vector3s() const {
- // Convert rotation from degrees to radians.
- Vector3 prop_rotation = rotation_property->get_vector();
- prop_rotation.x = Math::deg2rad(prop_rotation.x);
- prop_rotation.y = Math::deg2rad(prop_rotation.y);
- prop_rotation.z = Math::deg2rad(prop_rotation.z);
-
- return Transform3D(
- Basis(prop_rotation, scale_property->get_vector()),
- translation_property->get_vector());
-}
-
-void BoneTransformEditor::_value_changed_transform(const String p_property_name, const Transform3D p_transform, const StringName p_edited_property_name, const bool p_boolean) {
- if (updating) {
- return;
- }
- _change_transform(p_transform);
-}
-
-void BoneTransformEditor::_change_transform(Transform3D p_new_transform) {
- if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {
- undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS);
- undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int()));
- undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), p_new_transform);
- undo_redo->commit_action();
- } else if (property.get_slicec('/', 0) == "bones") {
+ if (skeleton) {
undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
- undo_redo->add_undo_property(skeleton, property, skeleton->get(property));
- undo_redo->add_do_property(skeleton, property, p_new_transform);
+ undo_redo->add_undo_property(skeleton, p_property, skeleton->get(p_property));
+ undo_redo->add_do_property(skeleton, p_property, p_value);
undo_redo->commit_action();
}
}
-void BoneTransformEditor::update_enabled_checkbox() {
- if (enabled_checkbox) {
- const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
- const bool is_enabled = skeleton->get(path);
- enabled_checkbox->set_pressed(is_enabled);
- }
-}
-
-void BoneTransformEditor::_update_properties() {
- if (updating) {
- return;
- }
-
- if (!skeleton) {
- return;
- }
-
- updating = true;
-
- Transform3D tform = skeleton->get(property);
- _update_transform_properties(tform);
-}
-
-void BoneTransformEditor::_update_custom_pose_properties() {
- if (updating) {
- return;
- }
-
- if (!skeleton) {
- return;
- }
-
- updating = true;
-
- Transform3D tform = skeleton->get_bone_custom_pose(property.to_int());
- _update_transform_properties(tform);
-}
-
-void BoneTransformEditor::_update_transform_properties(Transform3D tform) {
- Basis rotation_basis = tform.get_basis();
- Vector3 rotation_radians = rotation_basis.get_rotation_euler();
- Vector3 rotation_degrees = Vector3(Math::rad2deg(rotation_radians.x), Math::rad2deg(rotation_radians.y), Math::rad2deg(rotation_radians.z));
- Vector3 translation = tform.get_origin();
- Vector3 scale = tform.basis.get_scale();
-
- translation_property->update_using_vector(translation);
- rotation_property->update_using_vector(rotation_degrees);
- scale_property->update_using_vector(scale);
- transform_property->update_using_transform(tform);
-
- update_enabled_checkbox();
- updating = false;
-}
-
BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
skeleton(p_skeleton) {
undo_redo = EditorNode::get_undo_redo();
}
void BoneTransformEditor::set_target(const String &p_prop) {
- property = p_prop;
-}
-
-void BoneTransformEditor::set_keyable(const bool p_keyable) {
- keyable = p_keyable;
-}
+ enabled_checkbox->set_object_and_property(skeleton, p_prop + "enabled");
+ enabled_checkbox->update_property();
-void BoneTransformEditor::_update_key_button(const bool p_keyable) {
- bool is_keyable = keyable && p_keyable;
- if (key_button) {
- key_button->set_visible(is_keyable);
- }
-}
+ position_property->set_object_and_property(skeleton, p_prop + "position");
+ position_property->update_property();
-void BoneTransformEditor::set_properties_read_only(const bool p_readonly) {
- enabled_checkbox->set_disabled(p_readonly);
- enabled_checkbox->update();
-}
+ rotation_property->set_object_and_property(skeleton, p_prop + "rotation");
+ rotation_property->update_property();
-void BoneTransformEditor::set_transform_read_only(const bool p_readonly) {
- translation_property->set_read_only(p_readonly);
- rotation_property->set_read_only(p_readonly);
- scale_property->set_read_only(p_readonly);
- transform_property->set_read_only(p_readonly);
- translation_property->update();
- rotation_property->update();
- scale_property->update();
- transform_property->update();
- _update_key_button(!p_readonly);
-}
+ scale_property->set_object_and_property(skeleton, p_prop + "scale");
+ scale_property->update_property();
-void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) {
- toggle_enabled = p_enabled;
- if (enabled_checkbox) {
- enabled_checkbox->set_visible(p_enabled);
- }
+ rest_matrix->set_object_and_property(skeleton, p_prop + "rest");
+ rest_matrix->update_property();
}
-void BoneTransformEditor::_key_button_pressed() {
- if (!skeleton) {
- return;
- }
-
- const BoneId bone_id = property.get_slicec('/', 1).to_int();
- const String name = skeleton->get_bone_name(bone_id);
-
- if (name.is_empty()) {
- return;
- }
-
- Transform3D tform = compute_transform_from_vector3s();
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(skeleton, name, tform);
-}
-
-void BoneTransformEditor::_checkbox_pressed() {
+void BoneTransformEditor::_update_properties() {
if (!skeleton) {
return;
}
-
- const BoneId bone_id = property.get_slicec('/', 1).to_int();
- if (enabled_checkbox) {
- undo_redo->create_action(TTR("Set Pose Enabled"));
- bool enabled = skeleton->is_bone_enabled(bone_id);
- undo_redo->add_do_method(skeleton, "set_bone_enabled", bone_id, !enabled);
- undo_redo->add_undo_method(skeleton, "set_bone_enabled", bone_id, enabled);
- undo_redo->commit_action();
+ int selected = Skeleton3DEditor::get_singleton()->get_selected_bone();
+ List<PropertyInfo> props;
+ skeleton->get_property_list(&props);
+ for (const PropertyInfo &E : props) {
+ PackedStringArray spr = E.name.split("/");
+ if (spr.size() == 3 && spr[0] == "bones") {
+ if (spr[1].to_int() == selected) {
+ if (spr[2] == "enabled") {
+ enabled_checkbox->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ enabled_checkbox->update_property();
+ enabled_checkbox->update();
+ }
+ if (spr[2] == "position") {
+ position_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ position_property->update_property();
+ position_property->update();
+ }
+ if (spr[2] == "rotation") {
+ rotation_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ rotation_property->update_property();
+ rotation_property->update();
+ }
+ if (spr[2] == "scale") {
+ scale_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ scale_property->update_property();
+ scale_property->update();
+ }
+ if (spr[2] == "rest") {
+ rest_matrix->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ rest_matrix->update_property();
+ rest_matrix->update();
+ }
+ }
+ }
}
}
@@ -354,24 +186,6 @@ void Skeleton3DEditor::set_rest_options_enabled(const bool p_rest_options_enable
rest_options->get_popup()->set_item_disabled(REST_OPTION_POSE_TO_REST, !p_rest_options_enabled);
};
-void Skeleton3DEditor::_update_show_rest_only() {
- _update_pose_enabled(-1);
-}
-
-void Skeleton3DEditor::_update_pose_enabled(int p_bone) {
- if (!skeleton) {
- return;
- }
- if (pose_editor) {
- pose_editor->set_properties_read_only(skeleton->is_show_rest_only());
-
- if (selected_bone > 0) {
- pose_editor->set_transform_read_only(skeleton->is_show_rest_only() || !(skeleton->is_bone_enabled(selected_bone)));
- }
- }
- _update_gizmo_visible();
-}
-
void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
if (!skeleton) {
return;
@@ -418,8 +232,13 @@ void Skeleton3DEditor::init_pose() {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
for (int i = 0; i < bone_len; i++) {
- ur->add_do_method(skeleton, "set_bone_pose", i, Transform3D());
- ur->add_undo_method(skeleton, "set_bone_pose", i, skeleton->get_bone_pose(i));
+ Transform3D rest = skeleton->get_bone_rest(i);
+ ur->add_do_method(skeleton, "set_bone_pose_position", i, rest.origin);
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", i, rest.basis.get_rotation_quaternion());
+ ur->add_do_method(skeleton, "set_bone_pose_scale", i, rest.basis.get_scale());
+ ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));
+ ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));
}
ur->commit_action();
}
@@ -459,15 +278,9 @@ void Skeleton3DEditor::pose_to_rest() {
// Todo: Do method with multiple bone selection.
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
-
- ur->add_do_method(skeleton, "set_bone_pose", selected_bone, Transform3D());
- ur->add_undo_method(skeleton, "set_bone_pose", selected_bone, skeleton->get_bone_pose(selected_bone));
- ur->add_do_method(skeleton, "set_bone_custom_pose", selected_bone, Transform3D());
- ur->add_undo_method(skeleton, "set_bone_custom_pose", selected_bone, skeleton->get_bone_custom_pose(selected_bone));
- ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone) * skeleton->get_bone_custom_pose(selected_bone) * skeleton->get_bone_pose(selected_bone));
+ ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
+ ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));
ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
-
ur->commit_action();
}
@@ -652,20 +465,14 @@ void Skeleton3DEditor::_joint_tree_selection_changed() {
const int b_idx = path.get_slicec('/', 1).to_int();
const String bone_path = "bones/" + itos(b_idx) + "/";
- pose_editor->set_target(bone_path + "pose");
- rest_editor->set_target(bone_path + "rest");
- custom_pose_editor->set_target(bone_path + "custom_pose");
-
- pose_editor->set_visible(true);
- rest_editor->set_visible(true);
- custom_pose_editor->set_visible(true);
-
+ pose_editor->set_target(bone_path);
selected_bone = b_idx;
}
}
+ pose_editor->set_visible(selected);
set_rest_options_enabled(selected);
_update_properties();
- _update_pose_enabled();
+ _update_gizmo_visible();
}
// May be not used with single select mode.
@@ -673,16 +480,10 @@ void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos) {
}
void Skeleton3DEditor::_update_properties() {
- if (rest_editor) {
- rest_editor->_update_properties();
- }
if (pose_editor) {
pose_editor->_update_properties();
}
- if (custom_pose_editor) {
- custom_pose_editor->_update_custom_pose_properties();
- }
- _update_gizmo_transform();
+ Node3DEditor::get_singleton()->update_transform_gizmo();
}
void Skeleton3DEditor::update_joint_tree() {
@@ -809,23 +610,9 @@ void Skeleton3DEditor::create_editors() {
s_con->add_child(joint_tree);
pose_editor = memnew(BoneTransformEditor(skeleton));
- pose_editor->set_label(TTR("Bone Pose"));
- pose_editor->set_toggle_enabled(true);
- pose_editor->set_keyable(te->has_keying());
+ pose_editor->set_label(TTR("Bone Transform"));
pose_editor->set_visible(false);
add_child(pose_editor);
-
- rest_editor = memnew(BoneTransformEditor(skeleton));
- rest_editor->set_label(TTR("Bone Rest"));
- rest_editor->set_visible(false);
- add_child(rest_editor);
- rest_editor->set_transform_read_only(true);
-
- custom_pose_editor = memnew(BoneTransformEditor(skeleton));
- custom_pose_editor->set_label(TTR("Bone Custom Pose"));
- custom_pose_editor->set_visible(false);
- add_child(custom_pose_editor);
- custom_pose_editor->set_transform_read_only(true);
}
void Skeleton3DEditor::_notification(int p_what) {
@@ -844,8 +631,8 @@ void Skeleton3DEditor::_notification(int p_what) {
#ifdef TOOLS_ENABLED
skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_draw_gizmo));
skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
- skeleton->connect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_update_pose_enabled));
- skeleton->connect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_show_rest_only));
+ skeleton->connect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_bone_enabled_changed));
+ skeleton->connect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_gizmo_visible));
#endif
break;
}
@@ -866,8 +653,6 @@ void Skeleton3DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_node_removed"), &Skeleton3DEditor::_node_removed);
ClassDB::bind_method(D_METHOD("_joint_tree_selection_changed"), &Skeleton3DEditor::_joint_tree_selection_changed);
ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &Skeleton3DEditor::_joint_tree_rmb_select);
- ClassDB::bind_method(D_METHOD("_update_show_rest_only"), &Skeleton3DEditor::_update_show_rest_only);
- ClassDB::bind_method(D_METHOD("_update_pose_enabled"), &Skeleton3DEditor::_update_pose_enabled);
ClassDB::bind_method(D_METHOD("_update_properties"), &Skeleton3DEditor::_update_properties);
ClassDB::bind_method(D_METHOD("_on_click_skeleton_option"), &Skeleton3DEditor::_on_click_skeleton_option);
ClassDB::bind_method(D_METHOD("_on_click_rest_option"), &Skeleton3DEditor::_on_click_rest_option);
@@ -938,7 +723,9 @@ void Skeleton3DEditor::update_bone_original() {
if (skeleton->get_bone_count() == 0 || selected_bone == -1) {
return;
}
- bone_original = skeleton->get_bone_pose(selected_bone);
+ bone_original_position = skeleton->get_bone_pose_position(selected_bone);
+ bone_original_rotation = skeleton->get_bone_pose_rotation(selected_bone);
+ bone_original_scale = skeleton->get_bone_pose_scale(selected_bone);
}
void Skeleton3DEditor::_hide_handles() {
@@ -1066,8 +853,8 @@ void Skeleton3DEditor::select_bone(int p_idx) {
Skeleton3DEditor::~Skeleton3DEditor() {
if (skeleton) {
#ifdef TOOLS_ENABLED
- skeleton->disconnect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_show_rest_only));
- skeleton->disconnect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_update_pose_enabled));
+ skeleton->disconnect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_gizmo_visible));
+ skeleton->disconnect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_bone_enabled_changed));
skeleton->disconnect("pose_updated", callable_mp(this, &Skeleton3DEditor::_draw_gizmo));
skeleton->disconnect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
skeleton->set_transform_gizmo_visible(true);
@@ -1148,9 +935,9 @@ bool Skeleton3DEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("Skeleton3D");
}
-void Skeleton3DEditor::_update_gizmo_transform() {
- Node3DEditor::get_singleton()->update_transform_gizmo();
-};
+void Skeleton3DEditor::_bone_enabled_changed(const int p_bone_id) {
+ _update_gizmo_visible();
+}
void Skeleton3DEditor::_update_gizmo_visible() {
_subgizmo_selection_change();
@@ -1289,7 +1076,6 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi
if (parent_idx >= 0) {
original_to_local = original_to_local * skeleton->get_bone_global_pose(parent_idx);
}
- original_to_local = original_to_local * skeleton->get_bone_rest(p_id) * skeleton->get_bone_custom_pose(p_id);
Basis to_local = original_to_local.get_basis().inverse();
// Prepare transform.
@@ -1305,7 +1091,9 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi
t.origin = orig + to_local.xform(sub);
// Apply transform.
- skeleton->set_bone_pose(p_id, t);
+ skeleton->set_bone_pose_position(p_id, t.origin);
+ skeleton->set_bone_pose_rotation(p_id, t.basis.get_rotation_quaternion());
+ skeleton->set_bone_pose_scale(p_id, t.basis.get_scale());
}
void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
@@ -1313,12 +1101,30 @@ void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, c
ERR_FAIL_COND(!skeleton);
Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ Node3DEditor *ne = Node3DEditor::get_singleton();
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- for (int i = 0; i < p_ids.size(); i++) {
- ur->create_action(TTR("Set Bone Transform"));
- ur->add_do_method(skeleton, "set_bone_pose", p_ids[i], skeleton->get_bone_pose(p_ids[i]));
- ur->add_undo_method(skeleton, "set_bone_pose", p_ids[i], se->get_bone_original());
+ ur->create_action(TTR("Set Bone Transform"));
+ if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {
+ for (int i = 0; i < p_ids.size(); i++) {
+ ur->add_do_method(skeleton, "set_bone_pose_position", p_ids[i], skeleton->get_bone_pose_position(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose_position", p_ids[i], se->get_bone_original_position());
+ }
+ }
+ if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) {
+ for (int i = 0; i < p_ids.size(); i++) {
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", p_ids[i], skeleton->get_bone_pose_rotation(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", p_ids[i], se->get_bone_original_rotation());
+ }
+ }
+ if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) {
+ for (int i = 0; i < p_ids.size(); i++) {
+ // If the axis is swapped by scaling, the rotation can be changed.
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", p_ids[i], skeleton->get_bone_pose_rotation(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", p_ids[i], se->get_bone_original_rotation());
+ ur->add_do_method(skeleton, "set_bone_pose_scale", p_ids[i], skeleton->get_bone_pose_scale(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose_scale", p_ids[i], se->get_bone_original_scale());
+ }
}
ur->commit_action();
}
@@ -1516,5 +1322,5 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
Ref<ArrayMesh> m = surface_tool->commit();
- p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(Ref<Skin>()));
+ p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms()));
}
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index e2a1d9a628..3b4dd362fb 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -52,21 +52,22 @@ class BoneTransformEditor : public VBoxContainer {
EditorInspectorSection *section = nullptr;
- EditorPropertyVector3 *translation_property = nullptr;
- EditorPropertyVector3 *rotation_property = nullptr;
+ EditorPropertyCheck *enabled_checkbox = nullptr;
+ EditorPropertyVector3 *position_property = nullptr;
+ EditorPropertyQuaternion *rotation_property = nullptr;
EditorPropertyVector3 *scale_property = nullptr;
- EditorInspectorSection *transform_section = nullptr;
- EditorPropertyTransform3D *transform_property = nullptr;
+
+ EditorInspectorSection *rest_section = nullptr;
+ EditorPropertyTransform3D *rest_matrix = nullptr;
Rect2 background_rects[5];
Skeleton3D *skeleton;
- String property;
+ // String property;
UndoRedo *undo_redo;
- Button *key_button = nullptr;
- CheckBox *enabled_checkbox = nullptr;
+ // Button *key_button = nullptr;
bool keyable = false;
bool toggle_enabled = false;
@@ -76,20 +77,7 @@ class BoneTransformEditor : public VBoxContainer {
void create_editors();
- // Called when one of the EditorSpinSliders are changed.
- void _value_changed(const double p_value);
- // Called when the one of the EditorPropertyVector3 are updated.
- void _value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean);
- // Called when the transform_property is updated.
- void _value_changed_transform(const String p_property_name, const Transform3D p_transform, const StringName p_edited_property_name, const bool p_boolean);
- // Changes the transform to the given transform and updates the UI accordingly.
- void _change_transform(Transform3D p_new_transform);
- // Update it is truely keyable then.
- void _update_key_button(const bool p_keyable);
- // Creates a Transform using the EditorPropertyVector3 properties.
- Transform3D compute_transform_from_vector3s() const;
-
- void update_enabled_checkbox();
+ void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing);
protected:
void _notification(int p_what);
@@ -102,24 +90,6 @@ public:
void set_label(const String &p_label) { label = p_label; }
void _update_properties();
- void _update_custom_pose_properties();
- void _update_transform_properties(Transform3D p_transform);
-
- // Transform can be keyed, whether or not to show the button.
- void set_keyable(const bool p_keyable);
-
- // When rest mode, pose and custom_pose editor are diasbled.
- void set_properties_read_only(const bool p_readonly);
- void set_transform_read_only(const bool p_readonly);
-
- // Bone can be toggled enabled or disabled, whether or not to show the checkbox.
- void set_toggle_enabled(const bool p_enabled);
-
- // Key Transform Button pressed.
- void _key_button_pressed();
-
- // Bone Enabled Checkbox toggled.
- void _checkbox_pressed();
};
class Skeleton3DEditor : public VBoxContainer {
@@ -151,7 +121,6 @@ class Skeleton3DEditor : public VBoxContainer {
Tree *joint_tree = nullptr;
BoneTransformEditor *rest_editor = nullptr;
BoneTransformEditor *pose_editor = nullptr;
- BoneTransformEditor *custom_pose_editor = nullptr;
VSeparator *separator;
MenuButton *skeleton_options = nullptr;
@@ -199,13 +168,12 @@ class Skeleton3DEditor : public VBoxContainer {
Ref<ShaderMaterial> handle_material;
Ref<Shader> handle_shader;
- Transform3D bone_original;
-
- void _update_pose_enabled(int p_bone = -1);
- void _update_show_rest_only();
+ Vector3 bone_original_position;
+ Quaternion bone_original_rotation;
+ Vector3 bone_original_scale;
- void _update_gizmo_transform();
void _update_gizmo_visible();
+ void _bone_enabled_changed(const int p_bone_id);
void _hide_handles();
@@ -239,7 +207,9 @@ public:
bool is_edit_mode() const { return edit_mode; }
void update_bone_original();
- Transform3D get_bone_original() { return bone_original; };
+ Vector3 get_bone_original_position() const { return bone_original_position; };
+ Quaternion get_bone_original_rotation() const { return bone_original_rotation; };
+ Vector3 get_bone_original_scale() const { return bone_original_scale; };
Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *skeleton);
~Skeleton3DEditor();
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 06ba8a6168..1fc7eb98e0 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -407,10 +407,6 @@ void TextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) {
code_editor->convert_case(p_case);
}
-void TextEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &TextEditor::add_syntax_highlighter);
-}
-
static ScriptEditorBase *create_editor(const RES &p_resource) {
if (Object::cast_to<TextFile>(*p_resource)) {
return memnew(TextEditor);
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index 9308fec210..7404557f46 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -87,8 +87,6 @@ private:
};
protected:
- static void _bind_methods();
-
void _edit_option(int p_op);
void _make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded, Vector2 p_position);
void _text_edit_gui_input(const Ref<InputEvent> &ev);
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index f21d5098d3..c064073b77 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -97,15 +97,6 @@ Size2i TileAtlasView::_compute_base_tiles_control_size() {
if (texture.is_valid()) {
size = texture->get_size();
}
-
- // Extend the size to all existing tiles.
- Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
- for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
- Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
- grid_size = grid_size.max(tile_id + Vector2i(1, 1));
- }
- size = size.max(grid_size * (tile_set_atlas_source->get_texture_region_size() + tile_set_atlas_source->get_separation()) + tile_set_atlas_source->get_margins());
-
return size;
}
@@ -213,43 +204,56 @@ void TileAtlasView::_draw_base_tiles() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
-
- // Draw the texture, square by square.
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+
+ // Draw the texture where there is no tile.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
- Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size);
- base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
+ rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (rect.size.x > 0 && rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
}
}
}
// Draw the texture around the grid.
Rect2i rect;
+
// Top.
rect.position = Vector2i();
rect.set_end(Vector2i(texture->get_size().x, margins.y));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+
// Bottom
- int bottom_border = margins.y + (grid_size.y * texture_region_size.y);
+ int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));
if (bottom_border < texture->get_size().y) {
rect.position = Vector2i(0, bottom_border);
rect.set_end(texture->get_size());
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
+
// Left
rect.position = Vector2i(0, margins.y);
- rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y)));
+ rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+
// Right.
- int right_border = margins.x + (grid_size.x * texture_region_size.x);
+ int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));
if (right_border < texture->get_size().x) {
rect.position = Vector2i(right_border, margins.y);
- rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y)));
+ rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Draw actual tiles, using their properties (modulation, etc...)
@@ -258,12 +262,30 @@ void TileAtlasView::_draw_base_tiles() {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value.
- int animation_columns = tile_set_atlas_source->get_tile_animation_columns(atlas_coords);
- Vector2i frame_coords = atlas_coords + (tile_set_atlas_source->get_tile_size_in_atlas(atlas_coords) + tile_set_atlas_source->get_tile_animation_separation(atlas_coords)) * ((animation_columns > 0) ? Vector2i(frame % animation_columns, frame / animation_columns) : Vector2i(frame, 0));
- Vector2i offset_pos = (margins + (frame_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
+ Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
+ Vector2i offset_pos = base_frame_rect.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0);
// Draw the tile.
TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame);
+
+ // Draw, the texture in the separation areas
+ if (separation.x > 0) {
+ Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y));
+ right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);
+ base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
+ }
+
+ if (separation.y > 0) {
+ Rect2i bottom_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(0, base_frame_rect.size.y), Vector2i(base_frame_rect.size.x + separation.x, separation.y));
+ bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);
+ base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
+ }
}
}
}
@@ -299,30 +321,6 @@ void TileAtlasView::_draw_base_tiles_texture_grid() {
}
}
-void TileAtlasView::_draw_base_tiles_dark() {
- Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
- if (texture.is_valid()) {
- Vector2i margins = tile_set_atlas_source->get_margins();
- Vector2i separation = tile_set_atlas_source->get_separation();
- Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
-
- Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
-
- // Draw each tile texture region.
- for (int x = 0; x < grid_size.x; x++) {
- for (int y = 0; y < grid_size.y; y++) {
- Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
- Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
-
- if (base_tile_coords == TileSetSource::INVALID_ATLAS_COORDS) {
- // Draw the grid.
- base_tiles_dark->draw_rect(Rect2i(origin, texture_region_size), Color(0.0, 0.0, 0.0, 0.5), true);
- }
- }
- }
- }
-}
-
void TileAtlasView::_draw_base_tiles_shape_grid() {
// Draw the shapes.
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
@@ -453,7 +451,6 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_
base_tiles_draw->update();
base_tiles_texture_grid->update();
base_tiles_shape_grid->update();
- base_tiles_dark->update();
alternatives_draw->update();
background_left->update();
background_right->update();
@@ -544,7 +541,6 @@ void TileAtlasView::update() {
base_tiles_draw->update();
base_tiles_texture_grid->update();
base_tiles_shape_grid->update();
- base_tiles_dark->update();
alternatives_draw->update();
background_left->update();
background_right->update();
@@ -660,12 +656,6 @@ TileAtlasView::TileAtlasView() {
base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
- base_tiles_dark = memnew(Control);
- base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark));
- base_tiles_drawing_root->add_child(base_tiles_dark);
-
// Alternative tiles.
Label *alternative_tiles_label = memnew(Label);
alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index 5b0df366ae..e1ca3eebee 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -93,9 +93,6 @@ private:
Control *base_tiles_shape_grid;
void _draw_base_tiles_shape_grid();
- Control *base_tiles_dark;
- void _draw_base_tiles_dark();
-
Size2i _compute_base_tiles_control_size();
// Right side.
@@ -124,7 +121,6 @@ public:
// Left side.
void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); };
- void set_dark_visible(bool p_visible) { base_tiles_dark->set_visible(p_visible); };
void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); };
Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos) const;
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 1a69d19d3c..104f46f773 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -38,8 +38,16 @@
#include "editor/editor_properties.h"
#include "editor/editor_scale.h"
-void TileDataEditor::_call_tile_set_changed() {
- _tile_set_changed();
+void TileDataEditor::_tile_set_changed_plan_update() {
+ _tile_set_changed_update_needed = true;
+ call_deferred("_tile_set_changed_deferred_update");
+}
+
+void TileDataEditor::_tile_set_changed_deferred_update() {
+ if (_tile_set_changed_update_needed) {
+ _tile_set_changed();
+ _tile_set_changed_update_needed = false;
+ }
}
TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) {
@@ -59,18 +67,20 @@ TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) {
}
void TileDataEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileDataEditor::_tile_set_changed_deferred_update);
+
ADD_SIGNAL(MethodInfo("needs_redraw"));
}
void TileDataEditor::set_tile_set(Ref<TileSet> p_tile_set) {
if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed));
+ tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
}
tile_set = p_tile_set;
if (tile_set.is_valid()) {
- tile_set->connect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed));
+ tile_set->connect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
}
- _call_tile_set_changed();
+ _tile_set_changed_plan_update();
}
bool DummyObject::_set(const StringName &p_name, const Variant &p_value) {
diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h
index 99998dc779..acb4c5882d 100644
--- a/editor/plugins/tiles/tile_data_editors.h
+++ b/editor/plugins/tiles/tile_data_editors.h
@@ -45,7 +45,9 @@ class TileDataEditor : public VBoxContainer {
GDCLASS(TileDataEditor, VBoxContainer);
private:
- void _call_tile_set_changed();
+ bool _tile_set_changed_update_needed = false;
+ void _tile_set_changed_plan_update();
+ void _tile_set_changed_deferred_update();
protected:
Ref<TileSet> tile_set;
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index cc5ff90541..5ca39b82b2 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -456,7 +456,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
case DRAG_TYPE_PAINT: {
Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos);
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
Vector2i coords = E.key;
@@ -465,6 +465,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
+ _fix_invalid_tiles_in_tile_map_selection();
} break;
case DRAG_TYPE_BUCKET: {
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
@@ -472,7 +473,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (!drag_modified.has(line[i])) {
Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
Vector2i coords = E.key;
@@ -483,6 +484,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
}
}
+ _fix_invalid_tiles_in_tile_map_selection();
} break;
default:
break;
@@ -499,8 +501,12 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT || mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
if (mb->is_pressed()) {
+ if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ rmb_erasing = true;
+ }
+
// Pressed
if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
// Do nothing.
@@ -508,6 +514,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
drag_start_mouse_pos = mpos;
if (tile_map_selection.has(tile_map->world_to_map(drag_start_mouse_pos)) && !mb->is_shift_pressed()) {
// Move the selection
+ _update_selection_pattern_from_tilemap_selection(); // Make sure the pattern is up to date before moving.
drag_type = DRAG_TYPE_MOVE;
drag_modified.clear();
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
@@ -521,18 +528,18 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
} else {
// Check if we are picking a tile.
- if (picker_button->is_pressed()) {
+ if (picker_button->is_pressed() || (Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
drag_type = DRAG_TYPE_PICK;
drag_start_mouse_pos = mpos;
} else {
// Paint otherwise.
- if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
drag_type = DRAG_TYPE_PAINT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos);
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
Vector2i coords = E.key;
@@ -541,11 +548,12 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
- } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ _fix_invalid_tiles_in_tile_map_selection();
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(KEY_SHIFT) && !Input::get_singleton()->is_key_pressed(KEY_CTRL))) {
drag_type = DRAG_TYPE_LINE;
drag_start_mouse_pos = mpos;
drag_modified.clear();
- } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(KEY_SHIFT) && Input::get_singleton()->is_key_pressed(KEY_CTRL))) {
drag_type = DRAG_TYPE_RECT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
@@ -558,7 +566,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (!drag_modified.has(line[i])) {
Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
Vector2i coords = E.key;
@@ -569,6 +577,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
}
}
+ _fix_invalid_tiles_in_tile_map_selection();
}
}
}
@@ -576,6 +585,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
} else {
// Released
_stop_dragging();
+ if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ rmb_erasing = false;
+ }
}
CanvasItemEditor::get_singleton()->update_viewport();
@@ -682,13 +694,13 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
Vector2i coords = tile_map->map_pattern(tile_map->world_to_map(drag_last_mouse_pos - mouse_offset), clipboard_used_cells[i], tile_map_clipboard);
preview[coords] = TileMapCell(tile_map_clipboard->get_cell_source_id(clipboard_used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(clipboard_used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(clipboard_used_cells[i]));
}
- } else if (!picker_button->is_pressed()) {
+ } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
bool expand_grid = false;
if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
// Preview for a single pattern.
preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos);
expand_grid = true;
- } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || drag_type == DRAG_TYPE_LINE) {
if (drag_type == DRAG_TYPE_NONE) {
// Preview for a single pattern.
preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos);
@@ -698,12 +710,12 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
preview = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, drag_last_mouse_pos);
expand_grid = true;
}
- } else if (tool_buttons_group->get_pressed_button() == rect_tool_button && drag_type == DRAG_TYPE_RECT) {
- // Preview for a line pattern.
+ } else if (drag_type == DRAG_TYPE_RECT) {
+ // Preview for a rect pattern.
preview = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos));
expand_grid = true;
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button && drag_type == DRAG_TYPE_NONE) {
- // Preview for a line pattern.
+ // Preview for a fill pattern.
preview = _draw_bucket_fill(tile_map->world_to_map(drag_last_mouse_pos), bucket_continuous_checkbox->is_pressed());
}
@@ -752,7 +764,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
Transform2D tile_xform;
tile_xform.set_origin(tile_map->map_to_world(E.key));
tile_xform.set_scale(tile_set->get_tile_size());
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
} else {
if (tile_set->has_source(E.value.source_id)) {
@@ -877,12 +889,12 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_
// Get or create the pattern.
TileMapPattern erase_pattern;
erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+ TileMapPattern *pattern = _is_erasing() ? &erase_pattern : selection_pattern;
Map<Vector2i, TileMapCell> output;
if (!pattern->is_empty()) {
// Paint the tiles on the tile map.
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(p_from_mouse_pos), tile_map->world_to_map(p_to_mouse_pos));
for (int i = 0; i < line.size(); i++) {
@@ -929,7 +941,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
// Get or create the pattern.
TileMapPattern erase_pattern;
erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+ TileMapPattern *pattern = _is_erasing() ? &erase_pattern : selection_pattern;
Map<Vector2i, TileMapCell> err_output;
ERR_FAIL_COND_V(pattern->is_empty(), err_output);
@@ -940,7 +952,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
Map<Vector2i, TileMapCell> output;
if (!pattern->is_empty()) {
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
for (int x = 0; x < rect.size.x; x++) {
for (int y = 0; y < rect.size.y; y++) {
@@ -988,7 +1000,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
// Get or create the pattern.
TileMapPattern erase_pattern;
erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+ TileMapPattern *pattern = _is_erasing() ? &erase_pattern : selection_pattern;
if (!pattern->is_empty()) {
TileMapCell source = tile_map->get_cell(tile_map_layer, p_coords);
@@ -1012,7 +1024,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
(source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
output.insert(coords, _pick_random_tile(pattern));
} else {
@@ -1058,7 +1070,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
(source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
output.insert(coords, _pick_random_tile(pattern));
} else {
@@ -1215,7 +1227,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos);
undo_redo->create_action(TTR("Paint tiles"));
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
@@ -1227,7 +1239,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos));
undo_redo->create_action(TTR("Paint tiles"));
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
@@ -1323,6 +1335,29 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
}
}
+void TileMapEditorTilesPlugin::_fix_invalid_tiles_in_tile_map_selection() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Set<Vector2i> to_remove;
+ for (Vector2i selected : tile_map_selection) {
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, selected);
+ if (cell.source_id == TileSet::INVALID_SOURCE && cell.get_atlas_coords() == TileSetSource::INVALID_ATLAS_COORDS && cell.alternative_tile == TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ to_remove.insert(selected);
+ }
+ }
+
+ for (Vector2i cell : to_remove) {
+ tile_map_selection.erase(cell);
+ }
+}
+
+bool TileMapEditorTilesPlugin::_is_erasing() const {
+ return erase_button->is_pressed() || rmb_erasing;
+}
+
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -1423,6 +1458,8 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern(
}
}
_update_bottom_panel();
+ tile_atlas_control->update();
+ alternative_tiles_control->update();
}
void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
@@ -1804,6 +1841,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
paint_tool_button->set_toggle_mode(true);
paint_tool_button->set_button_group(tool_buttons_group);
paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", KEY_D));
+ paint_tool_button->set_tooltip(TTR("Shift: Draw line.") + "\n" + TTR("Shift+Ctrl: Draw rectangle."));
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
@@ -1844,6 +1882,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
picker_button->set_flat(true);
picker_button->set_toggle_mode(true);
picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P));
+ picker_button->set_tooltip(TTR("Alternatively hold Ctrl with other tools to pick tile."));
picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(picker_button);
@@ -1852,6 +1891,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
erase_button->set_flat(true);
erase_button->set_toggle_mode(true);
erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", KEY_E));
+ erase_button->set_tooltip(TTR("Alternatively use RMB to erase tiles."));
erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 6126db59e9..5fbd9cada8 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -104,12 +104,14 @@ private:
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
Map<Vector2i, TileMapCell> drag_modified;
+ bool rmb_erasing = false;
TileMapCell _pick_random_tile(const TileMapPattern *p_pattern);
Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos);
Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell);
Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous);
void _stop_dragging();
+ bool _is_erasing() const;
///// Selection system. /////
Set<Vector2i> tile_map_selection;
@@ -124,6 +126,7 @@ private:
void _update_selection_pattern_from_tileset_selection();
void _update_tileset_selection_from_selection_pattern();
void _update_fix_selected_and_hovered();
+ void _fix_invalid_tiles_in_tile_map_selection();
///// Bottom panel. ////.
Label *missing_source_label;
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 3fbd315aec..c43a854d9a 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -520,9 +520,6 @@ void TileSetAtlasSourceEditor::_update_tile_id_label() {
void TileSetAtlasSourceEditor::_update_source_inspector() {
// Update the proxy object.
atlas_source_proxy_object->edit(tile_set, tile_set_atlas_source, tile_set_atlas_source_id);
-
- // Update the "clear outside texture" button.
- tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, !tile_set_atlas_source->has_tiles_outside_texture());
}
void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() {
@@ -763,7 +760,11 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
// Update visibility.
bool is_visible = tools_button_group->get_pressed_button() == tool_paint_button;
tile_data_editor_dropdown_button->set_visible(is_visible);
- tile_data_editor_dropdown_button->set_text(TTR("Select a property editor"));
+ if (tile_data_editors_tree->get_selected()) {
+ tile_data_editor_dropdown_button->set_text(tile_data_editors_tree->get_selected()->get_text(0));
+ } else {
+ tile_data_editor_dropdown_button->set_text(TTR("Select a property editor"));
+ }
tile_data_editors_label->set_visible(is_visible);
}
@@ -959,7 +960,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
current_tile_data_editor->forward_painting_atlas_gui_input(tile_atlas_view, tile_set_atlas_source, p_event);
}
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
tile_atlas_control->update();
tile_atlas_control_unscaled->update();
@@ -1061,7 +1062,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_current_tile = coords;
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
_update_tile_inspector();
_update_atlas_view();
_update_tile_id_label();
@@ -1101,7 +1102,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_current_tile = new_rect.position;
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
_update_tile_inspector();
_update_atlas_view();
_update_tile_id_label();
@@ -1600,9 +1601,6 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) {
undo_redo->commit_action();
_update_tile_id_label();
} break;
- case ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE: {
- tile_set_atlas_source->clear_tiles_outside_texture();
- } break;
case ADVANCED_AUTO_CREATE_TILES: {
_auto_create_tiles();
} break;
@@ -2012,12 +2010,12 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
}
}
-void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() {
- tile_set_atlas_source_changed_needs_update = true;
+void TileSetAtlasSourceEditor::_tile_set_changed() {
+ tile_set_changed_needs_update = true;
}
void TileSetAtlasSourceEditor::_tile_proxy_object_changed(String p_what) {
- tile_set_atlas_source_changed_needs_update = false; // Avoid updating too many things.
+ tile_set_changed_needs_update = false; // Avoid updating too many things.
_update_atlas_view();
}
@@ -2035,30 +2033,66 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
- AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited);
- if (tile_data) {
+ undo_redo->start_force_keep_in_merge_ends();
+ AtlasTileProxyObject *tile_data_proxy = Object::cast_to<AtlasTileProxyObject>(p_edited);
+ if (tile_data_proxy) {
Vector<String> components = String(p_property).split("/", true, 2);
if (components.size() == 2 && components[1] == "polygons_count") {
int layer_index = components[0].trim_prefix("physics_layer_").to_int();
int new_polygons_count = p_new_value;
- int old_polygons_count = tile_data->get(vformat("physics_layer_%d/polygons_count", layer_index));
+ int old_polygons_count = tile_data_proxy->get(vformat("physics_layer_%d/polygons_count", layer_index));
if (new_polygons_count < old_polygons_count) {
for (int i = new_polygons_count - 1; i < old_polygons_count; i++) {
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, i));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i));
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/points", layer_index, i));
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i));
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i));
}
}
} else if (p_property == "terrain_set") {
- int current_terrain_set = tile_data->get("terrain_set");
+ int current_terrain_set = tile_data_proxy->get("terrain_set");
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) {
- ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
+ ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
}
}
}
}
+
+ TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited);
+ if (atlas_source_proxy) {
+ TileSetAtlasSource *atlas_source = atlas_source_proxy->get_edited();
+ ERR_FAIL_COND(!atlas_source);
+
+ PackedVector2Array arr;
+ if (p_property == "texture") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size());
+ } else if (p_property == "margins") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), p_new_value, atlas_source->get_separation(), atlas_source->get_texture_region_size());
+ } else if (p_property == "separation") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), p_new_value, atlas_source->get_texture_region_size());
+ } else if (p_property == "texture_region_size") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), atlas_source->get_separation(), p_new_value);
+ }
+
+ if (!arr.is_empty()) {
+ // Get all properties assigned to a tile.
+ List<PropertyInfo> properties;
+ atlas_source->get_property_list(&properties);
+
+ for (int i = 0; i < arr.size(); i++) {
+ Vector2i coords = arr[i];
+ String prefix = vformat("%d:%d/", coords.x, coords.y);
+ for (PropertyInfo pi : properties) {
+ if (pi.name.begins_with(prefix)) {
+ ADD_UNDO(atlas_source, pi.name);
+ }
+ }
+ }
+ }
+ }
+ undo_redo->end_force_keep_in_merge_ends();
+
#undef ADD_UNDO
}
@@ -2073,8 +2107,8 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
}
// Remove listener for old objects.
- if (tile_set_atlas_source) {
- tile_set_atlas_source->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed));
+ if (tile_set.is_valid()) {
+ tile_set->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
// Clear the selection.
@@ -2086,8 +2120,8 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
tile_set_atlas_source_id = p_source_id;
// Add the listener again.
- if (tile_set_atlas_source) {
- tile_set_atlas_source->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed));
+ if (tile_set.is_valid()) {
+ tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
// Update everything.
@@ -2228,7 +2262,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
resize_handle_disabled = get_theme_icon(SNAME("EditorHandleDisabled"), SNAME("EditorIcons"));
break;
case NOTIFICATION_INTERNAL_PROCESS:
- if (tile_set_atlas_source_changed_needs_update) {
+ if (tile_set_changed_needs_update) {
// Update everything.
_update_source_inspector();
@@ -2241,7 +2275,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
_update_tile_data_editors();
_update_current_tile_data_editor();
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
}
break;
default:
@@ -2406,8 +2440,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tool_advanced_menu_buttom = memnew(MenuButton);
tool_advanced_menu_buttom->set_flat(true);
- tool_advanced_menu_buttom->get_popup()->add_item(TTR("Cleanup Tiles Outside Texture"), ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE);
- tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, true);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES);
tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
@@ -2481,6 +2513,8 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view_missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view_missing_source_label->hide();
right_panel->add_child(tile_atlas_view_missing_source_label);
+
+ EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetAtlasSourceEditor::_undo_redo_inspector_callback));
}
TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index 6448b55feb..ea6f2847ae 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -78,6 +78,7 @@ private:
int get_id();
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
+ TileSetAtlasSource *get_edited() { return tile_set_atlas_source; };
};
// -- Proxy object for a tile, needed by the inspector --
@@ -112,7 +113,7 @@ private:
UndoRedo *undo_redo = EditorNode::get_undo_redo();
- bool tile_set_atlas_source_changed_needs_update = false;
+ bool tile_set_changed_needs_update = false;
// -- Properties painting --
VBoxContainer *tile_data_painting_editor_container;
@@ -189,7 +190,6 @@ private:
TILE_CREATE_ALTERNATIVE,
TILE_DELETE,
- ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE,
ADVANCED_AUTO_CREATE_TILES,
ADVANCED_AUTO_REMOVE_TILES,
};
@@ -263,7 +263,7 @@ private:
void _auto_remove_tiles();
AcceptDialog *confirm_auto_create_tiles;
- void _tile_set_atlas_source_changed();
+ void _tile_set_changed();
void _tile_proxy_object_changed(String p_what);
void _atlas_source_proxy_object_changed(String p_what);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index b1b64564bb..0a067dfc17 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -491,6 +491,35 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
bool is_curve = curve.is_valid() || curve_xyz.is_valid();
if (is_curve) {
+ // a default value handling
+ {
+ Variant default_value;
+ bool port_left_used = false;
+
+ for (const VisualShader::Connection &E : connections) {
+ if (E.to_node == p_id && E.to_port == 0) {
+ port_left_used = true;
+ break;
+ }
+ }
+
+ if (!port_left_used) {
+ default_value = vsnode->get_input_port_default_value(0);
+ }
+
+ Button *button = memnew(Button);
+ custom_editor->add_child(button);
+ register_default_input_button(p_id, 0, button);
+ custom_editor->move_child(button, 0);
+
+ button->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_edit_port_default_input), varray(button, p_id, 0));
+ if (default_value.get_type() != Variant::NIL) {
+ set_input_port_default_value(p_type, p_id, 0, default_value);
+ } else {
+ button->hide();
+ }
+ }
+
VisualShaderEditor::get_singleton()->graph->add_child(node);
VisualShaderEditor::get_singleton()->_update_created_node(node);
@@ -643,6 +672,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
for (const VisualShader::Connection &E : connections) {
if (E.to_node == p_id && E.to_port == j) {
port_left_used = true;
+ break;
}
}
}
@@ -777,7 +807,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
expand->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_expand_output_port), varray(p_id, i, !vsnode->_is_output_port_expanded(i)), CONNECT_DEFERRED);
hb->add_child(expand);
}
- if (visual_shader->get_shader_type() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) {
+ if (vsnode->has_output_port_preview(i) && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) {
TextureButton *preview = memnew(TextureButton);
preview->set_toggle_mode(true);
preview->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon(SNAME("GuiVisibilityHidden"), SNAME("EditorIcons")));
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index 9062169e06..78dc85aa7d 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -433,7 +433,7 @@ void EditorSettingsDialog::_update_shortcuts() {
}
Array original = sc->get_meta("original");
- Array shortcuts_array = sc->get_events();
+ Array shortcuts_array = sc->get_events().duplicate(true);
bool same_as_defaults = Shortcut::is_event_array_equal(original, shortcuts_array);
bool collapse = !collapsed.has(E) || (collapsed.has(E) && collapsed[E]);