315 lines
12 KiB
C#
315 lines
12 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
#if UNITY_2021_2_OR_NEWER
|
|
using UnityEditor.SceneManagement;
|
|
#else
|
|
using UnityEditor.Experimental.SceneManagement;
|
|
#endif
|
|
using UnityEngine;
|
|
using UnityEngine.Timeline;
|
|
using UnityEngine.Playables;
|
|
using Object = UnityEngine.Object;
|
|
|
|
|
|
namespace UnityEditor.Timeline
|
|
{
|
|
static class TimelineUtility
|
|
{
|
|
public static void ReorderTracks(List<ScriptableObject> allTracks, List<TrackAsset> tracks, ScriptableObject insertAfterAsset, bool up)
|
|
{
|
|
foreach (var i in tracks)
|
|
allTracks.Remove(i);
|
|
|
|
int index = allTracks.IndexOf(insertAfterAsset);
|
|
|
|
index = up ? Math.Max(index, 0) : index + 1;
|
|
|
|
allTracks.InsertRange(index, tracks.OfType<ScriptableObject>());
|
|
}
|
|
|
|
// Gets the track that holds the game object reference for this track.
|
|
public static TrackAsset GetSceneReferenceTrack(TrackAsset asset)
|
|
{
|
|
if (asset == null)
|
|
return null;
|
|
if (asset.isSubTrack)
|
|
return GetSceneReferenceTrack(asset.parent as TrackAsset);
|
|
return asset;
|
|
}
|
|
|
|
public static bool TrackHasAnimationCurves(TrackAsset track)
|
|
{
|
|
if (track.hasCurves)
|
|
return true;
|
|
|
|
var animTrack = track as AnimationTrack;
|
|
if (animTrack != null && animTrack.infiniteClip != null && !animTrack.infiniteClip.empty)
|
|
return true;
|
|
|
|
for (int i = 0; i < track.clips.Length; i++)
|
|
{
|
|
var curveClip = track.clips[i].curves;
|
|
var animationClip = track.clips[i].animationClip;
|
|
|
|
// prune out clip with zero curves
|
|
if (curveClip != null && curveClip.empty)
|
|
curveClip = null;
|
|
|
|
if (animationClip != null && animationClip.empty)
|
|
animationClip = null;
|
|
|
|
// prune out clips coming from FBX
|
|
if (animationClip != null && ((animationClip.hideFlags & HideFlags.NotEditable) != 0))
|
|
animationClip = null;
|
|
|
|
if (!track.clips[i].recordable)
|
|
animationClip = null;
|
|
|
|
if ((curveClip != null) || (animationClip != null))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// get the game object reference associated with this
|
|
public static GameObject GetSceneGameObject(PlayableDirector director, TrackAsset asset)
|
|
{
|
|
if (director == null || asset == null)
|
|
return null;
|
|
|
|
asset = GetSceneReferenceTrack(asset);
|
|
|
|
var gameObject = director.GetGenericBinding(asset) as GameObject;
|
|
var component = director.GetGenericBinding(asset) as Component;
|
|
if (component != null)
|
|
gameObject = component.gameObject;
|
|
return gameObject;
|
|
}
|
|
|
|
public static PlayableDirector[] GetDirectorsInSceneUsingAsset(PlayableAsset asset)
|
|
{
|
|
const HideFlags hideFlags =
|
|
HideFlags.HideInHierarchy | HideFlags.HideInInspector |
|
|
HideFlags.DontSaveInEditor | HideFlags.NotEditable;
|
|
|
|
var prefabMode = PrefabStageUtility.GetCurrentPrefabStage();
|
|
|
|
var inScene = new List<PlayableDirector>();
|
|
var allDirectors = Resources.FindObjectsOfTypeAll(typeof(PlayableDirector)) as PlayableDirector[];
|
|
foreach (var director in allDirectors)
|
|
{
|
|
if ((director.hideFlags & hideFlags) != 0)
|
|
continue;
|
|
|
|
string assetPath = AssetDatabase.GetAssetPath(director.transform.root.gameObject);
|
|
if (!String.IsNullOrEmpty(assetPath))
|
|
continue;
|
|
|
|
if (prefabMode != null && !prefabMode.IsPartOfPrefabContents(director.gameObject))
|
|
continue;
|
|
|
|
if (asset == null || (asset != null && director.playableAsset == asset))
|
|
{
|
|
inScene.Add(director);
|
|
}
|
|
}
|
|
return inScene.ToArray();
|
|
}
|
|
|
|
public static PlayableDirector GetDirectorComponentForGameObject(GameObject gameObject)
|
|
{
|
|
return gameObject != null ? gameObject.GetComponent<PlayableDirector>() : null;
|
|
}
|
|
|
|
public static TimelineAsset GetTimelineAssetForDirectorComponent(PlayableDirector director)
|
|
{
|
|
return director != null ? director.playableAsset as TimelineAsset : null;
|
|
}
|
|
|
|
public static bool IsPrefabOrAsset(Object obj)
|
|
{
|
|
return EditorUtility.IsPersistent(obj) || (obj.hideFlags & HideFlags.NotEditable) != 0;
|
|
}
|
|
|
|
// TODO -- Need to add this to SerializedProperty so we can get replicate the accuracy that exists
|
|
// in the undo system
|
|
internal static string PropertyToString(SerializedProperty property)
|
|
{
|
|
switch (property.propertyType)
|
|
{
|
|
case SerializedPropertyType.Integer:
|
|
return property.intValue.ToString(CultureInfo.InvariantCulture);
|
|
case SerializedPropertyType.Float:
|
|
return property.floatValue.ToString(CultureInfo.InvariantCulture);
|
|
case SerializedPropertyType.String:
|
|
return property.stringValue;
|
|
case SerializedPropertyType.Boolean:
|
|
return property.boolValue ? "1" : "0";
|
|
case SerializedPropertyType.Color:
|
|
return property.colorValue.ToString();
|
|
case SerializedPropertyType.ArraySize:
|
|
return property.intValue.ToString(CultureInfo.InvariantCulture);
|
|
case SerializedPropertyType.Enum:
|
|
return property.intValue.ToString(CultureInfo.InvariantCulture);
|
|
case SerializedPropertyType.ObjectReference:
|
|
return string.Empty;
|
|
case SerializedPropertyType.LayerMask:
|
|
return property.intValue.ToString(CultureInfo.InvariantCulture);
|
|
case SerializedPropertyType.Character:
|
|
return property.intValue.ToString(CultureInfo.InvariantCulture);
|
|
case SerializedPropertyType.AnimationCurve:
|
|
return property.animationCurveValue.ToString();
|
|
case SerializedPropertyType.Gradient:
|
|
return property.gradientValue.ToString();
|
|
case SerializedPropertyType.Vector3:
|
|
return property.vector3Value.ToString();
|
|
case SerializedPropertyType.Vector4:
|
|
return property.vector4Value.ToString();
|
|
case SerializedPropertyType.Vector2:
|
|
return property.vector2Value.ToString();
|
|
case SerializedPropertyType.Rect:
|
|
return property.rectValue.ToString();
|
|
case SerializedPropertyType.Bounds:
|
|
return property.boundsValue.ToString();
|
|
case SerializedPropertyType.Quaternion:
|
|
return property.quaternionValue.ToString();
|
|
case SerializedPropertyType.Generic:
|
|
return string.Empty;
|
|
default:
|
|
Debug.LogWarning("Unknown Property Type: " + property.propertyType);
|
|
return string.Empty;
|
|
}
|
|
}
|
|
|
|
// Is this a recordable clip on an animation track.
|
|
internal static bool IsRecordableAnimationClip(TimelineClip clip)
|
|
{
|
|
if (!clip.recordable)
|
|
return false;
|
|
|
|
AnimationPlayableAsset asset = clip.asset as AnimationPlayableAsset;
|
|
if (asset == null)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool HasCustomEditor(TimelineClip clip)
|
|
{
|
|
var editor = CustomTimelineEditorCache.GetClipEditor(clip);
|
|
return editor != CustomTimelineEditorCache.GetDefaultClipEditor();
|
|
}
|
|
|
|
public static IList<PlayableDirector> GetSubTimelines(TimelineClip clip, IExposedPropertyTable director)
|
|
{
|
|
var editor = CustomTimelineEditorCache.GetClipEditor(clip);
|
|
List<PlayableDirector> directors = new List<PlayableDirector>();
|
|
try
|
|
{
|
|
editor.GetSubTimelines(clip, director as PlayableDirector, directors);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
}
|
|
|
|
return directors;
|
|
}
|
|
|
|
public static bool IsAllSubTrackMuted(TrackAsset asset)
|
|
{
|
|
if (asset is GroupTrack)
|
|
return asset.mutedInHierarchy;
|
|
|
|
foreach (TrackAsset t in asset.GetChildTracks())
|
|
{
|
|
if (!t.muted)
|
|
return false;
|
|
|
|
var childMuted = IsAllSubTrackMuted(t);
|
|
|
|
if (!childMuted)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static bool IsParentMuted(TrackAsset asset)
|
|
{
|
|
TrackAsset p = asset.parent as TrackAsset;
|
|
if (p == null) return false;
|
|
return p is GroupTrack ? p.mutedInHierarchy : IsParentMuted(p);
|
|
}
|
|
|
|
public static IEnumerable<PlayableDirector> GetAllDirectorsInHierarchy(PlayableDirector mainDirector)
|
|
{
|
|
var directors = new HashSet<PlayableDirector> { mainDirector };
|
|
GetAllDirectorsInHierarchy(mainDirector, directors);
|
|
return directors;
|
|
}
|
|
|
|
static void GetAllDirectorsInHierarchy(PlayableDirector director, ISet<PlayableDirector> directors)
|
|
{
|
|
var timelineAsset = director.playableAsset as TimelineAsset;
|
|
if (timelineAsset == null)
|
|
return;
|
|
|
|
foreach (var track in timelineAsset.GetOutputTracks())
|
|
{
|
|
foreach (var clip in track.clips)
|
|
{
|
|
foreach (var subDirector in GetSubTimelines(clip, director))
|
|
{
|
|
if (!directors.Contains(subDirector))
|
|
{
|
|
directors.Add(subDirector);
|
|
GetAllDirectorsInHierarchy(subDirector, directors);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<T> GetBindingsFromDirectors<T>(IEnumerable<PlayableDirector> directors) where T : Object
|
|
{
|
|
var bindings = new HashSet<T>();
|
|
foreach (var director in directors)
|
|
{
|
|
if (director.playableAsset == null) continue;
|
|
foreach (var output in director.playableAsset.outputs)
|
|
{
|
|
var binding = director.GetGenericBinding(output.sourceObject) as T;
|
|
if (binding != null)
|
|
bindings.Add(binding);
|
|
}
|
|
}
|
|
return bindings;
|
|
}
|
|
|
|
public static bool IsLockedFromGroup(TrackAsset asset)
|
|
{
|
|
TrackAsset p = asset.parent as TrackAsset;
|
|
if (p == null) return false;
|
|
return p is GroupTrack ? p.lockedInHierarchy : IsLockedFromGroup(p);
|
|
}
|
|
|
|
internal static bool IsCurrentSequenceValid()
|
|
{
|
|
return TimelineWindow.instance != null
|
|
&& TimelineWindow.instance.state != null
|
|
&& TimelineWindow.instance.state.editSequence != null;
|
|
}
|
|
|
|
public static TimelineAsset CreateAndSaveTimelineAsset(string path)
|
|
{
|
|
var newAsset = ScriptableObject.CreateInstance<TimelineAsset>();
|
|
newAsset.editorSettings.frameRate = TimelineProjectSettings.instance.defaultFrameRate;
|
|
AssetDatabase.CreateAsset(newAsset, path);
|
|
return newAsset;
|
|
}
|
|
}
|
|
}
|