// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR using System; using System.Collections.Generic; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; using static Animancer.Editor.AnimancerGUI; namespace Animancer.Editor { /// An for editing spring definitions. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/WeightedMaskLayersDefinitionWindow public class WeightedMaskLayersDefinitionWindow : TransformTreeWindow { /************************************************************************************************************************/ /// public override IList Transforms => Data.Transforms; /// public override WeightedMaskLayersDefinition SourceData { get => SourceObject.Definition; set => SourceObject.Definition = value; } /************************************************************************************************************************/ /// protected override MultiColumnHeaderState.Column[] CreateColumns(float width) { const float TransformWidth = 300; const float GroupWidth = 100; var groupCount = Data.GroupCount; var oldColumns = HeaderState?.columns; var newColumns = new MultiColumnHeaderState.Column[groupCount + 2]; int index; if (oldColumns == null) { var tooltip = "Select which objects to control the weight of"; newColumns[0] = CreateColumn( "Transform", tooltip, TransformWidth); var includedColumn = newColumns[1] = CreateColumn( "?", tooltip, LineHeight + StandardSpacing); includedColumn.minWidth = includedColumn.maxWidth = includedColumn.width; index = 2; } else { var copyCount = Math.Min(oldColumns.Length, groupCount + 2); for (int i = 0; i < copyCount; i++) newColumns[i] = oldColumns[i]; index = copyCount; } for (int i = index; i < groupCount + 2; i++) { var groupIndex = i - 2; var name = "Group " + groupIndex.ToStringCached(); var tooltip = "The weights for " + name; if (groupIndex == 0) { name = "Default " + name; tooltip += " (this group will be applied on startup by default)"; } newColumns[i] = CreateColumn( name, tooltip, GroupWidth); } return newColumns; } /************************************************************************************************************************/ /// protected override Color GetRowColor(TreeViewItem item) { if (!TreeView.Transforms.TryGetObject(item.id, out var transform)) return default; if (Transforms.IndexOf(transform) < 0) return default; return GetChainColor(transform, Transforms, 0.15f); } /// Returns a color based on the name of the `transform`'s highest included parent. public static Color GetChainColor(Transform transform, IList transforms, float alpha) { transform = GetChainRoot(transform, transforms); return GetHashColor(transform.name.GetHashCode(), 1, 1, alpha); } /// Gets the highest parent of `transform` which is included in the `transforms`. public static Transform GetChainRoot(Transform transform, IList transforms) { var parent = transform.parent; while (parent != null) { if (transforms.IndexOf(parent) >= 0) transform = parent; parent = parent.parent; } return transform; } /************************************************************************************************************************/ /// public override void DrawCellGUI(Rect area, int column, int row, TreeViewItem item, ref bool isSelectionClick) { if (!TreeView.Transforms.TryGetObject(item.id, out var transform)) return; var definitionIndex = GetDefinitionIndex(item.id); switch (column) { case 0:// Transform. DrawTransformCellGUI(area, transform); break; case 1:// Included. DrawIsIncludedCellGUI(area, item.id, definitionIndex, ref isSelectionClick); break; default:// Groups. DrawWeightGUI(area, item.id, column - 2, definitionIndex); break; } } /************************************************************************************************************************/ /// protected override void SetIncluded( int treeItemID, int definitionIndex, bool isIncluded) { var data = RecordUndo(); if (isIncluded) { if (definitionIndex < 0 && TreeView.Transforms.TryGetObject(treeItemID, out var transform)) { var groupCount = data.GroupCount; data.AddTransform(transform); if (groupCount != data.GroupCount) CreateHeader(); } } else { if (definitionIndex >= 0) { data.RemoveTransform(definitionIndex); if (data.GroupCount <= 0) CreateHeader(); } } } /************************************************************************************************************************/ private void DrawWeightGUI( Rect area, int treeItemID, int groupIndex, int transformIndex) { if (transformIndex < 0) return; var weight = Data.GetWeight(groupIndex, transformIndex); if (float.IsNaN(weight)) return; EditorGUI.BeginChangeCheck(); weight = EditorGUI.FloatField(area, weight); if (EditorGUI.EndChangeCheck()) { weight = Mathf.Clamp01(weight); SetValue(treeItemID, i => RecordUndo().SetWeight(groupIndex, i, weight)); } } /************************************************************************************************************************/ private static readonly GUIContent AddGroupLabel = new( "Add Group", "Add another weight group"), RemoveGroupLabel = new( "Remove Group", "Remove the last weight group"); /// protected override void DoFooterCenterGUI() { var hasTransforms = !Data.Transforms.IsNullOrEmpty(); GUI.enabled = hasTransforms; if (GUILayout.Button(AddGroupLabel)) { RecordUndo().GroupCount++; CreateHeader(); } if (hasTransforms) GUI.enabled = Data.GroupCount > 1; if (GUILayout.Button(RemoveGroupLabel)) { RecordUndo().GroupCount--; CreateHeader(); } } /************************************************************************************************************************/ /// public override void Apply() { base.Apply(); CreateHeader(); SceneView.RepaintAll(); } /// public override void Revert() { base.Revert(); CreateHeader(); SceneView.RepaintAll(); } /************************************************************************************************************************/ /// public override WeightedMaskLayersDefinition RecordUndo(string name = "Animancer") { SceneView.RepaintAll(); return base.RecordUndo(name); } /************************************************************************************************************************/ } } #endif