123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- #if UNITY_EDITOR
- using Animancer.TransitionLibraries;
- using System;
- using System.Collections.Generic;
- using UnityEditor;
- using UnityEngine;
- namespace Animancer.Editor.TransitionLibraries
- {
- /// <summary>[Editor-Only] Utility for sorting a <see cref="TransitionLibraryAsset"/>.</summary>
- /// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibrarySort
- public class TransitionLibrarySort : AssetModificationProcessor
- {
- /************************************************************************************************************************/
- #region Automation
- /************************************************************************************************************************/
- /// <summary>Ensures that a <see cref="TransitionLibraryAsset"/> is sorted before being saved.</summary>
- private static string[] OnWillSaveAssets(string[] paths)
- {
- foreach (var path in paths)
- {
- if (!path.EndsWith(".asset", StringComparison.Ordinal))
- continue;
- var library = AssetDatabase.LoadAssetAtPath<TransitionLibraryAsset>(path);
- if (library == null)
- continue;
- Sort(library);
- }
- return paths;
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Sort Modes
- /************************************************************************************************************************/
- /// <summary>Applies the <see cref="TransitionLibraryEditorData.TransitionSortMode"/>.</summary>
- public static void Sort(TransitionLibraryAsset library)
- {
- // Can't have editor data if not an asset, so the sort mode will be custom anyway.
- if (!AssetDatabase.Contains(library))
- return;
- var data = library.GetOrCreateEditorData();
- if (data.TransitionSortMode == TransitionSortMode.Custom)
- return;
- NameCache.Clear();
- switch (data.TransitionSortMode)
- {
- case TransitionSortMode.Name:
- Sort(library.Definition, Static<CompareName>.Instance);
- break;
- case TransitionSortMode.Path:
- Sort(library.Definition, Static<ComparePath>.Instance);
- break;
- case TransitionSortMode.TypeThenName:
- Sort(library.Definition, Static<CompareTypeThenName>.Instance);
- break;
- case TransitionSortMode.TypeThenPath:
- Sort(library.Definition, Static<CompareTypeThenPath>.Instance);
- break;
- }
- }
- /************************************************************************************************************************/
- /// <summary>Compares the asset names then GUIDs.</summary>
- private class CompareName : IComparer<TransitionAssetBase>
- {
- public int Compare(TransitionAssetBase a, TransitionAssetBase b)
- {
- var result = CompareNulls(a, b);
- if (result != 0)
- return result;
- result = CompareCachedNames(a, b);
- if (result != 0)
- return result;
- return CompareGUIDs(a, b);
- }
- }
- /************************************************************************************************************************/
- /// <summary>Compares the asset paths then GUIDs.</summary>
- private class ComparePath : IComparer<TransitionAssetBase>
- {
- public int Compare(TransitionAssetBase a, TransitionAssetBase b)
- {
- var result = CompareNulls(a, b);
- if (result != 0)
- return result;
- result = ComparePaths(a, b);
- if (result != 0)
- return result;
- result = CompareCachedNames(a, b);
- if (result != 0)
- return result;
- return CompareGUIDs(a, b);
- }
- }
- /************************************************************************************************************************/
- /// <summary>Compares the transition types then asset names then GUIDs.</summary>
- private class CompareTypeThenName : IComparer<TransitionAssetBase>
- {
- public int Compare(TransitionAssetBase a, TransitionAssetBase b)
- {
- var result = CompareNulls(a, b);
- if (result != 0)
- return result;
- result = CompareTypes(a, b);
- if (result != 0)
- return result;
- result = CompareCachedNames(a, b);
- if (result != 0)
- return result;
- return CompareGUIDs(a, b);
- }
- }
- /************************************************************************************************************************/
- /// <summary>Compares the transition types then asset paths then GUIDs.</summary>
- private class CompareTypeThenPath : IComparer<TransitionAssetBase>
- {
- public int Compare(TransitionAssetBase a, TransitionAssetBase b)
- {
- var result = CompareNulls(a, b);
- if (result != 0)
- return result;
- result = CompareTypes(a, b);
- if (result != 0)
- return result;
- result = ComparePaths(a, b);
- if (result != 0)
- return result;
- result = CompareCachedNames(a, b);
- if (result != 0)
- return result;
- return CompareGUIDs(a, b);
- }
- }
- /************************************************************************************************************************/
- /// <summary>Compares objects to put null or destroyed ones at the end.</summary>
- private static int CompareNulls(TransitionAssetBase a, TransitionAssetBase b)
- => (a == null).CompareTo(b == null);
- /// <summary>Compares the asset GUIDs.</summary>
- private static int CompareGUIDs(TransitionAssetBase a, TransitionAssetBase b)
- {
- var gotA = AssetDatabase.TryGetGUIDAndLocalFileIdentifier(a, out var aGUID, out long aLocalID);
- var gotB = AssetDatabase.TryGetGUIDAndLocalFileIdentifier(b, out var bGUID, out long bLocalID);
- var result = gotA.CompareTo(gotB);
- if (result != 0)
- return result;
- result = aGUID.CompareTo(bGUID);
- if (result != 0)
- return result;
- return aLocalID.CompareTo(bLocalID);
- }
- /// <summary>Compares the asset names.</summary>
- private static int CompareCachedNames(TransitionAssetBase a, TransitionAssetBase b)
- => a.GetCachedName().CompareTo(b.GetCachedName());
- /// <summary>Compares the asset paths.</summary>
- private static int ComparePaths(TransitionAssetBase a, TransitionAssetBase b)
- => AssetDatabase.GetAssetPath(a).CompareTo(AssetDatabase.GetAssetPath(b));
- /// <summary>Compares the transition types.</summary>
- private static int CompareTypes(TransitionAssetBase a, TransitionAssetBase b)
- {
- if (AnimancerUtilities.TryGetWrappedObject<ITransition>(a, out var transitionA) &&
- AnimancerUtilities.TryGetWrappedObject<ITransition>(b, out var transitionB))
- {
- var result = transitionA.GetType().GetNameCS().CompareTo(transitionB.GetType().GetNameCS());
- if (result != 0)
- return result;
- }
- return a.GetType().GetNameCS().CompareTo(b.GetType().GetNameCS());
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Sorting
- /************************************************************************************************************************/
- private static TransitionAssetBase[]
- _SortingTransitions = Array.Empty<TransitionAssetBase>();
- private static int[] _OldIndexToNew;
- /************************************************************************************************************************/
- /// <summary>Sorts the <see cref="TransitionLibraryDefinition.Transitions"/>.</summary>
- public static void Sort(
- TransitionLibraryDefinition library,
- Comparison<TransitionAssetBase> comparison)
- => Sort(library, new Comparison<TransitionAssetBase>(comparison));
- /// <summary>Sorts the <see cref="TransitionLibraryDefinition.Transitions"/>.</summary>
- public static void Sort(
- TransitionLibraryDefinition library,
- IComparer<TransitionAssetBase> comparer)
- {
- var transitions = library.Transitions;
- var count = transitions.Length;
- if (_SortingTransitions.Length < count)
- {
- var length = Mathf.NextPowerOfTwo(count);
- _SortingTransitions = new TransitionAssetBase[length];
- _OldIndexToNew = new int[length];
- }
- Array.Copy(transitions, _SortingTransitions, count);
- // Indices 0 -> Count.
- var newIndexToOld = GetTempSequentialIndices(count);
- Array.Sort(_SortingTransitions, newIndexToOld, 0, count, comparer);
- // Remove nulls which should have been sorted to the end.
- for (int i = count - 1; i >= 0; i--)
- if (_SortingTransitions[i] == null)
- count--;
- else
- break;
- // _NewIndexToOld[x] is now the index that Transitions[x] was at previously.
- // We need to invert that so _OldIndexToNew[x] is the new index of whatever was previously at Transitions[x].
- // That allows the library to update any index references using a simple x = _OldIndexToNew[x];
- for (int i = 0; i < count; i++)
- _OldIndexToNew[newIndexToOld[i]] = i;
- SetTransitions(library, _SortingTransitions, _OldIndexToNew, count);
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Sets the <see cref="TransitionLibraryDefinition.Transitions"/>
- /// using `oldIndexToNew` to remap any references to the old order.
- /// </summary>
- public static void SetTransitions(
- TransitionLibraryDefinition library,
- TransitionAssetBase[] transitions,
- int[] oldIndexToNew,
- int count)
- {
- var libraryTransitions = library.Transitions;
- if (libraryTransitions != transitions)
- {
- AnimancerUtilities.SetLength(ref libraryTransitions, count);
- Array.Copy(transitions, libraryTransitions, count);
- library.Transitions = libraryTransitions;
- }
- var modifiers = library.Modifiers;
- for (int i = modifiers.Length - 1; i >= 0; i--)
- {
- var modifier = modifiers[i];
- var isValid = true;
- var fromIndex = ConvertIndex(modifier.FromIndex, oldIndexToNew, count, ref isValid);
- var toIndex = ConvertIndex(modifier.ToIndex, oldIndexToNew, count, ref isValid);
- if (isValid)
- modifiers[i] = modifier.WithIndices(fromIndex, toIndex);
- else
- AnimancerUtilities.RemoveAt(ref modifiers, i);
- }
- var aliases = library.Aliases;
- for (int i = aliases.Length - 1; i >= 0; i--)
- {
- var alias = aliases[i];
- var isValid = true;
- var index = ConvertIndex(alias.Index, oldIndexToNew, count, ref isValid);
- if (isValid)
- aliases[i] = alias.With(index);
- else
- AnimancerUtilities.RemoveAt(ref aliases, i);
- }
- library.SortAliases();
- }
- /************************************************************************************************************************/
- /// <summary>Converts an old index to a new one.</summary>
- private static int ConvertIndex(int index, int[] oldIndexToNew, int count, ref bool isValid)
- {
- if ((uint)index >= (uint)count)
- {
- isValid = false;
- return -1;
- }
- index = oldIndexToNew[index];
- if ((uint)index >= (uint)count)
- {
- isValid = false;
- return -1;
- }
- return index;
- }
- /************************************************************************************************************************/
- private static int[] _SequentialIndices = Array.Empty<int>();
- /// <summary>Returns a cached array containing sequential indices, i.e. <c>array[i] = i</c>.</summary>
- public static int[] GetTempSequentialIndices(int count)
- {
- if (_SequentialIndices.Length < count)
- _SequentialIndices = new int[Mathf.NextPowerOfTwo(count)];
- for (int i = 0; i < _SequentialIndices.Length; i++)
- _SequentialIndices[i] = i;
- return _SequentialIndices;
- }
- /************************************************************************************************************************/
- /// <summary>Changes the index of a transition.</summary>
- public static void MoveTransition(TransitionLibraryWindow window, int from, int to)
- {
- var transitions = window.Data.Transitions;
- to = Mathf.Clamp(to, 0, transitions.Length - 1);
- if (from == to)
- return;
- var editorData = window.SourceObject.GetOrCreateEditorData();
- var definition = window.RecordUndo();
- editorData.TransitionSortMode = TransitionSortMode.Custom;
- var moving = transitions[from];
- var indices = GetTempSequentialIndices(transitions.Length);
- if (to > from)// Moving forwards.
- {
- Array.Copy(transitions, from + 1, transitions, from, to - from);
- Array.Copy(indices, from, indices, from + 1, to - from);
- }
- else// Moving backwards.
- {
- Array.Copy(transitions, to, transitions, to + 1, from - to);
- Array.Copy(indices, to + 1, indices, to, from - to);
- }
- transitions[to] = moving;
- indices[from] = to;
- SetTransitions(
- definition,
- transitions,
- indices,
- transitions.Length);
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- }
- }
- #endif
|