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.
 				Returns the arguments values to be called on a method track for a given key in a given track.
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="remove_track">
 			<return type="void" />
 			<return type="void" />
 			<argument index="0" name="track_idx" type="int" />
 			<argument index="0" name="track_idx" type="int" />
@@ -260,6 +268,22 @@
 				Removes a track by specifying the track index.
 				Removes a track by specifying the track index.
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="track_find_key" qualifiers="const">
 			<return type="int" />
 			<return type="int" />
 			<argument index="0" name="track_idx" 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].
 				Swaps the track [code]idx[/code]'s index position with the track [code]with_idx[/code].
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="value_track_get_key_indices" qualifiers="const">
 			<return type="PackedInt32Array" />
 			<return type="PackedInt32Array" />
 			<argument index="0" name="track_idx" type="int" />
 			<argument index="0" name="track_idx" type="int" />
@@ -541,19 +546,22 @@
 		<constant name="TYPE_VALUE" value="0" enum="TrackType">
 		<constant name="TYPE_VALUE" value="0" enum="TrackType">
 			Value tracks set values in node properties, but only those which can be Interpolated.
 			Value tracks set values in node properties, but only those which can be Interpolated.
 		</constant>
 		</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>
-		<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.
 			Method tracks call functions with given arguments per key.
 		</constant>
 		</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]).
 			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>
-		<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.
 			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>
-		<constant name="TYPE_ANIMATION" value="5" enum="TrackType">
+		<constant name="TYPE_ANIMATION" value="7" enum="TrackType">
 			Animation tracks play animations in other [AnimationPlayer] nodes.
 			Animation tracks play animations in other [AnimationPlayer] nodes.
 		</constant>
 		</constant>
 		<constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType">
 		<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">
 		<method name="get_root_motion_transform" qualifiers="const">
 			<return type="Transform3D" />
 			<return type="Transform3D" />
 			<description>
 			<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>
 			</description>
 		</method>
 		</method>
 		<method name="rename_parameter">
 		<method name="rename_parameter">
@@ -45,7 +45,7 @@
 		</member>
 		</member>
 		<member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath(&quot;&quot;)">
 		<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].
 			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>
 		<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
 		<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
 			The root animation node of this [AnimationTree]. See [AnimationNode].
 			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.
 				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>
 			</description>
 		</method>
 		</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">
 		<method name="get_bone_rest" qualifiers="const">
 			<return type="Transform3D" />
 			<return type="Transform3D" />
 			<argument index="0" name="bone_idx" type="int" />
 			<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].
 				[b]Note:[/b] [code]parent_idx[/code] must be less than [code]bone_idx[/code].
 			</description>
 			</description>
 		</method>
 		</method>
-		<method name="set_bone_pose">
+		<method name="set_bone_pose_position">
 			<return type="void" />
 			<return type="void" />
 			<argument index="0" name="bone_idx" type="int" />
 			<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>
 			<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>
 			</description>
 		</method>
 		</method>
 		<method name="set_bone_rest">
 		<method name="set_bone_rest">

+ 202 - 94
editor/animation_track_editor.cpp

@@ -165,20 +165,38 @@ public:
 		}
 		}
 
 
 		switch (animation->track_get_type(track)) {
 		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;
 			} break;
 			case Animation::TYPE_VALUE: {
 			case Animation::TYPE_VALUE: {
 				if (name == "value") {
 				if (name == "value") {
@@ -412,12 +430,13 @@ public:
 		}
 		}
 
 
 		switch (animation->track_get_type(track)) {
 		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;
 			} break;
 			case Animation::TYPE_VALUE: {
 			case Animation::TYPE_VALUE: {
 				if (name == "value") {
 				if (name == "value") {
@@ -523,11 +542,14 @@ public:
 		}
 		}
 
 
 		switch (animation->track_get_type(track)) {
 		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"));
 				p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
-
 			} break;
 			} break;
 			case Animation::TYPE_VALUE: {
 			case Animation::TYPE_VALUE: {
 				Variant v = animation->track_get_key_value(track, key);
 				Variant v = animation->track_get_key_value(track, key);
@@ -779,17 +801,31 @@ public:
 				}
 				}
 
 
 				switch (animation->track_get_type(track)) {
 				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) {
 						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;
 							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;
 						update_obj = true;
 					} break;
 					} break;
 					case Animation::TYPE_VALUE: {
 					case Animation::TYPE_VALUE: {
@@ -1009,11 +1045,13 @@ public:
 				}
 				}
 
 
 				switch (animation->track_get_type(track)) {
 				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;
 					} break;
 					case Animation::TYPE_VALUE: {
 					case Animation::TYPE_VALUE: {
@@ -1159,9 +1197,13 @@ public:
 
 
 		if (same_track_type) {
 		if (same_track_type) {
 			switch (animation->track_get_type(first_track)) {
 			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"));
 					p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
 				} break;
 				} break;
 				case Animation::TYPE_VALUE: {
 				case Animation::TYPE_VALUE: {
@@ -1359,7 +1401,9 @@ void AnimationTimelineEdit::_notification(int p_what) {
 
 
 		add_track->get_popup()->clear();
 		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("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("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("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"));
 		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"));
 		Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
 		int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
 		int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
 		Color color = get_theme_color(SNAME("font_color"), 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("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("KeyCall"), SNAME("EditorIcons")),
 			get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
 			get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
 			get_theme_icon(SNAME("KeyAudio"), 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.position.y = int(get_size().height - icon->get_height()) / 2;
 				interp_mode_rect.size = icon->get_size();
 				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);
 					draw_texture(icon, interp_mode_rect.position);
 				}
 				}
 				// Make it easier to click.
 				// Make it easier to click.
@@ -2031,7 +2077,7 @@ void AnimationTrackEdit::_notification(int p_what) {
 				ofs += icon->get_width() + hsep;
 				ofs += icon->get_width() + hsep;
 				interp_mode_rect.size.x += 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));
 					draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
 					interp_mode_rect.size.x += down_icon->get_width();
 					interp_mode_rect.size.x += down_icon->get_width();
 				} else {
 				} 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.position.y = int(get_size().height - icon->get_height()) / 2;
 				loop_mode_rect.size = icon->get_size();
 				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);
 					draw_texture(icon, loop_mode_rect.position);
 				}
 				}
 
 
@@ -2064,7 +2110,7 @@ void AnimationTrackEdit::_notification(int p_what) {
 				ofs += icon->get_width() + hsep;
 				ofs += icon->get_width() + hsep;
 				loop_mode_rect.size.x += 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));
 					draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
 					loop_mode_rect.size.x += down_icon->get_width();
 					loop_mode_rect.size.x += down_icon->get_width();
 				} else {
 				} else {
@@ -2284,9 +2330,11 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati
 	track = p_track;
 	track = p_track;
 	update();
 	update();
 
 
-	Ref<Texture2D> type_icons[6] = {
+	Ref<Texture2D> type_icons[8] = {
 		get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
 		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("KeyCall"), SNAME("EditorIcons")),
 		get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
 		get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
 		get_theme_icon(SNAME("KeyAudio"), 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) {
 		if (key_idx != -1) {
 			String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n";
 			String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n";
 			switch (animation->track_get_type(track)) {
 			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;
 				} break;
 				case Animation::TYPE_VALUE: {
 				case Animation::TYPE_VALUE: {
 					const Variant &v = animation->track_get_key_value(track, key_idx);
 					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]];
 			[[fallthrough]];
 		case Animation::TYPE_BEZIER:
 		case Animation::TYPE_BEZIER:
 			[[fallthrough]];
 			[[fallthrough]];
-		case Animation::TYPE_TRANSFORM3D:
+		case Animation::TYPE_POSITION_3D:
+			[[fallthrough]];
+		case Animation::TYPE_ROTATION_3D:
+			[[fallthrough]];
+		case Animation::TYPE_SCALE_3D:
 			return true;
 			return true;
 		default:
 		default:
 			return false;
 			return false;
@@ -3483,33 +3535,50 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
 
 
 	NodePath np = path;
 	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++) {
 	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) {
 		if (animation->track_get_path(i) != np) {
 			continue;
 			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;
 	InsertData id;
-	Dictionary val;
-
 	id.path = np;
 	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.
 	// 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.query = vformat(TTR("node '%s'"), p_node->get_name());
 	id.advance = false;
 	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) {
 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);
 	int track_id = animation->find_track(path);
 	if (track_id >= 0) {
 	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;
 			return true;
 		}
 		}
 	}
 	}
@@ -3977,18 +4046,13 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
 	Variant value;
 	Variant value;
 
 
 	switch (p_id.type) {
 	switch (p_id.type) {
+		case Animation::TYPE_POSITION_3D:
+		case Animation::TYPE_ROTATION_3D:
+		case Animation::TYPE_SCALE_3D:
 		case Animation::TYPE_VALUE: {
 		case Animation::TYPE_VALUE: {
 			value = p_id.value;
 			value = p_id.value;
 
 
 		} break;
 		} 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: {
 		case Animation::TYPE_BEZIER: {
 			Array array;
 			Array array;
 			array.resize(5);
 			array.resize(5);
@@ -4404,8 +4468,8 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
 	ERR_FAIL_COND(!node);
 	ERR_FAIL_COND(!node);
 	NodePath path_to = root->get_path_to(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;
 		return;
 	}
 	}
 
 
@@ -4415,7 +4479,9 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
 			prop_selector->set_type_filter(Vector<Variant::Type>());
 			prop_selector->set_type_filter(Vector<Variant::Type>());
 			prop_selector->select_property_from_instance(node);
 			prop_selector->select_property_from_instance(node);
 		} break;
 		} break;
-		case Animation::TYPE_TRANSFORM3D:
+		case Animation::TYPE_POSITION_3D:
+		case Animation::TYPE_ROTATION_3D:
+		case Animation::TYPE_SCALE_3D:
 		case Animation::TYPE_METHOD: {
 		case Animation::TYPE_METHOD: {
 			undo_redo->create_action(TTR("Add Track"));
 			undo_redo->create_action(TTR("Add Track"));
 			undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
 			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)) {
 	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))) {
 			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."));
 				EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
 				return;
 				return;
@@ -4596,14 +4682,30 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
 				return;
 				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->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
 			undo_redo->commit_action();
 			undo_redo->commit_action();
 
 
@@ -5277,8 +5379,14 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
 				}
 				}
 
 
 				switch (animation->track_get_type(i)) {
 				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;
 						break;
 					case Animation::TYPE_METHOD:
 					case Animation::TYPE_METHOD:
 						text += " (Methods)";
 						text += " (Methods)";

+ 1 - 1
editor/animation_track_editor.h

@@ -35,7 +35,7 @@
 #include "editor/editor_spin_slider.h"
 #include "editor/editor_spin_slider.h"
 #include "editor/property_editor.h"
 #include "editor/property_editor.h"
 #include "editor/property_selector.h"
 #include "editor/property_selector.h"
-#include "scene/animation/animation_cache.h"
+
 #include "scene/gui/control.h"
 #include "scene/gui/control.h"
 #include "scene/gui/file_dialog.h"
 #include "scene/gui/file_dialog.h"
 #include "scene/gui/menu_button.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 _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);
 	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 _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;
 	Set<String> tracks_in_clips;
 	Vector<String> missing_textures;
 	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();
 	_fix_param_animation_tracks();
 	for (int i = 0; i < collada.state.animation_clips.size(); i++) {
 	for (int i = 0; i < collada.state.animation_clips.size(); i++) {
 		for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) {
 		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++) {
 	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));
 	Ref<Animation> animation = Ref<Animation>(memnew(Animation));
 
 
 	if (p_clip == -1) {
 	if (p_clip == -1) {
@@ -1522,10 +1522,55 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
 			continue;
 			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;
 		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();
 			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);
 			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;
 			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) {
 		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) {
 		if (found_anim) {
 			tracks_found = true;
 			tracks_found = true;
 		} else {
 		} 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) {
 	if (p_flags & IMPORT_ANIMATION) {
-		state.create_animations(true, true);
+		state.create_animations(true);
 		AnimationPlayer *ap = memnew(AnimationPlayer);
 		AnimationPlayer *ap = memnew(AnimationPlayer);
 		for (int i = 0; i < state.animations.size(); i++) {
 		for (int i = 0; i < state.animations.size(); i++) {
 			String name;
 			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);
 	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 + "'.");
 	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) {
 	if (state.scene) {
 		memdelete(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 "core/io/resource_saver.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
-#include "editor/import/editor_importer_bake_reset.h"
+
 #include "editor/import/scene_import_settings.h"
 #include "editor/import/scene_import_settings.h"
 #include "scene/3d/area_3d.h"
 #include "scene/3d/area_3d.h"
 #include "scene/3d/collision_shape_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));
 						new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
 
 
 						if (kt > (from + 0.01) && k > 0) {
 						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;
 								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;
 								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);
 								Variant var = default_anim->value_track_interpolate(j, from);
 								new_anim->track_insert_key(dtrack, 0, var);
 								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;
 						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;
 						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);
 						Variant var = default_anim->track_get_key_value(j, k);
 						new_anim->track_insert_key(dtrack, kt - from, var);
 						new_anim->track_insert_key(dtrack, kt - from, var);
 					}
 					}
 				}
 				}
 
 
 				if (dtrack != -1 && kt >= to) {
 				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;
 						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;
 						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);
 						Variant var = default_anim->value_track_interpolate(j, to);
 						new_anim->track_insert_key(dtrack, to - from, var);
 						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));
 				new_anim->add_track(default_anim->track_get_type(j));
 				dtrack = new_anim->get_track_count() - 1;
 				dtrack = new_anim->get_track_count() - 1;
 				new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
 				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;
 					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;
 					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);
 					Variant var = default_anim->value_track_interpolate(j, from);
 					new_anim->track_insert_key(dtrack, 0, var);
 					new_anim->track_insert_key(dtrack, 0, var);
 					Variant to_var = default_anim->value_track_interpolate(j, to);
 					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_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_angular_error"), 0.01));
 			r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22));
 			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));
 			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++) {
 			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::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, "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/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::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), ""));
 	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);
 	_pre_fix_node(scene, scene, collision_map);
 	_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
 	_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"];
 	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.
 	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,
 		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 uint32_t get_import_flags() const;
 	virtual void get_extensions(List<String> *r_extensions) 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);
 	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);
 	t.origin = orig + to_local.xform(sub);
 
 
 	// Apply transform.
 	// 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) {
 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.
 						// track count is 5.
 						// next track id is 5.
 						// next track id is 5.
 						const uint64_t target_id = track.key;
 						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;
 						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.
 						// 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.
 						// if this is a skeleton mapped track we can just set the path for the track.
 						// todo: implement node paths here at some
 						// 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 (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) {
 							if (bone->fbx_skeleton.is_valid() && bone.is_valid()) {
 							if (bone->fbx_skeleton.is_valid() && bone.is_valid()) {
 								Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton;
 								Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton;
 								String bone_path = state.root->get_path_to(fbx_skeleton->skeleton);
 								String bone_path = state.root->get_path_to(fbx_skeleton->skeleton);
 								bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id);
 								bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id);
 								print_verbose("[doc] track bone path: " + bone_path);
 								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)) {
 						} else if (state.fbx_target_map.has(target_id)) {
 							//print_verbose("[doc] we have a valid target for a node animation");
 							//print_verbose("[doc] we have a valid target for a node animation");
 							Ref<FBXNode> target_node = state.fbx_target_map[target_id];
 							Ref<FBXNode> target_node = state.fbx_target_map[target_id];
 							if (target_node.is_valid() && target_node->godot_node != nullptr) {
 							if (target_node.is_valid() && target_node->godot_node != nullptr) {
 								String node_path = state.root->get_path_to(target_node->godot_node);
 								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);
 								//print_verbose("[doc] node animation path: " + node_path);
 							}
 							}
 						} else {
 						} 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();
 						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 + ")");
 						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) {
 						while (true) {
 							Vector3 pos = def_pos;
 							Vector3 pos = def_pos;
 							Quaternion rot = def_rot;
 							Quaternion rot = def_rot;
@@ -1220,7 +1241,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
 								pos = t.origin;
 								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) {
 							if (last) {
 								break;
 								break;

+ 84 - 12
modules/gltf/gltf_document.cpp

@@ -5733,7 +5733,7 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> {
 template <class T>
 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) {
 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());
 	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.");
 		ERR_PRINT_ONCE("The interpolated values are not corresponding to its times.");
 		return p_values[0];
 		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;
 		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) {
 		if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
 			//make transform track
 			//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
 			//first determine animation length
 
 
 			const double increment = 1.0 / bake_fps;
 			const double increment = 1.0 / bake_fps;
@@ -5880,15 +5938,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 			Quaternion base_rot;
 			Quaternion base_rot;
 			Vector3 base_scale = Vector3(1, 1, 1);
 			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();
 				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;
 				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;
 				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;
 				Quaternion rot = base_rot;
 				Vector3 scale = base_scale;
 				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);
 					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);
 					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);
 					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;
 					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) {
 				if (last) {
 					break;
 					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) {
 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);
 	Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i);
 
 
 	GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
 	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++) {
 	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);
 		times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i);
 	}
 	}
+
+
 	if (track_type == Animation::TYPE_TRANSFORM3D) {
 	if (track_type == Animation::TYPE_TRANSFORM3D) {
 		p_track.position_track.times = times;
 		p_track.position_track.times = times;
 		p_track.position_track.interpolation = gltf_interpolation;
 		p_track.position_track.interpolation = gltf_interpolation;
@@ -6323,7 +6395,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
 			}
 			}
 		}
 		}
 	}
 	}
-
+#endif
 	return p_track;
 	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);
 		set_bone_rest(which, p_value);
 	} else if (what == "enabled") {
 	} else if (what == "enabled") {
 		set_bone_enabled(which, p_value);
 		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 {
 	} else {
 		return false;
 		return false;
 	}
 	}
@@ -135,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
 		r_ret = get_bone_rest(which);
 		r_ret = get_bone_rest(which);
 	} else if (what == "enabled") {
 	} else if (what == "enabled") {
 		r_ret = is_bone_enabled(which);
 		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 {
 	} else {
 		return false;
 		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::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::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::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
 #ifndef _3D_DISABLED
@@ -657,19 +667,60 @@ void Skeleton3D::clear_bones() {
 
 
 // Posing api
 // 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();
 	const int bone_size = bones.size();
 	ERR_FAIL_INDEX(p_bone, bone_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()) {
 	if (is_inside_tree()) {
 		_make_dirty();
 		_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 {
 Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
 	const int bone_size = bones.size();
 	const int bone_size = bones.size();
 	ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
 	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) {
 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 (b.disable_rest) {
 			if (bone_enabled) {
 			if (bone_enabled) {
-				Transform3D pose = b.pose;
+				b.update_pose_cache();
+				Transform3D pose = b.pose_cache;
 				if (b.custom_pose_enable) {
 				if (b.custom_pose_enable) {
 					pose = b.custom_pose * pose;
 					pose = b.custom_pose * pose;
 				}
 				}
@@ -1012,7 +1064,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
 
 
 		} else {
 		} else {
 			if (bone_enabled) {
 			if (bone_enabled) {
-				Transform3D pose = b.pose;
+				b.update_pose_cache();
+				Transform3D pose = b.pose_cache;
 				if (b.custom_pose_enable) {
 				if (b.custom_pose_enable) {
 					pose = b.custom_pose * pose;
 					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("clear_bones"), &Skeleton3D::clear_bones);
 
 
 	ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
 	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("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));
 	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;
 		bool disable_rest = false;
 		Transform3D rest;
 		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;
 		Transform3D pose_global_no_override;
 		Transform3D pose_global_no_override;
 
 
@@ -206,9 +218,16 @@ public:
 
 
 	// posing api
 	// 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;
 	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);
 	void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose);
 	Transform3D get_bone_custom_pose(int p_bone) const;
 	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) {
 		if (entry->bone_idx == -1) {
 			entry->object->set_indexed(entry->subpath, entry->value);
 			entry->object->set_indexed(entry->subpath, entry->value);
 		} else {
 		} 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());
 	p_anim->node_cache.resize(a->get_track_count());
 
 
+	setup_pass++;
+
 	for (int i = 0; i < a->get_track_count(); i++) {
 	for (int i = 0; i < a->get_track_count(); i++) {
 		p_anim->node_cache.write[i] = nullptr;
 		p_anim->node_cache.write[i] = nullptr;
 		RES resource;
 		RES resource;
@@ -275,46 +282,68 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
 			node_cache_map[key] = TrackNodeCache();
 			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
 #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
 			// 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
 			// 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
 			// 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) {
 				if (a->track_get_path(i).get_subname_count() == 1) {
 					StringName bone_name = a->track_get_path(i).get_subname(0);
 					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)
 						// 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 {
 				} else {
 					// no property, just use spatialnode
 					// 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
 #endif // _3D_DISABLED
 
 
 		if (a->track_get_type(i) == Animation::TYPE_VALUE) {
 		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;
 				TrackNodeCache::PropertyAnim pa;
 				pa.subpath = leftover_path;
 				pa.subpath = leftover_path;
 				pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
 				pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
 				pa.special = SP_NONE;
 				pa.special = SP_NONE;
 				pa.owner = p_anim->node_cache[i];
 				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) {
 					if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) {
 						pa.special = SP_NODE2D_POS;
 						pa.special = SP_NODE2D_POS;
 					} else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) {
 					} 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;
 						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 (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;
 				TrackNodeCache::BezierAnim ba;
 				ba.bezier_property = leftover_path;
 				ba.bezier_property = leftover_path;
 				ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
 				ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
 				ba.owner = p_anim->node_cache[i];
 				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)) {
 		switch (a->track_get_type(i)) {
-			case Animation::TYPE_TRANSFORM3D: {
+			case Animation::TYPE_POSITION_3D: {
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 				if (!nc->node_3d) {
 				if (!nc->node_3d) {
 					continue;
 					continue;
 				}
 				}
 
 
 				Vector3 loc;
 				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
 				//ERR_CONTINUE(err!=OK); //used for testing, should be removed
 
 
 				if (err != OK) {
 				if (err != OK) {
@@ -391,12 +420,63 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 					cache_update[cache_update_size++] = nc;
 					cache_update[cache_update_size++] = nc;
 					nc->accum_pass = accum_pass;
 					nc->accum_pass = accum_pass;
 					nc->loc_accum = loc;
 					nc->loc_accum = loc;
-					nc->rot_accum = rot;
-					nc->scale_accum = scale;
-
+					nc->rot_accum = Quaternion();
+					nc->scale_accum = Vector3();
 				} else {
 				} else {
 					nc->loc_accum = nc->loc_accum.lerp(loc, p_interp);
 					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);
 					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);
 					nc->scale_accum = nc->scale_accum.lerp(scale, p_interp);
 				}
 				}
 #endif // _3D_DISABLED
 #endif // _3D_DISABLED
@@ -855,15 +935,30 @@ void AnimationPlayer::_animation_update_transforms() {
 			TrackNodeCache *nc = cache_update[i];
 			TrackNodeCache *nc = cache_update[i];
 
 
 			ERR_CONTINUE(nc->accum_pass != accum_pass);
 			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
 #ifndef _3D_DISABLED
 			if (nc->skeleton && nc->bone_idx >= 0) {
 			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) {
 			} 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
 #endif // _3D_DISABLED
 		}
 		}
 	}
 	}
@@ -1527,7 +1622,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o
 			AnimatedValuesBackup::Entry entry;
 			AnimatedValuesBackup::Entry entry;
 			entry.object = nc->skeleton;
 			entry.object = nc->skeleton;
 			entry.bone_idx = nc->bone_idx;
 			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);
 			backup->entries.push_back(entry);
 		} else {
 		} else {
 			if (nc->node_3d) {
 			if (nc->node_3d) {

+ 7 - 0
scene/animation/animation_player.h

@@ -88,6 +88,8 @@ private:
 		SP_NODE2D_SCALE,
 		SP_NODE2D_SCALE,
 	};
 	};
 
 
+	uint32_t setup_pass = 1;
+
 	struct TrackNodeCache {
 	struct TrackNodeCache {
 		NodePath path;
 		NodePath path;
 		uint32_t id = 0;
 		uint32_t id = 0;
@@ -101,6 +103,10 @@ private:
 		int bone_idx = -1;
 		int bone_idx = -1;
 		// accumulated transforms
 		// accumulated transforms
 
 
+		bool loc_used = false;
+		bool rot_used = false;
+		bool scale_used = false;
+
 		Vector3 loc_accum;
 		Vector3 loc_accum;
 		Quaternion rot_accum;
 		Quaternion rot_accum;
 		Vector3 scale_accum;
 		Vector3 scale_accum;
@@ -134,6 +140,7 @@ private:
 
 
 		Map<StringName, BezierAnim> bezier_anim;
 		Map<StringName, BezierAnim> bezier_anim;
 
 
+		uint32_t last_setup_pass = 0;
 		TrackNodeCache() {}
 		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);
 			NodePath path = anim->track_get_path(i);
 			Animation::TrackType track_type = anim->track_get_type(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;
 			TrackCache *track = nullptr;
 			if (track_cache.has(path)) {
 			if (track_cache.has(path)) {
 				track = track_cache.get(path);
 				track = track_cache.get(path);
 			}
 			}
 
 
 			//if not valid, delete track
 			//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);
 				playing_caches.erase(track);
 				memdelete(track);
 				memdelete(track);
 				track_cache.erase(path);
 				track_cache.erase(path);
@@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 						track = track_value;
 						track = track_value;
 
 
 					} break;
 					} break;
-					case Animation::TYPE_TRANSFORM3D: {
+					case Animation::TYPE_POSITION_3D:
+					case Animation::TYPE_ROTATION_3D:
+					case Animation::TYPE_SCALE_3D: {
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 						Node3D *node_3d = Object::cast_to<Node3D>(child);
 						Node3D *node_3d = Object::cast_to<Node3D>(child);
 
 
@@ -597,6 +604,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 						}
 						}
 
 
 						TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
 						TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
+						track_xform->type = Animation::TYPE_POSITION_3D;
 
 
 						track_xform->node_3d = node_3d;
 						track_xform->node_3d = node_3d;
 						track_xform->skeleton = nullptr;
 						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_xform->object_id = track_xform->object->get_instance_id();
 
 
 						track = track_xform;
 						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
 #endif // _3D_DISABLED
 					} break;
 					} break;
 					case Animation::TYPE_METHOD: {
 					case Animation::TYPE_METHOD: {
@@ -670,6 +693,26 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 				}
 				}
 
 
 				track_cache[path] = track;
 				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;
 			track->setup_pass = setup_pass;
@@ -831,8 +874,11 @@ void AnimationTree::_process_graph(real_t p_delta) {
 				ERR_CONTINUE(!track_cache.has(path));
 				ERR_CONTINUE(!track_cache.has(path));
 
 
 				TrackCache *track = track_cache[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;
 				track->root_motion = root_motion_track == path;
@@ -848,20 +894,81 @@ void AnimationTree::_process_graph(real_t p_delta) {
 					continue; //nothing to blend
 					continue; //nothing to blend
 				}
 				}
 
 
-				switch (track->type) {
-					case Animation::TYPE_TRANSFORM3D: {
+				switch (ttype) {
+					case Animation::TYPE_POSITION_3D: {
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 						TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
 						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 (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;
 							real_t prev_time = time - delta;
 							if (prev_time < 0) {
 							if (prev_time < 0) {
 								if (!a->has_loop()) {
 								if (!a->has_loop()) {
@@ -871,61 +978,44 @@ void AnimationTree::_process_graph(real_t p_delta) {
 								}
 								}
 							}
 							}
 
 
-							Vector3 loc[2];
 							Quaternion rot[2];
 							Quaternion rot[2];
-							Vector3 scale[2];
 
 
 							if (prev_time > time) {
 							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) {
 								if (err != OK) {
 									continue;
 									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();
 								Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
 								t->rot = (t->rot * q).normalized();
 								t->rot = (t->rot * q).normalized();
 
 
 								prev_time = 0;
 								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) {
 							if (err != OK) {
 								continue;
 								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();
 							Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
 							t->rot = (t->rot * q).normalized();
 							t->rot = (t->rot * q).normalized();
 
 
 							prev_time = 0;
 							prev_time = 0;
 
 
 						} else {
 						} else {
-							Vector3 loc;
 							Quaternion rot;
 							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
 							//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) {
 							if (err != OK) {
 								continue;
 								continue;
 							}
 							}
 
 
-							t->loc = t->loc.lerp(loc, blend);
 							if (t->rot_blend_accum == 0) {
 							if (t->rot_blend_accum == 0) {
 								t->rot = rot;
 								t->rot = rot;
 								t->rot_blend_accum = blend;
 								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 = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
 								t->rot_blend_accum = rot_total;
 								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);
 							t->scale = t->scale.lerp(scale, blend);
 						}
 						}
 #endif // _3D_DISABLED
 #endif // _3D_DISABLED
@@ -1198,26 +1349,41 @@ void AnimationTree::_process_graph(real_t p_delta) {
 			}
 			}
 
 
 			switch (track->type) {
 			switch (track->type) {
-				case Animation::TYPE_TRANSFORM3D: {
+				case Animation::TYPE_POSITION_3D: {
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 					TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
 					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) {
 					if (t->root_motion) {
+						Transform3D xform;
+						xform.origin = t->loc;
+						xform.basis.set_quaternion_scale(t->rot, t->scale);
+
 						root_motion_transform = xform;
 						root_motion_transform = xform;
 
 
 						if (t->skeleton && t->bone_idx >= 0) {
 						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();
 							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) {
 					} 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) {
 					} 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
 #endif // _3D_DISABLED
 				} break;
 				} break;

+ 4 - 1
scene/animation/animation_tree.h

@@ -197,13 +197,16 @@ private:
 		Skeleton3D *skeleton = nullptr;
 		Skeleton3D *skeleton = nullptr;
 #endif // _3D_DISABLED
 #endif // _3D_DISABLED
 		int bone_idx = -1;
 		int bone_idx = -1;
+		bool loc_used = false;
+		bool rot_used = false;
+		bool scale_used = false;
 		Vector3 loc;
 		Vector3 loc;
 		Quaternion rot;
 		Quaternion rot;
 		real_t rot_blend_accum = 0.0;
 		real_t rot_blend_accum = 0.0;
 		Vector3 scale;
 		Vector3 scale;
 
 
 		TrackCacheTransform() {
 		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:
 public:
 	enum TrackType {
 	enum TrackType {
 		TYPE_VALUE, ///< Set a value in a property, can be interpolated.
 		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_METHOD, ///< Call any method on a specific node.
 		TYPE_BEZIER, ///< Bezier curve
 		TYPE_BEZIER, ///< Bezier curve
 		TYPE_AUDIO,
 		TYPE_AUDIO,
@@ -86,21 +88,31 @@ private:
 		T value;
 		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 */
 	/* PROPERTY VALUE TRACK */
@@ -186,14 +198,11 @@ private:
 	template <class K>
 	template <class K>
 	inline int _find(const Vector<K> &p_keys, double p_time) const;
 	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_ 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_ 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_ 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_ 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_ 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_ 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;
 	_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
 	// bind helpers
 private:
 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 {
 	Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
 		List<int> idxs;
 		List<int> idxs;
 		value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
 		value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
@@ -247,8 +244,13 @@ private:
 		return idxr;
 		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:
 protected:
 	bool _set(const StringName &p_name, const Variant &p_value);
 	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;
 	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;
 	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);
 	void track_set_interpolation_type(int p_track, InterpolationType p_interp);
 	InterpolationType track_get_interpolation_type(int p_track) const;
 	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);
 	void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
 	bool track_get_interpolation_loop_wrap(int p_track) const;
 	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;
 	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_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);
 	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