using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; namespace UnityEditor.U2D.Animation { internal class BoundedBiharmonicWeightsGenerator : IWeightsGenerator { internal static readonly BoneWeight defaultWeight = new BoneWeight() { weight0 = 1 }; private const int kNumIterations = -1; private const int kNumSamples = 10; private const float kMinAngle = 20f; private const float kLargestTriangleAreaFactor = 0.25f; private const float kMeshAreaFactor = 0.0015f; [DllImport("BoundedBiharmonicWeightsModule")] private static extern int Bbw(int iterations, [In, Out] IntPtr vertices, int vertexCount, int originalVertexCount, [In, Out] IntPtr indices, int indexCount, [In, Out] IntPtr controlPoints, int controlPointsCount, [In, Out] IntPtr boneEdges, int boneEdgesCount, [In, Out] IntPtr pinIndices, int pinIndexCount, [In, Out] IntPtr weights ); public BoneWeight[] Calculate(Vector2[] vertices, Edge[] edges, Vector2[] controlPoints, Edge[] bones, int[] pins) { var weights = new BoneWeight[vertices.Length]; for (var i = 0; i < weights.Length; ++i) weights[i] = defaultWeight; var indices = new List(vertices.Length); TriangulationUtility.Triangulate(vertices, edges, indices); if (indices.Count < 3) return weights; var boneSamples = SampleBones(controlPoints, bones, kNumSamples); var verticesList = new List(vertices.Length + controlPoints.Length + boneSamples.Length); var edgesList = new List(edges); var indicesList = new List(); verticesList.AddRange(vertices); verticesList.AddRange(controlPoints); verticesList.AddRange(boneSamples); try { TriangulationUtility.Tessellate(kMinAngle, 0f, kMeshAreaFactor, kLargestTriangleAreaFactor, 0, verticesList, edgesList, indicesList); } catch (Exception) { return weights; } var tessellatedVertices = verticesList.ToArray(); var tessellatedIndices = indicesList.ToArray(); GCHandle verticesHandle = GCHandle.Alloc(tessellatedVertices, GCHandleType.Pinned); GCHandle indicesHandle = GCHandle.Alloc(tessellatedIndices, GCHandleType.Pinned); GCHandle controlPointsHandle = GCHandle.Alloc(controlPoints, GCHandleType.Pinned); GCHandle bonesHandle = GCHandle.Alloc(bones, GCHandleType.Pinned); GCHandle pinsHandle = GCHandle.Alloc(pins, GCHandleType.Pinned); GCHandle weightsHandle = GCHandle.Alloc(weights, GCHandleType.Pinned); Bbw(kNumIterations, verticesHandle.AddrOfPinnedObject(), tessellatedVertices.Length, vertices.Length, indicesHandle.AddrOfPinnedObject(), tessellatedIndices.Length, controlPointsHandle.AddrOfPinnedObject(), controlPoints.Length, bonesHandle.AddrOfPinnedObject(), bones.Length, pinsHandle.AddrOfPinnedObject(), pins.Length, weightsHandle.AddrOfPinnedObject()); verticesHandle.Free(); indicesHandle.Free(); controlPointsHandle.Free(); bonesHandle.Free(); pinsHandle.Free(); weightsHandle.Free(); for (var i = 0; i < weights.Length; ++i) { var weight = weights[i]; if (weight.Sum() == 0f) weights[i] = defaultWeight; } return weights; } public void DebugMesh(ISpriteMeshData spriteMeshData, Vector2[] vertices, Edge[] edges, Vector2[] controlPoints, Edge[] bones, int[] pins) { var boneSamples = SampleBones(controlPoints, bones, kNumSamples); var verticesList = new List(vertices.Length + controlPoints.Length + boneSamples.Length); var edgesList = new List(edges); var indicesList = new List(); verticesList.AddRange(vertices); verticesList.AddRange(controlPoints); verticesList.AddRange(boneSamples); TriangulationUtility.Tessellate(kMinAngle, 0f, kMeshAreaFactor, kLargestTriangleAreaFactor, 0, verticesList, edgesList, indicesList); spriteMeshData.Clear(); verticesList.ForEach(v => spriteMeshData.AddVertex(v, new BoneWeight())); spriteMeshData.edges.AddRange(edgesList); spriteMeshData.indices.AddRange(indicesList); } private Vector2[] SampleBones(Vector2[] points, Edge[] edges, int numSamples) { Debug.Assert(numSamples > 0); var sampledEdges = new List(); for (var i = 0; i < edges.Length; i++) { var edge = edges[i]; var tip = points[edge.index1]; var tail = points[edge.index2]; var length = (tip - tail).magnitude; for (var s = 0; s < numSamples; s++) { var f = (s + 1f) / (float)(numSamples + 1f); sampledEdges.Add(f * tail + (1f - f) * tip); } } return sampledEdges.ToArray(); } } }