123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- using UnityEngine;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System;
- using Object = UnityEngine.Object;
- namespace UnityEditor.Rendering.Universal.Converters
- {
- internal static class MaterialReferenceBuilder
- {
- public static readonly Dictionary<Type, List<MethodInfo>> MaterialReferenceLookup;
- static MaterialReferenceBuilder()
- {
- MaterialReferenceLookup = GetMaterialReferenceLookup();
- }
- private static Dictionary<Type, List<MethodInfo>> GetMaterialReferenceLookup()
- {
- var result = new Dictionary<Type, List<MethodInfo>>();
- var allObjectsWithMaterialProperties = TypeCache.GetTypesDerivedFrom<Object>()
- .Where(type => type.GetProperties().Any(HasMaterialProperty));
- foreach (var property in allObjectsWithMaterialProperties)
- {
- if (!result.ContainsKey(property))
- {
- result.Add(property, new List<MethodInfo>());
- }
- var materialProps = GetMaterialPropertiesWithoutLeaking(property);
- foreach (var prop in materialProps)
- {
- result[property].Add(prop.GetGetMethod());
- }
- }
- return result;
- }
- private static bool HasMaterialProperty(PropertyInfo prop)
- {
- return prop.PropertyType == typeof(Material) || prop.PropertyType == typeof(Material[]);
- }
- private static List<Material> GetMaterials(Object obj)
- {
- var result = new List<Material>();
- var allMaterialProperties = obj.GetType().GetMaterialPropertiesWithoutLeaking();
- foreach (var property in allMaterialProperties)
- {
- var value = property.GetGetMethod().GetMaterialFromMethod(obj, (methodName, objectName) =>
- $"The method {methodName} was not found on {objectName}. This property will not be indexed.");
- if (value is Material materialResult)
- {
- result.Add(materialResult);
- }
- else if (value is Material[] materialList)
- {
- result.AddRange(materialList);
- }
- }
- return result;
- }
- /// <summary>
- /// Gets all of the types in the Material Reference lookup that are components. Used to determine whether to run the
- /// method directly or on the component
- /// </summary>
- /// <returns>List of types that are components</returns>
- public static List<Type> GetComponentTypes()
- {
- return MaterialReferenceLookup.Keys.Where(key => typeof(Component).IsAssignableFrom(key)).ToList();
- }
- /// <summary>
- /// Gets all material properties from an object or a component of an object
- /// </summary>
- /// <param name="obj">The GameObject or Scriptable Object</param>
- /// <returns>List of Materials</returns>
- public static List<Material> GetMaterialsFromObject(Object obj)
- {
- var result = new List<Material>();
- if (obj is GameObject go)
- {
- foreach (var key in GetComponentTypes())
- {
- var components = go.GetComponentsInChildren(key);
- foreach (var component in components)
- {
- result.AddRange(GetMaterials(component));
- }
- }
- }
- else
- {
- result.AddRange(GetMaterials(obj));
- }
- return result.Distinct().ToList();
- }
- /// <summary>
- /// Text Mesh pro will sometimes be missing the GetFontSharedMaterials method, even though the property is supposed
- /// to have that method. This gracefully handles that case.
- /// </summary>
- /// <param name="method">The Method being invoked</param>
- /// <param name="obj">The Unity Object the method is invoked upon</param>
- /// <param name="generateErrorString">The function that takes the method name and object name and produces an error string</param>
- /// <returns>The resulting object from invoking the method on the Object</returns>
- /// <exception cref="Exception">Any exception that is not the missing method exception</exception>
- public static object GetMaterialFromMethod(this MethodInfo method,
- Object obj,
- Func<string, string, string> generateErrorString)
- {
- object result = null;
- try
- {
- result = method.Invoke(obj, null);
- }
- catch (Exception e)
- {
- // swallow the missing method exception, there's nothing we can do about it at this point
- // and we've already checked for other possible null exceptions here
- if ((e.InnerException is NullReferenceException))
- {
- Debug.LogWarning(generateErrorString(method.Name, obj.name));
- }
- else
- {
- throw e;
- }
- }
- return result;
- }
- /// <summary>
- /// Gets the SharedMaterial(s) properties when there are shared materials so that we don't leak material instances into the scene
- /// </summary>
- /// <param name="property">The property Type that we are getting the SharedMaterial(s) properties from</param>
- /// <returns>List of shared material properties and other material properties that won't leak material instances</returns>
- public static IEnumerable<PropertyInfo> GetMaterialPropertiesWithoutLeaking(this Type property)
- {
- var materialProps = property.GetProperties().Where(HasMaterialProperty).ToList();
- // if there is a sharedMaterial property or sharedMaterials property, remove the property that will leak materials
- var sharedMaterialProps =
- materialProps.Where(prop => prop.Name.ToLowerInvariant().Contains("shared")).ToList();
- var propsToRemove = sharedMaterialProps
- .Select(prop => prop.Name.ToLowerInvariant().Replace("shared", string.Empty))
- .ToList();
- materialProps.RemoveAll(prop => propsToRemove.Contains(prop.Name.ToLowerInvariant()));
- // also remove any property which has no setter
- materialProps.RemoveAll(prop => prop.SetMethod == null);
- return materialProps;
- }
- /// <summary>
- /// Get whether or not a Material is considered readonly (Built In Resource)
- /// </summary>
- /// <param name="material">The Material to test</param>
- /// <returns>Boolean of whether or not that Material is considered readonly</returns>
- public static bool GetIsReadonlyMaterial(Material material)
- {
- var assetPath = AssetDatabase.GetAssetPath(material);
- return string.IsNullOrEmpty(assetPath) || assetPath.Equals(@"Resources/unity_builtin_extra", StringComparison.OrdinalIgnoreCase);
- }
- }
- }
|