// 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