Преглед на файлове

Remove Object pointer/solve types conflict in AnimationTrackCache

Co-authored-by: Rémi Verschelde <[email protected]>
Silc Lizard (Tokage) Renew преди 1 година
родител
ревизия
a51958a2a0

+ 3 - 3
doc/classes/AnimationMixer.xml

@@ -15,10 +15,10 @@
 			<param index="0" name="animation" type="Animation" />
 			<param index="1" name="track" type="int" />
 			<param index="2" name="value" type="Variant" />
-			<param index="3" name="object" type="Object" />
-			<param index="4" name="object_idx" type="int" />
+			<param index="3" name="object_id" type="int" />
+			<param index="4" name="object_sub_idx" type="int" />
 			<description>
-				A virtual function for processing after key getting during playback.
+				A virtual function for processing after getting a key during playback.
 			</description>
 		</method>
 		<method name="add_animation_library">

+ 1 - 0
editor/plugins/animation_blend_tree_editor_plugin.cpp

@@ -41,6 +41,7 @@
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
 #include "editor/gui/editor_file_dialog.h"
+#include "scene/3d/skeleton_3d.h"
 #include "scene/animation/animation_player.h"
 #include "scene/gui/check_box.h"
 #include "scene/gui/menu_button.h"

+ 1 - 0
editor/plugins/root_motion_editor_plugin.cpp

@@ -32,6 +32,7 @@
 
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
+#include "scene/3d/skeleton_3d.h"
 #include "scene/animation/animation_mixer.h"
 #include "scene/gui/button.h"
 #include "scene/gui/dialogs.h"

+ 7 - 0
misc/extension_api_validation/4.2-stable.expected

@@ -21,3 +21,10 @@ GH-85393
 Validate extension JSON: Error: Field 'classes/PhysicsShapeQueryParameters3D/properties/motion': type changed value in new API, from "Vector2" to "Vector3".
 
 The type was registered wrongly, this was a bug.
+
+
+GH-86687
+--------
+Validate extension JSON: Error: Field 'classes/AnimationMixer/methods/_post_process_key_value/arguments/3': type changed value in new API, from "Object" to "int".
+
+Exposing the pointer was dangerous and it must be changed to avoid crash. Compatibility methods registered.

+ 2 - 0
modules/gltf/extensions/gltf_document_extension.h

@@ -33,6 +33,8 @@
 
 #include "../gltf_state.h"
 
+#include "scene/3d/node_3d.h"
+
 class GLTFDocumentExtension : public Resource {
 	GDCLASS(GLTFDocumentExtension, Resource);
 

+ 1 - 0
modules/gltf/structures/gltf_skin.h

@@ -34,6 +34,7 @@
 #include "../gltf_defines.h"
 
 #include "core/io/resource.h"
+#include "scene/resources/skin.h"
 
 template <typename T>
 class TypedArray;

+ 44 - 0
scene/animation/animation_mixer.compat.inc

@@ -0,0 +1,44 @@
+/**************************************************************************/
+/*  animation_mixer.compat.inc                                            */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+Variant AnimationMixer::_post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx) {
+	if (!p_object) {
+		return Variant();
+	}
+	return _post_process_key_value(p_anim, p_track, p_value, p_object->get_instance_id(), p_object_idx);
+}
+
+void AnimationMixer::_bind_compatibility_methods() {
+	ClassDB::bind_compatibility_method(D_METHOD("_post_process_key_value_bind_compat", "animation", "track", "value", "object", "object_idx"), &AnimationMixer::_post_process_key_value_bind_compat_86687);
+}
+
+#endif // DISABLE_DEPRECATED

+ 203 - 208
scene/animation/animation_mixer.cpp

@@ -29,8 +29,12 @@
 /**************************************************************************/
 
 #include "animation_mixer.h"
+#include "animation_mixer.compat.inc"
 
 #include "core/config/engine.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
 #include "scene/animation/animation_player.h"
 #include "scene/resources/animation.h"
 #include "scene/scene_string_names.h"
@@ -543,7 +547,7 @@ void AnimationMixer::_clear_caches() {
 	_init_root_motion_cache();
 	_clear_audio_streams();
 	_clear_playing_caches();
-	for (KeyValue<NodePath, TrackCache *> &K : track_cache) {
+	for (KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
 		memdelete(K.value);
 	}
 	track_cache.clear();
@@ -562,8 +566,9 @@ void AnimationMixer::_clear_audio_streams() {
 
 void AnimationMixer::_clear_playing_caches() {
 	for (const TrackCache *E : playing_caches) {
-		if (ObjectDB::get_instance(E->object_id)) {
-			E->object->call(SNAME("stop"), true);
+		Object *t_obj = ObjectDB::get_instance(E->object_id);
+		if (t_obj) {
+			t_obj->call(SNAME("stop"), true);
 		}
 	}
 	playing_caches.clear();
@@ -606,23 +611,20 @@ bool AnimationMixer::_update_caches() {
 		Ref<Animation> anim = get_animation(E);
 		for (int i = 0; i < anim->get_track_count(); i++) {
 			NodePath path = anim->track_get_path(i);
-			Animation::TrackType track_type = anim->track_get_type(i);
-
-			Animation::TrackType track_cache_type = track_type;
-			if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) {
-				track_cache_type = Animation::TYPE_POSITION_3D; // Reference them as position3D tracks, even if they modify rotation or scale.
-			}
+			Animation::TypeHash thash = anim->track_get_type_hash(i);
+			Animation::TrackType track_src_type = anim->track_get_type(i);
+			Animation::TrackType track_cache_type = Animation::get_cache_type(track_src_type);
 
 			TrackCache *track = nullptr;
-			if (track_cache.has(path)) {
-				track = track_cache.get(path);
+			if (track_cache.has(thash)) {
+				track = track_cache.get(thash);
 			}
 
 			// If not valid, delete track.
 			if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
 				playing_caches.erase(track);
 				memdelete(track);
-				track_cache.erase(path);
+				track_cache.erase(thash);
 				track = nullptr;
 			}
 
@@ -636,7 +638,8 @@ bool AnimationMixer::_update_caches() {
 					continue;
 				}
 
-				switch (track_type) {
+				switch (track_src_type) {
+					case Animation::TYPE_BEZIER:
 					case Animation::TYPE_VALUE: {
 						// 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.
@@ -645,16 +648,20 @@ bool AnimationMixer::_update_caches() {
 						TrackCacheValue *track_value = memnew(TrackCacheValue);
 
 						if (resource.is_valid()) {
-							track_value->object = resource.ptr();
+							track_value->object_id = resource->get_instance_id();
 						} else {
-							track_value->object = child;
+							track_value->object_id = child->get_instance_id();
 						}
 
-						track_value->is_continuous = anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
-						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 (track_src_type == Animation::TYPE_VALUE) {
+							track_value->is_continuous = anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
+							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;
+						} else {
+							track_value->is_continuous = true;
+							track_value->is_using_angle = false;
+						}
 
 						track_value->subpath = leftover_path;
-						track_value->object_id = track_value->object->get_instance_id();
 
 						track = track_value;
 
@@ -663,9 +670,9 @@ bool AnimationMixer::_update_caches() {
 
 						// If there is a Reset Animation, it takes precedence by overwriting.
 						if (has_reset_anim) {
-							int rt = reset_anim->find_track(path, track_type);
+							int rt = reset_anim->find_track(path, track_src_type);
 							if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
-								track_value->init_value = reset_anim->track_get_key_value(rt, 0);
+								track_value->init_value = track_src_type == Animation::TYPE_VALUE ? reset_anim->track_get_key_value(rt, 0) : (reset_anim->track_get_key_value(rt, 0).operator Array())[0];
 							}
 						}
 
@@ -684,14 +691,12 @@ bool AnimationMixer::_update_caches() {
 						TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
 						track_xform->type = Animation::TYPE_POSITION_3D;
 
-						track_xform->node_3d = node_3d;
-						track_xform->skeleton = nullptr;
 						track_xform->bone_idx = -1;
 
 						bool has_rest = false;
-						if (path.get_subname_count() == 1 && Object::cast_to<Skeleton3D>(node_3d)) {
-							Skeleton3D *sk = Object::cast_to<Skeleton3D>(node_3d);
-							track_xform->skeleton = sk;
+						Skeleton3D *sk = Object::cast_to<Skeleton3D>(node_3d);
+						if (sk && path.get_subname_count() == 1) {
+							track_xform->skeleton_id = sk->get_instance_id();
 							int bone_idx = sk->find_bone(path.get_subname(0));
 							if (bone_idx != -1) {
 								has_rest = true;
@@ -703,12 +708,11 @@ bool AnimationMixer::_update_caches() {
 							}
 						}
 
-						track_xform->object = node_3d;
-						track_xform->object_id = track_xform->object->get_instance_id();
+						track_xform->object_id = node_3d->get_instance_id();
 
 						track = track_xform;
 
-						switch (track_type) {
+						switch (track_src_type) {
 							case Animation::TYPE_POSITION_3D: {
 								track_xform->loc_used = true;
 							} break;
@@ -724,9 +728,9 @@ bool AnimationMixer::_update_caches() {
 
 						// For non Skeleton3D bone animation.
 						if (has_reset_anim && !has_rest) {
-							int rt = reset_anim->find_track(path, track_type);
+							int rt = reset_anim->find_track(path, track_src_type);
 							if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
-								switch (track_type) {
+								switch (track_src_type) {
 									case Animation::TYPE_POSITION_3D: {
 										track_xform->init_loc = reset_anim->track_get_key_value(rt, 0);
 									} break;
@@ -765,15 +769,12 @@ bool AnimationMixer::_update_caches() {
 
 						TrackCacheBlendShape *track_bshape = memnew(TrackCacheBlendShape);
 
-						track_bshape->mesh_3d = mesh_3d;
 						track_bshape->shape_index = blend_shape_idx;
-
-						track_bshape->object = mesh_3d;
 						track_bshape->object_id = mesh_3d->get_instance_id();
 						track = track_bshape;
 
 						if (has_reset_anim) {
-							int rt = reset_anim->find_track(path, track_type);
+							int rt = reset_anim->find_track(path, track_src_type);
 							if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
 								track_bshape->init_value = reset_anim->track_get_key_value(rt, 0);
 							}
@@ -784,43 +785,18 @@ bool AnimationMixer::_update_caches() {
 						TrackCacheMethod *track_method = memnew(TrackCacheMethod);
 
 						if (resource.is_valid()) {
-							track_method->object = resource.ptr();
+							track_method->object_id = resource->get_instance_id();
 						} else {
-							track_method->object = child;
+							track_method->object_id = child->get_instance_id();
 						}
 
-						track_method->object_id = track_method->object->get_instance_id();
-
 						track = track_method;
 
-					} break;
-					case Animation::TYPE_BEZIER: {
-						TrackCacheBezier *track_bezier = memnew(TrackCacheBezier);
-
-						if (resource.is_valid()) {
-							track_bezier->object = resource.ptr();
-						} else {
-							track_bezier->object = child;
-						}
-
-						track_bezier->subpath = leftover_path;
-						track_bezier->object_id = track_bezier->object->get_instance_id();
-
-						track = track_bezier;
-
-						if (has_reset_anim) {
-							int rt = reset_anim->find_track(path, track_type);
-							if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
-								track_bezier->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0];
-							}
-						}
-
 					} break;
 					case Animation::TYPE_AUDIO: {
 						TrackCacheAudio *track_audio = memnew(TrackCacheAudio);
 
-						track_audio->object = child;
-						track_audio->object_id = track_audio->object->get_instance_id();
+						track_audio->object_id = child->get_instance_id();
 						track_audio->audio_stream.instantiate();
 						track_audio->audio_stream->set_polyphony(audio_max_polyphony);
 
@@ -830,8 +806,7 @@ bool AnimationMixer::_update_caches() {
 					case Animation::TYPE_ANIMATION: {
 						TrackCacheAnimation *track_animation = memnew(TrackCacheAnimation);
 
-						track_animation->object = child;
-						track_animation->object_id = track_animation->object->get_instance_id();
+						track_animation->object_id = child->get_instance_id();
 
 						track = track_animation;
 
@@ -841,8 +816,8 @@ bool AnimationMixer::_update_caches() {
 						continue;
 					}
 				}
-
-				track_cache[path] = track;
+				track->path = path;
+				track_cache[thash] = track;
 			} else if (track_cache_type == Animation::TYPE_POSITION_3D) {
 				TrackCacheTransform *track_xform = static_cast<TrackCacheTransform *>(track);
 				if (track->setup_pass != setup_pass) {
@@ -850,7 +825,7 @@ bool AnimationMixer::_update_caches() {
 					track_xform->rot_used = false;
 					track_xform->scale_used = false;
 				}
-				switch (track_type) {
+				switch (track_src_type) {
 					case Animation::TYPE_POSITION_3D: {
 						track_xform->loc_used = true;
 					} break;
@@ -868,8 +843,13 @@ bool AnimationMixer::_update_caches() {
 				TrackCacheValue *track_value = static_cast<TrackCacheValue *>(track);
 				bool was_continuous = track_value->is_continuous;
 				bool was_using_angle = track_value->is_using_angle;
-				track_value->is_continuous |= anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
-				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 (track_src_type == Animation::TYPE_VALUE) {
+					track_value->is_continuous |= anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
+					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;
+				} else {
+					track_value->is_continuous |= true;
+				}
 
 				// TODO: Currently, misc type cannot be blended.
 				// In the future, it should have a separate blend weight, just as bool is converted to 0 and 1.
@@ -898,27 +878,26 @@ bool AnimationMixer::_update_caches() {
 		}
 	}
 
-	List<NodePath> to_delete;
+	List<Animation::TypeHash> to_delete;
 
-	for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
-		TrackCache *tc = track_cache[K.key];
-		if (tc->setup_pass != setup_pass) {
+	for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
+		if (K.value->setup_pass != setup_pass) {
 			to_delete.push_back(K.key);
 		}
 	}
 
 	while (to_delete.front()) {
-		NodePath np = to_delete.front()->get();
-		memdelete(track_cache[np]);
-		track_cache.erase(np);
+		Animation::TypeHash thash = to_delete.front()->get();
+		memdelete(track_cache[thash]);
+		track_cache.erase(thash);
 		to_delete.pop_front();
 	}
 
 	track_map.clear();
 
 	int idx = 0;
-	for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
-		track_map[K.key] = idx;
+	for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
+		track_map[K.value->path] = idx;
 		idx++;
 	}
 
@@ -944,19 +923,28 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) {
 	clear_animation_instances();
 }
 
-Variant AnimationMixer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) {
+Variant AnimationMixer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) {
 	Variant res;
-	if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, const_cast<Object *>(p_object), p_object_idx, res)) {
+	if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, p_object_id, p_object_sub_idx, res)) {
 		return res;
 	}
-	return _post_process_key_value(p_anim, p_track, p_value, p_object, p_object_idx);
+	return _post_process_key_value(p_anim, p_track, p_value, p_object_id, p_object_sub_idx);
 }
 
-Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) {
+Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) {
 #ifndef _3D_DISABLED
-	if (p_object_idx >= 0 && p_anim->track_get_type(p_track) == Animation::TYPE_POSITION_3D) {
-		const Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_object);
-		return Vector3(p_value) * skel->get_motion_scale();
+	switch (p_anim->track_get_type(p_track)) {
+		case Animation::TYPE_POSITION_3D: {
+			if (p_object_sub_idx >= 0) {
+				Skeleton3D *skel = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(p_object_id));
+				if (skel) {
+					return Vector3(p_value) * skel->get_motion_scale();
+				}
+			}
+			return p_value;
+		} break;
+		default: {
+		} break;
 	}
 #endif // _3D_DISABLED
 	return p_value;
@@ -978,7 +966,7 @@ void AnimationMixer::_blend_init() {
 	}
 
 	// Init all value/transform/blend/bezier tracks that track_cache has.
-	for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+	for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
 		TrackCache *track = K.value;
 
 		track->total_weight = 0.0;
@@ -1004,10 +992,6 @@ void AnimationMixer::_blend_init() {
 				t->value = Animation::cast_to_blendwise(t->init_value);
 				t->element_size = t->init_value.is_string() ? (real_t)(t->init_value.operator String()).length() : 0;
 			} break;
-			case Animation::TYPE_BEZIER: {
-				TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
-				t->value = t->init_value;
-			} break;
 			case Animation::TYPE_AUDIO: {
 				TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
 				for (KeyValue<ObjectID, PlayingAudioTrackInfo> &L : t->playing_streams) {
@@ -1039,12 +1023,12 @@ void AnimationMixer::_blend_calc_total_weight() {
 			if (!a->track_is_enabled(i)) {
 				continue;
 			}
-			NodePath path = a->track_get_path(i);
-			if (!track_cache.has(path)) {
+			Animation::TypeHash thash = a->track_get_type_hash(i);
+			if (!track_cache.has(thash)) {
 				continue; // No path, but avoid error spamming.
 			}
-			TrackCache *track = track_cache[path];
-			int blend_idx = track_map[path];
+			TrackCache *track = track_cache[thash];
+			int blend_idx = track_map[track->path];
 			if (processed_indices.has(blend_idx)) {
 				continue; // There is the case different track type with same path... Is there more faster iterating way than has()?
 			}
@@ -1079,13 +1063,13 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 			if (!a->track_is_enabled(i)) {
 				continue;
 			}
-			NodePath path = a->track_get_path(i);
-			if (!track_cache.has(path)) {
+			Animation::TypeHash thash = a->track_get_type_hash(i);
+			if (!track_cache.has(thash)) {
 				continue; // No path, but avoid error spamming.
 			}
-			TrackCache *track = track_cache[path];
-			ERR_CONTINUE(!track_map.has(path));
-			int blend_idx = track_map[path];
+			TrackCache *track = track_cache[thash];
+			ERR_CONTINUE(!track_map.has(track->path));
+			int blend_idx = track_map[track->path];
 			ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count);
 			real_t blend = blend_idx < track_weights.size() ? track_weights[blend_idx] * weight : weight;
 			if (!deterministic) {
@@ -1097,11 +1081,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 				blend = blend / track->total_weight;
 			}
 			Animation::TrackType ttype = a->track_get_type(i);
-			if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) {
-				// Broken animation, but avoid error spamming.
-				continue;
-			}
-			track->root_motion = root_motion_track == path;
+			track->root_motion = root_motion_track == a->track_get_path(i);
 			switch (ttype) {
 				case Animation::TYPE_POSITION_3D: {
 #ifndef _3D_DISABLED
@@ -1151,9 +1131,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 								if (err != OK) {
 									continue;
 								}
-								loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
+								loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
 								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);
+								loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
 								root_motion_cache.loc += (loc[1] - loc[0]) * blend;
 								prev_time = 0;
 							}
@@ -1163,9 +1143,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 								if (err != OK) {
 									continue;
 								}
-								loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
+								loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
 								a->try_position_track_interpolate(i, 0, &loc[1]);
-								loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
+								loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
 								root_motion_cache.loc += (loc[1] - loc[0]) * blend;
 								prev_time = (double)a->get_length();
 							}
@@ -1174,9 +1154,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						if (err != OK) {
 							continue;
 						}
-						loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
+						loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
 						a->try_position_track_interpolate(i, time, &loc[1]);
-						loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
+						loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
 						root_motion_cache.loc += (loc[1] - loc[0]) * blend;
 						prev_time = !backward ? 0 : (double)a->get_length();
 					}
@@ -1186,7 +1166,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						if (err != OK) {
 							continue;
 						}
-						loc = post_process_key_value(a, i, loc, t->object, t->bone_idx);
+						loc = post_process_key_value(a, i, loc, t->object_id, t->bone_idx);
 						t->loc += (loc - t->init_loc) * blend;
 					}
 #endif // _3D_DISABLED
@@ -1239,9 +1219,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 								if (err != OK) {
 									continue;
 								}
-								rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
+								rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
 								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);
+								rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
 								root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
 								prev_time = 0;
 							}
@@ -1251,7 +1231,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 								if (err != OK) {
 									continue;
 								}
-								rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
+								rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
 								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();
@@ -1261,9 +1241,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						if (err != OK) {
 							continue;
 						}
-						rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
+						rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
 						a->try_rotation_track_interpolate(i, time, &rot[1]);
-						rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
+						rot[1] = post_process_key_value(a, i, rot[1], t->object_id, 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();
 					}
@@ -1273,7 +1253,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						if (err != OK) {
 							continue;
 						}
-						rot = post_process_key_value(a, i, rot, t->object, t->bone_idx);
+						rot = post_process_key_value(a, i, rot, t->object_id, t->bone_idx);
 						t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized();
 					}
 #endif // _3D_DISABLED
@@ -1326,10 +1306,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 								if (err != OK) {
 									continue;
 								}
-								scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
+								scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
 								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);
+								scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
 								prev_time = 0;
 							}
 						} else {
@@ -1338,9 +1318,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 								if (err != OK) {
 									continue;
 								}
-								scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
+								scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
 								a->try_scale_track_interpolate(i, 0, &scale[1]);
-								scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
+								scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
 								root_motion_cache.scale += (scale[1] - scale[0]) * blend;
 								prev_time = (double)a->get_length();
 							}
@@ -1349,9 +1329,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						if (err != OK) {
 							continue;
 						}
-						scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
+						scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
 						a->try_scale_track_interpolate(i, time, &scale[1]);
-						scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
+						scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
 						root_motion_cache.scale += (scale[1] - scale[0]) * blend;
 						prev_time = !backward ? 0 : (double)a->get_length();
 					}
@@ -1361,7 +1341,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						if (err != OK) {
 							continue;
 						}
-						scale = post_process_key_value(a, i, scale, t->object, t->bone_idx);
+						scale = post_process_key_value(a, i, scale, t->object_id, t->bone_idx);
 						t->scale += (scale - t->init_scale) * blend;
 					}
 #endif // _3D_DISABLED
@@ -1378,18 +1358,19 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 					if (err != OK) {
 						continue;
 					}
-					value = post_process_key_value(a, i, value, t->object, t->shape_index);
+					value = post_process_key_value(a, i, value, t->object_id, t->shape_index);
 					t->value += (value - t->init_value) * blend;
 #endif // _3D_DISABLED
 				} break;
+				case Animation::TYPE_BEZIER:
 				case Animation::TYPE_VALUE: {
 					if (Math::is_zero_approx(blend)) {
 						continue; // Nothing to blend.
 					}
 					TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
 					if (t->is_continuous) {
-						Variant value = a->value_track_interpolate(i, time);
-						value = post_process_key_value(a, i, value, t->object);
+						Variant value = ttype == Animation::TYPE_VALUE ? a->value_track_interpolate(i, time) : Variant(a->bezier_track_interpolate(i, time));
+						value = post_process_key_value(a, i, value, t->object_id);
 						if (value == Variant()) {
 							continue;
 						}
@@ -1429,15 +1410,21 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 								continue;
 							}
 							Variant value = a->track_get_key_value(i, idx);
-							value = post_process_key_value(a, i, value, t->object);
-							t->object->set_indexed(t->subpath, value);
+							value = post_process_key_value(a, i, value, t->object_id);
+							Object *t_obj = ObjectDB::get_instance(t->object_id);
+							if (t_obj) {
+								t_obj->set_indexed(t->subpath, value);
+							}
 						} else {
 							List<int> indices;
 							a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
 							for (int &F : indices) {
 								Variant value = a->track_get_key_value(i, F);
-								value = post_process_key_value(a, i, value, t->object);
-								t->object->set_indexed(t->subpath, value);
+								value = post_process_key_value(a, i, value, t->object_id);
+								Object *t_obj = ObjectDB::get_instance(t->object_id);
+								if (t_obj) {
+									t_obj->set_indexed(t->subpath, value);
+								}
 							}
 						}
 					}
@@ -1459,31 +1446,23 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						}
 						StringName method = a->method_track_get_name(i, idx);
 						Vector<Variant> params = a->method_track_get_params(i, idx);
-						_call_object(t->object, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED);
+						_call_object(t->object_id, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED);
 					} else {
 						List<int> indices;
 						a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
 						for (int &F : indices) {
 							StringName method = a->method_track_get_name(i, F);
 							Vector<Variant> params = a->method_track_get_params(i, F);
-							_call_object(t->object, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED);
+							_call_object(t->object_id, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED);
 						}
 					}
 				} break;
-				case Animation::TYPE_BEZIER: {
-					if (Math::is_zero_approx(blend)) {
-						continue; // Nothing to blend.
-					}
-					TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
-					real_t bezier = a->bezier_track_interpolate(i, time);
-					bezier = post_process_key_value(a, i, bezier, t->object);
-					t->value += (bezier - t->init_value) * blend;
-				} break;
 				case Animation::TYPE_AUDIO: {
 					// The end of audio should be observed even if the blend value is 0, build up the information and store to the cache for that.
 					TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
-					Node *asp = Object::cast_to<Node>(t->object);
-					if (!asp) {
+					Object *t_obj = ObjectDB::get_instance(t->object_id);
+					Node *asp = t_obj ? Object::cast_to<Node>(t_obj) : nullptr;
+					if (!t_obj || !asp) {
 						t->playing_streams.clear();
 						continue;
 					}
@@ -1533,22 +1512,22 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						if (seeked) {
 							start_ofs += time - a->track_get_key_time(i, idx);
 						}
-						if (t->object->call(SNAME("get_stream")) != t->audio_stream) {
-							t->object->call(SNAME("set_stream"), t->audio_stream);
+						if (t_obj->call(SNAME("get_stream")) != t->audio_stream) {
+							t_obj->call(SNAME("set_stream"), t->audio_stream);
 							t->audio_stream_playback.unref();
 							if (!playing_audio_stream_players.has(asp)) {
 								playing_audio_stream_players.push_back(asp);
 							}
 						}
-						if (!t->object->call(SNAME("is_playing"))) {
-							t->object->call(SNAME("play"));
+						if (!t_obj->call(SNAME("is_playing"))) {
+							t_obj->call(SNAME("play"));
 						}
-						if (!t->object->call(SNAME("has_stream_playback"))) {
+						if (!t_obj->call(SNAME("has_stream_playback"))) {
 							t->audio_stream_playback.unref();
 							continue;
 						}
 						if (t->audio_stream_playback.is_null()) {
-							t->audio_stream_playback = t->object->call(SNAME("get_stream_playback"));
+							t->audio_stream_playback = t_obj->call(SNAME("get_stream_playback"));
 						}
 						PlayingAudioStreamInfo pasi;
 						pasi.index = t->audio_stream_playback->play_stream(stream, start_ofs);
@@ -1566,7 +1545,11 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 						continue;
 					}
 					TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track);
-					AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object);
+					Object *t_obj = ObjectDB::get_instance(t->object_id);
+					if (!t_obj) {
+						continue;
+					}
+					AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t_obj);
 					if (!player2) {
 						continue;
 					}
@@ -1633,7 +1616,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
 
 void AnimationMixer::_blend_apply() {
 	// Finally, set the tracks.
-	for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+	for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
 		TrackCache *track = K.value;
 		if (!deterministic && Math::is_zero_approx(track->total_weight)) {
 			continue;
@@ -1650,26 +1633,34 @@ void AnimationMixer::_blend_apply() {
 					root_motion_position_accumulator = t->loc;
 					root_motion_rotation_accumulator = t->rot;
 					root_motion_scale_accumulator = t->scale;
-				} else if (t->skeleton && t->bone_idx >= 0) {
+				} else if (t->skeleton_id.is_valid() && t->bone_idx >= 0) {
+					Skeleton3D *t_skeleton = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(t->skeleton_id));
+					if (!t_skeleton) {
+						return;
+					}
 					if (t->loc_used) {
-						t->skeleton->set_bone_pose_position(t->bone_idx, t->loc);
+						t_skeleton->set_bone_pose_position(t->bone_idx, t->loc);
 					}
 					if (t->rot_used) {
-						t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot);
+						t_skeleton->set_bone_pose_rotation(t->bone_idx, t->rot);
 					}
 					if (t->scale_used) {
-						t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale);
+						t_skeleton->set_bone_pose_scale(t->bone_idx, t->scale);
 					}
 
-				} else if (!t->skeleton) {
+				} else if (!t->skeleton_id.is_valid()) {
+					Node3D *t_node_3d = Object::cast_to<Node3D>(ObjectDB::get_instance(t->object_id));
+					if (!t_node_3d) {
+						return;
+					}
 					if (t->loc_used) {
-						t->node_3d->set_position(t->loc);
+						t_node_3d->set_position(t->loc);
 					}
 					if (t->rot_used) {
-						t->node_3d->set_rotation(t->rot.get_euler());
+						t_node_3d->set_rotation(t->rot.get_euler());
 					}
 					if (t->scale_used) {
-						t->node_3d->set_scale(t->scale);
+						t_node_3d->set_scale(t->scale);
 					}
 				}
 #endif // _3D_DISABLED
@@ -1678,8 +1669,9 @@ void AnimationMixer::_blend_apply() {
 #ifndef _3D_DISABLED
 				TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);
 
-				if (t->mesh_3d) {
-					t->mesh_3d->set_blend_shape_value(t->shape_index, t->value);
+				MeshInstance3D *t_mesh_3d = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(t->object_id));
+				if (t_mesh_3d) {
+					t_mesh_3d->set_blend_shape_value(t->shape_index, t->value);
 				}
 #endif // _3D_DISABLED
 			} break;
@@ -1703,18 +1695,11 @@ void AnimationMixer::_blend_apply() {
 					}
 				}
 
-				// t->object isn't safe here, get instance from id (GH-85365).
-				Object *obj = ObjectDB::get_instance(t->object_id);
-				if (obj) {
-					obj->set_indexed(t->subpath, Animation::cast_from_blendwise(t->value, t->init_value.get_type()));
+				Object *t_obj = ObjectDB::get_instance(t->object_id);
+				if (t_obj) {
+					t_obj->set_indexed(t->subpath, Animation::cast_from_blendwise(t->value, t->init_value.get_type()));
 				}
 
-			} break;
-			case Animation::TYPE_BEZIER: {
-				TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
-
-				t->object->set_indexed(t->subpath, t->value);
-
 			} break;
 			case Animation::TYPE_AUDIO: {
 				TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
@@ -1780,7 +1765,7 @@ void AnimationMixer::_blend_apply() {
 	}
 }
 
-void AnimationMixer::_call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) {
+void AnimationMixer::_call_object(ObjectID p_object_id, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) {
 	// Separate function to use alloca() more efficiently
 	const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * p_params.size());
 	const Variant *args = p_params.ptr();
@@ -1788,11 +1773,15 @@ void AnimationMixer::_call_object(Object *p_object, const StringName &p_method,
 	for (uint32_t i = 0; i < argcount; i++) {
 		argptrs[i] = &args[i];
 	}
+	Object *t_obj = ObjectDB::get_instance(p_object_id);
+	if (!t_obj) {
+		return;
+	}
 	if (p_deferred) {
-		MessageQueue::get_singleton()->push_callp(p_object, p_method, argptrs, argcount);
+		MessageQueue::get_singleton()->push_callp(t_obj, p_method, argptrs, argcount);
 	} else {
 		Callable::CallError ce;
-		p_object->callp(p_method, argptrs, argcount, ce);
+		t_obj->callp(p_method, argptrs, argcount, ce);
 	}
 }
 
@@ -1872,7 +1861,7 @@ bool AnimationMixer::can_apply_reset() const {
 }
 
 void AnimationMixer::_build_backup_track_cache() {
-	for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+	for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
 		TrackCache *track = K.value;
 		track->total_weight = 1.0;
 		switch (track->type) {
@@ -1881,25 +1870,33 @@ void AnimationMixer::_build_backup_track_cache() {
 				TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
 				if (t->root_motion) {
 					// Do nothing.
-				} else if (t->skeleton && t->bone_idx >= 0) {
+				} else if (t->skeleton_id.is_valid() && t->bone_idx >= 0) {
+					Skeleton3D *t_skeleton = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(t->skeleton_id));
+					if (!t_skeleton) {
+						return;
+					}
 					if (t->loc_used) {
-						t->loc = t->skeleton->get_bone_pose_position(t->bone_idx);
+						t->loc = t_skeleton->get_bone_pose_position(t->bone_idx);
 					}
 					if (t->rot_used) {
-						t->rot = t->skeleton->get_bone_pose_rotation(t->bone_idx);
+						t->rot = t_skeleton->get_bone_pose_rotation(t->bone_idx);
 					}
 					if (t->scale_used) {
-						t->scale = t->skeleton->get_bone_pose_scale(t->bone_idx);
+						t->scale = t_skeleton->get_bone_pose_scale(t->bone_idx);
+					}
+				} else if (!t->skeleton_id.is_valid()) {
+					Node3D *t_node_3d = Object::cast_to<Node3D>(ObjectDB::get_instance(t->object_id));
+					if (!t_node_3d) {
+						return;
 					}
-				} else if (!t->skeleton) {
 					if (t->loc_used) {
-						t->loc = t->node_3d->get_position();
+						t->loc = t_node_3d->get_position();
 					}
 					if (t->rot_used) {
-						t->rot = t->node_3d->get_quaternion();
+						t->rot = t_node_3d->get_quaternion();
 					}
 					if (t->scale_used) {
-						t->scale = t->node_3d->get_scale();
+						t->scale = t_node_3d->get_scale();
 					}
 				}
 #endif // _3D_DISABLED
@@ -1907,25 +1904,28 @@ void AnimationMixer::_build_backup_track_cache() {
 			case Animation::TYPE_BLEND_SHAPE: {
 #ifndef _3D_DISABLED
 				TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);
-				if (t->mesh_3d) {
-					t->value = t->mesh_3d->get_blend_shape_value(t->shape_index);
+				MeshInstance3D *t_mesh_3d = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(t->object_id));
+				if (t_mesh_3d) {
+					t->value = t_mesh_3d->get_blend_shape_value(t->shape_index);
 				}
 #endif // _3D_DISABLED
 			} break;
 			case Animation::TYPE_VALUE: {
 				TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
-				t->value = t->object->get_indexed(t->subpath);
+				Object *t_obj = ObjectDB::get_instance(t->object_id);
+				if (t_obj) {
+					t->value = t_obj->get_indexed(t->subpath);
+				}
 				t->is_continuous = true;
 			} break;
-			case Animation::TYPE_BEZIER: {
-				TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
-				t->value = t->object->get_indexed(t->subpath);
-			} break;
 			case Animation::TYPE_AUDIO: {
 				TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
-				Node *asp = Object::cast_to<Node>(t->object);
-				if (asp) {
-					t->object->call(SNAME("set_stream"), Ref<AudioStream>());
+				Object *t_obj = ObjectDB::get_instance(t->object_id);
+				if (t_obj) {
+					Node *asp = Object::cast_to<Node>(t_obj);
+					if (asp) {
+						asp->call(SNAME("set_stream"), Ref<AudioStream>());
+					}
 				}
 			} break;
 			default: {
@@ -1982,7 +1982,7 @@ void AnimationMixer::restore(const Ref<AnimatedValuesBackup> &p_backup) {
 	ERR_FAIL_COND(p_backup.is_null());
 	track_cache = p_backup->get_data();
 	_blend_apply();
-	track_cache = HashMap<NodePath, AnimationMixer::TrackCache *>();
+	track_cache = HashMap<Animation::TypeHash, AnimationMixer::TrackCache *>();
 	cache_valid = false;
 }
 
@@ -2113,7 +2113,7 @@ void AnimationMixer::_bind_methods() {
 	/* ---- Blending processor ---- */
 	ClassDB::bind_method(D_METHOD("clear_caches"), &AnimationMixer::clear_caches);
 	ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationMixer::advance);
-	GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object", "object_idx");
+	GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object_id", "object_sub_idx");
 
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic");
@@ -2159,10 +2159,10 @@ AnimationMixer::AnimationMixer() {
 AnimationMixer::~AnimationMixer() {
 }
 
-void AnimatedValuesBackup::set_data(const HashMap<NodePath, AnimationMixer::TrackCache *> &p_data) {
+void AnimatedValuesBackup::set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data) {
 	clear_data();
 
-	for (const KeyValue<NodePath, AnimationMixer::TrackCache *> &E : p_data) {
+	for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : p_data) {
 		AnimationMixer::TrackCache *track = get_cache_copy(E.value);
 		if (!track) {
 			continue; // Some types of tracks do not get a copy and must be ignored.
@@ -2172,9 +2172,9 @@ void AnimatedValuesBackup::set_data(const HashMap<NodePath, AnimationMixer::Trac
 	}
 }
 
-HashMap<NodePath, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() const {
-	HashMap<NodePath, AnimationMixer::TrackCache *> ret;
-	for (const KeyValue<NodePath, AnimationMixer::TrackCache *> &E : data) {
+HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() const {
+	HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> ret;
+	for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : data) {
 		AnimationMixer::TrackCache *track = get_cache_copy(E.value);
 		ERR_CONTINUE(!track); // Backup shouldn't contain tracks that cannot be copied, this is a mistake.
 
@@ -2184,7 +2184,7 @@ HashMap<NodePath, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data()
 }
 
 void AnimatedValuesBackup::clear_data() {
-	for (KeyValue<NodePath, AnimationMixer::TrackCache *> &K : data) {
+	for (KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &K : data) {
 		memdelete(K.value);
 	}
 	data.clear();
@@ -2192,6 +2192,7 @@ void AnimatedValuesBackup::clear_data() {
 
 AnimationMixer::TrackCache *AnimatedValuesBackup::get_cache_copy(AnimationMixer::TrackCache *p_cache) const {
 	switch (p_cache->type) {
+		case Animation::TYPE_BEZIER:
 		case Animation::TYPE_VALUE: {
 			AnimationMixer::TrackCacheValue *src = static_cast<AnimationMixer::TrackCacheValue *>(p_cache);
 			AnimationMixer::TrackCacheValue *tc = memnew(AnimationMixer::TrackCacheValue(*src));
@@ -2212,12 +2213,6 @@ AnimationMixer::TrackCache *AnimatedValuesBackup::get_cache_copy(AnimationMixer:
 			return tc;
 		}
 
-		case Animation::TYPE_BEZIER: {
-			AnimationMixer::TrackCacheBezier *src = static_cast<AnimationMixer::TrackCacheBezier *>(p_cache);
-			AnimationMixer::TrackCacheBezier *tc = memnew(AnimationMixer::TrackCacheBezier(*src));
-			return tc;
-		}
-
 		case Animation::TYPE_AUDIO: {
 			AnimationMixer::TrackCacheAudio *src = static_cast<AnimationMixer::TrackCacheAudio *>(p_cache);
 			AnimationMixer::TrackCacheAudio *tc = memnew(AnimationMixer::TrackCacheAudio(*src));

+ 18 - 36
scene/animation/animation_mixer.h

@@ -31,9 +31,7 @@
 #ifndef ANIMATION_MIXER_H
 #define ANIMATION_MIXER_H
 
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/3d/skeleton_3d.h"
+#include "scene/main/node.h"
 #include "scene/resources/animation.h"
 #include "scene/resources/animation_library.h"
 #include "scene/resources/audio_stream_polyphonic.h"
@@ -138,7 +136,7 @@ protected:
 		bool root_motion = false;
 		uint64_t setup_pass = 0;
 		Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION;
-		Object *object = nullptr;
+		NodePath path;
 		ObjectID object_id;
 		real_t total_weight = 0.0;
 
@@ -147,7 +145,6 @@ protected:
 				root_motion(p_other.root_motion),
 				setup_pass(p_other.setup_pass),
 				type(p_other.type),
-				object(p_other.object),
 				object_id(p_other.object_id),
 				total_weight(p_other.total_weight) {}
 
@@ -156,8 +153,7 @@ protected:
 
 	struct TrackCacheTransform : public TrackCache {
 #ifndef _3D_DISABLED
-		Node3D *node_3d = nullptr;
-		Skeleton3D *skeleton = nullptr;
+		ObjectID skeleton_id;
 #endif // _3D_DISABLED
 		int bone_idx = -1;
 		bool loc_used = false;
@@ -173,8 +169,7 @@ protected:
 		TrackCacheTransform(const TrackCacheTransform &p_other) :
 				TrackCache(p_other),
 #ifndef _3D_DISABLED
-				node_3d(p_other.node_3d),
-				skeleton(p_other.skeleton),
+				skeleton_id(p_other.skeleton_id),
 #endif
 				bone_idx(p_other.bone_idx),
 				loc_used(p_other.loc_used),
@@ -201,14 +196,12 @@ protected:
 	};
 
 	struct TrackCacheBlendShape : public TrackCache {
-		MeshInstance3D *mesh_3d = nullptr;
 		float init_value = 0;
 		float value = 0;
 		int shape_index = -1;
 
 		TrackCacheBlendShape(const TrackCacheBlendShape &p_other) :
 				TrackCache(p_other),
-				mesh_3d(p_other.mesh_3d),
 				init_value(p_other.init_value),
 				value(p_other.value),
 				shape_index(p_other.shape_index) {}
@@ -247,23 +240,6 @@ protected:
 		~TrackCacheMethod() {}
 	};
 
-	struct TrackCacheBezier : public TrackCache {
-		real_t init_value = 0.0;
-		real_t value = 0.0;
-		Vector<StringName> subpath;
-
-		TrackCacheBezier(const TrackCacheBezier &p_other) :
-				TrackCache(p_other),
-				init_value(p_other.init_value),
-				value(p_other.value),
-				subpath(p_other.subpath) {}
-
-		TrackCacheBezier() {
-			type = Animation::TYPE_BEZIER;
-		}
-		~TrackCacheBezier() {}
-	};
-
 	// Audio stream information for each audio stream placed on the track.
 	struct PlayingAudioStreamInfo {
 		AudioStreamPlaybackPolyphonic::ID index = -1; // ID retrieved from AudioStreamPlaybackPolyphonic.
@@ -309,7 +285,7 @@ protected:
 	};
 
 	RootMotionCache root_motion_cache;
-	HashMap<NodePath, TrackCache *> track_cache;
+	HashMap<Animation::TypeHash, TrackCache *> track_cache;
 	HashSet<TrackCache *> playing_caches;
 	Vector<Node *> playing_audio_stream_players;
 
@@ -352,9 +328,9 @@ protected:
 
 	/* ---- Blending processor ---- */
 	virtual void _process_animation(double p_delta, bool p_update_only = false);
-	virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1);
-	Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1);
-	GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, Object *, int);
+	virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
+	Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
+	GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int);
 
 	void _blend_init();
 	virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map);
@@ -362,7 +338,13 @@ protected:
 	void _blend_process(double p_delta, bool p_update_only = false);
 	void _blend_apply();
 	virtual void _blend_post_process();
-	void _call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred);
+	void _call_object(ObjectID p_object_id, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred);
+
+#ifndef DISABLE_DEPRECATED
+	virtual Variant _post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx = -1);
+
+	static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
 
 public:
 	/* ---- Data lists ---- */
@@ -444,11 +426,11 @@ public:
 class AnimatedValuesBackup : public RefCounted {
 	GDCLASS(AnimatedValuesBackup, RefCounted);
 
-	HashMap<NodePath, AnimationMixer::TrackCache *> data;
+	HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> data;
 
 public:
-	void set_data(const HashMap<NodePath, AnimationMixer::TrackCache *> &p_data);
-	HashMap<NodePath, AnimationMixer::TrackCache *> get_data() const;
+	void set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data);
+	HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> get_data() const;
 	void clear_data();
 
 	AnimationMixer::TrackCache *get_cache_copy(AnimationMixer::TrackCache *p_cache) const;

+ 22 - 0
scene/resources/animation.cpp

@@ -979,6 +979,7 @@ Animation::TrackType Animation::track_get_type(int p_track) const {
 void Animation::track_set_path(int p_track, const NodePath &p_path) {
 	ERR_FAIL_INDEX(p_track, tracks.size());
 	tracks[p_track]->path = p_path;
+	_track_update_hash(p_track);
 	emit_changed();
 }
 
@@ -996,6 +997,27 @@ int Animation::find_track(const NodePath &p_path, const TrackType p_type) const
 	return -1;
 };
 
+Animation::TrackType Animation::get_cache_type(TrackType p_type) {
+	if (p_type == Animation::TYPE_BEZIER) {
+		return Animation::TYPE_VALUE;
+	}
+	if (p_type == Animation::TYPE_ROTATION_3D || p_type == Animation::TYPE_SCALE_3D) {
+		return Animation::TYPE_POSITION_3D; // Reference them as position3D tracks, even if they modify rotation or scale.
+	}
+	return p_type;
+}
+
+void Animation::_track_update_hash(int p_track) {
+	NodePath track_path = tracks[p_track]->path;
+	TrackType track_cache_type = get_cache_type(tracks[p_track]->type);
+	tracks[p_track]->thash = StringName(String(track_path.get_concatenated_names()) + String(track_path.get_concatenated_subnames()) + itos(track_cache_type)).hash();
+}
+
+Animation::TypeHash Animation::track_get_type_hash(int p_track) const {
+	ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
+	return tracks[p_track]->thash;
+}
+
 void Animation::track_set_interpolation_type(int p_track, InterpolationType p_interp) {
 	ERR_FAIL_INDEX(p_track, tracks.size());
 	tracks[p_track]->interpolation = p_interp;

+ 10 - 1
scene/resources/animation.h

@@ -41,6 +41,8 @@ class Animation : public Resource {
 	RES_BASE_EXTENSION("anim");
 
 public:
+	typedef uint32_t TypeHash;
+
 	enum TrackType {
 		TYPE_VALUE, ///< Set a value in a property, can be interpolated.
 		TYPE_POSITION_3D, ///< Position 3D track
@@ -104,7 +106,8 @@ private:
 		TrackType type = TrackType::TYPE_ANIMATION;
 		InterpolationType interpolation = INTERPOLATION_LINEAR;
 		bool loop_wrap = true;
-		NodePath path; // path to something
+		NodePath path; // Path to something.
+		TypeHash thash = 0; // Hash by Path + SubPath + TrackType.
 		bool imported = false;
 		bool enabled = true;
 		Track() {}
@@ -268,6 +271,8 @@ private:
 	real_t step = 0.1;
 	LoopMode loop_mode = LOOP_NONE;
 
+	void _track_update_hash(int p_track);
+
 	/* Animation compression page format (version 1):
 	 *
 	 * Animation uses bitwidth based compression separated into small pages. The intention is that pages fit easily in the cache, so decoding is cache efficient.
@@ -386,6 +391,8 @@ public:
 	NodePath track_get_path(int p_track) const;
 	int find_track(const NodePath &p_path, const TrackType p_type) const;
 
+	TypeHash track_get_type_hash(int p_track) const;
+
 	void track_move_up(int p_track);
 	void track_move_down(int p_track);
 	void track_move_to(int p_track, int p_to_index);
@@ -504,6 +511,8 @@ public:
 	static Variant interpolate_variant(const Variant &a, const Variant &b, float c, bool p_snap_array_element = false);
 	static Variant cubic_interpolate_in_time_variant(const Variant &pre_a, const Variant &a, const Variant &b, const Variant &post_b, float c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t, bool p_snap_array_element = false);
 
+	static TrackType get_cache_type(TrackType p_type);
+
 	Animation();
 	~Animation();
 };