A game about forced loneliness, made by TACStudios
at master 628 lines 25 kB view raw
1using System.Collections.Generic; 2using System.Linq; 3using UnityEngine; 4using UnityEngine.U2D.Animation; 5using UnityEngine.U2D.Common; 6 7namespace UnityEditor.U2D.Animation.Upgrading 8{ 9 internal class AnimClipUpgrader : BaseUpgrader 10 { 11 static class Contents 12 { 13 public static readonly string ProgressBarTitle = L10n.Tr("Upgrading Animation Clips"); 14 public static readonly string VerifyingSelection = L10n.Tr("Verifying the selection"); 15 public static readonly string UpgradingSpriteKeys = L10n.Tr("Upgrading Sprite Keys"); 16 public static readonly string UpgradingCategoryLabelHash = L10n.Tr("Upgrading Category and Label hashes"); 17 } 18 19 enum HashType 20 { 21 Label, 22 Category, 23 SpriteKey, 24 SpriteHash 25 } 26 27 class BindingData 28 { 29 public string BindingPath; 30 public System.Type BindingType; 31 public List<KeyData> RawKeys; 32 public List<ConvertedKeyData> ConvertedKeys; 33 } 34 35 class KeyData 36 { 37 public HashType HashType; 38 public float Time; 39 public float Value; 40 } 41 42 class ConvertedKeyData 43 { 44 public float Time; 45 public float Value; 46 public string Category; 47 public string Label; 48 } 49 50 const string k_LabelHashId = "m_labelHash"; 51 const string k_CategoryHashId = "m_CategoryHash"; 52 const string k_SpriteKeyId = "m_SpriteKey"; 53 const string k_SpriteHashId = "m_SpriteHash"; 54 const string k_AnimClipTypeId = "t:AnimationClip"; 55 56 static bool IsSpriteHashBinding(EditorCurveBinding b) => 57 b.type == typeof(SpriteResolver) 58 && !string.IsNullOrEmpty(b.propertyName) 59 && b.propertyName == k_SpriteHashId; 60 61 static bool IsSpriteKeyBinding(EditorCurveBinding b) => 62 b.type == typeof(SpriteResolver) 63 && !string.IsNullOrEmpty(b.propertyName) 64 && b.propertyName == k_SpriteKeyId; 65 66 static bool IsSpriteCategoryBinding(EditorCurveBinding b) => 67 b.type == typeof(SpriteResolver) 68 && !string.IsNullOrEmpty(b.propertyName) 69 && b.propertyName == k_CategoryHashId; 70 71 static bool IsSpriteLabelBinding(EditorCurveBinding b) => 72 b.type == typeof(SpriteResolver) 73 && !string.IsNullOrEmpty(b.propertyName) 74 && b.propertyName == k_LabelHashId; 75 76 static SpriteLibUpgrader s_SpriteLibUpgrader = new SpriteLibUpgrader(false, false); 77 78 internal override List<Object> GetUpgradableAssets() 79 { 80 var assets = AssetDatabase.FindAssets(k_AnimClipTypeId, new[] { "Assets" }) 81 .Select(AssetDatabase.GUIDToAssetPath) 82 .Select(AssetDatabase.LoadAssetAtPath<Object>) 83 .ToArray(); 84 85 var clips = assets 86 .Select(x => x as AnimationClip) 87 .Where(clip => clip != null) 88 .Where(clip => 89 { 90 var bindings = AnimationUtility.GetCurveBindings(clip) 91 .Where(m => IsSpriteKeyBinding(m) || IsSpriteCategoryBinding(m) || IsSpriteLabelBinding(m)) 92 .ToArray(); 93 return bindings.Length > 0; 94 }).ToArray(); 95 96 var assetList = new List<Object>(clips); 97 return assetList; 98 } 99 100 internal override UpgradeReport UpgradeSelection(List<ObjectIndexPair> objects) 101 { 102 var entries = new List<UpgradeEntry>(); 103 104 AssetDatabase.StartAssetEditing(); 105 106 string msg; 107 foreach (var obj in objects) 108 { 109 m_Logger.Add($"Verifying if the asset {obj.Target} is an AnimationClip."); 110 EditorUtility.DisplayProgressBar( 111 Contents.ProgressBarTitle, 112 Contents.VerifyingSelection, 113 GetUpgradeProgress(entries, objects)); 114 115 if (obj.Target == null) 116 { 117 msg = "The upgrade failed. Invalid selection."; 118 m_Logger.Add(msg); 119 m_Logger.AddLineBreak(); 120 entries.Add(new UpgradeEntry() 121 { 122 Result = UpgradeResult.Error, 123 Target = obj.Target, 124 Index = obj.Index, 125 Message = msg 126 }); 127 continue; 128 } 129 130 var clip = obj.Target as AnimationClip; 131 if (clip == null) 132 { 133 msg = $"The upgrade failed. The asset {obj.Target.name} is not an AnimationClip."; 134 m_Logger.Add(msg); 135 m_Logger.AddLineBreak(); 136 entries.Add(new UpgradeEntry() 137 { 138 Result = UpgradeResult.Error, 139 Target = obj.Target, 140 Index = obj.Index, 141 Message = msg 142 }); 143 continue; 144 } 145 146 var extractedData = ExtractDataFromClip(clip); 147 ConvertData(ref extractedData); 148 149 var wasCleanupSuccessful = CleanupData(ref extractedData); 150 if (!wasCleanupSuccessful) 151 { 152 msg = $"The upgrade of the clip {obj.Target.name} failed. Some keyframes could not be converted in the animation clip."; 153 m_Logger.Add(msg); 154 m_Logger.AddLineBreak(); 155 entries.Add(new UpgradeEntry() 156 { 157 Result = UpgradeResult.Error, 158 Target = obj.Target, 159 Index = obj.Index, 160 Message = msg 161 }); 162 continue; 163 } 164 165 var isDataValid = ValidateConvertedData(extractedData, obj, entries); 166 if (!isDataValid) 167 continue; 168 169 UpdateClipWithConvertedData(clip, extractedData); 170 RemoveOldData(clip, extractedData); 171 172 msg = $"Upgrade successful. The clip {obj.Target.name} now uses the latest SpriteResolver data format."; 173 m_Logger.Add(msg); 174 m_Logger.AddLineBreak(); 175 entries.Add(new UpgradeEntry() 176 { 177 Result = UpgradeResult.Successful, 178 Target = obj.Target, 179 Index = obj.Index, 180 Message = msg 181 }); 182 } 183 184 AssetDatabase.SaveAssets(); 185 AssetDatabase.StopAssetEditing(); 186 187 EditorUtility.ClearProgressBar(); 188 189 var report = new UpgradeReport() 190 { 191 UpgradeEntries = entries, 192 Log = m_Logger.GetLog() 193 }; 194 195 m_Logger.Clear(); 196 return report; 197 } 198 199 bool ValidateConvertedData(List<BindingData> extractedData, ObjectIndexPair upgradingObject, List<UpgradeEntry> entries) 200 { 201 var isDataValid = extractedData.All(data => data.ConvertedKeys.Count != 0); 202 if (!isDataValid) 203 { 204 var msg = $"The upgrade of the clip {upgradingObject.Target.name} failed. One or more bindings could not convert its keyframes to the latest data format."; 205 m_Logger.Add(msg); 206 m_Logger.AddLineBreak(); 207 entries.Add(new UpgradeEntry() 208 { 209 Result = UpgradeResult.Error, 210 Target = upgradingObject.Target, 211 Index = upgradingObject.Index, 212 Message = msg 213 }); 214 } 215 216 return isDataValid; 217 } 218 219 List<BindingData> ExtractDataFromClip(AnimationClip clip) 220 { 221 var spriteHashBindings = ExtractBindingsFromClip(clip, HashType.SpriteHash, IsSpriteHashBinding); 222 var spriteKeyBindings = ExtractBindingsFromClip(clip, HashType.SpriteKey, IsSpriteKeyBinding); 223 var categoryBindings = ExtractBindingsFromClip(clip, HashType.Category, IsSpriteCategoryBinding); 224 var labelBindings = ExtractBindingsFromClip(clip, HashType.Label, IsSpriteLabelBinding); 225 226 var bindings = new List<BindingData>(); 227 bindings.AddRange(spriteHashBindings); 228 bindings.AddRange(spriteKeyBindings); 229 bindings.AddRange(categoryBindings); 230 bindings.AddRange(labelBindings); 231 232 bindings = MergeBindingData(bindings); 233 for (var i = 0; i < bindings.Count; ++i) 234 SortKeyData(bindings[i]); 235 return bindings; 236 } 237 238 BindingData[] ExtractBindingsFromClip(AnimationClip clip, HashType hashType, System.Func<EditorCurveBinding, bool> isBindingFunc) 239 { 240 var spriteHashBindings = AnimationUtility.GetCurveBindings(clip) 241 .Where(isBindingFunc.Invoke) 242 .ToArray(); 243 244 var bindingData = new BindingData[spriteHashBindings.Length]; 245 for (var i = 0; i < spriteHashBindings.Length; ++i) 246 bindingData[i] = ExtractKeyframesFromClip(clip, spriteHashBindings[i], hashType); 247 248 m_Logger.Add($"Extracting {hashType} bindings from clip. Found {bindingData.Length} bindings."); 249 250 return bindingData; 251 } 252 253 BindingData ExtractKeyframesFromClip(AnimationClip clip, EditorCurveBinding curveBinding, HashType hashType) 254 { 255 var bindingPath = curveBinding.path; 256 var bindingType = curveBinding.type; 257 258 var curves = AnimationUtility.GetEditorCurve(clip, curveBinding); 259 var keys = curves.keys; 260 var keyData = new List<KeyData>(keys.Length); 261 keyData.AddRange(keys 262 .Select(t => new KeyData() { Time = t.time, Value = t.value, HashType = hashType })); 263 264 var data = new BindingData() 265 { 266 BindingPath = bindingPath, 267 BindingType = bindingType, 268 RawKeys = keyData 269 }; 270 271 m_Logger.Add($"Extracting {hashType} keyframes from clip. Found {keyData.Count} keyframes."); 272 273 return data; 274 } 275 276 List<BindingData> MergeBindingData(List<BindingData> bindingData) 277 { 278 var mergedData = new List<BindingData>(); 279 for (var i = 0; i < bindingData.Count; i++) 280 { 281 var index = mergedData.FindIndex(x => 282 x.BindingPath == bindingData[i].BindingPath && 283 x.BindingType == bindingData[i].BindingType); 284 285 if (index != -1) 286 mergedData[index].RawKeys.AddRange(bindingData[i].RawKeys); 287 else 288 mergedData.Add(bindingData[i]); 289 } 290 291 m_Logger.Add($"Merging different types keyframes from the same bindings, into the same binding list. We now have {mergedData.Count} binding lists."); 292 293 return mergedData; 294 } 295 296 void SortKeyData(BindingData bindingData) 297 { 298 bindingData.RawKeys.Sort((a, b) => a.Time.CompareTo(b.Time)); 299 m_Logger.Add($"Order the keyframe data in binding={bindingData.BindingPath} by time."); 300 } 301 302 void ConvertData(ref List<BindingData> bindingData) 303 { 304 for (var i = 0; i < bindingData.Count; ++i) 305 { 306 bindingData[i].ConvertedKeys = ConvertKeyData(bindingData[i]); 307 MergeKeyData(bindingData[i]); 308 RepairMissingKeyData(bindingData[i]); 309 } 310 } 311 312 List<ConvertedKeyData> ConvertKeyData(BindingData bindingData) 313 { 314 var keyData = bindingData.RawKeys; 315 var convertedData = new List<ConvertedKeyData>(); 316 for (var i = 0; i < keyData.Count; ++i) 317 { 318 switch (keyData[i].HashType) 319 { 320 case HashType.SpriteHash: 321 convertedData.Add(ConvertSpriteHash(keyData[i])); 322 break; 323 case HashType.SpriteKey: 324 convertedData.Add(ConvertSpriteKey(keyData[i])); 325 break; 326 case HashType.Category: 327 convertedData.Add(ConvertSpriteCategory(keyData[i])); 328 break; 329 case HashType.Label: 330 convertedData.Add(ConvertSpriteLabel(keyData[i])); 331 break; 332 } 333 334 if (convertedData[i].Category == string.Empty && convertedData[i].Label == string.Empty) 335 m_Logger.Add($"Conversion of key={i} of type={keyData[i].HashType} for binding={bindingData.BindingPath} failed to resolve Category and Label values from the Sprite Libraries in the project."); 336 } 337 338 m_Logger.Add($"Converting keyframes into uniformed format for binding={bindingData.BindingPath}"); 339 return convertedData; 340 } 341 342 static ConvertedKeyData ConvertSpriteHash(KeyData keyData) 343 { 344 var spriteHash = InternalEngineBridge.ConvertFloatToInt(keyData.Value); 345 SpriteHashToCategoryAndLabelName(spriteHash, out var categoryName, out var labelName); 346 var convertedData = new ConvertedKeyData() 347 { 348 Time = keyData.Time, 349 Value = keyData.Value, 350 Category = categoryName, 351 Label = labelName 352 }; 353 return convertedData; 354 } 355 356 static ConvertedKeyData ConvertSpriteKey(KeyData keyData) 357 { 358 var newHash = InternalEngineBridge.ConvertFloatToInt(keyData.Value); 359 var spriteHash = SpriteLibraryUtility.Convert32BitTo30BitHash(newHash); 360 361 SpriteHashToCategoryAndLabelName(spriteHash, out var categoryName, out var labelName); 362 var convertedData = new ConvertedKeyData() 363 { 364 Time = keyData.Time, 365 Value = InternalEngineBridge.ConvertIntToFloat(spriteHash), 366 Category = categoryName, 367 Label = labelName 368 }; 369 return convertedData; 370 } 371 372 static ConvertedKeyData ConvertSpriteCategory(KeyData keyData) 373 { 374 var newHash = InternalEngineBridge.ConvertFloatToInt(keyData.Value); 375 var categoryHash = SpriteLibraryUtility.Convert32BitTo30BitHash(newHash); 376 CategoryHashToCategoryName(categoryHash, out var categoryName); 377 378 var convertedData = new ConvertedKeyData() 379 { 380 Time = keyData.Time, 381 Value = 0f, 382 Category = categoryName, 383 Label = string.Empty 384 }; 385 return convertedData; 386 } 387 388 static ConvertedKeyData ConvertSpriteLabel(KeyData keyData) 389 { 390 var newHash = InternalEngineBridge.ConvertFloatToInt(keyData.Value); 391 var labelHash = SpriteLibraryUtility.Convert32BitTo30BitHash(newHash); 392 LabelHashToLabelName(labelHash, out var labelName); 393 394 var convertedData = new ConvertedKeyData() 395 { 396 Time = keyData.Time, 397 Value = 0f, 398 Category = string.Empty, 399 Label = labelName 400 }; 401 return convertedData; 402 } 403 404 static void SpriteHashToCategoryAndLabelName(int spriteHash, out string categoryName, out string labelName) 405 { 406 var spriteLibraryAssets = s_SpriteLibUpgrader.GetUpgradableAssets() 407 .Cast<SpriteLibraryAsset>().ToArray(); 408 409 categoryName = string.Empty; 410 labelName = string.Empty; 411 412 foreach (var spriteLib in spriteLibraryAssets) 413 { 414 foreach (var category in spriteLib.categories) 415 { 416 foreach (var label in category.categoryList) 417 { 418 var combinedHash = SpriteLibrary.GetHashForCategoryAndEntry(category.name, label.name); 419 if (combinedHash == spriteHash) 420 { 421 categoryName = category.name; 422 labelName = label.name; 423 return; 424 } 425 } 426 } 427 } 428 } 429 430 static void CategoryHashToCategoryName(int categoryHash, out string categoryName) 431 { 432 var spriteLibraryAssets = s_SpriteLibUpgrader.GetUpgradableAssets() 433 .Cast<SpriteLibraryAsset>().ToArray(); 434 435 categoryName = string.Empty; 436 437 foreach (var spriteLib in spriteLibraryAssets) 438 { 439 foreach (var category in spriteLib.categories) 440 { 441 if (category.hash == categoryHash) 442 { 443 categoryName = category.name; 444 return; 445 } 446 } 447 } 448 } 449 450 static void LabelHashToLabelName(int labelHash, out string labelName) 451 { 452 var spriteLibraryAssets = s_SpriteLibUpgrader.GetUpgradableAssets() 453 .Cast<SpriteLibraryAsset>().ToArray(); 454 455 labelName = string.Empty; 456 457 foreach (var spriteLib in spriteLibraryAssets) 458 { 459 foreach (var category in spriteLib.categories) 460 { 461 foreach (var label in category.categoryList) 462 { 463 if (label.hash == labelHash) 464 { 465 labelName = label.name; 466 return; 467 } 468 } 469 } 470 } 471 } 472 473 void MergeKeyData(BindingData bindingData) 474 { 475 var keys = bindingData.ConvertedKeys; 476 for (var i = 0; i < keys.Count; ++i) 477 { 478 var categoryName = keys[i].Category; 479 480 if (categoryName == string.Empty) 481 continue; 482 483 var labelName = keys[i].Label; 484 if (labelName != string.Empty) 485 continue; 486 487 for (var m = 0; m < keys.Count; ++m) 488 { 489 labelName = keys[m].Label; 490 if (labelName == string.Empty) 491 continue; 492 if (Mathf.Abs(keys[i].Time - keys[m].Time) > Mathf.Epsilon) 493 continue; 494 495 keys[m].Category = categoryName; 496 keys[i].Label = labelName; 497 498 m_Logger.Add($"Merged Category={categoryName} and Label={labelName} at time={keys[i].Time} in binding={bindingData.BindingPath}."); 499 } 500 } 501 } 502 503 void RepairMissingKeyData(BindingData bindingData) 504 { 505 var keys = bindingData.ConvertedKeys; 506 var categoryName = string.Empty; 507 var labelName = string.Empty; 508 for (var i = 0; i < keys.Count; ++i) 509 { 510 if (keys[i].Category != string.Empty) 511 categoryName = keys[i].Category; 512 if (keys[i].Label != string.Empty) 513 labelName = keys[i].Label; 514 515 if (keys[i].Value == 0f && categoryName == string.Empty) 516 { 517 m_Logger.Add($"Cannot find a category for keyframe at time={keys[i].Time} in binding={bindingData.BindingPath}."); 518 continue; 519 } 520 if (keys[i].Value == 0f && labelName == string.Empty) 521 { 522 m_Logger.Add($"Cannot find a label for keyframe at time={keys[i].Time} in binding={bindingData.BindingPath}."); 523 continue; 524 } 525 526 if (keys[i].Value == 0f) 527 { 528 var spriteHash = SpriteLibrary.GetHashForCategoryAndEntry(categoryName, labelName); 529 keys[i].Value = InternalEngineBridge.ConvertIntToFloat(spriteHash); 530 531 if (keys[i].Value != 0f) 532 m_Logger.Add($"Combining categoryName={categoryName} labelName={labelName} into spriteHash={spriteHash}."); 533 else 534 m_Logger.Add($"Could not repair keyframe at time={keys[i].Time} for binding={bindingData.BindingPath}. The Sprite Library Asset might be missing."); 535 } 536 } 537 } 538 539 bool CleanupData(ref List<BindingData> bindingData) 540 { 541 foreach (var data in bindingData) 542 { 543 var keys = data.ConvertedKeys; 544 545 var keyTimes = new List<float>(); 546 for (var i = 0; i < keys.Count; ++i) 547 { 548 var time = keys[i].Time; 549 if (i == 0 || (time - keyTimes[keyTimes.Count - 1]) > Mathf.Epsilon) 550 keyTimes.Add(time); 551 } 552 553 for (var m = keys.Count - 1; m >= 0; --m) 554 { 555 if (keys[m].Value == 0f) 556 keys.RemoveAt(m); 557 } 558 559 for (var m = keys.Count - 1; m > 0; --m) 560 { 561 if (Mathf.Abs(keys[m].Time - keys[m - 1].Time) < Mathf.Epsilon) 562 keys.RemoveAt(m); 563 } 564 565 if (keyTimes.Count == keys.Count) 566 m_Logger.Add($"Cleaned up keyframes for binding={data.BindingPath}. It now has {keys.Count} keyframes."); 567 else 568 { 569 m_Logger.Add($"Expected {keyTimes.Count} keyframes after cleanup for binding={data.BindingPath}, but ended up with {keys.Count}."); 570 return false; 571 } 572 } 573 574 return true; 575 } 576 577 void UpdateClipWithConvertedData(AnimationClip clip, List<BindingData> convertedBindings) 578 { 579 var destData = new EditorCurveBinding[convertedBindings.Count]; 580 for (var i = 0; i < convertedBindings.Count; ++i) 581 destData[i] = EditorCurveBinding.DiscreteCurve(convertedBindings[i].BindingPath, convertedBindings[i].BindingType, k_SpriteHashId); 582 583 var curves = new AnimationCurve[destData.Length]; 584 for (var i = 0; i < curves.Length; ++i) 585 { 586 var convertedKeys = convertedBindings[i].ConvertedKeys; 587 var keyFrames = new Keyframe[convertedKeys.Count]; 588 for (var m = 0; m < keyFrames.Length; ++m) 589 { 590 keyFrames[m].inTangent = float.PositiveInfinity; 591 keyFrames[m].outTangent = float.PositiveInfinity; 592 keyFrames[m].time = convertedKeys[m].Time; 593 keyFrames[m].value = convertedKeys[m].Value; 594 } 595 596 curves[i] = new AnimationCurve(keyFrames); 597 } 598 599 AnimationUtility.SetEditorCurves(clip, destData, curves); 600 m_Logger.Add($"Injected updated bindings into AnimationClip={clip.name}."); 601 } 602 603 void RemoveOldData(AnimationClip clip, List<BindingData> bindingData) 604 { 605 var spriteKeyCurves = new EditorCurveBinding[bindingData.Count]; 606 var spriteCategoryCurves = new EditorCurveBinding[bindingData.Count]; 607 var spriteLabelCurves = new EditorCurveBinding[bindingData.Count]; 608 609 for (var i = 0; i < bindingData.Count; ++i) 610 { 611 spriteKeyCurves[i] = EditorCurveBinding.DiscreteCurve(bindingData[i].BindingPath, bindingData[i].BindingType, k_SpriteKeyId); 612 spriteCategoryCurves[i] = EditorCurveBinding.DiscreteCurve(bindingData[i].BindingPath, bindingData[i].BindingType, k_CategoryHashId); 613 spriteLabelCurves[i] = EditorCurveBinding.DiscreteCurve(bindingData[i].BindingPath, bindingData[i].BindingType, k_LabelHashId); 614 } 615 616 AnimationUtility.SetEditorCurves(clip, spriteKeyCurves, new AnimationCurve[spriteKeyCurves.Length]); 617 AnimationUtility.SetEditorCurves(clip, spriteCategoryCurves, new AnimationCurve[spriteCategoryCurves.Length]); 618 AnimationUtility.SetEditorCurves(clip, spriteLabelCurves, new AnimationCurve[spriteLabelCurves.Length]); 619 620 m_Logger.Add($"Removed old bindings in AnimationClip={clip.name}."); 621 } 622 623 static float GetUpgradeProgress(List<UpgradeEntry> reports, List<ObjectIndexPair> totalNoOfObjects) 624 { 625 return reports.Count / (float)totalNoOfObjects.Count; 626 } 627 } 628}