mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
Initial commit
This commit is contained in:
commit
3c7cc0c973
8391 changed files with 704313 additions and 0 deletions
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal static class BoneWeightExtensions
|
||||
{
|
||||
public static int GetBoneIndex(this BoneWeight boneWeight, int channelIndex)
|
||||
{
|
||||
if (channelIndex < 0 || channelIndex >= 4)
|
||||
throw new ArgumentOutOfRangeException("Channel index out of range");
|
||||
|
||||
if (channelIndex == 0)
|
||||
return boneWeight.boneIndex0;
|
||||
if (channelIndex == 1)
|
||||
return boneWeight.boneIndex1;
|
||||
if (channelIndex == 2)
|
||||
return boneWeight.boneIndex2;
|
||||
if (channelIndex == 3)
|
||||
return boneWeight.boneIndex3;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void SetBoneIndex(ref BoneWeight boneWeight, int channelIndex, int boneIndex)
|
||||
{
|
||||
if (channelIndex < 0 || channelIndex >= 4)
|
||||
throw new ArgumentOutOfRangeException("Channel index out of range");
|
||||
|
||||
if (channelIndex == 0)
|
||||
boneWeight.boneIndex0 = boneIndex;
|
||||
if (channelIndex == 1)
|
||||
boneWeight.boneIndex1 = boneIndex;
|
||||
if (channelIndex == 2)
|
||||
boneWeight.boneIndex2 = boneIndex;
|
||||
if (channelIndex == 3)
|
||||
boneWeight.boneIndex3 = boneIndex;
|
||||
}
|
||||
|
||||
public static float GetWeight(this BoneWeight boneWeight, int channelIndex)
|
||||
{
|
||||
if (channelIndex < 0 || channelIndex >= 4)
|
||||
throw new ArgumentOutOfRangeException("Channel index out of range");
|
||||
|
||||
if (channelIndex == 0)
|
||||
return boneWeight.weight0;
|
||||
if (channelIndex == 1)
|
||||
return boneWeight.weight1;
|
||||
if (channelIndex == 2)
|
||||
return boneWeight.weight2;
|
||||
if (channelIndex == 3)
|
||||
return boneWeight.weight3;
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public static void SetWeight(ref BoneWeight boneWeight, int channelIndex, float weight)
|
||||
{
|
||||
if (channelIndex < 0 || channelIndex >= 4)
|
||||
throw new ArgumentOutOfRangeException("Channel index out of range");
|
||||
|
||||
if (channelIndex == 0)
|
||||
boneWeight.weight0 = weight;
|
||||
if (channelIndex == 1)
|
||||
boneWeight.weight1 = weight;
|
||||
if (channelIndex == 2)
|
||||
boneWeight.weight2 = weight;
|
||||
if (channelIndex == 3)
|
||||
boneWeight.weight3 = weight;
|
||||
}
|
||||
|
||||
public static float Sum(this BoneWeight boneWeight)
|
||||
{
|
||||
return boneWeight.weight0 + boneWeight.weight1 + boneWeight.weight2 + boneWeight.weight3;
|
||||
}
|
||||
|
||||
public static BoneWeight Normalized(this BoneWeight boneWeight)
|
||||
{
|
||||
var sum = boneWeight.Sum();
|
||||
|
||||
if (sum == 0 || sum == 1f)
|
||||
return boneWeight;
|
||||
|
||||
var normalized = boneWeight;
|
||||
var sumInv = 1f / sum;
|
||||
|
||||
for (var i = 0; i < 4; ++i)
|
||||
SetWeight(ref normalized, i, normalized.GetWeight(i) * sumInv);
|
||||
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a4ccab030e59cd542a67539ba0c86549
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,62 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
[Serializable]
|
||||
internal struct Edge
|
||||
{
|
||||
[SerializeField]
|
||||
int m_Index1;
|
||||
[SerializeField]
|
||||
int m_Index2;
|
||||
|
||||
public int index1
|
||||
{
|
||||
get { return m_Index1; }
|
||||
set { m_Index1 = value; }
|
||||
}
|
||||
|
||||
public int index2
|
||||
{
|
||||
get { return m_Index2; }
|
||||
set { m_Index2 = value; }
|
||||
}
|
||||
|
||||
public Edge(int inIndex1, int inIndex2)
|
||||
{
|
||||
m_Index1 = inIndex1;
|
||||
m_Index2 = inIndex2;
|
||||
}
|
||||
|
||||
public bool Contains(int index)
|
||||
{
|
||||
return index1 == index || index2 == index;
|
||||
}
|
||||
|
||||
public static bool operator==(Edge lhs, Edge rhs)
|
||||
{
|
||||
return lhs.Equals(rhs);
|
||||
}
|
||||
|
||||
public static bool operator!=(Edge lhs, Edge rhs)
|
||||
{
|
||||
return !lhs.Equals(rhs);
|
||||
}
|
||||
|
||||
public override bool Equals(System.Object obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
return false;
|
||||
|
||||
Edge p = (Edge)obj;
|
||||
|
||||
return (index1 == p.index1) && (index2 == p.index2) || (index1 == p.index2) && (index2 == p.index1);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return index1.GetHashCode() ^ index2.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9d39e60df00ea46b4af0f2ee69c0541b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,108 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
[Serializable]
|
||||
internal class BoneWeightChannel : IComparable<BoneWeightChannel>
|
||||
{
|
||||
[SerializeField]
|
||||
private bool m_Enabled;
|
||||
[SerializeField]
|
||||
private int m_BoneIndex;
|
||||
[SerializeField]
|
||||
private float m_Weight;
|
||||
|
||||
public bool enabled
|
||||
{
|
||||
get { return m_Enabled; }
|
||||
set { m_Enabled = value; }
|
||||
}
|
||||
|
||||
public int boneIndex
|
||||
{
|
||||
get { return m_BoneIndex; }
|
||||
set { m_BoneIndex = value; }
|
||||
}
|
||||
|
||||
public float weight
|
||||
{
|
||||
get { return m_Weight; }
|
||||
set { m_Weight = value; }
|
||||
}
|
||||
|
||||
public BoneWeightChannel() : this(0, 0f, false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BoneWeightChannel(int i, float w, bool e)
|
||||
{
|
||||
enabled = e;
|
||||
boneIndex = i;
|
||||
weight = w;
|
||||
}
|
||||
|
||||
public int CompareTo(BoneWeightChannel other)
|
||||
{
|
||||
int result = other.enabled.CompareTo(enabled);
|
||||
|
||||
if (result == 0)
|
||||
result = other.weight.CompareTo(weight);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class EditableBoneWeight : IEnumerable<BoneWeightChannel>
|
||||
{
|
||||
[SerializeField]
|
||||
private List<BoneWeightChannel> m_Channels = new List<BoneWeightChannel>(5);
|
||||
|
||||
public BoneWeightChannel this[int i]
|
||||
{
|
||||
get { return m_Channels[i]; }
|
||||
set { m_Channels[i] = value; }
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_Channels.Count; }
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Channels.Clear();
|
||||
}
|
||||
|
||||
public void AddChannel(int boneIndex, float weight, bool enabled)
|
||||
{
|
||||
m_Channels.Add(new BoneWeightChannel(boneIndex, weight, enabled));
|
||||
}
|
||||
|
||||
public void RemoveChannel(int channelIndex)
|
||||
{
|
||||
Debug.Assert(channelIndex < Count);
|
||||
|
||||
m_Channels.RemoveAt(channelIndex);
|
||||
}
|
||||
|
||||
public void Sort()
|
||||
{
|
||||
m_Channels.Sort();
|
||||
}
|
||||
|
||||
public IEnumerator<BoneWeightChannel> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<BoneWeightChannel>)m_Channels).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<BoneWeightChannel>)m_Channels).GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 976d01c510fb7407580d39aaa0294cb0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,262 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal struct BoneWeightData : IComparable<BoneWeightData>
|
||||
{
|
||||
public int boneIndex;
|
||||
public float weight;
|
||||
|
||||
public int CompareTo(BoneWeightData other)
|
||||
{
|
||||
return other.weight.CompareTo(weight);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class EditableBoneWeightUtility
|
||||
{
|
||||
private static List<BoneWeightData> s_BoneWeightDataList = new List<BoneWeightData>();
|
||||
private static EditableBoneWeight s_LerpFirst = new EditableBoneWeight();
|
||||
private static EditableBoneWeight s_LerpSecond = new EditableBoneWeight();
|
||||
private static EditableBoneWeight s_LerpResult = new EditableBoneWeight();
|
||||
|
||||
public static EditableBoneWeight CreateFromBoneWeight(BoneWeight boneWeight)
|
||||
{
|
||||
EditableBoneWeight editableBoneWeight = new EditableBoneWeight();
|
||||
|
||||
editableBoneWeight.SetFromBoneWeight(boneWeight);
|
||||
editableBoneWeight.UnifyChannelsWithSameBoneIndex();
|
||||
|
||||
return editableBoneWeight;
|
||||
}
|
||||
|
||||
public static void SetFromBoneWeight(this EditableBoneWeight editableBoneWeight, BoneWeight boneWeight)
|
||||
{
|
||||
editableBoneWeight.Clamp(4, false);
|
||||
|
||||
while (editableBoneWeight.Count < 4)
|
||||
editableBoneWeight.AddChannel(0, 0f, false);
|
||||
|
||||
for (var i = 0; i < 4; ++i)
|
||||
{
|
||||
var weight = boneWeight.GetWeight(i);
|
||||
editableBoneWeight[i].boneIndex = boneWeight.GetBoneIndex(i);
|
||||
editableBoneWeight[i].weight = weight;
|
||||
editableBoneWeight[i].enabled = weight > 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public static BoneWeight ToBoneWeight(this EditableBoneWeight editableBoneWeight, bool sortByWeight)
|
||||
{
|
||||
var boneWeight = new BoneWeight();
|
||||
|
||||
if (editableBoneWeight.Count > 0)
|
||||
{
|
||||
s_BoneWeightDataList.Clear();
|
||||
s_BoneWeightDataList.Capacity = editableBoneWeight.Count;
|
||||
|
||||
for (var i = 0; i < editableBoneWeight.Count; ++i)
|
||||
{
|
||||
s_BoneWeightDataList.Add(new BoneWeightData()
|
||||
{
|
||||
boneIndex = editableBoneWeight[i].boneIndex,
|
||||
weight = editableBoneWeight[i].weight
|
||||
});
|
||||
}
|
||||
|
||||
if (sortByWeight)
|
||||
s_BoneWeightDataList.Sort();
|
||||
|
||||
var count = Mathf.Min(editableBoneWeight.Count, 4);
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
BoneWeightExtensions.SetBoneIndex(ref boneWeight, i, s_BoneWeightDataList[i].boneIndex);
|
||||
BoneWeightExtensions.SetWeight(ref boneWeight, i, s_BoneWeightDataList[i].weight);
|
||||
}
|
||||
}
|
||||
|
||||
return boneWeight;
|
||||
}
|
||||
|
||||
public static bool ContainsBoneIndex(this EditableBoneWeight editableBoneWeight, int boneIndex)
|
||||
{
|
||||
return GetChannelFromBoneIndex(editableBoneWeight, boneIndex) > -1;
|
||||
}
|
||||
|
||||
public static int GetChannelFromBoneIndex(this EditableBoneWeight editableBoneWeight, int boneIndex)
|
||||
{
|
||||
for (int i = 0; i < editableBoneWeight.Count; ++i)
|
||||
if (editableBoneWeight[i].enabled && editableBoneWeight[i].boneIndex == boneIndex)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void Clamp(this EditableBoneWeight editableBoneWeight, int numChannels, bool sortChannels = true)
|
||||
{
|
||||
if (sortChannels)
|
||||
editableBoneWeight.Sort();
|
||||
|
||||
while (editableBoneWeight.Count > numChannels)
|
||||
editableBoneWeight.RemoveChannel(numChannels);
|
||||
}
|
||||
|
||||
public static void ValidateChannels(this EditableBoneWeight editableBoneWeight)
|
||||
{
|
||||
for (int i = 0; i < editableBoneWeight.Count; ++i)
|
||||
{
|
||||
var weight = editableBoneWeight[i].weight;
|
||||
|
||||
if (!editableBoneWeight[i].enabled)
|
||||
weight = 0f;
|
||||
|
||||
weight = Mathf.Clamp01(weight);
|
||||
editableBoneWeight[i].weight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
public static float Sum(this EditableBoneWeight editableBoneWeight)
|
||||
{
|
||||
var sum = 0f;
|
||||
|
||||
for (var i = 0; i < editableBoneWeight.Count; ++i)
|
||||
if (editableBoneWeight[i].enabled)
|
||||
sum += editableBoneWeight[i].weight;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static void Normalize(this EditableBoneWeight editableBoneWeight)
|
||||
{
|
||||
ValidateChannels(editableBoneWeight);
|
||||
|
||||
var sum = editableBoneWeight.Sum();
|
||||
|
||||
if (sum == 0f || sum == 1f)
|
||||
return;
|
||||
|
||||
var sumInv = 1f / sum;
|
||||
|
||||
for (var i = 0; i < editableBoneWeight.Count; ++i)
|
||||
if (editableBoneWeight[i].enabled)
|
||||
editableBoneWeight[i].weight *= sumInv;
|
||||
}
|
||||
|
||||
public static void CompensateOtherChannels(this EditableBoneWeight editableBoneWeight, int masterChannel)
|
||||
{
|
||||
ValidateChannels(editableBoneWeight);
|
||||
|
||||
var validChannelCount = 0;
|
||||
var sum = 0f;
|
||||
|
||||
for (int i = 0; i < editableBoneWeight.Count; ++i)
|
||||
{
|
||||
if (i != masterChannel && editableBoneWeight[i].enabled)
|
||||
{
|
||||
sum += editableBoneWeight[i].weight;
|
||||
++validChannelCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (validChannelCount == 0)
|
||||
return;
|
||||
|
||||
var targetSum = 1f - editableBoneWeight[masterChannel].weight;
|
||||
|
||||
for (var i = 0; i < editableBoneWeight.Count; ++i)
|
||||
{
|
||||
if (i != masterChannel && editableBoneWeight[i].enabled)
|
||||
{
|
||||
if (sum == 0f)
|
||||
editableBoneWeight[i].weight = targetSum / validChannelCount;
|
||||
else
|
||||
editableBoneWeight[i].weight *= targetSum / sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnifyChannelsWithSameBoneIndex(this EditableBoneWeight editableBoneWeight)
|
||||
{
|
||||
for (var i = 0; i < editableBoneWeight.Count; ++i)
|
||||
{
|
||||
if (!editableBoneWeight[i].enabled)
|
||||
continue;
|
||||
|
||||
bool weightChanged = false;
|
||||
|
||||
for (var j = i + 1; j < editableBoneWeight.Count; ++j)
|
||||
{
|
||||
if (editableBoneWeight[j].boneIndex == editableBoneWeight[i].boneIndex)
|
||||
{
|
||||
weightChanged = true;
|
||||
editableBoneWeight[i].weight += editableBoneWeight[j].weight;
|
||||
editableBoneWeight[j].enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (weightChanged)
|
||||
editableBoneWeight.CompensateOtherChannels(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static void FilterChannels(this EditableBoneWeight editableBoneWeight, float weightTolerance)
|
||||
{
|
||||
for (var i = 0; i < editableBoneWeight.Count; ++i)
|
||||
{
|
||||
if (editableBoneWeight[i].weight <= weightTolerance)
|
||||
{
|
||||
editableBoneWeight[i].boneIndex = 0;
|
||||
editableBoneWeight[i].weight = 0f;
|
||||
editableBoneWeight[i].enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static BoneWeight Lerp(BoneWeight first, BoneWeight second, float t)
|
||||
{
|
||||
s_LerpFirst.SetFromBoneWeight(first);
|
||||
s_LerpSecond.SetFromBoneWeight(second);
|
||||
Lerp(s_LerpFirst, s_LerpSecond, ref s_LerpResult, t);
|
||||
|
||||
return s_LerpResult.ToBoneWeight(true);
|
||||
}
|
||||
|
||||
private static void Lerp(EditableBoneWeight first, EditableBoneWeight second, ref EditableBoneWeight result, float t)
|
||||
{
|
||||
result.Clear();
|
||||
|
||||
foreach (BoneWeightChannel channel in first)
|
||||
{
|
||||
if (!channel.enabled)
|
||||
continue;
|
||||
|
||||
var weight = channel.weight * (1f - t);
|
||||
|
||||
if (weight > 0f)
|
||||
result.AddChannel(channel.boneIndex, weight, true);
|
||||
}
|
||||
|
||||
foreach (BoneWeightChannel channel in second)
|
||||
{
|
||||
if (!channel.enabled)
|
||||
continue;
|
||||
|
||||
var weight = channel.weight * t;
|
||||
|
||||
if (weight > 0f)
|
||||
result.AddChannel(channel.boneIndex, weight, true);
|
||||
}
|
||||
|
||||
result.UnifyChannelsWithSameBoneIndex();
|
||||
result.Clamp(4);
|
||||
|
||||
if (result.Sum() > 1f)
|
||||
result.Normalize();
|
||||
|
||||
result.FilterChannels(0f);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0799df1a3fe414d198d40c6f6535e890
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class SmoothingUtility
|
||||
{
|
||||
private static float[,] m_DataInTemp;
|
||||
private static float[,] m_DataOutTemp;
|
||||
private static float[] m_DenominatorTemp;
|
||||
private static EditableBoneWeight s_BoneWeight = new EditableBoneWeight();
|
||||
|
||||
public static void SmoothWeights(BoneWeight[] boneWeightIn, IList<int> indices, int boneCount, out BoneWeight[] boneWeightOut)
|
||||
{
|
||||
SmoothWeights(boneWeightIn, indices, boneCount, 1, out boneWeightOut);
|
||||
}
|
||||
|
||||
public static void SmoothWeights(BoneWeight[] boneWeightIn, IList<int> indices, int boneCount, int iterations, out BoneWeight[] boneWeightOut)
|
||||
{
|
||||
Debug.Assert(boneWeightIn != null);
|
||||
|
||||
boneWeightOut = new BoneWeight[boneWeightIn.Length];
|
||||
|
||||
PrepareTempBuffers(boneWeightIn.Length, boneCount);
|
||||
|
||||
for (int i = 0; i < boneWeightIn.Length; ++i)
|
||||
{
|
||||
s_BoneWeight.SetFromBoneWeight(boneWeightIn[i]);
|
||||
for (var j = 0; j < s_BoneWeight.Count; ++j)
|
||||
{
|
||||
if (s_BoneWeight[j].enabled)
|
||||
m_DataInTemp[i, s_BoneWeight[j].boneIndex] = s_BoneWeight[j].weight;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < iterations; ++i)
|
||||
SmoothPerVertexData(indices, m_DataInTemp, m_DataOutTemp);
|
||||
|
||||
for (var i = 0; i < boneWeightIn.Length; ++i)
|
||||
{
|
||||
s_BoneWeight.Clear();
|
||||
|
||||
for (var j = 0; j < boneCount; ++j)
|
||||
{
|
||||
var weight = m_DataOutTemp[i, j];
|
||||
var boneIndex = weight > 0f ? j : 0;
|
||||
s_BoneWeight.AddChannel(boneIndex, weight, weight > 0);
|
||||
}
|
||||
|
||||
s_BoneWeight.Clamp(4);
|
||||
s_BoneWeight.Normalize();
|
||||
|
||||
boneWeightOut[i] = s_BoneWeight.ToBoneWeight(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SmoothPerVertexData(IList<int> indices, float[,] dataIn, float[,] dataOut)
|
||||
{
|
||||
Debug.Assert(dataIn != null);
|
||||
Debug.Assert(dataOut != null);
|
||||
Debug.Assert(dataIn != dataOut);
|
||||
Debug.Assert(dataIn.Length == dataOut.Length);
|
||||
|
||||
int rowLength = dataIn.GetLength(0);
|
||||
int colLength = dataIn.GetLength(1);
|
||||
|
||||
PrepareDenominatorBuffer(rowLength);
|
||||
|
||||
for (int i = 0; i < indices.Count / 3; ++i)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
int j1 = (j + 1) % 3;
|
||||
int j2 = (j + 2) % 3;
|
||||
|
||||
for (int k = 0; k < colLength; ++k)
|
||||
dataOut[indices[i * 3 + j], k] += dataIn[indices[i * 3 + j1], k] + dataIn[indices[i * 3 + j2], k];
|
||||
|
||||
m_DenominatorTemp[indices[i * 3 + j]] += 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < rowLength; ++i)
|
||||
{
|
||||
var dInv = 1f / Mathf.Max(1f, m_DenominatorTemp[i]);
|
||||
for (int j = 0; j < colLength; ++j)
|
||||
dataOut[i, j] *= dInv;
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrepareDenominatorBuffer(int rowLength)
|
||||
{
|
||||
if (m_DenominatorTemp == null || m_DenominatorTemp.Length != rowLength)
|
||||
m_DenominatorTemp = new float[rowLength];
|
||||
else
|
||||
Array.Clear(m_DenominatorTemp, 0, m_DenominatorTemp.Length);
|
||||
}
|
||||
|
||||
private static void PrepareTempBuffers(int rowLength, int colLength)
|
||||
{
|
||||
if (m_DataInTemp == null || m_DataInTemp.GetLength(0) != rowLength || m_DataInTemp.GetLength(1) != colLength)
|
||||
m_DataInTemp = new float[rowLength, colLength];
|
||||
else
|
||||
Array.Clear(m_DataInTemp, 0, m_DataInTemp.Length);
|
||||
|
||||
if (m_DataOutTemp == null || m_DataOutTemp.GetLength(0) != rowLength || m_DataOutTemp.GetLength(1) != colLength)
|
||||
m_DataOutTemp = new float[rowLength, colLength];
|
||||
else
|
||||
Array.Clear(m_DataOutTemp, 0, m_DataOutTemp.Length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3add3986a01573a4186a96dd45cd2f5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
[Serializable]
|
||||
internal class SpriteBoneData
|
||||
{
|
||||
public string name;
|
||||
public int parentId = -1;
|
||||
public Vector2 localPosition;
|
||||
public Quaternion localRotation = Quaternion.identity;
|
||||
public Vector2 position;
|
||||
public Vector2 endPosition;
|
||||
public float depth;
|
||||
public float length;
|
||||
}
|
||||
|
||||
internal interface ISpriteMeshData
|
||||
{
|
||||
Rect frame { get; set; }
|
||||
List<int> indices { get; set; }
|
||||
List<Edge> edges { get; set; }
|
||||
int vertexCount { get; }
|
||||
int boneCount { get; }
|
||||
Vector2 GetPosition(int index);
|
||||
void SetPosition(int index, Vector2 position);
|
||||
EditableBoneWeight GetWeight(int index);
|
||||
void SetWeight(int index, EditableBoneWeight weight);
|
||||
void AddVertex(Vector2 position, BoneWeight weight);
|
||||
void RemoveVertex(int index);
|
||||
SpriteBoneData GetBoneData(int index);
|
||||
float GetBoneDepth(int index);
|
||||
void Clear();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class SpriteMeshData : ISpriteMeshData
|
||||
{
|
||||
public GUID spriteID = new GUID();
|
||||
[SerializeField]
|
||||
private Rect m_Frame;
|
||||
public Vector2 pivot = Vector2.zero;
|
||||
[SerializeField]
|
||||
private List<Vertex2D> m_Vertices = new List<Vertex2D>();
|
||||
[SerializeField]
|
||||
private List<int> m_Indices = new List<int>();
|
||||
[SerializeField]
|
||||
public List<SpriteBoneData> m_Bones = new List<SpriteBoneData>();
|
||||
[SerializeField]
|
||||
private List<Edge> m_Edges = new List<Edge>();
|
||||
|
||||
public Rect frame
|
||||
{
|
||||
get { return m_Frame; }
|
||||
set { m_Frame = value; }
|
||||
}
|
||||
|
||||
public List<Vertex2D> vertices
|
||||
{
|
||||
get { return m_Vertices; }
|
||||
set { m_Vertices = value; }
|
||||
}
|
||||
|
||||
public List<int> indices
|
||||
{
|
||||
get { return m_Indices; }
|
||||
set { m_Indices = value; }
|
||||
}
|
||||
|
||||
public List<Edge> edges
|
||||
{
|
||||
get { return m_Edges; }
|
||||
set { m_Edges = value; }
|
||||
}
|
||||
|
||||
public List<SpriteBoneData> bones
|
||||
{
|
||||
get { return m_Bones; }
|
||||
set { m_Bones = value; }
|
||||
}
|
||||
|
||||
public int vertexCount { get { return m_Vertices.Count; } }
|
||||
public int boneCount { get { return m_Bones.Count; } }
|
||||
|
||||
public Vector2 GetPosition(int index)
|
||||
{
|
||||
return m_Vertices[index].position;
|
||||
}
|
||||
|
||||
public void SetPosition(int index, Vector2 position)
|
||||
{
|
||||
m_Vertices[index].position = position;
|
||||
}
|
||||
|
||||
public EditableBoneWeight GetWeight(int index)
|
||||
{
|
||||
return m_Vertices[index].editableBoneWeight;
|
||||
}
|
||||
|
||||
public void SetWeight(int index, EditableBoneWeight weight)
|
||||
{
|
||||
m_Vertices[index].editableBoneWeight = weight;
|
||||
}
|
||||
|
||||
public void AddVertex(Vector2 position, BoneWeight weight)
|
||||
{
|
||||
m_Vertices.Add(new Vertex2D(position, weight));
|
||||
}
|
||||
|
||||
public void RemoveVertex(int index)
|
||||
{
|
||||
m_Vertices.RemoveAt(index);
|
||||
}
|
||||
|
||||
public SpriteBoneData GetBoneData(int index)
|
||||
{
|
||||
return m_Bones[index];
|
||||
}
|
||||
|
||||
public float GetBoneDepth(int index)
|
||||
{
|
||||
return m_Bones[index].depth;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Vertices.Clear();
|
||||
m_Indices.Clear();
|
||||
m_Edges.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d9921ba0f389d3045ac52c4880cf5397
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,577 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.U2D.Sprites;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal struct WeightedTriangle : IComparable<WeightedTriangle>
|
||||
{
|
||||
public int p1;
|
||||
public int p2;
|
||||
public int p3;
|
||||
public float weight;
|
||||
|
||||
public int CompareTo(WeightedTriangle other)
|
||||
{
|
||||
return weight.CompareTo(other.weight);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpriteMeshDataController
|
||||
{
|
||||
public ISpriteMeshData spriteMeshData;
|
||||
private List<Vector2> m_VerticesTemp = new List<Vector2>();
|
||||
private List<Edge> m_EdgesTemp = new List<Edge>();
|
||||
|
||||
public void CreateVertex(Vector2 position)
|
||||
{
|
||||
CreateVertex(position, -1);
|
||||
}
|
||||
|
||||
public void CreateVertex(Vector2 position, int edgeIndex)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
spriteMeshData.AddVertex(position, default(BoneWeight));
|
||||
|
||||
if (edgeIndex != -1)
|
||||
{
|
||||
Edge edge = spriteMeshData.edges[edgeIndex];
|
||||
RemoveEdge(edge);
|
||||
CreateEdge(edge.index1, spriteMeshData.vertexCount - 1);
|
||||
CreateEdge(edge.index2, spriteMeshData.vertexCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateEdge(int index1, int index2)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
Debug.Assert(index1 >= 0);
|
||||
Debug.Assert(index2 >= 0);
|
||||
Debug.Assert(index1 < spriteMeshData.vertexCount);
|
||||
Debug.Assert(index2 < spriteMeshData.vertexCount);
|
||||
Debug.Assert(index1 != index2);
|
||||
|
||||
Edge newEdge = new Edge(index1, index2);
|
||||
|
||||
if (!spriteMeshData.edges.Contains(newEdge))
|
||||
spriteMeshData.edges.Add(newEdge);
|
||||
}
|
||||
|
||||
public void RemoveVertex(int index)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
//We need to delete the edges that reference the index
|
||||
List<Edge> edgesWithIndex;
|
||||
if (FindEdgesContainsIndex(index, out edgesWithIndex))
|
||||
{
|
||||
//If there are 2 edges referencing the same index we are removing, we can create a new one that connects the endpoints ("Unsplit").
|
||||
if (edgesWithIndex.Count == 2)
|
||||
{
|
||||
Edge first = edgesWithIndex[0];
|
||||
Edge second = edgesWithIndex[1];
|
||||
|
||||
int index1 = first.index1 != index ? first.index1 : first.index2;
|
||||
int index2 = second.index1 != index ? second.index1 : second.index2;
|
||||
|
||||
CreateEdge(index1, index2);
|
||||
}
|
||||
|
||||
//remove found edges
|
||||
for (int i = 0; i < edgesWithIndex.Count; i++)
|
||||
{
|
||||
RemoveEdge(edgesWithIndex[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//Fix indices in edges greater than the one we are removing
|
||||
for (int i = 0; i < spriteMeshData.edges.Count; i++)
|
||||
{
|
||||
Edge edge = spriteMeshData.edges[i];
|
||||
|
||||
if (edge.index1 > index)
|
||||
edge.index1--;
|
||||
if (edge.index2 > index)
|
||||
edge.index2--;
|
||||
|
||||
spriteMeshData.edges[i] = edge;
|
||||
}
|
||||
|
||||
spriteMeshData.RemoveVertex(index);
|
||||
}
|
||||
|
||||
public void RemoveVertex(IEnumerable<int> indices)
|
||||
{
|
||||
List<int> sortedIndexList = new List<int>(indices);
|
||||
|
||||
if (sortedIndexList.Count == 0)
|
||||
return;
|
||||
|
||||
sortedIndexList.Sort();
|
||||
|
||||
for (int i = sortedIndexList.Count - 1; i >= 0; --i)
|
||||
{
|
||||
RemoveVertex(sortedIndexList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEdge(Edge edge)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
spriteMeshData.edges.Remove(edge);
|
||||
}
|
||||
|
||||
public bool FindEdgesContainsIndex(int index, out List<Edge> result)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
bool found = false;
|
||||
|
||||
result = new List<Edge>();
|
||||
|
||||
for (int i = 0; i < spriteMeshData.edges.Count; ++i)
|
||||
{
|
||||
Edge edge = spriteMeshData.edges[i];
|
||||
|
||||
if (edge.Contains(index))
|
||||
{
|
||||
found = true;
|
||||
result.Add(edge);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public void Triangulate(ITriangulator triangulator)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
Debug.Assert(triangulator != null);
|
||||
|
||||
m_VerticesTemp.Clear();
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; ++i)
|
||||
m_VerticesTemp.Add(spriteMeshData.GetPosition(i));
|
||||
|
||||
triangulator.Triangulate(m_VerticesTemp, spriteMeshData.edges, spriteMeshData.indices);
|
||||
}
|
||||
|
||||
public void Subdivide(ITriangulator triangulator, float largestAreaFactor)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
Debug.Assert(triangulator != null);
|
||||
|
||||
m_VerticesTemp.Clear();
|
||||
m_EdgesTemp.Clear();
|
||||
m_EdgesTemp.AddRange(spriteMeshData.edges);
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; ++i)
|
||||
m_VerticesTemp.Add(spriteMeshData.GetPosition(i));
|
||||
|
||||
try
|
||||
{
|
||||
var indices = new List<int>();
|
||||
|
||||
triangulator.Tessellate(0f, 0f, 0f, largestAreaFactor, 100, m_VerticesTemp, m_EdgesTemp, indices);
|
||||
|
||||
spriteMeshData.Clear();
|
||||
|
||||
for (int i = 0; i < m_VerticesTemp.Count; ++i)
|
||||
spriteMeshData.AddVertex(m_VerticesTemp[i], default(BoneWeight));
|
||||
|
||||
spriteMeshData.edges.AddRange(m_EdgesTemp);
|
||||
spriteMeshData.indices.AddRange(indices);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearWeights(ISelection<int> selection)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; ++i)
|
||||
if (selection == null || (selection.Count == 0 || selection.Contains(i)))
|
||||
spriteMeshData.GetWeight(i).SetFromBoneWeight(default(BoneWeight));
|
||||
}
|
||||
|
||||
public void OutlineFromAlpha(IOutlineGenerator outlineGenerator, ITextureDataProvider textureDataProvider, float outlineDetail, byte alphaTolerance)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
Debug.Assert(textureDataProvider != null);
|
||||
Debug.Assert(textureDataProvider.texture != null);
|
||||
|
||||
int width, height;
|
||||
textureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
|
||||
Vector2 scale = new Vector2(textureDataProvider.texture.width / (float)width, textureDataProvider.texture.height / (float)height);
|
||||
Vector2 scaleInv = new Vector2(1f / scale.x, 1f / scale.y);
|
||||
Vector2 rectOffset = spriteMeshData.frame.size * 0.5f + spriteMeshData.frame.position;
|
||||
|
||||
Rect scaledRect = spriteMeshData.frame;
|
||||
scaledRect.min = Vector2.Scale(scaledRect.min, scale);
|
||||
scaledRect.max = Vector2.Scale(scaledRect.max, scale);
|
||||
|
||||
spriteMeshData.Clear();
|
||||
|
||||
Vector2[][] paths;
|
||||
outlineGenerator.GenerateOutline(textureDataProvider, scaledRect, outlineDetail, alphaTolerance, false, out paths);
|
||||
|
||||
int vertexIndexBase = 0;
|
||||
for (int i = 0; i < paths.Length; ++i)
|
||||
{
|
||||
int numPathVertices = paths[i].Length;
|
||||
|
||||
for (int j = 0; j <= numPathVertices; j++)
|
||||
{
|
||||
if (j < numPathVertices)
|
||||
spriteMeshData.AddVertex(Vector2.Scale(paths[i][j], scaleInv) + rectOffset, default(BoneWeight));
|
||||
|
||||
if (j > 0)
|
||||
spriteMeshData.edges.Add(new Edge(vertexIndexBase + j - 1, vertexIndexBase + j % numPathVertices));
|
||||
}
|
||||
|
||||
vertexIndexBase += numPathVertices;
|
||||
}
|
||||
}
|
||||
|
||||
public void NormalizeWeights(ISelection<int> selection)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; ++i)
|
||||
if (selection == null || (selection.Count == 0 || selection.Contains(i)))
|
||||
spriteMeshData.GetWeight(i).Normalize();
|
||||
}
|
||||
|
||||
public void CalculateWeights(IWeightsGenerator weightsGenerator, ISelection<int> selection, float filterTolerance)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
Vector2[] controlPoints;
|
||||
Edge[] bones;
|
||||
int[] pins;
|
||||
|
||||
GetControlPoints(out controlPoints, out bones, out pins);
|
||||
|
||||
Vector2[] vertices = new Vector2[spriteMeshData.vertexCount];
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; ++i)
|
||||
vertices[i] = spriteMeshData.GetPosition(i);
|
||||
|
||||
BoneWeight[] boneWeights = weightsGenerator.Calculate(vertices, spriteMeshData.edges.ToArray(), controlPoints, bones, pins);
|
||||
|
||||
Debug.Assert(boneWeights.Length == spriteMeshData.vertexCount);
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; ++i)
|
||||
{
|
||||
if (selection == null || (selection.Count == 0 || selection.Contains(i)))
|
||||
{
|
||||
EditableBoneWeight editableBoneWeight = EditableBoneWeightUtility.CreateFromBoneWeight(boneWeights[i]);
|
||||
|
||||
if (filterTolerance > 0f)
|
||||
{
|
||||
editableBoneWeight.FilterChannels(filterTolerance);
|
||||
editableBoneWeight.Normalize();
|
||||
}
|
||||
|
||||
spriteMeshData.SetWeight(i, editableBoneWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CalculateWeightsSafe(IWeightsGenerator weightsGenerator, ISelection<int> selection, float filterTolerance)
|
||||
{
|
||||
var tempSelection = new IndexedSelection();
|
||||
var vertexSelector = new GenericVertexSelector();
|
||||
|
||||
vertexSelector.spriteMeshData = spriteMeshData;
|
||||
vertexSelector.selection = tempSelection;
|
||||
vertexSelector.SelectionCallback = (int i) => {
|
||||
return spriteMeshData.GetWeight(i).Sum() == 0f && (selection == null || selection.Count == 0 || selection.Contains(i));
|
||||
};
|
||||
vertexSelector.Select();
|
||||
|
||||
if (tempSelection.Count > 0)
|
||||
CalculateWeights(weightsGenerator, tempSelection, filterTolerance);
|
||||
}
|
||||
|
||||
public void SmoothWeights(int iterations, ISelection<int> selection)
|
||||
{
|
||||
var boneWeights = new BoneWeight[spriteMeshData.vertexCount];
|
||||
|
||||
for (var i = 0; i < spriteMeshData.vertexCount; i++)
|
||||
boneWeights[i] = spriteMeshData.GetWeight(i).ToBoneWeight(false);
|
||||
|
||||
BoneWeight[] smoothedWeights;
|
||||
SmoothingUtility.SmoothWeights(boneWeights, spriteMeshData.indices, spriteMeshData.boneCount, iterations, out smoothedWeights);
|
||||
|
||||
for (var i = 0; i < spriteMeshData.vertexCount; i++)
|
||||
if (selection == null || (selection.Count == 0 || selection.Contains(i)))
|
||||
spriteMeshData.GetWeight(i).SetFromBoneWeight(smoothedWeights[i]);
|
||||
}
|
||||
|
||||
public bool FindTriangle(Vector2 point, out Vector3Int indices, out Vector3 barycentricCoords)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
indices = Vector3Int.zero;
|
||||
barycentricCoords = Vector3.zero;
|
||||
|
||||
if (spriteMeshData.indices.Count < 3)
|
||||
return false;
|
||||
|
||||
int triangleCount = spriteMeshData.indices.Count / 3;
|
||||
|
||||
for (int i = 0; i < triangleCount; ++i)
|
||||
{
|
||||
indices.x = spriteMeshData.indices[i * 3];
|
||||
indices.y = spriteMeshData.indices[i * 3 + 1];
|
||||
indices.z = spriteMeshData.indices[i * 3 + 2];
|
||||
|
||||
MathUtility.Barycentric(
|
||||
point,
|
||||
spriteMeshData.GetPosition(indices.x),
|
||||
spriteMeshData.GetPosition(indices.y),
|
||||
spriteMeshData.GetPosition(indices.z),
|
||||
out barycentricCoords);
|
||||
|
||||
if (barycentricCoords.x >= 0f && barycentricCoords.y >= 0f && barycentricCoords.z >= 0f)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<float> m_VertexOrderList = new List<float>(1000);
|
||||
private List<WeightedTriangle> m_WeightedTriangles = new List<WeightedTriangle>(1000);
|
||||
|
||||
public void SortTrianglesByDepth()
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
if (spriteMeshData.boneCount == 0)
|
||||
return;
|
||||
|
||||
m_VertexOrderList.Clear();
|
||||
m_WeightedTriangles.Clear();
|
||||
|
||||
for (var i = 0; i < spriteMeshData.vertexCount; i++)
|
||||
{
|
||||
var vertexOrder = 0f;
|
||||
var boneWeight = spriteMeshData.GetWeight(i);
|
||||
|
||||
for (var j = 0; j < boneWeight.Count; ++j)
|
||||
vertexOrder += spriteMeshData.GetBoneDepth(boneWeight[j].boneIndex) * boneWeight[j].weight;
|
||||
|
||||
m_VertexOrderList.Add(vertexOrder);
|
||||
}
|
||||
|
||||
for (int i = 0; i < spriteMeshData.indices.Count; i += 3)
|
||||
{
|
||||
int p1 = spriteMeshData.indices[i];
|
||||
int p2 = spriteMeshData.indices[i + 1];
|
||||
int p3 = spriteMeshData.indices[i + 2];
|
||||
float weight = (m_VertexOrderList[p1] + m_VertexOrderList[p2] + m_VertexOrderList[p3]) / 3f;
|
||||
|
||||
m_WeightedTriangles.Add(new WeightedTriangle() { p1 = p1, p2 = p2, p3 = p3, weight = weight });
|
||||
}
|
||||
|
||||
m_WeightedTriangles.Sort();
|
||||
|
||||
spriteMeshData.indices.Clear();
|
||||
|
||||
for (var i = 0; i < m_WeightedTriangles.Count; ++i)
|
||||
{
|
||||
var triangle = m_WeightedTriangles[i];
|
||||
spriteMeshData.indices.Add(triangle.p1);
|
||||
spriteMeshData.indices.Add(triangle.p2);
|
||||
spriteMeshData.indices.Add(triangle.p3);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetMultiEditChannelData(ISelection<int> selection, int channel,
|
||||
out bool enabled, out int boneIndex, out float weight,
|
||||
out bool isEnabledMixed, out bool isBoneIndexMixed, out bool isWeightMixed)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
if (selection == null)
|
||||
throw new ArgumentNullException("selection is null");
|
||||
|
||||
var first = true;
|
||||
enabled = false;
|
||||
boneIndex = -1;
|
||||
weight = 0f;
|
||||
isEnabledMixed = false;
|
||||
isBoneIndexMixed = false;
|
||||
isWeightMixed = false;
|
||||
|
||||
var indices = selection.elements;
|
||||
|
||||
foreach (int i in indices)
|
||||
{
|
||||
var editableBoneWeight = spriteMeshData.GetWeight(i);
|
||||
|
||||
if (first)
|
||||
{
|
||||
enabled = editableBoneWeight[channel].enabled;
|
||||
boneIndex = editableBoneWeight[channel].boneIndex;
|
||||
weight = editableBoneWeight[channel].weight;
|
||||
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enabled != editableBoneWeight[channel].enabled)
|
||||
{
|
||||
isEnabledMixed = true;
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
if (boneIndex != editableBoneWeight[channel].boneIndex)
|
||||
{
|
||||
isBoneIndexMixed = true;
|
||||
boneIndex = -1;
|
||||
}
|
||||
|
||||
if (weight != editableBoneWeight[channel].weight)
|
||||
{
|
||||
isWeightMixed = true;
|
||||
weight = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMultiEditChannelData(ISelection<int> selection, int channel,
|
||||
bool oldEnabled, bool newEnabled, int oldBoneIndex, int newBoneIndex, float oldWeight, float newWeight)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
if (selection == null)
|
||||
throw new ArgumentNullException("selection is null");
|
||||
|
||||
bool channelEnabledChanged = oldEnabled != newEnabled;
|
||||
bool boneIndexChanged = oldBoneIndex != newBoneIndex;
|
||||
bool weightChanged = oldWeight != newWeight;
|
||||
|
||||
var indices = selection.elements;
|
||||
|
||||
foreach (int i in indices)
|
||||
{
|
||||
var editableBoneWeight = spriteMeshData.GetWeight(i);
|
||||
|
||||
if (channelEnabledChanged)
|
||||
editableBoneWeight[channel].enabled = newEnabled;
|
||||
|
||||
if (boneIndexChanged)
|
||||
editableBoneWeight[channel].boneIndex = newBoneIndex;
|
||||
|
||||
if (weightChanged)
|
||||
editableBoneWeight[channel].weight = newWeight;
|
||||
|
||||
if (channelEnabledChanged || weightChanged)
|
||||
editableBoneWeight.CompensateOtherChannels(channel);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetControlPoints(out Vector2[] points, out Edge[] edges, out int[] pins)
|
||||
{
|
||||
Debug.Assert(spriteMeshData != null);
|
||||
|
||||
points = null;
|
||||
edges = null;
|
||||
|
||||
List<Vector2> pointList = new List<Vector2>();
|
||||
List<Edge> edgeList = new List<Edge>();
|
||||
List<int> pinList = new List<int>();
|
||||
List<SpriteBoneData> bones = new List<SpriteBoneData>(spriteMeshData.boneCount);
|
||||
|
||||
for (int i = 0; i < spriteMeshData.boneCount; ++i)
|
||||
bones.Add(spriteMeshData.GetBoneData(i));
|
||||
|
||||
foreach (var bone in bones)
|
||||
{
|
||||
var length = (bone.endPosition - bone.position).magnitude;
|
||||
|
||||
if (length > 0f)
|
||||
{
|
||||
int index1 = FindPoint(pointList, bone.position, 0.01f);
|
||||
int index2 = FindPoint(pointList, bone.endPosition, 0.01f);
|
||||
|
||||
if (index1 == -1)
|
||||
{
|
||||
pointList.Add(bone.position);
|
||||
index1 = pointList.Count - 1;
|
||||
}
|
||||
|
||||
if (index2 == -1)
|
||||
{
|
||||
pointList.Add(bone.endPosition);
|
||||
index2 = pointList.Count - 1;
|
||||
}
|
||||
|
||||
edgeList.Add(new Edge(index1, index2));
|
||||
}
|
||||
else if (bone.length == 0f)
|
||||
{
|
||||
pointList.Add(bone.position);
|
||||
pinList.Add(pointList.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
points = pointList.ToArray();
|
||||
edges = edgeList.ToArray();
|
||||
pins = pinList.ToArray();
|
||||
}
|
||||
|
||||
private int FindPoint(List<Vector2> points, Vector2 point, float distanceTolerance)
|
||||
{
|
||||
float sqrTolerance = distanceTolerance * distanceTolerance;
|
||||
|
||||
for (int i = 0; i < points.Count; ++i)
|
||||
{
|
||||
if ((points[i] - point).sqrMagnitude <= sqrTolerance)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void SmoothFill()
|
||||
{
|
||||
var tempSelection = new IndexedSelection();
|
||||
var vertexSelector = new GenericVertexSelector();
|
||||
var currentWeightSum = 0f;
|
||||
var prevWeightSum = 0f;
|
||||
|
||||
vertexSelector.spriteMeshData = spriteMeshData;
|
||||
vertexSelector.selection = tempSelection;
|
||||
vertexSelector.SelectionCallback = (int i) => {
|
||||
var sum = spriteMeshData.GetWeight(i).Sum();
|
||||
currentWeightSum += sum;
|
||||
return sum < 0.99f;
|
||||
};
|
||||
|
||||
do
|
||||
{
|
||||
prevWeightSum = currentWeightSum;
|
||||
currentWeightSum = 0f;
|
||||
vertexSelector.Select();
|
||||
|
||||
if (tempSelection.Count > 0)
|
||||
SmoothWeights(1, tempSelection);
|
||||
}
|
||||
while (currentWeightSum - prevWeightSum > 0.001f);
|
||||
|
||||
if (tempSelection.Count > 0)
|
||||
NormalizeWeights(tempSelection);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5b0085fc7caad034f8513c17d7e40646
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,39 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
[Serializable]
|
||||
internal class Vertex2D
|
||||
{
|
||||
public Vector2 position
|
||||
{
|
||||
get { return m_Position; }
|
||||
set { m_Position = value; }
|
||||
}
|
||||
|
||||
public EditableBoneWeight editableBoneWeight
|
||||
{
|
||||
get { return m_EditableBoneWeight; }
|
||||
set { m_EditableBoneWeight = value; }
|
||||
}
|
||||
|
||||
public Vertex2D(Vector2 position)
|
||||
{
|
||||
m_Position = position;
|
||||
m_EditableBoneWeight = EditableBoneWeightUtility.CreateFromBoneWeight(new BoneWeight());
|
||||
}
|
||||
|
||||
public Vertex2D(Vector2 position, BoneWeight weights)
|
||||
{
|
||||
m_Position = position;
|
||||
m_EditableBoneWeight = EditableBoneWeightUtility.CreateFromBoneWeight(weights);
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
Vector2 m_Position;
|
||||
|
||||
[SerializeField]
|
||||
EditableBoneWeight m_EditableBoneWeight;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 879d7ab84cd1a4d3c9481c194ae09404
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,244 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal enum WeightEditorMode
|
||||
{
|
||||
AddAndSubtract,
|
||||
GrowAndShrink,
|
||||
Smooth
|
||||
}
|
||||
|
||||
internal class WeightEditor
|
||||
{
|
||||
public ISpriteMeshData spriteMeshData
|
||||
{
|
||||
get { return 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 WeightEditorMode currentMode { get; private set; }
|
||||
public bool useRelativeValues { get; private set; }
|
||||
public bool emptySelectionEditsAll { get; set; }
|
||||
public bool autoNormalize { get; set; }
|
||||
|
||||
private SpriteMeshDataController m_SpriteMeshDataController = new SpriteMeshDataController();
|
||||
private const int maxSmoothIterations = 8;
|
||||
private float[] m_SmoothValues;
|
||||
private readonly List<BoneWeight[]> m_SmoothedBoneWeights = new List<BoneWeight[]>();
|
||||
private readonly List<BoneWeight> m_StoredBoneWeights = new List<BoneWeight>();
|
||||
private int BoneCount
|
||||
{
|
||||
get { return 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.GetWeight(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);
|
||||
}
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
if (spriteMeshData == null)
|
||||
throw (new Exception(TextContent.noSpriteSelected));
|
||||
}
|
||||
|
||||
private void RegisterUndo()
|
||||
{
|
||||
Debug.Assert(cacheUndo != null);
|
||||
|
||||
cacheUndo.BeginUndoOperation(TextContent.editWeights);
|
||||
}
|
||||
|
||||
private 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.GetWeight(i);
|
||||
|
||||
int 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SmoothWeights(float value)
|
||||
{
|
||||
Debug.Assert(selection != null);
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; ++i)
|
||||
{
|
||||
if (selection.Count == 0 && emptySelectionEditsAll ||
|
||||
selection.Count > 0 && selection.Contains(i))
|
||||
{
|
||||
var smoothValue = m_SmoothValues[i];
|
||||
|
||||
if (smoothValue >= maxSmoothIterations)
|
||||
continue;
|
||||
|
||||
m_SmoothValues[i] = Mathf.Clamp(smoothValue + value, 0f, maxSmoothIterations);
|
||||
|
||||
float lerpValue = GetLerpValue(m_SmoothValues[i]);
|
||||
int lerpIndex = GetLerpIndex(m_SmoothValues[i]);
|
||||
BoneWeight[] smoothedBoneWeightsFloor = GetSmoothedBoneWeights(lerpIndex - 1);
|
||||
BoneWeight[] smoothedBoneWeightsCeil = GetSmoothedBoneWeights(lerpIndex);
|
||||
|
||||
BoneWeight boneWeight = EditableBoneWeightUtility.Lerp(smoothedBoneWeightsFloor[i], smoothedBoneWeightsCeil[i], lerpValue);
|
||||
spriteMeshData.GetWeight(i).SetFromBoneWeight(boneWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected 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();
|
||||
|
||||
BoneWeight[] boneWeights = new BoneWeight[spriteMeshData.vertexCount];
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; i++)
|
||||
{
|
||||
EditableBoneWeight editableBoneWeight = spriteMeshData.GetWeight(i);
|
||||
boneWeights[i] = editableBoneWeight.ToBoneWeight(false);
|
||||
}
|
||||
|
||||
m_SmoothedBoneWeights.Add(boneWeights);
|
||||
}
|
||||
|
||||
private BoneWeight[] GetSmoothedBoneWeights(int lerpIndex)
|
||||
{
|
||||
Debug.Assert(lerpIndex >= 0);
|
||||
|
||||
while (lerpIndex >= m_SmoothedBoneWeights.Count && lerpIndex <= maxSmoothIterations)
|
||||
{
|
||||
BoneWeight[] boneWeights;
|
||||
SmoothingUtility.SmoothWeights(m_SmoothedBoneWeights[m_SmoothedBoneWeights.Count - 1], spriteMeshData.indices, BoneCount, out boneWeights);
|
||||
m_SmoothedBoneWeights.Add(boneWeights);
|
||||
}
|
||||
|
||||
return m_SmoothedBoneWeights[Mathf.Min(lerpIndex, maxSmoothIterations)];
|
||||
}
|
||||
|
||||
private float GetLerpValue(float smoothValue)
|
||||
{
|
||||
Debug.Assert(smoothValue >= 0f);
|
||||
return smoothValue - Mathf.Floor(smoothValue);
|
||||
}
|
||||
|
||||
private int GetLerpIndex(float smoothValue)
|
||||
{
|
||||
Debug.Assert(smoothValue >= 0f);
|
||||
return Mathf.RoundToInt(Mathf.Floor(smoothValue) + 1);
|
||||
}
|
||||
|
||||
private void StoreBoneWeights()
|
||||
{
|
||||
Debug.Assert(selection != null);
|
||||
|
||||
m_StoredBoneWeights.Clear();
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; i++)
|
||||
{
|
||||
EditableBoneWeight editableBoneWeight = spriteMeshData.GetWeight(i);
|
||||
m_StoredBoneWeights.Add(editableBoneWeight.ToBoneWeight(false));
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreBoneWeights()
|
||||
{
|
||||
Debug.Assert(selection != null);
|
||||
|
||||
for (int i = 0; i < spriteMeshData.vertexCount; i++)
|
||||
{
|
||||
EditableBoneWeight editableBoneWeight = spriteMeshData.GetWeight(i);
|
||||
editableBoneWeight.SetFromBoneWeight(m_StoredBoneWeights[i]);
|
||||
}
|
||||
|
||||
if (m_SmoothValues != null)
|
||||
Array.Clear(m_SmoothValues, 0, m_SmoothValues.Length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9df99dfc0f67dd145a0774a4fa06b839
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue