// Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.Internal; using UnityEngine.Scripting; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor.AssetImporters; using Object = UnityEngine.Object; using UnityEditor.Profiling; using UnityEditor.Callbacks; namespace UnityEditor { // AssetPostprocessor lets you hook into the import pipeline and run scripts prior or after importing assets. public partial class AssetPostprocessor { internal struct PostprocessorInfo { public Type Type { get; } public string[] Methods { get; } public uint Version { get; } public int Priority { get; } /// /// StaticDependency is true if any method in the postprocessor is not part of the NonAutomaticDependencyMethods list. /// This is used to know which PostprocessorInfo should be used for the static importer dependency hash. /// public bool StaticDependency { get; } public PostprocessorInfo(Type assetPostprocessorType, int importerPriority) { Type = assetPostprocessorType; Methods = null; Version = 0; StaticDependency = false; Priority = importerPriority; } public PostprocessorInfo(Type assetPostprocessorType, string[] implementedMethods) { Type = assetPostprocessorType; Methods = implementedMethods; StaticDependency = Methods.Intersect(AssetPostprocessingInternal.k_NonAutomaticDependencyMethods).Count() != Methods.Length; var inst = (AssetPostprocessor)Activator.CreateInstance(assetPostprocessorType); Version = inst.GetVersion(); Priority = inst.GetPostprocessOrder(); } } private string m_PathName; private AssetImportContext m_Context; // The path name of the asset being imported. public string assetPath { get { return m_PathName; } set { m_PathName = value; } } // The context of the import, used to specify dependencies public AssetImportContext context { get { return m_Context; } internal set { m_Context = value; } } // Logs an import warning to the console. [ExcludeFromDocs] [Obsolete("Use context.LogImportWarning(string) instead.")] public void LogWarning(string warning) { Object context = null; LogWarning(warning, context); } [Obsolete("Use context.LogImportWarning(string, Object) instead.")] public void LogWarning(string warning, [DefaultValue("null")] Object context) { if (m_Context != null) m_Context.LogImportWarning(warning, context); else Debug.LogWarning(warning, context); } // Logs an import error message to the console. [ExcludeFromDocs] [Obsolete("Use context.LogImportError(string) instead.")] public void LogError(string warning) { Object context = null; LogError(warning, context); } [Obsolete("Use context.LogImportError(string, Object) instead.")] public void LogError(string warning, [DefaultValue("null")] Object context) { if (m_Context != null) m_Context.LogImportError(warning, context); else Debug.LogError(warning, context); } // Returns the version of the asset postprocessor. public virtual uint GetVersion() { return 0; } // Reference to the asset importer public AssetImporter assetImporter { get { return AssetImporter.GetAtPath(assetPath); } } [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("To set or get the preview, call EditorUtility.SetAssetPreview or AssetPreview.GetAssetPreview instead", true)] public Texture2D preview { get { return null; } set {} } // Override the order in which importers are processed. public virtual int GetPostprocessOrder() { return 0; } } class OnPostprocessAllAssetsCallbackCollection : OrderedCallbackCollection { public class MethodInfoCallback : Callback { public MethodInfo Method { get; } public override Type classType => Method.DeclaringType; public bool MethodDomainReload { get; } public override string name => classType.FullName; public MethodInfoCallback(MethodInfo method, bool methodDomainReload) { Method = method; MethodDomainReload = methodDomainReload; } public override IEnumerable GetCustomAttributes() => Method.GetCustomAttributes(); } public override string name => "OnPostprocessAllAssets"; public override List GetCallbacks() { var methodArgTypes = new Type[] { typeof(string).MakeArrayType(), typeof(string).MakeArrayType(), typeof(string).MakeArrayType(), typeof(string).MakeArrayType() }; var methodDomainReloadParamArgTypes = new Type[] { methodArgTypes[0], methodArgTypes[1], methodArgTypes[2], methodArgTypes[3], typeof(bool) }; const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; var callbacks = new List(); foreach (var assetPostprocessorClass in TypeCache.GetTypesDerivedFrom()) { var method = assetPostprocessorClass.GetMethod("OnPostprocessAllAssets", flags, null, methodArgTypes, null); if (method != null) { callbacks.Add(new MethodInfoCallback(method, false)); } else { // OnPostprocessAllAssets with didDomainReload parameter method = assetPostprocessorClass.GetMethod("OnPostprocessAllAssets", flags, null, methodDomainReloadParamArgTypes, null); if (method != null) { callbacks.Add(new MethodInfoCallback(method, true)); } } } return callbacks; } } internal class AssetPostprocessingInternal { // What is it: // Static postprocessor methods always called for each importer that are part of importer static dependency. // No new postprocessors should be added to these lists. Please reach out to #devs-import-workflow to talk about new additions. internal static readonly string[] k_NonAutomaticDependencyMethods = { "OnPreprocessAsset", }; static readonly string[] k_ModelImporterPostprocessors = { "OnPreprocessModel", "OnPostprocessMeshHierarchy", "OnPostprocessModel", "OnPreprocessAnimation", "OnPostprocessAnimation", "OnPostprocessGameObjectWithAnimatedUserProperties", "OnPostprocessGameObjectWithUserProperties", "OnPostprocessMaterial", "OnAssignMaterialModel", "OnPreprocessMaterialDescription", }; static readonly string[] k_DynamicModelImporterPostprocessors = { "OnPreprocessCameraDescription", "OnPreprocessLightDescription" }; static readonly string[] k_TextureImporterPostprocessors = { "OnPreprocessTexture", "OnPostprocessTexture", "OnPostprocessCubemap", "OnPostprocessSprites", "OnPostprocessTexture3D", "OnPostprocessTexture2DArray" }; static readonly string[] k_IHVImporterPostprocessors = { "OnPostprocessTexture", }; static readonly string[] k_AudioImporterPostprocessors = { "OnPreprocessAudio", "OnPostprocessAudio", }; static readonly string[] k_SpeedTreeImporterPostprocessors = { "OnPreprocessSpeedTree", "OnPostprocessSpeedTree", }; static readonly string[] k_PrefabImporterPostprocessors = { "OnPostprocessPrefab", }; static readonly string[] k_CameraPostprocessors = { "OnPreprocessCameraDescription", }; static readonly string[] k_LightPostprocessors = { "OnPreprocessLightDescription", }; static readonly string[] k_TexturePreprocessors = { "OnPreprocessTexture", }; static readonly string[] k_Texture2DPostprocessors = { "OnPostprocessTexture", }; static readonly string[] k_Texture2DArrayPostprocessors = { "OnPostprocessTexture2DArray" }; static readonly string[] k_Texture3DPostprocessors = { "OnPostprocessTexture3D" }; static readonly string[] k_TextureCubePostprocessors = { "OnPostprocessCubemap" }; static readonly string[] k_SpritePostprocessors = { "OnPostprocessSprites", }; static Dictionary s_PostprocessorMethodsByDependencyKey; static Dictionary s_StaticPostprocessorMethodsByImporterType; static Dictionary s_DynamicPostprocessorMethodsByImporterType; // Internal for debugging purposes. We can generate dependency graphs to help understand issues. internal static OnPostprocessAllAssetsCallbackCollection s_OnPostprocessAllAssetsCallbacks = new OnPostprocessAllAssetsCallbackCollection(); static AssetPostprocessingInternal() { s_StaticPostprocessorMethodsByImporterType = new Dictionary(); s_StaticPostprocessorMethodsByImporterType.Add(typeof(ModelImporter), k_ModelImporterPostprocessors); s_StaticPostprocessorMethodsByImporterType.Add(typeof(IHVImageFormatImporter), k_IHVImporterPostprocessors); s_StaticPostprocessorMethodsByImporterType.Add(typeof(SpeedTreeImporter), k_SpeedTreeImporterPostprocessors); s_StaticPostprocessorMethodsByImporterType.Add(typeof(AudioImporter), k_AudioImporterPostprocessors); s_StaticPostprocessorMethodsByImporterType.Add(typeof(PrefabImporter), k_PrefabImporterPostprocessors); s_DynamicPostprocessorMethodsByImporterType = new Dictionary(); s_DynamicPostprocessorMethodsByImporterType.Add(typeof(ModelImporter), k_DynamicModelImporterPostprocessors); s_DynamicPostprocessorMethodsByImporterType.Add(typeof(TextureImporter), k_TextureImporterPostprocessors); s_PostprocessorMethodsByDependencyKey = new Dictionary(); s_PostprocessorMethodsByDependencyKey.Add(kCameraPostprocessorDependencyName, k_CameraPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kLightPostprocessorDependencyName, k_LightPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTexturePreprocessorDependencyName, k_TexturePreprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTexture2DPostprocessorDependencyName, k_Texture2DPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTexture2DArrayPostprocessorDependencyName, k_Texture2DArrayPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTexture3DPostprocessorDependencyName, k_Texture3DPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTextureCubePostprocessorDependencyName, k_TextureCubePostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTextureSpritePostprocessorDependencyName, k_SpritePostprocessors); } [Serializable] class AssetPostProcessorAnalyticsData { public string importActionId; public List postProcessorCalls = new List(); } [Serializable] struct AssetPostProcessorMethodCallAnalyticsData { public string methodName; public float duration_sec; public int invocationCount; } static void LogPostProcessorMissingDefaultConstructor(Type type) { Debug.LogErrorFormat("{0} requires a default constructor to be used as an asset post processor", type); } [RequiredByNativeCode] // Postprocess on all assets once an automatic import has completed static void PostprocessAllAssets(string[] importedAssets, string[] addedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromPathAssets, bool didDomainReload) { object[] args = { importedAssets, deletedAssets, movedAssets, movedFromPathAssets }; object[] argsWithDidDomainReload = { importedAssets, deletedAssets, movedAssets, movedFromPathAssets, didDomainReload}; var containsNoAssets = importedAssets.Length == 0 && addedAssets.Length == 0 && deletedAssets.Length == 0 && movedAssets.Length == 0 && movedFromPathAssets.Length == 0; foreach (OnPostprocessAllAssetsCallbackCollection.MethodInfoCallback assetPostProcessor in s_OnPostprocessAllAssetsCallbacks.sortedCallbacks) { try { if (assetPostProcessor.MethodDomainReload) { using (new EditorPerformanceMarker($"{assetPostProcessor.classType.Name}.OnPostprocessAllAssets", assetPostProcessor.classType).Auto()) InvokeMethod(assetPostProcessor.Method, argsWithDidDomainReload); } else { if (containsNoAssets) continue; using (new EditorPerformanceMarker($"{assetPostProcessor.classType.Name}.OnPostprocessAllAssets", assetPostProcessor.classType).Auto()) InvokeMethod(assetPostProcessor.Method, args); } } catch (Exception e) { Debug.LogException(e); } } using (new EditorPerformanceMarker("SyncVS.PostprocessSyncProject").Auto()) CodeEditorProjectSync.PostprocessSyncProject(importedAssets, addedAssets, deletedAssets, movedAssets, movedFromPathAssets); } internal class CompareAssetImportPriority : IComparer, IComparer { public int Compare(AssetPostprocessor.PostprocessorInfo x, AssetPostprocessor.PostprocessorInfo y) { int xo = x.Priority; int yo = y.Priority; var compare = xo.CompareTo(yo); if (compare == 0) { compare = x.Type.FullName.CompareTo(y.Type.FullName); if (compare == 0) compare = x.Type.AssemblyQualifiedName.CompareTo(y.Type.AssemblyQualifiedName); } return compare; } public int Compare(AssetPostprocessor x, AssetPostprocessor y) { var xi = new AssetPostprocessor.PostprocessorInfo(x.GetType(), x.GetPostprocessOrder()); var yi = new AssetPostprocessor.PostprocessorInfo(y.GetType(), y.GetPostprocessOrder()); return Compare(xi, yi); } } private static string BuildStaticDependencyHashString(SortedSet list) { var hashStr = ""; foreach (var info in list) { if (info.StaticDependency) { hashStr += info.Type.AssemblyQualifiedName; hashStr += '.'; hashStr += info.Version; hashStr += '|'; } } return hashStr; } private static string BuildHashString(SortedList list) { var hashStr = ""; foreach (var pair in list) { hashStr += pair.Key; hashStr += '.'; hashStr += pair.Value; hashStr += '|'; } return hashStr; } internal const string kCameraPostprocessorDependencyName = "postprocessor/camera"; internal const string kLightPostprocessorDependencyName = "postprocessor/light"; internal const string kTexture2DPostprocessorDependencyName = "postprocessor/texture2D"; internal const string kTextureCubePostprocessorDependencyName = "postprocessor/textureCube"; internal const string kTexture3DPostprocessorDependencyName = "postprocessor/texture3D"; internal const string kTexture2DArrayPostprocessorDependencyName = "postprocessor/texture2DArray"; internal const string kTextureSpritePostprocessorDependencyName = "postprocessor/textureSprite"; internal const string kTexturePreprocessorDependencyName = "postprocessor/texturePreprocessor"; static Stack> m_PostprocessStack = null; static SortedSet m_ImportProcessors = null; static Type[] m_PostprocessorClasses = null; static string m_MeshProcessorsHashString = null; static string m_AudioProcessorsHashString = null; static string m_SpeedTreeProcessorsHashString = null; static string m_PrefabProcessorsHashString = null; static string m_CameraProcessorsHashString = null; static string m_LightProcessorsHashString = null; static string m_Texture2DProcessorsHashString = null; static string m_TextureCubeProcessorsHashString = null; static string m_Texture3DPostprocessorDependencyName = null; static string m_Texture2DArrayDependencyName = null; static string m_TextureSpriteDependencyName = null; static string m_TexturePreprocessorDependencyName = null; static Dictionary> s_StaticPostprocessorsPerImporterType = new Dictionary>(); static Dictionary> s_DynamicPostprocessorsPerImporterType = new Dictionary>(); static Stack s_AnalyticsEventsStack = new Stack(); static Type[] GetCachedAssetPostprocessorClasses() { if (m_PostprocessorClasses == null) m_PostprocessorClasses = TypeCache.GetTypesDerivedFrom().ToArray(); return m_PostprocessorClasses; } [RequiredByNativeCode] static void InitPostprocessorsForTextureGenerator(string pathName) { var analyticsEvent = new AssetPostProcessorAnalyticsData(); analyticsEvent.importActionId = "None"; s_AnalyticsEventsStack.Push(analyticsEvent); m_ImportProcessors = new SortedSet(new CompareAssetImportPriority()); foreach (var postprocessorInfo in GetSortedStaticPostprocessorTypes(typeof(TextureImporter))) { var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type); assetPostprocessor.assetPath = pathName; assetPostprocessor.context = null; m_ImportProcessors.Add(assetPostprocessor); } foreach (var postprocessorInfo in GetSortedDynamicPostprocessorTypes(typeof(TextureImporter))) { var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type); assetPostprocessor.assetPath = pathName; assetPostprocessor.context = null; m_ImportProcessors.Add(assetPostprocessor); } // Setup postprocessing stack to support reentrancy (Import asset immediate) if (m_PostprocessStack == null) m_PostprocessStack = new Stack>(); m_PostprocessStack.Push(m_ImportProcessors); } [RequiredByNativeCode] static void InitPostprocessors(AssetImportContext context, string pathName, Type importerType, double importStartTime) { var analyticsEvent = new AssetPostProcessorAnalyticsData(); analyticsEvent.importActionId = ((int)Math.Floor(importStartTime * 1000)).ToString(); s_AnalyticsEventsStack.Push(analyticsEvent); m_ImportProcessors = new SortedSet(new CompareAssetImportPriority()); foreach (var postprocessorInfo in GetSortedStaticPostprocessorTypes(importerType)) { var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type); assetPostprocessor.assetPath = pathName; assetPostprocessor.context = context; m_ImportProcessors.Add(assetPostprocessor); } foreach (var postprocessorInfo in GetSortedDynamicPostprocessorTypes(importerType)) { var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type); assetPostprocessor.assetPath = pathName; assetPostprocessor.context = context; m_ImportProcessors.Add(assetPostprocessor); } // Setup postprocessing stack to support reentrancy (Import asset immediate) if (m_PostprocessStack == null) m_PostprocessStack = new Stack>(); m_PostprocessStack.Push(m_ImportProcessors); } [RequiredByNativeCode] static void CleanupPostprocessors() { if (m_PostprocessStack != null) { m_PostprocessStack.Pop(); m_ImportProcessors = m_PostprocessStack.Count > 0 ? m_PostprocessStack.Peek() : null; } if (s_AnalyticsEventsStack.Count > 0) { var lastEvent = s_AnalyticsEventsStack.Pop(); if (lastEvent.postProcessorCalls.Count > 0) EditorAnalytics.SendAssetPostprocessorsUsage(lastEvent); } } static bool ImplementsAnyOfTheses(Type type, IEnumerable methods, out List usedMethods) { usedMethods = new List(methods.Where(method => type.GetMethod(method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null)); return usedMethods.Count > 0; } /* * Returns the list of actual dynamic postprocessor methods for a particular asset. * Note: That where the asset is not yet imported, this list will be empty. */ internal static SortedSet GetSortedDynamicPostprocessorsForAsset(string path) { var list = new SortedSet(new CompareAssetImportPriority()); var guid = AssetDatabase.GUIDFromAssetPath(path); if (guid.Empty()) return list; //Artifact Infos may contains multiple artifacts, associated with different version of the object (E.g. Main, Preview etc.) var artifactInfos = AssetDatabase.GetArtifactInfos(guid); var allMethodsNames = new List(); foreach (var info in artifactInfos) { if (!info.isCurrentArtifact) continue; foreach (var kvp in info.dependencies) { if (kvp.Value.type == ArtifactInfoDependencyType.Dynamic) { //Try to retrieve Postprocessor Methods associated with the supplied Dependency keys string dependencyName = kvp.Key.Replace(ArtifactDifferenceReporter.kEnvironment_CustomDependency + "/", ""); if (s_PostprocessorMethodsByDependencyKey.TryGetValue(dependencyName, out var methodNames)) allMethodsNames.AddRange(methodNames); } } } if (allMethodsNames.Count == 0) return list; /* * The asset has dynamic dependencies to an Asset Postprocessor, so let's find any Postprocessors which * implements those methods. */ var distinctMethodNames = allMethodsNames.Distinct(); foreach (Type assetPostprocessorClass in GetCachedAssetPostprocessorClasses()) { if (ImplementsAnyOfTheses(assetPostprocessorClass, distinctMethodNames, out var methods)) { if (assetPostprocessorClass.GetConstructors().Any(t => t.GetParameters().Count() == 0)) list.Add(new AssetPostprocessor.PostprocessorInfo(assetPostprocessorClass, methods.ToArray())); else LogPostProcessorMissingDefaultConstructor(assetPostprocessorClass); } } return list; } internal static SortedSet GetSortedStaticPostprocessorTypes(Type importer) { var defaultMethods = new string[] { "OnPreprocessAsset" }; return GetSortedPostprocessorTypes(importer, s_StaticPostprocessorMethodsByImporterType, defaultMethods, s_StaticPostprocessorsPerImporterType); } /* * Returns the list of *possible* dynamic postprocessor methods associated with a particular importer type. * See also: GetSortedDynamicPostprocessorsForAsset, to get the actual postprocessor methods for a given asset. */ internal static SortedSet GetSortedDynamicPostprocessorTypes(Type importer) { return GetSortedPostprocessorTypes(importer, s_DynamicPostprocessorMethodsByImporterType, new string[0], s_DynamicPostprocessorsPerImporterType); } static SortedSet GetSortedPostprocessorTypes(Type importer, Dictionary postprocessorMethodsByImporterType, string[] defaultMethods, Dictionary> cache) { if (cache.TryGetValue(importer, out var cachedPostprocessors)) return cachedPostprocessors; var list = new SortedSet(new CompareAssetImportPriority()); var allMethodsNames = defaultMethods.ToList(); var methodsType = importer; while (methodsType != null && methodsType != typeof(AssetImporter)) { if (postprocessorMethodsByImporterType.TryGetValue(methodsType, out var methodNames)) allMethodsNames.AddRange(methodNames); methodsType = methodsType.BaseType; } foreach (Type assetPostprocessorClass in GetCachedAssetPostprocessorClasses()) { if (ImplementsAnyOfTheses(assetPostprocessorClass, allMethodsNames, out var methods)) { if (assetPostprocessorClass.GetConstructors().Any(t => t.GetParameters().Count() == 0)) list.Add(new AssetPostprocessor.PostprocessorInfo(assetPostprocessorClass, methods.ToArray())); else LogPostProcessorMissingDefaultConstructor(assetPostprocessorClass); } } cache.Add(importer, list); return list; } [RequiredByNativeCode] static string GetMeshProcessorsHashString() { if (m_MeshProcessorsHashString != null) return m_MeshProcessorsHashString; m_MeshProcessorsHashString = BuildStaticDependencyHashString(GetSortedStaticPostprocessorTypes(typeof(ModelImporter))); return m_MeshProcessorsHashString; } [RequiredByNativeCode] static void PreprocessAsset() { foreach (AssetPostprocessor inst in m_ImportProcessors) { InvokeMethodIfAvailable(inst, "OnPreprocessAsset", null); } } [RequiredByNativeCode] static void PreprocessModel(string pathName) { CallPostProcessMethods("OnPreprocessModel", null); } [RequiredByNativeCode] static void PreprocessSpeedTree(string pathName) { CallPostProcessMethods("OnPreprocessSpeedTree", null); } [RequiredByNativeCode] static void PreprocessAnimation(string pathName) { CallPostProcessMethods("OnPreprocessAnimation", null); } [RequiredByNativeCode] static void PostprocessAnimation(GameObject root, AnimationClip clip) { object[] args = { root, clip }; CallPostProcessMethods("OnPostprocessAnimation", args); } [RequiredByNativeCode] static Material ProcessMeshAssignMaterial(Renderer renderer, Material material) { object[] args = { material, renderer }; Material assignedMaterial; CallPostProcessMethodsUntilReturnedObjectIsValid("OnAssignMaterialModel", args, out assignedMaterial); return assignedMaterial; } [RequiredByNativeCode] static bool ProcessMeshHasAssignMaterial() { foreach (AssetPostprocessor inst in m_ImportProcessors) { if (inst.GetType().GetMethod("OnAssignMaterialModel", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null) return true; } return false; } [RequiredByNativeCode] static void PostprocessMeshHierarchy(GameObject root) { object[] args = { root }; CallPostProcessMethods("OnPostprocessMeshHierarchy", args); } [RequiredByNativeCode] static void PostprocessMesh(GameObject gameObject) { object[] args = { gameObject }; CallPostProcessMethods("OnPostprocessModel", args); } [RequiredByNativeCode] static void PostprocessSpeedTree(GameObject gameObject) { object[] args = { gameObject }; CallPostProcessMethods("OnPostprocessSpeedTree", args); } [RequiredByNativeCode] static void PostprocessMaterial(Material material) { object[] args = { material }; CallPostProcessMethods("OnPostprocessMaterial", args); } [RequiredByNativeCode] static void PreprocessCameraDescription(AssetImportContext assetImportContext, CameraDescription description, Camera camera, AnimationClip[] animations) { assetImportContext.DependsOnCustomDependency(kCameraPostprocessorDependencyName); object[] args = { description, camera, animations }; CallPostProcessMethods("OnPreprocessCameraDescription", args); } [RequiredByNativeCode] static void PreprocessLightDescription(AssetImportContext assetImportContext, LightDescription description, Light light, AnimationClip[] animations) { assetImportContext.DependsOnCustomDependency(kLightPostprocessorDependencyName); object[] args = { description, light, animations }; CallPostProcessMethods("OnPreprocessLightDescription", args); } [RequiredByNativeCode] static void PreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] animations) { object[] args = { description, material, animations }; CallPostProcessMethods("OnPreprocessMaterialDescription", args); } [RequiredByNativeCode] static void PostprocessGameObjectWithUserProperties(GameObject go, string[] prop_names, object[] prop_values) { object[] args = { go, prop_names, prop_values }; CallPostProcessMethods("OnPostprocessGameObjectWithUserProperties", args); } [RequiredByNativeCode] static EditorCurveBinding[] PostprocessGameObjectWithAnimatedUserProperties(GameObject go, EditorCurveBinding[] bindings) { object[] args = { go, bindings }; CallPostProcessMethods("OnPostprocessGameObjectWithAnimatedUserProperties", args); return bindings; } [RequiredByNativeCode] static void PreprocessTexture(string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTexturePreprocessorDependencyName); } CallPostProcessMethods("OnPreprocessTexture", null); } [RequiredByNativeCode] static void PreprocessTextureFromScript(string pathName) { CallPostProcessMethods("OnPreprocessTexture", null); } [RequiredByNativeCode] static void PostprocessTexture(Texture2D tex, string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTexture2DPostprocessorDependencyName); } object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture", args); } [RequiredByNativeCode] static void PostprocessTextureFromScript(Texture2D tex, string pathName) { object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture", args); } [RequiredByNativeCode] static void PostprocessCubemap(Cubemap tex, string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTextureCubePostprocessorDependencyName); } object[] args = { tex }; CallPostProcessMethods("OnPostprocessCubemap", args); } [RequiredByNativeCode] static void PostprocessCubemapFromScript(Cubemap tex, string pathName) { object[] args = { tex }; CallPostProcessMethods("OnPostprocessCubemap", args); } [RequiredByNativeCode] static void PostprocessTexture3D(Texture3D tex, string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTexture3DPostprocessorDependencyName); } object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture3D", args); } [RequiredByNativeCode] static void PostprocessTexture3DFromScript(Texture3D tex, string pathName) { object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture3D", args); } [RequiredByNativeCode] static void PostprocessTexture2DArray(Texture2DArray tex, string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTexture2DArrayPostprocessorDependencyName); } object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture2DArray", args); } [RequiredByNativeCode] static void PostprocessTexture2DArrayFromScript(Texture2DArray tex, string pathName) { object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture2DArray", args); } [RequiredByNativeCode] static void PostprocessSprites(Texture2D tex, string pathName, Sprite[] sprites, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTextureSpritePostprocessorDependencyName); } object[] args = { tex, sprites }; CallPostProcessMethods("OnPostprocessSprites", args); } [RequiredByNativeCode] static void PostprocessSpritesFromScript(Texture2D tex, string pathName, Sprite[] sprites) { object[] args = { tex, sprites }; CallPostProcessMethods("OnPostprocessSprites", args); } [RequiredByNativeCode] static string GetAudioProcessorsHashString() { if (m_AudioProcessorsHashString != null) return m_AudioProcessorsHashString; m_AudioProcessorsHashString = BuildStaticDependencyHashString(GetSortedStaticPostprocessorTypes(typeof(AudioImporter))); return m_AudioProcessorsHashString; } [RequiredByNativeCode] static void PreprocessAudio(string pathName) { CallPostProcessMethods("OnPreprocessAudio", null); } [RequiredByNativeCode] static void PostprocessAudio(AudioClip clip, string pathName) { object[] args = { clip }; CallPostProcessMethods("OnPostprocessAudio", args); } [RequiredByNativeCode] static string GetPrefabProcessorsHashString() { if (m_PrefabProcessorsHashString != null) return m_PrefabProcessorsHashString; m_PrefabProcessorsHashString = BuildStaticDependencyHashString(GetSortedStaticPostprocessorTypes(typeof(PrefabImporter))); return m_PrefabProcessorsHashString; } [RequiredByNativeCode] static void PostprocessPrefab(GameObject prefabAssetRoot) { object[] args = { prefabAssetRoot }; CallPostProcessMethods("OnPostprocessPrefab", args); } [RequiredByNativeCode] static void PostprocessAssetbundleNameChanged(string assetPath, string previousAssetBundleName, string newAssetBundleName) { object[] args = { assetPath, previousAssetBundleName, newAssetBundleName }; foreach (var assetPostprocessorClass in GetCachedAssetPostprocessorClasses()) { var assetPostprocessor = Activator.CreateInstance(assetPostprocessorClass) as AssetPostprocessor; InvokeMethodIfAvailable(assetPostprocessor, "OnPostprocessAssetbundleNameChanged", args); } } [RequiredByNativeCode] static string GetSpeedTreeProcessorsHashString() { if (m_SpeedTreeProcessorsHashString != null) return m_SpeedTreeProcessorsHashString; m_SpeedTreeProcessorsHashString = BuildStaticDependencyHashString(GetSortedStaticPostprocessorTypes(typeof(SpeedTreeImporter))); return m_SpeedTreeProcessorsHashString; } [InitializeOnLoadMethod] static void RefreshCustomDependencies() { AssetDatabase.RegisterCustomDependency(kCameraPostprocessorDependencyName, Hash128.Compute(GetCameraProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kLightPostprocessorDependencyName, Hash128.Compute(GetLightProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTexture2DPostprocessorDependencyName, Hash128.Compute(GetTexture2DProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTextureCubePostprocessorDependencyName, Hash128.Compute(GetTextureCubeProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTexture3DPostprocessorDependencyName, Hash128.Compute(GetTexture3DProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTexture2DArrayPostprocessorDependencyName, Hash128.Compute(GetTexture2DArrayProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTextureSpritePostprocessorDependencyName, Hash128.Compute(GetTextureSpriteProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTexturePreprocessorDependencyName, Hash128.Compute(GetTexturePreProcessorsHashString())); } static void GetProcessorHashString(string methodName, ref string hashString) { if (hashString != null) return; var versionsByType = new SortedList(); foreach (var assetPostprocessorClass in GetCachedAssetPostprocessorClasses()) { try { if (assetPostprocessorClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null) { var inst = Activator.CreateInstance(assetPostprocessorClass) as AssetPostprocessor; uint version = inst.GetVersion(); versionsByType.Add(assetPostprocessorClass.FullName, version); } } catch (MissingMethodException) { LogPostProcessorMissingDefaultConstructor(assetPostprocessorClass); } catch (Exception e) { Debug.LogException(e); } } hashString = BuildHashString(versionsByType); } [RequiredByNativeCode] static string GetCameraProcessorsHashString() { GetProcessorHashString("OnPreprocessCameraDescription", ref m_CameraProcessorsHashString); return m_CameraProcessorsHashString; } [RequiredByNativeCode] static string GetLightProcessorsHashString() { GetProcessorHashString("OnPreprocessLightDescription", ref m_LightProcessorsHashString); return m_LightProcessorsHashString; } [RequiredByNativeCode] static string GetTexturePreProcessorsHashString() { GetProcessorHashString("OnPreprocessTexture", ref m_TexturePreprocessorDependencyName); return m_TexturePreprocessorDependencyName; } [RequiredByNativeCode] static string GetTexture2DProcessorsHashString() { GetProcessorHashString("OnPostprocessTexture", ref m_Texture2DProcessorsHashString); return m_Texture2DProcessorsHashString; } [RequiredByNativeCode] static string GetTextureCubeProcessorsHashString() { GetProcessorHashString("OnPostprocessCubemap", ref m_TextureCubeProcessorsHashString); return m_TextureCubeProcessorsHashString; } [RequiredByNativeCode] static string GetTexture3DProcessorsHashString() { GetProcessorHashString("OnPostprocessTexture3D", ref m_Texture3DPostprocessorDependencyName); return m_Texture3DPostprocessorDependencyName; } [RequiredByNativeCode] static string GetTexture2DArrayProcessorsHashString() { GetProcessorHashString("OnPostprocessTexture2DArray", ref m_Texture2DArrayDependencyName); return m_Texture2DArrayDependencyName; } [RequiredByNativeCode] static string GetTextureSpriteProcessorsHashString() { GetProcessorHashString("OnPostprocessSprites", ref m_TextureSpriteDependencyName); return m_TextureSpriteDependencyName; } static bool IsAssetPostprocessorAnalyticsEnabled() { return EditorAnalytics.enabled; } static void CallPostProcessMethodsUntilReturnedObjectIsValid(string methodName, object[] args, out T returnedObject) where T : class { returnedObject = default(T); int invocationCount = 0; float startTime = Time.realtimeSinceStartup; foreach (AssetPostprocessor inst in m_ImportProcessors) { if (InvokeMethodIfAvailable(inst, methodName, args, ref returnedObject)) { invocationCount++; break; } } if (IsAssetPostprocessorAnalyticsEnabled() && invocationCount > 0) { var methodCallAnalytics = new AssetPostProcessorMethodCallAnalyticsData(); methodCallAnalytics.invocationCount = invocationCount; methodCallAnalytics.methodName = methodName; methodCallAnalytics.duration_sec = Time.realtimeSinceStartup - startTime; s_AnalyticsEventsStack.Peek().postProcessorCalls.Add(methodCallAnalytics); } } static void CallPostProcessMethods(string methodName, object[] args) { if (m_ImportProcessors == null) { throw new Exception("m_ImportProcessors is null, InitPostProcessors should be called before any of the post process methods are called."); } if (IsAssetPostprocessorAnalyticsEnabled()) { int invocationCount = 0; float startTime = Time.realtimeSinceStartup; foreach (AssetPostprocessor inst in m_ImportProcessors) { if (InvokeMethodIfAvailable(inst, methodName, args)) invocationCount++; } if (invocationCount > 0) { var methodCallAnalytics = new AssetPostProcessorMethodCallAnalyticsData(); methodCallAnalytics.invocationCount = invocationCount; methodCallAnalytics.methodName = methodName; methodCallAnalytics.duration_sec = Time.realtimeSinceStartup - startTime; s_AnalyticsEventsStack.Peek().postProcessorCalls.Add(methodCallAnalytics); } } else { foreach (AssetPostprocessor inst in m_ImportProcessors) { InvokeMethodIfAvailable(inst, methodName, args); } } } static object InvokeMethod(MethodInfo method, object[] args) { object res = null; using (new EditorPerformanceMarker(method.DeclaringType.Name + "." + method.Name, method.DeclaringType).Auto()) { res = method.Invoke(null, args); } return res; } static bool InvokeMethodIfAvailable(object target, string methodName, object[] args) { var type = target.GetType(); MethodInfo method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (method != null) { using (new EditorPerformanceMarker(type.Name + "." + methodName, type).Auto()) { method.Invoke(target, args); } return true; } return false; } static bool InvokeMethodIfAvailable(object target, string methodName, object[] args, ref T returnedObject) where T : class { var type = target.GetType(); MethodInfo method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (method != null) { using (new EditorPerformanceMarker(type.Name + "." + methodName, type).Auto()) { returnedObject = method.Invoke(target, args) as T; } return returnedObject != null; } return false; } } }