void AnimationPlayer::_generate_node_caches(AnimationData* p_anim) {

	Node *parent = get_node(root);
	
	ERR_FAIL_COND(!parent);

	Animation *a=p_anim->animation.operator->();
	
	p_anim->node_cache.resize( a->get_track_count() );
	
	for (int i=0;i<a->get_track_count();i++) {
	
		p_anim->node_cache[i]=NULL;
		RES resource;
		Node *child = parent->get_node_and_resource(a->track_get_path(i),resource);
		if (!child) {
			ERR_EXPLAIN("On Animation: '"+p_anim->name+"', couldn't resolve track:  '"+String(a->track_get_path(i))+"'");
		}
		ERR_CONTINUE(!child); // couldn't find the child node
		uint32_t id=resource.is_valid()?resource->get_instance_ID():child->get_instance_ID();
		int bone_idx=-1;

		if (a->track_get_path(i).get_property() && child->cast_to<Skeleton>()) {

			bone_idx = child->cast_to<Skeleton>()->find_bone( a->track_get_path(i).get_property() );
			if (bone_idx==-1) {

				continue;
			}
		}
		
		{
			if (!child->is_connected("exit_tree",this,"_node_removed"))
				child->connect("exit_tree",this,"_node_removed",make_binds(child),CONNECT_ONESHOT);
		}

		TrackNodeCacheKey key;
		key.id=id;
		key.bone_idx=bone_idx;

		
		if (node_cache_map.has(key)) {
		
			p_anim->node_cache[i]=&node_cache_map[key];
		} else {
		

			node_cache_map[key]=TrackNodeCache();
			
			p_anim->node_cache[i]=&node_cache_map[key];
			p_anim->node_cache[i]->path=a->track_get_path(i);
			p_anim->node_cache[i]->node=child;
			p_anim->node_cache[i]->resource=resource;
			p_anim->node_cache[i]->node_2d=child->cast_to<Node2D>();
			if (a->track_get_type(i)==Animation::TYPE_TRANSFORM) {
				// special cases and caches for transform tracks
				
				// cache spatial
				p_anim->node_cache[i]->spatial=child->cast_to<Spatial>();
				// cache skeleton
				p_anim->node_cache[i]->skeleton=child->cast_to<Skeleton>();
				if (p_anim->node_cache[i]->skeleton) {

					StringName bone_name=a->track_get_path(i).get_property();
					if (bone_name.operator String()!="") {
					
						p_anim->node_cache[i]->bone_idx=p_anim->node_cache[i]->skeleton->find_bone(bone_name);
						if (p_anim->node_cache[i]->bone_idx<0) {
							// broken track (nonexistent bone)
							p_anim->node_cache[i]->skeleton=NULL;
							p_anim->node_cache[i]->spatial=NULL;
							printf("bone is %ls\n", String(bone_name).c_str());
							ERR_CONTINUE( p_anim->node_cache[i]->bone_idx<0 );
						} else {
						}
					} else {
						// no property, just use spatialnode
						p_anim->node_cache[i]->skeleton=NULL;						
					}
					
				}
			}
		}

		if (a->track_get_type(i)==Animation::TYPE_VALUE) {

			StringName property = a->track_get_path(i).get_property();
			if (!p_anim->node_cache[i]->property_anim.has(property)) {

				TrackNodeCache::PropertyAnim pa;
				pa.prop=property;
				pa.object=resource.is_valid()?(Object*)resource.ptr():(Object*)child;
				pa.special=SP_NONE;
				pa.owner=p_anim->node_cache[i];
				if (false && p_anim->node_cache[i]->node_2d) {

					if (pa.prop==SceneStringNames::get_singleton()->transform_pos)
						pa.special=SP_NODE2D_POS;
					else if (pa.prop==SceneStringNames::get_singleton()->transform_rot)
						pa.special=SP_NODE2D_ROT;
					else if (pa.prop==SceneStringNames::get_singleton()->transform_scale)
						pa.special=SP_NODE2D_SCALE;
				}
				p_anim->node_cache[i]->property_anim[property]=pa;
			}
		}
	}
}