using UnityEngine;
using UnityEngine.Timeline;
using System.Collections.Generic;
using UnityEngine.Playables;
namespace UnityEditor.Formats.Fbx.Exporter
{
///
/// Export data containing extra information required to export
///
internal interface IExportData
{
HashSet Objects { get; }
}
///
/// Export data containing what to export when
/// exporting animation only.
///
internal class AnimationOnlyExportData : IExportData
{
// map from animation clip to GameObject that has Animation/Animator
// component containing clip
public Dictionary animationClips;
// set of all GameObjects to export
public HashSet goExportSet;
public HashSet Objects { get { return goExportSet; } }
// map from GameObject to component type to export
public Dictionary exportComponent;
// first clip to export
public AnimationClip defaultClip;
public AnimationOnlyExportData(
Dictionary animClips,
HashSet exportSet,
Dictionary exportComponent
)
{
this.animationClips = animClips;
this.goExportSet = exportSet;
this.exportComponent = exportComponent;
this.defaultClip = null;
}
public AnimationOnlyExportData()
{
this.animationClips = new Dictionary();
this.goExportSet = new HashSet();
this.exportComponent = new Dictionary();
this.defaultClip = null;
}
///
/// collect all object dependencies for given animation clip
///
public void CollectDependencies(
AnimationClip animClip,
GameObject rootObject,
IExportOptions exportOptions
)
{
Debug.Assert(rootObject != null);
Debug.Assert(exportOptions != null);
if (this.animationClips.ContainsKey(animClip))
{
// we have already exported gameobjects for this clip
return;
}
// NOTE: the object (animationRootObject) containing the animation is not necessarily animated
// when driven by an animator or animation component.
this.animationClips.Add(animClip, rootObject);
foreach (EditorCurveBinding uniCurveBinding in AnimationUtility.GetCurveBindings(animClip))
{
Object uniObj = AnimationUtility.GetAnimatedObject(rootObject, uniCurveBinding);
if (!uniObj)
{
continue;
}
GameObject unityGo = ModelExporter.GetGameObject(uniObj);
if (!unityGo)
{
continue;
}
if (!exportOptions.AnimateSkinnedMesh && unityGo.GetComponent())
{
continue;
}
// If we have a clip driving a camera or light then force the export of FbxNodeAttribute
// so that they point the right way when imported into Maya.
if (unityGo.GetComponent())
this.exportComponent[unityGo] = typeof(Light);
else if (unityGo.GetComponent())
this.exportComponent[unityGo] = typeof(Camera);
else if ((uniCurveBinding.type == typeof(SkinnedMeshRenderer)) && unityGo.GetComponent())
{
// only export mesh if there are animation keys for it (e.g. for blendshapes)
if (FbxPropertyChannelPair.TryGetValue(uniCurveBinding.propertyName, out FbxPropertyChannelPair[] channelPairs))
{
this.exportComponent[unityGo] = typeof(SkinnedMeshRenderer);
}
}
this.goExportSet.Add(unityGo);
}
}
///
/// collect all objects dependencies for animation clips.
///
public void CollectDependencies(
AnimationClip[] animClips,
GameObject rootObject,
IExportOptions exportOptions
)
{
Debug.Assert(rootObject != null);
Debug.Assert(exportOptions != null);
foreach (var animClip in animClips)
{
CollectDependencies(animClip, rootObject, exportOptions);
}
}
///
/// Get the GameObject that the clip is bound to in the timeline.
///
///
/// The GameObject bound to the timeline clip or null if none.
private static GameObject GetGameObjectBoundToTimelineClip(TimelineClip timelineClip, PlayableDirector director = null)
{
object parentTrack = timelineClip.GetParentTrack();
AnimationTrack animTrack = parentTrack as AnimationTrack;
var inspectedDirector = director ? director : UnityEditor.Timeline.TimelineEditor.inspectedDirector;
if (!inspectedDirector)
{
Debug.LogWarning("No Timeline selected in inspector, cannot retrieve GameObject bound to track");
return null;
}
Object animationTrackObject = inspectedDirector.GetGenericBinding(animTrack);
GameObject animationTrackGO = null;
if (animationTrackObject is GameObject)
{
animationTrackGO = animationTrackObject as GameObject;
}
else if (animationTrackObject is Animator)
{
animationTrackGO = (animationTrackObject as Animator).gameObject;
}
if (animationTrackGO == null)
{
Debug.LogErrorFormat("Could not export animation track object of type {0}", animationTrackObject.GetType().Name);
return null;
}
return animationTrackGO;
}
///
/// Get the GameObject and it's corresponding animation clip from the given timeline clip.
///
///
/// KeyValuePair containing GameObject and corresponding AnimationClip
public static KeyValuePair GetGameObjectAndAnimationClip(TimelineClip timelineClip, PlayableDirector director = null)
{
var animationTrackGO = GetGameObjectBoundToTimelineClip(timelineClip, director);
if (!animationTrackGO)
{
return new KeyValuePair();
}
return new KeyValuePair(animationTrackGO, timelineClip.animationClip);
}
///
/// Get the filename of the format {model}@{anim}.fbx from the given timeline clip
///
///
/// filename for use for exporting animation clip
public static string GetFileName(TimelineClip timelineClip)
{
// if the timeline clip name already contains an @, then take this as the
// filename to avoid duplicate @
if (timelineClip.displayName.Contains("@"))
{
return timelineClip.displayName;
}
var goBound = GetGameObjectBoundToTimelineClip(timelineClip);
if (goBound == null)
{
return timelineClip.displayName;
}
return string.Format("{0}@{1}", goBound.name, timelineClip.displayName);
}
}
}