A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using Unity.Mathematics;
4using UnityEngine;
5using UnityEditor.U2D.Sprites;
6using Unity.Jobs;
7using Unity.Collections;
8
9namespace UnityEditor.U2D.Animation
10{
11 internal struct WeightedTriangle : IComparable<WeightedTriangle>
12 {
13 public int p1;
14 public int p2;
15 public int p3;
16 public float weight;
17
18 public int CompareTo(WeightedTriangle other)
19 {
20 return weight.CompareTo(other.weight);
21 }
22 }
23
24 internal struct SpriteJobData
25 {
26 public BaseSpriteMeshData spriteMesh;
27 public NativeArray<float2> vertices;
28 public NativeArray<int2> edges;
29 public NativeArray<int> indices;
30 public NativeArray<BoneWeight> weights;
31 public NativeArray<int4> result;
32 };
33
34 internal class SpriteMeshDataController
35 {
36 public BaseSpriteMeshData spriteMeshData;
37 float2[] m_VerticesTemp = new float2[0];
38 int2[] m_EdgesTemp = new int2[0];
39
40 public void CreateVertex(Vector2 position)
41 {
42 CreateVertex(position, -1);
43 }
44
45 public void CreateVertex(Vector2 position, int edgeIndex)
46 {
47 Debug.Assert(spriteMeshData != null, "Assert failed. Expected: spriteMeshData != null. Actual: spriteMeshData == null");
48
49 spriteMeshData.AddVertex(position, default(BoneWeight));
50
51 if (edgeIndex != -1)
52 {
53 var edge = spriteMeshData.edges[edgeIndex];
54 RemoveEdge(edge);
55 CreateEdge(edge.x, spriteMeshData.vertexCount - 1);
56 CreateEdge(edge.y, spriteMeshData.vertexCount - 1);
57 }
58 }
59
60 public void CreateEdge(int index1, int index2)
61 {
62 Debug.Assert(spriteMeshData != null, "Assert failed. Expected: spriteMeshData != null. Actual: spriteMeshData == null");
63 Debug.Assert(index1 >= 0, $"Assert failed. Expected: index1 >= 0. Actual: index1 == {index1}");
64 Debug.Assert(index2 >= 0, $"Assert failed. Expected: index2 >= 0. Actual: index2 == {index2}");
65 Debug.Assert(index1 < spriteMeshData.vertexCount, $"Assert failed. Expected: index1 < spriteMeshData.vertexCount. Actual: index1 == {index1} spriteMeshData.vertexCount == {spriteMeshData.vertexCount}");
66 Debug.Assert(index2 < spriteMeshData.vertexCount, $"Assert failed. Expected: index2 < spriteMeshData.vertexCount. Actual: index2 == {index2} spriteMeshData.vertexCount == {spriteMeshData.vertexCount}");
67 Debug.Assert(index1 != index2, $"Assert failed. Expected: index1 != index2. Actual: index1 == {index1} index2 == {index2}");
68
69 var newEdge = new int2(index1, index2);
70 if (!spriteMeshData.edges.ContainsAny(newEdge))
71 {
72 var listOfEdges = new List<int2>(spriteMeshData.edges)
73 {
74 newEdge
75 };
76 spriteMeshData.SetEdges(listOfEdges.ToArray());
77 }
78 }
79
80 public void RemoveVertex(int index)
81 {
82 Debug.Assert(spriteMeshData != null);
83
84 //We need to delete the edges that reference the index
85 if (FindEdgesContainsIndex(index, out var edgesWithIndex))
86 {
87 //If there are 2 edges referencing the same index we are removing, we can create a new one that connects the endpoints ("Unsplit").
88 if (edgesWithIndex.Count == 2)
89 {
90 var first = edgesWithIndex[0];
91 var second = edgesWithIndex[1];
92
93 int index1 = first.x != index ? first.x : first.y;
94 int index2 = second.x != index ? second.x : second.y;
95
96 CreateEdge(index1, index2);
97 }
98
99 //remove found edges
100 for (int i = 0; i < edgesWithIndex.Count; i++)
101 {
102 RemoveEdge(edgesWithIndex[i]);
103 }
104 }
105
106 //Fix indices in edges greater than the one we are removing
107 for (int i = 0; i < spriteMeshData.edges.Length; i++)
108 {
109 var edge = spriteMeshData.edges[i];
110
111 if (edge.x > index)
112 edge.x--;
113 if (edge.y > index)
114 edge.y--;
115
116 spriteMeshData.edges[i] = edge;
117 }
118
119 spriteMeshData.RemoveVertex(index);
120 }
121
122 public void RemoveVertex(IEnumerable<int> indices)
123 {
124 var sortedIndexList = new List<int>(indices);
125
126 if (sortedIndexList.Count == 0)
127 return;
128
129 sortedIndexList.Sort();
130
131 for (var i = sortedIndexList.Count - 1; i >= 0; --i)
132 {
133 RemoveVertex(sortedIndexList[i]);
134 }
135 }
136
137 void RemoveEdge(int2 edge)
138 {
139 Debug.Assert(spriteMeshData != null);
140 var listOfEdges = new List<int2>(spriteMeshData.edges);
141 listOfEdges.Remove(edge);
142 spriteMeshData.SetEdges(listOfEdges.ToArray());
143 }
144
145 bool FindEdgesContainsIndex(int index, out List<int2> result)
146 {
147 Debug.Assert(spriteMeshData != null);
148
149 bool found = false;
150
151 result = new List<int2>();
152
153 for (int i = 0; i < spriteMeshData.edges.Length; ++i)
154 {
155 var edge = spriteMeshData.edges[i];
156 if (edge.x == index || edge.y == index)
157 {
158 found = true;
159 result.Add(edge);
160 }
161 }
162
163 return found;
164 }
165
166 public void CreateQuad()
167 {
168 var frame = new Rect(Vector2.zero, spriteMeshData.frame.size);
169 var verts = new Vector2[]
170 {
171 new Vector2(frame.xMin, frame.yMin),
172 new Vector2(frame.xMax, frame.yMin),
173 new Vector2(frame.xMin, frame.yMax),
174 new Vector2(frame.xMax, frame.yMax)
175 };
176
177 for (var i = 0; i < verts.Length; ++i)
178 CreateVertex(verts[i]);
179
180 var tris = new int[]
181 {
182 0, 2, 3, 1
183 };
184
185 for (var i = 0; i < tris.Length; ++i)
186 {
187 var n = (i + 1) % tris.Length;
188 CreateEdge(tris[i], tris[n]);
189 }
190 }
191
192 public JobHandle TriangulateJob(ITriangulator triangulator, SpriteJobData spriteData)
193 {
194 Debug.Assert(spriteMeshData != null);
195 Debug.Assert(triangulator != null);
196
197 FillMeshDataContainers(out m_VerticesTemp, out m_EdgesTemp, out var weightData, out var hasWeightData);
198 return triangulator.ScheduleTriangulate(in m_VerticesTemp, in m_EdgesTemp, ref spriteData.vertices, ref spriteData.indices, ref spriteData.edges, ref spriteData.result);
199 }
200
201 public void Triangulate(ITriangulator triangulator)
202 {
203 Debug.Assert(spriteMeshData != null);
204 Debug.Assert(triangulator != null);
205
206 FillMeshDataContainers(out m_VerticesTemp, out m_EdgesTemp, out var weightData, out var hasWeightData);
207 triangulator.Triangulate(ref m_EdgesTemp, ref m_VerticesTemp, out var indices);
208
209 if (m_VerticesTemp.Length == 0 || indices.Length == 0)
210 {
211 spriteMeshData.Clear();
212 CreateQuad();
213
214 FillMeshDataContainers(out m_VerticesTemp, out m_EdgesTemp, out weightData, out hasWeightData);
215 triangulator.Triangulate(ref m_EdgesTemp, ref m_VerticesTemp, out indices);
216 }
217
218 spriteMeshData.Clear();
219 spriteMeshData.SetIndices(indices);
220 spriteMeshData.SetEdges(m_EdgesTemp);
221
222 var hasNewVertices = m_VerticesTemp.Length != weightData.Length;
223 for (var i = 0; i < m_VerticesTemp.Length; ++i)
224 {
225 var boneWeight = default(BoneWeight);
226 if (!hasNewVertices)
227 boneWeight = weightData[i].ToBoneWeight(true);
228 spriteMeshData.AddVertex(m_VerticesTemp[i], boneWeight);
229 }
230
231 if (hasNewVertices && hasWeightData)
232 CalculateWeights(new BoundedBiharmonicWeightsGenerator(), null, 0.01f);
233 }
234
235 void FillMeshDataContainers(out float2[] vertices, out int2[] edges, out EditableBoneWeight[] weightData, out bool hasWeightData)
236 {
237 edges = spriteMeshData.edges;
238 vertices = EditorUtilities.ToFloat2(spriteMeshData.vertices);
239
240 weightData = new EditableBoneWeight[spriteMeshData.vertexWeights.Length];
241 Array.Copy(spriteMeshData.vertexWeights, weightData, weightData.Length);
242
243 hasWeightData = false;
244 if (weightData.Length > 0 && weightData[0] != default)
245 hasWeightData = true;
246 }
247
248 public JobHandle Subdivide(ITriangulator triangulator, SpriteJobData spriteData, float largestAreaFactor, float areaThreshold)
249 {
250 Debug.Assert(spriteMeshData != null);
251 Debug.Assert(triangulator != null);
252
253 m_EdgesTemp = spriteMeshData.edges;
254 m_VerticesTemp = EditorUtilities.ToFloat2(spriteMeshData.vertices);
255
256 try
257 {
258 triangulator.Tessellate(0f, 0f, 0f, largestAreaFactor, areaThreshold, 100, ref m_VerticesTemp, ref m_EdgesTemp, out var indices);
259
260 spriteMeshData.Clear();
261
262 for (var i = 0; i < m_VerticesTemp.Length; ++i)
263 spriteMeshData.AddVertex(m_VerticesTemp[i], default(BoneWeight));
264
265 spriteMeshData.SetIndices(indices);
266 spriteMeshData.SetEdges(m_EdgesTemp);
267 }
268 catch (Exception) { }
269
270 return default(JobHandle);
271 }
272
273 public void ClearWeights(ISelection<int> selection)
274 {
275 Debug.Assert(spriteMeshData != null);
276
277 for (var i = 0; i < spriteMeshData.vertexCount; ++i)
278 if (selection == null || (selection.Count == 0 || selection.Contains(i)))
279 spriteMeshData.vertexWeights[i].SetFromBoneWeight(default(BoneWeight));
280 }
281
282 public void OutlineFromAlpha(IOutlineGenerator outlineGenerator, ITextureDataProvider textureDataProvider, float outlineDetail, byte alphaTolerance)
283 {
284 Debug.Assert(spriteMeshData != null, "Assert failed. Expected: spriteMeshData != null. Actual: spriteMeshData == null");
285 Debug.Assert(textureDataProvider != null, "Assert failed. Expected: textureDataProvider != null. Actual: textureDataProvider == null");
286 Debug.Assert(textureDataProvider.texture != null, "Assert failed. Expected: textureDataProvider.texture != null. Actual: textureDataProvider.texture == null");
287
288 int width, height;
289 textureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
290
291 var scale = new Vector2(textureDataProvider.texture.width / (float)width, textureDataProvider.texture.height / (float)height);
292 var scaleInv = new Vector2(1f / scale.x, 1f / scale.y);
293 var rectOffset = spriteMeshData.frame.size * 0.5f;
294
295 var scaledRect = spriteMeshData.frame;
296 scaledRect.min = Vector2.Scale(scaledRect.min, scale);
297 scaledRect.max = Vector2.Scale(scaledRect.max, scale);
298
299 spriteMeshData.Clear();
300
301 outlineGenerator.GenerateOutline(textureDataProvider, scaledRect, outlineDetail, alphaTolerance, false, out var paths);
302
303 var vertexIndexBase = 0;
304
305 var vertices = new List<Vector2>(spriteMeshData.vertices);
306 var edges = new List<int2>(spriteMeshData.edges);
307 for (var i = 0; i < paths.Length; ++i)
308 {
309 var numPathVertices = paths[i].Length;
310
311 for (var j = 0; j <= numPathVertices; j++)
312 {
313 if (j < numPathVertices)
314 vertices.Add(Vector2.Scale(paths[i][j], scaleInv) + rectOffset);
315 if (j > 0)
316 edges.Add(new int2(vertexIndexBase + j - 1, vertexIndexBase + j % numPathVertices));
317 }
318
319 vertexIndexBase += numPathVertices;
320 }
321
322 var vertexWeights = new EditableBoneWeight[vertices.Count];
323 for (var i = 0; i < vertexWeights.Length; ++i)
324 vertexWeights[i] = new EditableBoneWeight();
325
326 spriteMeshData.SetVertices(vertices.ToArray(), vertexWeights);
327 spriteMeshData.SetEdges(edges.ToArray());
328 }
329
330 public void NormalizeWeights(ISelection<int> selection)
331 {
332 Debug.Assert(spriteMeshData != null);
333
334 for (var i = 0; i < spriteMeshData.vertexCount; ++i)
335 if (selection == null || (selection.Count == 0 || selection.Contains(i)))
336 spriteMeshData.vertexWeights[i].Normalize();
337 }
338
339 public JobHandle CalculateWeightsJob(IWeightsGenerator weightsGenerator, ISelection<int> selection, float filterTolerance, SpriteJobData sd)
340 {
341 Debug.Assert(spriteMeshData != null);
342
343 GetControlPoints(out var controlPoints, out var bones, out var pins);
344
345 var vertices = EditorUtilities.ToFloat2(spriteMeshData.vertices);
346 var indices = spriteMeshData.indices;
347 var edges = spriteMeshData.edges;
348
349 return weightsGenerator.CalculateJob(spriteMeshData.spriteName, in vertices, in indices, in edges, in controlPoints, in bones, in pins, sd);
350 }
351
352 public void CalculateWeights(IWeightsGenerator weightsGenerator, ISelection<int> selection, float filterTolerance)
353 {
354 Debug.Assert(spriteMeshData != null);
355
356 GetControlPoints(out var controlPoints, out var bones, out var pins);
357
358 var vertices = EditorUtilities.ToFloat2(spriteMeshData.vertices);
359 var indices = spriteMeshData.indices;
360 var edges = spriteMeshData.edges;
361
362 var boneWeights = weightsGenerator.Calculate(spriteMeshData.spriteName, in vertices, in indices, in edges, in controlPoints, in bones, in pins);
363
364 Debug.Assert(boneWeights.Length == spriteMeshData.vertexCount);
365
366 for (var i = 0; i < spriteMeshData.vertexCount; ++i)
367 {
368 if (selection == null || (selection.Count == 0 || selection.Contains(i)))
369 {
370 var editableBoneWeight = EditableBoneWeightUtility.CreateFromBoneWeight(boneWeights[i]);
371
372 if (filterTolerance > 0f)
373 {
374 editableBoneWeight.FilterChannels(filterTolerance);
375 editableBoneWeight.Normalize();
376 }
377
378 spriteMeshData.vertexWeights[i] = editableBoneWeight;
379 }
380 }
381 }
382
383 public void CalculateWeightsSafe(IWeightsGenerator weightsGenerator, ISelection<int> selection, float filterTolerance)
384 {
385 var tempSelection = new IndexedSelection();
386 var vertexSelector = new GenericVertexSelector();
387
388 vertexSelector.spriteMeshData = spriteMeshData;
389 vertexSelector.selection = tempSelection;
390 vertexSelector.SelectionCallback = (int i) =>
391 {
392 return spriteMeshData.vertexWeights[i].Sum() == 0f && (selection == null || selection.Count == 0 || selection.Contains(i));
393 };
394 vertexSelector.Select();
395
396 if (tempSelection.Count > 0)
397 CalculateWeights(weightsGenerator, tempSelection, filterTolerance);
398 }
399
400 public void SmoothWeights(int iterations, ISelection<int> selection)
401 {
402 var boneWeights = new BoneWeight[spriteMeshData.vertexCount];
403
404 for (var i = 0; i < spriteMeshData.vertexCount; i++)
405 boneWeights[i] = spriteMeshData.vertexWeights[i].ToBoneWeight(false);
406
407 SmoothingUtility.SmoothWeights(boneWeights, spriteMeshData.indices, spriteMeshData.boneCount, iterations, out var smoothedWeights);
408
409 for (var i = 0; i < spriteMeshData.vertexCount; i++)
410 if (selection == null || (selection.Count == 0 || selection.Contains(i)))
411 spriteMeshData.vertexWeights[i].SetFromBoneWeight(smoothedWeights[i]);
412 }
413
414 public bool FindTriangle(Vector2 point, out Vector3Int indices, out Vector3 barycentricCoords)
415 {
416 Debug.Assert(spriteMeshData != null);
417
418 indices = Vector3Int.zero;
419 barycentricCoords = Vector3.zero;
420
421 if (spriteMeshData.indices.Length < 3)
422 return false;
423
424 var triangleCount = spriteMeshData.indices.Length / 3;
425
426 for (var i = 0; i < triangleCount; ++i)
427 {
428 indices.x = spriteMeshData.indices[i * 3];
429 indices.y = spriteMeshData.indices[i * 3 + 1];
430 indices.z = spriteMeshData.indices[i * 3 + 2];
431
432 MathUtility.Barycentric(
433 point,
434 spriteMeshData.vertices[indices.x],
435 spriteMeshData.vertices[indices.y],
436 spriteMeshData.vertices[indices.z],
437 out barycentricCoords);
438
439 if (barycentricCoords.x >= 0f && barycentricCoords.y >= 0f && barycentricCoords.z >= 0f)
440 return true;
441 }
442
443 return false;
444 }
445
446 List<float> m_VertexOrderList = new List<float>(1000);
447 List<WeightedTriangle> m_WeightedTriangles = new List<WeightedTriangle>(1000);
448
449 public void SortTrianglesByDepth()
450 {
451 Debug.Assert(spriteMeshData != null);
452
453 if (spriteMeshData.boneCount == 0)
454 return;
455
456 m_VertexOrderList.Clear();
457 m_WeightedTriangles.Clear();
458
459 for (var i = 0; i < spriteMeshData.vertexCount; i++)
460 {
461 var vertexOrder = 0f;
462 var boneWeight = spriteMeshData.vertexWeights[i];
463
464 for (var j = 0; j < boneWeight.Count; ++j)
465 vertexOrder += spriteMeshData.GetBoneDepth(boneWeight[j].boneIndex) * boneWeight[j].weight;
466
467 m_VertexOrderList.Add(vertexOrder);
468 }
469
470 for (var i = 0; i < spriteMeshData.indices.Length; i += 3)
471 {
472 var p1 = spriteMeshData.indices[i];
473 var p2 = spriteMeshData.indices[i + 1];
474 var p3 = spriteMeshData.indices[i + 2];
475 var weight = (m_VertexOrderList[p1] + m_VertexOrderList[p2] + m_VertexOrderList[p3]) / 3f;
476
477 m_WeightedTriangles.Add(new WeightedTriangle() { p1 = p1, p2 = p2, p3 = p3, weight = weight });
478 }
479
480 m_WeightedTriangles.Sort();
481
482 var newIndices = new int[m_WeightedTriangles.Count * 3];
483 for (var i = 0; i < m_WeightedTriangles.Count; ++i)
484 {
485 var triangle = m_WeightedTriangles[i];
486 var indexCount = i * 3;
487 newIndices[indexCount] = triangle.p1;
488 newIndices[indexCount + 1] = triangle.p2;
489 newIndices[indexCount + 2] = triangle.p3;
490 }
491
492 spriteMeshData.SetIndices(newIndices);
493 }
494
495 public void GetMultiEditChannelData(ISelection<int> selection, int channel,
496 out bool enabled, out int boneIndex, out float weight,
497 out bool isEnabledMixed, out bool isBoneIndexMixed, out bool isWeightMixed)
498 {
499 Debug.Assert(spriteMeshData != null);
500
501 if (selection == null)
502 throw new ArgumentNullException("selection is null");
503
504 var first = true;
505 enabled = false;
506 boneIndex = -1;
507 weight = 0f;
508 isEnabledMixed = false;
509 isBoneIndexMixed = false;
510 isWeightMixed = false;
511
512 var indices = selection.elements;
513
514 foreach (var i in indices)
515 {
516 var editableBoneWeight = spriteMeshData.vertexWeights[i];
517
518 if (first)
519 {
520 enabled = editableBoneWeight[channel].enabled;
521 boneIndex = editableBoneWeight[channel].boneIndex;
522 weight = editableBoneWeight[channel].weight;
523
524 first = false;
525 }
526 else
527 {
528 if (enabled != editableBoneWeight[channel].enabled)
529 {
530 isEnabledMixed = true;
531 enabled = false;
532 }
533
534 if (boneIndex != editableBoneWeight[channel].boneIndex)
535 {
536 isBoneIndexMixed = true;
537 boneIndex = -1;
538 }
539
540 if (Mathf.Abs(weight - editableBoneWeight[channel].weight) > Mathf.Epsilon)
541 {
542 isWeightMixed = true;
543 weight = 0f;
544 }
545 }
546 }
547 }
548
549 public void SetMultiEditChannelData(ISelection<int> selection, int channel,
550 bool oldEnabled, bool newEnabled, int oldBoneIndex, int newBoneIndex, float oldWeight, float newWeight)
551 {
552 Debug.Assert(spriteMeshData != null);
553
554 if (selection == null)
555 throw new ArgumentNullException("selection is null");
556
557 var channelEnabledChanged = oldEnabled != newEnabled;
558 var boneIndexChanged = oldBoneIndex != newBoneIndex;
559 var weightChanged = Mathf.Abs(oldWeight - newWeight) > Mathf.Epsilon;
560
561 var indices = selection.elements;
562
563 foreach (var i in indices)
564 {
565 var editableBoneWeight = spriteMeshData.vertexWeights[i];
566
567 if (channelEnabledChanged)
568 editableBoneWeight[channel].enabled = newEnabled;
569
570 if (boneIndexChanged)
571 editableBoneWeight[channel].boneIndex = newBoneIndex;
572
573 if (weightChanged)
574 editableBoneWeight[channel].weight = newWeight;
575
576 if (channelEnabledChanged || weightChanged)
577 editableBoneWeight.CompensateOtherChannels(channel);
578 }
579 }
580
581 public void GetControlPoints(out float2[] points, out int2[] edges, out int[] pins)
582 {
583 Debug.Assert(spriteMeshData != null);
584
585 points = null;
586 edges = null;
587
588 var pointList = new List<Vector2>();
589 var edgeList = new List<int2>();
590 var pinList = new List<int>();
591 var bones = new List<SpriteBoneData>(spriteMeshData.boneCount);
592
593 for (var i = 0; i < spriteMeshData.boneCount; ++i)
594 bones.Add(spriteMeshData.GetBoneData(i));
595
596 foreach (var bone in bones)
597 {
598 var length = (bone.endPosition - bone.position).magnitude;
599
600 if (length > 0f)
601 {
602 var index1 = FindPoint(pointList, bone.position, 0.01f);
603 var index2 = FindPoint(pointList, bone.endPosition, 0.01f);
604
605 if (index1 == -1)
606 {
607 pointList.Add(bone.position);
608 index1 = pointList.Count - 1;
609 }
610
611 if (index2 == -1)
612 {
613 pointList.Add(bone.endPosition);
614 index2 = pointList.Count - 1;
615 }
616
617 edgeList.Add(new int2(index1, index2));
618 }
619 else if (bone.length == 0f)
620 {
621 pointList.Add(bone.position);
622 pinList.Add(pointList.Count - 1);
623 }
624 }
625
626 points = new float2[pointList.Count];
627 for (var i = 0; i < pointList.Count; ++i)
628 points[i] = pointList[i];
629
630 edges = edgeList.ToArray();
631 pins = pinList.ToArray();
632 }
633
634 static int FindPoint(IReadOnlyList<Vector2> points, Vector2 point, float distanceTolerance)
635 {
636 var sqrTolerance = distanceTolerance * distanceTolerance;
637 for (var i = 0; i < points.Count; ++i)
638 {
639 if ((points[i] - point).sqrMagnitude <= sqrTolerance)
640 return i;
641 }
642
643 return -1;
644 }
645
646 public void SmoothFill()
647 {
648 var tempSelection = new IndexedSelection();
649 var vertexSelector = new GenericVertexSelector();
650 var currentWeightSum = 0f;
651 var prevWeightSum = 0f;
652
653 vertexSelector.spriteMeshData = spriteMeshData;
654 vertexSelector.selection = tempSelection;
655 vertexSelector.SelectionCallback = (int i) =>
656 {
657 var sum = spriteMeshData.vertexWeights[i].Sum();
658 currentWeightSum += sum;
659 return sum < 0.99f;
660 };
661
662 do
663 {
664 prevWeightSum = currentWeightSum;
665 currentWeightSum = 0f;
666 vertexSelector.Select();
667
668 if (tempSelection.Count > 0)
669 SmoothWeights(1, tempSelection);
670 } while (currentWeightSum - prevWeightSum > 0.001f);
671
672 if (tempSelection.Count > 0)
673 NormalizeWeights(tempSelection);
674 }
675 }
676}