Browse Source

Merge pull request #53689 from reduz/remove-animation-transform3d-track

Rémi Verschelde 3 years ago
parent
commit
3e86ca5586

+ 33 - 25
doc/classes/Animation.xml

@@ -253,6 +253,14 @@
 				Returns the arguments values to be called on a method track for a given key in a given track.
 			</description>
 		</method>
+		<method name="position_track_insert_key">
+			<return type="int" />
+			<argument index="0" name="track_idx" type="int" />
+			<argument index="1" name="time" type="float" />
+			<argument index="2" name="position" type="Vector3" />
+			<description>
+			</description>
+		</method>
 		<method name="remove_track">
 			<return type="void" />
 			<argument index="0" name="track_idx" type="int" />
@@ -260,6 +268,22 @@
 				Removes a track by specifying the track index.
 			</description>
 		</method>
+		<method name="rotation_track_insert_key">
+			<return type="int" />
+			<argument index="0" name="track_idx" type="int" />
+			<argument index="1" name="time" type="float" />
+			<argument index="2" name="rotation" type="Quaternion" />
+			<description>
+			</description>
+		</method>
+		<method name="scale_track_insert_key">
+			<return type="int" />
+			<argument index="0" name="track_idx" type="int" />
+			<argument index="1" name="time" type="float" />
+			<argument index="2" name="scale" type="Vector3" />
+			<description>
+			</description>
+		</method>
 		<method name="track_find_key" qualifiers="const">
 			<return type="int" />
 			<argument index="0" name="track_idx" type="int" />
@@ -466,25 +490,6 @@
 				Swaps the track [code]idx[/code]'s index position with the track [code]with_idx[/code].
 			</description>
 		</method>
-		<method name="transform_track_insert_key">
-			<return type="int" />
-			<argument index="0" name="track_idx" type="int" />
-			<argument index="1" name="time" type="float" />
-			<argument index="2" name="location" type="Vector3" />
-			<argument index="3" name="rotation" type="Quaternion" />
-			<argument index="4" name="scale" type="Vector3" />
-			<description>
-				Insert a transform key for a transform track.
-			</description>
-		</method>
-		<method name="transform_track_interpolate" qualifiers="const">
-			<return type="Array" />
-			<argument index="0" name="track_idx" type="int" />
-			<argument index="1" name="time_sec" type="float" />
-			<description>
-				Returns the interpolated value of a transform track at a given time (in seconds). An array consisting of 3 elements: position ([Vector3]), rotation ([Quaternion]) and scale ([Vector3]).
-			</description>
-		</method>
 		<method name="value_track_get_key_indices" qualifiers="const">
 			<return type="PackedInt32Array" />
 			<argument index="0" name="track_idx" type="int" />
@@ -541,19 +546,22 @@
 		<constant name="TYPE_VALUE" value="0" enum="TrackType">
 			Value tracks set values in node properties, but only those which can be Interpolated.
 		</constant>
-		<constant name="TYPE_TRANSFORM3D" value="1" enum="TrackType">
-			Transform3D tracks are used to change node local transforms or skeleton pose bones of 3D nodes. Transitions are interpolated.
+		<constant name="TYPE_POSITION_3D" value="1" enum="TrackType">
+		</constant>
+		<constant name="TYPE_ROTATION_3D" value="2" enum="TrackType">
+		</constant>
+		<constant name="TYPE_SCALE_3D" value="3" enum="TrackType">
 		</constant>
-		<constant name="TYPE_METHOD" value="2" enum="TrackType">
+		<constant name="TYPE_METHOD" value="4" enum="TrackType">
 			Method tracks call functions with given arguments per key.
 		</constant>
-		<constant name="TYPE_BEZIER" value="3" enum="TrackType">
+		<constant name="TYPE_BEZIER" value="5" enum="TrackType">
 			Bezier tracks are used to interpolate a value using custom curves. They can also be used to animate sub-properties of vectors and colors (e.g. alpha value of a [Color]).
 		</constant>
-		<constant name="TYPE_AUDIO" value="4" enum="TrackType">
+		<constant name="TYPE_AUDIO" value="6" enum="TrackType">
 			Audio tracks are used to play an audio stream with either type of [AudioStreamPlayer]. The stream can be trimmed and previewed in the animation.
 		</constant>
-		<constant name="TYPE_ANIMATION" value="5" enum="TrackType">
+		<constant name="TYPE_ANIMATION" value="7" enum="TrackType">
 			Animation tracks play animations in other [AnimationPlayer] nodes.
 		</constant>
 		<constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType">

+ 2 - 2
doc/classes/AnimationTree.xml

@@ -22,7 +22,7 @@
 		<method name="get_root_motion_transform" qualifiers="const">
 			<return type="Transform3D" />
 			<description>
-				Retrieve the motion of the [member root_motion_track] as a [Transform3D] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_TRANSFORM3D], returns an identity transformation. See also [member root_motion_track] and [RootMotionView].
+				Retrieve the motion of the [member root_motion_track] as a [Transform3D] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_SCALE_3D] or [constant Animation.TYPE_ROTATION_3D], returns an identity transformation. See also [member root_motion_track] and [RootMotionView].
 			</description>
 		</method>
 		<method name="rename_parameter">
@@ -45,7 +45,7 @@
 		</member>
 		<member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath(&quot;&quot;)">
 			The path to the Animation track used for root motion. Paths must be valid scene-tree paths to a node, and must be specified starting from the parent node of the node that will reproduce the animation. To specify a track that controls properties or bones, append its name after the path, separated by [code]":"[/code]. For example, [code]"character/skeleton:ankle"[/code] or [code]"character/mesh:transform/local"[/code].
-			If the track has type [constant Animation.TYPE_TRANSFORM3D], the transformation will be cancelled visually, and the animation will appear to stay in place. See also [method get_root_motion_transform] and [RootMotionView].
+			If the track has type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_ROTATION_3D] or [constant Animation.TYPE_SCALE_3D] the transformation will be cancelled visually, and the animation will appear to stay in place. See also [method get_root_motion_transform] and [RootMotionView].
 		</member>
 		<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
 			The root animation node of this [AnimationTree]. See [AnimationNode].

+ 34 - 4
doc/classes/Skeleton3D.xml

@@ -144,6 +144,24 @@
 				Returns the pose transform of the specified bone. Pose is applied on top of the custom pose, which is applied on top the rest pose.
 			</description>
 		</method>
+		<method name="get_bone_pose_position" qualifiers="const">
+			<return type="Vector3" />
+			<argument index="0" name="bone_idx" type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="get_bone_pose_rotation" qualifiers="const">
+			<return type="Quaternion" />
+			<argument index="0" name="bone_idx" type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="get_bone_pose_scale" qualifiers="const">
+			<return type="Vector3" />
+			<argument index="0" name="bone_idx" type="int" />
+			<description>
+			</description>
+		</method>
 		<method name="get_bone_rest" qualifiers="const">
 			<return type="Transform3D" />
 			<argument index="0" name="bone_idx" type="int" />
@@ -337,13 +355,25 @@
 				[b]Note:[/b] [code]parent_idx[/code] must be less than [code]bone_idx[/code].
 			</description>
 		</method>
-		<method name="set_bone_pose">
+		<method name="set_bone_pose_position">
 			<return type="void" />
 			<argument index="0" name="bone_idx" type="int" />
-			<argument index="1" name="pose" type="Transform3D" />
+			<argument index="1" name="position" type="Vector3" />
+			<description>
+			</description>
+		</method>
+		<method name="set_bone_pose_rotation">
+			<return type="void" />
+			<argument index="0" name="bone_idx" type="int" />
+			<argument index="1" name="rotation" type="Quaternion" />
+			<description>
+			</description>
+		</method>
+		<method name="set_bone_pose_scale">
+			<return type="void" />
+			<argument index="0" name="bone_idx" type="int" />
+			<argument index="1" name="scale" type="Vector3" />
 			<description>
-				Sets the pose transform for bone [code]bone_idx[/code].
-				[b]Note:[/b] The pose transform needs to be in bone space. Use [method world_transform_to_global_pose] to convert a world transform, like one you can get from a [Node3D], to bone space.
 			</description>
 		</method>
 		<method name="set_bone_rest">

+ 202 - 94
editor/animation_track_editor.cpp

@@ -165,20 +165,38 @@ public:
 		}
 
 		switch (animation->track_get_type(track)) {
-			case Animation::TYPE_TRANSFORM3D: {
-				Dictionary d_old = animation->track_get_key_value(track, key);
-				Dictionary d_new = d_old.duplicate();
-				d_new[p_name] = p_value;
-				setting = true;
-				undo_redo->create_action(TTR("Anim Change Transform"));
-				undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
-				undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
-				undo_redo->add_do_method(this, "_update_obj", animation);
-				undo_redo->add_undo_method(this, "_update_obj", animation);
-				undo_redo->commit_action();
+			case Animation::TYPE_POSITION_3D:
+			case Animation::TYPE_ROTATION_3D:
+			case Animation::TYPE_SCALE_3D: {
+				if (name == "position" || name == "rotation" || name == "scale") {
+					Variant old = animation->track_get_key_value(track, key);
+					setting = true;
+					String chan;
+					switch (animation->track_get_type(track)) {
+						case Animation::TYPE_POSITION_3D:
+							chan = "Position3D";
+							break;
+						case Animation::TYPE_ROTATION_3D:
+							chan = "Rotation3D";
+							break;
+						case Animation::TYPE_SCALE_3D:
+							chan = "Scale3D";
+							break;
+						default: {
+						}
+					}
+
+					undo_redo->create_action(vformat(TTR("Anim Change %s"), chan));
+					undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value);
+					undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
+					undo_redo->add_do_method(this, "_update_obj", animation);
+					undo_redo->add_undo_method(this, "_update_obj", animation);
+					undo_redo->commit_action();
+
+					setting = false;
+					return true;
+				}
 
-				setting = false;
-				return true;
 			} break;
 			case Animation::TYPE_VALUE: {
 				if (name == "value") {
@@ -412,12 +430,13 @@ public:
 		}
 
 		switch (animation->track_get_type(track)) {
-			case Animation::TYPE_TRANSFORM3D: {
-				Dictionary d = animation->track_get_key_value(track, key);
-				ERR_FAIL_COND_V(!d.has(name), false);
-				r_ret = d[p_name];
-				return true;
-
+			case Animation::TYPE_POSITION_3D:
+			case Animation::TYPE_ROTATION_3D:
+			case Animation::TYPE_SCALE_3D: {
+				if (name == "position" || name == "rotation" || name == "scale") {
+					r_ret = animation->track_get_key_value(track, key);
+					return true;
+				}
 			} break;
 			case Animation::TYPE_VALUE: {
 				if (name == "value") {
@@ -523,11 +542,14 @@ public:
 		}
 
 		switch (animation->track_get_type(track)) {
-			case Animation::TYPE_TRANSFORM3D: {
-				p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
-				p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
+			case Animation::TYPE_POSITION_3D: {
+				p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
+			} break;
+			case Animation::TYPE_ROTATION_3D: {
+				p_list->push_back(PropertyInfo(Variant::VECTOR3, "rotation"));
+			} break;
+			case Animation::TYPE_SCALE_3D: {
 				p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
-
 			} break;
 			case Animation::TYPE_VALUE: {
 				Variant v = animation->track_get_key_value(track, key);
@@ -779,17 +801,31 @@ public:
 				}
 
 				switch (animation->track_get_type(track)) {
-					case Animation::TYPE_TRANSFORM3D: {
-						Dictionary d_old = animation->track_get_key_value(track, key);
-						Dictionary d_new = d_old.duplicate();
-						d_new[p_name] = p_value;
-
+					case Animation::TYPE_POSITION_3D:
+					case Animation::TYPE_ROTATION_3D:
+					case Animation::TYPE_SCALE_3D: {
+						Variant old = animation->track_get_key_value(track, key);
 						if (!setting) {
+							String chan;
+							switch (animation->track_get_type(track)) {
+								case Animation::TYPE_POSITION_3D:
+									chan = "Position3D";
+									break;
+								case Animation::TYPE_ROTATION_3D:
+									chan = "Rotation3D";
+									break;
+								case Animation::TYPE_SCALE_3D:
+									chan = "Scale3D";
+									break;
+								default: {
+								}
+							}
+
 							setting = true;
-							undo_redo->create_action(TTR("Anim Multi Change Transform"));
+							undo_redo->create_action(vformat(TTR("Anim Multi Change %s"), chan));
 						}
-						undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
-						undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
+						undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value);
+						undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
 						update_obj = true;
 					} break;
 					case Animation::TYPE_VALUE: {
@@ -1009,11 +1045,13 @@ public:
 				}
 
 				switch (animation->track_get_type(track)) {
-					case Animation::TYPE_TRANSFORM3D: {
-						Dictionary d = animation->track_get_key_value(track, key);
-						ERR_FAIL_COND_V(!d.has(name), false);
-						r_ret = d[p_name];
-						return true;
+					case Animation::TYPE_POSITION_3D:
+					case Animation::TYPE_ROTATION_3D:
+					case Animation::TYPE_SCALE_3D: {
+						if (name == "position" || name == "rotation" || name == "scale") {
+							r_ret = animation->track_get_key_value(track, key);
+							return true;
+						}
 
 					} break;
 					case Animation::TYPE_VALUE: {
@@ -1159,9 +1197,13 @@ public:
 
 		if (same_track_type) {
 			switch (animation->track_get_type(first_track)) {
-				case Animation::TYPE_TRANSFORM3D: {
-					p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
-					p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
+				case Animation::TYPE_POSITION_3D: {
+					p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
+				} break;
+				case Animation::TYPE_ROTATION_3D: {
+					p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale"));
+				} break;
+				case Animation::TYPE_SCALE_3D: {
 					p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
 				} break;
 				case Animation::TYPE_VALUE: {
@@ -1359,7 +1401,9 @@ void AnimationTimelineEdit::_notification(int p_what) {
 
 		add_track->get_popup()->clear();
 		add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), TTR("Property Track"));
-		add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")), TTR("3D Transform Track"));
+		add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track"));
+		add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track"));
+		add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track"));
 		add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track"));
 		add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track"));
 		add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track"));
@@ -1828,9 +1872,11 @@ void AnimationTrackEdit::_notification(int p_what) {
 		Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
 		int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
 		Color color = get_theme_color(SNAME("font_color"), SNAME("Label"));
-		Ref<Texture2D> type_icons[6] = {
+		Ref<Texture2D> type_icons[8] = {
 			get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
-			get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")),
+			get_theme_icon(SNAME("KeyTrackPosition"), SNAME("EditorIcons")),
+			get_theme_icon(SNAME("KeyTrackRotation"), SNAME("EditorIcons")),
+			get_theme_icon(SNAME("KeyTrackScale"), SNAME("EditorIcons")),
 			get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
 			get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
 			get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
@@ -2021,7 +2067,7 @@ void AnimationTrackEdit::_notification(int p_what) {
 				interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
 				interp_mode_rect.size = icon->get_size();
 
-				if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+				if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
 					draw_texture(icon, interp_mode_rect.position);
 				}
 				// Make it easier to click.
@@ -2031,7 +2077,7 @@ void AnimationTrackEdit::_notification(int p_what) {
 				ofs += icon->get_width() + hsep;
 				interp_mode_rect.size.x += hsep;
 
-				if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+				if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
 					draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
 					interp_mode_rect.size.x += down_icon->get_width();
 				} else {
@@ -2054,7 +2100,7 @@ void AnimationTrackEdit::_notification(int p_what) {
 				loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
 				loop_mode_rect.size = icon->get_size();
 
-				if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+				if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
 					draw_texture(icon, loop_mode_rect.position);
 				}
 
@@ -2064,7 +2110,7 @@ void AnimationTrackEdit::_notification(int p_what) {
 				ofs += icon->get_width() + hsep;
 				loop_mode_rect.size.x += hsep;
 
-				if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+				if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
 					draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
 					loop_mode_rect.size.x += down_icon->get_width();
 				} else {
@@ -2284,9 +2330,11 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati
 	track = p_track;
 	update();
 
-	Ref<Texture2D> type_icons[6] = {
+	Ref<Texture2D> type_icons[8] = {
 		get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
-		get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")),
+		get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")),
+		get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")),
+		get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")),
 		get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
 		get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
 		get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
@@ -2456,17 +2504,17 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
 		if (key_idx != -1) {
 			String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n";
 			switch (animation->track_get_type(track)) {
-				case Animation::TYPE_TRANSFORM3D: {
-					Dictionary d = animation->track_get_key_value(track, key_idx);
-					if (d.has("location")) {
-						text += "Pos: " + String(d["location"]) + "\n";
-					}
-					if (d.has("rotation")) {
-						text += "Rot: " + String(d["rotation"]) + "\n";
-					}
-					if (d.has("scale")) {
-						text += "Scale: " + String(d["scale"]) + "\n";
-					}
+				case Animation::TYPE_POSITION_3D: {
+					Vector3 t = animation->track_get_key_value(track, key_idx);
+					text += "Position: " + String(t) + "\n";
+				} break;
+				case Animation::TYPE_ROTATION_3D: {
+					Quaternion t = animation->track_get_key_value(track, key_idx);
+					text += "Rotation: " + String(t) + "\n";
+				} break;
+				case Animation::TYPE_SCALE_3D: {
+					Vector3 t = animation->track_get_key_value(track, key_idx);
+					text += "Scale: " + String(t) + "\n";
 				} break;
 				case Animation::TYPE_VALUE: {
 					const Variant &v = animation->track_get_key_value(track, key_idx);
@@ -3323,7 +3371,11 @@ static bool track_type_is_resettable(Animation::TrackType p_type) {
 			[[fallthrough]];
 		case Animation::TYPE_BEZIER:
 			[[fallthrough]];
-		case Animation::TYPE_TRANSFORM3D:
+		case Animation::TYPE_POSITION_3D:
+			[[fallthrough]];
+		case Animation::TYPE_ROTATION_3D:
+			[[fallthrough]];
+		case Animation::TYPE_SCALE_3D:
 			return true;
 		default:
 			return false;
@@ -3483,33 +3535,50 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
 
 	NodePath np = path;
 
-	int track_idx = -1;
+	int position_idx = -1;
+	int rotation_idx = -1;
+	int scale_idx = -1;
 
 	for (int i = 0; i < animation->get_track_count(); i++) {
-		if (animation->track_get_type(i) != Animation::TYPE_TRANSFORM3D) {
-			continue;
-		}
 		if (animation->track_get_path(i) != np) {
 			continue;
 		}
 
-		track_idx = i;
-		break;
+		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;
+		}
 	}
 
 	InsertData id;
-	Dictionary val;
-
 	id.path = np;
-	id.track_idx = track_idx;
-	id.value = p_xform;
-	id.type = Animation::TYPE_TRANSFORM3D;
 	// 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;
 
-	// Dialog insert.
-	_query_insert(id);
+	{
+		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);
+	}
 }
 
 bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_sub) {
@@ -3530,7 +3599,7 @@ bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_s
 	}
 	int track_id = animation->find_track(path);
 	if (track_id >= 0) {
-		if (animation->track_get_type(track_id) == Animation::TYPE_TRANSFORM3D) {
+		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;
 		}
 	}
@@ -3977,18 +4046,13 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
 	Variant value;
 
 	switch (p_id.type) {
+		case Animation::TYPE_POSITION_3D:
+		case Animation::TYPE_ROTATION_3D:
+		case Animation::TYPE_SCALE_3D:
 		case Animation::TYPE_VALUE: {
 			value = p_id.value;
 
 		} break;
-		case Animation::TYPE_TRANSFORM3D: {
-			Transform3D tr = p_id.value;
-			Dictionary d;
-			d["location"] = tr.origin;
-			d["scale"] = tr.basis.get_scale();
-			d["rotation"] = Quaternion(tr.basis);
-			value = d;
-		} break;
 		case Animation::TYPE_BEZIER: {
 			Array array;
 			array.resize(5);
@@ -4404,8 +4468,8 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
 	ERR_FAIL_COND(!node);
 	NodePath path_to = root->get_path_to(node);
 
-	if (adding_track_type == Animation::TYPE_TRANSFORM3D && !node->is_class("Node3D")) {
-		EditorNode::get_singleton()->show_warning(TTR("Transform3D tracks only apply to 3D-based nodes."));
+	if ((adding_track_type == Animation::TYPE_POSITION_3D || adding_track_type == Animation::TYPE_ROTATION_3D || adding_track_type == Animation::TYPE_SCALE_3D) && !node->is_class("Node3D")) {
+		EditorNode::get_singleton()->show_warning(TTR("Position/Rotation/Scale 3D tracks only apply to 3D-based nodes."));
 		return;
 	}
 
@@ -4415,7 +4479,9 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
 			prop_selector->set_type_filter(Vector<Variant::Type>());
 			prop_selector->select_property_from_instance(node);
 		} break;
-		case Animation::TYPE_TRANSFORM3D:
+		case Animation::TYPE_POSITION_3D:
+		case Animation::TYPE_ROTATION_3D:
+		case Animation::TYPE_SCALE_3D:
 		case Animation::TYPE_METHOD: {
 			undo_redo->create_action(TTR("Add Track"));
 			undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
@@ -4584,7 +4650,27 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
 	}
 
 	switch (animation->track_get_type(p_track)) {
-		case Animation::TYPE_TRANSFORM3D: {
+		case Animation::TYPE_POSITION_3D: {
+			if (!root->has_node(animation->track_get_path(p_track))) {
+				EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
+				return;
+			}
+			Node3D *base = Object::cast_to<Node3D>(root->get_node(animation->track_get_path(p_track)));
+
+			if (!base) {
+				EditorNode::get_singleton()->show_warning(TTR("Track is not of type Node3D, can't insert key"));
+				return;
+			}
+
+			Vector3 pos = base->get_position();
+
+			undo_redo->create_action(TTR("Add Position Key"));
+			undo_redo->add_do_method(animation.ptr(), "position_track_insert_key", p_track, p_ofs, pos);
+			undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
+			undo_redo->commit_action();
+
+		} break;
+		case Animation::TYPE_ROTATION_3D: {
 			if (!root->has_node(animation->track_get_path(p_track))) {
 				EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
 				return;
@@ -4596,14 +4682,30 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
 				return;
 			}
 
-			Transform3D xf = base->get_transform();
+			Quaternion rot = base->get_transform().basis.operator Quaternion();
+
+			undo_redo->create_action(TTR("Add Rotation Key"));
+			undo_redo->add_do_method(animation.ptr(), "rotation_track_insert_key", p_track, p_ofs, rot);
+			undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
+			undo_redo->commit_action();
+
+		} break;
+		case Animation::TYPE_SCALE_3D: {
+			if (!root->has_node(animation->track_get_path(p_track))) {
+				EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
+				return;
+			}
+			Node3D *base = Object::cast_to<Node3D>(root->get_node(animation->track_get_path(p_track)));
+
+			if (!base) {
+				EditorNode::get_singleton()->show_warning(TTR("Track is not of type Node3D, can't insert key"));
+				return;
+			}
 
-			Vector3 loc = xf.get_origin();
-			Vector3 scale = xf.basis.get_scale_local();
-			Quaternion rot = xf.basis;
+			Vector3 scale = base->get_scale();
 
-			undo_redo->create_action(TTR("Add Transform Track Key"));
-			undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale);
+			undo_redo->create_action(TTR("Add Scale Key"));
+			undo_redo->add_do_method(animation.ptr(), "scale_track_insert_key", p_track, p_ofs, scale);
 			undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
 			undo_redo->commit_action();
 
@@ -5277,8 +5379,14 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
 				}
 
 				switch (animation->track_get_type(i)) {
-					case Animation::TYPE_TRANSFORM3D:
-						text += " (Transform)";
+					case Animation::TYPE_POSITION_3D:
+						text += " (Position)";
+						break;
+					case Animation::TYPE_ROTATION_3D:
+						text += " (Rotation)";
+						break;
+					case Animation::TYPE_SCALE_3D:
+						text += " (Scale)";
 						break;
 					case Animation::TYPE_METHOD:
 						text += " (Methods)";

+ 1 - 1
editor/animation_track_editor.h

@@ -35,7 +35,7 @@
 #include "editor/editor_spin_slider.h"
 #include "editor/property_editor.h"
 #include "editor/property_selector.h"
-#include "scene/animation/animation_cache.h"
+
 #include "scene/gui/control.h"
 #include "scene/gui/file_dialog.h"
 #include "scene/gui/menu_button.h"

+ 47 - 0
editor/icons/KeyTrackPosition.svg

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   height="10"
+   viewBox="0 0 10 10"
+   width="10"
+   version="1.1"
+   id="svg12"
+   sodipodi:docname="KeyTrackPosition.svg"
+   inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs16" />
+  <sodipodi:namedview
+     id="namedview14"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="42.2"
+     inkscape:cx="12.78436"
+     inkscape:cy="6.1729858"
+     inkscape:window-width="1848"
+     inkscape:window-height="1016"
+     inkscape:window-x="72"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg12" />
+  <ellipse
+     style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+     id="path921"
+     cx="-0.88986981"
+     cy="6.0959954"
+     rx="1.2495773"
+     ry="1.0867468" />
+  <path
+     d="M 4.949661,0.60426977 A 0.6444116,0.6444116 0 0 0 4.504153,0.79178767 L 3.215459,2.0804819 4.12663,2.9916532 4.95977,2.1585124 5.792911,2.9916532 6.704083,2.0804819 5.415388,0.79178767 A 0.6444116,0.6444116 0 0 0 4.949744,0.60426977 Z M 1.926771,3.3691634 0.638077,4.6578577 a 0.6444116,0.6444116 0 0 0 0,0.9111713 L 1.926771,6.8577233 2.837942,5.946552 2.004801,5.1134111 2.837942,4.2802702 1.926771,3.3690989 Z m 6.065948,0 -0.911171,0.9111713 0.83314,0.8331409 -0.83314,0.8331408 0.911171,0.9111714 1.288694,-1.2886944 a 0.6444116,0.6444116 0 0 0 0,-0.9111713 L 7.992719,3.3692278 Z M 4.959777,3.8247361 A 1.2886943,1.2886943 0 0 0 3.671083,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,6.4021248 1.2886943,1.2886943 0 0 0 6.248471,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,3.8247361 Z m -0.83314,3.4105296 -0.911172,0.9111715 1.288694,1.288694 a 0.6444116,0.6444116 0 0 0 0.911171,0 L 6.704025,8.1464372 5.792853,7.2352657 4.959712,8.0684062 4.126572,7.2352657 Z"
+     fill="#e0e0e0"
+     fill-opacity="0.99608"
+     id="path1400"
+     style="fill:#ea7940;fill-opacity:1;stroke-width:0.644347" />
+</svg>

+ 47 - 0
editor/icons/KeyTrackRotation.svg

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   height="10"
+   viewBox="0 0 10 10"
+   width="10"
+   version="1.1"
+   id="svg12"
+   sodipodi:docname="KeyTrackRotation.svg"
+   inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs16" />
+  <sodipodi:namedview
+     id="namedview14"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="42.2"
+     inkscape:cx="1.9075829"
+     inkscape:cy="5.8175355"
+     inkscape:window-width="1848"
+     inkscape:window-height="1016"
+     inkscape:window-x="72"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg12" />
+  <ellipse
+     style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+     id="path921"
+     cx="-0.88986981"
+     cy="6.0959954"
+     rx="1.2495773"
+     ry="1.0867468" />
+  <path
+     d="m 5.0711986,0.88214062 a 4.1086779,4.1086779 0 0 0 -0.178839,0.00115 4.1086779,4.1086779 0 0 0 -0.409265,0.033245 A 4.1086779,4.1086779 0 0 0 0.99001362,4.1883346 4.1086779,4.1086779 0 0 0 2.1467236,7.9244144 h -0.648877 v 1.1739077 h 2.347816 a 0.58701268,0.58701268 0 0 0 0.569756,-0.729119 l -0.586953,-2.3478164 -1.139514,0.2854534 0.165082,0.6580347 a 2.9347699,2.9347699 0 0 1 -0.769204,-1.9752178 2.9347699,2.9347699 0 0 1 2.93477,-2.93477 2.9347699,2.9347699 0 0 1 2.93477,2.93477 2.9347699,2.9347699 0 0 1 -0.860944,2.0738257 l 0.831127,0.8311266 A 4.1086779,4.1086779 0 0 0 8.7040866,3.1726236 4.1086779,4.1086779 0 0 0 5.0711336,0.88215292 Z m -0.05159,2.93359608 a 1.173908,1.173908 0 0 0 -1.173907,1.173908 1.173908,1.173908 0 0 0 1.173907,1.173908 1.173908,1.173908 0 0 0 1.173909,-1.173908 1.173908,1.173908 0 0 0 -1.173909,-1.173908 z"
+     fill="#e0e0e0"
+     fill-opacity="0.99608"
+     id="path1165"
+     style="fill:#ff2b88;fill-opacity:1;stroke-width:0.586954" />
+</svg>

+ 47 - 0
editor/icons/KeyTrackScale.svg

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   height="10"
+   viewBox="0 0 10 10"
+   width="10"
+   version="1.1"
+   id="svg12"
+   sodipodi:docname="KeyTrackScale.svg"
+   inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs16" />
+  <sodipodi:namedview
+     id="namedview14"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="42.2"
+     inkscape:cx="1.9075829"
+     inkscape:cy="5.8175355"
+     inkscape:window-width="1848"
+     inkscape:window-height="1016"
+     inkscape:window-x="72"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg12" />
+  <ellipse
+     style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+     id="path921"
+     cx="-0.88986981"
+     cy="6.0959954"
+     rx="1.2495773"
+     ry="1.0867468" />
+  <path
+     d="M 5.5742494,0.94786723 A 0.58774585,0.58774585 0 0 0 4.9865036,1.535613 0.58774585,0.58774585 0 0 0 5.5742494,2.1233589 h 1.519852 L 6.334146,2.8833142 7.1652774,3.7144456 7.9252328,2.9544903 V 4.4743422 A 0.58774585,0.58774585 0 0 0 8.5129787,5.0620881 0.58774585,0.58774585 0 0 0 9.1007245,4.4743422 V 1.535613 A 0.58780462,0.58780462 0 0 0 8.5129787,0.94786723 Z M 4.9865036,3.8865964 A 1.1754917,1.1754917 0 0 0 3.8110119,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,6.2375798 1.1754917,1.1754917 0 0 0 6.1619953,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,3.8865964 Z M 1.4600285,5.0620881 A 0.58774585,0.58774585 0 0 0 0.8722826,5.6498339 V 8.5885638 A 0.58780462,0.58780462 0 0 0 1.4600285,9.1763092 H 4.3987577 A 0.58774585,0.58774585 0 0 0 4.9865036,8.5885638 0.58774585,0.58774585 0 0 0 4.3987577,8.0008173 H 2.8789057 L 3.6388611,7.2408619 2.8077297,6.4097305 2.0477743,7.1696859 V 5.6498339 A 0.58774585,0.58774585 0 0 0 1.4600285,5.0620881 Z"
+     fill="#e0e0e0"
+     fill-opacity="0.99608"
+     id="path7"
+     style="fill:#eac840;fill-opacity:1;stroke-width:0.587745" />
+</svg>

+ 43 - 0
editor/icons/KeyXPosition.svg

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   height="10"
+   viewBox="0 0 10 10"
+   width="10"
+   version="1.1"
+   id="svg1678"
+   sodipodi:docname="KeyXPosition.svg"
+   inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs1682" />
+  <sodipodi:namedview
+     id="namedview1680"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="84.4"
+     inkscape:cx="4.9940758"
+     inkscape:cy="5.0059242"
+     inkscape:window-width="1848"
+     inkscape:window-height="1016"
+     inkscape:window-x="72"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg1678" />
+  <rect
+     fill="#ea7940"
+     height="6.1027"
+     ry=".76286"
+     transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)"
+     width="6.1027"
+     x="-740.13947"
+     y="741.10779"
+     id="rect1676" />
+</svg>

+ 44 - 0
editor/icons/KeyXRotation.svg

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   height="10"
+   viewBox="0 0 10 10"
+   width="10"
+   version="1.1"
+   id="svg1678"
+   sodipodi:docname="KeyXRotation.svg"
+   inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs1682" />
+  <sodipodi:namedview
+     id="namedview1680"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="84.4"
+     inkscape:cx="0.32582938"
+     inkscape:cy="5.0059242"
+     inkscape:window-width="1848"
+     inkscape:window-height="1016"
+     inkscape:window-x="72"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg1678" />
+  <rect
+     fill="#ea7940"
+     height="6.1027002"
+     ry="0.76286"
+     transform="rotate(-45,-1258.2881,-521.2)"
+     width="6.1030002"
+     x="-740.13947"
+     y="741.10779"
+     id="rect1676"
+     style="fill:#ee3c94;fill-opacity:1" />
+</svg>

+ 44 - 0
editor/icons/KeyXScale.svg

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   height="10"
+   viewBox="0 0 10 10"
+   width="10"
+   version="1.1"
+   id="svg1678"
+   sodipodi:docname="KeyXScale.svg"
+   inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs1682" />
+  <sodipodi:namedview
+     id="namedview1680"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="84.4"
+     inkscape:cx="2.6658768"
+     inkscape:cy="5.0059242"
+     inkscape:window-width="1473"
+     inkscape:window-height="752"
+     inkscape:window-x="122"
+     inkscape:window-y="114"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg1678" />
+  <rect
+     fill="#ea7940"
+     height="6.1027002"
+     ry="0.76286"
+     transform="rotate(-45,-1258.2881,-521.2)"
+     width="6.1030002"
+     x="-740.13947"
+     y="741.10779"
+     id="rect1676"
+     style="fill:#dbbf4f;fill-opacity:1" />
+</svg>

+ 75 - 54
editor/import/editor_import_collada.cpp

@@ -91,8 +91,8 @@ struct ColladaImport {
 	Error _create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ImporterMesh>> p_morph_meshes = Vector<Ref<ImporterMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false);
 	Error load(const String &p_path, int p_flags, bool p_force_make_tangents = false, bool p_use_compression = false);
 	void _fix_param_animation_tracks();
-	void create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
-	void create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
+	void create_animation(int p_clip, bool p_import_value_tracks);
+	void create_animations(bool p_import_value_tracks);
 
 	Set<String> tracks_in_clips;
 	Vector<String> missing_textures;
@@ -1384,7 +1384,7 @@ void ColladaImport::_fix_param_animation_tracks() {
 	}
 }
 
-void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
+void ColladaImport::create_animations(bool p_import_value_tracks) {
 	_fix_param_animation_tracks();
 	for (int i = 0; i < collada.state.animation_clips.size(); i++) {
 		for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) {
@@ -1417,13 +1417,13 @@ void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_im
 		}
 	}
 
-	create_animation(-1, p_make_tracks_in_all_bones, p_import_value_tracks);
+	create_animation(-1, p_import_value_tracks);
 	for (int i = 0; i < collada.state.animation_clips.size(); i++) {
-		create_animation(i, p_make_tracks_in_all_bones, p_import_value_tracks);
+		create_animation(i, p_import_value_tracks);
 	}
 }
 
-void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
+void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
 	Ref<Animation> animation = Ref<Animation>(memnew(Animation));
 
 	if (p_clip == -1) {
@@ -1522,10 +1522,55 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
 			continue;
 		}
 
-		animation->add_track(Animation::TYPE_TRANSFORM3D);
-		int track = animation->get_track_count() - 1;
-		animation->track_set_path(track, path);
-		animation->track_set_imported(track, true); //helps merging later
+		bool has_position = false;
+		bool has_rotation = false;
+		bool has_scale = false;
+
+		for (int i = 0; cn->xform_list.size(); i++) {
+			switch (cn->xform_list[i].op) {
+				case Collada::Node::XForm::OP_ROTATE: {
+					has_rotation = true;
+				} break;
+				case Collada::Node::XForm::OP_SCALE: {
+					has_scale = true;
+				} break;
+				case Collada::Node::XForm::OP_TRANSLATE: {
+					has_position = true;
+				} break;
+				case Collada::Node::XForm::OP_MATRIX: {
+					has_position = true;
+					has_rotation = true;
+					has_scale = true;
+				} break;
+				case Collada::Node::XForm::OP_VISIBILITY: {
+				} break;
+			}
+		}
+
+		int base_track = animation->get_track_count();
+		int position_idx = -1;
+		if (has_position) {
+			position_idx = animation->get_track_count();
+			animation->add_track(Animation::TYPE_POSITION_3D);
+			animation->track_set_path(position_idx, path);
+			animation->track_set_imported(position_idx, true); //helps merging later
+		}
+
+		int rotation_idx = -1;
+		if (has_rotation) {
+			rotation_idx = animation->get_track_count();
+			animation->add_track(Animation::TYPE_ROTATION_3D);
+			animation->track_set_path(rotation_idx, path);
+			animation->track_set_imported(rotation_idx, true); //helps merging later
+		}
+
+		int scale_idx = -1;
+		if (has_scale) {
+			scale_idx = animation->get_track_count();
+			animation->add_track(Animation::TYPE_SCALE_3D);
+			animation->track_set_path(scale_idx, path);
+			animation->track_set_imported(scale_idx, true); //helps merging later
+		}
 
 		Vector<real_t> snapshots = base_snapshots;
 
@@ -1606,10 +1651,19 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
 
 			Vector3 s = xform.basis.get_scale();
 			bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
-			Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
+			Quaternion q = singular_matrix ? Quaternion() :
+											   xform.basis.get_rotation_quaternion();
 			Vector3 l = xform.origin;
 
-			animation->transform_track_insert_key(track, snapshots[i], l, q, s);
+			if (position_idx >= 0) {
+				animation->position_track_insert_key(position_idx, snapshots[i], l);
+			}
+			if (rotation_idx >= 0) {
+				animation->rotation_track_insert_key(rotation_idx, snapshots[i], q);
+			}
+			if (scale_idx >= 0) {
+				animation->scale_track_insert_key(scale_idx, snapshots[i], s);
+			}
 		}
 
 		if (nm.bone >= 0) {
@@ -1621,48 +1675,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
 		if (found_anim) {
 			tracks_found = true;
 		} else {
-			animation->remove_track(track);
-		}
-	}
-
-	if (p_make_tracks_in_all_bones) {
-		//some bones may lack animation, but since we don't store pose as a property, we must add keyframes!
-		for (const KeyValue<String, bool> &E : bones_with_animation) {
-			if (E.value) {
-				continue;
+			if (position_idx >= 0) {
+				animation->remove_track(base_track);
 			}
-
-			NodeMap &nm = node_map[E.key];
-			String path = scene->get_path_to(nm.node);
-			ERR_CONTINUE(nm.bone < 0);
-			Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node);
-			String name = sk->get_bone_name(nm.bone);
-			path = path + ":" + name;
-
-			Collada::Node *cn = collada.state.scene_map[E.key];
-			if (cn->ignore_anim) {
-				WARN_PRINT("Collada: Ignoring animation on node: " + path);
-				continue;
+			if (rotation_idx >= 0) {
+				animation->remove_track(base_track);
+			}
+			if (scale_idx >= 0) {
+				animation->remove_track(base_track);
 			}
-
-			animation->add_track(Animation::TYPE_TRANSFORM3D);
-			int track = animation->get_track_count() - 1;
-			animation->track_set_path(track, path);
-			animation->track_set_imported(track, true); //helps merging later
-
-			Transform3D xform = cn->compute_transform(collada);
-			xform = collada.fix_transform(xform) * cn->post_transform;
-
-			xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
-
-			Vector3 s = xform.basis.get_scale();
-			bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
-			Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
-			Vector3 l = xform.origin;
-
-			animation->transform_track_insert_key(track, 0, l, q, s);
-
-			tracks_found = true;
 		}
 	}
 
@@ -1773,7 +1794,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
 	}
 
 	if (p_flags & IMPORT_ANIMATION) {
-		state.create_animations(true, true);
+		state.create_animations(true);
 		AnimationPlayer *ap = memnew(AnimationPlayer);
 		for (int i = 0; i < state.animations.size(); i++) {
 			String name;
@@ -1800,7 +1821,7 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
 	Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
 	ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
 
-	state.create_animations(true, true);
+	state.create_animations(true);
 	if (state.scene) {
 		memdelete(state.scene);
 	}

+ 0 - 234
editor/import/editor_importer_bake_reset.cpp

@@ -1,234 +0,0 @@
-/*************************************************************************/
-/*  editor_importer_bake_reset.cpp                                       */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "editor/import/editor_importer_bake_reset.h"
-
-#include "core/error/error_list.h"
-#include "core/error/error_macros.h"
-#include "core/math/transform_3d.h"
-#include "resource_importer_scene.h"
-#include "scene/3d/importer_mesh_instance_3d.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/animation/animation_player.h"
-
-// Given that an engineering team has made a reference character, one wants ten animators to create animations.
-// Currently, a tech artist needs to combine the ten files into one exported gltf2 to import into Godot Engine.
-// We bake the RESET animation and then set it to identity,
-// so that rigs with corresponding RESET animation can have their animations transferred with ease.
-//
-// The original algorithm for the code was used to change skeleton bone rolls to be parent to child.
-//
-// Reference https://github.com/godotengine/godot-proposals/issues/2961
-void BakeReset::_bake_animation_pose(Node *scene, const String &p_bake_anim) {
-	Map<StringName, BakeResetRestBone> r_rest_bones;
-	Vector<Node3D *> r_meshes;
-	List<Node *> queue;
-	queue.push_back(scene);
-	while (!queue.is_empty()) {
-		List<Node *>::Element *E = queue.front();
-		Node *node = E->get();
-		AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
-		// Step 1: import scene with animations into the rest bones data structure.
-		_fetch_reset_animation(ap, r_rest_bones, p_bake_anim);
-
-		int child_count = node->get_child_count();
-		for (int i = 0; i < child_count; i++) {
-			queue.push_back(node->get_child(i));
-		}
-		queue.pop_front();
-	}
-
-	queue.push_back(scene);
-	while (!queue.is_empty()) {
-		List<Node *>::Element *E = queue.front();
-		Node *node = E->get();
-		ImporterMeshInstance3D *editor_mesh_3d = scene->cast_to<ImporterMeshInstance3D>(node);
-		MeshInstance3D *mesh_3d = scene->cast_to<MeshInstance3D>(node);
-		if (scene->cast_to<Skeleton3D>(node)) {
-			Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
-
-			// Step 2: Bake the RESET animation from the RestBone to the skeleton.
-			_fix_skeleton(skeleton, r_rest_bones);
-		}
-		if (editor_mesh_3d) {
-			NodePath path = editor_mesh_3d->get_skeleton_path();
-			if (!path.is_empty() && editor_mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(editor_mesh_3d->get_node_or_null(path))) {
-				r_meshes.push_back(editor_mesh_3d);
-			}
-		} else if (mesh_3d) {
-			NodePath path = mesh_3d->get_skeleton_path();
-			if (!path.is_empty() && mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(mesh_3d->get_node_or_null(path))) {
-				r_meshes.push_back(mesh_3d);
-			}
-		}
-		int child_count = node->get_child_count();
-		for (int i = 0; i < child_count; i++) {
-			queue.push_back(node->get_child(i));
-		}
-		queue.pop_front();
-	}
-
-	queue.push_back(scene);
-	while (!queue.is_empty()) {
-		List<Node *>::Element *E = queue.front();
-		Node *node = E->get();
-		AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
-		if (ap) {
-			// Step 3: Key all RESET animation frames to identity.
-			_align_animations(ap, r_rest_bones);
-		}
-
-		int child_count = node->get_child_count();
-		for (int i = 0; i < child_count; i++) {
-			queue.push_back(node->get_child(i));
-		}
-		queue.pop_front();
-	}
-}
-
-void BakeReset::_align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones) {
-	ERR_FAIL_NULL(p_ap);
-	List<StringName> anim_names;
-	p_ap->get_animation_list(&anim_names);
-	for (List<StringName>::Element *anim_i = anim_names.front(); anim_i; anim_i = anim_i->next()) {
-		Ref<Animation> a = p_ap->get_animation(anim_i->get());
-		ERR_CONTINUE(a.is_null());
-		for (const KeyValue<StringName, BakeResetRestBone> &rest_bone_i : r_rest_bones) {
-			int track = a->find_track(NodePath(rest_bone_i.key));
-			if (track == -1) {
-				continue;
-			}
-			int new_track = a->add_track(Animation::TYPE_TRANSFORM3D);
-			NodePath new_path = NodePath(rest_bone_i.key);
-			const BakeResetRestBone rest_bone = rest_bone_i.value;
-			a->track_set_path(new_track, new_path);
-			for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
-				Vector3 loc;
-				Quaternion rot;
-				Vector3 scale;
-				Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
-				ERR_CONTINUE(err);
-				real_t time = a->track_get_key_time(track, key_i);
-				rot.normalize();
-				loc = loc - rest_bone.loc;
-				rot = rest_bone.rest_delta.get_rotation_quaternion().inverse() * rot;
-				rot.normalize();
-				scale = Vector3(1, 1, 1) - (rest_bone.rest_delta.get_scale() - scale);
-				// Apply the reverse of the rest changes to make the key be close to identity transform.
-				a->transform_track_insert_key(new_track, time, loc, rot, scale);
-			}
-			a->remove_track(track);
-		}
-	}
-}
-
-void BakeReset::_fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim) {
-	if (!p_ap) {
-		return;
-	}
-	List<StringName> anim_names;
-	p_ap->get_animation_list(&anim_names);
-	Node *root = p_ap->get_owner();
-	ERR_FAIL_NULL(root);
-	if (!p_ap->has_animation(p_bake_anim)) {
-		return;
-	}
-	Ref<Animation> a = p_ap->get_animation(p_bake_anim);
-	if (a.is_null()) {
-		return;
-	}
-	for (int32_t track = 0; track < a->get_track_count(); track++) {
-		NodePath path = a->track_get_path(track);
-		String string_path = path;
-		Skeleton3D *skeleton = root->cast_to<Skeleton3D>(root->get_node(string_path.get_slice(":", 0)));
-		if (!skeleton) {
-			continue;
-		}
-		String bone_name = string_path.get_slice(":", 1);
-		for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
-			Vector3 loc;
-			Quaternion rot;
-			Vector3 scale;
-			Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
-			if (err != OK) {
-				ERR_PRINT_ONCE("Reset animation baker can't get key.");
-				continue;
-			}
-			rot.normalize();
-			Basis rot_basis = Basis(rot, scale);
-			BakeResetRestBone rest_bone;
-			rest_bone.rest_delta = rot_basis;
-			rest_bone.loc = loc;
-			// Store the animation into the RestBone.
-			r_rest_bones[StringName(String(skeleton->get_owner()->get_path_to(skeleton)) + ":" + bone_name)] = rest_bone;
-			break;
-		}
-	}
-}
-
-void BakeReset::_fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones) {
-	int bone_count = p_skeleton->get_bone_count();
-
-	// First iterate through all the bones and update the RestBone.
-	for (int j = 0; j < bone_count; j++) {
-		StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(j);
-		BakeResetRestBone &rest_bone = r_rest_bones[final_path];
-		rest_bone.rest_local = p_skeleton->get_bone_rest(j);
-	}
-	for (int i = 0; i < bone_count; i++) {
-		int parent_bone = p_skeleton->get_bone_parent(i);
-		String path = p_skeleton->get_owner()->get_path_to(p_skeleton);
-		StringName final_path = String(path) + String(":") + p_skeleton->get_bone_name(parent_bone);
-		if (parent_bone >= 0) {
-			r_rest_bones[path].children.push_back(i);
-		}
-	}
-
-	// When we apply transform to a bone, we also have to move all of its children in the opposite direction.
-	for (int i = 0; i < bone_count; i++) {
-		StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
-		r_rest_bones[final_path].rest_local = r_rest_bones[final_path].rest_local * Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc);
-		// Iterate through the children and move in the opposite direction.
-		for (int j = 0; j < r_rest_bones[final_path].children.size(); j++) {
-			int child_index = r_rest_bones[final_path].children[j];
-			StringName children_path = String(p_skeleton->get_name()) + String(":") + p_skeleton->get_bone_name(child_index);
-			r_rest_bones[children_path].rest_local = Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc).affine_inverse() * r_rest_bones[children_path].rest_local;
-		}
-	}
-
-	for (int i = 0; i < bone_count; i++) {
-		StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
-		ERR_CONTINUE(!r_rest_bones.has(final_path));
-		Transform3D rest_transform = r_rest_bones[final_path].rest_local;
-		p_skeleton->set_bone_rest(i, rest_transform);
-	}
-}

+ 0 - 54
editor/import/editor_importer_bake_reset.h

@@ -1,54 +0,0 @@
-/*************************************************************************/
-/*  editor_importer_bake_reset.h                                         */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef RESOURCE_IMPORTER_BAKE_RESET_H
-#define RESOURCE_IMPORTER_BAKE_RESET_H
-
-#include "scene/main/node.h"
-
-class Skeleton3D;
-class AnimationPlayer;
-class BakeReset {
-	struct BakeResetRestBone {
-		Transform3D rest_local;
-		Basis rest_delta;
-		Vector3 loc;
-		Vector<int> children;
-	};
-
-public:
-	void _bake_animation_pose(Node *scene, const String &p_bake_anim);
-
-private:
-	void _fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones);
-	void _align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones);
-	void _fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim);
-};
-#endif

+ 54 - 33
editor/import/resource_importer_scene.cpp

@@ -32,7 +32,7 @@
 
 #include "core/io/resource_saver.h"
 #include "editor/editor_node.h"
-#include "editor/import/editor_importer_bake_reset.h"
+
 #include "editor/import/scene_import_settings.h"
 #include "scene/3d/area_3d.h"
 #include "scene/3d/collision_shape_3d.h"
@@ -851,42 +851,57 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
 						new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
 
 						if (kt > (from + 0.01) && k > 0) {
-							if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
-								Quaternion q;
+							if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
 								Vector3 p;
+								default_anim->position_track_interpolate(j, from, &p);
+								new_anim->position_track_insert_key(dtrack, 0, p);
+							} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+								Quaternion r;
+								default_anim->rotation_track_interpolate(j, from, &r);
+								new_anim->rotation_track_insert_key(dtrack, 0, r);
+							} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
 								Vector3 s;
-								default_anim->transform_track_interpolate(j, from, &p, &q, &s);
-								new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
-							}
-							if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+								default_anim->scale_track_interpolate(j, from, &s);
+								new_anim->scale_track_insert_key(dtrack, 0, s);
+							} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
 								Variant var = default_anim->value_track_interpolate(j, from);
 								new_anim->track_insert_key(dtrack, 0, var);
 							}
 						}
 					}
 
-					if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
-						Quaternion q;
+					if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
 						Vector3 p;
+						default_anim->position_track_get_key(j, k, &p);
+						new_anim->position_track_insert_key(dtrack, kt - from, p);
+					} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+						Quaternion r;
+						default_anim->rotation_track_get_key(j, k, &r);
+						new_anim->rotation_track_insert_key(dtrack, kt - from, r);
+					} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
 						Vector3 s;
-						default_anim->transform_track_get_key(j, k, &p, &q, &s);
-						new_anim->transform_track_insert_key(dtrack, kt - from, p, q, s);
-					}
-					if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+						default_anim->scale_track_get_key(j, k, &s);
+						new_anim->scale_track_insert_key(dtrack, kt - from, s);
+					} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
 						Variant var = default_anim->track_get_key_value(j, k);
 						new_anim->track_insert_key(dtrack, kt - from, var);
 					}
 				}
 
 				if (dtrack != -1 && kt >= to) {
-					if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
-						Quaternion q;
+					if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
 						Vector3 p;
+						default_anim->position_track_interpolate(j, to, &p);
+						new_anim->position_track_insert_key(dtrack, to - from, p);
+					} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+						Quaternion r;
+						default_anim->rotation_track_interpolate(j, to, &r);
+						new_anim->rotation_track_insert_key(dtrack, to - from, r);
+					} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
 						Vector3 s;
-						default_anim->transform_track_interpolate(j, to, &p, &q, &s);
-						new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
-					}
-					if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+						default_anim->scale_track_interpolate(j, to, &s);
+						new_anim->scale_track_insert_key(dtrack, to - from, s);
+					} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
 						Variant var = default_anim->value_track_interpolate(j, to);
 						new_anim->track_insert_key(dtrack, to - from, var);
 					}
@@ -897,16 +912,25 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
 				new_anim->add_track(default_anim->track_get_type(j));
 				dtrack = new_anim->get_track_count() - 1;
 				new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
-				if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
-					Quaternion q;
+				if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
 					Vector3 p;
+					default_anim->position_track_interpolate(j, from, &p);
+					new_anim->position_track_insert_key(dtrack, 0, p);
+					default_anim->position_track_interpolate(j, to, &p);
+					new_anim->position_track_insert_key(dtrack, to - from, p);
+				} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+					Quaternion r;
+					default_anim->rotation_track_interpolate(j, from, &r);
+					new_anim->rotation_track_insert_key(dtrack, 0, r);
+					default_anim->rotation_track_interpolate(j, to, &r);
+					new_anim->rotation_track_insert_key(dtrack, to - from, r);
+				} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
 					Vector3 s;
-					default_anim->transform_track_interpolate(j, from, &p, &q, &s);
-					new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
-					default_anim->transform_track_interpolate(j, to, &p, &q, &s);
-					new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
-				}
-				if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+					default_anim->scale_track_interpolate(j, from, &s);
+					new_anim->scale_track_insert_key(dtrack, 0, s);
+					default_anim->scale_track_interpolate(j, to, &s);
+					new_anim->scale_track_insert_key(dtrack, to - from, s);
+				} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
 					Variant var = default_anim->value_track_interpolate(j, from);
 					new_anim->track_insert_key(dtrack, 0, var);
 					Variant to_var = default_anim->value_track_interpolate(j, to);
@@ -1000,6 +1024,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
 			r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05));
 			r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01));
 			r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22));
+			r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
+			r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
+			r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
 			r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
 
 			for (int i = 0; i < 256; i++) {
@@ -1171,7 +1198,6 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
 	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
-	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/bake_reset_animation"), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
 
@@ -1533,11 +1559,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
 
 	_pre_fix_node(scene, scene, collision_map);
 	_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
-	bool use_bake_reset_animation = p_options["animation/bake_reset_animation"];
-	if (use_bake_reset_animation) {
-		BakeReset bake_reset;
-		bake_reset._bake_animation_pose(scene, "RESET");
-	}
 
 	String root_type = p_options["nodes/root_type"];
 	root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.

+ 7 - 0
editor/import/resource_importer_scene.h

@@ -65,6 +65,13 @@ public:
 		IMPORT_USE_NAMED_SKIN_BINDS = 16,
 	};
 
+	enum AnimationImportBoneTracks {
+		ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT,
+		ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT_FOR_ALL,
+		ANIMATION_IMPORT_BONE_TRACKS_ALWAYS,
+		ANIMATION_IMPORT_BONE_TRACKS_NEVER,
+	};
+
 	virtual uint32_t get_import_flags() const;
 	virtual void get_extensions(List<String> *r_extensions) const;
 	virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr);

+ 3 - 1
editor/plugins/skeleton_3d_editor_plugin.cpp

@@ -1305,7 +1305,9 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi
 	t.origin = orig + to_local.xform(sub);
 
 	// Apply transform.
-	skeleton->set_bone_pose(p_id, t);
+	skeleton->set_bone_pose_position(p_id, t.origin);
+	skeleton->set_bone_pose_rotation(p_id, t.basis.operator Quaternion());
+	skeleton->set_bone_pose_scale(p_id, t.basis.get_scale());
 }
 
 void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {

+ 36 - 7
modules/fbx/editor_scene_importer_fbx.cpp

@@ -1011,9 +1011,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
 						// track count is 5.
 						// next track id is 5.
 						const uint64_t target_id = track.key;
-						int track_idx = animation->add_track(Animation::TYPE_TRANSFORM3D);
 
-						// animation->track_set_path(track_idx, node_path);
 						Ref<FBXBone> bone;
 
 						// note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad.
@@ -1037,22 +1035,21 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
 
 						// if this is a skeleton mapped track we can just set the path for the track.
 						// todo: implement node paths here at some
+						NodePath track_path;
 						if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) {
 							if (bone->fbx_skeleton.is_valid() && bone.is_valid()) {
 								Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton;
 								String bone_path = state.root->get_path_to(fbx_skeleton->skeleton);
 								bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id);
 								print_verbose("[doc] track bone path: " + bone_path);
-								NodePath path = bone_path;
-								animation->track_set_path(track_idx, path);
+								track_path = bone_path;
 							}
 						} else if (state.fbx_target_map.has(target_id)) {
 							//print_verbose("[doc] we have a valid target for a node animation");
 							Ref<FBXNode> target_node = state.fbx_target_map[target_id];
 							if (target_node.is_valid() && target_node->godot_node != nullptr) {
 								String node_path = state.root->get_path_to(target_node->godot_node);
-								NodePath path = node_path;
-								animation->track_set_path(track_idx, path);
+								track_path = node_path;
 								//print_verbose("[doc] node animation path: " + node_path);
 							}
 						} else {
@@ -1186,6 +1183,30 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
 						const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale();
 						print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")");
 
+						int position_idx = -1;
+						if (pos_values.size()) {
+							position_idx = animation->get_track_count();
+							animation->add_track(Animation::TYPE_POSITION_3D);
+							animation->track_set_path(position_idx, track_path);
+							animation->track_set_imported(position_idx, true);
+						}
+
+						int rotation_idx = -1;
+						if (pos_values.size()) {
+							rotation_idx = animation->get_track_count();
+							animation->add_track(Animation::TYPE_ROTATION_3D);
+							animation->track_set_path(rotation_idx, track_path);
+							animation->track_set_imported(rotation_idx, true);
+						}
+
+						int scale_idx = -1;
+						if (pos_values.size()) {
+							scale_idx = animation->get_track_count();
+							animation->add_track(Animation::TYPE_SCALE_3D);
+							animation->track_set_path(scale_idx, track_path);
+							animation->track_set_imported(scale_idx, true);
+						}
+
 						while (true) {
 							Vector3 pos = def_pos;
 							Quaternion rot = def_rot;
@@ -1220,7 +1241,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
 								pos = t.origin;
 							}
 
-							animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
+							if (position_idx >= 0) {
+								animation->position_track_insert_key(position_idx, time, pos);
+							}
+							if (rotation_idx >= 0) {
+								animation->rotation_track_insert_key(rotation_idx, time, rot);
+							}
+							if (scale_idx >= 0) {
+								animation->scale_track_insert_key(scale_idx, time, scale);
+							}
 
 							if (last) {
 								break;

+ 84 - 12
modules/gltf/gltf_document.cpp

@@ -5733,7 +5733,7 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> {
 template <class T>
 T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
 	ERR_FAIL_COND_V(!p_values.size(), T());
-	if (p_times.size() != p_values.size()) {
+	if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) {
 		ERR_PRINT_ONCE("The interpolated values are not corresponding to its times.");
 		return p_values[0];
 	}
@@ -5868,9 +5868,67 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 		const bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0;
 		if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
 			//make transform track
-			int track_idx = animation->get_track_count();
-			animation->add_track(Animation::TYPE_TRANSFORM3D);
-			animation->track_set_path(track_idx, transform_node_path);
+			int base_idx = animation->get_track_count();
+			int position_idx = -1;
+			int rotation_idx = -1;
+			int scale_idx = -1;
+
+			if (track.position_track.values.size()) {
+				Vector3 base_pos = state->nodes[track_i.key]->position;
+				bool not_default = false; //discard the track if all it contains is default values
+				for (int i = 0; i < track.position_track.times.size(); i++) {
+					Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
+					if (!value.is_equal_approx(base_pos)) {
+						not_default = true;
+						break;
+					}
+				}
+				if (not_default) {
+					position_idx = base_idx;
+					animation->add_track(Animation::TYPE_POSITION_3D);
+					animation->track_set_path(position_idx, transform_node_path);
+					animation->track_set_imported(position_idx, true); //helps merging later
+
+					base_idx++;
+				}
+			}
+			if (track.rotation_track.values.size()) {
+				Quaternion base_rot = state->nodes[track_i.key]->rotation.normalized();
+				bool not_default = false; //discard the track if all it contains is default values
+				for (int i = 0; i < track.rotation_track.times.size(); i++) {
+					Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized();
+					if (!value.is_equal_approx(base_rot)) {
+						not_default = true;
+						break;
+					}
+				}
+				if (not_default) {
+					rotation_idx = base_idx;
+					animation->add_track(Animation::TYPE_ROTATION_3D);
+					animation->track_set_path(rotation_idx, transform_node_path);
+					animation->track_set_imported(rotation_idx, true); //helps merging later
+					base_idx++;
+				}
+			}
+			if (track.scale_track.values.size()) {
+				Vector3 base_scale = state->nodes[track_i.key]->scale;
+				bool not_default = false; //discard the track if all it contains is default values
+				for (int i = 0; i < track.scale_track.times.size(); i++) {
+					Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
+					if (!value.is_equal_approx(base_scale)) {
+						not_default = true;
+						break;
+					}
+				}
+				if (not_default) {
+					scale_idx = base_idx;
+					animation->add_track(Animation::TYPE_SCALE_3D);
+					animation->track_set_path(scale_idx, transform_node_path);
+					animation->track_set_imported(scale_idx, true); //helps merging later
+					base_idx++;
+				}
+			}
+
 			//first determine animation length
 
 			const double increment = 1.0 / bake_fps;
@@ -5880,15 +5938,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 			Quaternion base_rot;
 			Vector3 base_scale = Vector3(1, 1, 1);
 
-			if (!track.rotation_track.values.size()) {
+			if (rotation_idx == -1) {
 				base_rot = state->nodes[track_i.key]->rotation.normalized();
 			}
 
-			if (!track.position_track.values.size()) {
+			if (position_idx == -1) {
 				base_pos = state->nodes[track_i.key]->position;
 			}
 
-			if (!track.scale_track.values.size()) {
+			if (scale_idx == -1) {
 				base_scale = state->nodes[track_i.key]->scale;
 			}
 
@@ -5898,15 +5956,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 				Quaternion rot = base_rot;
 				Vector3 scale = base_scale;
 
-				if (track.position_track.times.size()) {
+				if (position_idx >= 0) {
 					pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation);
 				}
 
-				if (track.rotation_track.times.size()) {
+				if (rotation_idx >= 0) {
 					rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation);
 				}
 
-				if (track.scale_track.times.size()) {
+				if (scale_idx >= 0) {
 					scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation);
 				}
 
@@ -5925,7 +5983,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 					pos = xform.origin;
 				}
 
-				animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
+				if (position_idx >= 0) {
+					animation->position_track_insert_key(position_idx, time, pos);
+				}
+				if (rotation_idx >= 0) {
+					animation->rotation_track_insert_key(rotation_idx, time, rot);
+				}
+				if (scale_idx >= 0) {
+					animation->scale_track_insert_key(scale_idx, time, scale);
+				}
 
 				if (last) {
 					break;
@@ -6146,6 +6212,10 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo
 }
 
 GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, Transform3D p_bone_rest, int32_t p_track_i, GLTFNodeIndex p_node_i) {
+#ifndef _MSC_VER
+#warning this needs to be redone
+#endif
+#if 0
 	Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i);
 
 	GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
@@ -6164,6 +6234,8 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
 	for (int32_t key_i = 0; key_i < key_count; key_i++) {
 		times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i);
 	}
+
+
 	if (track_type == Animation::TYPE_TRANSFORM3D) {
 		p_track.position_track.times = times;
 		p_track.position_track.interpolation = gltf_interpolation;
@@ -6323,7 +6395,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
 			}
 		}
 	}
-
+#endif
 	return p_track;
 }
 

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

@@ -99,8 +99,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
 		set_bone_rest(which, p_value);
 	} else if (what == "enabled") {
 		set_bone_enabled(which, p_value);
-	} else if (what == "pose") {
-		set_bone_pose(which, p_value);
+	} else if (what == "position") {
+		set_bone_pose_position(which, p_value);
+	} else if (what == "rotation") {
+		set_bone_pose_rotation(which, p_value);
+	} else if (what == "scale") {
+		set_bone_pose_scale(which, p_value);
 	} else {
 		return false;
 	}
@@ -135,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
 		r_ret = get_bone_rest(which);
 	} else if (what == "enabled") {
 		r_ret = is_bone_enabled(which);
-	} else if (what == "pose") {
-		r_ret = get_bone_pose(which);
+	} else if (what == "position") {
+		r_ret = get_bone_pose_position(which);
+	} else if (what == "rotation") {
+		r_ret = get_bone_pose_rotation(which);
+	} else if (what == "scale") {
+		r_ret = get_bone_pose_scale(which);
 	} else {
 		return false;
 	}
@@ -151,7 +159,9 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
 		p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR));
 		p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
 		p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
-		p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+		p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+		p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+		p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
 	}
 
 #ifndef _3D_DISABLED
@@ -657,19 +667,60 @@ void Skeleton3D::clear_bones() {
 
 // Posing api
 
-void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
+void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
 	const int bone_size = bones.size();
 	ERR_FAIL_INDEX(p_bone, bone_size);
 
-	bones.write[p_bone].pose = p_pose;
+	bones.write[p_bone].pose_position = p_position;
+	bones.write[p_bone].pose_cache_dirty = true;
 	if (is_inside_tree()) {
 		_make_dirty();
 	}
 }
+void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) {
+	const int bone_size = bones.size();
+	ERR_FAIL_INDEX(p_bone, bone_size);
+
+	bones.write[p_bone].pose_rotation = p_rotation;
+	bones.write[p_bone].pose_cache_dirty = true;
+	if (is_inside_tree()) {
+		_make_dirty();
+	}
+}
+void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) {
+	const int bone_size = bones.size();
+	ERR_FAIL_INDEX(p_bone, bone_size);
+
+	bones.write[p_bone].pose_scale = p_scale;
+	bones.write[p_bone].pose_cache_dirty = true;
+	if (is_inside_tree()) {
+		_make_dirty();
+	}
+}
+
+Vector3 Skeleton3D::get_bone_pose_position(int p_bone) const {
+	const int bone_size = bones.size();
+	ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3());
+	return bones[p_bone].pose_position;
+}
+
+Quaternion Skeleton3D::get_bone_pose_rotation(int p_bone) const {
+	const int bone_size = bones.size();
+	ERR_FAIL_INDEX_V(p_bone, bone_size, Quaternion());
+	return bones[p_bone].pose_rotation;
+}
+
+Vector3 Skeleton3D::get_bone_pose_scale(int p_bone) const {
+	const int bone_size = bones.size();
+	ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3());
+	return bones[p_bone].pose_scale;
+}
+
 Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
 	const int bone_size = bones.size();
 	ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
-	return bones[p_bone].pose;
+	((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache();
+	return bones[p_bone].pose_cache;
 }
 
 void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) {
@@ -989,7 +1040,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
 
 		if (b.disable_rest) {
 			if (bone_enabled) {
-				Transform3D pose = b.pose;
+				b.update_pose_cache();
+				Transform3D pose = b.pose_cache;
 				if (b.custom_pose_enable) {
 					pose = b.custom_pose * pose;
 				}
@@ -1012,7 +1064,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
 
 		} else {
 			if (bone_enabled) {
-				Transform3D pose = b.pose;
+				b.update_pose_cache();
+				Transform3D pose = b.pose_cache;
 				if (b.custom_pose_enable) {
 					pose = b.custom_pose * pose;
 				}
@@ -1193,7 +1246,13 @@ void Skeleton3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones);
 
 	ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
-	ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose);
+	ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position);
+	ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation);
+	ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale);
+
+	ClassDB::bind_method(D_METHOD("get_bone_pose_position", "bone_idx"), &Skeleton3D::get_bone_pose_position);
+	ClassDB::bind_method(D_METHOD("get_bone_pose_rotation", "bone_idx"), &Skeleton3D::get_bone_pose_rotation);
+	ClassDB::bind_method(D_METHOD("get_bone_pose_scale", "bone_idx"), &Skeleton3D::get_bone_pose_scale);
 
 	ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
 	ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));

+ 21 - 2
scene/3d/skeleton_3d.h

@@ -79,7 +79,19 @@ private:
 		bool disable_rest = false;
 		Transform3D rest;
 
-		Transform3D pose;
+		_FORCE_INLINE_ void update_pose_cache() {
+			if (pose_cache_dirty) {
+				pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale);
+				pose_cache.origin = pose_position;
+				pose_cache_dirty = false;
+			}
+		}
+		bool pose_cache_dirty = true;
+		Transform3D pose_cache;
+		Vector3 pose_position;
+		Quaternion pose_rotation;
+		Vector3 pose_scale = Vector3(1, 1, 1);
+
 		Transform3D pose_global;
 		Transform3D pose_global_no_override;
 
@@ -206,9 +218,16 @@ public:
 
 	// posing api
 
-	void set_bone_pose(int p_bone, const Transform3D &p_pose);
+	void set_bone_pose_position(int p_bone, const Vector3 &p_position);
+	void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation);
+	void set_bone_pose_scale(int p_bone, const Vector3 &p_scale);
+
 	Transform3D get_bone_pose(int p_bone) const;
 
+	Vector3 get_bone_pose_position(int p_bone) const;
+	Quaternion get_bone_pose_rotation(int p_bone) const;
+	Vector3 get_bone_pose_scale(int p_bone) const;
+
 	void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose);
 	Transform3D get_bone_custom_pose(int p_bone) const;
 

+ 0 - 314
scene/animation/animation_cache.cpp

@@ -1,314 +0,0 @@
-/*************************************************************************/
-/*  animation_cache.cpp                                                  */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "animation_cache.h"
-
-void AnimationCache::_node_exit_tree(Node *p_node) {
-	//it is one shot, so it disconnects upon arrival
-
-	ERR_FAIL_COND(!connected_nodes.has(p_node));
-
-	connected_nodes.erase(p_node);
-
-	for (int i = 0; i < path_cache.size(); i++) {
-		if (path_cache[i].node != p_node) {
-			continue;
-		}
-
-		path_cache.write[i].valid = false; //invalidate path cache
-	}
-}
-
-void AnimationCache::_animation_changed() {
-	_clear_cache();
-}
-
-void AnimationCache::_clear_cache() {
-	while (connected_nodes.size()) {
-		connected_nodes.front()->get()->disconnect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree));
-		connected_nodes.erase(connected_nodes.front());
-	}
-	path_cache.clear();
-	cache_valid = false;
-	cache_dirty = true;
-}
-
-void AnimationCache::_update_cache() {
-	cache_valid = false;
-
-	ERR_FAIL_COND(!root);
-	ERR_FAIL_COND(!root->is_inside_tree());
-	ERR_FAIL_COND(animation.is_null());
-
-	for (int i = 0; i < animation->get_track_count(); i++) {
-		NodePath np = animation->track_get_path(i);
-
-		Node *node = root->get_node(np);
-		if (!node) {
-			path_cache.push_back(Path());
-			ERR_CONTINUE_MSG(!node, "Invalid track path in animation '" + np + "'.");
-		}
-
-		Path path;
-
-		Ref<Resource> res;
-
-		if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D) {
-#ifndef _3D_DISABLED
-			if (np.get_subname_count() > 1) {
-				path_cache.push_back(Path());
-				ERR_CONTINUE_MSG(animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D, "Transform tracks can't have a subpath '" + np + "'.");
-			}
-
-			Node3D *sp = Object::cast_to<Node3D>(node);
-
-			if (!sp) {
-				path_cache.push_back(Path());
-				ERR_CONTINUE_MSG(!sp, "Transform track not of type Node3D '" + np + "'.");
-			}
-
-			if (np.get_subname_count() == 1) {
-				StringName property = np.get_subname(0);
-				String ps = property;
-
-				Skeleton3D *sk = Object::cast_to<Skeleton3D>(node);
-				if (!sk) {
-					path_cache.push_back(Path());
-					ERR_CONTINUE_MSG(!sk, "Property defined in Transform track, but not a Skeleton! '" + np + "'.");
-				}
-
-				int idx = sk->find_bone(ps);
-				if (idx == -1) {
-					path_cache.push_back(Path());
-					ERR_CONTINUE_MSG(idx == -1, "Property defined in Transform track, but not a Skeleton Bone! '" + np + "'.");
-				}
-
-				path.bone_idx = idx;
-				path.skeleton = sk;
-			}
-
-			path.node_3d = sp;
-#endif // _3D_DISABLED
-		} else {
-			if (np.get_subname_count() > 0) {
-				RES res2;
-				Vector<StringName> leftover_subpath;
-
-				// We don't want to cache the last resource unless it is a method call
-				bool is_method = animation->track_get_type(i) == Animation::TYPE_METHOD;
-				root->get_node_and_resource(np, res2, leftover_subpath, is_method);
-
-				if (res2.is_valid()) {
-					path.resource = res2;
-				} else {
-					path.node = node;
-				}
-				path.object = res2.is_valid() ? res2.ptr() : (Object *)node;
-				path.subpath = leftover_subpath;
-
-			} else {
-				path.node = node;
-				path.object = node;
-				path.subpath = np.get_subnames();
-			}
-		}
-
-		if (animation->track_get_type(i) == Animation::TYPE_VALUE) {
-			if (np.get_subname_count() == 0) {
-				path_cache.push_back(Path());
-				ERR_CONTINUE_MSG(np.get_subname_count() == 0, "Value Track lacks property: " + np + ".");
-			}
-
-		} else if (animation->track_get_type(i) == Animation::TYPE_METHOD) {
-			if (path.subpath.size() != 0) { // Trying to call a method of a non-resource
-
-				path_cache.push_back(Path());
-				ERR_CONTINUE_MSG(path.subpath.size() != 0, "Method Track has property: " + np + ".");
-			}
-		}
-
-		path.valid = true;
-
-		path_cache.push_back(path);
-
-		if (!connected_nodes.has(path.node)) {
-			connected_nodes.insert(path.node);
-			path.node->connect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree), Node::make_binds(path.node), CONNECT_ONESHOT);
-		}
-	}
-
-	cache_dirty = false;
-	cache_valid = true;
-}
-
-void AnimationCache::set_track_transform(int p_idx, const Transform3D &p_transform) {
-	if (cache_dirty) {
-		_update_cache();
-	}
-
-	ERR_FAIL_COND(!cache_valid);
-	ERR_FAIL_INDEX(p_idx, path_cache.size());
-	Path &p = path_cache.write[p_idx];
-	if (!p.valid) {
-		return;
-	}
-
-#ifndef _3D_DISABLED
-	ERR_FAIL_COND(!p.node);
-	ERR_FAIL_COND(!p.node_3d);
-
-	if (p.skeleton) {
-		p.skeleton->set_bone_pose(p.bone_idx, p_transform);
-	} else {
-		p.node_3d->set_transform(p_transform);
-	}
-#endif // _3D_DISABLED
-}
-
-void AnimationCache::set_track_value(int p_idx, const Variant &p_value) {
-	if (cache_dirty) {
-		_update_cache();
-	}
-
-	ERR_FAIL_COND(!cache_valid);
-	ERR_FAIL_INDEX(p_idx, path_cache.size());
-	Path &p = path_cache.write[p_idx];
-	if (!p.valid) {
-		return;
-	}
-
-	ERR_FAIL_COND(!p.object);
-	p.object->set_indexed(p.subpath, p_value);
-}
-
-void AnimationCache::call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
-	if (cache_dirty) {
-		_update_cache();
-	}
-
-	ERR_FAIL_COND(!cache_valid);
-	ERR_FAIL_INDEX(p_idx, path_cache.size());
-	Path &p = path_cache.write[p_idx];
-	if (!p.valid) {
-		return;
-	}
-
-	ERR_FAIL_COND(!p.object);
-	p.object->call(p_method, p_args, p_argcount, r_error);
-}
-
-void AnimationCache::set_all(float p_time, float p_delta) {
-	if (cache_dirty) {
-		_update_cache();
-	}
-
-	ERR_FAIL_COND(!cache_valid);
-
-	int tc = animation->get_track_count();
-	for (int i = 0; i < tc; i++) {
-		switch (animation->track_get_type(i)) {
-			case Animation::TYPE_TRANSFORM3D: {
-				Vector3 loc, scale;
-				Quaternion rot;
-				animation->transform_track_interpolate(i, p_time, &loc, &rot, &scale);
-				Transform3D tr(Basis(rot), loc);
-				tr.basis.scale(scale);
-
-				set_track_transform(i, tr);
-
-			} break;
-			case Animation::TYPE_VALUE: {
-				if (animation->value_track_get_update_mode(i) == Animation::UPDATE_CONTINUOUS || (animation->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE && p_delta == 0)) {
-					Variant v = animation->value_track_interpolate(i, p_time);
-					set_track_value(i, v);
-				} else {
-					List<int> indices;
-					animation->value_track_get_key_indices(i, p_time, p_delta, &indices);
-
-					for (int &E : indices) {
-						Variant v = animation->track_get_key_value(i, E);
-						set_track_value(i, v);
-					}
-				}
-
-			} break;
-			case Animation::TYPE_METHOD: {
-				List<int> indices;
-				animation->method_track_get_key_indices(i, p_time, p_delta, &indices);
-
-				for (int &E : indices) {
-					Vector<Variant> args = animation->method_track_get_params(i, E);
-					StringName name = animation->method_track_get_name(i, E);
-					Callable::CallError err;
-
-					if (!args.size()) {
-						call_track(i, name, nullptr, 0, err);
-					} else {
-						Vector<const Variant *> argptrs;
-						argptrs.resize(args.size());
-						for (int j = 0; j < args.size(); j++) {
-							argptrs.write[j] = &args.write[j];
-						}
-
-						call_track(i, name, (const Variant **)&argptrs[0], args.size(), err);
-					}
-				}
-
-			} break;
-			default: {
-			}
-		}
-	}
-}
-
-void AnimationCache::set_animation(const Ref<Animation> &p_animation) {
-	_clear_cache();
-
-	if (animation.is_valid()) {
-		animation->disconnect("changed", callable_mp(this, &AnimationCache::_animation_changed));
-	}
-
-	animation = p_animation;
-
-	if (animation.is_valid()) {
-		animation->connect("changed", callable_mp(this, &AnimationCache::_animation_changed));
-	}
-}
-
-void AnimationCache::_bind_methods() {
-}
-
-void AnimationCache::set_root(Node *p_root) {
-	_clear_cache();
-	root = p_root;
-}
-
-AnimationCache::AnimationCache() {
-}

+ 0 - 84
scene/animation/animation_cache.h

@@ -1,84 +0,0 @@
-/*************************************************************************/
-/*  animation_cache.h                                                    */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef ANIMATION_CACHE_H
-#define ANIMATION_CACHE_H
-
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/animation.h"
-
-class AnimationCache : public Object {
-	GDCLASS(AnimationCache, Object);
-
-	struct Path {
-		RES resource;
-		Object *object = nullptr;
-#ifndef _3D_DISABLED
-		Skeleton3D *skeleton = nullptr;
-		Node3D *node_3d = nullptr;
-#endif // _3D_DISABLED
-		Node *node = nullptr;
-
-		int bone_idx = -1;
-		Vector<StringName> subpath;
-		bool valid = false;
-	};
-
-	Set<Node *> connected_nodes;
-	Vector<Path> path_cache;
-
-	Node *root = nullptr;
-	Ref<Animation> animation;
-	bool cache_dirty = true;
-	bool cache_valid = false;
-
-	void _node_exit_tree(Node *p_node);
-
-	void _clear_cache();
-	void _update_cache();
-	void _animation_changed();
-
-protected:
-	static void _bind_methods();
-
-public:
-	void set_track_transform(int p_idx, const Transform3D &p_transform);
-	void set_track_value(int p_idx, const Variant &p_value);
-	void call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
-
-	void set_all(float p_time, float p_delta = 0);
-
-	void set_animation(const Ref<Animation> &p_animation);
-	void set_root(Node *p_root);
-
-	AnimationCache();
-};
-
-#endif // ANIMATION_CACHE_H

+ 134 - 34
scene/animation/animation_player.cpp

@@ -60,7 +60,12 @@ void AnimatedValuesBackup::restore() const {
 		if (entry->bone_idx == -1) {
 			entry->object->set_indexed(entry->subpath, entry->value);
 		} else {
-			Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value);
+			Array arr = entry->value;
+			if (arr.size() == 3) {
+				Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]);
+				Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]);
+				Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[0]);
+			}
 		}
 	}
 }
@@ -242,6 +247,8 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
 
 	p_anim->node_cache.resize(a->get_track_count());
 
+	setup_pass++;
+
 	for (int i = 0; i < a->get_track_count(); i++) {
 		p_anim->node_cache.write[i] = nullptr;
 		RES resource;
@@ -275,46 +282,68 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
 			node_cache_map[key] = TrackNodeCache();
 		}
 
-		p_anim->node_cache.write[i] = &node_cache_map[key];
-		p_anim->node_cache[i]->path = a->track_get_path(i);
-		p_anim->node_cache[i]->node = child;
-		p_anim->node_cache[i]->resource = resource;
-		p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child);
+		TrackNodeCache *node_cache = &node_cache_map[key];
+		p_anim->node_cache.write[i] = node_cache;
+
+		node_cache->path = a->track_get_path(i);
+		node_cache->node = child;
+		node_cache->resource = resource;
+		node_cache->node_2d = Object::cast_to<Node2D>(child);
 #ifndef _3D_DISABLED
-		if (a->track_get_type(i) == Animation::TYPE_TRANSFORM3D) {
+		if (a->track_get_type(i) == Animation::TYPE_POSITION_3D || a->track_get_type(i) == Animation::TYPE_ROTATION_3D || a->track_get_type(i) == Animation::TYPE_SCALE_3D) {
 			// special cases and caches for transform tracks
 
+			if (node_cache->last_setup_pass != setup_pass) {
+				node_cache->loc_used = false;
+				node_cache->rot_used = false;
+				node_cache->scale_used = false;
+			}
+
 			// cache node_3d
-			p_anim->node_cache[i]->node_3d = Object::cast_to<Node3D>(child);
+			node_cache->node_3d = Object::cast_to<Node3D>(child);
 			// cache skeleton
-			p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton3D>(child);
-			if (p_anim->node_cache[i]->skeleton) {
+			node_cache->skeleton = Object::cast_to<Skeleton3D>(child);
+			if (node_cache->skeleton) {
 				if (a->track_get_path(i).get_subname_count() == 1) {
 					StringName bone_name = a->track_get_path(i).get_subname(0);
 
-					p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name);
-					if (p_anim->node_cache[i]->bone_idx < 0) {
+					node_cache->bone_idx = node_cache->skeleton->find_bone(bone_name);
+					if (node_cache->bone_idx < 0) {
 						// broken track (nonexistent bone)
-						p_anim->node_cache[i]->skeleton = nullptr;
-						p_anim->node_cache[i]->node_3d = nullptr;
-						ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0);
+						node_cache->skeleton = nullptr;
+						node_cache->node_3d = nullptr;
+						ERR_CONTINUE(node_cache->bone_idx < 0);
 					}
 				} else {
 					// no property, just use spatialnode
-					p_anim->node_cache[i]->skeleton = nullptr;
+					node_cache->skeleton = nullptr;
+				}
+			}
+
+			switch (a->track_get_type(i)) {
+				case Animation::TYPE_POSITION_3D: {
+					node_cache->loc_used = true;
+				} break;
+				case Animation::TYPE_ROTATION_3D: {
+					node_cache->rot_used = true;
+				} break;
+				case Animation::TYPE_SCALE_3D: {
+					node_cache->scale_used = true;
+				} break;
+				default: {
 				}
 			}
 		}
 #endif // _3D_DISABLED
 
 		if (a->track_get_type(i) == Animation::TYPE_VALUE) {
-			if (!p_anim->node_cache[i]->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
+			if (!node_cache->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
 				TrackNodeCache::PropertyAnim pa;
 				pa.subpath = leftover_path;
 				pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
 				pa.special = SP_NONE;
 				pa.owner = p_anim->node_cache[i];
-				if (false && p_anim->node_cache[i]->node_2d) {
+				if (false && node_cache->node_2d) {
 					if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) {
 						pa.special = SP_NODE2D_POS;
 					} else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) {
@@ -323,20 +352,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
 						pa.special = SP_NODE2D_SCALE;
 					}
 				}
-				p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
+				node_cache->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
 			}
 		}
 
 		if (a->track_get_type(i) == Animation::TYPE_BEZIER && leftover_path.size()) {
-			if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
+			if (!node_cache->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
 				TrackNodeCache::BezierAnim ba;
 				ba.bezier_property = leftover_path;
 				ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
 				ba.owner = p_anim->node_cache[i];
 
-				p_anim->node_cache[i]->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
+				node_cache->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
 			}
 		}
+
+		node_cache->last_setup_pass = setup_pass;
 	}
 }
 
@@ -369,17 +400,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 		}
 
 		switch (a->track_get_type(i)) {
-			case Animation::TYPE_TRANSFORM3D: {
+			case Animation::TYPE_POSITION_3D: {
 #ifndef _3D_DISABLED
 				if (!nc->node_3d) {
 					continue;
 				}
 
 				Vector3 loc;
-				Quaternion rot;
-				Vector3 scale;
 
-				Error err = a->transform_track_interpolate(i, p_time, &loc, &rot, &scale);
+				Error err = a->position_track_interpolate(i, p_time, &loc);
 				//ERR_CONTINUE(err!=OK); //used for testing, should be removed
 
 				if (err != OK) {
@@ -391,12 +420,63 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 					cache_update[cache_update_size++] = nc;
 					nc->accum_pass = accum_pass;
 					nc->loc_accum = loc;
-					nc->rot_accum = rot;
-					nc->scale_accum = scale;
-
+					nc->rot_accum = Quaternion();
+					nc->scale_accum = Vector3();
 				} else {
 					nc->loc_accum = nc->loc_accum.lerp(loc, p_interp);
+				}
+#endif // _3D_DISABLED
+			} break;
+			case Animation::TYPE_ROTATION_3D: {
+#ifndef _3D_DISABLED
+				if (!nc->node_3d) {
+					continue;
+				}
+
+				Quaternion rot;
+
+				Error err = a->rotation_track_interpolate(i, p_time, &rot);
+				//ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+				if (err != OK) {
+					continue;
+				}
+
+				if (nc->accum_pass != accum_pass) {
+					ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
+					cache_update[cache_update_size++] = nc;
+					nc->accum_pass = accum_pass;
+					nc->loc_accum = Vector3();
+					nc->rot_accum = rot;
+					nc->scale_accum = Vector3();
+				} else {
 					nc->rot_accum = nc->rot_accum.slerp(rot, p_interp);
+				}
+#endif // _3D_DISABLED
+			} break;
+			case Animation::TYPE_SCALE_3D: {
+#ifndef _3D_DISABLED
+				if (!nc->node_3d) {
+					continue;
+				}
+
+				Vector3 scale;
+
+				Error err = a->scale_track_interpolate(i, p_time, &scale);
+				//ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+				if (err != OK) {
+					continue;
+				}
+
+				if (nc->accum_pass != accum_pass) {
+					ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
+					cache_update[cache_update_size++] = nc;
+					nc->accum_pass = accum_pass;
+					nc->loc_accum = Vector3();
+					nc->rot_accum = Quaternion();
+					nc->scale_accum = scale;
+				} else {
 					nc->scale_accum = nc->scale_accum.lerp(scale, p_interp);
 				}
 #endif // _3D_DISABLED
@@ -855,15 +935,30 @@ void AnimationPlayer::_animation_update_transforms() {
 			TrackNodeCache *nc = cache_update[i];
 
 			ERR_CONTINUE(nc->accum_pass != accum_pass);
-
-			t.origin = nc->loc_accum;
-			t.basis.set_quaternion_scale(nc->rot_accum, nc->scale_accum);
 #ifndef _3D_DISABLED
 			if (nc->skeleton && nc->bone_idx >= 0) {
-				nc->skeleton->set_bone_pose(nc->bone_idx, t);
+				if (nc->loc_used) {
+					nc->skeleton->set_bone_pose_position(nc->bone_idx, nc->loc_accum);
+				}
+				if (nc->rot_used) {
+					nc->skeleton->set_bone_pose_rotation(nc->bone_idx, nc->rot_accum);
+				}
+				if (nc->scale_used) {
+					nc->skeleton->set_bone_pose_scale(nc->bone_idx, nc->scale_accum);
+				}
+
 			} else if (nc->node_3d) {
-				nc->node_3d->set_transform(t);
+				if (nc->loc_used) {
+					nc->node_3d->set_position(nc->loc_accum);
+				}
+				if (nc->rot_used) {
+					nc->node_3d->set_rotation(nc->rot_accum.get_euler());
+				}
+				if (nc->scale_used) {
+					nc->node_3d->set_scale(nc->scale_accum);
+				}
 			}
+
 #endif // _3D_DISABLED
 		}
 	}
@@ -1527,7 +1622,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o
 			AnimatedValuesBackup::Entry entry;
 			entry.object = nc->skeleton;
 			entry.bone_idx = nc->bone_idx;
-			entry.value = nc->skeleton->get_bone_pose(nc->bone_idx);
+			Array arr;
+			arr.resize(3);
+			arr[0] = nc->skeleton->get_bone_pose_position(nc->bone_idx);
+			arr[1] = nc->skeleton->get_bone_pose_rotation(nc->bone_idx);
+			arr[2] = nc->skeleton->get_bone_pose_scale(nc->bone_idx);
+			entry.value = nc;
 			backup->entries.push_back(entry);
 		} else {
 			if (nc->node_3d) {

+ 7 - 0
scene/animation/animation_player.h

@@ -88,6 +88,8 @@ private:
 		SP_NODE2D_SCALE,
 	};
 
+	uint32_t setup_pass = 1;
+
 	struct TrackNodeCache {
 		NodePath path;
 		uint32_t id = 0;
@@ -101,6 +103,10 @@ private:
 		int bone_idx = -1;
 		// accumulated transforms
 
+		bool loc_used = false;
+		bool rot_used = false;
+		bool scale_used = false;
+
 		Vector3 loc_accum;
 		Quaternion rot_accum;
 		Vector3 scale_accum;
@@ -134,6 +140,7 @@ private:
 
 		Map<StringName, BezierAnim> bezier_anim;
 
+		uint32_t last_setup_pass = 0;
 		TrackNodeCache() {}
 	};
 

+ 208 - 42
scene/animation/animation_tree.cpp

@@ -544,13 +544,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 			NodePath path = anim->track_get_path(i);
 			Animation::TrackType track_type = anim->track_get_type(i);
 
+			Animation::TrackType track_cache_type = track_type;
+			if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) {
+				track_cache_type = Animation::TYPE_POSITION_3D; //reference them as position3D tracks, even if they modify rotation or scale
+			}
+
 			TrackCache *track = nullptr;
 			if (track_cache.has(path)) {
 				track = track_cache.get(path);
 			}
 
 			//if not valid, delete track
-			if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
+			if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
 				playing_caches.erase(track);
 				memdelete(track);
 				track_cache.erase(path);
@@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 						track = track_value;
 
 					} break;
-					case Animation::TYPE_TRANSFORM3D: {
+					case Animation::TYPE_POSITION_3D:
+					case Animation::TYPE_ROTATION_3D:
+					case Animation::TYPE_SCALE_3D: {
 #ifndef _3D_DISABLED
 						Node3D *node_3d = Object::cast_to<Node3D>(child);
 
@@ -597,6 +604,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 						}
 
 						TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
+						track_xform->type = Animation::TYPE_POSITION_3D;
 
 						track_xform->node_3d = node_3d;
 						track_xform->skeleton = nullptr;
@@ -615,6 +623,21 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 						track_xform->object_id = track_xform->object->get_instance_id();
 
 						track = track_xform;
+
+						switch (track_type) {
+							case Animation::TYPE_POSITION_3D: {
+								track_xform->loc_used = true;
+							} break;
+							case Animation::TYPE_ROTATION_3D: {
+								track_xform->rot_used = true;
+							} break;
+							case Animation::TYPE_SCALE_3D: {
+								track_xform->scale_used = true;
+							} break;
+							default: {
+							}
+						}
+
 #endif // _3D_DISABLED
 					} break;
 					case Animation::TYPE_METHOD: {
@@ -670,6 +693,26 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 				}
 
 				track_cache[path] = track;
+			} else if (track_cache_type == Animation::TYPE_POSITION_3D) {
+				TrackCacheTransform *track_xform = static_cast<TrackCacheTransform *>(track);
+				if (track->setup_pass != setup_pass) {
+					track_xform->loc_used = false;
+					track_xform->rot_used = false;
+					track_xform->scale_used = false;
+				}
+				switch (track_type) {
+					case Animation::TYPE_POSITION_3D: {
+						track_xform->loc_used = true;
+					} break;
+					case Animation::TYPE_ROTATION_3D: {
+						track_xform->rot_used = true;
+					} break;
+					case Animation::TYPE_SCALE_3D: {
+						track_xform->scale_used = true;
+					} break;
+					default: {
+					}
+				}
 			}
 
 			track->setup_pass = setup_pass;
@@ -831,8 +874,11 @@ void AnimationTree::_process_graph(real_t p_delta) {
 				ERR_CONTINUE(!track_cache.has(path));
 
 				TrackCache *track = track_cache[path];
-				if (track->type != a->track_get_type(i)) {
-					continue; //may happen should not
+
+				Animation::TrackType ttype = a->track_get_type(i);
+				if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) {
+					//broken animation, but avoid error spamming
+					continue;
 				}
 
 				track->root_motion = root_motion_track == path;
@@ -848,20 +894,81 @@ void AnimationTree::_process_graph(real_t p_delta) {
 					continue; //nothing to blend
 				}
 
-				switch (track->type) {
-					case Animation::TYPE_TRANSFORM3D: {
+				switch (ttype) {
+					case Animation::TYPE_POSITION_3D: {
 #ifndef _3D_DISABLED
 						TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
 
+						if (t->process_pass != process_pass) {
+							t->process_pass = process_pass;
+							t->loc = Vector3();
+							t->rot = Quaternion();
+							t->rot_blend_accum = 0;
+							t->scale = Vector3(1, 1, 1);
+						}
+
 						if (track->root_motion) {
-							if (t->process_pass != process_pass) {
-								t->process_pass = process_pass;
-								t->loc = Vector3();
-								t->rot = Quaternion();
-								t->rot_blend_accum = 0;
-								t->scale = Vector3(1, 1, 1);
+							real_t prev_time = time - delta;
+							if (prev_time < 0) {
+								if (!a->has_loop()) {
+									prev_time = 0;
+								} else {
+									prev_time = a->get_length() + prev_time;
+								}
 							}
 
+							Vector3 loc[2];
+
+							if (prev_time > time) {
+								Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+								if (err != OK) {
+									continue;
+								}
+
+								a->position_track_interpolate(i, a->get_length(), &loc[1]);
+
+								t->loc += (loc[1] - loc[0]) * blend;
+								prev_time = 0;
+							}
+
+							Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+							if (err != OK) {
+								continue;
+							}
+
+							a->position_track_interpolate(i, time, &loc[1]);
+
+							t->loc += (loc[1] - loc[0]) * blend;
+
+							prev_time = 0;
+
+						} else {
+							Vector3 loc;
+
+							Error err = a->position_track_interpolate(i, time, &loc);
+							//ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+							if (err != OK) {
+								continue;
+							}
+
+							t->loc = t->loc.lerp(loc, blend);
+						}
+#endif // _3D_DISABLED
+					} break;
+					case Animation::TYPE_ROTATION_3D: {
+#ifndef _3D_DISABLED
+						TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
+						if (t->process_pass != process_pass) {
+							t->process_pass = process_pass;
+							t->loc = Vector3();
+							t->rot = Quaternion();
+							t->rot_blend_accum = 0;
+							t->scale = Vector3(1, 1, 1);
+						}
+
+						if (track->root_motion) {
 							real_t prev_time = time - delta;
 							if (prev_time < 0) {
 								if (!a->has_loop()) {
@@ -871,61 +978,44 @@ void AnimationTree::_process_graph(real_t p_delta) {
 								}
 							}
 
-							Vector3 loc[2];
 							Quaternion rot[2];
-							Vector3 scale[2];
 
 							if (prev_time > time) {
-								Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+								Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
 								if (err != OK) {
 									continue;
 								}
 
-								a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]);
+								a->rotation_track_interpolate(i, a->get_length(), &rot[1]);
 
-								t->loc += (loc[1] - loc[0]) * blend;
-								t->scale += (scale[1] - scale[0]) * blend;
 								Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
 								t->rot = (t->rot * q).normalized();
 
 								prev_time = 0;
 							}
 
-							Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+							Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
 							if (err != OK) {
 								continue;
 							}
 
-							a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]);
+							a->rotation_track_interpolate(i, time, &rot[1]);
 
-							t->loc += (loc[1] - loc[0]) * blend;
-							t->scale += (scale[1] - scale[0]) * blend;
 							Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
 							t->rot = (t->rot * q).normalized();
 
 							prev_time = 0;
 
 						} else {
-							Vector3 loc;
 							Quaternion rot;
-							Vector3 scale;
 
-							Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale);
+							Error err = a->rotation_track_interpolate(i, time, &rot);
 							//ERR_CONTINUE(err!=OK); //used for testing, should be removed
 
-							if (t->process_pass != process_pass) {
-								t->process_pass = process_pass;
-								t->loc = loc;
-								t->rot = rot;
-								t->rot_blend_accum = 0;
-								t->scale = scale;
-							}
-
 							if (err != OK) {
 								continue;
 							}
 
-							t->loc = t->loc.lerp(loc, blend);
 							if (t->rot_blend_accum == 0) {
 								t->rot = rot;
 								t->rot_blend_accum = blend;
@@ -934,6 +1024,67 @@ void AnimationTree::_process_graph(real_t p_delta) {
 								t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
 								t->rot_blend_accum = rot_total;
 							}
+						}
+#endif // _3D_DISABLED
+					} break;
+					case Animation::TYPE_SCALE_3D: {
+#ifndef _3D_DISABLED
+						TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
+						if (t->process_pass != process_pass) {
+							t->process_pass = process_pass;
+							t->loc = Vector3();
+							t->rot = Quaternion();
+							t->rot_blend_accum = 0;
+							t->scale = Vector3(1, 1, 1);
+						}
+
+						if (track->root_motion) {
+							real_t prev_time = time - delta;
+							if (prev_time < 0) {
+								if (!a->has_loop()) {
+									prev_time = 0;
+								} else {
+									prev_time = a->get_length() + prev_time;
+								}
+							}
+
+							Vector3 scale[2];
+
+							if (prev_time > time) {
+								Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+								if (err != OK) {
+									continue;
+								}
+
+								a->scale_track_interpolate(i, a->get_length(), &scale[1]);
+
+								t->scale += (scale[1] - scale[0]) * blend;
+
+								prev_time = 0;
+							}
+
+							Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+							if (err != OK) {
+								continue;
+							}
+
+							a->scale_track_interpolate(i, time, &scale[1]);
+
+							t->scale += (scale[1] - scale[0]) * blend;
+
+							prev_time = 0;
+
+						} else {
+							Vector3 scale;
+
+							Error err = a->scale_track_interpolate(i, time, &scale);
+							//ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+							if (err != OK) {
+								continue;
+							}
+
 							t->scale = t->scale.lerp(scale, blend);
 						}
 #endif // _3D_DISABLED
@@ -1198,26 +1349,41 @@ void AnimationTree::_process_graph(real_t p_delta) {
 			}
 
 			switch (track->type) {
-				case Animation::TYPE_TRANSFORM3D: {
+				case Animation::TYPE_POSITION_3D: {
 #ifndef _3D_DISABLED
 					TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
 
-					Transform3D xform;
-					xform.origin = t->loc;
-
-					xform.basis.set_quaternion_scale(t->rot, t->scale);
-
 					if (t->root_motion) {
+						Transform3D xform;
+						xform.origin = t->loc;
+						xform.basis.set_quaternion_scale(t->rot, t->scale);
+
 						root_motion_transform = xform;
 
 						if (t->skeleton && t->bone_idx >= 0) {
 							root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse();
 						}
 					} else if (t->skeleton && t->bone_idx >= 0) {
-						t->skeleton->set_bone_pose(t->bone_idx, xform);
+						if (t->loc_used) {
+							t->skeleton->set_bone_pose_position(t->bone_idx, t->loc);
+						}
+						if (t->rot_used) {
+							t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot);
+						}
+						if (t->scale_used) {
+							t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale);
+						}
 
 					} else if (!t->skeleton) {
-						t->node_3d->set_transform(xform);
+						if (t->loc_used) {
+							t->node_3d->set_position(t->loc);
+						}
+						if (t->rot_used) {
+							t->node_3d->set_rotation(t->rot.get_euler());
+						}
+						if (t->scale_used) {
+							t->node_3d->set_scale(t->scale);
+						}
 					}
 #endif // _3D_DISABLED
 				} break;

+ 4 - 1
scene/animation/animation_tree.h

@@ -197,13 +197,16 @@ private:
 		Skeleton3D *skeleton = nullptr;
 #endif // _3D_DISABLED
 		int bone_idx = -1;
+		bool loc_used = false;
+		bool rot_used = false;
+		bool scale_used = false;
 		Vector3 loc;
 		Quaternion rot;
 		real_t rot_blend_accum = 0.0;
 		Vector3 scale;
 
 		TrackCacheTransform() {
-			type = Animation::TYPE_TRANSFORM3D;
+			type = Animation::TYPE_POSITION_3D;
 		}
 	};
 

File diff suppressed because it is too large
+ 624 - 342
scene/resources/animation.cpp


+ 42 - 32
scene/resources/animation.h

@@ -42,7 +42,9 @@ class Animation : public Resource {
 public:
 	enum TrackType {
 		TYPE_VALUE, ///< Set a value in a property, can be interpolated.
-		TYPE_TRANSFORM3D, ///< Transform a node or a bone.
+		TYPE_POSITION_3D, ///< Position 3D track
+		TYPE_ROTATION_3D, ///< Rotation 3D track
+		TYPE_SCALE_3D, ///< Scale 3D track
 		TYPE_METHOD, ///< Call any method on a specific node.
 		TYPE_BEZIER, ///< Bezier curve
 		TYPE_AUDIO,
@@ -86,21 +88,31 @@ private:
 		T value;
 	};
 
-	struct TransformKey {
-		Vector3 loc;
-		Quaternion rot;
-		Vector3 scale;
+	const int32_t POSITION_TRACK_SIZE = 5;
+	const int32_t ROTATION_TRACK_SIZE = 6;
+	const int32_t SCALE_TRACK_SIZE = 5;
+
+	/* POSITION TRACK */
+
+	struct PositionTrack : public Track {
+		Vector<TKey<Vector3>> positions;
+
+		PositionTrack() { type = TYPE_POSITION_3D; }
 	};
 
-	// Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey.
-	const int32_t TRANSFORM_TRACK_SIZE = 12;
+	/* ROTATION TRACK */
 
-	/* TRANSFORM TRACK */
+	struct RotationTrack : public Track {
+		Vector<TKey<Quaternion>> rotations;
 
-	struct TransformTrack : public Track {
-		Vector<TKey<TransformKey>> transforms;
+		RotationTrack() { type = TYPE_ROTATION_3D; }
+	};
 
-		TransformTrack() { type = TYPE_TRANSFORM3D; }
+	/* SCALE TRACK */
+
+	struct ScaleTrack : public Track {
+		Vector<TKey<Vector3>> scales;
+		ScaleTrack() { type = TYPE_SCALE_3D; }
 	};
 
 	/* PROPERTY VALUE TRACK */
@@ -186,14 +198,11 @@ private:
 	template <class K>
 	inline int _find(const Vector<K> &p_keys, double p_time) const;
 
-	_FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const;
-
 	_FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
 	_FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
 	_FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const;
 	_FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const;
 
-	_FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const;
 	_FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const;
 	_FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const;
 	_FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const;
@@ -214,18 +223,6 @@ private:
 
 	// bind helpers
 private:
-	Array _transform_track_interpolate(int p_track, double p_time) const {
-		Vector3 loc;
-		Quaternion rot;
-		Vector3 scale;
-		transform_track_interpolate(p_track, p_time, &loc, &rot, &scale);
-		Array ret;
-		ret.push_back(loc);
-		ret.push_back(rot);
-		ret.push_back(scale);
-		return ret;
-	}
-
 	Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
 		List<int> idxs;
 		value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
@@ -247,8 +244,13 @@ private:
 		return idxr;
 	}
 
-	bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm);
-	void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
+	bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm);
+	bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle);
+	bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error);
+
+	void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err);
+	void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle);
+	void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err);
 
 protected:
 	bool _set(const StringName &p_name, const Variant &p_value);
@@ -294,8 +296,18 @@ public:
 	double track_get_key_time(int p_track, int p_key_idx) const;
 	real_t track_get_key_transition(int p_track, int p_key_idx) const;
 
-	int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3());
-	Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
+	int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
+	Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const;
+	Error position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+
+	int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation);
+	Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const;
+	Error rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const;
+
+	int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale);
+	Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const;
+	Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+
 	void track_set_interpolation_type(int p_track, InterpolationType p_interp);
 	InterpolationType track_get_interpolation_type(int p_track) const;
 
@@ -324,8 +336,6 @@ public:
 	void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
 	bool track_get_interpolation_loop_wrap(int p_track) const;
 
-	Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
-
 	Variant value_track_interpolate(int p_track, double p_time) const;
 	void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
 	void value_track_set_update_mode(int p_track, UpdateMode p_mode);

Some files were not shown because too many files changed in this diff