123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- #if UNITY_EDITOR
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using Object = UnityEngine.Object;
- namespace Animancer.Editor
- {
-
-
-
-
-
- public class AnimationGatherer : IAnimationClipCollection
- {
-
- #region Recursion Guard
-
- private const int MaxFieldDepth = 7;
-
- private static readonly HashSet<object>
- RecursionGuard = new HashSet<object>();
- private static int _CallCount;
- private static bool BeginRecursionGuard(object obj)
- {
- if (RecursionGuard.Contains(obj))
- return false;
- RecursionGuard.Add(obj);
- return true;
- }
- private static void EndCall()
- {
- if (_CallCount == 0)
- RecursionGuard.Clear();
- }
-
- #endregion
-
- #region Fields and Accessors
-
-
- public readonly HashSet<AnimationClip> Clips = new HashSet<AnimationClip>();
-
- public readonly HashSet<ITransition> Transitions = new HashSet<ITransition>();
-
-
- public void GatherAnimationClips(ICollection<AnimationClip> clips)
- {
- try
- {
- foreach (var clip in Clips)
- clips.Add(clip);
- foreach (var transition in Transitions)
- clips.GatherFromSource(transition);
- }
- catch (Exception exception)
- {
- HandleException(exception);
- }
- }
-
- #endregion
-
- #region Cache
-
- private static readonly Dictionary<GameObject, AnimationGatherer>
- ObjectToGatherer = new Dictionary<GameObject, AnimationGatherer>();
-
- static AnimationGatherer()
- {
- UnityEditor.EditorApplication.hierarchyChanged += ClearCache;
- UnityEditor.Selection.selectionChanged += ClearCache;
- }
-
-
- public static void ClearCache() => ObjectToGatherer.Clear();
-
- #endregion
-
-
- public static bool logExceptions;
-
- private static void HandleException(Exception exception)
- {
- if (logExceptions)
- Debug.LogException(exception);
- }
-
-
-
-
-
- public static AnimationGatherer GatherFromGameObject(GameObject gameObject)
- {
- if (!BeginRecursionGuard(gameObject))
- return null;
- try
- {
- _CallCount++;
- if (!ObjectToGatherer.TryGetValue(gameObject, out var gatherer))
- {
- gatherer = new AnimationGatherer();
- ObjectToGatherer.Add(gameObject, gatherer);
- gatherer.GatherFromComponents(gameObject);
- }
- return gatherer;
- }
- catch (Exception exception)
- {
- HandleException(exception);
- return null;
- }
- finally
- {
- _CallCount--;
- EndCall();
- }
- }
-
-
-
-
- public static void GatherFromGameObject(GameObject gameObject, ICollection<AnimationClip> clips)
- {
- var gatherer = GatherFromGameObject(gameObject);
- gatherer?.GatherAnimationClips(clips);
- }
-
-
-
-
- public static void GatherFromGameObject(GameObject gameObject, ref AnimationClip[] clips, bool sort)
- {
- var gatherer = GatherFromGameObject(gameObject);
- if (gatherer == null)
- return;
- using (ObjectPool.Disposable.AcquireSet<AnimationClip>(out var clipSet))
- {
- gatherer.GatherAnimationClips(clipSet);
- AnimancerUtilities.SetLength(ref clips, clipSet.Count);
- clipSet.CopyTo(clips);
- }
- if (sort)
- Array.Sort(clips, (a, b) => a.name.CompareTo(b.name));
- }
-
- private void GatherFromComponents(GameObject gameObject)
- {
- var root = AnimancerEditorUtilities.FindRoot(gameObject);
- using (ObjectPool.Disposable.AcquireList<MonoBehaviour>(out var components))
- {
- root.GetComponentsInChildren(true, components);
- GatherFromComponents(components);
- }
- }
-
- private void GatherFromComponents(List<MonoBehaviour> components)
- {
- var i = components.Count;
- GatherClips:
- try
- {
- while (--i >= 0)
- {
- GatherFromObject(components[i], 0);
- }
- }
- catch (Exception exception)
- {
- HandleException(exception);
- goto GatherClips;
- }
- }
-
-
- private void GatherFromObject(object source, int depth)
- {
- if (source == null)
- return;
- if (source is AnimationClip clip)
- {
- Clips.Add(clip);
- return;
- }
- if (!MightContainAnimations(source.GetType()))
- return;
- if (!BeginRecursionGuard(source))
- return;
- try
- {
- if (Clips.GatherFromSource(source))
- return;
- }
- catch (Exception exception)
- {
- HandleException(exception);
- }
- finally
- {
- RecursionGuard.Remove(source);
- }
- GatherFromFields(source, depth);
- }
-
-
- private static readonly Dictionary<Type, Action<object, AnimationGatherer>>
- TypeToGathererDelegate = new Dictionary<Type, Action<object, AnimationGatherer>>();
-
-
-
- private void GatherFromFields(object source, int depth)
- {
- if (depth >= MaxFieldDepth ||
- source == null ||
- !BeginRecursionGuard(source))
- return;
- var type = source.GetType();
- if (!TypeToGathererDelegate.TryGetValue(type, out var gatherClips))
- {
- gatherClips = BuildClipGathererDelegate(type, depth);
- TypeToGathererDelegate.Add(type, gatherClips);
- }
- gatherClips?.Invoke(source, this);
- }
-
-
-
-
- private static Action<object, AnimationGatherer> BuildClipGathererDelegate(Type type, int depth)
- {
- if (!MightContainAnimations(type))
- return null;
- Action<object, AnimationGatherer> gathererDelegate = null;
- while (type != null)
- {
- var fields = type.GetFields(AnimancerEditorUtilities.InstanceBindings);
- for (int i = 0; i < fields.Length; i++)
- {
- var field = fields[i];
- var fieldType = field.FieldType;
- if (!MightContainAnimations(fieldType))
- continue;
- if (fieldType == typeof(AnimationClip))
- {
- gathererDelegate += (obj, gatherer) =>
- {
- var clip = (AnimationClip)field.GetValue(obj);
- gatherer.Clips.Gather(clip);
- };
- }
- else if (typeof(IAnimationClipSource).IsAssignableFrom(fieldType) ||
- typeof(IAnimationClipCollection).IsAssignableFrom(fieldType))
- {
- gathererDelegate += (obj, gatherer) =>
- {
- var source = field.GetValue(obj);
- gatherer.Clips.GatherFromSource(source);
- };
- }
- else if (typeof(ICollection).IsAssignableFrom(fieldType))
- {
- gathererDelegate += (obj, gatherer) =>
- {
- var collection = (ICollection)field.GetValue(obj);
- if (collection != null)
- {
- foreach (var item in collection)
- {
- gatherer.GatherFromObject(item, depth + 1);
- }
- }
- };
- }
- else
- {
- gathererDelegate += (obj, gatherer) =>
- {
- var source = field.GetValue(obj);
- if (source == null ||
- (source is Object sourceObject && sourceObject == null))
- return;
- gatherer.GatherFromObject(source, depth + 1);
- };
- }
- }
- type = type.BaseType;
- }
- return gathererDelegate;
- }
-
- private static bool MightContainAnimations(Type type)
- {
- return
- !type.IsPrimitive &&
- !type.IsEnum &&
- !type.IsAutoClass &&
- !type.IsPointer;
- }
-
- }
- }
- #endif
|