first commit

This commit is contained in:
SimonSayeBabu
2025-01-17 13:10:20 +01:00
commit bd1057cec0
16967 changed files with 1048699 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace UnityEditor.Timeline
{
static class AnimatedParameterCache
{
static readonly Dictionary<Type, FieldInfo[]> k_ScriptPlayableFieldsCache = new Dictionary<Type, FieldInfo[]>();
static readonly Dictionary<PropertyKey, FieldInfo> k_PropertyFieldInfoCache = new Dictionary<PropertyKey, FieldInfo>();
static readonly Dictionary<PropertyKey, bool> k_PropertyIsAnimatableCache = new Dictionary<PropertyKey, bool>();
static readonly Dictionary<PropertyKey, string> k_BindingNameCache = new Dictionary<PropertyKey, string>();
public static bool TryGetScriptPlayableFields(Type type, out FieldInfo[] scriptPlayableFields)
{
return k_ScriptPlayableFieldsCache.TryGetValue(type, out scriptPlayableFields);
}
public static void SetScriptPlayableFields(Type type, FieldInfo[] scriptPlayableFields)
{
k_ScriptPlayableFieldsCache[type] = scriptPlayableFields;
}
public static bool TryGetFieldInfoForProperty(SerializedProperty property, out FieldInfo fieldInfo)
{
return k_PropertyFieldInfoCache.TryGetValue(new PropertyKey(property), out fieldInfo);
}
public static void SetFieldInfoForProperty(SerializedProperty property, FieldInfo fieldInfo)
{
k_PropertyFieldInfoCache[new PropertyKey(property)] = fieldInfo;
}
public static bool TryGetIsPropertyAnimatable(SerializedProperty property, out bool isAnimatable)
{
return k_PropertyIsAnimatableCache.TryGetValue(new PropertyKey(property), out isAnimatable);
}
public static void SetIsPropertyAnimatable(SerializedProperty property, bool isAnimatable)
{
k_PropertyIsAnimatableCache[new PropertyKey(property)] = isAnimatable;
}
public static bool TryGetBindingName(Type type, string path, out string bindingName)
{
return k_BindingNameCache.TryGetValue(new PropertyKey(type, path), out bindingName);
}
public static void SetBindingName(Type type, string path, string bindingName)
{
k_BindingNameCache[new PropertyKey(type, path)] = bindingName;
}
}
struct PropertyKey : IEquatable<PropertyKey>
{
readonly Type m_Type;
readonly string m_Path;
public PropertyKey(SerializedProperty property)
{
m_Type = property.serializedObject.targetObject.GetType();
m_Path = property.propertyPath;
}
public PropertyKey(Type type, string path)
{
m_Type = type;
m_Path = path;
}
public bool Equals(PropertyKey other)
{
return m_Type == other.m_Type && string.Equals(m_Path, other.m_Path);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is PropertyKey && Equals((PropertyKey)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((m_Type != null ? m_Type.GetHashCode() : 0) * 397) ^ (m_Path != null ? m_Path.GetHashCode() : 0);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1fe0f539450e54dbc85bfb2fa6b466fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,358 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Timeline
{
static class AnimatedParameterUtility
{
static readonly Type k_DefaultAnimationType = typeof(TimelineAsset);
static SerializedObject s_CachedObject;
public static ICurvesOwner ToCurvesOwner(IPlayableAsset playableAsset, TimelineAsset timeline)
{
if (playableAsset == null)
return null;
var curvesOwner = playableAsset as ICurvesOwner;
if (curvesOwner == null)
{
// If the asset is not directly an ICurvesOwner, it might be the asset for a TimelineClip
curvesOwner = TimelineRecording.FindClipWithAsset(timeline, playableAsset);
}
return curvesOwner;
}
public static bool TryGetSerializedPlayableAsset(UnityObject asset, out SerializedObject serializedObject)
{
serializedObject = null;
if (asset == null || Attribute.IsDefined(asset.GetType(), typeof(NotKeyableAttribute)) || !HasScriptPlayable(asset))
return false;
serializedObject = GetSerializedPlayableAsset(asset);
return serializedObject != null;
}
public static SerializedObject GetSerializedPlayableAsset(UnityObject asset)
{
if (!(asset is IPlayableAsset))
return null;
var scriptObject = asset as ScriptableObject;
if (scriptObject == null)
return null;
if (s_CachedObject == null || s_CachedObject.targetObject != asset)
{
s_CachedObject = new SerializedObject(scriptObject);
}
return s_CachedObject;
}
public static void UpdateSerializedPlayableAsset(UnityObject asset)
{
var so = GetSerializedPlayableAsset(asset);
if (so != null)
so.UpdateIfRequiredOrScript();
}
public static bool HasScriptPlayable(UnityObject asset)
{
if (asset == null)
return false;
var scriptPlayable = asset as IPlayableBehaviour;
return scriptPlayable != null || GetScriptPlayableFields(asset as IPlayableAsset).Any();
}
public static FieldInfo[] GetScriptPlayableFields(IPlayableAsset asset)
{
if (asset == null)
return new FieldInfo[0];
FieldInfo[] scriptPlayableFields;
if (!AnimatedParameterCache.TryGetScriptPlayableFields(asset.GetType(), out scriptPlayableFields))
{
scriptPlayableFields = GetScriptPlayableFields_Internal(asset);
AnimatedParameterCache.SetScriptPlayableFields(asset.GetType(), scriptPlayableFields);
}
return scriptPlayableFields;
}
static FieldInfo[] GetScriptPlayableFields_Internal(IPlayableAsset asset)
{
return asset.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(
f => typeof(IPlayableBehaviour).IsAssignableFrom(f.FieldType) && // The field is an IPlayableBehaviour
(f.IsPublic || f.GetCustomAttributes(typeof(SerializeField), false).Any()) && // The field is either public or marked with [SerializeField]
!f.GetCustomAttributes(typeof(NotKeyableAttribute), false).Any() && // The field is not marked with [NotKeyable]
!f.GetCustomAttributes(typeof(HideInInspector), false).Any() && // The field is not marked with [HideInInspector]
!f.FieldType.GetCustomAttributes(typeof(NotKeyableAttribute), false).Any()) // The field is not of a type marked with [NotKeyable]
.ToArray();
}
public static bool HasAnyAnimatableParameters(UnityObject asset)
{
return GetAllAnimatableParameters(asset).Any();
}
public static IEnumerable<SerializedProperty> GetAllAnimatableParameters(UnityObject asset)
{
SerializedObject serializedObject;
if (!TryGetSerializedPlayableAsset(asset, out serializedObject))
yield break;
var prop = serializedObject.GetIterator();
// We need to keep this variable because prop starts invalid
var outOfBounds = false;
while (!outOfBounds && prop.NextVisible(true))
{
foreach (var property in SelectAnimatableProperty(prop))
yield return property;
// We can become out of bounds by calling SelectAnimatableProperty, if the last iterated property is a color.
outOfBounds = !prop.isValid;
}
}
static IEnumerable<SerializedProperty> SelectAnimatableProperty(SerializedProperty prop)
{
// We're only interested by animatable leaf parameters
if (!prop.hasChildren && IsParameterAnimatable(prop))
yield return prop.Copy();
// Color type is not considered "visible" when iterating
if (prop.propertyType == SerializedPropertyType.Color)
{
var end = prop.GetEndProperty();
// For some reasons, if the last 2+ serialized properties are of type Color, prop becomes invalid and
// Next() throws an exception. This is not the case when only the last serialized property is a Color.
while (!SerializedProperty.EqualContents(prop, end) && prop.isValid && prop.Next(true))
{
foreach (var property in SelectAnimatableProperty(prop))
yield return property;
}
}
}
public static bool IsParameterAnimatable(UnityObject asset, string parameterName)
{
SerializedObject serializedObject;
if (!TryGetSerializedPlayableAsset(asset, out serializedObject))
return false;
var prop = serializedObject.FindProperty(parameterName);
return IsParameterAnimatable(prop);
}
public static bool IsParameterAnimatable(SerializedProperty property)
{
if (property == null)
return false;
bool isAnimatable;
if (!AnimatedParameterCache.TryGetIsPropertyAnimatable(property, out isAnimatable))
{
isAnimatable = IsParameterAnimatable_Internal(property);
AnimatedParameterCache.SetIsPropertyAnimatable(property, isAnimatable);
}
return isAnimatable;
}
static bool IsParameterAnimatable_Internal(SerializedProperty property)
{
if (property == null)
return false;
var asset = property.serializedObject.targetObject;
// Currently not supported
if (asset is AnimationTrack)
return false;
if (IsParameterKeyable(property))
return asset is IPlayableBehaviour || IsParameterAtPathAnimatable(asset, property.propertyPath);
return false;
}
static bool IsParameterKeyable(SerializedProperty property)
{
return IsTypeAnimatable(property.propertyType) && IsKeyableInHierarchy(property);
}
static bool IsKeyableInHierarchy(SerializedProperty property)
{
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var pathSegments = property.propertyPath.Split('.');
var type = property.serializedObject.targetObject.GetType();
foreach (var segment in pathSegments)
{
if (type.GetCustomAttributes(typeof(NotKeyableAttribute), false).Any())
{
return false;
}
if (type.IsArray)
return false;
var fieldInfo = type.GetField(segment, bindingFlags);
if (fieldInfo == null ||
fieldInfo.GetCustomAttributes(typeof(NotKeyableAttribute), false).Any() ||
fieldInfo.GetCustomAttributes(typeof(HideInInspector), false).Any())
{
return false;
}
type = fieldInfo.FieldType;
// only value types are supported
if (!type.IsValueType && !typeof(IPlayableBehaviour).IsAssignableFrom(type))
return false;
}
return true;
}
static bool IsParameterAtPathAnimatable(UnityObject asset, string path)
{
if (asset == null)
return false;
return GetScriptPlayableFields(asset as IPlayableAsset)
.Any(
f => path.StartsWith(f.Name, StringComparison.Ordinal) &&
path.Length > f.Name.Length &&
path[f.Name.Length] == '.');
}
public static bool IsTypeAnimatable(SerializedPropertyType type)
{
// Note: Integer is not currently supported by the animated property system
switch (type)
{
case SerializedPropertyType.Boolean:
case SerializedPropertyType.Float:
case SerializedPropertyType.Vector2:
case SerializedPropertyType.Vector3:
case SerializedPropertyType.Color:
case SerializedPropertyType.Quaternion:
case SerializedPropertyType.Vector4:
return true;
default:
return false;
}
}
public static bool IsParameterAnimated(UnityObject asset, AnimationClip animationData, string parameterName)
{
if (asset == null || animationData == null)
return false;
var binding = GetCurveBinding(asset, parameterName);
var bindings = AnimationClipCurveCache.Instance.GetCurveInfo(animationData).bindings;
return bindings.Any(x => BindingMatchesParameterName(x, binding.propertyName));
}
// Retrieve an animated parameter curve. parameter name is required to include the appropriate field for vectors
// e.g.: position
public static AnimationCurve GetAnimatedParameter(UnityObject asset, AnimationClip animationData, string parameterName)
{
if (!(asset is ScriptableObject) || animationData == null)
return null;
var binding = GetCurveBinding(asset, parameterName);
return AnimationUtility.GetEditorCurve(animationData, binding);
}
// get an animatable curve binding for this parameter
public static EditorCurveBinding GetCurveBinding(UnityObject asset, string parameterName)
{
var animationName = GetAnimatedParameterBindingName(asset, parameterName);
return EditorCurveBinding.FloatCurve(string.Empty, GetValidAnimationType(asset), animationName);
}
public static string GetAnimatedParameterBindingName(UnityObject asset, string parameterName)
{
if (asset == null)
return parameterName;
string bindingName;
if (!AnimatedParameterCache.TryGetBindingName(asset.GetType(), parameterName, out bindingName))
{
bindingName = GetAnimatedParameterBindingName_Internal(asset, parameterName);
AnimatedParameterCache.SetBindingName(asset.GetType(), parameterName, bindingName);
}
return bindingName;
}
static string GetAnimatedParameterBindingName_Internal(UnityObject asset, string parameterName)
{
if (asset is IPlayableBehaviour)
return parameterName;
// strip the IScript playable field name
var fields = GetScriptPlayableFields(asset as IPlayableAsset);
foreach (var f in fields)
{
if (parameterName.StartsWith(f.Name, StringComparison.Ordinal))
{
if (parameterName.Length > f.Name.Length && parameterName[f.Name.Length] == '.')
return parameterName.Substring(f.Name.Length + 1);
}
}
return parameterName;
}
public static bool BindingMatchesParameterName(EditorCurveBinding binding, string parameterName)
{
if (binding.propertyName == parameterName)
return true;
var indexOfDot = binding.propertyName.LastIndexOf('.');
return indexOfDot > 0 && parameterName.Length == indexOfDot &&
binding.propertyName.StartsWith(parameterName, StringComparison.Ordinal);
}
// the animated type must be a non-abstract instantiable object.
public static Type GetValidAnimationType(UnityObject asset)
{
return asset != null ? asset.GetType() : k_DefaultAnimationType;
}
public static FieldInfo GetFieldInfoForProperty(SerializedProperty property)
{
FieldInfo fieldInfo;
if (!AnimatedParameterCache.TryGetFieldInfoForProperty(property, out fieldInfo))
{
Type _;
fieldInfo = ScriptAttributeUtility.GetFieldInfoFromProperty(property, out _);
AnimatedParameterCache.SetFieldInfoForProperty(property, fieldInfo);
}
return fieldInfo;
}
public static T GetAttributeForProperty<T>(SerializedProperty property) where T : Attribute
{
var fieldInfo = GetFieldInfoForProperty(property);
return fieldInfo.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84d86c98104d94063ad70bc591530f65
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
// Helper methods for animated properties
internal static class AnimatedPropertyUtility
{
public static bool IsMaterialProperty(string propertyName)
{
return propertyName.StartsWith("material.");
}
/// <summary>
/// Given a propertyName (from an EditorCurveBinding), and the gameObject it refers to,
/// remaps the path to include the exposed name of the shader parameter
/// </summary>
/// <param name="gameObject">The gameObject being referenced.</param>
/// <param name="propertyName">The propertyName to remap.</param>
/// <returns>The remapped propertyName, or the original propertyName if it cannot be remapped</returns>
public static string RemapMaterialName(GameObject gameObject, string propertyName)
{
if (!IsMaterialProperty(propertyName) || gameObject == null)
return propertyName;
var renderers = gameObject.GetComponents<Renderer>();
if (renderers == null || renderers.Length == 0)
return propertyName;
var propertySplits = propertyName.Split('.');
if (propertySplits.Length <= 1)
return propertyName;
// handles post fixes for texture properties
var exposedParameter = HandleTextureProperties(propertySplits[1], out var postFix);
foreach (var renderer in renderers)
{
foreach (var material in renderer.sharedMaterials)
{
if (material.shader == null)
continue;
var index = material.shader.FindPropertyIndex(exposedParameter);
if (index >= 0)
{
propertySplits[1] = material.shader.GetPropertyDescription(index) + postFix;
return String.Join(".", propertySplits);
}
}
}
return propertyName;
}
private static string HandleTextureProperties(string exposedParameter, out string postFix)
{
postFix = String.Empty;
RemoveEnding(ref exposedParameter, ref postFix, "_ST");
RemoveEnding(ref exposedParameter, ref postFix, "_TexelSize");
RemoveEnding(ref exposedParameter, ref postFix, "_HDR");
return exposedParameter;
}
private static void RemoveEnding(ref string name, ref string postFix, string ending)
{
if (name.EndsWith(ending))
{
name = name.Substring(0, name.Length - ending.Length);
postFix = ending;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e22173cccd50433098692e06d6811255
timeCreated: 1602163952

View File

@@ -0,0 +1,130 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
static class BindingUtility
{
public enum BindingAction
{
DoNotBind,
BindDirectly,
BindToExistingComponent,
BindToMissingComponent
}
const string k_BindingOperation = "Bind Track";
public static void Bind(PlayableDirector director, TrackAsset bindTo, Object objectToBind)
{
if (director == null || bindTo == null || TimelineWindow.instance == null)
return;
if (director.GetGenericBinding(bindTo) == objectToBind)
return;
TimelineWindow.instance.state.previewMode = false; // returns all objects to previous state
TimelineUndo.PushUndo(director, k_BindingOperation);
director.SetGenericBinding(bindTo, objectToBind);
TimelineWindow.instance.state.rebuildGraph = true;
}
public static void BindWithEditorValidation(PlayableDirector director, TrackAsset bindTo, Object objectToBind)
{
TrackEditor trackEditor = CustomTimelineEditorCache.GetTrackEditor(bindTo);
Object validatedObject = trackEditor.GetBindingFrom_Safe(objectToBind, bindTo);
Bind(director, bindTo, validatedObject);
}
public static void BindWithInteractiveEditorValidation(PlayableDirector director, TrackAsset bindTo, Object objectToBind)
{
TrackEditor trackEditor = CustomTimelineEditorCache.GetTrackEditor(bindTo);
if (trackEditor.SupportsBindingAssign())
BindWithEditorValidation(director, bindTo, objectToBind);
else
{
Type bindingType = TypeUtility.GetTrackBindingAttribute(bindTo.GetType())?.type;
BindingAction action = GetBindingAction(bindingType, objectToBind);
if (action == BindingAction.BindToMissingComponent)
InteractiveBindToMissingComponent(director, bindTo, objectToBind, bindingType);
else
{
var validatedObject = GetBinding(action, objectToBind, bindingType);
Bind(director, bindTo, validatedObject);
}
}
}
public static BindingAction GetBindingAction(Type requiredBindingType, Object objectToBind)
{
if (requiredBindingType == null || objectToBind == null)
return BindingAction.DoNotBind;
// prevent drag and drop of prefab assets
if (PrefabUtility.IsPartOfPrefabAsset(objectToBind))
return BindingAction.DoNotBind;
if (requiredBindingType.IsInstanceOfType(objectToBind))
return BindingAction.BindDirectly;
var draggedGameObject = objectToBind as GameObject;
if (!typeof(Component).IsAssignableFrom(requiredBindingType) || draggedGameObject == null)
return BindingAction.DoNotBind;
if (draggedGameObject.GetComponent(requiredBindingType) == null)
return BindingAction.BindToMissingComponent;
return BindingAction.BindToExistingComponent;
}
public static Object GetBinding(BindingAction bindingAction, Object objectToBind, Type requiredBindingType)
{
if (objectToBind == null) return null;
switch (bindingAction)
{
case BindingAction.BindDirectly:
{
return objectToBind;
}
case BindingAction.BindToExistingComponent:
{
var gameObjectBeingDragged = objectToBind as GameObject;
Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject");
return gameObjectBeingDragged.GetComponent(requiredBindingType);
}
case BindingAction.BindToMissingComponent:
{
var gameObjectBeingDragged = objectToBind as GameObject;
Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject");
return Undo.AddComponent(gameObjectBeingDragged, requiredBindingType);
}
default:
return null;
}
}
static void InteractiveBindToMissingComponent(PlayableDirector director, TrackAsset bindTo, Object objectToBind, Type requiredComponentType)
{
var gameObjectBeingDragged = objectToBind as GameObject;
Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject");
string typeNameOfComponent = requiredComponentType.ToString().Split(".".ToCharArray()).Last();
var bindMenu = new GenericMenu();
bindMenu.AddItem(
EditorGUIUtility.TextContent("Create " + typeNameOfComponent + " on " + gameObjectBeingDragged.name),
false,
nullParam => Bind(director, bindTo, Undo.AddComponent(gameObjectBeingDragged, requiredComponentType)),
null);
bindMenu.AddSeparator("");
bindMenu.AddItem(EditorGUIUtility.TrTextContent("Cancel"), false, userData => { }, null);
bindMenu.ShowAsContext();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef5fa6e2005defb4ab5142723827b58e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
#else
using UnityEditor.Experimental.SceneManagement;
#endif
using UnityEngine;
namespace UnityEditor.Timeline
{
enum TitleMode
{
None,
DisabledComponent,
Prefab,
PrefabOutOfContext,
Asset,
GameObject
}
struct BreadCrumbTitle
{
public string name;
public TitleMode mode;
}
class BreadcrumbDrawer
{
static readonly GUIContent s_TextContent = new GUIContent();
static readonly string k_DisabledComponentText = L10n.Tr("The PlayableDirector is disabled");
static readonly string k_PrefabOutOfContext = L10n.Tr("Prefab Isolation not enabled. Click to Enable.");
static readonly GUIStyle k_BreadCrumbLeft;
static readonly GUIStyle k_BreadCrumbMid;
static readonly GUIStyle k_BreadCrumbLeftBg;
static readonly GUIStyle k_BreadCrumbMidBg;
static readonly GUIStyle k_BreadCrumbMidSelected;
static readonly GUIStyle k_BreadCrumbMidBgSelected;
static readonly Texture k_TimelineIcon;
const string k_Elipsis = "…";
static BreadcrumbDrawer()
{
k_BreadCrumbLeft = "GUIEditor.BreadcrumbLeft";
k_BreadCrumbMid = "GUIEditor.BreadcrumbMid";
k_BreadCrumbLeftBg = "GUIEditor.BreadcrumbLeftBackground";
k_BreadCrumbMidBg = "GUIEditor.BreadcrumbMidBackground";
k_BreadCrumbMidSelected = k_BreadCrumbMid;
k_BreadCrumbMidSelected.normal = k_BreadCrumbMidSelected.onNormal;
k_BreadCrumbMidBgSelected = k_BreadCrumbMidBg;
k_BreadCrumbMidBgSelected.normal = k_BreadCrumbMidBgSelected.onNormal;
k_TimelineIcon = EditorGUIUtility.IconContent("TimelineAsset Icon").image;
}
static string FitTextInArea(float areaWidth, string text, GUIStyle style)
{
var borderWidth = style.border.left + style.border.right;
var textWidth = style.CalcSize(EditorGUIUtility.TextContent(text)).x;
if (borderWidth + textWidth < areaWidth)
return text;
// Need to truncate the text to fit in the areaWidth
var textAreaWidth = areaWidth - borderWidth;
var pixByChar = textWidth / text.Length;
var charNeeded = (int)Mathf.Floor(textAreaWidth / pixByChar);
charNeeded -= k_Elipsis.Length;
if (charNeeded <= 0)
return k_Elipsis;
if (charNeeded <= text.Length)
return k_Elipsis + " " + text.Substring(text.Length - charNeeded);
return k_Elipsis;
}
public static void Draw(float breadcrumbAreaWidth, List<BreadCrumbTitle> labels, Action<int> navigateToBreadcrumbIndex)
{
GUILayout.BeginHorizontal();
{
var labelWidth = (int)(breadcrumbAreaWidth / labels.Count);
for (var i = 0; i < labels.Count; i++)
{
var label = labels[i];
var style = i == 0 ? k_BreadCrumbLeft : k_BreadCrumbMid;
var backgroundStyle = i == 0 ? k_BreadCrumbLeftBg : k_BreadCrumbMidBg;
if (i == labels.Count - 1)
{
if (i > 0) // Only tint last breadcrumb if we are dug-in
DrawBreadcrumbAsSelectedSubSequence(labelWidth, label, k_BreadCrumbMidSelected, k_BreadCrumbMidBgSelected);
else
DrawActiveBreadcrumb(labelWidth, label, style, backgroundStyle);
}
else
{
var previousContentColor = GUI.contentColor;
GUI.contentColor = new Color(previousContentColor.r,
previousContentColor.g,
previousContentColor.b,
previousContentColor.a * 0.6f);
var content = GetTextContent(labelWidth, label, style);
var rect = GetBreadcrumbLayoutRect(content, style);
if (Event.current.type == EventType.Repaint)
backgroundStyle.Draw(rect, GUIContent.none, 0);
if (GUI.Button(rect, content, style))
navigateToBreadcrumbIndex.Invoke(i);
GUI.contentColor = previousContentColor;
}
}
}
GUILayout.EndHorizontal();
}
static GUIContent GetTextContent(int width, BreadCrumbTitle text, GUIStyle style)
{
s_TextContent.tooltip = string.Empty;
s_TextContent.image = null;
if (text.mode == TitleMode.DisabledComponent)
{
s_TextContent.tooltip = k_DisabledComponentText;
s_TextContent.image = EditorGUIUtility.GetHelpIcon(MessageType.Warning);
}
else if (text.mode == TitleMode.Prefab)
s_TextContent.image = PrefabUtility.GameObjectStyles.prefabIcon;
else if (text.mode == TitleMode.GameObject)
s_TextContent.image = PrefabUtility.GameObjectStyles.gameObjectIcon;
else if (text.mode == TitleMode.Asset)
s_TextContent.image = k_TimelineIcon;
else if (text.mode == TitleMode.PrefabOutOfContext)
{
s_TextContent.image = PrefabUtility.GameObjectStyles.prefabIcon;
if (!TimelineWindow.instance.locked)
s_TextContent.tooltip = k_PrefabOutOfContext;
}
if (s_TextContent.image != null)
width = Math.Max(0, width - s_TextContent.image.width);
s_TextContent.text = FitTextInArea(width, text.name, style);
return s_TextContent;
}
static void DrawBreadcrumbAsSelectedSubSequence(int width, BreadCrumbTitle label, GUIStyle style, GUIStyle backgroundStyle)
{
var rect = DrawActiveBreadcrumb(width, label, style, backgroundStyle);
const float underlineThickness = 2.0f;
const float underlineVerticalOffset = 0.0f;
var underlineHorizontalOffset = backgroundStyle.border.right * 0.333f;
var underlineRect = Rect.MinMaxRect(
rect.xMin - underlineHorizontalOffset,
rect.yMax - underlineThickness - underlineVerticalOffset,
rect.xMax - underlineHorizontalOffset,
rect.yMax - underlineVerticalOffset);
EditorGUI.DrawRect(underlineRect, DirectorStyles.Instance.customSkin.colorSubSequenceDurationLine);
}
static Rect GetBreadcrumbLayoutRect(GUIContent content, GUIStyle style)
{
// the image makes the button far too big compared to non-image versions
var image = content.image;
content.image = null;
var size = style.CalcSizeWithConstraints(content, Vector2.zero);
content.image = image;
if (image != null)
size.x += size.y; // assumes square image, constrained by height
return GUILayoutUtility.GetRect(content, style, GUILayout.MaxWidth(size.x));
}
static Rect DrawActiveBreadcrumb(int width, BreadCrumbTitle label, GUIStyle style, GUIStyle backgroundStyle)
{
var content = GetTextContent(width, label, style);
var rect = GetBreadcrumbLayoutRect(content, style);
if (Event.current.type == EventType.Repaint)
{
backgroundStyle.Draw(rect, GUIContent.none, 0);
}
if (GUI.Button(rect, content, style))
{
UnityEngine.Object target = TimelineEditor.inspectedDirector;
if (target == null)
target = TimelineEditor.inspectedAsset;
if (target != null)
{
bool ping = true;
if (label.mode == TitleMode.PrefabOutOfContext)
{
var gameObject = PrefabUtility.GetRootGameObject(target);
if (gameObject != null)
{
target = gameObject; // ping the prefab root if it's locked.
if (!TimelineWindow.instance.locked)
{
var assetPath = AssetDatabase.GetAssetPath(gameObject);
if (!string.IsNullOrEmpty(assetPath))
{
var stage = PrefabStageUtility.OpenPrefab(assetPath);
if (stage != null)
ping = false;
}
}
}
}
if (ping)
{
EditorGUIUtility.PingObject(target);
}
}
}
return rect;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 526f285e8d4fb8140b4cdfeb9102d8cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,397 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Timeline;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
static class ClipModifier
{
public static bool Delete(TimelineAsset timeline, TimelineClip clip)
{
return timeline.DeleteClip(clip);
}
public static bool Tile(IEnumerable<TimelineClip> clips)
{
if (clips.Count() < 2)
return false;
var clipsByTracks = clips.GroupBy(x => x.GetParentTrack())
.Select(track => new { track.Key, Items = track.OrderBy(c => c.start) });
foreach (var track in clipsByTracks)
{
UndoExtensions.RegisterTrack(track.Key, L10n.Tr("Tile"));
}
foreach (var track in clipsByTracks)
{
double newStart = track.Items.First().start;
foreach (var c in track.Items)
{
c.start = newStart;
newStart += c.duration;
}
}
return true;
}
public static bool TrimStart(IEnumerable<TimelineClip> clips, double trimTime)
{
var result = false;
foreach (var clip in clips)
result |= TrimStart(clip, trimTime);
return result;
}
public static bool TrimStart(TimelineClip clip, double trimTime)
{
if (clip.asset == null)
return false;
if (clip.start > trimTime)
return false;
if (clip.end < trimTime)
return false;
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip Start"));
// Note: We are NOT using edit modes in this case because we want the same result
// regardless of the selected EditMode: split at cursor and delete left part
SetStart(clip, trimTime, false);
clip.ConformEaseValues();
return true;
}
public static bool TrimEnd(IEnumerable<TimelineClip> clips, double trimTime)
{
var result = false;
foreach (var clip in clips)
result |= TrimEnd(clip, trimTime);
return result;
}
public static bool TrimEnd(TimelineClip clip, double trimTime)
{
if (clip.asset == null)
return false;
if (clip.start > trimTime)
return false;
if (clip.end < trimTime)
return false;
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip End"));
TrimClipWithEditMode(clip, TrimEdge.End, trimTime);
return true;
}
public static bool MatchDuration(IEnumerable<TimelineClip> clips)
{
double referenceDuration = clips.First().duration;
UndoExtensions.RegisterClips(clips, L10n.Tr("Match Clip Duration"));
foreach (var clip in clips)
{
var newEnd = clip.start + referenceDuration;
TrimClipWithEditMode(clip, TrimEdge.End, newEnd);
}
return true;
}
public static bool Split(IEnumerable<TimelineClip> clips, double splitTime, PlayableDirector director)
{
var result = false;
foreach (var clip in clips)
{
if (clip.start >= splitTime)
continue;
if (clip.end <= splitTime)
continue;
UndoExtensions.RegisterClip(clip, L10n.Tr("Split Clip"));
TimelineClip newClip = TimelineHelpers.Clone(clip, director, director, clip.start);
clip.easeInDuration = 0;
newClip.easeOutDuration = 0;
SetStart(clip, splitTime, false);
SetEnd(newClip, splitTime, false);
// Sort produced by cloning clips on top of each other is unpredictable (it varies between mono runtimes)
clip.GetParentTrack().SortClips();
result = true;
}
return result;
}
public static void SetStart(TimelineClip clip, double time, bool affectTimeScale)
{
var supportsClipIn = clip.SupportsClipIn();
var supportsPadding = TimelineUtility.IsRecordableAnimationClip(clip);
bool calculateTimeScale = (affectTimeScale && clip.SupportsSpeedMultiplier());
// treat empty recordable clips as not supporting clip in (there are no keys to modify)
if (supportsPadding && (clip.animationClip == null || clip.animationClip.empty))
{
supportsClipIn = false;
}
if (supportsClipIn && !supportsPadding && !calculateTimeScale)
{
var minStart = clip.FromLocalTimeUnbound(0.0);
if (time < minStart)
time = minStart;
}
var maxStart = clip.end - TimelineClip.kMinDuration;
if (time > maxStart)
time = maxStart;
var timeOffset = time - clip.start;
var duration = clip.duration - timeOffset;
if (calculateTimeScale)
{
var f = clip.duration / duration;
clip.timeScale *= f;
}
if (supportsClipIn && !calculateTimeScale)
{
if (supportsPadding)
{
double clipInGlobal = clip.clipIn / clip.timeScale;
double keyShift = -timeOffset;
if (timeOffset < 0) // left drag, eliminate clipIn before shifting
{
double clipInDelta = Math.Max(-clipInGlobal, timeOffset);
keyShift = -Math.Min(0, timeOffset - clipInDelta);
clip.clipIn += clipInDelta * clip.timeScale;
}
else if (timeOffset > 0) // right drag, elimate padding in animation clip before adding clip in
{
var clipInfo = AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip);
double keyDelta = clip.FromLocalTimeUnbound(clipInfo.keyTimes.Min()) - clip.start;
keyShift = -Math.Max(0, Math.Min(timeOffset, keyDelta));
clip.clipIn += Math.Max(timeOffset + keyShift, 0) * clip.timeScale;
}
if (keyShift != 0)
{
AnimationTrackRecorder.ShiftAnimationClip(clip.animationClip, (float)(keyShift * clip.timeScale));
}
}
else
{
clip.clipIn += timeOffset * clip.timeScale;
}
}
clip.start = time;
clip.duration = duration;
}
public static void SetEnd(TimelineClip clip, double time, bool affectTimeScale)
{
var duration = Math.Max(time - clip.start, TimelineClip.kMinDuration);
if (affectTimeScale && clip.SupportsSpeedMultiplier())
{
var f = clip.duration / duration;
clip.timeScale *= f;
}
clip.duration = duration;
}
public static bool ResetEditing(IEnumerable<TimelineClip> clips)
{
var result = false;
foreach (var clip in clips)
result = result || ResetEditing(clip);
return result;
}
public static bool ResetEditing(TimelineClip clip)
{
if (clip.asset == null)
return false;
UndoExtensions.RegisterClip(clip, L10n.Tr("Reset Clip Editing"));
clip.clipIn = 0.0;
if (clip.clipAssetDuration < double.MaxValue)
{
var duration = clip.clipAssetDuration / clip.timeScale;
TrimClipWithEditMode(clip, TrimEdge.End, clip.start + duration);
}
return true;
}
public static bool MatchContent(IEnumerable<TimelineClip> clips)
{
var result = false;
foreach (var clip in clips)
result |= MatchContent(clip);
return result;
}
public static bool MatchContent(TimelineClip clip)
{
if (clip.asset == null)
return false;
UndoExtensions.RegisterClip(clip, L10n.Tr("Match Clip Content"));
var newStartCandidate = clip.start - clip.clipIn / clip.timeScale;
var newStart = newStartCandidate < 0.0 ? 0.0 : newStartCandidate;
TrimClipWithEditMode(clip, TrimEdge.Start, newStart);
// In case resetting the start was blocked by edit mode or timeline start, we do the best we can
clip.clipIn = (clip.start - newStartCandidate) * clip.timeScale;
if (clip.clipAssetDuration > 0 && TimelineHelpers.HasUsableAssetDuration(clip))
{
var duration = TimelineHelpers.GetLoopDuration(clip);
var offset = (clip.clipIn / clip.timeScale) % duration;
TrimClipWithEditMode(clip, TrimEdge.End, clip.start - offset + duration);
}
return true;
}
public static void TrimClipWithEditMode(TimelineClip clip, TrimEdge edge, double time)
{
var clipItem = ItemsUtils.ToItem(clip);
EditMode.BeginTrim(clipItem, edge);
if (edge == TrimEdge.Start)
EditMode.TrimStart(clipItem, time, false);
else
EditMode.TrimEnd(clipItem, time, false);
EditMode.FinishTrim();
}
public static bool CompleteLastLoop(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
CompleteLastLoop(clip);
}
return true;
}
public static void CompleteLastLoop(TimelineClip clip)
{
FixLoops(clip, true);
}
public static bool TrimLastLoop(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
TrimLastLoop(clip);
}
return true;
}
public static void TrimLastLoop(TimelineClip clip)
{
FixLoops(clip, false);
}
static void FixLoops(TimelineClip clip, bool completeLastLoop)
{
if (!TimelineHelpers.HasUsableAssetDuration(clip))
return;
var loopDuration = TimelineHelpers.GetLoopDuration(clip);
var firstLoopDuration = loopDuration - clip.clipIn * (1.0 / clip.timeScale);
// Making sure we don't trim to zero
if (!completeLastLoop && firstLoopDuration > clip.duration)
return;
var numLoops = (clip.duration - firstLoopDuration) / loopDuration;
var numCompletedLoops = Math.Floor(numLoops);
if (!(numCompletedLoops < numLoops))
return;
if (completeLastLoop)
numCompletedLoops += 1;
var newEnd = clip.start + firstLoopDuration + loopDuration * numCompletedLoops;
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip Last Loop"));
TrimClipWithEditMode(clip, TrimEdge.End, newEnd);
}
public static bool DoubleSpeed(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
if (clip.SupportsSpeedMultiplier())
{
UndoExtensions.RegisterClip(clip, L10n.Tr("Double Clip Speed"));
clip.timeScale = clip.timeScale * 2.0f;
}
}
return true;
}
public static bool HalfSpeed(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
if (clip.SupportsSpeedMultiplier())
{
UndoExtensions.RegisterClip(clip, L10n.Tr("Half Clip Speed"));
clip.timeScale = clip.timeScale * 0.5f;
}
}
return true;
}
public static bool ResetSpeed(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
if (clip.timeScale != 1.0)
{
UndoExtensions.RegisterClip(clip, L10n.Tr("Reset Clip Speed"));
clip.timeScale = 1.0;
}
}
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0eeee3cdfa56734abca5c1a4e7989ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,156 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
class Clipboard
{
class ExposedReferenceTable : IExposedPropertyTable
{
Dictionary<PropertyName, Object> m_ReferenceTable = new Dictionary<PropertyName, Object>();
public void SetReferenceValue(PropertyName id, Object value)
{
m_ReferenceTable[id] = value;
}
public Object GetReferenceValue(PropertyName id, out bool idValid)
{
Object reference;
idValid = m_ReferenceTable.TryGetValue(id, out reference);
return reference;
}
public void ClearReferenceValue(PropertyName id)
{
m_ReferenceTable.Remove(id);
}
public void Clear()
{
m_ReferenceTable.Clear();
}
}
public struct ClipboardTrackEntry
{
public TrackAsset item;
public TrackAsset parent;
public List<Object> bindings;
}
static readonly int kListInitialSize = 10;
readonly List<ItemsPerTrack> m_ItemsData = new List<ItemsPerTrack>(kListInitialSize);
readonly List<ClipboardTrackEntry> m_trackData = new List<ClipboardTrackEntry>(kListInitialSize);
TimelineAsset rootTimeline;
public readonly IExposedPropertyTable exposedPropertyTable = new ExposedReferenceTable();
public Clipboard()
{
rootTimeline = CreateTimeline();
EditorApplication.playModeStateChanged += OnPlayModeChanged;
}
public void CopyItems(IEnumerable<ITimelineItem> items)
{
using (new TimelineUndo.DisableUndoGuard(true))
{
var itemsByParent = items.ToLookup(i => i.parentTrack);
foreach (var itemsGroup in itemsByParent)
{
var parent = itemsGroup.Key;
var itemsList = new List<ITimelineItem>();
foreach (var item in itemsGroup)
{
if (item is ClipItem)
itemsList.Add(CopyItem((ClipItem)item));
else if (item is MarkerItem)
itemsList.Add(CopyItem((MarkerItem)item));
}
m_ItemsData.Add(new ItemsPerTrack(parent, itemsList));
}
}
}
ClipItem CopyItem(ClipItem clipItem)
{
var newClip = TimelineHelpers.Clone(clipItem.clip, TimelineWindow.instance.state.editSequence.director, exposedPropertyTable, rootTimeline);
return new ClipItem(newClip);
}
static MarkerItem CopyItem(MarkerItem markerItem)
{
var markerObject = markerItem.marker as Object;
if (markerObject != null)
{
var newMarker = Object.Instantiate(markerObject);
newMarker.name = markerObject.name;
return new MarkerItem((IMarker)newMarker);
}
return null;
}
public void CopyTracks(IEnumerable<TrackAsset> tracks)
{
using (new TimelineUndo.DisableUndoGuard(true))
{
foreach (var track in TrackExtensions.FilterTracks(tracks))
{
var newTrack = track.Duplicate(TimelineEditor.inspectedDirector, TimelineEditor.clipboard.exposedPropertyTable, rootTimeline);
var originalTracks = track.GetFlattenedChildTracks().Append(track);
var newTracks = newTrack.GetFlattenedChildTracks().Append(newTrack);
var toBind = new List<Object>();
// Collect all track bindings to duplicate
var originalIt = originalTracks.GetEnumerator();
var newIt = newTracks.GetEnumerator();
while (originalIt.MoveNext() && newIt.MoveNext())
{
toBind.Add(TimelineEditor.inspectedDirector != null ? TimelineEditor.inspectedDirector.GetGenericBinding(originalIt.Current) : null);
}
m_trackData.Add(new ClipboardTrackEntry { item = newTrack, parent = track.parent as TrackAsset, bindings = toBind });
}
}
}
public IEnumerable<ClipboardTrackEntry> GetTracks()
{
return m_trackData;
}
public IEnumerable<ItemsPerTrack> GetCopiedItems()
{
return m_ItemsData;
}
public void Clear()
{
m_ItemsData.Clear();
m_trackData.Clear();
rootTimeline = CreateTimeline();
((ExposedReferenceTable)exposedPropertyTable).Clear();
}
private void OnPlayModeChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.EnteredPlayMode)
Clear();
}
static TimelineAsset CreateTimeline()
{
var timeline = ScriptableObject.CreateInstance<TimelineAsset>();
timeline.hideFlags |= HideFlags.DontSave;
timeline.name = "Clipboard";
return timeline;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b57629d89799e004182564256307b0cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using System.Collections.Generic;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class ControlPlayableUtility
{
public static bool DetectCycle(
ControlPlayableAsset asset, PlayableDirector director, HashSet<PlayableDirector> set = null)
{
if (director == null || asset == null || !asset.updateDirector)
return false;
if (set == null)
set = new HashSet<PlayableDirector>();
if (set.Contains(director))
return true;
var gameObject = asset.sourceGameObject.Resolve(director);
if (gameObject == null)
return false;
set.Add(director);
foreach (var subDirector in asset.GetComponent<PlayableDirector>(gameObject))
{
foreach (var childAsset in GetPlayableAssets(subDirector))
{
if (DetectCycle(childAsset, subDirector, set))
return true;
}
}
set.Remove(director);
return false;
}
public static IEnumerable<ControlPlayableAsset> GetPlayableAssets(PlayableDirector director)
{
var timeline = director != null ? (director.playableAsset as TimelineAsset) : null;
if (timeline != null)
{
foreach (var t in timeline.GetOutputTracks())
{
var controlTrack = t as ControlTrack;
if (controlTrack != null)
{
foreach (var c in t.GetClips())
{
var asset = c.asset as ControlPlayableAsset;
if (asset != null)
yield return asset;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e801faa3b0dd2478dbe801a2441b679e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
using System;
namespace UnityEditor.Timeline
{
// Tells a custom [[TrackDrawer]] which [[TrackAsset]] it's a drawer for.
sealed class CustomTrackDrawerAttribute : Attribute
{
public Type assetType;
public CustomTrackDrawerAttribute(Type type)
{
assetType = type;
}
}
/// <summary>
/// Attribute that specifies a class as an editor for an extended Timeline type.
/// </summary>
/// <remarks>
/// Use this attribute on a class that extends ClipEditor, TrackEditor, or MarkerEditor to specify either the PlayableAsset, Marker, or TrackAsset derived classes for associated customization.
/// </remarks>
/// <example>
/// <code source="../../DocCodeExamples/TimelineAttributesExamples.cs" region="declare-customTimelineEditorAttr" title="customTimelineEditorAttr"/>
/// </example>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class CustomTimelineEditorAttribute : Attribute
{
/// <summary>
/// The type that that this editor applies to.
/// </summary>
public Type classToEdit { get; private set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="type"> The type that that this editor applies to.</param>
/// <exception cref="ArgumentNullException">Thrown if type is null</exception>
public CustomTimelineEditorAttribute(Type type)
{
if (type == null)
throw new System.ArgumentNullException(nameof(type));
classToEdit = type;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1e957d39ca70834f9212a1289b6a0d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System.Text;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
static class DisplayNameHelper
{
static readonly string k_NoAssetDisplayName = L10n.Tr("<No Asset>");
static readonly string k_ReadOnlyDisplayName = L10n.Tr("[Read Only]");
static readonly StringBuilder k_StringBuilder = new StringBuilder();
public static string GetDisplayName(ISequenceState sequence)
{
string displayName = sequence.director != null ? GetDisplayName(sequence.director) : GetDisplayName(sequence.asset);
if (sequence.asset != null && sequence.isReadOnly)
displayName += " " + k_ReadOnlyDisplayName;
return displayName;
}
public static string GetDisplayName(PlayableAsset asset)
{
return asset != null ? asset.name : k_NoAssetDisplayName;
}
public static string GetDisplayName(PlayableDirector director)
{
k_StringBuilder.Length = 0;
k_StringBuilder.Append(GetDisplayName(director.playableAsset));
k_StringBuilder.Append(" (").Append(director.name).Append(')');
return k_StringBuilder.ToString();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7fc2147e42d71644aad0eaf9a3526249
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using System.IO;
using UnityEditor.VersionControl;
namespace UnityEditor.Timeline
{
static class FileUtility
{
internal static bool IsReadOnly(UnityEngine.Object asset)
{
return IsReadOnlyImpl(asset);
}
#if UNITY_2021_2_OR_NEWER
static bool IsReadOnlyImpl(UnityEngine.Object asset)
{
string assetPath = AssetDatabase.GetAssetPath(asset);
if (string.IsNullOrEmpty(assetPath))
return false;
if (Provider.enabled && VersionControlUtils.IsPathVersioned(assetPath))
{
return !AssetDatabase.CanOpenForEdit(asset, StatusQueryOptions.UseCachedIfPossible);
}
return (uint)(File.GetAttributes(assetPath) & FileAttributes.ReadOnly) > 0U;
}
#else
static bool IsReadOnlyImpl(UnityEngine.Object asset)
{
string assetPath = AssetDatabase.GetAssetPath(asset);
if (Provider.enabled)
{
if (!Provider.isActive)
return false;
Asset vcAsset = Provider.GetAssetByPath(assetPath);
if (Provider.IsOpenForEdit(vcAsset))
return false;
//I can't get any of the Provider checks to work, but here we should check for exclusive checkout issues.
return false;
}
if (!string.IsNullOrEmpty(assetPath))
{
return (File.GetAttributes(assetPath) & FileAttributes.ReadOnly) != 0;
}
return false;
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0303d12aca9843d3bd68adf722d9c9fd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class FrameRateDisplayUtility
{
private static string[] s_FrameRateLabels;
public static bool GetStandardFromFrameRate(double frameRate, out StandardFrameRates standard)
{
FrameRate frameRateObj = TimeUtility.GetClosestFrameRate(RoundFrameRate(frameRate));
return TimeUtility.ToStandardFrameRate(frameRateObj, out standard);
}
public static double RoundFrameRate(double frameRate)
{
double trunc = Math.Truncate(frameRate * (1 / TimeUtility.kFrameRateRounding)) * TimeUtility.kFrameRateRounding;
return Math.Min(Math.Max(TimelineAsset.EditorSettings.kMinFrameRate, trunc),
TimelineAsset.EditorSettings.kMaxFrameRate);
}
public static string[] GetDefaultFrameRatesLabels(StandardFrameRates option)
{
string[] labels;
if (s_FrameRateLabels == null || !s_FrameRateLabels.Any())
{
var frameRates = (StandardFrameRates[])Enum.GetValues(typeof(StandardFrameRates));
labels = Array.ConvertAll(frameRates, GetLabelForStandardFrameRate);
s_FrameRateLabels = labels;
}
else
{
labels = s_FrameRateLabels;
}
if (!Enum.IsDefined(typeof(StandardFrameRates), option))
{
Array.Resize(ref labels, (int)option + 1);
labels[(int)option] = GetLabelForStandardFrameRate(option);
}
return labels;
}
static string GetLabelForStandardFrameRate(StandardFrameRates option)
{
switch (option)
{
case StandardFrameRates.Fps23_97:
return L10n.Tr("Film NTSC: 23.97 fps");
case StandardFrameRates.Fps24:
return L10n.Tr("Film: 24 fps");
case StandardFrameRates.Fps25:
return L10n.Tr("PAL: 25 fps");
case StandardFrameRates.Fps29_97:
return L10n.Tr("NTSC: 29.97 fps");
case StandardFrameRates.Fps30:
return L10n.Tr("HD: 30 fps");
case StandardFrameRates.Fps50:
return L10n.Tr("Interlaced PAL: 50 fps");
case StandardFrameRates.Fps59_94:
return L10n.Tr("Interlaced NTSC: 59.94 fps");
case StandardFrameRates.Fps60:
return L10n.Tr("Game: 60 fps");
default:
return L10n.Tr("Custom");
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e6372298ccbe45b9a771c50dd0293b6e
timeCreated: 1612896067

View File

@@ -0,0 +1,121 @@
using UnityEngine;
namespace UnityEditor.Timeline
{
static class Graphics
{
public static void ShadowLabel(Rect rect, string text, GUIStyle style, Color textColor, Color shadowColor)
{
ShadowLabel(rect, GUIContent.Temp(text), style, textColor, shadowColor);
}
public static void ShadowLabel(Rect rect, GUIContent content, GUIStyle style, Color textColor, Color shadowColor)
{
var shadowRect = rect;
shadowRect.xMin += 2.0f;
shadowRect.yMin += 2.0f;
style.normal.textColor = shadowColor;
style.hover.textColor = shadowColor;
GUI.Label(shadowRect, content, style);
style.normal.textColor = textColor;
style.hover.textColor = textColor;
GUI.Label(rect, content, style);
}
public static void DrawLine(Vector3 p1, Vector3 p2, Color color)
{
var c = Handles.color;
Handles.color = color;
Handles.DrawLine(p1, p2);
Handles.color = c;
}
public static void DrawPolygonAA(Color color, Vector3[] vertices)
{
var prevColor = Handles.color;
Handles.color = color;
Handles.DrawAAConvexPolygon(vertices);
Handles.color = prevColor;
}
public static void DrawDottedLine(Vector3 p1, Vector3 p2, float segmentsLength, Color col)
{
HandleUtility.ApplyWireMaterial();
GL.Begin(GL.LINES);
GL.Color(col);
var length = Vector3.Distance(p1, p2); // ignore z component
var count = Mathf.CeilToInt(length / segmentsLength);
for (var i = 0; i < count; i += 2)
{
GL.Vertex((Vector3.Lerp(p1, p2, i * segmentsLength / length)));
GL.Vertex((Vector3.Lerp(p1, p2, (i + 1) * segmentsLength / length)));
}
GL.End();
}
public static void DrawLineAtTime(WindowState state, double time, Color color, bool dotted = false)
{
var t = state.TimeToPixel(time);
var p0 = new Vector3(t, state.timeAreaRect.yMax);
var p1 = new Vector3(t, state.timeAreaRect.yMax + state.windowHeight - WindowConstants.sliderWidth);
if (dotted)
DrawDottedLine(p0, p1, 4.0f, color);
else
DrawLine(p0, p1, color);
}
public static void DrawTextureRepeated(Rect area, Texture texture)
{
if (texture == null || Event.current.type != EventType.Repaint)
return;
GUI.BeginClip(area);
int w = Mathf.CeilToInt(area.width / texture.width);
int h = Mathf.CeilToInt(area.height / texture.height);
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
GUI.DrawTexture(new Rect(x * texture.width, y * texture.height, texture.width, texture.height), texture);
}
}
GUI.EndClip();
}
public static void DrawShadow(Rect clientRect)
{
var rect = clientRect;
rect.height = WindowConstants.shadowUnderTimelineHeight;
GUI.Box(rect, GUIContent.none, DirectorStyles.Instance.bottomShadow);
}
public static void DrawBackgroundRect(WindowState state, Rect rect, bool subSequenceMode = false)
{
Color c = subSequenceMode ? DirectorStyles.Instance.customSkin.colorSubSequenceBackground : DirectorStyles.Instance.customSkin.colorSequenceBackground;
EditorGUI.DrawRect(rect, c);
if (state.IsEditingAPrefabAsset())
{
c = SceneView.kSceneViewPrefabBackground.Color;
c.a = 0.5f;
EditorGUI.DrawRect(rect, c);
}
}
public static Rect CalculateTextBoxSize(Rect trackRect, GUIStyle font, GUIContent content, float padding)
{
Rect textRect = trackRect;
textRect.width = font.CalcSize(content).x + padding;
textRect.x += (trackRect.width - textRect.width) / 2f;
textRect.height -= 4f;
textRect.y += 2f;
return textRect;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4545bb65ccebf8040ac212d5792979b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Utilities
{
class KeyTraverser
{
float[] m_KeyCache;
int m_DirtyStamp = -1;
int m_LastHash = -1;
readonly TimelineAsset m_Asset;
readonly float m_Epsilon;
int m_LastIndex = -1;
public int lastIndex
{
get { return m_LastIndex; }
}
public static IEnumerable<float> GetClipKeyTimes(TimelineClip clip)
{
if (clip == null || clip.animationClip == null || clip.animationClip.empty)
return new float[0];
return AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip).keyTimes.
Select(k => (float)clip.FromLocalTimeUnbound(k)). // convert to sequence time
Where(k => k >= clip.start && k <= clip.end); // remove non visible keys
}
public static IEnumerable<float> GetTrackKeyTimes(AnimationTrack track)
{
if (track != null)
{
if (track.inClipMode)
return track.clips.Where(c => c.recordable).
SelectMany(x => GetClipKeyTimes(x));
if (track.infiniteClip != null && !track.infiniteClip.empty)
return AnimationClipCurveCache.Instance.GetCurveInfo(track.infiniteClip).keyTimes;
}
return new float[0];
}
static int CalcAnimClipHash(TrackAsset asset)
{
int hash = 0;
if (asset != null)
{
AnimationTrack animTrack = asset as AnimationTrack;
if (animTrack != null)
{
for (var i = 0; i != animTrack.clips.Length; ++i)
{
hash ^= (animTrack.clips[i]).Hash();
}
}
foreach (var subTrack in asset.GetChildTracks())
{
if (subTrack != null)
hash ^= CalcAnimClipHash(subTrack);
}
}
return hash;
}
internal static int CalcAnimClipHash(TimelineAsset asset)
{
int hash = 0;
foreach (var t in asset.GetRootTracks())
{
if (t != null)
hash ^= CalcAnimClipHash(t);
}
return hash;
}
void RebuildKeyCache()
{
m_KeyCache = m_Asset.flattenedTracks.Where(x => (x as AnimationTrack) != null)
.Cast<AnimationTrack>()
.SelectMany(t => GetTrackKeyTimes(t)).
OrderBy(x => x).ToArray();
if (m_KeyCache.Length > 0)
{
float[] unique = new float[m_KeyCache.Length];
unique[0] = m_KeyCache[0];
int index = 0;
for (int i = 1; i < m_KeyCache.Length; i++)
{
if (m_KeyCache[i] - unique[index] > m_Epsilon)
{
index++;
unique[index] = m_KeyCache[i];
}
}
m_KeyCache = unique;
Array.Resize(ref m_KeyCache, index + 1);
}
}
public KeyTraverser(TimelineAsset timeline, float epsilon)
{
m_Asset = timeline;
m_Epsilon = epsilon;
}
void CheckCache(int dirtyStamp)
{
int hash = CalcAnimClipHash(m_Asset);
if (dirtyStamp != m_DirtyStamp || hash != m_LastHash)
{
RebuildKeyCache();
m_DirtyStamp = dirtyStamp;
m_LastHash = hash;
}
}
public float GetNextKey(float key, int dirtyStamp)
{
CheckCache(dirtyStamp);
if (m_KeyCache.Length > 0)
{
if (key < m_KeyCache.Last() - m_Epsilon)
{
if (key > m_KeyCache[0] - m_Epsilon)
{
float t = key + m_Epsilon;
// binary search
int max = m_KeyCache.Length - 1;
int min = 0;
while (max - min > 1)
{
int imid = (min + max) / 2;
if (t > m_KeyCache[imid])
min = imid;
else
max = imid;
}
m_LastIndex = max;
return m_KeyCache[max];
}
m_LastIndex = 0;
return m_KeyCache[0];
}
if (key < m_KeyCache.Last() + m_Epsilon)
{
m_LastIndex = m_KeyCache.Length - 1;
return Mathf.Max(key, m_KeyCache.Last());
}
}
m_LastIndex = -1;
return key;
}
public float GetPrevKey(float key, int dirtyStamp)
{
CheckCache(dirtyStamp);
if (m_KeyCache.Length > 0)
{
if (key > m_KeyCache[0] + m_Epsilon)
{
if (key < m_KeyCache.Last() + m_Epsilon)
{
float t = key - m_Epsilon;
// binary search
int max = m_KeyCache.Length - 1;
int min = 0;
while (max - min > 1)
{
int imid = (min + max) / 2;
if (t < m_KeyCache[imid])
max = imid;
else
min = imid;
}
m_LastIndex = min;
return m_KeyCache[min];
}
m_LastIndex = m_KeyCache.Length - 1;
return m_KeyCache.Last();
}
if (key >= m_KeyCache[0] - m_Epsilon)
{
m_LastIndex = 0;
return Mathf.Min(key, m_KeyCache[0]);
}
}
m_LastIndex = -1;
return key;
}
public int GetKeyCount(int dirtyStamp)
{
CheckCache(dirtyStamp);
return m_KeyCache.Length;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b57f909f22642d469a39e9628535312
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
static class MarkerModifier
{
public static void DeleteMarker(IMarker marker)
{
var trackAsset = marker.parent;
if (trackAsset != null)
{
SelectionManager.Remove(marker);
trackAsset.DeleteMarker(marker);
}
}
public static IEnumerable<IMarker> CloneMarkersToParent(IEnumerable<IMarker> markers, TrackAsset parent)
{
if (!markers.Any()) return Enumerable.Empty<IMarker>();
var clonedMarkers = new List<IMarker>();
foreach (var marker in markers)
clonedMarkers.Add(CloneMarkerToParent(marker, parent));
return clonedMarkers;
}
public static IMarker CloneMarkerToParent(IMarker marker, TrackAsset parent)
{
var markerObject = marker as ScriptableObject;
if (markerObject == null) return null;
var newMarkerObject = Object.Instantiate(markerObject);
AddMarkerToParent(newMarkerObject, parent);
newMarkerObject.name = markerObject.name;
try
{
CustomTimelineEditorCache.GetMarkerEditor((IMarker)newMarkerObject).OnCreate((IMarker)newMarkerObject, marker);
}
catch (Exception e)
{
Debug.LogException(e);
}
return (IMarker)newMarkerObject;
}
static void AddMarkerToParent(ScriptableObject marker, TrackAsset parent)
{
TimelineCreateUtilities.SaveAssetIntoObject(marker, parent);
TimelineUndo.RegisterCreatedObjectUndo(marker, L10n.Tr("Duplicate Marker"));
UndoExtensions.RegisterTrack(parent, L10n.Tr("Duplicate Marker"));
if (parent != null)
{
parent.AddMarker(marker);
((IMarker)marker).Initialize(parent);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7cfaad4e53832d94c9421d2dd1ad82f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Timeline
{
static class ObjectExtension
{
public static bool IsSceneObject(this Object obj)
{
if (obj == null)
return false;
bool isSceneType = obj is GameObject || obj is Component;
if (!isSceneType)
return false;
return !PrefabUtility.IsPartOfPrefabAsset(obj);
}
public static bool IsPrefab(this Object obj)
{
if (obj == null)
return false;
return PrefabUtility.IsPartOfPrefabAsset(obj);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4722a1362908a1843ab03a055c5c3fa0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.Playables;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
// Describes the object references on a ScriptableObject, ignoring script fields
struct ObjectReferenceField
{
public string propertyPath;
public bool isSceneReference;
public System.Type type;
private readonly static ObjectReferenceField[] None = new ObjectReferenceField[0];
private readonly static Dictionary<System.Type, ObjectReferenceField[]> s_Cache = new Dictionary<System.Type, ObjectReferenceField[]>();
public static ObjectReferenceField[] FindObjectReferences(System.Type type)
{
if (type == null)
return None;
if (type.IsAbstract || type.IsInterface)
return None;
if (!typeof(ScriptableObject).IsAssignableFrom(type))
return None;
ObjectReferenceField[] result = null;
if (s_Cache.TryGetValue(type, out result))
return result;
result = SearchForFields(type);
s_Cache[type] = result;
return result;
}
public static ObjectReferenceField[] FindObjectReferences<T>() where T : ScriptableObject, new()
{
return FindObjectReferences(typeof(T));
}
private static ObjectReferenceField[] SearchForFields(System.Type t)
{
Object instance = ScriptableObject.CreateInstance(t);
var list = new List<ObjectReferenceField>();
var serializableObject = new SerializedObject(instance);
var prop = serializableObject.GetIterator();
bool enterChildren = true;
while (prop.NextVisible(enterChildren))
{
enterChildren = true;
var ppath = prop.propertyPath;
if (ppath == "m_Script")
{
enterChildren = false;
}
else if (prop.propertyType == SerializedPropertyType.ObjectReference || prop.propertyType == SerializedPropertyType.ExposedReference)
{
enterChildren = false;
var exposedType = GetTypeFromPath(t, prop.propertyPath);
if (exposedType != null && typeof(Object).IsAssignableFrom(exposedType))
{
bool isSceneRef = prop.propertyType == SerializedPropertyType.ExposedReference;
list.Add(
new ObjectReferenceField() { propertyPath = prop.propertyPath, isSceneReference = isSceneRef, type = exposedType }
);
}
}
}
Object.DestroyImmediate(instance);
if (list.Count == 0)
return None;
return list.ToArray();
}
private static System.Type GetTypeFromPath(System.Type baseType, string path)
{
if (string.IsNullOrEmpty(path))
return null;
System.Type parentType = baseType;
FieldInfo field = null;
var pathTo = path.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
var flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance;
foreach (string s in pathTo)
{
field = parentType.GetField(s, flags);
while (field == null)
{
if (parentType.BaseType == null)
return null; // Should not happen really. Means SerializedObject got the property, but the reflection missed it
parentType = parentType.BaseType;
field = parentType.GetField(s, flags);
}
parentType = field.FieldType;
}
// dig out exposed reference types
if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(ExposedReference<Object>).GetGenericTypeDefinition())
{
return field.FieldType.GetGenericArguments()[0];
}
return field.FieldType;
}
public Object Find(ScriptableObject sourceObject, Object context = null)
{
if (sourceObject == null)
return null;
SerializedObject obj = new SerializedObject(sourceObject, context);
var prop = obj.FindProperty(propertyPath);
if (prop == null)
throw new InvalidOperationException("sourceObject is not of the proper type. It does not contain a path to " + propertyPath);
Object result = null;
if (isSceneReference)
{
if (prop.propertyType != SerializedPropertyType.ExposedReference)
throw new InvalidOperationException(propertyPath + " is marked as a Scene Reference, but is not an exposed reference type");
if (context == null)
Debug.LogWarning("ObjectReferenceField.Find " + " is called on a scene reference without a context, will always be null");
result = prop.exposedReferenceValue;
}
else
{
if (prop.propertyType != SerializedPropertyType.ObjectReference)
throw new InvalidOperationException(propertyPath + "is marked as an asset reference, but is not an object reference type");
result = prop.objectReferenceValue;
}
return result;
}
/// <summary>
/// Check if an Object satisfies this field, including components
/// </summary>
public bool IsAssignable(Object obj)
{
if (obj == null)
return false;
// types match
bool potentialMatch = type.IsAssignableFrom(obj.GetType());
// field is component, and it exists on the gameObject
if (!potentialMatch && typeof(Component).IsAssignableFrom(type) && obj is GameObject)
potentialMatch = ((GameObject)obj).GetComponent(type) != null;
return potentialMatch && isSceneReference == obj.IsSceneObject();
}
/// <summary>
/// Assigns a value to the field
/// </summary>
public bool Assign(ScriptableObject scriptableObject, Object value, IExposedPropertyTable exposedTable = null)
{
var serializedObject = new SerializedObject(scriptableObject, exposedTable as Object);
var property = serializedObject.FindProperty(propertyPath);
if (property == null)
return false;
// if the value is a game object, but the field is a component
if (value is GameObject && typeof(Component).IsAssignableFrom(type))
value = ((GameObject)value).GetComponent(type);
if (isSceneReference)
{
property.exposedReferenceValue = value;
// the object gets dirtied, but not the scene which is where the reference is stored
var component = exposedTable as Component;
if (component != null && !EditorApplication.isPlaying)
EditorSceneManager.MarkSceneDirty(component.gameObject.scene);
}
else
property.objectReferenceValue = value;
serializedObject.ApplyModifiedProperties();
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29bf1d4ec1012bc45967ce95b729b8b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Playables;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
readonly struct PreviewedBindings<T> where T : Object
{
readonly IEnumerable<T> m_UniqueBindings;
readonly IReadOnlyDictionary<Object, HashSet<T>> m_BindingsPerObject;
PreviewedBindings(IEnumerable<T> uniqueBindings, IReadOnlyDictionary<Object, HashSet<T>> bindingsPerObject)
{
m_UniqueBindings = uniqueBindings;
m_BindingsPerObject = bindingsPerObject;
}
public IEnumerable<T> GetUniqueBindings() => m_UniqueBindings ?? Enumerable.Empty<T>();
public IEnumerable<T> GetBindingsForObject(Object track)
{
if (m_BindingsPerObject != null && m_BindingsPerObject.TryGetValue(track, out HashSet<T> bindings))
return bindings;
return Enumerable.Empty<T>();
}
public static PreviewedBindings<T> GetPreviewedBindings(IEnumerable<PlayableDirector> directors)
{
var uniqueBindings = new HashSet<T>();
var bindingsPerTrack = new Dictionary<Object, HashSet<T>>();
foreach (PlayableDirector director in directors)
{
if (director.playableAsset == null) continue;
foreach (PlayableBinding output in director.playableAsset.outputs)
{
var binding = director.GetGenericBinding(output.sourceObject) as T;
Add(output.sourceObject, binding, uniqueBindings, bindingsPerTrack);
}
}
return new PreviewedBindings<T>(uniqueBindings, bindingsPerTrack);
}
static void Add(Object obj, T binding, HashSet<T> bindings, Dictionary<Object, HashSet<T>> bindingsPerObject)
{
if (binding == null)
return;
bindings.Add(binding);
if (bindingsPerObject.TryGetValue(obj, out HashSet<T> objectBindings))
objectBindings.Add(binding);
else
bindingsPerObject.Add(obj, new HashSet<T> { binding });
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 907eeaaade2f4a3c9b079743c2bf77ee
timeCreated: 1686150636

View File

@@ -0,0 +1,226 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
class PropertyCollector : IPropertyCollector
{
readonly Stack<GameObject> m_ObjectStack = new Stack<GameObject>();
// Call immediately before use
public void Reset()
{
m_ObjectStack.Clear();
}
// call to reset caches. should be called when switching master timelines
public void Clear()
{
m_ObjectStack.Clear();
AnimationPreviewUtilities.ClearCaches();
}
public void PushActiveGameObject(GameObject gameObject)
{
m_ObjectStack.Push(gameObject);
}
public void PopActiveGameObject()
{
m_ObjectStack.Pop();
}
public void AddFromClip(AnimationClip clip)
{
var go = m_ObjectStack.Peek(); // allow it to throw if empty
if (go != null && clip != null) // null game object is allowed for calls to be ignored
AddFromClip(go, clip);
}
public void AddFromClips(IEnumerable<AnimationClip> clips)
{
var go = m_ObjectStack.Peek();
if (go != null)
AddFromClips(go, clips);
}
public void AddFromName<T>(string name) where T : Component
{
var go = m_ObjectStack.Peek(); // allow it to throw if empty
if (go != null) // null game object is allowed for calls to be ignored
AddFromName<T>(go, name);
}
public void AddFromName(string name)
{
var go = m_ObjectStack.Peek(); // allow it to throw if empty
if (go != null) // null game object is allowed for calls to be ignored
AddFromName(go, name);
}
public void AddFromClip(GameObject obj, AnimationClip clip)
{
if (!Application.isPlaying)
AddPropertiesFromClip(obj, clip);
}
public void AddFromClips(GameObject animatorRoot, IEnumerable<AnimationClip> clips)
{
if (Application.isPlaying)
return;
AnimationPreviewUtilities.PreviewFromCurves(animatorRoot, AnimationPreviewUtilities.GetBindings(animatorRoot, clips));
}
public void AddFromName<T>(GameObject obj, string name) where T : Component
{
if (!Application.isPlaying)
AddPropertiesFromName(obj, typeof(T), name);
}
public void AddFromName(GameObject obj, string name)
{
if (!Application.isPlaying)
AddPropertiesFromName(obj, name);
}
public void AddFromName(Component component, string name)
{
if (!Application.isPlaying)
AddPropertyModification(component, name);
}
public void AddFromComponent(GameObject obj, Component component)
{
if (Application.isPlaying)
return;
if (obj == null || component == null)
return;
var serializedObject = new SerializedObject(component);
SerializedProperty property = serializedObject.GetIterator();
while (property.NextVisible(true))
{
if (property.hasVisibleChildren || !AnimatedParameterUtility.IsTypeAnimatable(property.propertyType))
continue;
AddPropertyModification(component, property.propertyPath);
}
}
void AddPropertiesFromClip(GameObject go, AnimationClip clip)
{
if (go != null && clip != null)
{
AnimationMode.InitializePropertyModificationForGameObject(go, clip);
}
}
static void AddPropertiesFromName(GameObject go, string property)
{
if (go == null)
return;
AddPropertyModification(go, property);
}
static void AddPropertiesFromName(GameObject go, Type compType, string property)
{
if (go == null)
return;
var comp = go.GetComponent(compType);
if (comp == null)
return;
AddPropertyModification(comp, property);
}
public void AddObjectProperties(Object obj, AnimationClip clip)
{
if (obj == null || clip == null)
return;
IPlayableAsset asset = obj as IPlayableAsset;
IPlayableBehaviour playable = obj as IPlayableBehaviour;
// special case for assets that contain animated script playables.
// The paths in the clip start from the field with the templated playable
if (asset != null)
{
if (playable == null)
{
AddSerializedPlayableModifications(asset, clip);
}
else
{
// in this case the asset is the playable. The clip applies directly
AnimationMode.InitializePropertyModificationForObject(obj, clip);
}
}
}
void AddSerializedPlayableModifications(IPlayableAsset asset, AnimationClip clip)
{
var obj = asset as Object;
if (obj == null)
return;
var driver = WindowState.previewDriver;
if (driver == null || !AnimationMode.InAnimationMode(driver))
return;
var serializedObj = new SerializedObject(obj);
var bindings = AnimationClipCurveCache.Instance.GetCurveInfo(clip).bindings;
var fields = AnimatedParameterUtility.GetScriptPlayableFields(asset);
// go through each binding and offset using the field name
// so the modification system can find the particle object using the asset as a root
foreach (var b in bindings)
{
foreach (var f in fields)
{
var propertyPath = f.Name + "." + b.propertyName;
if (serializedObj.FindProperty(propertyPath) != null)
{
DrivenPropertyManager.RegisterProperty(driver, obj, propertyPath);
break;
}
}
}
}
private static void AddPropertyModification(GameObject obj, string propertyName)
{
var driver = WindowState.previewDriver;
if (driver == null || !AnimationMode.InAnimationMode(driver))
return;
DrivenPropertyManager.RegisterProperty(driver, obj, propertyName);
}
private static void AddPropertyModification(Component comp, string name)
{
if (comp == null)
return;
var driver = WindowState.previewDriver;
if (driver == null || !AnimationMode.InAnimationMode(driver))
return;
// Register Property will display an error if a property doesn't exist (wanted behaviour)
// However, it also displays an error on Monobehaviour m_Script property, since it can't be driven. (not wanted behaviour)
// case 967026
if (name == "m_Script" && (comp as MonoBehaviour) != null)
return;
DrivenPropertyManager.RegisterProperty(driver, comp, name);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f3a562675833b4448299e4f627b0cec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
struct Range
{
public double start;
public double end;
public double length { get { return end - start; } }
public static Range Union(Range lhs, Range rhs)
{
return new Range
{
start = Math.Min(lhs.start, rhs.start),
end = Math.Max(lhs.end, rhs.end)
};
}
public static Range Intersection(Range lhs, Range rhs)
{
var s = Math.Max(lhs.start, rhs.start);
var e = Math.Min(lhs.end, rhs.end);
if (s > e)
{
// No intersection returns a 0-length range from 0 to 0
return new Range();
}
return new Range
{
start = s,
end = e
};
}
public override string ToString()
{
return ToString("F3");
}
public string ToString(string format)
{
return UnityString.Format("({0}, {1})", start.ToString(format), end.ToString(format));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d31dfeaa131921f4eae00783cc48146f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d55400f78d024c05b04dc124bb181d9e
timeCreated: 1605887929

View File

@@ -0,0 +1,21 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
struct GUIColorOverride : IDisposable
{
readonly Color m_OldColor;
public GUIColorOverride(Color newColor)
{
m_OldColor = GUI.color;
GUI.color = newColor;
}
public void Dispose()
{
GUI.color = m_OldColor;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44507a833d0ca8a42aaec1c3d752eb5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using System;
using UnityEngine;
namespace UnityEditor
{
struct GUIGroupScope : IDisposable
{
public GUIGroupScope(Rect position)
{
GUI.BeginGroup(position);
}
public void Dispose()
{
GUI.EndGroup();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bee8aba5e8a40446b7098666c5314d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
using System;
using UnityEngine;
namespace UnityEditor
{
struct GUIMixedValueScope : IDisposable
{
readonly bool m_PrevValue;
public GUIMixedValueScope(bool newValue)
{
m_PrevValue = EditorGUI.showMixedValue;
EditorGUI.showMixedValue = newValue;
}
public void Dispose()
{
EditorGUI.showMixedValue = m_PrevValue;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d59cefc45e3c31d4a90563364e7258fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
using System;
using UnityEngine;
namespace UnityEditor
{
// Special Clip Scope that only effects painting, and keeps the coordinate system identical
struct GUIViewportScope : IDisposable
{
bool m_open;
public GUIViewportScope(Rect position)
{
m_open = false;
if (Event.current.type == EventType.Repaint || Event.current.type == EventType.Layout)
{
GUI.BeginClip(position, -position.min, Vector2.zero, false);
m_open = true;
}
}
public void Dispose()
{
CloseScope();
}
void CloseScope()
{
if (m_open)
{
GUI.EndClip();
m_open = false;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af84cf39b8fa0654badd9278cbd00d77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
readonly struct HorizontalScope : IDisposable
{
public readonly Rect rect;
public HorizontalScope(GUIContent content, GUIStyle style)
{
rect = EditorGUILayout.BeginHorizontal(content, style);
}
public void Dispose()
{
EditorGUILayout.EndHorizontal();
}
}
}

Some files were not shown because too many files have changed in this diff Show More