Merge pull request #89629 from lyuma/import_rest_as_reset

Add option to import skeleton rest as RESET animation
This commit is contained in:
Rémi Verschelde 2024-03-24 01:19:49 +01:00
commit 8e6d011013
No known key found for this signature in database
GPG key ID: C3336907360768E1
5 changed files with 90 additions and 5 deletions

View file

@ -21,6 +21,9 @@
<member name="animation/import" type="bool" setter="" getter="" default="true"> <member name="animation/import" type="bool" setter="" getter="" default="true">
If [code]true[/code], import animations from the 3D scene. If [code]true[/code], import animations from the 3D scene.
</member> </member>
<member name="animation/import_rest_as_RESET" type="bool" setter="" getter="" default="false">
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.
</member>
<member name="animation/remove_immutable_tracks" type="bool" setter="" getter="" default="true"> <member name="animation/remove_immutable_tracks" type="bool" setter="" getter="" default="true">
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. 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.
</member> </member>

View file

@ -312,6 +312,71 @@ String ResourceImporterScene::get_preset_name(int p_idx) const {
return String(); return String();
} }
void ResourceImporterScene::_pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const {
if (p_options.has("animation/import_rest_as_RESET") && (bool)p_options["animation/import_rest_as_RESET"]) {
TypedArray<Node> 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<Animation> reset_anim;
for (int i = 0; i < anim_players.size(); i++) {
AnimationPlayer *player = cast_to<AnimationPlayer>(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<AnimationPlayer>(anim_players[0]);
reset_anim.instantiate();
Ref<AnimationLibrary> 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<Node> skeletons = p_scene->find_children("*", "Skeleton3D");
for (int i = 0; i < skeletons.size(); i++) {
Skeleton3D *skeleton = cast_to<Skeleton3D>(skeletons[i]);
NodePath skeleton_path = p_scene->get_path_to(skeleton);
HashSet<NodePath> existing_pos_tracks;
HashSet<NodePath> 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<StringName>{ 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) { static bool _teststr(const String &p_what, const String &p_str) {
String what = p_what; String what = p_what;
@ -1945,6 +2010,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30)); r_options->push_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/trimming"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true)); 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::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())); 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; return nullptr;
} }
_pre_fix_global(scene, p_options);
HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map; HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
List<Pair<NodePath, Node *>> node_renames; List<Pair<NodePath, Node *>> node_renames;
_pre_fix_node(scene, scene, collision_map, nullptr, 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<Ref<ImporterMesh>> scanned_meshes; HashSet<Ref<ImporterMesh>> scanned_meshes;
HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map; HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
Pair<PackedVector3Array, PackedInt32Array> occluder_arrays; Pair<PackedVector3Array, PackedInt32Array> occluder_arrays;

View file

@ -282,6 +282,7 @@ public:
// Import scenes *after* everything else (such as textures). // Import scenes *after* everything else (such as textures).
virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; } virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
void _pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const;
Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames); Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &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 *_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<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &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); Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &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);

View file

@ -433,13 +433,20 @@ void SceneImportSettingsDialog::_update_view_gizmos() {
return; return;
} }
const HashMap<StringName, Variant> &main_settings = scene_import_settings_data->current; const HashMap<StringName, Variant> &main_settings = scene_import_settings_data->current;
bool reshow_settings = false;
if (main_settings.has("nodes/import_as_skeleton_bones")) { if (main_settings.has("nodes/import_as_skeleton_bones")) {
bool new_import_as_skeleton = main_settings["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) { reshow_settings = reshow_settings || (new_import_as_skeleton != previous_import_as_skeleton);
previous_import_as_skeleton = new_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(); _re_import();
open_settings(base_path); open_settings(base_path);
}
return; return;
} }
for (const KeyValue<String, NodeData> &e : node_map) { for (const KeyValue<String, NodeData> &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")) { if (main_settings.has("nodes/import_as_skeleton_bones")) {
previous_import_as_skeleton = main_settings["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(); popup_centered_ratio();
_update_view_gizmos(); _update_view_gizmos();
_update_camera(); _update_camera();

View file

@ -97,6 +97,7 @@ class SceneImportSettingsDialog : public ConfirmationDialog {
Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE; Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE;
bool animation_pingpong = false; bool animation_pingpong = false;
bool previous_import_as_skeleton = false; bool previous_import_as_skeleton = false;
bool previous_rest_as_reset = false;
Ref<StandardMaterial3D> collider_mat; Ref<StandardMaterial3D> collider_mat;