674 lines
25 KiB
C#
674 lines
25 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using Unity.Mathematics;
|
||
|
using UnityEngine;
|
||
|
using UnityEditor.U2D.Sprites;
|
||
|
using Unity.Jobs;
|
||
|
using Unity.Collections;
|
||
|
|
||
|
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 struct SpriteJobData
|
||
|
{
|
||
|
public BaseSpriteMeshData spriteMesh;
|
||
|
public NativeArray<float2> vertices;
|
||
|
public NativeArray<int2> edges;
|
||
|
public NativeArray<int> indices;
|
||
|
public NativeArray<BoneWeight> weights;
|
||
|
public NativeArray<int4> result;
|
||
|
};
|
||
|
|
||
|
internal class SpriteMeshDataController
|
||
|
{
|
||
|
public BaseSpriteMeshData spriteMeshData;
|
||
|
float2[] m_VerticesTemp = new float2[0];
|
||
|
int2[] m_EdgesTemp = new int2[0];
|
||
|
|
||
|
public void CreateVertex(Vector2 position)
|
||
|
{
|
||
|
CreateVertex(position, -1);
|
||
|
}
|
||
|
|
||
|
public void CreateVertex(Vector2 position, int edgeIndex)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null, "Assert failed. Expected: spriteMeshData != null. Actual: spriteMeshData == null");
|
||
|
|
||
|
spriteMeshData.AddVertex(position, default(BoneWeight));
|
||
|
|
||
|
if (edgeIndex != -1)
|
||
|
{
|
||
|
var edge = spriteMeshData.edges[edgeIndex];
|
||
|
RemoveEdge(edge);
|
||
|
CreateEdge(edge.x, spriteMeshData.vertexCount - 1);
|
||
|
CreateEdge(edge.y, spriteMeshData.vertexCount - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void CreateEdge(int index1, int index2)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null, "Assert failed. Expected: spriteMeshData != null. Actual: spriteMeshData == null");
|
||
|
Debug.Assert(index1 >= 0, $"Assert failed. Expected: index1 >= 0. Actual: index1 == {index1}");
|
||
|
Debug.Assert(index2 >= 0, $"Assert failed. Expected: index2 >= 0. Actual: index2 == {index2}");
|
||
|
Debug.Assert(index1 < spriteMeshData.vertexCount, $"Assert failed. Expected: index1 < spriteMeshData.vertexCount. Actual: index1 == {index1} spriteMeshData.vertexCount == {spriteMeshData.vertexCount}");
|
||
|
Debug.Assert(index2 < spriteMeshData.vertexCount, $"Assert failed. Expected: index2 < spriteMeshData.vertexCount. Actual: index2 == {index2} spriteMeshData.vertexCount == {spriteMeshData.vertexCount}");
|
||
|
Debug.Assert(index1 != index2, $"Assert failed. Expected: index1 != index2. Actual: index1 == {index1} index2 == {index2}");
|
||
|
|
||
|
var newEdge = new int2(index1, index2);
|
||
|
if (!spriteMeshData.edges.ContainsAny(newEdge))
|
||
|
{
|
||
|
var listOfEdges = new List<int2>(spriteMeshData.edges)
|
||
|
{
|
||
|
newEdge
|
||
|
};
|
||
|
spriteMeshData.SetEdges(listOfEdges.ToArray());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void RemoveVertex(int index)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
|
||
|
//We need to delete the edges that reference the index
|
||
|
if (FindEdgesContainsIndex(index, out var 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)
|
||
|
{
|
||
|
var first = edgesWithIndex[0];
|
||
|
var second = edgesWithIndex[1];
|
||
|
|
||
|
int index1 = first.x != index ? first.x : first.y;
|
||
|
int index2 = second.x != index ? second.x : second.y;
|
||
|
|
||
|
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.Length; i++)
|
||
|
{
|
||
|
var edge = spriteMeshData.edges[i];
|
||
|
|
||
|
if (edge.x > index)
|
||
|
edge.x--;
|
||
|
if (edge.y > index)
|
||
|
edge.y--;
|
||
|
|
||
|
spriteMeshData.edges[i] = edge;
|
||
|
}
|
||
|
|
||
|
spriteMeshData.RemoveVertex(index);
|
||
|
}
|
||
|
|
||
|
public void RemoveVertex(IEnumerable<int> indices)
|
||
|
{
|
||
|
var sortedIndexList = new List<int>(indices);
|
||
|
|
||
|
if (sortedIndexList.Count == 0)
|
||
|
return;
|
||
|
|
||
|
sortedIndexList.Sort();
|
||
|
|
||
|
for (var i = sortedIndexList.Count - 1; i >= 0; --i)
|
||
|
{
|
||
|
RemoveVertex(sortedIndexList[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RemoveEdge(int2 edge)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
var listOfEdges = new List<int2>(spriteMeshData.edges);
|
||
|
listOfEdges.Remove(edge);
|
||
|
spriteMeshData.SetEdges(listOfEdges.ToArray());
|
||
|
}
|
||
|
|
||
|
bool FindEdgesContainsIndex(int index, out List<int2> result)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
|
||
|
bool found = false;
|
||
|
|
||
|
result = new List<int2>();
|
||
|
|
||
|
for (int i = 0; i < spriteMeshData.edges.Length; ++i)
|
||
|
{
|
||
|
var edge = spriteMeshData.edges[i];
|
||
|
if (edge.x == index || edge.y == index)
|
||
|
{
|
||
|
found = true;
|
||
|
result.Add(edge);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
public void CreateQuad()
|
||
|
{
|
||
|
var frame = new Rect(Vector2.zero, spriteMeshData.frame.size);
|
||
|
var verts = new Vector2[]
|
||
|
{
|
||
|
new Vector2(frame.xMin, frame.yMin),
|
||
|
new Vector2(frame.xMax, frame.yMin),
|
||
|
new Vector2(frame.xMin, frame.yMax),
|
||
|
new Vector2(frame.xMax, frame.yMax)
|
||
|
};
|
||
|
|
||
|
for(var i = 0; i < verts.Length; ++i)
|
||
|
CreateVertex(verts[i]);
|
||
|
|
||
|
var tris = new int[]
|
||
|
{
|
||
|
0, 2, 3, 1
|
||
|
};
|
||
|
|
||
|
for (var i = 0; i < tris.Length; ++i)
|
||
|
{
|
||
|
var n = (i + 1) % tris.Length;
|
||
|
CreateEdge(tris[i], tris[n]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public JobHandle TriangulateJob(ITriangulator triangulator, SpriteJobData spriteData)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
Debug.Assert(triangulator != null);
|
||
|
|
||
|
FillMeshDataContainers(out m_VerticesTemp, out m_EdgesTemp, out var weightData, out var hasWeightData);
|
||
|
return triangulator.ScheduleTriangulate(in m_VerticesTemp, in m_EdgesTemp, ref spriteData.vertices, ref spriteData.indices, ref spriteData.edges, ref spriteData.result);
|
||
|
}
|
||
|
|
||
|
public void Triangulate(ITriangulator triangulator)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
Debug.Assert(triangulator != null);
|
||
|
|
||
|
FillMeshDataContainers(out m_VerticesTemp, out m_EdgesTemp, out var weightData, out var hasWeightData);
|
||
|
triangulator.Triangulate(ref m_EdgesTemp, ref m_VerticesTemp, out var indices);
|
||
|
|
||
|
if (m_VerticesTemp.Length == 0 || indices.Length == 0)
|
||
|
{
|
||
|
spriteMeshData.Clear();
|
||
|
CreateQuad();
|
||
|
|
||
|
FillMeshDataContainers(out m_VerticesTemp, out m_EdgesTemp, out weightData, out hasWeightData);
|
||
|
triangulator.Triangulate(ref m_EdgesTemp, ref m_VerticesTemp, out indices);
|
||
|
}
|
||
|
|
||
|
spriteMeshData.Clear();
|
||
|
spriteMeshData.SetIndices(indices);
|
||
|
spriteMeshData.SetEdges(m_EdgesTemp);
|
||
|
|
||
|
var hasNewVertices = m_VerticesTemp.Length != weightData.Length;
|
||
|
for (var i = 0; i < m_VerticesTemp.Length; ++i)
|
||
|
{
|
||
|
var boneWeight = default(BoneWeight);
|
||
|
if (!hasNewVertices)
|
||
|
boneWeight = weightData[i].ToBoneWeight(true);
|
||
|
spriteMeshData.AddVertex(m_VerticesTemp[i], boneWeight);
|
||
|
}
|
||
|
|
||
|
if(hasNewVertices && hasWeightData)
|
||
|
CalculateWeights(new BoundedBiharmonicWeightsGenerator(), null, 0.01f);
|
||
|
}
|
||
|
|
||
|
void FillMeshDataContainers(out float2[] vertices, out int2[] edges, out EditableBoneWeight[] weightData, out bool hasWeightData)
|
||
|
{
|
||
|
edges = spriteMeshData.edges;
|
||
|
vertices = EditorUtilities.ToFloat2(spriteMeshData.vertices);
|
||
|
|
||
|
weightData = new EditableBoneWeight[spriteMeshData.vertexWeights.Length];
|
||
|
Array.Copy(spriteMeshData.vertexWeights, weightData, weightData.Length);
|
||
|
|
||
|
hasWeightData = false;
|
||
|
if (weightData.Length > 0 && weightData[0] != default)
|
||
|
hasWeightData = true;
|
||
|
}
|
||
|
|
||
|
public JobHandle Subdivide(ITriangulator triangulator, SpriteJobData spriteData, float largestAreaFactor, float areaThreshold)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
Debug.Assert(triangulator != null);
|
||
|
|
||
|
m_EdgesTemp = spriteMeshData.edges;
|
||
|
m_VerticesTemp = EditorUtilities.ToFloat2(spriteMeshData.vertices);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
triangulator.Tessellate(0f, 0f, 0f, largestAreaFactor, areaThreshold, 100, ref m_VerticesTemp, ref m_EdgesTemp, out var indices);
|
||
|
|
||
|
spriteMeshData.Clear();
|
||
|
|
||
|
for (var i = 0; i < m_VerticesTemp.Length; ++i)
|
||
|
spriteMeshData.AddVertex(m_VerticesTemp[i], default(BoneWeight));
|
||
|
|
||
|
spriteMeshData.SetIndices(indices);
|
||
|
spriteMeshData.SetEdges(m_EdgesTemp);
|
||
|
}
|
||
|
catch (Exception) { }
|
||
|
return default(JobHandle);
|
||
|
}
|
||
|
|
||
|
public void ClearWeights(ISelection<int> selection)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
|
||
|
for (var i = 0; i < spriteMeshData.vertexCount; ++i)
|
||
|
if (selection == null || (selection.Count == 0 || selection.Contains(i)))
|
||
|
spriteMeshData.vertexWeights[i].SetFromBoneWeight(default(BoneWeight));
|
||
|
}
|
||
|
|
||
|
public void OutlineFromAlpha(IOutlineGenerator outlineGenerator, ITextureDataProvider textureDataProvider, float outlineDetail, byte alphaTolerance)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null, "Assert failed. Expected: spriteMeshData != null. Actual: spriteMeshData == null");
|
||
|
Debug.Assert(textureDataProvider != null, "Assert failed. Expected: textureDataProvider != null. Actual: textureDataProvider == null");
|
||
|
Debug.Assert(textureDataProvider.texture != null, "Assert failed. Expected: textureDataProvider.texture != null. Actual: textureDataProvider.texture == null");
|
||
|
|
||
|
int width, height;
|
||
|
textureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||
|
|
||
|
var scale = new Vector2(textureDataProvider.texture.width / (float)width, textureDataProvider.texture.height / (float)height);
|
||
|
var scaleInv = new Vector2(1f / scale.x, 1f / scale.y);
|
||
|
var rectOffset = spriteMeshData.frame.size * 0.5f;
|
||
|
|
||
|
var scaledRect = spriteMeshData.frame;
|
||
|
scaledRect.min = Vector2.Scale(scaledRect.min, scale);
|
||
|
scaledRect.max = Vector2.Scale(scaledRect.max, scale);
|
||
|
|
||
|
spriteMeshData.Clear();
|
||
|
|
||
|
outlineGenerator.GenerateOutline(textureDataProvider, scaledRect, outlineDetail, alphaTolerance, false, out var paths);
|
||
|
|
||
|
var vertexIndexBase = 0;
|
||
|
|
||
|
var vertices = new List<Vector2>(spriteMeshData.vertices);
|
||
|
var edges = new List<int2>(spriteMeshData.edges);
|
||
|
for (var i = 0; i < paths.Length; ++i)
|
||
|
{
|
||
|
var numPathVertices = paths[i].Length;
|
||
|
|
||
|
for (var j = 0; j <= numPathVertices; j++)
|
||
|
{
|
||
|
if (j < numPathVertices)
|
||
|
vertices.Add(Vector2.Scale(paths[i][j], scaleInv) + rectOffset);
|
||
|
if (j > 0)
|
||
|
edges.Add(new int2(vertexIndexBase + j - 1, vertexIndexBase + j % numPathVertices));
|
||
|
}
|
||
|
|
||
|
vertexIndexBase += numPathVertices;
|
||
|
}
|
||
|
|
||
|
var vertexWeights = new EditableBoneWeight[vertices.Count];
|
||
|
for (var i = 0; i < vertexWeights.Length; ++i)
|
||
|
vertexWeights[i] = new EditableBoneWeight();
|
||
|
|
||
|
spriteMeshData.SetVertices(vertices.ToArray(), vertexWeights);
|
||
|
spriteMeshData.SetEdges(edges.ToArray());
|
||
|
}
|
||
|
|
||
|
public void NormalizeWeights(ISelection<int> selection)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
|
||
|
for (var i = 0; i < spriteMeshData.vertexCount; ++i)
|
||
|
if (selection == null || (selection.Count == 0 || selection.Contains(i)))
|
||
|
spriteMeshData.vertexWeights[i].Normalize();
|
||
|
}
|
||
|
|
||
|
public JobHandle CalculateWeightsJob(IWeightsGenerator weightsGenerator, ISelection<int> selection, float filterTolerance, SpriteJobData sd)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
|
||
|
GetControlPoints(out var controlPoints, out var bones, out var pins);
|
||
|
|
||
|
var vertices = EditorUtilities.ToFloat2(spriteMeshData.vertices);
|
||
|
var indices = spriteMeshData.indices;
|
||
|
var edges = spriteMeshData.edges;
|
||
|
|
||
|
return weightsGenerator.CalculateJob(spriteMeshData.spriteName, in vertices, in indices, in edges, in controlPoints, in bones, in pins, sd);
|
||
|
}
|
||
|
|
||
|
public void CalculateWeights(IWeightsGenerator weightsGenerator, ISelection<int> selection, float filterTolerance)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
|
||
|
GetControlPoints(out var controlPoints, out var bones, out var pins);
|
||
|
|
||
|
var vertices = EditorUtilities.ToFloat2(spriteMeshData.vertices);
|
||
|
var indices = spriteMeshData.indices;
|
||
|
var edges = spriteMeshData.edges;
|
||
|
|
||
|
var boneWeights = weightsGenerator.Calculate(spriteMeshData.spriteName, in vertices, in indices, in edges, in controlPoints, in bones, in pins);
|
||
|
|
||
|
Debug.Assert(boneWeights.Length == spriteMeshData.vertexCount);
|
||
|
|
||
|
for (var i = 0; i < spriteMeshData.vertexCount; ++i)
|
||
|
{
|
||
|
if (selection == null || (selection.Count == 0 || selection.Contains(i)))
|
||
|
{
|
||
|
var editableBoneWeight = EditableBoneWeightUtility.CreateFromBoneWeight(boneWeights[i]);
|
||
|
|
||
|
if (filterTolerance > 0f)
|
||
|
{
|
||
|
editableBoneWeight.FilterChannels(filterTolerance);
|
||
|
editableBoneWeight.Normalize();
|
||
|
}
|
||
|
|
||
|
spriteMeshData.vertexWeights[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.vertexWeights[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.vertexWeights[i].ToBoneWeight(false);
|
||
|
|
||
|
SmoothingUtility.SmoothWeights(boneWeights, spriteMeshData.indices, spriteMeshData.boneCount, iterations, out var smoothedWeights);
|
||
|
|
||
|
for (var i = 0; i < spriteMeshData.vertexCount; i++)
|
||
|
if (selection == null || (selection.Count == 0 || selection.Contains(i)))
|
||
|
spriteMeshData.vertexWeights[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.Length < 3)
|
||
|
return false;
|
||
|
|
||
|
var triangleCount = spriteMeshData.indices.Length / 3;
|
||
|
|
||
|
for (var 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.vertices[indices.x],
|
||
|
spriteMeshData.vertices[indices.y],
|
||
|
spriteMeshData.vertices[indices.z],
|
||
|
out barycentricCoords);
|
||
|
|
||
|
if (barycentricCoords.x >= 0f && barycentricCoords.y >= 0f && barycentricCoords.z >= 0f)
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
List<float> m_VertexOrderList = new List<float>(1000);
|
||
|
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.vertexWeights[i];
|
||
|
|
||
|
for (var j = 0; j < boneWeight.Count; ++j)
|
||
|
vertexOrder += spriteMeshData.GetBoneDepth(boneWeight[j].boneIndex) * boneWeight[j].weight;
|
||
|
|
||
|
m_VertexOrderList.Add(vertexOrder);
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < spriteMeshData.indices.Length; i += 3)
|
||
|
{
|
||
|
var p1 = spriteMeshData.indices[i];
|
||
|
var p2 = spriteMeshData.indices[i + 1];
|
||
|
var p3 = spriteMeshData.indices[i + 2];
|
||
|
var 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();
|
||
|
|
||
|
var newIndices = new int[m_WeightedTriangles.Count * 3];
|
||
|
for (var i = 0; i < m_WeightedTriangles.Count; ++i)
|
||
|
{
|
||
|
var triangle = m_WeightedTriangles[i];
|
||
|
var indexCount = i * 3;
|
||
|
newIndices[indexCount] = triangle.p1;
|
||
|
newIndices[indexCount + 1] = triangle.p2;
|
||
|
newIndices[indexCount + 2] = triangle.p3;
|
||
|
}
|
||
|
spriteMeshData.SetIndices(newIndices);
|
||
|
}
|
||
|
|
||
|
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 (var i in indices)
|
||
|
{
|
||
|
var editableBoneWeight = spriteMeshData.vertexWeights[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 (Mathf.Abs(weight - editableBoneWeight[channel].weight) > Mathf.Epsilon)
|
||
|
{
|
||
|
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");
|
||
|
|
||
|
var channelEnabledChanged = oldEnabled != newEnabled;
|
||
|
var boneIndexChanged = oldBoneIndex != newBoneIndex;
|
||
|
var weightChanged = Mathf.Abs(oldWeight - newWeight) > Mathf.Epsilon;
|
||
|
|
||
|
var indices = selection.elements;
|
||
|
|
||
|
foreach (var i in indices)
|
||
|
{
|
||
|
var editableBoneWeight = spriteMeshData.vertexWeights[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 float2[] points, out int2[] edges, out int[] pins)
|
||
|
{
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
|
||
|
points = null;
|
||
|
edges = null;
|
||
|
|
||
|
var pointList = new List<Vector2>();
|
||
|
var edgeList = new List<int2>();
|
||
|
var pinList = new List<int>();
|
||
|
var bones = new List<SpriteBoneData>(spriteMeshData.boneCount);
|
||
|
|
||
|
for (var 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)
|
||
|
{
|
||
|
var index1 = FindPoint(pointList, bone.position, 0.01f);
|
||
|
var 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 int2(index1, index2));
|
||
|
}
|
||
|
else if (bone.length == 0f)
|
||
|
{
|
||
|
pointList.Add(bone.position);
|
||
|
pinList.Add(pointList.Count - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
points = new float2[pointList.Count];
|
||
|
for (var i = 0; i < pointList.Count; ++i)
|
||
|
points[i] = pointList[i];
|
||
|
|
||
|
edges = edgeList.ToArray();
|
||
|
pins = pinList.ToArray();
|
||
|
}
|
||
|
|
||
|
static int FindPoint(IReadOnlyList<Vector2> points, Vector2 point, float distanceTolerance)
|
||
|
{
|
||
|
var sqrTolerance = distanceTolerance * distanceTolerance;
|
||
|
for (var 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.vertexWeights[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);
|
||
|
}
|
||
|
}
|
||
|
}
|