A game about forced loneliness, made by TACStudios
at master 365 lines 14 kB view raw
1using System; 2using System.Collections.Generic; 3using Unity.Collections; 4using Unity.Jobs; 5using Unity.Mathematics; 6using UnityEditor.U2D.Common; 7using UnityEditor.U2D.Layout; 8using UnityEngine; 9 10namespace UnityEditor.U2D.Animation 11{ 12 internal class GenerateGeometryTool : MeshToolWrapper 13 { 14 private const float kWeightTolerance = 0.1f; 15 private SpriteMeshDataController m_SpriteMeshDataController = new SpriteMeshDataController(); 16 private ITriangulator m_Triangulator; 17 private IOutlineGenerator m_OutlineGenerator; 18 private IWeightsGenerator m_WeightGenerator; 19 private GenerateGeometryPanel m_GenerateGeometryPanel; 20 21 internal override void OnCreate() 22 { 23 m_Triangulator = new Triangulator(); 24 m_OutlineGenerator = new OutlineGenerator(); 25 m_WeightGenerator = new BoundedBiharmonicWeightsGenerator(); 26 } 27 28 public override void Initialize(LayoutOverlay layout) 29 { 30 base.Initialize(layout); 31 32 m_GenerateGeometryPanel = GenerateGeometryPanel.GenerateFromUXML(); 33 m_GenerateGeometryPanel.skinningCache = skinningCache; 34 35 layout.rightOverlay.Add(m_GenerateGeometryPanel); 36 37 BindElements(); 38 Hide(); 39 } 40 41 private void BindElements() 42 { 43 Debug.Assert(m_GenerateGeometryPanel != null); 44 45 m_GenerateGeometryPanel.onAutoGenerateGeometry += (float detail, byte alpha, float subdivide) => 46 { 47 var selectedSprite = skinningCache.selectedSprite; 48 if (selectedSprite != null) 49 GenerateGeometryForSprites(new[] { selectedSprite }, detail, alpha, subdivide); 50 }; 51 52 m_GenerateGeometryPanel.onAutoGenerateGeometryAll += (float detail, byte alpha, float subdivide) => 53 { 54 var sprites = skinningCache.GetSprites(); 55 GenerateGeometryForSprites(sprites, detail, alpha, subdivide); 56 }; 57 } 58 59 void GenerateGeometryForSprites(SpriteCache[] sprites, float detail, byte alpha, float subdivide) 60 { 61 var cancelProgress = false; 62 63 using (skinningCache.UndoScope(TextContent.generateGeometry)) 64 { 65 66 float progressMax = sprites.Length * 4; // for ProgressBar 67 int validSpriteCount = 0; 68 69 // 70 // Generate Outline 71 // 72 for (var i = 0; i < sprites.Length; ++i) 73 { 74 var sprite = sprites[i]; 75 if (!sprite.IsVisible()) 76 continue; 77 78 Debug.Assert(sprite != null); 79 var mesh = sprite.GetMesh(); 80 Debug.Assert(mesh != null); 81 82 m_SpriteMeshDataController.spriteMeshData = mesh; 83 validSpriteCount++; 84 85 cancelProgress = EditorUtility.DisplayCancelableProgressBar(TextContent.generatingOutline, sprite.name, i / progressMax); 86 if (cancelProgress) 87 break; 88 m_SpriteMeshDataController.OutlineFromAlpha(m_OutlineGenerator, mesh.textureDataProvider, detail / 100f, alpha); 89 } 90 91 // 92 // Generate Base Mesh Threaded. 93 // 94 const int maxDataCount = 65536; 95 var spriteList = new List<SpriteJobData>(); 96 var jobHandles = new NativeArray<JobHandle>(validSpriteCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory); 97 int jobCount = 0; 98 99 for (var i = 0; i < sprites.Length; ++i) 100 { 101 var sprite = sprites[i]; 102 if (!sprite.IsVisible()) 103 continue; 104 105 cancelProgress = EditorUtility.DisplayCancelableProgressBar(TextContent.triangulatingGeometry, sprite.name, 0.25f + (i / progressMax)); 106 if (cancelProgress) 107 break; 108 109 var mesh = sprite.GetMesh(); 110 m_SpriteMeshDataController.spriteMeshData = mesh; 111 112 SpriteJobData sd = new SpriteJobData(); 113 sd.spriteMesh = mesh; 114 sd.vertices = new NativeArray<float2>(maxDataCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); 115 sd.edges = new NativeArray<int2>(maxDataCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); 116 sd.indices = new NativeArray<int>(maxDataCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); 117 sd.weights = new NativeArray<BoneWeight>(maxDataCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); 118 sd.result = new NativeArray<int4>(1, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); 119 sd.result[0] = int4.zero; 120 spriteList.Add(sd); 121 if (m_GenerateGeometryPanel.generateWeights) 122 { 123 jobHandles[jobCount] = m_SpriteMeshDataController.TriangulateJob(m_Triangulator, sd); 124 } 125 else 126 { 127 jobHandles[jobCount] = default(JobHandle); 128 m_SpriteMeshDataController.Triangulate(m_Triangulator); 129 } 130 jobCount++; 131 } 132 JobHandle.CombineDependencies(jobHandles).Complete(); 133 134 // 135 // Generate Base Mesh Fallback. 136 // 137 for (var i = 0; i < spriteList.Count; i++) 138 { 139 var sd = spriteList[i]; 140 if (math.all(sd.result[0].xy)) 141 { 142 sd.spriteMesh.Clear(); 143 144 var edges = new int2[sd.result[0].z]; 145 var indices = new int[sd.result[0].y]; 146 147 for (var j = 0; j < sd.result[0].x; ++j) 148 sd.spriteMesh.AddVertex(sd.vertices[j], default(BoneWeight)); 149 for (var j = 0; j < sd.result[0].y; ++j) 150 indices[j] = sd.indices[j]; 151 for (var j = 0; j < sd.result[0].z; ++j) 152 edges[j] = sd.edges[j]; 153 154 sd.spriteMesh.SetEdges(edges); 155 sd.spriteMesh.SetIndices(indices); 156 } 157 else 158 { 159 m_SpriteMeshDataController.spriteMeshData = sd.spriteMesh; 160 m_SpriteMeshDataController.Triangulate(m_Triangulator); 161 } 162 } 163 164 // 165 // Subdivide. 166 // 167 168 jobCount = 0; 169 if (subdivide > 0f) 170 { 171 var largestAreaFactor = subdivide != 0 ? Mathf.Lerp(0.5f, 0.05f, Math.Min(subdivide, 100f) / 100f) : subdivide; 172 173 for (var i = 0; i < sprites.Length; ++i) 174 { 175 var sprite = sprites[i]; 176 if (!sprite.IsVisible()) 177 continue; 178 179 cancelProgress = EditorUtility.DisplayCancelableProgressBar(TextContent.subdividingGeometry, sprite.name, 0.5f + (i / progressMax)); 180 if (cancelProgress) 181 break; 182 183 var mesh = sprite.GetMesh(); 184 m_SpriteMeshDataController.spriteMeshData = mesh; 185 186 var sd = spriteList[i]; 187 sd.spriteMesh = mesh; 188 sd.result[0] = int4.zero; 189 m_SpriteMeshDataController.Subdivide(m_Triangulator, sd, largestAreaFactor, 0f); 190 } 191 192 } 193 194 // 195 // Weight. 196 // 197 jobCount = 0; 198 if (m_GenerateGeometryPanel.generateWeights) 199 { 200 201 for (var i = 0; i < sprites.Length; i++) 202 { 203 var sprite = sprites[i]; 204 if (!sprite.IsVisible()) 205 continue; 206 207 var mesh = sprite.GetMesh(); 208 m_SpriteMeshDataController.spriteMeshData = mesh; 209 210 cancelProgress = EditorUtility.DisplayCancelableProgressBar(TextContent.generatingWeights, sprite.name, 0.75f + (i / progressMax)); 211 if (cancelProgress) 212 break; 213 214 var sd = spriteList[i]; 215 jobHandles[jobCount] = GenerateWeights(sprite, sd); 216 jobCount++; 217 } 218 219 // Weight 220 JobHandle.CombineDependencies(jobHandles).Complete(); 221 222 for (var i = 0; i < sprites.Length; i++) 223 { 224 var sprite = sprites[i]; 225 if (!sprite.IsVisible()) 226 continue; 227 228 var mesh = sprite.GetMesh(); 229 m_SpriteMeshDataController.spriteMeshData = mesh; 230 var sd = spriteList[i]; 231 232 for (var j = 0; j < mesh.vertexCount; ++j) 233 { 234 var editableBoneWeight = EditableBoneWeightUtility.CreateFromBoneWeight(sd.weights[j]); 235 236 if (kWeightTolerance > 0f) 237 { 238 editableBoneWeight.FilterChannels(kWeightTolerance); 239 editableBoneWeight.Normalize(); 240 } 241 242 mesh.vertexWeights[j] = editableBoneWeight; 243 } 244 if (null != sprite.GetCharacterPart()) 245 sprite.DeassociateUnusedBones(); 246 m_SpriteMeshDataController.SortTrianglesByDepth(); 247 } 248 249 } 250 251 for (var i = 0; i < spriteList.Count; i++) 252 { 253 var sd = spriteList[i]; 254 sd.result.Dispose(); 255 sd.indices.Dispose(); 256 sd.edges.Dispose(); 257 sd.vertices.Dispose(); 258 sd.weights.Dispose(); 259 } 260 261 if (!cancelProgress) 262 { 263 skinningCache.vertexSelection.Clear(); 264 foreach(var sprite in sprites) 265 skinningCache.events.meshChanged.Invoke(sprite.GetMesh()); 266 } 267 268 EditorUtility.ClearProgressBar(); 269 } 270 271 if(cancelProgress) 272 Undo.PerformUndo(); 273 } 274 275 protected override void OnActivate() 276 { 277 base.OnActivate(); 278 UpdateButton(); 279 Show(); 280 skinningCache.events.selectedSpriteChanged.AddListener(OnSelectedSpriteChanged); 281 } 282 283 protected override void OnDeactivate() 284 { 285 base.OnDeactivate(); 286 Hide(); 287 skinningCache.events.selectedSpriteChanged.RemoveListener(OnSelectedSpriteChanged); 288 } 289 290 private void Show() 291 { 292 m_GenerateGeometryPanel.SetHiddenFromLayout(false); 293 } 294 295 private void Hide() 296 { 297 m_GenerateGeometryPanel.SetHiddenFromLayout(true); 298 } 299 300 private void UpdateButton() 301 { 302 var selectedSprite = skinningCache.selectedSprite; 303 304 if (selectedSprite == null) 305 m_GenerateGeometryPanel.SetMode(GenerateGeometryPanel.GenerateMode.Multiple); 306 else 307 m_GenerateGeometryPanel.SetMode(GenerateGeometryPanel.GenerateMode.Single); 308 } 309 310 private void OnSelectedSpriteChanged(SpriteCache sprite) 311 { 312 UpdateButton(); 313 } 314 315 private JobHandle GenerateWeights(SpriteCache sprite, SpriteJobData sd) 316 { 317 Debug.Assert(sprite != null); 318 319 var mesh = sprite.GetMesh(); 320 321 Debug.Assert(mesh != null); 322 323 using (new DefaultPoseScope(skinningCache.GetEffectiveSkeleton(sprite))) 324 { 325 sprite.AssociatePossibleBones(); 326 return GenerateWeights(mesh, sd); 327 } 328 } 329 330 // todo: Remove. This function seems dubious. Only associate if boneCount is 0 or if boneCount 1 and first bone matches ? 331 private bool NeedsAssociateBones(CharacterPartCache characterPart) 332 { 333 if (characterPart == null) 334 return false; 335 336 var skeleton = characterPart.skinningCache.character.skeleton; 337 338 return characterPart.boneCount == 0 || 339 (characterPart.boneCount == 1 && characterPart.GetBone(0) == skeleton.GetBone(0)); 340 } 341 342 private JobHandle GenerateWeights(MeshCache mesh, SpriteJobData sd) 343 { 344 Debug.Assert(mesh != null); 345 346 m_SpriteMeshDataController.spriteMeshData = mesh; 347 var JobHandle = m_SpriteMeshDataController.CalculateWeightsJob(m_WeightGenerator, null, kWeightTolerance, sd); 348 349 return JobHandle; 350 } 351 352 protected override void OnGUI() 353 { 354 m_MeshPreviewBehaviour.showWeightMap = m_GenerateGeometryPanel.generateWeights; 355 m_MeshPreviewBehaviour.overlaySelected = m_GenerateGeometryPanel.generateWeights; 356 357 skeletonTool.skeletonStyle = SkeletonStyles.Default; 358 359 if (m_GenerateGeometryPanel.generateWeights) 360 skeletonTool.skeletonStyle = SkeletonStyles.WeightMap; 361 362 DoSkeletonGUI(); 363 } 364 } 365}