Bläddra i källkod

Merge pull request #53885 from TokageItLab/fix-bone-animation-insertion

Fixed Pos/Rot/Scl 3D Tracks insertion in `SkeletonEditor`
Rémi Verschelde 3 år sedan
förälder
incheckning
c7b78b9538

+ 1 - 0
doc/classes/Animation.xml

@@ -226,6 +226,7 @@
 		<method name="find_track" qualifiers="const">
 			<return type="int" />
 			<argument index="0" name="path" type="NodePath" />
+			<argument index="1" name="type" type="int" enum="Animation.TrackType" />
 			<description>
 				Returns the index of the specified track. If the track is not found, return -1.
 			</description>

+ 20 - 43
editor/animation_track_editor.cpp

@@ -3487,7 +3487,7 @@ void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
 
 	for (const InsertData &E : insert_data) {
 		// Prevent insertion of multiple tracks.
-		if (E.path == p_id.path) {
+		if (E.path == p_id.path && E.type == p_id.type) {
 			return; // Already inserted a track this frame.
 		}
 	}
@@ -3537,7 +3537,11 @@ void AnimationTrackEditor::_insert_track(bool p_create_reset, bool p_create_bezi
 	}
 }
 
-void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_sub, const Transform3D &p_xform) {
+void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant p_value) {
+	ERR_FAIL_COND(!root);
+	ERR_FAIL_COND_MSG(
+			(p_type != Animation::TYPE_POSITION_3D && p_type != Animation::TYPE_ROTATION_3D && p_type != Animation::TYPE_SCALE_3D),
+			"Track type must be Position/Rotation/Scale 3D.");
 	if (!keying) {
 		return;
 	}
@@ -3545,7 +3549,6 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
 		return;
 	}
 
-	ERR_FAIL_COND(!root);
 	// Let's build a node path.
 	String path = root->get_path_to(p_node);
 	if (p_sub != "") {
@@ -3554,24 +3557,16 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
 
 	NodePath np = path;
 
-	int position_idx = -1;
-	int rotation_idx = -1;
-	int scale_idx = -1;
+	int track_idx = -1;
 
 	for (int i = 0; i < animation->get_track_count(); i++) {
 		if (animation->track_get_path(i) != np) {
 			continue;
 		}
-
-		if (animation->track_get_type(i) == Animation::TYPE_POSITION_3D) {
-			position_idx = i;
-		}
-		if (animation->track_get_type(i) == Animation::TYPE_ROTATION_3D) {
-			rotation_idx = i;
-		}
-		if (animation->track_get_type(i) == Animation::TYPE_SCALE_3D) {
-			scale_idx = i;
+		if (animation->track_get_type(i) != p_type) {
+			continue;
 		}
+		track_idx = i;
 	}
 
 	InsertData id;
@@ -3579,48 +3574,30 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
 	// TRANSLATORS: This describes the target of new animation track, will be inserted into another string.
 	id.query = vformat(TTR("node '%s'"), p_node->get_name());
 	id.advance = false;
-
-	{
-		id.track_idx = position_idx;
-		id.value = p_xform.origin;
-		id.type = Animation::TYPE_POSITION_3D;
-		_query_insert(id);
-	}
-	{
-		id.track_idx = rotation_idx;
-		id.value = p_xform.basis.get_rotation_quaternion();
-		id.type = Animation::TYPE_ROTATION_3D;
-		_query_insert(id);
-	}
-	{
-		id.track_idx = scale_idx;
-		id.value = p_xform.basis.get_scale();
-		id.type = Animation::TYPE_SCALE_3D;
-		_query_insert(id);
-	}
+	id.track_idx = track_idx;
+	id.value = p_value;
+	id.type = p_type;
+	_query_insert(id);
 }
 
-bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_sub) {
+bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type) {
+	ERR_FAIL_COND_V(!root, false);
 	if (!keying) {
 		return false;
 	}
 	if (!animation.is_valid()) {
 		return false;
 	}
-	if (!root) {
-		return false;
-	}
 
-	//let's build a node path
+	// Let's build a node path.
 	String path = root->get_path_to(p_node);
 	if (p_sub != "") {
 		path += ":" + p_sub;
 	}
-	int track_id = animation->find_track(path);
+
+	int track_id = animation->find_track(path, p_type);
 	if (track_id >= 0) {
-		if (animation->track_get_type(track_id) == Animation::TYPE_POSITION_3D || animation->track_get_type(track_id) == Animation::TYPE_ROTATION_3D || animation->track_get_type(track_id) == Animation::TYPE_SCALE_3D) {
-			return true;
-		}
+		return true;
 	}
 	return false;
 }

+ 2 - 2
editor/animation_track_editor.h

@@ -527,8 +527,8 @@ public:
 	void set_anim_pos(float p_pos);
 	void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false);
 	void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
-	void insert_transform_key(Node3D *p_node, const String &p_sub, const Transform3D &p_xform);
-	bool has_transform_track(Node3D *p_node, const String &p_sub);
+	void insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant p_value);
+	bool has_track(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type);
 	void make_insert_queue();
 	void commit_insert_queue();
 

+ 2 - 2
editor/animation_track_editor_plugins.cpp

@@ -419,7 +419,7 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se
 			// Go through other track to find if animation is set
 			String animation_path = get_animation()->track_get_path(get_track());
 			animation_path = animation_path.replace(":frame", ":animation");
-			int animation_track = get_animation()->find_track(animation_path);
+			int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
 			float track_time = get_animation()->track_get_key_time(get_track(), p_index);
 			int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
 			animation = get_animation()->track_get_key_value(animation_track, animaiton_index);
@@ -511,7 +511,7 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in
 			// Go through other track to find if animation is set
 			String animation_path = get_animation()->track_get_path(get_track());
 			animation_path = animation_path.replace(":frame", ":animation");
-			int animation_track = get_animation()->find_track(animation_path);
+			int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
 			float track_time = get_animation()->track_get_key_time(get_track(), p_index);
 			int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
 			animation = get_animation()->track_get_key_value(animation_track, animaiton_index);

+ 1 - 0
editor/icons/NewKey.svg

@@ -0,0 +1 @@
+<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-opacity=".9961"><path d="m13 9h-2v2h-2v2h2v2h2v-2h2v-2h-2z"/><path d="m10 9.723c-.596-.347-1-.985-1-1.723 0-1.104.896-2 2-2s2 .896 2 2h1v2h.445c.344-.591.555-1.268.555-2 0-2.209-1.791-4-4-4-1.822.002-3.414 1.235-3.869 3h-6.131v2h1v2h3v-2h2.133c.16.62.466 1.169.867 1.627v-.627h2z"/></g></svg>

+ 3 - 1
editor/inspector_dock.cpp

@@ -391,7 +391,9 @@ void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Tran
 	if (!s) {
 		return;
 	}
-	AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, p_key);
+	AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_POSITION_3D, p_key.origin);
+	AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_ROTATION_3D, p_key.basis.get_rotation_quaternion());
+	AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_SCALE_3D, p_key.basis.get_scale());
 }
 
 void InspectorDock::_warning_pressed() {

+ 190 - 78
editor/plugins/skeleton_3d_editor_plugin.cpp

@@ -54,6 +54,7 @@ void BoneTransformEditor::create_editors() {
 
 	enabled_checkbox = memnew(EditorPropertyCheck());
 	enabled_checkbox->set_label("Pose Enabled");
+	enabled_checkbox->set_selectable(false);
 	enabled_checkbox->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
 	section->get_vbox()->add_child(enabled_checkbox);
 
@@ -61,21 +62,27 @@ void BoneTransformEditor::create_editors() {
 	position_property = memnew(EditorPropertyVector3());
 	position_property->setup(-10000, 10000, 0.001f, true);
 	position_property->set_label("Position");
+	position_property->set_selectable(false);
 	position_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+	position_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
 	section->get_vbox()->add_child(position_property);
 
 	// Rotation property.
 	rotation_property = memnew(EditorPropertyQuaternion());
 	rotation_property->setup(-10000, 10000, 0.001f, true);
 	rotation_property->set_label("Rotation");
+	rotation_property->set_selectable(false);
 	rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+	rotation_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
 	section->get_vbox()->add_child(rotation_property);
 
 	// Scale property.
 	scale_property = memnew(EditorPropertyVector3());
 	scale_property->setup(-10000, 10000, 0.001f, true);
 	scale_property->set_label("Scale");
+	scale_property->set_selectable(false);
 	scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+	scale_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
 	section->get_vbox()->add_child(scale_property);
 
 	// Transform/Matrix section.
@@ -87,6 +94,7 @@ void BoneTransformEditor::create_editors() {
 	rest_matrix = memnew(EditorPropertyTransform3D());
 	rest_matrix->setup(-10000, 10000, 0.001f, true);
 	rest_matrix->set_label("Transform");
+	rest_matrix->set_selectable(false);
 	rest_section->get_vbox()->add_child(rest_matrix);
 }
 
@@ -116,6 +124,12 @@ BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
 	undo_redo = EditorNode::get_undo_redo();
 }
 
+void BoneTransformEditor::set_keyable(const bool p_keyable) {
+	position_property->set_keying(p_keyable);
+	rotation_property->set_keying(p_keyable);
+	scale_property->set_keying(p_keyable);
+}
+
 void BoneTransformEditor::set_target(const String &p_prop) {
 	enabled_checkbox->set_object_and_property(skeleton, p_prop + "enabled");
 	enabled_checkbox->update_property();
@@ -133,6 +147,23 @@ void BoneTransformEditor::set_target(const String &p_prop) {
 	rest_matrix->update_property();
 }
 
+void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) {
+	AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+	PackedStringArray split = p_path.split("/");
+	if (split.size() == 3 && split[0] == "bones") {
+		int bone_idx = split[1].to_int();
+		if (split[2] == "position") {
+			te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_POSITION_3D, skeleton->get(p_path));
+		}
+		if (split[2] == "rotation") {
+			te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_ROTATION_3D, skeleton->get(p_path));
+		}
+		if (split[2] == "scale") {
+			te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_SCALE_3D, skeleton->get(p_path));
+		}
+	}
+}
+
 void BoneTransformEditor::_update_properties() {
 	if (!skeleton) {
 		return;
@@ -141,30 +172,30 @@ void BoneTransformEditor::_update_properties() {
 	List<PropertyInfo> props;
 	skeleton->get_property_list(&props);
 	for (const PropertyInfo &E : props) {
-		PackedStringArray spr = E.name.split("/");
-		if (spr.size() == 3 && spr[0] == "bones") {
-			if (spr[1].to_int() == selected) {
-				if (spr[2] == "enabled") {
+		PackedStringArray split = E.name.split("/");
+		if (split.size() == 3 && split[0] == "bones") {
+			if (split[1].to_int() == selected) {
+				if (split[2] == "enabled") {
 					enabled_checkbox->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
 					enabled_checkbox->update_property();
 					enabled_checkbox->update();
 				}
-				if (spr[2] == "position") {
+				if (split[2] == "position") {
 					position_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
 					position_property->update_property();
 					position_property->update();
 				}
-				if (spr[2] == "rotation") {
+				if (split[2] == "rotation") {
 					rotation_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
 					rotation_property->update_property();
 					rotation_property->update();
 				}
-				if (spr[2] == "scale") {
+				if (split[2] == "scale") {
 					scale_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
 					scale_property->update_property();
 					scale_property->update();
 				}
-				if (spr[2] == "rest") {
+				if (split[2] == "rest") {
 					rest_matrix->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
 					rest_matrix->update_property();
 					rest_matrix->update();
@@ -178,12 +209,16 @@ Skeleton3DEditor *Skeleton3DEditor::singleton = nullptr;
 
 void Skeleton3DEditor::set_keyable(const bool p_keyable) {
 	keyable = p_keyable;
-	skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS, !p_keyable);
-	skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS_EXISTED, !p_keyable);
+	if (p_keyable) {
+		animation_hb->show();
+	} else {
+		animation_hb->hide();
+	}
 };
 
-void Skeleton3DEditor::set_rest_options_enabled(const bool p_rest_options_enabled) {
-	rest_options->get_popup()->set_item_disabled(REST_OPTION_POSE_TO_REST, !p_rest_options_enabled);
+void Skeleton3DEditor::set_bone_options_enabled(const bool p_bone_options_enabled) {
+	skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INIT_SELECTED_POSES, !p_bone_options_enabled);
+	skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_SELECTED_POSES_TO_RESTS, !p_bone_options_enabled);
 };
 
 void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
@@ -192,62 +227,76 @@ void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
 	}
 
 	switch (p_skeleton_option) {
-		case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {
-			create_physical_skeleton();
+		case SKELETON_OPTION_INIT_ALL_POSES: {
+			init_pose(true);
 			break;
 		}
-		case SKELETON_OPTION_INIT_POSE: {
-			init_pose();
+		case SKELETON_OPTION_INIT_SELECTED_POSES: {
+			init_pose(false);
 			break;
 		}
-		case SKELETON_OPTION_INSERT_KEYS: {
-			insert_keys(true);
+		case SKELETON_OPTION_ALL_POSES_TO_RESTS: {
+			pose_to_rest(true);
 			break;
 		}
-		case SKELETON_OPTION_INSERT_KEYS_EXISTED: {
-			insert_keys(false);
+		case SKELETON_OPTION_SELECTED_POSES_TO_RESTS: {
+			pose_to_rest(false);
+			break;
+		}
+		case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {
+			create_physical_skeleton();
 			break;
 		}
 	}
 }
 
-void Skeleton3DEditor::_on_click_rest_option(int p_rest_option) {
+void Skeleton3DEditor::init_pose(const bool p_all_bones) {
 	if (!skeleton) {
 		return;
 	}
-
-	switch (p_rest_option) {
-		case REST_OPTION_POSE_TO_REST: {
-			pose_to_rest();
-			break;
-		}
-	}
-}
-
-void Skeleton3DEditor::init_pose() {
 	const int bone_len = skeleton->get_bone_count();
 	if (!bone_len) {
 		return;
 	}
+
 	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
 	ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
-	for (int i = 0; i < bone_len; i++) {
-		Transform3D rest = skeleton->get_bone_rest(i);
-		ur->add_do_method(skeleton, "set_bone_pose_position", i, rest.origin);
-		ur->add_do_method(skeleton, "set_bone_pose_rotation", i, rest.basis.get_rotation_quaternion());
-		ur->add_do_method(skeleton, "set_bone_pose_scale", i, rest.basis.get_scale());
-		ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i));
-		ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));
-		ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));
+	if (p_all_bones) {
+		for (int i = 0; i < bone_len; i++) {
+			Transform3D rest = skeleton->get_bone_rest(i);
+			ur->add_do_method(skeleton, "set_bone_pose_position", i, rest.origin);
+			ur->add_do_method(skeleton, "set_bone_pose_rotation", i, rest.basis.get_rotation_quaternion());
+			ur->add_do_method(skeleton, "set_bone_pose_scale", i, rest.basis.get_scale());
+			ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i));
+			ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));
+			ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));
+		}
+	} else {
+		// Todo: Do method with multiple bone selection.
+		if (selected_bone == -1) {
+			ur->commit_action();
+			return;
+		}
+		Transform3D rest = skeleton->get_bone_rest(selected_bone);
+		ur->add_do_method(skeleton, "set_bone_pose_position", selected_bone, rest.origin);
+		ur->add_do_method(skeleton, "set_bone_pose_rotation", selected_bone, rest.basis.get_rotation_quaternion());
+		ur->add_do_method(skeleton, "set_bone_pose_scale", selected_bone, rest.basis.get_scale());
+		ur->add_undo_method(skeleton, "set_bone_pose_position", selected_bone, skeleton->get_bone_pose_position(selected_bone));
+		ur->add_undo_method(skeleton, "set_bone_pose_rotation", selected_bone, skeleton->get_bone_pose_rotation(selected_bone));
+		ur->add_undo_method(skeleton, "set_bone_pose_scale", selected_bone, skeleton->get_bone_pose_scale(selected_bone));
 	}
 	ur->commit_action();
 }
 
-void Skeleton3DEditor::insert_keys(bool p_all_bones) {
+void Skeleton3DEditor::insert_keys(const bool p_all_bones) {
 	if (!skeleton) {
 		return;
 	}
 
+	bool pos_enabled = key_loc_button->is_pressed();
+	bool rot_enabled = key_rot_button->is_pressed();
+	bool scl_enabled = key_scale_button->is_pressed();
+
 	int bone_len = skeleton->get_bone_count();
 	Node *root = EditorNode::get_singleton()->get_tree()->get_root();
 	String path = root->get_path_to(skeleton);
@@ -261,26 +310,44 @@ void Skeleton3DEditor::insert_keys(bool p_all_bones) {
 			continue;
 		}
 
-		if (!p_all_bones && !te->has_transform_track(skeleton, name)) {
-			continue;
+		if (pos_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_POSITION_3D))) {
+			te->insert_transform_key(skeleton, name, Animation::TYPE_POSITION_3D, skeleton->get_bone_pose_position(i));
+		}
+		if (rot_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_ROTATION_3D))) {
+			te->insert_transform_key(skeleton, name, Animation::TYPE_ROTATION_3D, skeleton->get_bone_pose_rotation(i));
+		}
+		if (scl_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_SCALE_3D))) {
+			te->insert_transform_key(skeleton, name, Animation::TYPE_SCALE_3D, skeleton->get_bone_pose_scale(i));
 		}
-
-		Transform3D tform = skeleton->get_bone_pose(i);
-		te->insert_transform_key(skeleton, name, tform);
 	}
 	te->commit_insert_queue();
 }
 
-void Skeleton3DEditor::pose_to_rest() {
+void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
 	if (!skeleton) {
 		return;
 	}
+	const int bone_len = skeleton->get_bone_count();
+	if (!bone_len) {
+		return;
+	}
 
-	// Todo: Do method with multiple bone selection.
 	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
 	ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
-	ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));
-	ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
+	if (p_all_bones) {
+		for (int i = 0; i < bone_len; i++) {
+			ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_pose(i));
+			ur->add_undo_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i));
+		}
+	} else {
+		// Todo: Do method with multiple bone selection.
+		if (selected_bone == -1) {
+			ur->commit_action();
+			return;
+		}
+		ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));
+		ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
+	}
 	ur->commit_action();
 }
 
@@ -466,11 +533,12 @@ void Skeleton3DEditor::_joint_tree_selection_changed() {
 			const String bone_path = "bones/" + itos(b_idx) + "/";
 
 			pose_editor->set_target(bone_path);
+			pose_editor->set_keyable(keyable);
 			selected_bone = b_idx;
 		}
 	}
 	pose_editor->set_visible(selected);
-	set_rest_options_enabled(selected);
+	set_bone_options_enabled(selected);
 	_update_properties();
 	_update_gizmo_visible();
 }
@@ -549,43 +617,82 @@ void Skeleton3DEditor::create_editors() {
 	skeleton_options->set_text(TTR("Skeleton3D"));
 	skeleton_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton3D"), SNAME("EditorIcons")));
 
-	skeleton_options->get_popup()->add_item(TTR("Init pose"), SKELETON_OPTION_INIT_POSE);
-	skeleton_options->get_popup()->add_item(TTR("Insert key of all bone poses"), SKELETON_OPTION_INSERT_KEYS);
-	skeleton_options->get_popup()->add_item(TTR("Insert key of bone poses already exist track"), SKELETON_OPTION_INSERT_KEYS_EXISTED);
-	skeleton_options->get_popup()->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
-
-	skeleton_options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));
+	// Skeleton options.
+	PopupMenu *p = skeleton_options->get_popup();
+	p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_all_poses", TTR("Init all Poses")), SKELETON_OPTION_INIT_ALL_POSES);
+	p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_selected_poses", TTR("Init selected Poses")), SKELETON_OPTION_INIT_SELECTED_POSES);
+	p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/all_poses_to_rests", TTR("Apply all poses to rests")), SKELETON_OPTION_ALL_POSES_TO_RESTS);
+	p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/selected_poses_to_rests", TTR("Apply selected poses to rests")), SKELETON_OPTION_SELECTED_POSES_TO_RESTS);
+	p->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
 
-	// Create Rest Option in Top Menu Bar.
-	rest_options = memnew(MenuButton);
-	ne->add_control_to_menu_panel(rest_options);
-
-	rest_options->set_text(TTR("Edit Rest"));
-	rest_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons")));
-
-	rest_options->get_popup()->add_item(TTR("Apply current pose to rest"), REST_OPTION_POSE_TO_REST);
-	rest_options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_rest_option));
-	set_rest_options_enabled(false);
+	p->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));
+	set_bone_options_enabled(false);
 
 	Vector<Variant> button_binds;
 	button_binds.resize(1);
 
 	edit_mode_button = memnew(Button);
 	ne->add_control_to_menu_panel(edit_mode_button);
-	edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
-	edit_mode_button->set_toggle_mode(true);
 	edit_mode_button->set_flat(true);
+	edit_mode_button->set_toggle_mode(true);
+	edit_mode_button->set_focus_mode(FOCUS_NONE);
+	edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
 	edit_mode_button->connect("toggled", callable_mp(this, &Skeleton3DEditor::edit_mode_toggled));
 
 	edit_mode = false;
 
-	set_keyable(te->has_keying());
-
 	if (skeleton) {
 		skeleton->add_child(handles_mesh_instance);
 		handles_mesh_instance->set_skeleton_path(NodePath(""));
 	}
 
+	// Keying buttons.
+	animation_hb = memnew(HBoxContainer);
+	ne->add_control_to_menu_panel(animation_hb);
+	animation_hb->add_child(memnew(VSeparator));
+	animation_hb->hide();
+
+	key_loc_button = memnew(Button);
+	key_loc_button->set_flat(true);
+	key_loc_button->set_toggle_mode(true);
+	key_loc_button->set_pressed(false);
+	key_loc_button->set_focus_mode(FOCUS_NONE);
+	key_loc_button->set_tooltip(TTR("Translation mask for inserting keys."));
+	animation_hb->add_child(key_loc_button);
+
+	key_rot_button = memnew(Button);
+	key_rot_button->set_flat(true);
+	key_rot_button->set_toggle_mode(true);
+	key_rot_button->set_pressed(true);
+	key_rot_button->set_focus_mode(FOCUS_NONE);
+	key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys."));
+	animation_hb->add_child(key_rot_button);
+
+	key_scale_button = memnew(Button);
+	key_scale_button->set_flat(true);
+	key_scale_button->set_toggle_mode(true);
+	key_scale_button->set_pressed(false);
+	key_scale_button->set_focus_mode(FOCUS_NONE);
+	key_scale_button->set_tooltip(TTR("Scale mask for inserting keys."));
+	animation_hb->add_child(key_scale_button);
+
+	key_insert_button = memnew(Button);
+	key_insert_button->set_flat(true);
+	key_insert_button->set_focus_mode(FOCUS_NONE);
+	key_insert_button->connect("pressed", callable_mp(this, &Skeleton3DEditor::insert_keys), varray(false));
+	key_insert_button->set_tooltip(TTR("Insert key of bone poses already exist track."));
+	key_insert_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_to_existing_tracks", TTR("Insert Key (Existing Tracks)"), KEY_INSERT));
+	animation_hb->add_child(key_insert_button);
+
+	key_insert_all_button = memnew(Button);
+	key_insert_all_button->set_flat(true);
+	key_insert_all_button->set_focus_mode(FOCUS_NONE);
+	key_insert_all_button->connect("pressed", callable_mp(this, &Skeleton3DEditor::insert_keys), varray(true));
+	key_insert_all_button->set_tooltip(TTR("Insert key of all bone poses."));
+	key_insert_all_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_of_all_bones", TTR("Insert Key (All Bones)"), KEY_MASK_CMD + KEY_INSERT));
+	animation_hb->add_child(key_insert_all_button);
+
+	// Bone tree.
 	const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
 
 	EditorInspectorSection *bones_section = memnew(EditorInspectorSection);
@@ -613,12 +720,19 @@ void Skeleton3DEditor::create_editors() {
 	pose_editor->set_label(TTR("Bone Transform"));
 	pose_editor->set_visible(false);
 	add_child(pose_editor);
+
+	set_keyable(te->has_keying());
 }
 
 void Skeleton3DEditor::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_READY: {
-			edit_mode_button->set_icon(get_theme_icon("ToolBoneSelect", "EditorIcons"));
+			edit_mode_button->set_icon(get_theme_icon(SNAME("ToolBoneSelect"), SNAME("EditorIcons")));
+			key_loc_button->set_icon(get_theme_icon(SNAME("KeyPosition"), SNAME("EditorIcons")));
+			key_rot_button->set_icon(get_theme_icon(SNAME("KeyRotation"), SNAME("EditorIcons")));
+			key_scale_button->set_icon(get_theme_icon(SNAME("KeyScale"), SNAME("EditorIcons")));
+			key_insert_button->set_icon(get_theme_icon(SNAME("Key"), SNAME("EditorIcons")));
+			key_insert_all_button->set_icon(get_theme_icon(SNAME("NewKey"), SNAME("EditorIcons")));
 			get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
 			break;
 		}
@@ -643,7 +757,6 @@ void Skeleton3DEditor::_node_removed(Node *p_node) {
 	if (skeleton && p_node == skeleton) {
 		skeleton = nullptr;
 		skeleton_options->hide();
-		rest_options->hide();
 	}
 
 	_update_properties();
@@ -655,7 +768,6 @@ void Skeleton3DEditor::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &Skeleton3DEditor::_joint_tree_rmb_select);
 	ClassDB::bind_method(D_METHOD("_update_properties"), &Skeleton3DEditor::_update_properties);
 	ClassDB::bind_method(D_METHOD("_on_click_skeleton_option"), &Skeleton3DEditor::_on_click_skeleton_option);
-	ClassDB::bind_method(D_METHOD("_on_click_rest_option"), &Skeleton3DEditor::_on_click_rest_option);
 
 	ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw);
 	ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw);
@@ -866,6 +978,11 @@ Skeleton3DEditor::~Skeleton3DEditor() {
 
 	Node3DEditor *ne = Node3DEditor::get_singleton();
 
+	if (animation_hb) {
+		ne->remove_control_from_menu_panel(animation_hb);
+		memdelete(animation_hb);
+	}
+
 	if (separator) {
 		ne->remove_control_from_menu_panel(separator);
 		memdelete(separator);
@@ -876,11 +993,6 @@ Skeleton3DEditor::~Skeleton3DEditor() {
 		memdelete(skeleton_options);
 	}
 
-	if (rest_options) {
-		ne->remove_control_from_menu_panel(rest_options);
-		memdelete(rest_options);
-	}
-
 	if (edit_mode_button) {
 		ne->remove_control_from_menu_panel(edit_mode_button);
 		memdelete(edit_mode_button);

+ 20 - 18
editor/plugins/skeleton_3d_editor_plugin.h

@@ -45,7 +45,6 @@ class Joint;
 class PhysicalBone3D;
 class Skeleton3DEditorPlugin;
 class Button;
-class CheckBox;
 
 class BoneTransformEditor : public VBoxContainer {
 	GDCLASS(BoneTransformEditor, VBoxContainer);
@@ -67,9 +66,6 @@ class BoneTransformEditor : public VBoxContainer {
 
 	UndoRedo *undo_redo;
 
-	// Button *key_button = nullptr;
-
-	bool keyable = false;
 	bool toggle_enabled = false;
 	bool updating = false;
 
@@ -79,6 +75,8 @@ class BoneTransformEditor : public VBoxContainer {
 
 	void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing);
 
+	void _property_keyed(const String &p_path, bool p_advance);
+
 protected:
 	void _notification(int p_what);
 
@@ -88,6 +86,7 @@ public:
 	// Which transform target to modify.
 	void set_target(const String &p_prop);
 	void set_label(const String &p_label) { label = p_label; }
+	void set_keyable(const bool p_keyable);
 
 	void _update_properties();
 };
@@ -98,14 +97,11 @@ class Skeleton3DEditor : public VBoxContainer {
 	friend class Skeleton3DEditorPlugin;
 
 	enum SkeletonOption {
-		SKELETON_OPTION_INIT_POSE,
-		SKELETON_OPTION_INSERT_KEYS,
-		SKELETON_OPTION_INSERT_KEYS_EXISTED,
-		SKELETON_OPTION_CREATE_PHYSICAL_SKELETON
-	};
-
-	enum RestOption {
-		REST_OPTION_POSE_TO_REST
+		SKELETON_OPTION_INIT_ALL_POSES,
+		SKELETON_OPTION_INIT_SELECTED_POSES,
+		SKELETON_OPTION_ALL_POSES_TO_RESTS,
+		SKELETON_OPTION_SELECTED_POSES_TO_RESTS,
+		SKELETON_OPTION_CREATE_PHYSICAL_SKELETON,
 	};
 
 	struct BoneInfo {
@@ -124,11 +120,17 @@ class Skeleton3DEditor : public VBoxContainer {
 
 	VSeparator *separator;
 	MenuButton *skeleton_options = nullptr;
-	MenuButton *rest_options = nullptr;
 	Button *edit_mode_button;
 
 	bool edit_mode = false;
 
+	HBoxContainer *animation_hb;
+	Button *key_loc_button;
+	Button *key_rot_button;
+	Button *key_scale_button;
+	Button *key_insert_button;
+	Button *key_insert_all_button;
+
 	EditorFileDialog *file_dialog = nullptr;
 
 	bool keyable;
@@ -136,7 +138,6 @@ class Skeleton3DEditor : public VBoxContainer {
 	static Skeleton3DEditor *singleton;
 
 	void _on_click_skeleton_option(int p_skeleton_option);
-	void _on_click_rest_option(int p_rest_option);
 	void _file_selected(const String &p_file);
 	TreeItem *_find(TreeItem *p_node, const NodePath &p_path);
 	void edit_mode_toggled(const bool pressed);
@@ -148,9 +149,10 @@ class Skeleton3DEditor : public VBoxContainer {
 
 	void create_editors();
 
-	void init_pose();
-	void insert_keys(bool p_all_bones);
-	void pose_to_rest();
+	void init_pose(const bool p_all_bones);
+	void pose_to_rest(const bool p_all_bones);
+
+	void insert_keys(const bool p_all_bones);
 
 	void create_physical_skeleton();
 	PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
@@ -160,7 +162,7 @@ class Skeleton3DEditor : public VBoxContainer {
 	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
 
 	void set_keyable(const bool p_keyable);
-	void set_rest_options_enabled(const bool p_rest_options_enabled);
+	void set_bone_options_enabled(const bool p_bone_options_enabled);
 
 	// Handle.
 	MeshInstance3D *handles_mesh_instance;

+ 1 - 1
modules/gltf/gltf_document.cpp

@@ -6460,7 +6460,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 				for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
 					String shape_name = mesh->get_blend_shape_name(shape_i);
 					NodePath shape_path = String(path) + ":" + shape_name;
-					int32_t shape_track_i = animation->find_track(shape_path);
+					int32_t shape_track_i = animation->find_track(shape_path, Animation::TYPE_BLEND_SHAPE);
 					if (shape_track_i == -1) {
 						GLTFAnimation::Channel<float> weight;
 						weight.interpolation = GLTFAnimation::INTERP_LINEAR;

+ 11 - 11
scene/3d/skeleton_3d.cpp

@@ -178,32 +178,32 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
 }
 
 void Skeleton3D::_validate_property(PropertyInfo &property) const {
-	PackedStringArray spr = property.name.split("/");
-	if (spr.size() == 3 && spr[0] == "bones") {
-		if (spr[2] == "rest") {
+	PackedStringArray split = property.name.split("/");
+	if (split.size() == 3 && split[0] == "bones") {
+		if (split[2] == "rest") {
 			property.usage |= PROPERTY_USAGE_READ_ONLY;
 		}
 		if (is_show_rest_only()) {
-			if (spr[2] == "enabled") {
+			if (split[2] == "enabled") {
 				property.usage |= PROPERTY_USAGE_READ_ONLY;
 			}
-			if (spr[2] == "position") {
+			if (split[2] == "position") {
 				property.usage |= PROPERTY_USAGE_READ_ONLY;
 			}
-			if (spr[2] == "rotation") {
+			if (split[2] == "rotation") {
 				property.usage |= PROPERTY_USAGE_READ_ONLY;
 			}
-			if (spr[2] == "scale") {
+			if (split[2] == "scale") {
 				property.usage |= PROPERTY_USAGE_READ_ONLY;
 			}
-		} else if (!is_bone_enabled(spr[1].to_int())) {
-			if (spr[2] == "position") {
+		} else if (!is_bone_enabled(split[1].to_int())) {
+			if (split[2] == "position") {
 				property.usage |= PROPERTY_USAGE_READ_ONLY;
 			}
-			if (spr[2] == "rotation") {
+			if (split[2] == "rotation") {
 				property.usage |= PROPERTY_USAGE_READ_ONLY;
 			}
-			if (spr[2] == "scale") {
+			if (split[2] == "scale") {
 				property.usage |= PROPERTY_USAGE_READ_ONLY;
 			}
 		}

+ 3 - 3
scene/resources/animation.cpp

@@ -837,9 +837,9 @@ NodePath Animation::track_get_path(int p_track) const {
 	return tracks[p_track]->path;
 }
 
-int Animation::find_track(const NodePath &p_path) const {
+int Animation::find_track(const NodePath &p_path, const TrackType p_type) const {
 	for (int i = 0; i < tracks.size(); i++) {
-		if (tracks[i]->path == p_path) {
+		if (tracks[i]->path == p_path && tracks[i]->type == p_type) {
 			return i;
 		}
 	};
@@ -3027,7 +3027,7 @@ void Animation::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("track_get_type", "track_idx"), &Animation::track_get_type);
 	ClassDB::bind_method(D_METHOD("track_get_path", "track_idx"), &Animation::track_get_path);
 	ClassDB::bind_method(D_METHOD("track_set_path", "track_idx", "path"), &Animation::track_set_path);
-	ClassDB::bind_method(D_METHOD("find_track", "path"), &Animation::find_track);
+	ClassDB::bind_method(D_METHOD("find_track", "path", "type"), &Animation::find_track);
 
 	ClassDB::bind_method(D_METHOD("track_move_up", "track_idx"), &Animation::track_move_up);
 	ClassDB::bind_method(D_METHOD("track_move_down", "track_idx"), &Animation::track_move_down);

+ 1 - 2
scene/resources/animation.h

@@ -281,8 +281,7 @@ public:
 
 	void track_set_path(int p_track, const NodePath &p_path);
 	NodePath track_get_path(int p_track) const;
-	int find_track(const NodePath &p_path) const;
-	// transform
+	int find_track(const NodePath &p_path, const TrackType p_type) const;
 
 	void track_move_up(int p_track);
 	void track_move_down(int p_track);