diff --git a/doc/classes/ResourceImporterScene.xml b/doc/classes/ResourceImporterScene.xml index 4e20fe150ead..900e028b2542 100644 --- a/doc/classes/ResourceImporterScene.xml +++ b/doc/classes/ResourceImporterScene.xml @@ -21,6 +21,9 @@ If [code]true[/code], import animations from the 3D scene. + + If [code]true[/code], adds an [Animation] named [code]RESET[/code], containing the [method Skeleton3D.get_bone_rest] from [Skeleton3D] nodes. This can be useful to extract an animation in the reference pose. + If [code]true[/code], remove animation tracks that only contain default values. This can reduce output file size and memory usage with certain 3D scenes, depending on the contents of their animation tracks. diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index ca128968deaa..c9a2ca758834 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -312,6 +312,71 @@ String ResourceImporterScene::get_preset_name(int p_idx) const { return String(); } +void ResourceImporterScene::_pre_fix_global(Node *p_scene, const HashMap &p_options) const { + if (p_options.has("animation/import_rest_as_RESET") && (bool)p_options["animation/import_rest_as_RESET"]) { + TypedArray anim_players = p_scene->find_children("*", "AnimationPlayer"); + if (anim_players.is_empty()) { + AnimationPlayer *anim_player = memnew(AnimationPlayer); + anim_player->set_name("AnimationPlayer"); + p_scene->add_child(anim_player); + anim_player->set_owner(p_scene); + anim_players.append(anim_player); + } + Ref reset_anim; + for (int i = 0; i < anim_players.size(); i++) { + AnimationPlayer *player = cast_to(anim_players[i]); + if (player->has_animation(SNAME("RESET"))) { + reset_anim = player->get_animation(SNAME("RESET")); + break; + } + } + if (reset_anim.is_null()) { + AnimationPlayer *anim_player = cast_to(anim_players[0]); + reset_anim.instantiate(); + Ref anim_library; + if (anim_player->has_animation_library(StringName())) { + anim_library = anim_player->get_animation_library(StringName()); + } else { + anim_library.instantiate(); + anim_player->add_animation_library(StringName(), anim_library); + } + anim_library->add_animation(SNAME("RESET"), reset_anim); + } + TypedArray skeletons = p_scene->find_children("*", "Skeleton3D"); + for (int i = 0; i < skeletons.size(); i++) { + Skeleton3D *skeleton = cast_to(skeletons[i]); + NodePath skeleton_path = p_scene->get_path_to(skeleton); + + HashSet existing_pos_tracks; + HashSet existing_rot_tracks; + for (int trk_i = 0; trk_i < reset_anim->get_track_count(); trk_i++) { + NodePath np = reset_anim->track_get_path(trk_i); + if (reset_anim->track_get_type(trk_i) == Animation::TYPE_POSITION_3D) { + existing_pos_tracks.insert(np); + } + if (reset_anim->track_get_type(trk_i) == Animation::TYPE_ROTATION_3D) { + existing_rot_tracks.insert(np); + } + } + for (int bone_i = 0; bone_i < skeleton->get_bone_count(); bone_i++) { + NodePath bone_path(skeleton_path.get_names(), Vector{ skeleton->get_bone_name(bone_i) }, false); + if (!existing_pos_tracks.has(bone_path)) { + int pos_t = reset_anim->add_track(Animation::TYPE_POSITION_3D); + reset_anim->track_set_path(pos_t, bone_path); + reset_anim->position_track_insert_key(pos_t, 0.0, skeleton->get_bone_rest(bone_i).origin); + reset_anim->track_set_imported(pos_t, true); + } + if (!existing_rot_tracks.has(bone_path)) { + int rot_t = reset_anim->add_track(Animation::TYPE_ROTATION_3D); + reset_anim->track_set_path(rot_t, bone_path); + reset_anim->rotation_track_insert_key(rot_t, 0.0, skeleton->get_bone_rest(bone_i).basis.get_rotation_quaternion()); + reset_anim->track_set_imported(rot_t, true); + } + } + } + } +} + static bool _teststr(const String &p_what, const String &p_str) { String what = p_what; @@ -1945,6 +2010,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, Listpush_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false)); 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_NO_EDITOR), Dictionary())); @@ -2387,6 +2453,8 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashM return nullptr; } + _pre_fix_global(scene, p_options); + HashMap, Vector>> collision_map; List> node_renames; _pre_fix_node(scene, scene, collision_map, nullptr, node_renames); @@ -2521,6 +2589,8 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p } } + _pre_fix_global(scene, p_options); + HashSet> scanned_meshes; HashMap, Vector>> collision_map; Pair occluder_arrays; diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h index 17fa9ef0e254..4e15471d175e 100644 --- a/editor/import/3d/resource_importer_scene.h +++ b/editor/import/3d/resource_importer_scene.h @@ -282,6 +282,7 @@ class ResourceImporterScene : public ResourceImporter { // Import scenes *after* everything else (such as textures). virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; } + void _pre_fix_global(Node *p_scene, const HashMap &p_options) const; Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap, Vector>> &r_collision_map, Pair *r_occluder_arrays, List> &r_node_renames); Node *_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps); Node *_post_fix_node(Node *p_node, Node *p_root, HashMap, Vector>> &collision_map, Pair &r_occluder_arrays, HashSet> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale); diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index 721eccdfddfc..260684ae3da2 100644 --- a/editor/import/3d/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp @@ -433,13 +433,20 @@ void SceneImportSettingsDialog::_update_view_gizmos() { return; } const HashMap &main_settings = scene_import_settings_data->current; + bool reshow_settings = false; if (main_settings.has("nodes/import_as_skeleton_bones")) { bool new_import_as_skeleton = main_settings["nodes/import_as_skeleton_bones"]; - if (new_import_as_skeleton != previous_import_as_skeleton) { - previous_import_as_skeleton = new_import_as_skeleton; - _re_import(); - open_settings(base_path); - } + reshow_settings = reshow_settings || (new_import_as_skeleton != previous_import_as_skeleton); + previous_import_as_skeleton = new_import_as_skeleton; + } + if (main_settings.has("animation/import_rest_as_RESET")) { + bool new_rest_as_reset = main_settings["animation/import_rest_as_RESET"]; + reshow_settings = reshow_settings || (new_rest_as_reset != previous_rest_as_reset); + previous_rest_as_reset = new_rest_as_reset; + } + if (reshow_settings) { + _re_import(); + open_settings(base_path); return; } for (const KeyValue &e : node_map) { @@ -688,6 +695,9 @@ void SceneImportSettingsDialog::open_settings(const String &p_path, bool p_for_a if (main_settings.has("nodes/import_as_skeleton_bones")) { previous_import_as_skeleton = main_settings["nodes/import_as_skeleton_bones"]; } + if (main_settings.has("animation/import_rest_as_RESET")) { + previous_rest_as_reset = main_settings["animation/import_rest_as_RESET"]; + } popup_centered_ratio(); _update_view_gizmos(); _update_camera(); diff --git a/editor/import/3d/scene_import_settings.h b/editor/import/3d/scene_import_settings.h index e1183dc5b0f9..f4954c41db1d 100644 --- a/editor/import/3d/scene_import_settings.h +++ b/editor/import/3d/scene_import_settings.h @@ -97,6 +97,7 @@ class SceneImportSettingsDialog : public ConfirmationDialog { Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE; bool animation_pingpong = false; bool previous_import_as_skeleton = false; + bool previous_rest_as_reset = false; Ref collider_mat;