diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml
index 99d21706ee6b..9026aa6a34fc 100644
--- a/doc/classes/AnimationNode.xml
+++ b/doc/classes/AnimationNode.xml
@@ -53,6 +53,7 @@
 			<return type="float" />
 			<argument index="0" name="time" type="float" />
 			<argument index="1" name="seek" type="bool" />
+			<argument index="2" name="seek_root" type="bool" />
 			<description>
 				User-defined callback called when a custom node is processed. The [code]time[/code] parameter is a relative delta, unless [code]seek[/code] is [code]true[/code], in which case it is absolute.
 				Here, call the [method blend_input], [method blend_node] or [method blend_animation] functions. You can also use [method get_parameter] and [method set_parameter] to modify local memory.
@@ -72,8 +73,9 @@
 			<argument index="1" name="time" type="float" />
 			<argument index="2" name="delta" type="float" />
 			<argument index="3" name="seeked" type="bool" />
-			<argument index="4" name="blend" type="float" />
-			<argument index="5" name="pingponged" type="int" default="0" />
+			<argument index="4" name="seek_root" type="bool" />
+			<argument index="5" name="blend" type="float" />
+			<argument index="6" name="pingponged" type="int" default="0" />
 			<description>
 				Blend an animation by [code]blend[/code] amount (name must be valid in the linked [AnimationPlayer]). A [code]time[/code] and [code]delta[/code] may be passed, as well as whether [code]seek[/code] happened.
 			</description>
@@ -83,9 +85,10 @@
 			<argument index="0" name="input_index" type="int" />
 			<argument index="1" name="time" type="float" />
 			<argument index="2" name="seek" type="bool" />
-			<argument index="3" name="blend" type="float" />
-			<argument index="4" name="filter" type="int" enum="AnimationNode.FilterAction" default="0" />
-			<argument index="5" name="optimize" type="bool" default="true" />
+			<argument index="3" name="seek_root" type="bool" />
+			<argument index="4" name="blend" type="float" />
+			<argument index="5" name="filter" type="int" enum="AnimationNode.FilterAction" default="0" />
+			<argument index="6" name="optimize" type="bool" default="true" />
 			<description>
 				Blend an input. This is only useful for nodes created for an [AnimationNodeBlendTree]. The [code]time[/code] parameter is a relative delta, unless [code]seek[/code] is [code]true[/code], in which case it is absolute. A filter mode may be optionally passed (see [enum FilterAction] for options).
 			</description>
@@ -96,9 +99,10 @@
 			<argument index="1" name="node" type="AnimationNode" />
 			<argument index="2" name="time" type="float" />
 			<argument index="3" name="seek" type="bool" />
-			<argument index="4" name="blend" type="float" />
-			<argument index="5" name="filter" type="int" enum="AnimationNode.FilterAction" default="0" />
-			<argument index="6" name="optimize" type="bool" default="true" />
+			<argument index="4" name="seek_root" type="bool" />
+			<argument index="5" name="blend" type="float" />
+			<argument index="6" name="filter" type="int" enum="AnimationNode.FilterAction" default="0" />
+			<argument index="7" name="optimize" type="bool" default="true" />
 			<description>
 				Blend another animation node (in case this node contains children animation nodes). This function is only useful if you inherit from [AnimationRootNode] instead, else editors will not display your node for addition.
 			</description>
diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml
index 727a09e11074..de2414cd43f9 100644
--- a/doc/classes/AnimationNodeOneShot.xml
+++ b/doc/classes/AnimationNodeOneShot.xml
@@ -20,9 +20,9 @@
 		<member name="autorestart_random_delay" type="float" setter="set_autorestart_random_delay" getter="get_autorestart_random_delay" default="0.0">
 			If [member autorestart] is [code]true[/code], a random additional delay (in seconds) between 0 and this value will be added to [member autorestart_delay].
 		</member>
-		<member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.1">
+		<member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.0">
 		</member>
-		<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.1">
+		<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.0">
 		</member>
 		<member name="mix_mode" type="int" setter="set_mix_mode" getter="get_mix_mode" enum="AnimationNodeOneShot.MixMode" default="0">
 		</member>
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index c425613262a1..a1f4cb2282dc 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1417,14 +1417,14 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
 void AnimationTimelineEdit::_anim_loop_pressed() {
 	undo_redo->create_action(TTR("Change Animation Loop"));
 	switch (animation->get_loop_mode()) {
-		case Animation::LoopMode::LOOP_NONE: {
-			undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_LINEAR);
+		case Animation::LOOP_NONE: {
+			undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_LINEAR);
 		} break;
-		case Animation::LoopMode::LOOP_LINEAR: {
-			undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_PINGPONG);
+		case Animation::LOOP_LINEAR: {
+			undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_PINGPONG);
 		} break;
-		case Animation::LoopMode::LOOP_PINGPONG: {
-			undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_NONE);
+		case Animation::LOOP_PINGPONG: {
+			undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_NONE);
 		} break;
 		default:
 			break;
@@ -1724,15 +1724,15 @@ void AnimationTimelineEdit::update_values() {
 	}
 
 	switch (animation->get_loop_mode()) {
-		case Animation::LoopMode::LOOP_NONE: {
+		case Animation::LOOP_NONE: {
 			loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons")));
 			loop->set_pressed(false);
 		} break;
-		case Animation::LoopMode::LOOP_LINEAR: {
+		case Animation::LOOP_LINEAR: {
 			loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons")));
 			loop->set_pressed(true);
 		} break;
-		case Animation::LoopMode::LOOP_PINGPONG: {
+		case Animation::LOOP_PINGPONG: {
 			loop->set_icon(get_theme_icon(SNAME("PingPongLoop"), SNAME("EditorIcons")));
 			loop->set_pressed(true);
 		} break;
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index bd66a4b2dfdc..1b8f5e678f37 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -478,10 +478,10 @@ class AnimationTrackEditor : public VBoxContainer {
 	struct TrackClipboard {
 		NodePath full_path;
 		NodePath base_path;
-		Animation::TrackType track_type = Animation::TrackType::TYPE_ANIMATION;
-		Animation::InterpolationType interp_type = Animation::InterpolationType::INTERPOLATION_CUBIC;
-		Animation::UpdateMode update_mode = Animation::UpdateMode::UPDATE_CAPTURE;
-		Animation::LoopMode loop_mode = Animation::LoopMode::LOOP_LINEAR;
+		Animation::TrackType track_type = Animation::TYPE_ANIMATION;
+		Animation::InterpolationType interp_type = Animation::INTERPOLATION_CUBIC;
+		Animation::UpdateMode update_mode = Animation::UPDATE_CAPTURE;
+		Animation::LoopMode loop_mode = Animation::LOOP_LINEAR;
 		bool loop_wrap = false;
 		bool enabled = false;
 
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 06a84ff23c38..7e9d581ae878 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -466,7 +466,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
 			static const char *loop_strings[loop_string_count] = { "loop_mode", "loop", "cycle" };
 			for (int i = 0; i < loop_string_count; i++) {
 				if (_teststr(animname, loop_strings[i])) {
-					anim->set_loop_mode(Animation::LoopMode::LOOP_LINEAR);
+					anim->set_loop_mode(Animation::LOOP_LINEAR);
 					animname = _fixstr(animname, loop_strings[i]);
 
 					Ref<AnimationLibrary> library = ap->get_animation_library(ap->find_animation_library(anim));
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 581dab84b4e4..57cf13d29880 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1338,7 +1338,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
 
 		float pos = cpos + step_off * anim->get_step();
 
-		bool valid = anim->get_loop_mode() != Animation::LoopMode::LOOP_NONE || (pos >= 0 && pos <= anim->get_length());
+		bool valid = anim->get_loop_mode() != Animation::LOOP_NONE || (pos >= 0 && pos <= anim->get_length());
 		onion.captures_valid.write[cidx] = valid;
 		if (valid) {
 			player->seek(pos, true);
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 849316c56838..594f98410e18 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -219,14 +219,14 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<Animatio
 	}
 }
 
-double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
+double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_seek_root) {
 	if (blend_points_used == 0) {
 		return 0.0;
 	}
 
 	if (blend_points_used == 1) {
 		// only one point available, just play that animation
-		return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+		return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
 	}
 
 	double blend_pos = get_parameter(blend_position);
@@ -295,7 +295,7 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
 	double max_time_remaining = 0.0;
 
 	for (int i = 0; i < blend_points_used; i++) {
-		double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
+		double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, false);
 
 		max_time_remaining = MAX(max_time_remaining, remaining);
 	}
diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h
index 7038cece0650..b2075c8c93a0 100644
--- a/scene/animation/animation_blend_space_1d.h
+++ b/scene/animation/animation_blend_space_1d.h
@@ -93,7 +93,7 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode {
 	void set_value_label(const String &p_label);
 	String get_value_label() const;
 
-	double process(double p_time, bool p_seek) override;
+	double process(double p_time, bool p_seek, bool p_seek_root) override;
 	String get_caption() const override;
 
 	Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index a3aa3f6cc8d5..acdce2d7de67 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -432,7 +432,7 @@ void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vect
 	r_weights[2] = w;
 }
 
-double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
+double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_seek_root) {
 	_update_triangles();
 
 	Vector2 blend_pos = get_parameter(blend_position);
@@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
 			for (int j = 0; j < 3; j++) {
 				if (i == triangle_points[j]) {
 					//blend with the given weight
-					double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
+					double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, blend_weights[j], FILTER_IGNORE, false);
 					if (first || t < mind) {
 						mind = t;
 						first = false;
@@ -514,7 +514,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
 
 			if (!found) {
 				//ignore
-				blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
+				blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, false);
 			}
 		}
 	} else {
@@ -539,16 +539,16 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
 					na_n->set_backward(na_c->is_backward());
 				}
 				//see how much animation remains
-				from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, 0.0, FILTER_IGNORE, false);
+				from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, p_seek_root, 0.0, FILTER_IGNORE, false);
 			}
 
-			mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, 1.0, FILTER_IGNORE, false);
+			mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_seek_root, 1.0, FILTER_IGNORE, false);
 			length_internal = from + mind;
 
 			closest = new_closest;
 
 		} else {
-			mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+			mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
 		}
 	}
 
diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h
index 1356656bf88b..01f53ed25a32 100644
--- a/scene/animation/animation_blend_space_2d.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -126,7 +126,7 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode {
 	void set_y_label(const String &p_label);
 	String get_y_label() const;
 
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 	virtual String get_caption() const override;
 
 	Vector2 get_closest_point(const Vector2 &p_point);
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 433f21f91f82..3de3ab256ed6 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -64,7 +64,7 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const {
 	}
 }
 
-double AnimationNodeAnimation::process(double p_time, bool p_seek) {
+double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_seek_root) {
 	AnimationPlayer *ap = state->player;
 	ERR_FAIL_COND_V(!ap, 0);
 
@@ -101,8 +101,8 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
 		}
 	}
 
-	if (anim->get_loop_mode() == Animation::LoopMode::LOOP_PINGPONG) {
-		if (anim_size) {
+	if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
+		if (!Math::is_zero_approx(anim_size)) {
 			if ((int)Math::floor(abs(time - prev_time) / anim_size) % 2 == 0) {
 				if (prev_time > 0 && time <= 0) {
 					backward = !backward;
@@ -116,22 +116,24 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
 			time = Math::pingpong(time, anim_size);
 		}
 	} else {
-		if (anim->get_loop_mode() == Animation::LoopMode::LOOP_LINEAR) {
-			if (anim_size) {
+		if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
+			if (!Math::is_zero_approx(anim_size)) {
 				time = Math::fposmod(time, anim_size);
 			}
 		} else if (time < 0) {
+			step += time;
 			time = 0;
 		} else if (time > anim_size) {
+			step += anim_size - time;
 			time = anim_size;
 		}
 		backward = false;
 	}
 
 	if (play_mode == PLAY_MODE_FORWARD) {
-		blend_animation(animation, time, step, p_seek, 1.0, pingponged);
+		blend_animation(animation, time, step, p_seek, p_seek_root, 1.0, pingponged);
 	} else {
-		blend_animation(animation, anim_size - time, -step, p_seek, 1.0, pingponged);
+		blend_animation(animation, anim_size - time, -step, p_seek, p_seek_root, 1.0, pingponged);
 	}
 	set_parameter(this->time, time);
 
@@ -251,7 +253,7 @@ bool AnimationNodeOneShot::has_filter() const {
 	return true;
 }
 
-double AnimationNodeOneShot::process(double p_time, bool p_seek) {
+double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_root) {
 	bool active = get_parameter(this->active);
 	bool prev_active = get_parameter(this->prev_active);
 	double time = get_parameter(this->time);
@@ -274,7 +276,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
 		}
 
 		if (!active) {
-			return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+			return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
 		}
 	}
 
@@ -311,12 +313,12 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
 
 	double main_rem;
 	if (mix == MIX_MODE_ADD) {
-		main_rem = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+		main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
 	} else {
-		main_rem = blend_input(0, p_time, p_seek, 1.0 - blend, FILTER_BLEND, !sync);
+		main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_BLEND, !sync);
 	}
 
-	double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, blend, FILTER_PASS, false);
+	double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, p_seek_root, blend, FILTER_PASS, false);
 
 	if (do_start) {
 		remaining = os_rem;
@@ -420,10 +422,10 @@ bool AnimationNodeAdd2::has_filter() const {
 	return true;
 }
 
-double AnimationNodeAdd2::process(double p_time, bool p_seek) {
+double AnimationNodeAdd2::process(double p_time, bool p_seek, bool p_seek_root) {
 	double amount = get_parameter(add_amount);
-	double rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
-	blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
+	double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
+	blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync);
 
 	return rem0;
 }
@@ -466,11 +468,11 @@ bool AnimationNodeAdd3::has_filter() const {
 	return true;
 }
 
-double AnimationNodeAdd3::process(double p_time, bool p_seek) {
+double AnimationNodeAdd3::process(double p_time, bool p_seek, bool p_seek_root) {
 	double amount = get_parameter(add_amount);
-	blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync);
-	double rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
-	blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync);
+	blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_PASS, !sync);
+	double rem0 = blend_input(1, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
+	blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_PASS, !sync);
 
 	return rem0;
 }
@@ -502,11 +504,11 @@ String AnimationNodeBlend2::get_caption() const {
 	return "Blend2";
 }
 
-double AnimationNodeBlend2::process(double p_time, bool p_seek) {
+double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_seek_root) {
 	double amount = get_parameter(blend_amount);
 
-	double rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync);
-	double rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
+	double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - amount, FILTER_BLEND, !sync);
+	double rem1 = blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync);
 
 	return amount > 0.5 ? rem1 : rem0; //hacky but good enough
 }
@@ -557,11 +559,11 @@ bool AnimationNodeBlend3::is_using_sync() const {
 	return sync;
 }
 
-double AnimationNodeBlend3::process(double p_time, bool p_seek) {
+double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_seek_root) {
 	double amount = get_parameter(blend_amount);
-	double rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync);
-	double rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync);
-	double rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync);
+	double rem0 = blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_IGNORE, !sync);
+	double rem1 = blend_input(1, p_time, p_seek, p_seek_root, 1.0 - ABS(amount), FILTER_IGNORE, !sync);
+	double rem2 = blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_IGNORE, !sync);
 
 	return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough
 }
@@ -595,12 +597,12 @@ String AnimationNodeTimeScale::get_caption() const {
 	return "TimeScale";
 }
 
-double AnimationNodeTimeScale::process(double p_time, bool p_seek) {
+double AnimationNodeTimeScale::process(double p_time, bool p_seek, bool p_seek_root) {
 	double scale = get_parameter(this->scale);
 	if (p_seek) {
-		return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
+		return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false);
 	} else {
-		return blend_input(0, p_time * scale, false, 1.0, FILTER_IGNORE, false);
+		return blend_input(0, p_time * scale, false, p_seek_root, 1.0, FILTER_IGNORE, false);
 	}
 }
 
@@ -625,16 +627,16 @@ String AnimationNodeTimeSeek::get_caption() const {
 	return "Seek";
 }
 
-double AnimationNodeTimeSeek::process(double p_time, bool p_seek) {
+double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_seek_root) {
 	double seek_pos = get_parameter(this->seek_pos);
 	if (p_seek) {
-		return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
+		return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false);
 	} else if (seek_pos >= 0) {
-		double ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false);
+		double ret = blend_input(0, seek_pos, true, true, 1.0, FILTER_IGNORE, false);
 		set_parameter(this->seek_pos, -1.0); //reset
 		return ret;
 	} else {
-		return blend_input(0, p_time, false, 1.0, FILTER_IGNORE, false);
+		return blend_input(0, p_time, false, p_seek_root, 1.0, FILTER_IGNORE, false);
 	}
 }
 
@@ -726,7 +728,7 @@ float AnimationNodeTransition::get_cross_fade_time() const {
 	return xfade;
 }
 
-double AnimationNodeTransition::process(double p_time, bool p_seek) {
+double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_root) {
 	int current = get_parameter(this->current);
 	int prev = get_parameter(this->prev);
 	int prev_current = get_parameter(this->prev_current);
@@ -754,7 +756,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
 
 	if (prev < 0) { // process current animation, check for transition
 
-		rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+		rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
 
 		if (p_seek) {
 			time = p_time;
@@ -772,16 +774,16 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
 
 		if (!p_seek && switched) { //just switched, seek to start of current
 
-			rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false);
+			rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, false);
 		} else {
-			rem = blend_input(current, p_time, p_seek, 1.0 - blend, FILTER_IGNORE, false);
+			rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, false);
 		}
 
 		if (p_seek) { // don't seek prev animation
-			blend_input(prev, 0, false, blend, FILTER_IGNORE, false);
+			blend_input(prev, 0, false, p_seek_root, blend, FILTER_IGNORE, false);
 			time = p_time;
 		} else {
-			blend_input(prev, p_time, false, blend, FILTER_IGNORE, false);
+			blend_input(prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, false);
 			time += p_time;
 			prev_xfading -= p_time;
 			if (prev_xfading < 0) {
@@ -844,8 +846,8 @@ String AnimationNodeOutput::get_caption() const {
 	return "Output";
 }
 
-double AnimationNodeOutput::process(double p_time, bool p_seek) {
-	return blend_input(0, p_time, p_seek, 1.0);
+double AnimationNodeOutput::process(double p_time, bool p_seek, bool p_seek_root) {
+	return blend_input(0, p_time, p_seek, p_seek_root, 1.0);
 }
 
 AnimationNodeOutput::AnimationNodeOutput() {
@@ -1057,9 +1059,9 @@ String AnimationNodeBlendTree::get_caption() const {
 	return "BlendTree";
 }
 
-double AnimationNodeBlendTree::process(double p_time, bool p_seek) {
+double AnimationNodeBlendTree::process(double p_time, bool p_seek, bool p_seek_root) {
 	Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node;
-	return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, 1.0);
+	return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_seek_root, 1.0);
 }
 
 void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) {
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 2acacd7396db..09971a61c0e5 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -53,7 +53,7 @@ class AnimationNodeAnimation : public AnimationRootNode {
 	static Vector<String> (*get_editable_animation_list)();
 
 	virtual String get_caption() const override;
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	void set_animation(const StringName &p_name);
 	StringName get_animation() const;
@@ -87,8 +87,8 @@ class AnimationNodeOneShot : public AnimationNode {
 	};
 
 private:
-	float fade_in = 0.1;
-	float fade_out = 0.1;
+	float fade_in = 0.0;
+	float fade_out = 0.0;
 
 	bool autorestart = false;
 	float autorestart_delay = 1.0;
@@ -138,7 +138,7 @@ class AnimationNodeOneShot : public AnimationNode {
 	bool is_using_sync() const;
 
 	virtual bool has_filter() const override;
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	AnimationNodeOneShot();
 };
@@ -164,7 +164,7 @@ class AnimationNodeAdd2 : public AnimationNode {
 	bool is_using_sync() const;
 
 	virtual bool has_filter() const override;
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	AnimationNodeAdd2();
 };
@@ -188,7 +188,7 @@ class AnimationNodeAdd3 : public AnimationNode {
 	bool is_using_sync() const;
 
 	virtual bool has_filter() const override;
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	AnimationNodeAdd3();
 };
@@ -207,7 +207,7 @@ class AnimationNodeBlend2 : public AnimationNode {
 	virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
 
 	virtual String get_caption() const override;
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	void set_use_sync(bool p_sync);
 	bool is_using_sync() const;
@@ -234,7 +234,7 @@ class AnimationNodeBlend3 : public AnimationNode {
 	void set_use_sync(bool p_sync);
 	bool is_using_sync() const;
 
-	double process(double p_time, bool p_seek) override;
+	double process(double p_time, bool p_seek, bool p_seek_root) override;
 	AnimationNodeBlend3();
 };
 
@@ -252,7 +252,7 @@ class AnimationNodeTimeScale : public AnimationNode {
 
 	virtual String get_caption() const override;
 
-	double process(double p_time, bool p_seek) override;
+	double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	AnimationNodeTimeScale();
 };
@@ -271,7 +271,7 @@ class AnimationNodeTimeSeek : public AnimationNode {
 
 	virtual String get_caption() const override;
 
-	double process(double p_time, bool p_seek) override;
+	double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	AnimationNodeTimeSeek();
 };
@@ -329,7 +329,7 @@ class AnimationNodeTransition : public AnimationNode {
 	void set_cross_fade_time(float p_fade);
 	float get_cross_fade_time() const;
 
-	double process(double p_time, bool p_seek) override;
+	double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	AnimationNodeTransition();
 };
@@ -339,7 +339,7 @@ class AnimationNodeOutput : public AnimationNode {
 
 public:
 	virtual String get_caption() const override;
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 	AnimationNodeOutput();
 };
 
@@ -408,7 +408,7 @@ class AnimationNodeBlendTree : public AnimationRootNode {
 	void get_node_connections(List<NodeConnection> *r_connections) const;
 
 	virtual String get_caption() const override;
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 
 	void get_node_list(List<StringName> *r_list);
 
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index b3cae4f5b50a..8ac195aabe39 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -292,7 +292,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
 	return true;
 }
 
-double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek) {
+double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) {
 	//if not playing and it can restart, then restart
 	if (!playing && start_request == StringName()) {
 		if (!stop_request && p_state_machine->start_node) {
@@ -356,7 +356,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
 			current = p_state_machine->start_node;
 		}
 
-		len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false);
+		len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, false);
 		pos_current = 0;
 	}
 
@@ -381,10 +381,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
 		}
 	}
 
-	float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false);
+	float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_seek_root, fade_blend, AnimationNode::FILTER_IGNORE, false);
 
 	if (fading_from != StringName()) {
-		p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false);
+		p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_seek_root, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false);
 	}
 
 	//guess playback position
@@ -538,12 +538,12 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
 			}
 			current = next;
 			if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
-				len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+				len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
 				pos_current = MIN(pos_current, len_current);
-				p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false);
+				p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
 
 			} else {
-				len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+				len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
 				pos_current = 0;
 			}
 
@@ -1071,11 +1071,11 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const {
 	return graph_offset;
 }
 
-double AnimationNodeStateMachine::process(double p_time, bool p_seek) {
+double AnimationNodeStateMachine::process(double p_time, bool p_seek, bool p_seek_root) {
 	Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback);
 	ERR_FAIL_COND_V(playback.is_null(), 0.0);
 
-	return playback->process(this, p_time, p_seek);
+	return playback->process(this, p_time, p_seek, p_seek_root);
 }
 
 String AnimationNodeStateMachine::get_caption() const {
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 39a84358fb88..ec56c2606a14 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -120,7 +120,7 @@ class AnimationNodeStateMachinePlayback : public Resource {
 
 	bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel);
 
-	double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek);
+	double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root);
 
 	bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
 
@@ -226,7 +226,7 @@ class AnimationNodeStateMachine : public AnimationRootNode {
 	void set_graph_offset(const Vector2 &p_offset);
 	Vector2 get_graph_offset() const;
 
-	virtual double process(double p_time, bool p_seek) override;
+	virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
 	virtual String get_caption() const override;
 
 	virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 081e6e809a8f..b49883784707 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -832,7 +832,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 							nc->audio_start = p_time;
 						}
 					} else if (nc->audio_playing) {
-						bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
+						bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
 
 						bool stop = false;
 
@@ -883,15 +883,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 					double at_anim_pos = 0.0;
 
 					switch (anim->get_loop_mode()) {
-						case Animation::LoopMode::LOOP_NONE: {
+						case Animation::LOOP_NONE: {
 							at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
 						} break;
 
-						case Animation::LoopMode::LOOP_LINEAR: {
+						case Animation::LOOP_LINEAR: {
 							at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
 						} break;
 
-						case Animation::LoopMode::LOOP_PINGPONG: {
+						case Animation::LOOP_PINGPONG: {
 							at_anim_pos = Math::pingpong(p_time - pos, (double)anim->get_length());
 						} break;
 
@@ -944,7 +944,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
 	int pingponged = 0;
 
 	switch (cd.from->animation->get_loop_mode()) {
-		case Animation::LoopMode::LOOP_NONE: {
+		case Animation::LOOP_NONE: {
 			if (next_pos < 0) {
 				next_pos = 0;
 			} else if (next_pos > len) {
@@ -969,7 +969,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
 			}
 		} break;
 
-		case Animation::LoopMode::LOOP_LINEAR: {
+		case Animation::LOOP_LINEAR: {
 			double looped_next_pos = Math::fposmod(next_pos, (double)len);
 			if (looped_next_pos == 0 && next_pos != 0) {
 				// Loop multiples of the length to it, rather than 0
@@ -980,7 +980,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
 			}
 		} break;
 
-		case Animation::LoopMode::LOOP_PINGPONG: {
+		case Animation::LOOP_PINGPONG: {
 			if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) {
 				if (next_pos < 0 && cd.pos >= 0) {
 					cd.speed_scale *= -1.0;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index b0590bc2bdd3..41d24fbfdfdd 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -88,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
 	}
 }
 
-void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
+void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged) {
 	ERR_FAIL_COND(!state);
 	ERR_FAIL_COND(!state->player->has_animation(p_animation));
 
@@ -115,17 +115,18 @@ void AnimationNode::blend_animation(const StringName &p_animation, double p_time
 	anim_state.animation = animation;
 	anim_state.seeked = p_seeked;
 	anim_state.pingponged = p_pingponged;
+	anim_state.seek_root = p_seek_root;
 
 	state->animation_states.push_back(anim_state);
 }
 
-double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections) {
+double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections) {
 	base_path = p_base_path;
 	parent = p_parent;
 	connections = p_connections;
 	state = p_state;
 
-	double t = process(p_time, p_seek);
+	double t = process(p_time, p_seek, p_seek_root);
 
 	state = nullptr;
 	parent = nullptr;
@@ -144,7 +145,7 @@ void AnimationNode::make_invalid(const String &p_reason) {
 	state->invalid_reasons += String::utf8("•  ") + p_reason;
 }
 
-double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) {
 	ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
 	ERR_FAIL_COND_V(!state, 0);
 
@@ -163,7 +164,7 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_
 
 	//inputs.write[p_input].last_pass = state->last_pass;
 	real_t activity = 0.0;
-	double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity);
+	double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize, &activity);
 
 	Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path);
 
@@ -174,11 +175,11 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_
 	return ret;
 }
 
-double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
-	return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_blend, p_filter, p_optimize);
+double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+	return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize);
 }
 
-double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
+double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
 	ERR_FAIL_COND_V(!p_node.is_valid(), 0);
 	ERR_FAIL_COND_V(!state, 0);
 
@@ -287,9 +288,9 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
 	}
 
 	if (!p_seek && p_optimize && !any_valid) {
-		return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_connections);
+		return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections);
 	}
-	return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_connections);
+	return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_seek_root, p_connections);
 }
 
 int AnimationNode::get_input_count() const {
@@ -333,9 +334,9 @@ void AnimationNode::remove_input(int p_index) {
 	emit_changed();
 }
 
-double AnimationNode::process(double p_time, bool p_seek) {
+double AnimationNode::process(double p_time, bool p_seek, bool p_seek_root) {
 	double ret;
-	if (GDVIRTUAL_CALL(_process, p_time, p_seek, ret)) {
+	if (GDVIRTUAL_CALL(_process, p_time, p_seek, p_seek_root, ret)) {
 		return ret;
 	}
 
@@ -420,9 +421,9 @@ void AnimationNode::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
 	ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
 
-	ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
-	ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
-	ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
+	ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "seek_root", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
+	ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
+	ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
 
 	ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter);
 	ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter);
@@ -434,7 +435,7 @@ void AnimationNode::_bind_methods() {
 	GDVIRTUAL_BIND(_get_parameter_list);
 	GDVIRTUAL_BIND(_get_child_by_name, "name");
 	GDVIRTUAL_BIND(_get_parameter_default_value, "parameter");
-	GDVIRTUAL_BIND(_process, "time", "seek");
+	GDVIRTUAL_BIND(_process, "time", "seek", "seek_root");
 	GDVIRTUAL_BIND(_get_caption);
 	GDVIRTUAL_BIND(_has_filter);
 
@@ -864,7 +865,6 @@ void AnimationTree::_process_graph(double p_delta) {
 	_update_properties(); //if properties need updating, update them
 
 	//check all tracks, see if they need modification
-
 	root_motion_transform = Transform3D();
 
 	if (!root.is_valid()) {
@@ -943,11 +943,11 @@ void AnimationTree::_process_graph(double p_delta) {
 	{
 		if (started) {
 			//if started, seek
-			root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, Vector<StringName>());
+			root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, false, Vector<StringName>());
 			started = false;
 		}
 
-		root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, Vector<StringName>());
+		root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, false, Vector<StringName>());
 	}
 
 	if (!state.valid) {
@@ -967,6 +967,7 @@ void AnimationTree::_process_graph(double p_delta) {
 			int pingponged = as.pingponged;
 #ifndef _3D_DISABLED
 			bool backward = signbit(delta);
+			bool calc_root = !seeked || as.seek_root;
 #endif // _3D_DISABLED
 
 			for (int i = 0; i < a->get_track_count(); i++) {
@@ -995,7 +996,7 @@ void AnimationTree::_process_graph(double p_delta) {
 					case Animation::TYPE_POSITION_3D: {
 #ifndef _3D_DISABLED
 						TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
-						if (track->root_motion) {
+						if (track->root_motion && calc_root) {
 							if (t->process_pass != process_pass) {
 								t->process_pass = process_pass;
 								t->loc = Vector3(0, 0, 0);
@@ -1057,7 +1058,7 @@ void AnimationTree::_process_graph(double p_delta) {
 									}
 									a->position_track_interpolate(i, 0, &loc[1]);
 									t->loc += (loc[1] - loc[0]) * blend;
-									prev_time = 0;
+									prev_time = (double)a->get_length();
 								}
 							}
 
@@ -1091,7 +1092,7 @@ void AnimationTree::_process_graph(double p_delta) {
 					case Animation::TYPE_ROTATION_3D: {
 #ifndef _3D_DISABLED
 						TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
-						if (track->root_motion) {
+						if (track->root_motion && calc_root) {
 							if (t->process_pass != process_pass) {
 								t->process_pass = process_pass;
 								t->loc = Vector3(0, 0, 0);
@@ -1153,7 +1154,7 @@ void AnimationTree::_process_graph(double p_delta) {
 									}
 									a->rotation_track_interpolate(i, 0, &rot[1]);
 									t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
-									prev_time = 0;
+									prev_time = (double)a->get_length();
 								}
 							}
 
@@ -1187,7 +1188,7 @@ void AnimationTree::_process_graph(double p_delta) {
 					case Animation::TYPE_SCALE_3D: {
 #ifndef _3D_DISABLED
 						TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
-						if (track->root_motion) {
+						if (track->root_motion && calc_root) {
 							if (t->process_pass != process_pass) {
 								t->process_pass = process_pass;
 								t->loc = Vector3(0, 0, 0);
@@ -1249,7 +1250,7 @@ void AnimationTree::_process_graph(double p_delta) {
 									}
 									a->scale_track_interpolate(i, 0, &scale[1]);
 									t->scale += (scale[1] - scale[0]) * blend;
-									prev_time = 0;
+									prev_time = (double)a->get_length();
 								}
 							}
 
@@ -1306,8 +1307,7 @@ void AnimationTree::_process_graph(double p_delta) {
 
 						Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
 
-						if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { //delta == 0 means seek
-
+						if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) {
 							Variant value = a->value_track_interpolate(i, time);
 
 							if (value == Variant()) {
@@ -1448,7 +1448,7 @@ void AnimationTree::_process_graph(double p_delta) {
 									t->start = time;
 								}
 							} else if (t->playing) {
-								bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
+								bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
 
 								bool stop = false;
 
@@ -1517,13 +1517,13 @@ void AnimationTree::_process_graph(double p_delta) {
 							double at_anim_pos = 0.0;
 
 							switch (anim->get_loop_mode()) {
-								case Animation::LoopMode::LOOP_NONE: {
+								case Animation::LOOP_NONE: {
 									at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end
 								} break;
-								case Animation::LoopMode::LOOP_LINEAR: {
+								case Animation::LOOP_LINEAR: {
 									at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
 								} break;
-								case Animation::LoopMode::LOOP_PINGPONG: {
+								case Animation::LOOP_PINGPONG: {
 									at_anim_pos = Math::pingpong(time - pos, (double)a->get_length());
 								} break;
 								default:
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 3f09bf7f4bf6..785c2d9c6432 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -68,6 +68,7 @@ class AnimationNode : public Resource {
 		const Vector<real_t> *track_blends = nullptr;
 		real_t blend = 0.0;
 		bool seeked = false;
+		bool seek_root = false;
 		int pingponged = 0;
 	};
 
@@ -85,7 +86,7 @@ class AnimationNode : public Resource {
 	Vector<real_t> blends;
 	State *state = nullptr;
 
-	double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections);
+	double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections);
 
 	//all this is temporary
 	StringName base_path;
@@ -98,12 +99,12 @@ class AnimationNode : public Resource {
 	Array _get_filters() const;
 	void _set_filters(const Array &p_filters);
 	friend class AnimationNodeBlendTree;
-	double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
+	double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
 
 protected:
-	void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
-	double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
-	double blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+	void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged = 0);
+	double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+	double blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
 
 	void make_invalid(const String &p_reason);
 
@@ -115,7 +116,7 @@ class AnimationNode : public Resource {
 	GDVIRTUAL0RC(Array, _get_parameter_list)
 	GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
 	GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName)
-	GDVIRTUAL2RC(double, _process, double, bool)
+	GDVIRTUAL3RC(double, _process, double, bool, bool)
 	GDVIRTUAL0RC(String, _get_caption)
 	GDVIRTUAL0RC(bool, _has_filter)
 
@@ -133,7 +134,7 @@ class AnimationNode : public Resource {
 
 	virtual void get_child_nodes(List<ChildNode> *r_child_nodes);
 
-	virtual double process(double p_time, bool p_seek);
+	virtual double process(double p_time, bool p_seek, bool p_seek_root);
 	virtual String get_caption() const;
 
 	int get_input_count() const;