Skip to content

Commit 7cd943f

Browse files
committed
Add animation playback preview to scene import settings
1 parent a3c49ad commit 7cd943f

2 files changed

Lines changed: 226 additions & 11 deletions

File tree

editor/import/scene_import_settings.cpp

Lines changed: 206 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class SceneImportSettingsData : public Object {
6464

6565
current[p_name] = p_value;
6666

67-
// SceneImportSettings must decide if a new collider should be generated or not
67+
// SceneImportSettings must decide if a new collider should be generated or not.
6868
if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE) {
6969
SceneImportSettings::get_singleton()->request_generate_collider();
7070
}
@@ -350,8 +350,12 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
350350
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
351351
} else if (Object::cast_to<AnimationPlayer>(p_node)) {
352352
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
353+
354+
animation_player = Object::cast_to<AnimationPlayer>(p_node);
355+
animation_player->connect(SNAME("animation_finished"), callable_mp(this, &SceneImportSettings::_animation_finished));
353356
} else if (Object::cast_to<Skeleton3D>(p_node)) {
354357
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
358+
skeletons.push_back(Object::cast_to<Skeleton3D>(p_node));
355359
} else {
356360
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
357361
}
@@ -413,7 +417,7 @@ void SceneImportSettings::_update_scene() {
413417
material_tree->clear();
414418
mesh_tree->clear();
415419

416-
//hidden roots
420+
// Hidden roots.
417421
material_tree->create_item();
418422
mesh_tree->create_item();
419423

@@ -432,7 +436,7 @@ void SceneImportSettings::_update_view_gizmos() {
432436

433437
MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(e.value.node);
434438
if (mesh_node == nullptr || mesh_node->get_mesh().is_null()) {
435-
// Nothing to do
439+
// Nothing to do.
436440
continue;
437441
}
438442

@@ -591,7 +595,7 @@ void SceneImportSettings::open_settings(const String &p_path, bool p_for_animati
591595
scene_import_settings_data->settings = nullptr;
592596
scene_import_settings_data->path = p_path;
593597

594-
// Visibility
598+
// Visibility.
595599
data_mode->set_tab_hidden(1, p_for_animation);
596600
data_mode->set_tab_hidden(2, p_for_animation);
597601
if (p_for_animation) {
@@ -691,12 +695,13 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
691695
scene_import_settings_data->hide_options = false;
692696

693697
if (p_type == "Node") {
694-
node_selected->hide(); //always hide just in case
698+
node_selected->hide(); // Always hide just in case.
695699
mesh_preview->hide();
700+
_reset_animation();
701+
696702
if (Object::cast_to<Node3D>(scene)) {
697703
Object::cast_to<Node3D>(scene)->show();
698704
}
699-
//NodeData &nd=node_map[p_id];
700705
material_tree->deselect_all();
701706
mesh_tree->deselect_all();
702707
NodeData &nd = node_map[p_id];
@@ -734,12 +739,13 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
734739
}
735740
}
736741
} else if (p_type == "Animation") {
737-
node_selected->hide(); //always hide just in case
742+
node_selected->hide(); // Always hide just in case.
738743
mesh_preview->hide();
744+
_reset_animation(p_id);
745+
739746
if (Object::cast_to<Node3D>(scene)) {
740747
Object::cast_to<Node3D>(scene)->show();
741748
}
742-
//NodeData &nd=node_map[p_id];
743749
material_tree->deselect_all();
744750
mesh_tree->deselect_all();
745751
AnimationData &ad = animation_map[p_id];
@@ -768,6 +774,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
768774

769775
mesh_preview->set_mesh(md.mesh);
770776
mesh_preview->show();
777+
_reset_animation();
771778

772779
material_tree->deselect_all();
773780

@@ -780,6 +787,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
780787
}
781788

782789
mesh_preview->show();
790+
_reset_animation();
783791

784792
MaterialData &md = material_map[p_id];
785793

@@ -836,7 +844,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
836844
if (scene_import_settings_data->settings) {
837845
for (const ResourceImporter::ImportOption &E : options) {
838846
scene_import_settings_data->defaults[E.option.name] = E.default_value;
839-
//needed for visibility toggling (fails if something is missing)
847+
// Needed for visibility toggling (fails if something is missing).
840848
if (scene_import_settings_data->settings->has(E.option.name)) {
841849
scene_import_settings_data->current[E.option.name] = (*scene_import_settings_data->settings)[E.option.name];
842850
} else {
@@ -850,6 +858,127 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
850858
scene_import_settings_data->notify_property_list_changed();
851859
}
852860

861+
void SceneImportSettings::_inspector_property_edited(const String &p_name) {
862+
if (p_name == "settings/loop_mode") {
863+
if (!animation_map.has(selected_id)) {
864+
return;
865+
}
866+
HashMap<StringName, Variant> settings = animation_map[selected_id].settings;
867+
if (settings.has(p_name)) {
868+
animation_loop_mode = static_cast<Animation::LoopMode>((int)settings[p_name]);
869+
} else {
870+
animation_loop_mode = Animation::LoopMode::LOOP_NONE;
871+
}
872+
}
873+
}
874+
875+
void SceneImportSettings::_reset_bone_transforms() {
876+
for (Skeleton3D *skeleton : skeletons) {
877+
skeleton->reset_bone_poses();
878+
}
879+
}
880+
881+
void SceneImportSettings::_play_animation() {
882+
if (animation_player == nullptr) {
883+
return;
884+
}
885+
StringName id = StringName(selected_id);
886+
if (animation_player->has_animation(id)) {
887+
if (animation_player->is_playing()) {
888+
animation_player->pause();
889+
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
890+
set_process(false);
891+
} else {
892+
animation_player->play(id);
893+
animation_play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
894+
set_process(true);
895+
}
896+
}
897+
}
898+
899+
void SceneImportSettings::_stop_current_animation() {
900+
animation_pingpong = false;
901+
animation_player->stop();
902+
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
903+
animation_slider->set_value_no_signal(0.0);
904+
set_process(false);
905+
}
906+
907+
void SceneImportSettings::_reset_animation(const String &p_animation_name) {
908+
if (p_animation_name.is_empty()) {
909+
animation_preview->hide();
910+
911+
if (animation_player != nullptr && animation_player->is_playing()) {
912+
animation_player->stop();
913+
}
914+
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
915+
916+
_reset_bone_transforms();
917+
set_process(false);
918+
} else {
919+
_reset_bone_transforms();
920+
animation_preview->show();
921+
922+
animation_loop_mode = Animation::LoopMode::LOOP_NONE;
923+
animation_pingpong = false;
924+
925+
if (animation_map.has(p_animation_name)) {
926+
HashMap<StringName, Variant> settings = animation_map[p_animation_name].settings;
927+
if (settings.has("settings/loop_mode")) {
928+
animation_loop_mode = static_cast<Animation::LoopMode>((int)settings["settings/loop_mode"]);
929+
}
930+
}
931+
932+
if (animation_player->is_playing() && animation_loop_mode != Animation::LoopMode::LOOP_NONE) {
933+
animation_player->play(p_animation_name);
934+
} else {
935+
animation_player->stop(true);
936+
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
937+
animation_player->set_assigned_animation(p_animation_name);
938+
animation_player->seek(0.0, true);
939+
animation_slider->set_value_no_signal(0.0);
940+
set_process(false);
941+
}
942+
}
943+
}
944+
945+
void SceneImportSettings::_animation_slider_value_changed(double p_value) {
946+
if (animation_player == nullptr || !animation_map.has(selected_id) || animation_map[selected_id].animation.is_null()) {
947+
return;
948+
}
949+
if (animation_player->is_playing()) {
950+
animation_player->stop();
951+
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
952+
set_process(false);
953+
}
954+
animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true);
955+
}
956+
957+
void SceneImportSettings::_animation_finished(const StringName &p_name) {
958+
Animation::LoopMode loop_mode = animation_loop_mode;
959+
960+
switch (loop_mode) {
961+
case Animation::LOOP_NONE: {
962+
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
963+
animation_slider->set_value_no_signal(1.0);
964+
set_process(false);
965+
} break;
966+
case Animation::LOOP_LINEAR: {
967+
animation_player->play(p_name);
968+
} break;
969+
case Animation::LOOP_PINGPONG: {
970+
if (animation_pingpong) {
971+
animation_player->play(p_name);
972+
} else {
973+
animation_player->play_backwards(p_name);
974+
}
975+
animation_pingpong = !animation_pingpong;
976+
} break;
977+
default: {
978+
} break;
979+
}
980+
}
981+
853982
void SceneImportSettings::_material_tree_selected() {
854983
if (selecting) {
855984
return;
@@ -884,6 +1013,15 @@ void SceneImportSettings::_scene_tree_selected() {
8841013
_select(scene_tree, type, import_id);
8851014
}
8861015

1016+
void SceneImportSettings::_cleanup() {
1017+
skeletons.clear();
1018+
if (animation_player != nullptr) {
1019+
animation_player->disconnect(SNAME("animation_finished"), callable_mp(this, &SceneImportSettings::_animation_finished));
1020+
animation_player = nullptr;
1021+
}
1022+
set_process(false);
1023+
}
1024+
8871025
void SceneImportSettings::_viewport_input(const Ref<InputEvent> &p_input) {
8881026
float *rot_x = &cam_rot_x;
8891027
float *rot_y = &cam_rot_y;
@@ -1005,6 +1143,25 @@ void SceneImportSettings::_notification(int p_what) {
10051143
action_menu->add_theme_style_override("normal", get_theme_stylebox("normal", "Button"));
10061144
action_menu->add_theme_style_override("hover", get_theme_stylebox("hover", "Button"));
10071145
action_menu->add_theme_style_override("pressed", get_theme_stylebox("pressed", "Button"));
1146+
1147+
if (animation_player != nullptr && animation_player->is_playing()) {
1148+
animation_play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
1149+
} else {
1150+
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
1151+
}
1152+
animation_stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
1153+
} break;
1154+
1155+
case NOTIFICATION_PROCESS: {
1156+
if (animation_player != nullptr) {
1157+
animation_slider->set_value_no_signal(animation_player->get_current_animation_position() / animation_player->get_current_animation_length());
1158+
}
1159+
} break;
1160+
1161+
case NOTIFICATION_VISIBILITY_CHANGED: {
1162+
if (!is_visible()) {
1163+
_cleanup();
1164+
}
10081165
} break;
10091166
}
10101167
}
@@ -1331,16 +1488,53 @@ SceneImportSettings::SceneImportSettings() {
13311488

13321489
material_tree->set_hide_root(true);
13331490

1491+
VBoxContainer *vp_vb = memnew(VBoxContainer);
1492+
vp_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1493+
vp_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1494+
vp_vb->set_anchors_and_offsets_preset(Control::LayoutPreset::PRESET_FULL_RECT);
1495+
property_split->add_child(vp_vb);
1496+
13341497
SubViewportContainer *vp_container = memnew(SubViewportContainer);
1335-
vp_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1498+
vp_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
13361499
vp_container->set_custom_minimum_size(Size2(10, 10));
13371500
vp_container->set_stretch(true);
13381501
vp_container->connect("gui_input", callable_mp(this, &SceneImportSettings::_viewport_input));
1339-
property_split->add_child(vp_container);
1502+
vp_vb->add_child(vp_container);
13401503

13411504
base_viewport = memnew(SubViewport);
13421505
vp_container->add_child(base_viewport);
13431506

1507+
animation_preview = memnew(PanelContainer);
1508+
animation_preview->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1509+
vp_vb->add_child(animation_preview);
1510+
animation_preview->hide();
1511+
1512+
HBoxContainer *animation_hbox = memnew(HBoxContainer);
1513+
animation_preview->add_child(animation_hbox);
1514+
1515+
animation_play_button = memnew(Button);
1516+
animation_hbox->add_child(animation_play_button);
1517+
animation_play_button->set_flat(true);
1518+
animation_play_button->set_focus_mode(Control::FOCUS_NONE);
1519+
animation_play_button->set_shortcut(ED_SHORTCUT("scene_import_settings/play_selected_animation", TTR("Selected Animation Play/Pause"), Key::SPACE));
1520+
animation_play_button->connect(SNAME("pressed"), callable_mp(this, &SceneImportSettings::_play_animation));
1521+
1522+
animation_stop_button = memnew(Button);
1523+
animation_hbox->add_child(animation_stop_button);
1524+
animation_stop_button->set_flat(true);
1525+
animation_stop_button->set_focus_mode(Control::FOCUS_NONE);
1526+
animation_stop_button->connect(SNAME("pressed"), callable_mp(this, &SceneImportSettings::_stop_current_animation));
1527+
1528+
animation_slider = memnew(HSlider);
1529+
animation_hbox->add_child(animation_slider);
1530+
animation_slider->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1531+
animation_slider->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1532+
animation_slider->set_max(1.0);
1533+
animation_slider->set_step(1.0 / 100.0);
1534+
animation_slider->set_value_no_signal(0.0);
1535+
animation_slider->set_focus_mode(Control::FOCUS_NONE);
1536+
animation_slider->connect(SNAME("value_changed"), callable_mp(this, &SceneImportSettings::_animation_slider_value_changed));
1537+
13441538
base_viewport->set_use_own_world_3d(true);
13451539

13461540
camera = memnew(Camera3D);
@@ -1406,6 +1600,7 @@ SceneImportSettings::SceneImportSettings() {
14061600

14071601
inspector = memnew(EditorInspector);
14081602
inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
1603+
inspector->connect(SNAME("property_edited"), callable_mp(this, &SceneImportSettings::_inspector_property_edited));
14091604

14101605
property_split->add_child(inspector);
14111606

editor/import/scene_import_settings.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@
3535
#include "scene/3d/camera_3d.h"
3636
#include "scene/3d/light_3d.h"
3737
#include "scene/3d/mesh_instance_3d.h"
38+
#include "scene/3d/skeleton_3d.h"
3839
#include "scene/gui/dialogs.h"
3940
#include "scene/gui/item_list.h"
4041
#include "scene/gui/menu_button.h"
4142
#include "scene/gui/option_button.h"
43+
#include "scene/gui/panel_container.h"
44+
#include "scene/gui/slider.h"
4245
#include "scene/gui/split_container.h"
4346
#include "scene/gui/subviewport_container.h"
4447
#include "scene/gui/tab_container.h"
@@ -85,6 +88,15 @@ class SceneImportSettings : public ConfirmationDialog {
8588
MeshInstance3D *mesh_preview = nullptr;
8689
Ref<SphereMesh> material_preview;
8790

91+
AnimationPlayer *animation_player = nullptr;
92+
List<Skeleton3D *> skeletons;
93+
PanelContainer *animation_preview = nullptr;
94+
HSlider *animation_slider = nullptr;
95+
Button *animation_play_button = nullptr;
96+
Button *animation_stop_button = nullptr;
97+
Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE;
98+
bool animation_pingpong = false;
99+
88100
Ref<StandardMaterial3D> collider_mat;
89101

90102
float cam_rot_x = 0.0f;
@@ -151,9 +163,17 @@ class SceneImportSettings : public ConfirmationDialog {
151163
void _update_view_gizmos();
152164
void _update_camera();
153165
void _select(Tree *p_from, String p_type, String p_id);
166+
void _inspector_property_edited(const String &p_name);
167+
void _reset_bone_transforms();
168+
void _play_animation();
169+
void _stop_current_animation();
170+
void _reset_animation(const String &p_animation_name = "");
171+
void _animation_slider_value_changed(double p_value);
172+
void _animation_finished(const StringName &p_name);
154173
void _material_tree_selected();
155174
void _mesh_tree_selected();
156175
void _scene_tree_selected();
176+
void _cleanup();
157177

158178
void _viewport_input(const Ref<InputEvent> &p_input);
159179

0 commit comments

Comments
 (0)