MaterialReplacer.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. #if !NET_STANDARD_2_0
  6. using System.Reflection.Emit;
  7. #endif
  8. using UnityEngine;
  9. namespace SoftMasking {
  10. /// <summary>
  11. /// Mark an implementation of the IMaterialReplacer interface with this attribute to
  12. /// register it in the global material replacer chain. The global replacers will be
  13. /// used automatically by all SoftMasks.
  14. ///
  15. /// Globally registered replacers are called in order of ascending of their `order`
  16. /// value. The traversal is stopped on the first IMaterialReplacer which returns a
  17. /// non-null value and this returned value is then used as a replacement.
  18. ///
  19. /// Implementation of IMaterialReplacer marked by this attribute should have a
  20. /// default constructor.
  21. /// </summary>
  22. /// <seealso cref="IMaterialReplacer"/>
  23. /// <seealso cref="MaterialReplacer.globalReplacers"/>
  24. [AttributeUsage(AttributeTargets.Class)]
  25. public class GlobalMaterialReplacerAttribute : Attribute { }
  26. /// <summary>
  27. /// Used by SoftMask to automatically replace materials which don't support
  28. /// Soft Mask by those that do.
  29. /// </summary>
  30. /// <seealso cref="GlobalMaterialReplacerAttribute"/>
  31. public interface IMaterialReplacer {
  32. /// <summary>
  33. /// Determines the mutual order in which IMaterialReplacers will be called.
  34. /// The lesser the return value, the earlier it will be called, that is,
  35. /// replacers are sorted by ascending of the `order` value.
  36. /// The order of default implementation is 0. If you want your function to be
  37. /// called before, return a value lesser than 0.
  38. /// </summary>
  39. int order { get; }
  40. /// <summary>
  41. /// Should return null if this replacer can't replace the given material.
  42. /// </summary>
  43. Material Replace(Material material);
  44. }
  45. public static class MaterialReplacer {
  46. static List<IMaterialReplacer> _globalReplacers;
  47. /// <summary>
  48. /// Returns the collection of all globally registered replacers.
  49. /// </summary>
  50. /// <seealso cref="GlobalMaterialReplacerAttribute"/>
  51. public static IEnumerable<IMaterialReplacer> globalReplacers {
  52. get {
  53. if (_globalReplacers == null)
  54. _globalReplacers = CollectGlobalReplacers().ToList();
  55. return _globalReplacers;
  56. }
  57. }
  58. static IEnumerable<IMaterialReplacer> CollectGlobalReplacers() {
  59. return AppDomain.CurrentDomain.GetAssemblies()
  60. .SelectMany(x => x.GetTypesSafe())
  61. .Where(t => IsMaterialReplacerType(t))
  62. .Select(t => TryCreateInstance(t))
  63. .Where(t => t != null);
  64. }
  65. static bool IsMaterialReplacerType(Type t) {
  66. #if NET_STANDARD_2_0
  67. var isTypeBuilder = false;
  68. #else
  69. var isTypeBuilder = t is TypeBuilder;
  70. #endif
  71. return !isTypeBuilder
  72. && !t.IsAbstract
  73. && t.IsDefined(typeof(GlobalMaterialReplacerAttribute), false)
  74. && typeof(IMaterialReplacer).IsAssignableFrom(t);
  75. }
  76. static IMaterialReplacer TryCreateInstance(Type t) {
  77. try {
  78. return (IMaterialReplacer)Activator.CreateInstance(t);
  79. } catch (Exception ex) {
  80. Debug.LogErrorFormat("Could not create instance of {0}: {1}", t.Name, ex);
  81. return null;
  82. }
  83. }
  84. static IEnumerable<Type> GetTypesSafe(this Assembly asm) {
  85. try {
  86. return asm.GetTypes();
  87. } catch (ReflectionTypeLoadException e) {
  88. return e.Types.Where(t => t != null);
  89. }
  90. }
  91. }
  92. public class MaterialReplacerChain : IMaterialReplacer {
  93. readonly List<IMaterialReplacer> _replacers;
  94. public MaterialReplacerChain(IEnumerable<IMaterialReplacer> replacers, IMaterialReplacer yetAnother) {
  95. _replacers = replacers.ToList();
  96. _replacers.Add(yetAnother);
  97. Initialize();
  98. }
  99. public int order { get; private set; }
  100. public Material Replace(Material material) {
  101. for (int i = 0; i < _replacers.Count; ++i) {
  102. var result = _replacers[i].Replace(material);
  103. if (result != null)
  104. return result;
  105. }
  106. return null;
  107. }
  108. void Initialize() {
  109. _replacers.Sort((a, b) => a.order.CompareTo(b.order));
  110. order = _replacers[0].order;
  111. }
  112. }
  113. }