A game about forced loneliness, made by TACStudios
at master 638 lines 25 kB view raw
1using System.Collections.Generic; 2using UnityEngine; 3using System; 4using UnityEngine.Rendering; 5using System.Text; 6 7namespace UnityEditor.Rendering 8{ 9 /// <summary> 10 /// Material Upgrader dialog text. 11 /// </summary> 12 public static class DialogText 13 { 14 /// <summary>Material Upgrader title.</summary> 15 public static readonly string title = "Material Upgrader"; 16 /// <summary>Material Upgrader proceed.</summary> 17 public static readonly string proceed = "Proceed"; 18 /// <summary>Material Upgrader Ok.</summary> 19 public static readonly string ok = "OK"; 20 /// <summary>Material Upgrader cancel.</summary> 21 public static readonly string cancel = "Cancel"; 22 /// <summary>Material Upgrader no selection message.</summary> 23 public static readonly string noSelectionMessage = "You must select at least one material."; 24 /// <summary>Material Upgrader project backup message.</summary> 25 public static readonly string projectBackMessage = "Make sure to have a project backup before proceeding."; 26 } 27 28 /// <summary> 29 /// Material Upgrader class. 30 /// </summary> 31 public class MaterialUpgrader 32 { 33 /// <summary> 34 /// Material Upgrader finalizer delegate. 35 /// </summary> 36 /// <param name="mat">Material</param> 37 public delegate void MaterialFinalizer(Material mat); 38 39 string m_OldShader; 40 string m_NewShader; 41 42 private static string[] s_PathsWhiteList = new[] 43 { 44 "Hidden/", 45 "HDRP/", 46 "Shader Graphs/" 47 }; 48 49 /// <summary> 50 /// Retrieves path to new shader. 51 /// </summary> 52 public string NewShaderPath 53 { 54 get => m_NewShader; 55 } 56 57 MaterialFinalizer m_Finalizer; 58 59 Dictionary<string, string> m_TextureRename = new Dictionary<string, string>(); 60 Dictionary<string, string> m_FloatRename = new Dictionary<string, string>(); 61 Dictionary<string, string> m_ColorRename = new Dictionary<string, string>(); 62 63 Dictionary<string, float> m_FloatPropertiesToSet = new Dictionary<string, float>(); 64 Dictionary<string, Color> m_ColorPropertiesToSet = new Dictionary<string, Color>(); 65 List<string> m_TexturesToRemove = new List<string>(); 66 Dictionary<string, Texture> m_TexturesToSet = new Dictionary<string, Texture>(); 67 68 class KeywordFloatRename 69 { 70 public string keyword; 71 public string property; 72 public float setVal, unsetVal; 73 } 74 List<KeywordFloatRename> m_KeywordFloatRename = new List<KeywordFloatRename>(); 75 76 /// <summary> 77 /// Type of property to rename. 78 /// </summary> 79 public enum MaterialPropertyType 80 { 81 /// <summary>Texture reference property.</summary> 82 Texture, 83 /// <summary>Float property.</summary> 84 Float, 85 /// <summary>Color property.</summary> 86 Color 87 } 88 89 /// <summary> 90 /// Retrieves a collection of renamed parameters of a specific MaterialPropertyType. 91 /// </summary> 92 /// <param name="type">Material Property Type</param> 93 /// <returns>Dictionary of property names to their renamed values.</returns> 94 /// <exception cref="ArgumentException">type is not valid.</exception> 95 public IReadOnlyDictionary<string, string> GetPropertyRenameMap(MaterialPropertyType type) 96 { 97 switch (type) 98 { 99 case MaterialPropertyType.Texture: return m_TextureRename; 100 case MaterialPropertyType.Float: return m_FloatRename; 101 case MaterialPropertyType.Color: return m_ColorRename; 102 default: throw new ArgumentException(nameof(type)); 103 } 104 } 105 106 /// <summary> 107 /// Upgrade Flags 108 /// </summary> 109 [Flags] 110 public enum UpgradeFlags 111 { 112 /// <summary>None.</summary> 113 None = 0, 114 /// <summary>LogErrorOnNonExistingProperty.</summary> 115 LogErrorOnNonExistingProperty = 1, 116 /// <summary>CleanupNonUpgradedProperties.</summary> 117 CleanupNonUpgradedProperties = 2, 118 /// <summary>LogMessageWhenNoUpgraderFound.</summary> 119 LogMessageWhenNoUpgraderFound = 4 120 } 121 122 /// <summary> 123 /// Upgrade method. 124 /// </summary> 125 /// <param name="material">Material to upgrade.</param> 126 /// <param name="flags">Upgrade flag</param> 127 public void Upgrade(Material material, UpgradeFlags flags) 128 { 129 Material newMaterial; 130 if ((flags & UpgradeFlags.CleanupNonUpgradedProperties) != 0) 131 { 132 newMaterial = new Material(Shader.Find(m_NewShader)); 133 } 134 else 135 { 136 newMaterial = UnityEngine.Object.Instantiate(material) as Material; 137 newMaterial.shader = Shader.Find(m_NewShader); 138 } 139 140 Convert(material, newMaterial); 141 142 material.shader = Shader.Find(m_NewShader); 143 material.CopyPropertiesFromMaterial(newMaterial); 144 UnityEngine.Object.DestroyImmediate(newMaterial); 145 146 if (m_Finalizer != null) 147 m_Finalizer(material); 148 } 149 150 // Overridable function to implement custom material upgrading functionality 151 /// <summary> 152 /// Custom material conversion method. 153 /// </summary> 154 /// <param name="srcMaterial">Source material.</param> 155 /// <param name="dstMaterial">Destination material.</param> 156 public virtual void Convert(Material srcMaterial, Material dstMaterial) 157 { 158 foreach (var t in m_TextureRename) 159 { 160 if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value)) 161 continue; 162 163 dstMaterial.SetTextureScale(t.Value, srcMaterial.GetTextureScale(t.Key)); 164 dstMaterial.SetTextureOffset(t.Value, srcMaterial.GetTextureOffset(t.Key)); 165 dstMaterial.SetTexture(t.Value, srcMaterial.GetTexture(t.Key)); 166 } 167 168 foreach (var t in m_FloatRename) 169 { 170 if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value)) 171 continue; 172 173 dstMaterial.SetFloat(t.Value, srcMaterial.GetFloat(t.Key)); 174 } 175 176 foreach (var t in m_ColorRename) 177 { 178 if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value)) 179 continue; 180 181 dstMaterial.SetColor(t.Value, srcMaterial.GetColor(t.Key)); 182 } 183 184 foreach (var prop in m_TexturesToRemove) 185 { 186 if (!dstMaterial.HasProperty(prop)) 187 continue; 188 189 dstMaterial.SetTexture(prop, null); 190 } 191 192 foreach (var prop in m_TexturesToSet) 193 { 194 if (!dstMaterial.HasProperty(prop.Key)) 195 continue; 196 197 dstMaterial.SetTexture(prop.Key, prop.Value); 198 } 199 200 foreach (var prop in m_FloatPropertiesToSet) 201 { 202 if (!dstMaterial.HasProperty(prop.Key)) 203 continue; 204 205 dstMaterial.SetFloat(prop.Key, prop.Value); 206 } 207 208 foreach (var prop in m_ColorPropertiesToSet) 209 { 210 if (!dstMaterial.HasProperty(prop.Key)) 211 continue; 212 213 dstMaterial.SetColor(prop.Key, prop.Value); 214 } 215 216 foreach (var t in m_KeywordFloatRename) 217 { 218 if (!dstMaterial.HasProperty(t.property)) 219 continue; 220 221 dstMaterial.SetFloat(t.property, srcMaterial.IsKeywordEnabled(t.keyword) ? t.setVal : t.unsetVal); 222 } 223 } 224 225 /// <summary> 226 /// Rename shader. 227 /// </summary> 228 /// <param name="oldName">Old name.</param> 229 /// <param name="newName">New name.</param> 230 /// <param name="finalizer">Finalizer delegate.</param> 231 public void RenameShader(string oldName, string newName, MaterialFinalizer finalizer = null) 232 { 233 m_OldShader = oldName; 234 m_NewShader = newName; 235 m_Finalizer = finalizer; 236 } 237 238 /// <summary> 239 /// Rename Texture Parameter. 240 /// </summary> 241 /// <param name="oldName">Old name.</param> 242 /// <param name="newName">New name.</param> 243 public void RenameTexture(string oldName, string newName) 244 { 245 m_TextureRename[oldName] = newName; 246 } 247 248 /// <summary> 249 /// Rename Float Parameter. 250 /// </summary> 251 /// <param name="oldName">Old name.</param> 252 /// <param name="newName">New name.</param> 253 public void RenameFloat(string oldName, string newName) 254 { 255 m_FloatRename[oldName] = newName; 256 } 257 258 /// <summary> 259 /// Rename Color Parameter. 260 /// </summary> 261 /// <param name="oldName">Old name.</param> 262 /// <param name="newName">New name.</param> 263 public void RenameColor(string oldName, string newName) 264 { 265 m_ColorRename[oldName] = newName; 266 } 267 268 /// <summary> 269 /// Remove Texture Parameter. 270 /// </summary> 271 /// <param name="name">Parameter name.</param> 272 public void RemoveTexture(string name) 273 { 274 m_TexturesToRemove.Add(name); 275 } 276 277 /// <summary> 278 /// Set float property. 279 /// </summary> 280 /// <param name="propertyName">Property name.</param> 281 /// <param name="value">Property value.</param> 282 public void SetFloat(string propertyName, float value) 283 { 284 m_FloatPropertiesToSet[propertyName] = value; 285 } 286 287 /// <summary> 288 /// Set color property. 289 /// </summary> 290 /// <param name="propertyName">Property name.</param> 291 /// <param name="value">Property value.</param> 292 public void SetColor(string propertyName, Color value) 293 { 294 m_ColorPropertiesToSet[propertyName] = value; 295 } 296 297 /// <summary> 298 /// Set texture property. 299 /// </summary> 300 /// <param name="propertyName">Property name.</param> 301 /// <param name="value">Property value.</param> 302 public void SetTexture(string propertyName, Texture value) 303 { 304 m_TexturesToSet[propertyName] = value; 305 } 306 307 /// <summary> 308 /// Rename a keyword to float. 309 /// </summary> 310 /// <param name="oldName">Old name.</param> 311 /// <param name="newName">New name.</param> 312 /// <param name="setVal">Value when set.</param> 313 /// <param name="unsetVal">Value when unset.</param> 314 public void RenameKeywordToFloat(string oldName, string newName, float setVal, float unsetVal) 315 { 316 m_KeywordFloatRename.Add(new KeywordFloatRename { keyword = oldName, property = newName, setVal = setVal, unsetVal = unsetVal }); 317 } 318 319 static MaterialUpgrader GetUpgrader(List<MaterialUpgrader> upgraders, Material material) 320 { 321 if (material == null || material.shader == null) 322 return null; 323 324 string shaderName = material.shader.name; 325 for (int i = 0; i != upgraders.Count; i++) 326 { 327 if (upgraders[i].m_OldShader == shaderName) 328 return upgraders[i]; 329 } 330 331 return null; 332 } 333 334 //@TODO: Only do this when it exceeds memory consumption... 335 static void SaveAssetsAndFreeMemory() 336 { 337 AssetDatabase.SaveAssets(); 338 GC.Collect(); 339 EditorUtility.UnloadUnusedAssetsImmediate(); 340 AssetDatabase.Refresh(); 341 } 342 343 /// <summary> 344 /// Checking if the passed in value is a path to a Material. 345 /// </summary> 346 /// <param name="material">Material to check.</param> 347 /// <param name="shaderNamesToIgnore">HashSet of strings to ignore.</param> 348 /// <returns>Returns true if the passed in material's shader is not in the passed in ignore list.</returns> 349 static bool ShouldUpgradeShader(Material material, HashSet<string> shaderNamesToIgnore) 350 { 351 if (material == null) 352 return false; 353 354 if (material.shader == null) 355 return false; 356 357 return !shaderNamesToIgnore.Contains(material.shader.name); 358 } 359 360 /// <summary> 361 /// Check if the materials in the list are variants of upgradable materials, and logs a infomative message to the user.. 362 /// </summary> 363 /// <param name="materialGUIDs">Array of materials GUIDs.</param> 364 /// <param name="upgraders">List or available upgraders.</param> 365 static void LogMaterialVariantMessage(string[] materialGUIDs, List<MaterialUpgrader> upgraders) 366 { 367 List<Material> materials = new List<Material>(); 368 foreach (var guid in materialGUIDs) 369 materials.Add(AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(guid))); 370 371 LogMaterialVariantMessage(materials, upgraders); 372 } 373 374 /// <summary> 375 /// Check if the materials in the list are variants of upgradable materials, and logs a infomative message to the user.. 376 /// </summary> 377 /// <param name="objects">Array of objects.</param> 378 /// <param name="upgraders">List or available upgraders.</param> 379 static void LogMaterialVariantMessage(UnityEngine.Object[] objects, List<MaterialUpgrader> upgraders) 380 { 381 Material mat; 382 List<Material> materials = new List<Material>(); 383 for (int i = 0; i<objects.Length; i++) 384 { 385 mat = objects[i] as Material; 386 if (mat != null) 387 materials.Add(mat); 388 } 389 390 LogMaterialVariantMessage(materials, upgraders); 391 } 392 393 /// <summary> 394 /// Check if the materials in the list are variants of upgradable materials, and logs a infomative message to the user.. 395 /// </summary> 396 /// <param name="materials">List of materials.</param> 397 /// <param name="upgraders">List or available upgraders.</param> 398 static void LogMaterialVariantMessage(List<Material> materials, List<MaterialUpgrader> upgraders) 399 { 400 StringBuilder sb = new StringBuilder(); 401 sb.AppendLine("Can not upgrade Material Variants, the following assets were skipped:"); 402 bool needsLogging = false; 403 404 Material rootMaterial; 405 406 foreach (Material material in materials) 407 { 408 if (material.isVariant) 409 { 410 rootMaterial = material; 411 while (rootMaterial.isVariant) 412 rootMaterial = rootMaterial.parent; 413 414 if (GetUpgrader(upgraders, rootMaterial) != null) 415 { 416 needsLogging = true; 417 sb.AppendLine($"- <a href=\"{AssetDatabase.GetAssetPath(material)}\" >{material.name}</a>, variant of {material.parent.name} with shader {rootMaterial.shader.name}."); 418 } 419 } 420 } 421 422 if (needsLogging) 423 Debug.Log(sb.ToString(), null); 424 } 425 426 427 private static bool IsNotAutomaticallyUpgradable(List<MaterialUpgrader> upgraders, Material material) 428 { 429 return GetUpgrader(upgraders, material) == null && !material.shader.name.ContainsAny(s_PathsWhiteList); 430 } 431 432 433 /// <summary> 434 /// Checking if project folder contains any materials that are not using built-in shaders. 435 /// </summary> 436 /// <param name="upgraders">List if MaterialUpgraders</param> 437 /// <returns>Returns true if at least one material uses a non-built-in shader (ignores Hidden, HDRP and Shader Graph Shaders)</returns> 438 public static bool ProjectFolderContainsNonBuiltinMaterials(List<MaterialUpgrader> upgraders) 439 { 440 foreach (var material in AssetDatabaseHelper.FindAssets<Material>(".mat")) 441 { 442 if(IsNotAutomaticallyUpgradable(upgraders, material)) 443 return true; 444 } 445 446 return false; 447 } 448 449 /// <summary> 450 /// Upgrade the project folder. 451 /// </summary> 452 /// <param name="upgraders">List of upgraders.</param> 453 /// <param name="progressBarName">Name of the progress bar.</param> 454 /// <param name="flags">Material Upgrader flags.</param> 455 public static void UpgradeProjectFolder(List<MaterialUpgrader> upgraders, string progressBarName, UpgradeFlags flags = UpgradeFlags.None) 456 { 457 HashSet<string> shaderNamesToIgnore = new HashSet<string>(); 458 UpgradeProjectFolder(upgraders, shaderNamesToIgnore, progressBarName, flags); 459 } 460 461 /// <summary> 462 /// Upgrade the project folder. 463 /// </summary> 464 /// <param name="upgraders">List of upgraders.</param> 465 /// <param name="shaderNamesToIgnore">Set of shader names to ignore.</param> 466 /// <param name="progressBarName">Name of the progress bar.</param> 467 /// <param name="flags">Material Upgrader flags.</param> 468 public static void UpgradeProjectFolder(List<MaterialUpgrader> upgraders, HashSet<string> shaderNamesToIgnore, string progressBarName, UpgradeFlags flags = UpgradeFlags.None) 469 { 470 if ((!Application.isBatchMode) && (!EditorUtility.DisplayDialog(DialogText.title, "The upgrade will overwrite materials in your project. " + DialogText.projectBackMessage, DialogText.proceed, DialogText.cancel))) 471 return; 472 473 var materialAssets = AssetDatabase.FindAssets($"t:{nameof(Material)} glob:\"**/*.mat\""); 474 int materialIndex = 0; 475 476 LogMaterialVariantMessage(materialAssets, upgraders); 477 478 foreach (var guid in materialAssets) 479 { 480 Material material = AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(guid)); 481 materialIndex++; 482 if (UnityEditor.EditorUtility.DisplayCancelableProgressBar(progressBarName, string.Format("({0} of {1}) {2}", materialIndex, materialAssets.Length, material), (float)materialIndex / (float)materialAssets.Length)) 483 break; 484 485 if (!ShouldUpgradeShader(material, shaderNamesToIgnore)) 486 continue; 487 488 if (material.isVariant) 489 continue; 490 491 Upgrade(material, upgraders, flags); 492 493 } 494 495 // Upgrade terrain specifically since it is a builtin material 496 if (Terrain.activeTerrains.Length > 0) 497 { 498 Material terrainMat = Terrain.activeTerrain.materialTemplate; 499 Upgrade(terrainMat, upgraders, flags); 500 } 501 502 UnityEditor.EditorUtility.ClearProgressBar(); 503 } 504 505 /// <summary> 506 /// Upgrade a material. 507 /// </summary> 508 /// <param name="material">Material to upgrade.</param> 509 /// <param name="upgrader">Material upgrader.</param> 510 /// <param name="flags">Material Upgrader flags.</param> 511 public static void Upgrade(Material material, MaterialUpgrader upgrader, UpgradeFlags flags) 512 { 513 using (ListPool<MaterialUpgrader>.Get(out List<MaterialUpgrader> upgraders)) 514 { 515 upgraders.Add(upgrader); 516 Upgrade(material, upgraders, flags); 517 } 518 } 519 520 /// <summary> 521 /// Upgrade a material. 522 /// </summary> 523 /// <param name="material">Material to upgrade.</param> 524 /// <param name="upgraders">List of Material upgraders.</param> 525 /// <param name="flags">Material Upgrader flags.</param> 526 public static void Upgrade(Material material, List<MaterialUpgrader> upgraders, UpgradeFlags flags) 527 { 528 string message = string.Empty; 529 if (Upgrade(material, upgraders, flags, ref message)) 530 return; 531 532 if (!string.IsNullOrEmpty(message)) 533 { 534 Debug.Log(message); 535 } 536 } 537 538 /// <summary> 539 /// Upgrade a material. 540 /// </summary> 541 /// <param name="material">Material to upgrade.</param> 542 /// <param name="upgraders">List of Material upgraders.</param> 543 /// <param name="flags">Material upgrader flags.</param> 544 /// <param name="message">Error message to be outputted when no material upgraders are suitable for given material if the flags <see cref="UpgradeFlags.LogMessageWhenNoUpgraderFound"/> is used.</param> 545 /// <returns>Returns true if the upgrader was found for the passed in material.</returns> 546 public static bool Upgrade(Material material, List<MaterialUpgrader> upgraders, UpgradeFlags flags, ref string message) 547 { 548 if (material == null) 549 return false; 550 551 var upgrader = GetUpgrader(upgraders, material); 552 553 if (upgrader != null) 554 { 555 upgrader.Upgrade(material, flags); 556 return true; 557 } 558 if ((flags & UpgradeFlags.LogMessageWhenNoUpgraderFound) == UpgradeFlags.LogMessageWhenNoUpgraderFound) 559 { 560 message = 561 $"{material.name} material was not upgraded. There's no upgrader to convert {material.shader.name} shader to selected pipeline"; 562 return false; 563 } 564 565 return true; 566 } 567 568 /// <summary> 569 /// Upgrade the selection. 570 /// </summary> 571 /// <param name="upgraders">List of upgraders.</param> 572 /// <param name="progressBarName">Name of the progress bar.</param> 573 /// <param name="flags">Material Upgrader flags.</param> 574 public static void UpgradeSelection(List<MaterialUpgrader> upgraders, string progressBarName, UpgradeFlags flags = UpgradeFlags.None) 575 { 576 HashSet<string> shaderNamesToIgnore = new HashSet<string>(); 577 UpgradeSelection(upgraders, shaderNamesToIgnore, progressBarName, flags); 578 } 579 580 /// <summary> 581 /// Upgrade the selection. 582 /// </summary> 583 /// <param name="upgraders">List of upgraders.</param> 584 /// <param name="shaderNamesToIgnore">Set of shader names to ignore.</param> 585 /// <param name="progressBarName">Name of the progress bar.</param> 586 /// <param name="flags">Material Upgrader flags.</param> 587 public static void UpgradeSelection(List<MaterialUpgrader> upgraders, HashSet<string> shaderNamesToIgnore, string progressBarName, UpgradeFlags flags = UpgradeFlags.None) 588 { 589 var selection = Selection.objects; 590 591 if (selection == null) 592 { 593 EditorUtility.DisplayDialog(DialogText.title, DialogText.noSelectionMessage, DialogText.ok); 594 return; 595 } 596 597 List<Material> selectedMaterials = new List<Material>(selection.Length); 598 599 LogMaterialVariantMessage(selection, upgraders); 600 601 for (int i = 0; i < selection.Length; ++i) 602 { 603 Material mat = selection[i] as Material; 604 if (mat != null && !mat.isVariant) 605 selectedMaterials.Add(mat); 606 } 607 608 int selectedMaterialsCount = selectedMaterials.Count; 609 if (selectedMaterialsCount == 0) 610 { 611 EditorUtility.DisplayDialog(DialogText.title, DialogText.noSelectionMessage, DialogText.ok); 612 return; 613 } 614 615 if (!EditorUtility.DisplayDialog(DialogText.title, string.Format("The upgrade will overwrite {0} selected material{1}. ", selectedMaterialsCount, selectedMaterialsCount > 1 ? "s" : "") + 616 DialogText.projectBackMessage, DialogText.proceed, DialogText.cancel)) 617 return; 618 619 string lastMaterialName = ""; 620 for (int i = 0; i < selectedMaterialsCount; i++) 621 { 622 if (UnityEditor.EditorUtility.DisplayCancelableProgressBar(progressBarName, string.Format("({0} of {1}) {2}", i, selectedMaterialsCount, lastMaterialName), (float)i / (float)selectedMaterialsCount)) 623 break; 624 625 var material = selectedMaterials[i]; 626 627 if (!ShouldUpgradeShader(material, shaderNamesToIgnore)) 628 continue; 629 630 Upgrade(material, upgraders, flags); 631 if (material != null) 632 lastMaterialName = material.name; 633 } 634 635 UnityEditor.EditorUtility.ClearProgressBar(); 636 } 637 } 638}