390 lines
13 KiB
C#
390 lines
13 KiB
C#
|
using UnityEngine;
|
||
|
using UnityEditor.U2D.Layout;
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
|
||
|
namespace UnityEditor.U2D.Animation
|
||
|
{
|
||
|
internal enum WeightPainterMode
|
||
|
{
|
||
|
Brush,
|
||
|
Slider
|
||
|
}
|
||
|
|
||
|
internal class WeightPainterTool : MeshToolWrapper
|
||
|
{
|
||
|
private WeightPainterPanel m_WeightPainterPanel;
|
||
|
private WeightEditor m_WeightEditor = new WeightEditor();
|
||
|
private Brush m_Brush = new Brush(new GUIWrapper());
|
||
|
private ISelection<int> m_BrushSelection = new IndexedSelection();
|
||
|
private CircleVertexSelector m_CircleVertexSelector = new CircleVertexSelector();
|
||
|
|
||
|
public WeightPainterMode paintMode
|
||
|
{
|
||
|
get { return m_WeightPainterPanel.paintMode; }
|
||
|
set { m_WeightPainterPanel.paintMode = value; }
|
||
|
}
|
||
|
|
||
|
public override int defaultControlID
|
||
|
{
|
||
|
get { return m_Brush.controlID; }
|
||
|
}
|
||
|
|
||
|
internal override void OnCreate()
|
||
|
{
|
||
|
m_WeightEditor.cacheUndo = skinningCache;
|
||
|
|
||
|
m_Brush.onMove += (brush) =>
|
||
|
{
|
||
|
UpdateBrushSelection(brush);
|
||
|
};
|
||
|
m_Brush.onRepaint += (brush) =>
|
||
|
{
|
||
|
DrawBrush(brush);
|
||
|
};
|
||
|
m_Brush.onSize += (brush) =>
|
||
|
{
|
||
|
UpdateBrushSelection(brush);
|
||
|
m_WeightPainterPanel.size = Mathf.RoundToInt(brush.size);
|
||
|
};
|
||
|
m_Brush.onStrokeBegin += (brush) =>
|
||
|
{
|
||
|
UpdateBrushSelection(brush);
|
||
|
EditStart(m_BrushSelection, true);
|
||
|
};
|
||
|
m_Brush.onStrokeDelta += (brush) =>
|
||
|
{
|
||
|
if (m_BrushSelection.Count > 0)
|
||
|
meshTool.UpdateWeights();
|
||
|
};
|
||
|
m_Brush.onStrokeStep += (brush) =>
|
||
|
{
|
||
|
UpdateBrushSelection(brush);
|
||
|
|
||
|
var hardness = brush.hardness / 100f;
|
||
|
|
||
|
if (EditorGUI.actionKey)
|
||
|
hardness *= -1f;
|
||
|
|
||
|
EditWeights(hardness, false);
|
||
|
};
|
||
|
m_Brush.onStrokeEnd += (brush) =>
|
||
|
{
|
||
|
EditEnd();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
public string panelTitle
|
||
|
{
|
||
|
set { m_WeightPainterPanel.title = value; }
|
||
|
}
|
||
|
|
||
|
protected override void OnActivate()
|
||
|
{
|
||
|
base.OnActivate();
|
||
|
m_WeightPainterPanel.SetHiddenFromLayout(false);
|
||
|
|
||
|
skinningCache.events.selectedSpriteChanged.AddListener(OnSelectedSpriteChanged);
|
||
|
skinningCache.events.skinningModeChanged.AddListener(OnSkinningModeChanged);
|
||
|
skinningCache.events.boneSelectionChanged.AddListener(OnBoneSelectionChanged);
|
||
|
|
||
|
m_Brush.size = skinningCache.brushSize;
|
||
|
m_Brush.hardness = skinningCache.brushHardness;
|
||
|
m_Brush.step = skinningCache.brushStep;
|
||
|
m_WeightPainterPanel.size = (int) m_Brush.size;
|
||
|
m_WeightPainterPanel.hardness = (int) m_Brush.hardness;
|
||
|
m_WeightPainterPanel.step = (int) m_Brush.step;
|
||
|
|
||
|
UpdatePanel();
|
||
|
}
|
||
|
|
||
|
protected override void OnDeactivate()
|
||
|
{
|
||
|
base.OnDeactivate();
|
||
|
|
||
|
skinningCache.events.selectedSpriteChanged.RemoveListener(OnSelectedSpriteChanged);
|
||
|
skinningCache.events.skinningModeChanged.RemoveListener(OnSkinningModeChanged);
|
||
|
skinningCache.events.boneSelectionChanged.RemoveListener(OnBoneSelectionChanged);
|
||
|
|
||
|
m_WeightPainterPanel.SetHiddenFromLayout(true);
|
||
|
}
|
||
|
|
||
|
private void OnBoneSelectionChanged()
|
||
|
{
|
||
|
UpdateSelectedBone();
|
||
|
}
|
||
|
|
||
|
private void OnSelectedSpriteChanged(SpriteCache sprite)
|
||
|
{
|
||
|
UpdatePanel();
|
||
|
}
|
||
|
|
||
|
private void OnSkinningModeChanged(SkinningMode mode)
|
||
|
{
|
||
|
UpdatePanel();
|
||
|
}
|
||
|
|
||
|
private string[] GetSkeletonBonesNames()
|
||
|
{
|
||
|
var names = new List<string>() { WeightPainterPanel.kNone };
|
||
|
var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
|
||
|
|
||
|
if (skeleton != null)
|
||
|
names.AddRange(GetUniqueBoneNames(skeleton.bones, skeleton));
|
||
|
|
||
|
return names.ToArray();
|
||
|
}
|
||
|
|
||
|
private string[] GetMeshBoneNames()
|
||
|
{
|
||
|
var mesh = meshTool.mesh;
|
||
|
var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
|
||
|
|
||
|
if (mesh != null && skeleton != null)
|
||
|
{
|
||
|
var bones = meshTool.mesh.bones.ToSpriteSheetIfNeeded();
|
||
|
return GetUniqueBoneNames(bones, skeleton);
|
||
|
}
|
||
|
|
||
|
return new string[0];
|
||
|
}
|
||
|
|
||
|
private string[] GetUniqueBoneNames(BoneCache[] bones, SkeletonCache skeleton)
|
||
|
{
|
||
|
return Array.ConvertAll(bones, b => skeleton.GetUniqueName(b));
|
||
|
}
|
||
|
|
||
|
private void UpdatePanel()
|
||
|
{
|
||
|
m_WeightPainterPanel.SetActive(skinningCache.selectedSprite != null);
|
||
|
m_WeightPainterPanel.UpdateWeightInspector(meshTool.mesh, GetMeshBoneNames(), skinningCache.vertexSelection, skinningCache);
|
||
|
m_WeightPainterPanel.UpdatePanel(GetSkeletonBonesNames());
|
||
|
UpdateSelectedBone();
|
||
|
}
|
||
|
|
||
|
private void UpdateSelectedBone()
|
||
|
{
|
||
|
var boneName = WeightPainterPanel.kNone;
|
||
|
var bone = skinningCache.skeletonSelection.activeElement.ToSpriteSheetIfNeeded();
|
||
|
var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
|
||
|
|
||
|
if (skeleton != null && skeleton.Contains(bone))
|
||
|
boneName = skeleton.GetUniqueName(bone);
|
||
|
|
||
|
m_WeightPainterPanel.SetBoneSelectionByName(boneName);
|
||
|
}
|
||
|
|
||
|
public override void Initialize(LayoutOverlay layout)
|
||
|
{
|
||
|
base.Initialize(layout);
|
||
|
|
||
|
m_WeightPainterPanel = WeightPainterPanel.GenerateFromUXML();
|
||
|
m_WeightPainterPanel.SetHiddenFromLayout(true);
|
||
|
layout.rightOverlay.Add(m_WeightPainterPanel);
|
||
|
|
||
|
m_WeightPainterPanel.sliderStarted += () =>
|
||
|
{
|
||
|
EditStart(skinningCache.vertexSelection, false);
|
||
|
};
|
||
|
m_WeightPainterPanel.sliderChanged += (value) =>
|
||
|
{
|
||
|
EditWeights(value, true);
|
||
|
meshTool.UpdateWeights();
|
||
|
};
|
||
|
m_WeightPainterPanel.sliderEnded += () =>
|
||
|
{
|
||
|
EditEnd();
|
||
|
};
|
||
|
m_WeightPainterPanel.bonePopupChanged += (i) =>
|
||
|
{
|
||
|
var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
|
||
|
|
||
|
if (skeleton != null)
|
||
|
{
|
||
|
BoneCache bone = null;
|
||
|
|
||
|
if (i != -1)
|
||
|
bone = skeleton.GetBone(i).ToCharacterIfNeeded();
|
||
|
|
||
|
if(bone != skinningCache.skeletonSelection.activeElement)
|
||
|
{
|
||
|
using (skinningCache.UndoScope(TextContent.boneSelection))
|
||
|
{
|
||
|
skinningCache.skeletonSelection.activeElement = bone;
|
||
|
InvokeBoneSelectionChanged();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
m_WeightPainterPanel.weightsChanged += () => meshTool.UpdateWeights();
|
||
|
}
|
||
|
|
||
|
internal void SetWeightPainterPanelTitle(string title)
|
||
|
{
|
||
|
m_WeightPainterPanel.title = title;
|
||
|
}
|
||
|
|
||
|
private void AssociateSelectedBoneToCharacterPart()
|
||
|
{
|
||
|
var mesh = meshTool.mesh;
|
||
|
|
||
|
if (skinningCache.hasCharacter
|
||
|
&& skinningCache.mode == SkinningMode.Character
|
||
|
&& m_WeightPainterPanel.boneIndex != -1
|
||
|
&& mesh != null)
|
||
|
{
|
||
|
var skeleton = skinningCache.character.skeleton;
|
||
|
|
||
|
Debug.Assert(skeleton != null);
|
||
|
|
||
|
var bone = skeleton.GetBone(m_WeightPainterPanel.boneIndex);
|
||
|
|
||
|
if (!mesh.ContainsBone(bone))
|
||
|
{
|
||
|
using (skinningCache.UndoScope(TextContent.addBoneInfluence))
|
||
|
{
|
||
|
var characterPart = mesh.sprite.GetCharacterPart();
|
||
|
var characterBones = characterPart.bones.ToList();
|
||
|
characterBones.Add(bone);
|
||
|
characterPart.bones = characterBones.ToArray();
|
||
|
skinningCache.events.characterPartChanged.Invoke(characterPart);
|
||
|
m_WeightPainterPanel.UpdateWeightInspector(meshTool.mesh, GetMeshBoneNames(), skinningCache.vertexSelection, skinningCache);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void EditStart(ISelection<int> selection, bool relative)
|
||
|
{
|
||
|
AssociateSelectedBoneToCharacterPart();
|
||
|
|
||
|
SetupWeightEditor(selection);
|
||
|
|
||
|
if (m_WeightEditor.spriteMeshData != null)
|
||
|
m_WeightEditor.OnEditStart(relative);
|
||
|
}
|
||
|
|
||
|
private void EditWeights(float hardness, bool emptySelectionEditsAll)
|
||
|
{
|
||
|
m_WeightEditor.emptySelectionEditsAll = emptySelectionEditsAll;
|
||
|
|
||
|
if (m_WeightEditor.spriteMeshData != null)
|
||
|
m_WeightEditor.DoEdit(hardness);
|
||
|
}
|
||
|
|
||
|
private void EditEnd()
|
||
|
{
|
||
|
if (m_WeightEditor.spriteMeshData != null)
|
||
|
{
|
||
|
m_WeightEditor.OnEditEnd();
|
||
|
meshTool.UpdateWeights();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void InvokeBoneSelectionChanged()
|
||
|
{
|
||
|
skinningCache.events.boneSelectionChanged.RemoveListener(OnBoneSelectionChanged);
|
||
|
skinningCache.events.boneSelectionChanged.Invoke();
|
||
|
skinningCache.events.boneSelectionChanged.AddListener(OnBoneSelectionChanged);
|
||
|
}
|
||
|
|
||
|
private int ConvertBoneIndex(int index)
|
||
|
{
|
||
|
if (index != -1 && meshTool.mesh != null)
|
||
|
{
|
||
|
var skeleton = skinningCache.GetEffectiveSkeleton(meshTool.mesh.sprite);
|
||
|
|
||
|
if (skeleton != null)
|
||
|
{
|
||
|
var bone = skeleton.GetBone(index).ToCharacterIfNeeded();
|
||
|
index = Array.IndexOf(meshTool.mesh.bones, bone);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
private void SetupWeightEditor(ISelection<int> selection)
|
||
|
{
|
||
|
m_WeightEditor.spriteMeshData = meshTool.mesh;
|
||
|
m_WeightEditor.mode = m_WeightPainterPanel.mode;
|
||
|
m_WeightEditor.boneIndex = ConvertBoneIndex(m_WeightPainterPanel.boneIndex);
|
||
|
m_WeightEditor.autoNormalize = m_WeightPainterPanel.normalize;
|
||
|
m_WeightEditor.selection = selection;
|
||
|
m_WeightEditor.emptySelectionEditsAll = true;
|
||
|
}
|
||
|
|
||
|
private void UpdateBrushSelection(Brush brush)
|
||
|
{
|
||
|
m_BrushSelection.Clear();
|
||
|
m_CircleVertexSelector.spriteMeshData = meshTool.mesh;
|
||
|
m_CircleVertexSelector.position = brush.position;
|
||
|
m_CircleVertexSelector.radius = brush.size;
|
||
|
m_CircleVertexSelector.selection = m_BrushSelection;
|
||
|
m_CircleVertexSelector.Select();
|
||
|
}
|
||
|
|
||
|
private void DrawBrush(Brush brush)
|
||
|
{
|
||
|
var oldColor = Handles.color;
|
||
|
Handles.color = Color.white;
|
||
|
|
||
|
if (EditorGUI.actionKey)
|
||
|
Handles.color = Color.red;
|
||
|
if (brush.isHot)
|
||
|
Handles.color = Color.yellow;
|
||
|
|
||
|
Handles.DrawWireDisc(brush.position, Vector3.forward, brush.size);
|
||
|
Handles.color = oldColor;
|
||
|
}
|
||
|
|
||
|
protected override void OnGUI()
|
||
|
{
|
||
|
m_MeshPreviewBehaviour.showWeightMap = true;
|
||
|
m_MeshPreviewBehaviour.overlaySelected = true;
|
||
|
skeletonTool.skeletonStyle = SkeletonStyles.WeightMap;
|
||
|
|
||
|
skeletonMode = SkeletonMode.EditPose;
|
||
|
meshMode = SpriteMeshViewMode.EditGeometry;
|
||
|
disableMeshEditor = true;
|
||
|
|
||
|
var isBoneHovered = skeletonTool.hoveredBone != null && !m_Brush.isHot;
|
||
|
var useBrush = paintMode == WeightPainterMode.Brush;
|
||
|
|
||
|
meshTool.selectionOverride = null;
|
||
|
|
||
|
if (useBrush)
|
||
|
meshTool.selectionOverride = m_BrushSelection;
|
||
|
|
||
|
DoSkeletonGUI();
|
||
|
DoMeshGUI();
|
||
|
|
||
|
if (useBrush && !isBoneHovered)
|
||
|
{
|
||
|
var handlesMatrix = Handles.matrix;
|
||
|
var selectedSprite = skinningCache.selectedSprite;
|
||
|
var matrix = Matrix4x4.identity;
|
||
|
|
||
|
if (selectedSprite != null)
|
||
|
matrix = selectedSprite.GetLocalToWorldMatrixFromMode();
|
||
|
|
||
|
Handles.matrix *= matrix;
|
||
|
|
||
|
skinningCache.brushSize = m_Brush.size = m_WeightPainterPanel.size;
|
||
|
skinningCache.brushHardness = m_Brush.hardness = m_WeightPainterPanel.hardness;
|
||
|
skinningCache.brushStep = m_Brush.step = m_WeightPainterPanel.step;
|
||
|
|
||
|
if (m_Brush.isHot || !skinningCache.IsOnVisualElement())
|
||
|
{
|
||
|
meshTool.BeginPositionOverride();
|
||
|
m_Brush.OnGUI();
|
||
|
meshTool.EndPositionOverride();
|
||
|
}
|
||
|
|
||
|
Handles.matrix = handlesMatrix;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|