Browse Source

Expose interpolation methods for 3D track in Animation class

Silc Renew 2 years ago
parent
commit
fac8a918f9

+ 32 - 0
doc/classes/Animation.xml

@@ -228,6 +228,14 @@
 				Inserts a key in a given blend shape track. Returns the key index.
 			</description>
 		</method>
+		<method name="blend_shape_track_interpolate" qualifiers="const">
+			<return type="float" />
+			<param index="0" name="track_idx" type="int" />
+			<param index="1" name="time_sec" type="float" />
+			<description>
+				Returns the interpolated blend shape value at the given time (in seconds). The [param track_idx] must be the index of a blend shape track.
+			</description>
+		</method>
 		<method name="clear">
 			<return type="void" />
 			<description>
@@ -291,6 +299,14 @@
 				Inserts a key in a given 3D position track. Returns the key index.
 			</description>
 		</method>
+		<method name="position_track_interpolate" qualifiers="const">
+			<return type="Vector3" />
+			<param index="0" name="track_idx" type="int" />
+			<param index="1" name="time_sec" type="float" />
+			<description>
+				Returns the interpolated position value at the given time (in seconds). The [param track_idx] must be the index of a 3D position track.
+			</description>
+		</method>
 		<method name="remove_track">
 			<return type="void" />
 			<param index="0" name="track_idx" type="int" />
@@ -307,6 +323,14 @@
 				Inserts a key in a given 3D rotation track. Returns the key index.
 			</description>
 		</method>
+		<method name="rotation_track_interpolate" qualifiers="const">
+			<return type="Quaternion" />
+			<param index="0" name="track_idx" type="int" />
+			<param index="1" name="time_sec" type="float" />
+			<description>
+				Returns the interpolated rotation value at the given time (in seconds). The [param track_idx] must be the index of a 3D rotation track.
+			</description>
+		</method>
 		<method name="scale_track_insert_key">
 			<return type="int" />
 			<param index="0" name="track_idx" type="int" />
@@ -316,6 +340,14 @@
 				Inserts a key in a given 3D scale track. Returns the key index.
 			</description>
 		</method>
+		<method name="scale_track_interpolate" qualifiers="const">
+			<return type="Vector3" />
+			<param index="0" name="track_idx" type="int" />
+			<param index="1" name="time_sec" type="float" />
+			<description>
+				Returns the interpolated scale value at the given time (in seconds). The [param track_idx] must be the index of a 3D scale track.
+			</description>
+		</method>
 		<method name="track_find_key" qualifiers="const">
 			<return type="int" />
 			<param index="0" name="track_idx" type="int" />

+ 1 - 1
doc/classes/Skeleton3D.xml

@@ -300,7 +300,7 @@
 		<member name="animate_physical_bones" type="bool" setter="set_animate_physical_bones" getter="get_animate_physical_bones" default="true">
 		</member>
 		<member name="motion_scale" type="float" setter="set_motion_scale" getter="get_motion_scale" default="1.0">
-			Multiplies the position 3D track animation.
+			Multiplies the 3D position track animation.
 			[b]Note:[/b] Unless this value is [code]1.0[/code], the key value in animation will not match the actual position value.
 		</member>
 		<member name="show_rest_only" type="bool" setter="set_show_rest_only" getter="is_show_rest_only" default="false">

+ 5 - 5
editor/animation_track_editor.cpp

@@ -6084,7 +6084,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
 								Pair<real_t, Variant> keydata;
 								keydata.first = delta_t;
 								Vector3 v;
-								animation->position_track_interpolate(i, delta_t, &v);
+								animation->try_position_track_interpolate(i, delta_t, &v);
 								keydata.second = v;
 								insert_queue_new.append(keydata);
 							}
@@ -6094,7 +6094,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
 								Pair<real_t, Variant> keydata;
 								keydata.first = delta_t;
 								Quaternion v;
-								animation->rotation_track_interpolate(i, delta_t, &v);
+								animation->try_rotation_track_interpolate(i, delta_t, &v);
 								keydata.second = v;
 								insert_queue_new.append(keydata);
 							}
@@ -6104,7 +6104,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
 								Pair<real_t, Variant> keydata;
 								keydata.first = delta_t;
 								Vector3 v;
-								animation->scale_track_interpolate(i, delta_t, &v);
+								animation->try_scale_track_interpolate(i, delta_t, &v);
 								keydata.second = v;
 								insert_queue_new.append(keydata);
 							}
@@ -6114,7 +6114,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
 								Pair<real_t, Variant> keydata;
 								keydata.first = delta_t;
 								float v;
-								animation->blend_shape_track_interpolate(i, delta_t, &v);
+								animation->try_blend_shape_track_interpolate(i, delta_t, &v);
 								keydata.second = v;
 								insert_queue_new.append(keydata);
 							}
@@ -6760,7 +6760,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
 	bake_fps->set_max(999);
 	bake_fps->set_step(1);
 	bake_fps->set_value(30); // Default
-	bake_grid->add_child(memnew(Label(TTR("Pos/Rot/Scl3D Track:"))));
+	bake_grid->add_child(memnew(Label(TTR("3D Pos/Rot/Scl Track:"))));
 	bake_grid->add_child(bake_trs);
 	bake_grid->add_child(memnew(Label(TTR("Blendshape Track:"))));
 	bake_grid->add_child(bake_blendshape);

+ 16 - 16
editor/import/resource_importer_scene.cpp

@@ -1455,22 +1455,22 @@ void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> a
 						if (kt > (from + 0.01) && k > 0) {
 							if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
 								Vector3 p;
-								anim->position_track_interpolate(j, from, &p);
+								anim->try_position_track_interpolate(j, from, &p);
 								new_anim->position_track_insert_key(dtrack, 0, p);
 							} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
 								Quaternion r;
-								anim->rotation_track_interpolate(j, from, &r);
+								anim->try_rotation_track_interpolate(j, from, &r);
 								new_anim->rotation_track_insert_key(dtrack, 0, r);
 							} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
 								Vector3 s;
-								anim->scale_track_interpolate(j, from, &s);
+								anim->try_scale_track_interpolate(j, from, &s);
 								new_anim->scale_track_insert_key(dtrack, 0, s);
 							} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {
 								Variant var = anim->value_track_interpolate(j, from);
 								new_anim->track_insert_key(dtrack, 0, var);
 							} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
 								float interp;
-								anim->blend_shape_track_interpolate(j, from, &interp);
+								anim->try_blend_shape_track_interpolate(j, from, &interp);
 								new_anim->blend_shape_track_insert_key(dtrack, 0, interp);
 							}
 						}
@@ -1501,22 +1501,22 @@ void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> a
 				if (dtrack != -1 && kt >= to) {
 					if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
 						Vector3 p;
-						anim->position_track_interpolate(j, to, &p);
+						anim->try_position_track_interpolate(j, to, &p);
 						new_anim->position_track_insert_key(dtrack, to - from, p);
 					} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
 						Quaternion r;
-						anim->rotation_track_interpolate(j, to, &r);
+						anim->try_rotation_track_interpolate(j, to, &r);
 						new_anim->rotation_track_insert_key(dtrack, to - from, r);
 					} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
 						Vector3 s;
-						anim->scale_track_interpolate(j, to, &s);
+						anim->try_scale_track_interpolate(j, to, &s);
 						new_anim->scale_track_insert_key(dtrack, to - from, s);
 					} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {
 						Variant var = anim->value_track_interpolate(j, to);
 						new_anim->track_insert_key(dtrack, to - from, var);
 					} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
 						float interp;
-						anim->blend_shape_track_interpolate(j, to, &interp);
+						anim->try_blend_shape_track_interpolate(j, to, &interp);
 						new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);
 					}
 				}
@@ -1528,21 +1528,21 @@ void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> a
 				new_anim->track_set_path(dtrack, anim->track_get_path(j));
 				if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
 					Vector3 p;
-					anim->position_track_interpolate(j, from, &p);
+					anim->try_position_track_interpolate(j, from, &p);
 					new_anim->position_track_insert_key(dtrack, 0, p);
-					anim->position_track_interpolate(j, to, &p);
+					anim->try_position_track_interpolate(j, to, &p);
 					new_anim->position_track_insert_key(dtrack, to - from, p);
 				} else if (anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
 					Quaternion r;
-					anim->rotation_track_interpolate(j, from, &r);
+					anim->try_rotation_track_interpolate(j, from, &r);
 					new_anim->rotation_track_insert_key(dtrack, 0, r);
-					anim->rotation_track_interpolate(j, to, &r);
+					anim->try_rotation_track_interpolate(j, to, &r);
 					new_anim->rotation_track_insert_key(dtrack, to - from, r);
 				} else if (anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
 					Vector3 s;
-					anim->scale_track_interpolate(j, from, &s);
+					anim->try_scale_track_interpolate(j, from, &s);
 					new_anim->scale_track_insert_key(dtrack, 0, s);
-					anim->scale_track_interpolate(j, to, &s);
+					anim->try_scale_track_interpolate(j, to, &s);
 					new_anim->scale_track_insert_key(dtrack, to - from, s);
 				} else if (anim->track_get_type(j) == Animation::TYPE_VALUE) {
 					Variant var = anim->value_track_interpolate(j, from);
@@ -1551,9 +1551,9 @@ void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> a
 					new_anim->track_insert_key(dtrack, to - from, to_var);
 				} else if (anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
 					float interp;
-					anim->blend_shape_track_interpolate(j, from, &interp);
+					anim->try_blend_shape_track_interpolate(j, from, &interp);
 					new_anim->blend_shape_track_insert_key(dtrack, 0, interp);
-					anim->blend_shape_track_interpolate(j, to, &interp);
+					anim->try_blend_shape_track_interpolate(j, to, &interp);
 					new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);
 				}
 			}

+ 3 - 3
editor/plugins/animation_player_editor_plugin.cpp

@@ -1607,7 +1607,7 @@ bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
 			for (int j = 0; j < key_len; j++) {
 				Quaternion q;
 				p_anim->rotation_track_get_key(i, j, &q);
-				ERR_BREAK_EDMSG(!q.is_normalized(), "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', rotation track:  '" + p_anim->track_get_path(i) + "' contains unnormalized Quaternion key.");
+				ERR_BREAK_EDMSG(!q.is_normalized(), "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', 3D Rotation Track:  '" + p_anim->track_get_path(i) + "' contains unnormalized Quaternion key.");
 			}
 		} else if (ttype == Animation::TYPE_VALUE) {
 			int key_len = p_anim->track_get_key_count(i);
@@ -1620,7 +1620,7 @@ bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
 						Quaternion q = Quaternion(p_anim->track_get_key_value(i, j));
 						if (!q.is_normalized()) {
 							is_valid = false;
-							ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', value track:  '" + p_anim->track_get_path(i) + "' contains unnormalized Quaternion key.");
+							ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', Value Track:  '" + p_anim->track_get_path(i) + "' contains unnormalized Quaternion key.");
 						}
 					}
 				} break;
@@ -1629,7 +1629,7 @@ bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
 						Transform3D t = Transform3D(p_anim->track_get_key_value(i, j));
 						if (!t.basis.orthonormalized().is_rotation()) {
 							is_valid = false;
-							ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', value track:  '" + p_anim->track_get_path(i) + "' contains corrupted basis (some axes are too close other axis or scaled by zero) Transform3D key.");
+							ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', Value Track:  '" + p_anim->track_get_path(i) + "' contains corrupted basis (some axes are too close other axis or scaled by zero) Transform3D key.");
 						}
 					}
 				} break;

+ 6 - 6
modules/gltf/gltf_document.cpp

@@ -6478,7 +6478,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
 			bool last = false;
 			while (true) {
 				Vector3 scale;
-				Error err = p_animation->scale_track_interpolate(p_track_i, time, &scale);
+				Error err = p_animation->try_scale_track_interpolate(p_track_i, time, &scale);
 				ERR_CONTINUE(err != OK);
 				p_track.scale_track.values.push_back(scale);
 				p_track.scale_track.times.push_back(time);
@@ -6513,7 +6513,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
 			bool last = false;
 			while (true) {
 				Vector3 scale;
-				Error err = p_animation->position_track_interpolate(p_track_i, time, &scale);
+				Error err = p_animation->try_position_track_interpolate(p_track_i, time, &scale);
 				ERR_CONTINUE(err != OK);
 				p_track.position_track.values.push_back(scale);
 				p_track.position_track.times.push_back(time);
@@ -6548,7 +6548,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
 			bool last = false;
 			while (true) {
 				Quaternion rotation;
-				Error err = p_animation->rotation_track_interpolate(p_track_i, time, &rotation);
+				Error err = p_animation->try_rotation_track_interpolate(p_track_i, time, &rotation);
 				ERR_CONTINUE(err != OK);
 				p_track.rotation_track.values.push_back(rotation);
 				p_track.rotation_track.times.push_back(time);
@@ -6588,7 +6588,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
 				bool last = false;
 				while (true) {
 					Vector3 position;
-					Error err = p_animation->position_track_interpolate(p_track_i, time, &position);
+					Error err = p_animation->try_position_track_interpolate(p_track_i, time, &position);
 					ERR_CONTINUE(err != OK);
 					p_track.position_track.values.push_back(position);
 					p_track.position_track.times.push_back(time);
@@ -6621,7 +6621,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
 				bool last = false;
 				while (true) {
 					Quaternion rotation;
-					Error err = p_animation->rotation_track_interpolate(p_track_i, time, &rotation);
+					Error err = p_animation->try_rotation_track_interpolate(p_track_i, time, &rotation);
 					ERR_CONTINUE(err != OK);
 					p_track.rotation_track.values.push_back(rotation);
 					p_track.rotation_track.times.push_back(time);
@@ -6657,7 +6657,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
 				bool last = false;
 				while (true) {
 					Vector3 scale;
-					Error err = p_animation->scale_track_interpolate(p_track_i, time, &scale);
+					Error err = p_animation->try_scale_track_interpolate(p_track_i, time, &scale);
 					ERR_CONTINUE(err != OK);
 					p_track.scale_track.values.push_back(scale);
 					p_track.scale_track.times.push_back(time);

+ 4 - 4
scene/animation/animation_player.cpp

@@ -528,7 +528,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 
 				Vector3 loc;
 
-				Error err = a->position_track_interpolate(i, p_time, &loc);
+				Error err = a->try_position_track_interpolate(i, p_time, &loc);
 				//ERR_CONTINUE(err!=OK); //used for testing, should be removed
 
 				if (err != OK) {
@@ -556,7 +556,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 
 				Quaternion rot;
 
-				Error err = a->rotation_track_interpolate(i, p_time, &rot);
+				Error err = a->try_rotation_track_interpolate(i, p_time, &rot);
 				//ERR_CONTINUE(err!=OK); //used for testing, should be removed
 
 				if (err != OK) {
@@ -584,7 +584,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 
 				Vector3 scale;
 
-				Error err = a->scale_track_interpolate(i, p_time, &scale);
+				Error err = a->try_scale_track_interpolate(i, p_time, &scale);
 				//ERR_CONTINUE(err!=OK); //used for testing, should be removed
 
 				if (err != OK) {
@@ -612,7 +612,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 
 				float blend;
 
-				Error err = a->blend_shape_track_interpolate(i, p_time, &blend);
+				Error err = a->try_blend_shape_track_interpolate(i, p_time, &blend);
 				//ERR_CONTINUE(err!=OK); //used for testing, should be removed
 
 				if (err != OK) {

+ 25 - 25
scene/animation/animation_tree.cpp

@@ -654,7 +654,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 
 						// If a value track without a key is cached first, the initial value cannot be determined.
 						// It is a corner case, but which may cause problems with blending.
-						ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, "AnimationTree: '" + String(E) + "', value track:  '" + String(path) + "' must have at least one key to cache for blending.");
+						ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, "AnimationTree: '" + String(E) + "', Value Track:  '" + String(path) + "' must have at least one key to cache for blending.");
 						track_value->init_value = anim->track_get_key_value(i, 0);
 						track_value->init_value.zero();
 
@@ -867,10 +867,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 				track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
 
 				if (was_discrete != track_value->is_discrete) {
-					ERR_PRINT_ED("Value track: " + String(path) + " with different update modes are blended. Blending prioritizes Discrete mode, so other update mode tracks will not be blended.");
+					ERR_PRINT_ED("Value Track: " + String(path) + " with different update modes are blended. Blending prioritizes Discrete mode, so other update mode tracks will not be blended.");
 				}
 				if (was_using_angle != track_value->is_using_angle) {
-					WARN_PRINT_ED("Value track: " + String(path) + " with different interpolation types for rotation are blended. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
+					WARN_PRINT_ED("Value Track: " + String(path) + " with different interpolation types for rotation are blended. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
 				}
 			}
 
@@ -1181,36 +1181,36 @@ void AnimationTree::_process_graph(double p_delta) {
 
 							if (!backward) {
 								if (prev_time > time) {
-									Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+									Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
 									if (err != OK) {
 										continue;
 									}
 									loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
-									a->position_track_interpolate(i, (double)a->get_length(), &loc[1]);
+									a->try_position_track_interpolate(i, (double)a->get_length(), &loc[1]);
 									loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
 									root_motion_cache.loc += (loc[1] - loc[0]) * blend;
 									prev_time = 0;
 								}
 							} else {
 								if (prev_time < time) {
-									Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+									Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
 									if (err != OK) {
 										continue;
 									}
 									loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
-									a->position_track_interpolate(i, 0, &loc[1]);
+									a->try_position_track_interpolate(i, 0, &loc[1]);
 									loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
 									root_motion_cache.loc += (loc[1] - loc[0]) * blend;
 									prev_time = (double)a->get_length();
 								}
 							}
 
-							Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+							Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
 							if (err != OK) {
 								continue;
 							}
 							loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
-							a->position_track_interpolate(i, time, &loc[1]);
+							a->try_position_track_interpolate(i, time, &loc[1]);
 							loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
 							root_motion_cache.loc += (loc[1] - loc[0]) * blend;
 							prev_time = !backward ? 0 : (double)a->get_length();
@@ -1219,7 +1219,7 @@ void AnimationTree::_process_graph(double p_delta) {
 						{
 							Vector3 loc;
 
-							Error err = a->position_track_interpolate(i, time, &loc);
+							Error err = a->try_position_track_interpolate(i, time, &loc);
 							if (err != OK) {
 								continue;
 							}
@@ -1276,36 +1276,36 @@ void AnimationTree::_process_graph(double p_delta) {
 
 							if (!backward) {
 								if (prev_time > time) {
-									Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+									Error err = a->try_rotation_track_interpolate(i, prev_time, &rot[0]);
 									if (err != OK) {
 										continue;
 									}
 									rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
-									a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
+									a->try_rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
 									rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
 									root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
 									prev_time = 0;
 								}
 							} else {
 								if (prev_time < time) {
-									Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+									Error err = a->try_rotation_track_interpolate(i, prev_time, &rot[0]);
 									if (err != OK) {
 										continue;
 									}
 									rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
-									a->rotation_track_interpolate(i, 0, &rot[1]);
+									a->try_rotation_track_interpolate(i, 0, &rot[1]);
 									root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
 									prev_time = (double)a->get_length();
 								}
 							}
 
-							Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+							Error err = a->try_rotation_track_interpolate(i, prev_time, &rot[0]);
 							if (err != OK) {
 								continue;
 							}
 							rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
 
-							a->rotation_track_interpolate(i, time, &rot[1]);
+							a->try_rotation_track_interpolate(i, time, &rot[1]);
 							rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
 							root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
 							prev_time = !backward ? 0 : (double)a->get_length();
@@ -1314,7 +1314,7 @@ void AnimationTree::_process_graph(double p_delta) {
 						{
 							Quaternion rot;
 
-							Error err = a->rotation_track_interpolate(i, time, &rot);
+							Error err = a->try_rotation_track_interpolate(i, time, &rot);
 							if (err != OK) {
 								continue;
 							}
@@ -1371,37 +1371,37 @@ void AnimationTree::_process_graph(double p_delta) {
 
 							if (!backward) {
 								if (prev_time > time) {
-									Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+									Error err = a->try_scale_track_interpolate(i, prev_time, &scale[0]);
 									if (err != OK) {
 										continue;
 									}
 									scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
-									a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]);
+									a->try_scale_track_interpolate(i, (double)a->get_length(), &scale[1]);
 									root_motion_cache.scale += (scale[1] - scale[0]) * blend;
 									scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
 									prev_time = 0;
 								}
 							} else {
 								if (prev_time < time) {
-									Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+									Error err = a->try_scale_track_interpolate(i, prev_time, &scale[0]);
 									if (err != OK) {
 										continue;
 									}
 									scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
-									a->scale_track_interpolate(i, 0, &scale[1]);
+									a->try_scale_track_interpolate(i, 0, &scale[1]);
 									scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
 									root_motion_cache.scale += (scale[1] - scale[0]) * blend;
 									prev_time = (double)a->get_length();
 								}
 							}
 
-							Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+							Error err = a->try_scale_track_interpolate(i, prev_time, &scale[0]);
 							if (err != OK) {
 								continue;
 							}
 							scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
 
-							a->scale_track_interpolate(i, time, &scale[1]);
+							a->try_scale_track_interpolate(i, time, &scale[1]);
 							scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
 							root_motion_cache.scale += (scale[1] - scale[0]) * blend;
 							prev_time = !backward ? 0 : (double)a->get_length();
@@ -1410,7 +1410,7 @@ void AnimationTree::_process_graph(double p_delta) {
 						{
 							Vector3 scale;
 
-							Error err = a->scale_track_interpolate(i, time, &scale);
+							Error err = a->try_scale_track_interpolate(i, time, &scale);
 							if (err != OK) {
 								continue;
 							}
@@ -1429,7 +1429,7 @@ void AnimationTree::_process_graph(double p_delta) {
 
 						float value;
 
-						Error err = a->blend_shape_track_interpolate(i, time, &value);
+						Error err = a->try_blend_shape_track_interpolate(i, time, &value);
 						//ERR_CONTINUE(err!=OK); //used for testing, should be removed
 
 						if (err != OK) {

+ 49 - 8
scene/resources/animation.cpp

@@ -1088,7 +1088,7 @@ Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_posit
 	return OK;
 }
 
-Error Animation::position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+Error Animation::try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
 	ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
 	Track *t = tracks[p_track];
 	ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
@@ -1114,6 +1114,14 @@ Error Animation::position_track_interpolate(int p_track, double p_time, Vector3
 	return OK;
 }
 
+Vector3 Animation::position_track_interpolate(int p_track, double p_time) const {
+	Vector3 ret = Vector3(0, 0, 0);
+	ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
+	bool err = try_position_track_interpolate(p_track, p_time, &ret);
+	ERR_FAIL_COND_V_MSG(err, ret, "3D Position Track: '" + tracks[p_track]->path + "' is unavailable.");
+	return ret;
+}
+
 ////
 
 int Animation::rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation) {
@@ -1160,7 +1168,7 @@ Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_ro
 	return OK;
 }
 
-Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const {
+Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const {
 	ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
 	Track *t = tracks[p_track];
 	ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
@@ -1186,6 +1194,14 @@ Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaterni
 	return OK;
 }
 
+Quaternion Animation::rotation_track_interpolate(int p_track, double p_time) const {
+	Quaternion ret = Quaternion(0, 0, 0, 1);
+	ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
+	bool err = try_rotation_track_interpolate(p_track, p_time, &ret);
+	ERR_FAIL_COND_V_MSG(err, ret, "3D Rotation Track: '" + tracks[p_track]->path + "' is unavailable.");
+	return ret;
+}
+
 ////
 
 int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale) {
@@ -1232,7 +1248,7 @@ Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) c
 	return OK;
 }
 
-Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
 	ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
 	Track *t = tracks[p_track];
 	ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
@@ -1258,6 +1274,16 @@ Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_
 	return OK;
 }
 
+Vector3 Animation::scale_track_interpolate(int p_track, double p_time) const {
+	Vector3 ret = Vector3(1, 1, 1);
+	ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
+	bool err = try_scale_track_interpolate(p_track, p_time, &ret);
+	ERR_FAIL_COND_V_MSG(err, ret, "3D Scale Track: '" + tracks[p_track]->path + "' is unavailable.");
+	return ret;
+}
+
+////
+
 int Animation::blend_shape_track_insert_key(int p_track, double p_time, float p_blend_shape) {
 	ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
 	Track *t = tracks[p_track];
@@ -1302,7 +1328,7 @@ Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blen
 	return OK;
 }
 
-Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation) const {
+Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation) const {
 	ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
 	Track *t = tracks[p_track];
 	ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
@@ -1328,6 +1354,16 @@ Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float
 	return OK;
 }
 
+float Animation::blend_shape_track_interpolate(int p_track, double p_time) const {
+	float ret = 0;
+	ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
+	bool err = try_blend_shape_track_interpolate(p_track, p_time, &ret);
+	ERR_FAIL_COND_V_MSG(err, ret, "Blend Shape Track: '" + tracks[p_track]->path + "' is unavailable.");
+	return ret;
+}
+
+////
+
 void Animation::track_remove_key_at_time(int p_track, double p_time) {
 	int idx = track_find_key(p_track, p_time, FIND_MODE_APPROX);
 	ERR_FAIL_COND(idx < 0);
@@ -3796,6 +3832,11 @@ void Animation::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key);
 	ClassDB::bind_method(D_METHOD("blend_shape_track_insert_key", "track_idx", "time", "amount"), &Animation::blend_shape_track_insert_key);
 
+	ClassDB::bind_method(D_METHOD("position_track_interpolate", "track_idx", "time_sec"), &Animation::position_track_interpolate);
+	ClassDB::bind_method(D_METHOD("rotation_track_interpolate", "track_idx", "time_sec"), &Animation::rotation_track_interpolate);
+	ClassDB::bind_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::scale_track_interpolate);
+	ClassDB::bind_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::blend_shape_track_interpolate);
+
 	ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
 	ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
 	ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time);
@@ -4551,7 +4592,7 @@ Vector3i Animation::_compress_key(uint32_t p_track, const AABB &p_bounds, int32_
 			if (p_key >= 0) {
 				position_track_get_key(p_track, p_key, &pos);
 			} else {
-				position_track_interpolate(p_track, p_time, &pos);
+				try_position_track_interpolate(p_track, p_time, &pos);
 			}
 			pos = (pos - p_bounds.position) / p_bounds.size;
 			for (int j = 0; j < 3; j++) {
@@ -4563,7 +4604,7 @@ Vector3i Animation::_compress_key(uint32_t p_track, const AABB &p_bounds, int32_
 			if (p_key >= 0) {
 				rotation_track_get_key(p_track, p_key, &rot);
 			} else {
-				rotation_track_interpolate(p_track, p_time, &rot);
+				try_rotation_track_interpolate(p_track, p_time, &rot);
 			}
 			Vector3 axis = rot.get_axis();
 			float angle = rot.get_angle();
@@ -4580,7 +4621,7 @@ Vector3i Animation::_compress_key(uint32_t p_track, const AABB &p_bounds, int32_
 			if (p_key >= 0) {
 				scale_track_get_key(p_track, p_key, &scale);
 			} else {
-				scale_track_interpolate(p_track, p_time, &scale);
+				try_scale_track_interpolate(p_track, p_time, &scale);
 			}
 			scale = (scale - p_bounds.position) / p_bounds.size;
 			for (int j = 0; j < 3; j++) {
@@ -4592,7 +4633,7 @@ Vector3i Animation::_compress_key(uint32_t p_track, const AABB &p_bounds, int32_
 			if (p_key >= 0) {
 				blend_shape_track_get_key(p_track, p_key, &blend);
 			} else {
-				blend_shape_track_interpolate(p_track, p_time, &blend);
+				try_blend_shape_track_interpolate(p_track, p_time, &blend);
 			}
 
 			blend = (blend / float(Compression::BLEND_SHAPE_RANGE)) * 0.5 + 0.5;

+ 8 - 4
scene/resources/animation.h

@@ -410,19 +410,23 @@ public:
 
 	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;
+	Error try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+	Vector3 position_track_interpolate(int p_track, double p_time) 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;
+	Error try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const;
+	Quaternion rotation_track_interpolate(int p_track, double p_time) 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;
+	Error try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+	Vector3 scale_track_interpolate(int p_track, double p_time) const;
 
 	int blend_shape_track_insert_key(int p_track, double p_time, float p_blend);
 	Error blend_shape_track_get_key(int p_track, int p_key, float *r_blend) const;
-	Error blend_shape_track_interpolate(int p_track, double p_time, float *r_blend) const;
+	Error try_blend_shape_track_interpolate(int p_track, double p_time, float *r_blend) const;
+	float blend_shape_track_interpolate(int p_track, double p_time) const;
 
 	void track_set_interpolation_type(int p_track, InterpolationType p_interp);
 	InterpolationType track_get_interpolation_type(int p_track) const;

+ 44 - 44
tests/scene/test_animation.h

@@ -82,11 +82,11 @@ TEST_CASE("[Animation] Create value track") {
 	CHECK(animation->track_get_key_transition(1, 0) == doctest::Approx(real_t(-1.0)));
 
 	// This is a value track, so the methods below should return errors.
-	CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(0.0));
-	CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	ERR_PRINT_ON;
 }
 
@@ -104,22 +104,22 @@ TEST_CASE("[Animation] Create 3D position track") {
 
 	Vector3 r_interpolation;
 
-	CHECK(animation->position_track_interpolate(0, -0.2, &r_interpolation) == OK);
+	CHECK(animation->try_position_track_interpolate(0, -0.2, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(0, 1, 2)));
 
-	CHECK(animation->position_track_interpolate(0, 0.0, &r_interpolation) == OK);
+	CHECK(animation->try_position_track_interpolate(0, 0.0, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(0, 1, 2)));
 
-	CHECK(animation->position_track_interpolate(0, 0.2, &r_interpolation) == OK);
+	CHECK(animation->try_position_track_interpolate(0, 0.2, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(1.4, 2.2, 3.2)));
 
-	CHECK(animation->position_track_interpolate(0, 0.4, &r_interpolation) == OK);
+	CHECK(animation->try_position_track_interpolate(0, 0.4, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(2.8, 3.4, 4.4)));
 
-	CHECK(animation->position_track_interpolate(0, 0.5, &r_interpolation) == OK);
+	CHECK(animation->try_position_track_interpolate(0, 0.5, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5)));
 
-	CHECK(animation->position_track_interpolate(0, 0.6, &r_interpolation) == OK);
+	CHECK(animation->try_position_track_interpolate(0, 0.6, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5)));
 
 	// 3D position tracks always use linear interpolation for performance reasons.
@@ -129,10 +129,10 @@ TEST_CASE("[Animation] Create 3D position track") {
 	// This is a 3D position track, so the methods below should return errors.
 	ERR_PRINT_OFF;
 	CHECK(animation->value_track_interpolate(0, 0.0).is_null());
-	CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(0.0));
-	CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	ERR_PRINT_ON;
 }
 
@@ -150,22 +150,22 @@ TEST_CASE("[Animation] Create 3D rotation track") {
 
 	Quaternion r_interpolation;
 
-	CHECK(animation->rotation_track_interpolate(0, -0.2, &r_interpolation) == OK);
+	CHECK(animation->try_rotation_track_interpolate(0, -0.2, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Quaternion(0.403423, 0.259035, 0.73846, 0.47416)));
 
-	CHECK(animation->rotation_track_interpolate(0, 0.0, &r_interpolation) == OK);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.0, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Quaternion(0.403423, 0.259035, 0.73846, 0.47416)));
 
-	CHECK(animation->rotation_track_interpolate(0, 0.2, &r_interpolation) == OK);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.2, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Quaternion(0.336182, 0.30704, 0.751515, 0.477425)));
 
-	CHECK(animation->rotation_track_interpolate(0, 0.4, &r_interpolation) == OK);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.4, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Quaternion(0.266585, 0.352893, 0.759303, 0.477344)));
 
-	CHECK(animation->rotation_track_interpolate(0, 0.5, &r_interpolation) == OK);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.5, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Quaternion(0.231055, 0.374912, 0.761204, 0.476048)));
 
-	CHECK(animation->rotation_track_interpolate(0, 0.6, &r_interpolation) == OK);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.6, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Quaternion(0.231055, 0.374912, 0.761204, 0.476048)));
 
 	// 3D rotation tracks always use linear interpolation for performance reasons.
@@ -175,10 +175,10 @@ TEST_CASE("[Animation] Create 3D rotation track") {
 	// This is a 3D rotation track, so the methods below should return errors.
 	ERR_PRINT_OFF;
 	CHECK(animation->value_track_interpolate(0, 0.0).is_null());
-	CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(real_t(0.0)));
-	CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	ERR_PRINT_ON;
 }
 
@@ -196,22 +196,22 @@ TEST_CASE("[Animation] Create 3D scale track") {
 
 	Vector3 r_interpolation;
 
-	CHECK(animation->scale_track_interpolate(0, -0.2, &r_interpolation) == OK);
+	CHECK(animation->try_scale_track_interpolate(0, -0.2, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(0, 1, 2)));
 
-	CHECK(animation->scale_track_interpolate(0, 0.0, &r_interpolation) == OK);
+	CHECK(animation->try_scale_track_interpolate(0, 0.0, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(0, 1, 2)));
 
-	CHECK(animation->scale_track_interpolate(0, 0.2, &r_interpolation) == OK);
+	CHECK(animation->try_scale_track_interpolate(0, 0.2, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(1.4, 2.2, 3.2)));
 
-	CHECK(animation->scale_track_interpolate(0, 0.4, &r_interpolation) == OK);
+	CHECK(animation->try_scale_track_interpolate(0, 0.4, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(2.8, 3.4, 4.4)));
 
-	CHECK(animation->scale_track_interpolate(0, 0.5, &r_interpolation) == OK);
+	CHECK(animation->try_scale_track_interpolate(0, 0.5, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5)));
 
-	CHECK(animation->scale_track_interpolate(0, 0.6, &r_interpolation) == OK);
+	CHECK(animation->try_scale_track_interpolate(0, 0.6, &r_interpolation) == OK);
 	CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5)));
 
 	// 3D scale tracks always use linear interpolation for performance reasons.
@@ -221,10 +221,10 @@ TEST_CASE("[Animation] Create 3D scale track") {
 	// This is a 3D scale track, so the methods below should return errors.
 	ERR_PRINT_OFF;
 	CHECK(animation->value_track_interpolate(0, 0.0).is_null());
-	CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(0.0));
-	CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	ERR_PRINT_ON;
 }
 
@@ -247,22 +247,22 @@ TEST_CASE("[Animation] Create blend shape track") {
 	CHECK(animation->blend_shape_track_get_key(0, 1, &r_blend) == OK);
 	CHECK(r_blend == doctest::Approx(1.0f));
 
-	CHECK(animation->blend_shape_track_interpolate(0, -0.2, &r_blend) == OK);
+	CHECK(animation->try_blend_shape_track_interpolate(0, -0.2, &r_blend) == OK);
 	CHECK(r_blend == doctest::Approx(-1.0f));
 
-	CHECK(animation->blend_shape_track_interpolate(0, 0.0, &r_blend) == OK);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.0, &r_blend) == OK);
 	CHECK(r_blend == doctest::Approx(-1.0f));
 
-	CHECK(animation->blend_shape_track_interpolate(0, 0.2, &r_blend) == OK);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.2, &r_blend) == OK);
 	CHECK(r_blend == doctest::Approx(-0.2f));
 
-	CHECK(animation->blend_shape_track_interpolate(0, 0.4, &r_blend) == OK);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.4, &r_blend) == OK);
 	CHECK(r_blend == doctest::Approx(0.6f));
 
-	CHECK(animation->blend_shape_track_interpolate(0, 0.5, &r_blend) == OK);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.5, &r_blend) == OK);
 	CHECK(r_blend == doctest::Approx(1.0f));
 
-	CHECK(animation->blend_shape_track_interpolate(0, 0.6, &r_blend) == OK);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.6, &r_blend) == OK);
 	CHECK(r_blend == doctest::Approx(1.0f));
 
 	// Blend shape tracks always use linear interpolation for performance reasons.
@@ -272,9 +272,9 @@ TEST_CASE("[Animation] Create blend shape track") {
 	// This is a blend shape track, so the methods below should return errors.
 	ERR_PRINT_OFF;
 	CHECK(animation->value_track_interpolate(0, 0.0).is_null());
-	CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(0.0));
 	ERR_PRINT_ON;
 }
@@ -302,10 +302,10 @@ TEST_CASE("[Animation] Create Bezier track") {
 	// This is a bezier track, so the methods below should return errors.
 	ERR_PRINT_OFF;
 	CHECK(animation->value_track_interpolate(0, 0.0).is_null());
-	CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
-	CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+	CHECK(animation->try_blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
 	ERR_PRINT_ON;
 }