Merge pull request #76367 from Chaosus/import_settings_animation_playback

Add animation playback preview to scene import settings
This commit is contained in:
Yuri Sizov 2023-07-12 17:14:57 +02:00
commit 9547de5b0e
2 changed files with 226 additions and 11 deletions

View File

@ -64,7 +64,7 @@ class SceneImportSettingsData : public Object {
current[p_name] = p_value;
// SceneImportSettings must decide if a new collider should be generated or not
// SceneImportSettings must decide if a new collider should be generated or not.
if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE) {
SceneImportSettings::get_singleton()->request_generate_collider();
}
@ -350,8 +350,12 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
} else if (Object::cast_to<AnimationPlayer>(p_node)) {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
animation_player = Object::cast_to<AnimationPlayer>(p_node);
animation_player->connect(SNAME("animation_finished"), callable_mp(this, &SceneImportSettings::_animation_finished));
} else if (Object::cast_to<Skeleton3D>(p_node)) {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
skeletons.push_back(Object::cast_to<Skeleton3D>(p_node));
} else {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
}
@ -413,7 +417,7 @@ void SceneImportSettings::_update_scene() {
material_tree->clear();
mesh_tree->clear();
//hidden roots
// Hidden roots.
material_tree->create_item();
mesh_tree->create_item();
@ -432,7 +436,7 @@ void SceneImportSettings::_update_view_gizmos() {
MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(e.value.node);
if (mesh_node == nullptr || mesh_node->get_mesh().is_null()) {
// Nothing to do
// Nothing to do.
continue;
}
@ -591,7 +595,7 @@ void SceneImportSettings::open_settings(const String &p_path, bool p_for_animati
scene_import_settings_data->settings = nullptr;
scene_import_settings_data->path = p_path;
// Visibility
// Visibility.
data_mode->set_tab_hidden(1, p_for_animation);
data_mode->set_tab_hidden(2, p_for_animation);
if (p_for_animation) {
@ -691,12 +695,13 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
scene_import_settings_data->hide_options = false;
if (p_type == "Node") {
node_selected->hide(); //always hide just in case
node_selected->hide(); // Always hide just in case.
mesh_preview->hide();
_reset_animation();
if (Object::cast_to<Node3D>(scene)) {
Object::cast_to<Node3D>(scene)->show();
}
//NodeData &nd=node_map[p_id];
material_tree->deselect_all();
mesh_tree->deselect_all();
NodeData &nd = node_map[p_id];
@ -734,12 +739,13 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
}
}
} else if (p_type == "Animation") {
node_selected->hide(); //always hide just in case
node_selected->hide(); // Always hide just in case.
mesh_preview->hide();
_reset_animation(p_id);
if (Object::cast_to<Node3D>(scene)) {
Object::cast_to<Node3D>(scene)->show();
}
//NodeData &nd=node_map[p_id];
material_tree->deselect_all();
mesh_tree->deselect_all();
AnimationData &ad = animation_map[p_id];
@ -768,6 +774,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
mesh_preview->set_mesh(md.mesh);
mesh_preview->show();
_reset_animation();
material_tree->deselect_all();
@ -780,6 +787,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
}
mesh_preview->show();
_reset_animation();
MaterialData &md = material_map[p_id];
@ -836,7 +844,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
if (scene_import_settings_data->settings) {
for (const ResourceImporter::ImportOption &E : options) {
scene_import_settings_data->defaults[E.option.name] = E.default_value;
//needed for visibility toggling (fails if something is missing)
// Needed for visibility toggling (fails if something is missing).
if (scene_import_settings_data->settings->has(E.option.name)) {
scene_import_settings_data->current[E.option.name] = (*scene_import_settings_data->settings)[E.option.name];
} else {
@ -850,6 +858,127 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
scene_import_settings_data->notify_property_list_changed();
}
void SceneImportSettings::_inspector_property_edited(const String &p_name) {
if (p_name == "settings/loop_mode") {
if (!animation_map.has(selected_id)) {
return;
}
HashMap<StringName, Variant> settings = animation_map[selected_id].settings;
if (settings.has(p_name)) {
animation_loop_mode = static_cast<Animation::LoopMode>((int)settings[p_name]);
} else {
animation_loop_mode = Animation::LoopMode::LOOP_NONE;
}
}
}
void SceneImportSettings::_reset_bone_transforms() {
for (Skeleton3D *skeleton : skeletons) {
skeleton->reset_bone_poses();
}
}
void SceneImportSettings::_play_animation() {
if (animation_player == nullptr) {
return;
}
StringName id = StringName(selected_id);
if (animation_player->has_animation(id)) {
if (animation_player->is_playing()) {
animation_player->pause();
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
set_process(false);
} else {
animation_player->play(id);
animation_play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
set_process(true);
}
}
}
void SceneImportSettings::_stop_current_animation() {
animation_pingpong = false;
animation_player->stop();
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
animation_slider->set_value_no_signal(0.0);
set_process(false);
}
void SceneImportSettings::_reset_animation(const String &p_animation_name) {
if (p_animation_name.is_empty()) {
animation_preview->hide();
if (animation_player != nullptr && animation_player->is_playing()) {
animation_player->stop();
}
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
_reset_bone_transforms();
set_process(false);
} else {
_reset_bone_transforms();
animation_preview->show();
animation_loop_mode = Animation::LoopMode::LOOP_NONE;
animation_pingpong = false;
if (animation_map.has(p_animation_name)) {
HashMap<StringName, Variant> settings = animation_map[p_animation_name].settings;
if (settings.has("settings/loop_mode")) {
animation_loop_mode = static_cast<Animation::LoopMode>((int)settings["settings/loop_mode"]);
}
}
if (animation_player->is_playing() && animation_loop_mode != Animation::LoopMode::LOOP_NONE) {
animation_player->play(p_animation_name);
} else {
animation_player->stop(true);
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
animation_player->set_assigned_animation(p_animation_name);
animation_player->seek(0.0, true);
animation_slider->set_value_no_signal(0.0);
set_process(false);
}
}
}
void SceneImportSettings::_animation_slider_value_changed(double p_value) {
if (animation_player == nullptr || !animation_map.has(selected_id) || animation_map[selected_id].animation.is_null()) {
return;
}
if (animation_player->is_playing()) {
animation_player->stop();
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
set_process(false);
}
animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true);
}
void SceneImportSettings::_animation_finished(const StringName &p_name) {
Animation::LoopMode loop_mode = animation_loop_mode;
switch (loop_mode) {
case Animation::LOOP_NONE: {
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
animation_slider->set_value_no_signal(1.0);
set_process(false);
} break;
case Animation::LOOP_LINEAR: {
animation_player->play(p_name);
} break;
case Animation::LOOP_PINGPONG: {
if (animation_pingpong) {
animation_player->play(p_name);
} else {
animation_player->play_backwards(p_name);
}
animation_pingpong = !animation_pingpong;
} break;
default: {
} break;
}
}
void SceneImportSettings::_material_tree_selected() {
if (selecting) {
return;
@ -884,6 +1013,15 @@ void SceneImportSettings::_scene_tree_selected() {
_select(scene_tree, type, import_id);
}
void SceneImportSettings::_cleanup() {
skeletons.clear();
if (animation_player != nullptr) {
animation_player->disconnect(SNAME("animation_finished"), callable_mp(this, &SceneImportSettings::_animation_finished));
animation_player = nullptr;
}
set_process(false);
}
void SceneImportSettings::_viewport_input(const Ref<InputEvent> &p_input) {
float *rot_x = &cam_rot_x;
float *rot_y = &cam_rot_y;
@ -1005,6 +1143,25 @@ void SceneImportSettings::_notification(int p_what) {
action_menu->add_theme_style_override("normal", get_theme_stylebox("normal", "Button"));
action_menu->add_theme_style_override("hover", get_theme_stylebox("hover", "Button"));
action_menu->add_theme_style_override("pressed", get_theme_stylebox("pressed", "Button"));
if (animation_player != nullptr && animation_player->is_playing()) {
animation_play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
} else {
animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
}
animation_stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
} break;
case NOTIFICATION_PROCESS: {
if (animation_player != nullptr) {
animation_slider->set_value_no_signal(animation_player->get_current_animation_position() / animation_player->get_current_animation_length());
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible()) {
_cleanup();
}
} break;
}
}
@ -1331,16 +1488,53 @@ SceneImportSettings::SceneImportSettings() {
material_tree->set_hide_root(true);
VBoxContainer *vp_vb = memnew(VBoxContainer);
vp_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vp_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vp_vb->set_anchors_and_offsets_preset(Control::LayoutPreset::PRESET_FULL_RECT);
property_split->add_child(vp_vb);
SubViewportContainer *vp_container = memnew(SubViewportContainer);
vp_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vp_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vp_container->set_custom_minimum_size(Size2(10, 10));
vp_container->set_stretch(true);
vp_container->connect("gui_input", callable_mp(this, &SceneImportSettings::_viewport_input));
property_split->add_child(vp_container);
vp_vb->add_child(vp_container);
base_viewport = memnew(SubViewport);
vp_container->add_child(base_viewport);
animation_preview = memnew(PanelContainer);
animation_preview->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vp_vb->add_child(animation_preview);
animation_preview->hide();
HBoxContainer *animation_hbox = memnew(HBoxContainer);
animation_preview->add_child(animation_hbox);
animation_play_button = memnew(Button);
animation_hbox->add_child(animation_play_button);
animation_play_button->set_flat(true);
animation_play_button->set_focus_mode(Control::FOCUS_NONE);
animation_play_button->set_shortcut(ED_SHORTCUT("scene_import_settings/play_selected_animation", TTR("Selected Animation Play/Pause"), Key::SPACE));
animation_play_button->connect(SNAME("pressed"), callable_mp(this, &SceneImportSettings::_play_animation));
animation_stop_button = memnew(Button);
animation_hbox->add_child(animation_stop_button);
animation_stop_button->set_flat(true);
animation_stop_button->set_focus_mode(Control::FOCUS_NONE);
animation_stop_button->connect(SNAME("pressed"), callable_mp(this, &SceneImportSettings::_stop_current_animation));
animation_slider = memnew(HSlider);
animation_hbox->add_child(animation_slider);
animation_slider->set_h_size_flags(Control::SIZE_EXPAND_FILL);
animation_slider->set_v_size_flags(Control::SIZE_EXPAND_FILL);
animation_slider->set_max(1.0);
animation_slider->set_step(1.0 / 100.0);
animation_slider->set_value_no_signal(0.0);
animation_slider->set_focus_mode(Control::FOCUS_NONE);
animation_slider->connect(SNAME("value_changed"), callable_mp(this, &SceneImportSettings::_animation_slider_value_changed));
base_viewport->set_use_own_world_3d(true);
camera = memnew(Camera3D);
@ -1406,6 +1600,7 @@ SceneImportSettings::SceneImportSettings() {
inspector = memnew(EditorInspector);
inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
inspector->connect(SNAME("property_edited"), callable_mp(this, &SceneImportSettings::_inspector_property_edited));
property_split->add_child(inspector);

View File

@ -35,10 +35,13 @@
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/slider.h"
#include "scene/gui/split_container.h"
#include "scene/gui/subviewport_container.h"
#include "scene/gui/tab_container.h"
@ -85,6 +88,15 @@ class SceneImportSettings : public ConfirmationDialog {
MeshInstance3D *mesh_preview = nullptr;
Ref<SphereMesh> material_preview;
AnimationPlayer *animation_player = nullptr;
List<Skeleton3D *> skeletons;
PanelContainer *animation_preview = nullptr;
HSlider *animation_slider = nullptr;
Button *animation_play_button = nullptr;
Button *animation_stop_button = nullptr;
Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE;
bool animation_pingpong = false;
Ref<StandardMaterial3D> collider_mat;
float cam_rot_x = 0.0f;
@ -151,9 +163,17 @@ class SceneImportSettings : public ConfirmationDialog {
void _update_view_gizmos();
void _update_camera();
void _select(Tree *p_from, String p_type, String p_id);
void _inspector_property_edited(const String &p_name);
void _reset_bone_transforms();
void _play_animation();
void _stop_current_animation();
void _reset_animation(const String &p_animation_name = "");
void _animation_slider_value_changed(double p_value);
void _animation_finished(const StringName &p_name);
void _material_tree_selected();
void _mesh_tree_selected();
void _scene_tree_selected();
void _cleanup();
void _viewport_input(const Ref<InputEvent> &p_input);