Add animation playback preview to scene import settings
This commit is contained in:
parent
a3c49ad2f0
commit
7cd943fc43
2 changed files with 226 additions and 11 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue