Singularity/Library/PackageCache/com.unity.2d.animation@7.0.10/Editor/SkinningModule/SpriteMeshData/WeightEditor.cs

241 lines
7.8 KiB
C#
Raw Normal View History

2024-05-06 14:45:45 -04:00
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.U2D.Animation
{
internal enum WeightEditorMode
{
AddAndSubtract,
GrowAndShrink,
Smooth
}
internal class WeightEditor
{
public BaseSpriteMeshData spriteMeshData
{
get => m_SpriteMeshDataController.spriteMeshData;
set => m_SpriteMeshDataController.spriteMeshData = value;
}
public ICacheUndo cacheUndo { get; set; }
public WeightEditorMode mode { get; set; }
public int boneIndex { get; set; }
public ISelection<int> selection { get; set; }
public bool emptySelectionEditsAll { get; set; }
public bool autoNormalize { get; set; }
WeightEditorMode currentMode { get; set; }
bool useRelativeValues { get; set; }
SpriteMeshDataController m_SpriteMeshDataController = new SpriteMeshDataController();
const int k_MaxSmoothIterations = 8;
float[] m_SmoothValues;
readonly List<BoneWeight[]> m_SmoothedBoneWeights = new List<BoneWeight[]>();
readonly List<BoneWeight> m_StoredBoneWeights = new List<BoneWeight>();
int boneCount => spriteMeshData != null ? spriteMeshData.boneCount : 0;
public WeightEditor()
{
autoNormalize = true;
}
public void OnEditStart(bool relative)
{
Validate();
RegisterUndo();
currentMode = mode;
useRelativeValues = relative;
if (!useRelativeValues)
StoreBoneWeights();
if (mode == WeightEditorMode.Smooth)
PrepareSmoothingBuffers();
}
public void OnEditEnd()
{
Validate();
if (currentMode == WeightEditorMode.AddAndSubtract)
{
for (int i = 0; i < spriteMeshData.vertexCount; ++i)
spriteMeshData.vertexWeights[i].Clamp(4);
}
if (autoNormalize)
m_SpriteMeshDataController.NormalizeWeights(null);
m_SpriteMeshDataController.SortTrianglesByDepth();
}
public void DoEdit(float value)
{
Validate();
if (!useRelativeValues)
RestoreBoneWeights();
if (currentMode == WeightEditorMode.AddAndSubtract)
SetWeight(value);
else if (currentMode == WeightEditorMode.GrowAndShrink)
SetWeight(value, false);
else if (currentMode == WeightEditorMode.Smooth)
SmoothWeights(value);
}
void Validate()
{
if (spriteMeshData == null)
throw (new Exception(TextContent.noSpriteSelected));
}
void RegisterUndo()
{
Debug.Assert(cacheUndo != null);
cacheUndo.BeginUndoOperation(TextContent.editWeights);
}
void SetWeight(float value, bool createNewChannel = true)
{
if (boneIndex == -1 || spriteMeshData == null)
return;
Debug.Assert(selection != null);
for (var i = 0; i < spriteMeshData.vertexCount; ++i)
{
if (selection.Count == 0 && emptySelectionEditsAll ||
selection.Count > 0 && selection.Contains(i))
{
var editableBoneWeight = spriteMeshData.vertexWeights[i];
var channel = editableBoneWeight.GetChannelFromBoneIndex(boneIndex);
if (channel == -1)
{
if (createNewChannel && value > 0f)
{
editableBoneWeight.AddChannel(boneIndex, 0f, true);
channel = editableBoneWeight.GetChannelFromBoneIndex(boneIndex);
}
else
{
continue;
}
}
editableBoneWeight[channel].weight += value;
if (editableBoneWeight.Sum() > 1f)
editableBoneWeight.CompensateOtherChannels(channel);
editableBoneWeight.FilterChannels(0f);
}
}
}
void SmoothWeights(float value)
{
Debug.Assert(selection != null);
for (var i = 0; i < spriteMeshData.vertexCount; ++i)
{
if (selection.Count == 0 && emptySelectionEditsAll ||
selection.Count > 0 && selection.Contains(i))
{
var smoothValue = m_SmoothValues[i];
if (smoothValue >= k_MaxSmoothIterations)
continue;
m_SmoothValues[i] = Mathf.Clamp(smoothValue + value, 0f, k_MaxSmoothIterations);
var lerpValue = GetLerpValue(m_SmoothValues[i]);
var lerpIndex = GetLerpIndex(m_SmoothValues[i]);
var smoothedBoneWeightsFloor = GetSmoothedBoneWeights(lerpIndex - 1);
var smoothedBoneWeightsCeil = GetSmoothedBoneWeights(lerpIndex);
var boneWeight = EditableBoneWeightUtility.Lerp(smoothedBoneWeightsFloor[i], smoothedBoneWeightsCeil[i], lerpValue);
spriteMeshData.vertexWeights[i].SetFromBoneWeight(boneWeight);
}
}
}
void PrepareSmoothingBuffers()
{
if (m_SmoothValues == null || m_SmoothValues.Length != spriteMeshData.vertexCount)
m_SmoothValues = new float[spriteMeshData.vertexCount];
Array.Clear(m_SmoothValues, 0, m_SmoothValues.Length);
m_SmoothedBoneWeights.Clear();
var boneWeights = new BoneWeight[spriteMeshData.vertexCount];
for (var i = 0; i < spriteMeshData.vertexCount; i++)
{
var editableBoneWeight = spriteMeshData.vertexWeights[i];
boneWeights[i] = editableBoneWeight.ToBoneWeight(false);
}
m_SmoothedBoneWeights.Add(boneWeights);
}
BoneWeight[] GetSmoothedBoneWeights(int lerpIndex)
{
Debug.Assert(lerpIndex >= 0);
while (lerpIndex >= m_SmoothedBoneWeights.Count && lerpIndex <= k_MaxSmoothIterations)
{
SmoothingUtility.SmoothWeights(m_SmoothedBoneWeights[^1], spriteMeshData.indices, boneCount, out var boneWeights);
m_SmoothedBoneWeights.Add(boneWeights);
}
return m_SmoothedBoneWeights[Mathf.Min(lerpIndex, k_MaxSmoothIterations)];
}
static float GetLerpValue(float smoothValue)
{
Debug.Assert(smoothValue >= 0f);
return smoothValue - Mathf.Floor(smoothValue);
}
static int GetLerpIndex(float smoothValue)
{
Debug.Assert(smoothValue >= 0f);
return Mathf.RoundToInt(Mathf.Floor(smoothValue) + 1);
}
void StoreBoneWeights()
{
Debug.Assert(selection != null);
m_StoredBoneWeights.Clear();
for (var i = 0; i < spriteMeshData.vertexCount; i++)
{
var editableBoneWeight = spriteMeshData.vertexWeights[i];
m_StoredBoneWeights.Add(editableBoneWeight.ToBoneWeight(false));
}
}
void RestoreBoneWeights()
{
Debug.Assert(selection != null);
for (var i = 0; i < spriteMeshData.vertexCount; i++)
{
var editableBoneWeight = spriteMeshData.vertexWeights[i];
editableBoneWeight.SetFromBoneWeight(m_StoredBoneWeights[i]);
}
if (m_SmoothValues != null)
Array.Clear(m_SmoothValues, 0, m_SmoothValues.Length);
}
}
}