Browse Source

Merge pull request #91437 from TokageItLab/auto-capture

Add `auto_capture` option to AnimationPlayer
Rémi Verschelde 1 year ago
parent
commit
06d105e268

+ 4 - 1
doc/classes/Animation.xml

@@ -595,6 +595,9 @@
 		</method>
 		</method>
 	</methods>
 	</methods>
 	<members>
 	<members>
+		<member name="capture_included" type="bool" setter="_set_capture_included" getter="is_capture_included" default="false">
+			Returns [code]true[/code] if the capture track is included. This is a cached readonly value for performance.
+		</member>
 		<member name="length" type="float" setter="set_length" getter="get_length" default="1.0">
 		<member name="length" type="float" setter="set_length" getter="get_length" default="1.0">
 			The total length of the animation (in seconds).
 			The total length of the animation (in seconds).
 			[b]Note:[/b] Length is not delimited by the last key, as this one may be before or after the end to ensure correct interpolation and looping.
 			[b]Note:[/b] Length is not delimited by the last key, as this one may be before or after the end to ensure correct interpolation and looping.
@@ -658,7 +661,7 @@
 			Update at the keyframes.
 			Update at the keyframes.
 		</constant>
 		</constant>
 		<constant name="UPDATE_CAPTURE" value="2" enum="UpdateMode">
 		<constant name="UPDATE_CAPTURE" value="2" enum="UpdateMode">
-			Same as [constant UPDATE_CONTINUOUS] but works as a flag to capture the value of the current object and perform interpolation in some methods. See also [method AnimationMixer.capture] and [method AnimationPlayer.play_with_capture].
+			Same as [constant UPDATE_CONTINUOUS] but works as a flag to capture the value of the current object and perform interpolation in some methods. See also [method AnimationMixer.capture], [member AnimationPlayer.playback_auto_capture], and [method AnimationPlayer.play_with_capture].
 		</constant>
 		</constant>
 		<constant name="LOOP_NONE" value="0" enum="LoopMode">
 		<constant name="LOOP_NONE" value="0" enum="LoopMode">
 			At both ends of the animation, the animation will stop playing.
 			At both ends of the animation, the animation will stop playing.

+ 7 - 3
doc/classes/AnimationPlayer.xml

@@ -112,7 +112,7 @@
 		</method>
 		</method>
 		<method name="play_with_capture">
 		<method name="play_with_capture">
 			<return type="void" />
 			<return type="void" />
-			<param index="0" name="name" type="StringName" />
+			<param index="0" name="name" type="StringName" default="&amp;&quot;&quot;" />
 			<param index="1" name="duration" type="float" default="-1.0" />
 			<param index="1" name="duration" type="float" default="-1.0" />
 			<param index="2" name="custom_blend" type="float" default="-1" />
 			<param index="2" name="custom_blend" type="float" default="-1" />
 			<param index="3" name="custom_speed" type="float" default="1.0" />
 			<param index="3" name="custom_speed" type="float" default="1.0" />
@@ -120,12 +120,13 @@
 			<param index="5" name="trans_type" type="int" enum="Tween.TransitionType" default="0" />
 			<param index="5" name="trans_type" type="int" enum="Tween.TransitionType" default="0" />
 			<param index="6" name="ease_type" type="int" enum="Tween.EaseType" default="0" />
 			<param index="6" name="ease_type" type="int" enum="Tween.EaseType" default="0" />
 			<description>
 			<description>
-				See [method AnimationMixer.capture]. It is almost the same as the following:
+				See also [method AnimationMixer.capture].
+				You can use this method to use more detailed options for capture than those performed by [member playback_auto_capture]. When [member playback_auto_capture] is [code]false[/code], this method is almost the same as the following:
 				[codeblock]
 				[codeblock]
 				capture(name, duration, trans_type, ease_type)
 				capture(name, duration, trans_type, ease_type)
 				play(name, custom_blend, custom_speed, from_end)
 				play(name, custom_blend, custom_speed, from_end)
 				[/codeblock]
 				[/codeblock]
-				If name is blank, it specifies [member assigned_animation].
+				If [param name] is blank, it specifies [member assigned_animation].
 				If [param duration] is a negative value, the duration is set to the interval between the current position and the first key, when [param from_end] is [code]true[/code], uses the interval between the current position and the last key instead.
 				If [param duration] is a negative value, the duration is set to the interval between the current position and the first key, when [param from_end] is [code]true[/code], uses the interval between the current position and the last key instead.
 				[b]Note:[/b] The [param duration] takes [member speed_scale] into account, but [param custom_speed] does not, because the capture cache is interpolated with the blend result and the result may contain multiple animations.
 				[b]Note:[/b] The [param duration] takes [member speed_scale] into account, but [param custom_speed] does not, because the capture cache is interpolated with the blend result and the result may contain multiple animations.
 			</description>
 			</description>
@@ -210,6 +211,9 @@
 			If [code]true[/code] and the engine is running in Movie Maker mode (see [MovieWriter]), exits the engine with [method SceneTree.quit] as soon as an animation is done playing in this [AnimationPlayer]. A message is printed when the engine quits for this reason.
 			If [code]true[/code] and the engine is running in Movie Maker mode (see [MovieWriter]), exits the engine with [method SceneTree.quit] as soon as an animation is done playing in this [AnimationPlayer]. A message is printed when the engine quits for this reason.
 			[b]Note:[/b] This obeys the same logic as the [signal AnimationMixer.animation_finished] signal, so it will not quit the engine if the animation is set to be looping.
 			[b]Note:[/b] This obeys the same logic as the [signal AnimationMixer.animation_finished] signal, so it will not quit the engine if the animation is set to be looping.
 		</member>
 		</member>
+		<member name="playback_auto_capture" type="bool" setter="set_auto_capture" getter="is_auto_capture" default="true">
+			If [code]true[/code], performs [method AnimationMixer.capture] before playback automatically. This means just [method play_with_capture] is executed with default arguments instead of [method play].
+		</member>
 		<member name="playback_default_blend_time" type="float" setter="set_default_blend_time" getter="get_default_blend_time" default="0.0">
 		<member name="playback_default_blend_time" type="float" setter="set_default_blend_time" getter="get_default_blend_time" default="0.0">
 			The default time in which to blend animations. Ranges from 0 to 4096 with 0.01 precision.
 			The default time in which to blend animations. Ranges from 0 to 4096 with 0.01 precision.
 		</member>
 		</member>

+ 1 - 1
scene/animation/animation_mixer.cpp

@@ -2093,7 +2093,7 @@ Ref<AnimatedValuesBackup> AnimationMixer::apply_reset(bool p_user_initiated) {
 void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) {
 void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) {
 	ERR_FAIL_COND(!active);
 	ERR_FAIL_COND(!active);
 	ERR_FAIL_COND(!has_animation(p_name));
 	ERR_FAIL_COND(!has_animation(p_name));
-	ERR_FAIL_COND(Math::is_zero_approx(p_duration));
+	ERR_FAIL_COND(p_duration <= 0);
 	Ref<Animation> reference_animation = get_animation(p_name);
 	Ref<Animation> reference_animation = get_animation(p_name);
 
 
 	if (!cache_valid) {
 	if (!cache_valid) {

+ 60 - 59
scene/animation/animation_player.cpp

@@ -370,73 +370,21 @@ void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_b
 	play(p_name, p_custom_blend, -1, true);
 	play(p_name, p_custom_blend, -1, true);
 }
 }
 
 
-void AnimationPlayer::play_with_capture(const StringName &p_name, double p_duration, double p_custom_blend, float p_custom_scale, bool p_from_end, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) {
-	StringName name = p_name;
-	if (name == StringName()) {
-		name = playback.assigned;
-	}
-
-	if (signbit(p_duration)) {
-		double max_dur = 0;
-		Ref<Animation> anim = get_animation(name);
-		if (anim.is_valid()) {
-			double current_pos = playback.current.pos;
-			if (playback.assigned != name) {
-				current_pos = p_from_end ? anim->get_length() : 0;
-			}
-			for (int i = 0; i < anim->get_track_count(); i++) {
-				if (anim->track_get_type(i) != Animation::TYPE_VALUE) {
-					continue;
-				}
-				if (anim->value_track_get_update_mode(i) != Animation::UPDATE_CAPTURE) {
-					continue;
-				}
-				if (anim->track_get_key_count(i) == 0) {
-					continue;
-				}
-				max_dur = MAX(max_dur, p_from_end ? current_pos - anim->track_get_key_time(i, anim->track_get_key_count(i) - 1) : anim->track_get_key_time(i, 0) - current_pos);
-			}
-		}
-		p_duration = max_dur;
+void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
+	if (auto_capture) {
+		play_with_capture(p_name, -1.0, p_custom_blend, p_custom_scale, p_from_end);
+	} else {
+		_play(p_name, p_custom_blend, p_custom_scale, p_from_end);
 	}
 	}
-
-	capture(name, p_duration, p_trans_type, p_ease_type);
-	play(name, p_custom_blend, p_custom_scale, p_from_end);
 }
 }
 
 
-void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
+void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
 	StringName name = p_name;
 	StringName name = p_name;
 
 
 	if (name == StringName()) {
 	if (name == StringName()) {
 		name = playback.assigned;
 		name = playback.assigned;
 	}
 	}
 
 
-#ifdef TOOLS_ENABLED
-	if (!Engine::get_singleton()->is_editor_hint()) {
-		bool warn_enabled = false;
-		if (capture_cache.animation.is_null()) {
-			Ref<Animation> anim = get_animation(name);
-			if (anim.is_valid()) {
-				for (int i = 0; i < anim->get_track_count(); i++) {
-					if (anim->track_get_type(i) != Animation::TYPE_VALUE) {
-						continue;
-					}
-					if (anim->value_track_get_update_mode(i) != Animation::UPDATE_CAPTURE) {
-						continue;
-					}
-					if (anim->track_get_key_count(i) == 0) {
-						continue;
-					}
-					warn_enabled = true;
-				}
-			}
-		}
-		if (warn_enabled) {
-			WARN_PRINT_ONCE_ED("Capture track found. If you want to interpolate animation with captured frame, you can use play_with_capture() instead of play().");
-		}
-	}
-#endif
-
 	ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
 	ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
 
 
 	Playback &c = playback;
 	Playback &c = playback;
@@ -525,6 +473,47 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
 	}
 	}
 }
 }
 
 
+void AnimationPlayer::_capture(const StringName &p_name, bool p_from_end, double p_duration, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) {
+	StringName name = p_name;
+	if (name == StringName()) {
+		name = playback.assigned;
+	}
+
+	Ref<Animation> anim = get_animation(name);
+	if (anim.is_null() || !anim->is_capture_included()) {
+		return;
+	}
+	if (signbit(p_duration)) {
+		double max_dur = 0;
+		double current_pos = playback.current.pos;
+		if (playback.assigned != name) {
+			current_pos = p_from_end ? anim->get_length() : 0;
+		}
+		for (int i = 0; i < anim->get_track_count(); i++) {
+			if (anim->track_get_type(i) != Animation::TYPE_VALUE) {
+				continue;
+			}
+			if (anim->value_track_get_update_mode(i) != Animation::UPDATE_CAPTURE) {
+				continue;
+			}
+			if (anim->track_get_key_count(i) == 0) {
+				continue;
+			}
+			max_dur = MAX(max_dur, p_from_end ? current_pos - anim->track_get_key_time(i, anim->track_get_key_count(i) - 1) : anim->track_get_key_time(i, 0) - current_pos);
+		}
+		p_duration = max_dur;
+	}
+	if (Math::is_zero_approx(p_duration)) {
+		return;
+	}
+	capture(name, p_duration, p_trans_type, p_ease_type);
+}
+
+void AnimationPlayer::play_with_capture(const StringName &p_name, double p_duration, double p_custom_blend, float p_custom_scale, bool p_from_end, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) {
+	_capture(p_name, p_from_end, p_duration, p_trans_type, p_ease_type);
+	_play(p_name, p_custom_blend, p_custom_scale, p_from_end);
+}
+
 bool AnimationPlayer::is_playing() const {
 bool AnimationPlayer::is_playing() const {
 	return playing;
 	return playing;
 }
 }
@@ -725,6 +714,14 @@ double AnimationPlayer::get_blend_time(const StringName &p_animation1, const Str
 	}
 	}
 }
 }
 
 
+void AnimationPlayer::set_auto_capture(bool p_auto_capture) {
+	auto_capture = p_auto_capture;
+}
+
+bool AnimationPlayer::is_auto_capture() const {
+	return auto_capture;
+}
+
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
 void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
 	const String pf = p_function;
 	const String pf = p_function;
@@ -815,9 +812,12 @@ void AnimationPlayer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_default_blend_time", "sec"), &AnimationPlayer::set_default_blend_time);
 	ClassDB::bind_method(D_METHOD("set_default_blend_time", "sec"), &AnimationPlayer::set_default_blend_time);
 	ClassDB::bind_method(D_METHOD("get_default_blend_time"), &AnimationPlayer::get_default_blend_time);
 	ClassDB::bind_method(D_METHOD("get_default_blend_time"), &AnimationPlayer::get_default_blend_time);
 
 
+	ClassDB::bind_method(D_METHOD("set_auto_capture", "auto_capture"), &AnimationPlayer::set_auto_capture);
+	ClassDB::bind_method(D_METHOD("is_auto_capture"), &AnimationPlayer::is_auto_capture);
+
 	ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(StringName()), DEFVAL(-1));
 	ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(StringName()), DEFVAL(-1));
-	ClassDB::bind_method(D_METHOD("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
+	ClassDB::bind_method(D_METHOD("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(StringName()), DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
 	ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
 	ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
 	ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("is_playing"), &AnimationPlayer::is_playing);
 	ClassDB::bind_method(D_METHOD("is_playing"), &AnimationPlayer::is_playing);
@@ -855,6 +855,7 @@ void AnimationPlayer::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_position");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_position");
 
 
 	ADD_GROUP("Playback Options", "playback_");
 	ADD_GROUP("Playback Options", "playback_");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_auto_capture"), "set_auto_capture", "is_auto_capture");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:s"), "set_default_blend_time", "get_default_blend_time");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:s"), "set_default_blend_time", "get_default_blend_time");
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-4,4,0.001,or_less,or_greater"), "set_speed_scale", "get_speed_scale");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-4,4,0.001,or_less,or_greater"), "set_speed_scale", "get_speed_scale");

+ 7 - 1
scene/animation/animation_player.h

@@ -56,6 +56,7 @@ private:
 
 
 	float speed_scale = 1.0;
 	float speed_scale = 1.0;
 	double default_blend_time = 0.0;
 	double default_blend_time = 0.0;
+	bool auto_capture = true;
 	bool is_stopping = false;
 	bool is_stopping = false;
 
 
 	struct PlaybackData {
 	struct PlaybackData {
@@ -108,6 +109,8 @@ private:
 	bool reset_on_save = true;
 	bool reset_on_save = true;
 	bool movie_quit_on_finish = false;
 	bool movie_quit_on_finish = false;
 
 
+	void _play(const StringName &p_name, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
+	void _capture(const StringName &p_name, bool p_from_end = false, double p_duration = -1.0, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
 	void _process_playback_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started, bool p_is_current = false);
 	void _process_playback_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started, bool p_is_current = false);
 	void _blend_playback_data(double p_delta, bool p_started);
 	void _blend_playback_data(double p_delta, bool p_started);
 	void _stop_internal(bool p_reset, bool p_keep_state);
 	void _stop_internal(bool p_reset, bool p_keep_state);
@@ -158,9 +161,12 @@ public:
 	void set_default_blend_time(double p_default);
 	void set_default_blend_time(double p_default);
 	double get_default_blend_time() const;
 	double get_default_blend_time() const;
 
 
+	void set_auto_capture(bool p_auto_capture);
+	bool is_auto_capture() const;
+
 	void play(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
 	void play(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
 	void play_backwards(const StringName &p_name = StringName(), double p_custom_blend = -1);
 	void play_backwards(const StringName &p_name = StringName(), double p_custom_blend = -1);
-	void play_with_capture(const StringName &p_name, double p_duration = -1.0, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
+	void play_with_capture(const StringName &p_name = StringName(), double p_duration = -1.0, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
 	void queue(const StringName &p_name);
 	void queue(const StringName &p_name);
 	Vector<String> get_queue();
 	Vector<String> get_queue();
 	void clear_queue();
 	void clear_queue();

+ 29 - 0
scene/resources/animation.cpp

@@ -247,6 +247,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
 					}
 					}
 					vt->update_mode = UpdateMode(um);
 					vt->update_mode = UpdateMode(um);
 				}
 				}
+				capture_included = capture_included || (vt->update_mode == UPDATE_CAPTURE);
 
 
 				Vector<real_t> times = d["times"];
 				Vector<real_t> times = d["times"];
 				Array values = d["values"];
 				Array values = d["values"];
@@ -966,6 +967,28 @@ void Animation::remove_track(int p_track) {
 	memdelete(t);
 	memdelete(t);
 	tracks.remove_at(p_track);
 	tracks.remove_at(p_track);
 	emit_changed();
 	emit_changed();
+	_check_capture_included();
+}
+
+void Animation::set_capture_included(bool p_capture_included) {
+	capture_included = p_capture_included;
+}
+
+bool Animation::is_capture_included() const {
+	return capture_included;
+}
+
+void Animation::_check_capture_included() {
+	capture_included = false;
+	for (int i = 0; i < tracks.size(); i++) {
+		if (tracks[i]->type == TYPE_VALUE) {
+			ValueTrack *vt = static_cast<ValueTrack *>(tracks[i]);
+			if (vt->update_mode == UPDATE_CAPTURE) {
+				capture_included = true;
+				break;
+			}
+		}
+	}
 }
 }
 
 
 int Animation::get_track_count() const {
 int Animation::get_track_count() const {
@@ -2681,6 +2704,8 @@ void Animation::value_track_set_update_mode(int p_track, UpdateMode p_mode) {
 
 
 	ValueTrack *vt = static_cast<ValueTrack *>(t);
 	ValueTrack *vt = static_cast<ValueTrack *>(t);
 	vt->update_mode = p_mode;
 	vt->update_mode = p_mode;
+
+	capture_included = capture_included || (p_mode == UPDATE_CAPTURE);
 	emit_changed();
 	emit_changed();
 }
 }
 
 
@@ -3870,9 +3895,13 @@ void Animation::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
 	ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
 
 
+	ClassDB::bind_method(D_METHOD("_set_capture_included", "capture_included"), &Animation::set_capture_included);
+	ClassDB::bind_method(D_METHOD("is_capture_included"), &Animation::is_capture_included);
+
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001,suffix:s"), "set_length", "get_length");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001,suffix:s"), "set_length", "get_length");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001,suffix:s"), "set_step", "get_step");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001,suffix:s"), "set_step", "get_step");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "capture_included", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY | PROPERTY_USAGE_NO_EDITOR), "_set_capture_included", "is_capture_included");
 
 
 	BIND_ENUM_CONSTANT(TYPE_VALUE);
 	BIND_ENUM_CONSTANT(TYPE_VALUE);
 	BIND_ENUM_CONSTANT(TYPE_POSITION_3D);
 	BIND_ENUM_CONSTANT(TYPE_POSITION_3D);

+ 5 - 0
scene/resources/animation.h

@@ -268,6 +268,8 @@ private:
 	double length = 1.0;
 	double length = 1.0;
 	real_t step = 1.0 / 30;
 	real_t step = 1.0 / 30;
 	LoopMode loop_mode = LOOP_NONE;
 	LoopMode loop_mode = LOOP_NONE;
+	bool capture_included = false;
+	void _check_capture_included();
 
 
 	void _track_update_hash(int p_track);
 	void _track_update_hash(int p_track);
 
 
@@ -392,6 +394,9 @@ public:
 	int add_track(TrackType p_type, int p_at_pos = -1);
 	int add_track(TrackType p_type, int p_at_pos = -1);
 	void remove_track(int p_track);
 	void remove_track(int p_track);
 
 
+	void set_capture_included(bool p_capture_included);
+	bool is_capture_included() const;
+
 	int get_track_count() const;
 	int get_track_count() const;
 	TrackType track_get_type(int p_track) const;
 	TrackType track_get_type(int p_track) const;