├── Asset Processors.meta ├── Asset Processors ├── Import Tasks.meta ├── Import Tasks │ ├── BaseImportTask.cs │ ├── BaseImportTask.cs.meta │ ├── ImportContext.cs │ ├── ImportContext.cs.meta │ ├── Importer Properties.meta │ ├── Importer Properties │ │ ├── ImporterPropertiesImportTask.cs │ │ ├── ImporterPropertiesImportTask.cs.meta │ │ ├── ImporterPropertiesImportTaskInspector.cs │ │ ├── ImporterPropertiesImportTaskInspector.cs.meta │ │ ├── PropertyConformObject.cs │ │ └── PropertyConformObject.cs.meta │ ├── Postprocessor.meta │ ├── Postprocessor │ │ ├── IPostprocessor.cs │ │ ├── IPostprocessor.cs.meta │ │ ├── PostprocessorConformObject.cs │ │ ├── PostprocessorConformObject.cs.meta │ │ ├── PostprocessorImplementorCache.cs │ │ ├── PostprocessorImplementorCache.cs.meta │ │ ├── PostprocessorImportTask.cs │ │ ├── PostprocessorImportTask.cs.meta │ │ ├── PostprocessorImportTaskInspector.cs │ │ └── PostprocessorImportTaskInspector.cs.meta │ ├── Preprocessor.meta │ └── Preprocessor │ │ ├── IPreprocessor.cs │ │ ├── IPreprocessor.cs.meta │ │ ├── PreprocessorConformObject.cs │ │ ├── PreprocessorConformObject.cs.meta │ │ ├── PreprocessorImplementorCache.cs │ │ ├── PreprocessorImplementorCache.cs.meta │ │ ├── PreprocessorImportTask.cs │ │ ├── PreprocessorImportTask.cs.meta │ │ ├── PreprocessorImportTaskInspector.cs │ │ └── PreprocessorImportTaskInspector.cs.meta ├── ImportDefinitionProfileAssetPostprocessor.cs ├── ImportDefinitionProfileAssetPostprocessor.cs.meta ├── Interfaces.meta ├── Interfaces │ ├── IConformObject.cs │ ├── IConformObject.cs.meta │ ├── IImportTask.cs │ └── IImportTask.cs.meta ├── UserDataSerialization.cs └── UserDataSerialization.cs.meta ├── Auditor.meta ├── Auditor ├── Assets Tree.meta ├── Assets Tree │ ├── AssetsTreeView.cs │ ├── AssetsTreeView.cs.meta │ ├── AssetsTreeViewItem.cs │ └── AssetsTreeViewItem.cs.meta ├── AuditWindow.cs ├── AuditWindow.cs.meta ├── Conform Tree.meta ├── Conform Tree │ ├── ConformObjectTreeViewItem.cs │ ├── ConformObjectTreeViewItem.cs.meta │ ├── ModularDetailTreeView.cs │ └── ModularDetailTreeView.cs.meta ├── GUIUtility.meta └── GUIUtility │ ├── ControlRect.cs │ ├── ControlRect.cs.meta │ ├── HierarchyTreeView.cs │ └── HierarchyTreeView.cs.meta ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── Documentation.meta ├── Documentation ├── AssetAuditor.md ├── AssetAuditor.md.meta ├── Images.meta └── Images │ ├── ConstrainToProperties.png │ ├── ConstrainToProperties.png.meta │ ├── ProfileInspector.png │ ├── ProfileInspector.png.meta │ ├── Rightclick.png │ └── Rightclick.png.meta ├── Import Definition Files.meta ├── Import Definition Files ├── ConformData.cs ├── ConformData.cs.meta ├── Filter.cs ├── Filter.cs.meta ├── ImportDefinitionProfile.cs ├── ImportDefinitionProfile.cs.meta ├── ImportDefinitionProfileCache.cs ├── ImportDefinitionProfileCache.cs.meta ├── ImportDefinitionProfileInspector.cs └── ImportDefinitionProfileInspector.cs.meta ├── README.md ├── README.md.meta ├── Unity.AssetTools.Editor.asmdef └── Unity.AssetTools.Editor.asmdef.meta /Asset Processors.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 75d31da4bfd4d493db870b492552c699 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6ad6b13ce753849e3928ad69d5ed20af 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/BaseImportTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using AssetTools.GUIUtility; 6 | using UnityEditor; 7 | using UnityEditor.IMGUI.Controls; 8 | using UnityEngine; 9 | 10 | namespace AssetTools 11 | { 12 | [System.Serializable] 13 | public abstract class BaseImportTask : ScriptableObject, IImportTask 14 | { 15 | public enum ProcessingType { Pre, Post } 16 | 17 | protected List m_AssetsToForceApply = new List(); 18 | protected string m_SearchFilter = ""; 19 | 20 | private SerializedObject m_SelfSerializedObject = null; 21 | 22 | protected SerializedObject SelfSerializedObject 23 | { 24 | get 25 | { 26 | if( m_SelfSerializedObject == null ) 27 | m_SelfSerializedObject = new SerializedObject( this ); 28 | return m_SelfSerializedObject; 29 | } 30 | } 31 | 32 | public virtual int Version 33 | { 34 | get { return 0; } 35 | } 36 | 37 | public virtual int MaximumCount 38 | { 39 | get { return 1; } 40 | } 41 | 42 | public virtual string ImportTaskName 43 | { 44 | get { return this.GetType().Name; } 45 | } 46 | 47 | public abstract string AssetMenuFixString { get; } 48 | 49 | public abstract ProcessingType TaskProcessType { get; } 50 | 51 | public abstract bool CanProcess( AssetImporter item ); 52 | 53 | public bool IsManuallyProcessing( AssetImporter item ) 54 | { 55 | return m_AssetsToForceApply.Contains( item.assetPath ); 56 | } 57 | 58 | public void SetManuallyProcessing( List assetPaths, bool value ) 59 | { 60 | for( int i = 0; i < assetPaths.Count; ++i ) 61 | { 62 | if( value && m_AssetsToForceApply.Contains( assetPaths[i] ) == false ) 63 | { 64 | m_AssetsToForceApply.Add( assetPaths[i] ); 65 | } 66 | else if( !value ) 67 | { 68 | m_AssetsToForceApply.Remove( assetPaths[i] ); 69 | } 70 | } 71 | } 72 | 73 | public void SetManuallyProcessing( string assetPath, bool value ) 74 | { 75 | if( value && m_AssetsToForceApply.Contains( assetPath ) == false ) 76 | { 77 | m_AssetsToForceApply.Add( assetPath ); 78 | } 79 | else if( !value ) 80 | { 81 | m_AssetsToForceApply.Remove( assetPath ); 82 | } 83 | } 84 | 85 | 86 | public virtual void DrawGUI( ControlRect layout ) 87 | { 88 | var type = GetType(); 89 | var so = new SerializedObject(this); 90 | var p = so.GetIterator(); 91 | p.Next(true); 92 | while (p.Next(false)) 93 | { 94 | var prop = type.GetField(p.name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); 95 | if(prop != null) 96 | EditorGUI.PropertyField(layout.Get(), p, false); 97 | } 98 | so.ApplyModifiedProperties(); 99 | } 100 | 101 | public abstract List GetConformObjects( string asset, ImportDefinitionProfile profile ); 102 | 103 | public abstract Type GetConformObjectType(); 104 | 105 | public virtual bool GetSearchFilter( out string searchFilter, List ignoreAssetPaths ) 106 | { 107 | searchFilter = m_SearchFilter; 108 | return true; 109 | } 110 | public virtual void PreprocessTask( ImportContext context, ImportDefinitionProfile profile ) 111 | { 112 | UserDataSerialization data = UserDataSerialization.Get( context.AssetPath ); 113 | string profileGuid = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( profile ) ); 114 | data.UpdateProcessing( new UserDataSerialization.ImportTaskData( profileGuid, ImportTaskName, Version ) ); 115 | } 116 | 117 | public virtual bool Apply( ImportContext context, ImportDefinitionProfile fromProfile ) 118 | { 119 | if( CanProcess( context.Importer ) == false ) 120 | return false; 121 | return true; 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/BaseImportTask.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 752adc69820084964b37390e2952949c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/ImportContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace AssetTools 7 | { 8 | 9 | public class ImportContext 10 | { 11 | public AssetImporter Importer; 12 | 13 | private Dictionary contextData = new Dictionary(); 14 | 15 | public void Add( string key, object data ) 16 | { 17 | if( contextData.ContainsKey( key ) ) 18 | contextData[key] = data; 19 | else 20 | contextData.Add( key, data ); 21 | } 22 | 23 | public object Get( string key ) 24 | { 25 | object rtn; 26 | if( contextData.TryGetValue( key, out rtn ) ) 27 | return rtn; 28 | Debug.LogError( "Could not find context data for " + key ); 29 | 30 | return null; 31 | } 32 | 33 | public string AssetPath 34 | { 35 | get 36 | { 37 | if( Importer == null ) 38 | return null; 39 | return Importer.assetPath; 40 | } 41 | } 42 | 43 | /// 44 | /// GetContextData for the Texture during the OnPostprocessTexture event 45 | /// 46 | public Texture2D PostprocessingTexture 47 | { 48 | get 49 | { 50 | object rtn; 51 | if( contextData.TryGetValue( "Texture2D", out rtn ) ) 52 | return rtn as Texture2D; 53 | return null; 54 | } 55 | set 56 | { 57 | Add( "Texture2D", value ); 58 | } 59 | } 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/ImportContext.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c6d8ec3dae25d4f769759309966eec25 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Importer Properties.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff7a1d5fad3124590a4c1582a6c7e9cf 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Importer Properties/ImporterPropertiesImportTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using AssetTools.GUIUtility; 5 | using UnityEditor; 6 | #if UNITY_2018_1_OR_NEWER 7 | using UnityEditor.Presets; 8 | #endif 9 | using UnityEngine; 10 | 11 | namespace AssetTools 12 | { 13 | public enum AssetType 14 | { 15 | Texture, 16 | Model, 17 | Audio, 18 | Folder, 19 | Native, 20 | NA 21 | } 22 | 23 | [System.Serializable] 24 | public class ImporterPropertiesImportTask : BaseImportTask 25 | { 26 | public UnityEngine.Object m_ImporterReference = null; 27 | 28 | public List m_ConstrainProperties = new List(); 29 | 30 | [NonSerialized] public List m_ConstrainPropertiesDisplayNames = new List(); 31 | [NonSerialized] public List m_SerialisedProperties = new List(); 32 | private int m_HasProperties = 0; 33 | 34 | internal AssetImporter m_AssetImporter; 35 | 36 | private ImporterPropertiesImportTaskInspector m_Inspector = null; 37 | 38 | public override Type GetConformObjectType() 39 | { 40 | return typeof(PropertyConformObject); 41 | } 42 | 43 | public override ProcessingType TaskProcessType 44 | { 45 | get { return ProcessingType.Pre; } 46 | } 47 | 48 | public override void PreprocessTask( ImportContext context, ImportDefinitionProfile profile ) 49 | { 50 | // this task doesn't need to set the metaData 51 | } 52 | 53 | public override string AssetMenuFixString 54 | { 55 | get { return "Conform to Importer Template Properties"; } 56 | } 57 | 58 | private AssetType GetAssetType() 59 | { 60 | if( m_ImporterReference == null ) 61 | return AssetType.NA; 62 | 63 | string path = AssetDatabase.GetAssetPath( m_ImporterReference ); 64 | AssetImporter a = AssetImporter.GetAtPath( path ); 65 | if( AssetDatabase.IsValidFolder( path ) ) 66 | return AssetType.Folder; 67 | if( a is TextureImporter ) 68 | return AssetType.Texture; 69 | if( a is ModelImporter ) 70 | return AssetType.Model; 71 | if( a is AudioImporter ) 72 | return AssetType.Audio; 73 | 74 | return AssetType.Native; 75 | } 76 | 77 | private AssetImporter GetAssetImporter() 78 | { 79 | return AssetImporter.GetAtPath( AssetDatabase.GetAssetPath( m_ImporterReference ) ); 80 | } 81 | 82 | public AssetImporter ReferenceAssetImporter 83 | { 84 | get 85 | { 86 | if( m_AssetImporter == null && m_ImporterReference != null ) 87 | m_AssetImporter = AssetImporter.GetAtPath( AssetDatabase.GetAssetPath( m_ImporterReference ) ); 88 | return m_AssetImporter; 89 | } 90 | } 91 | 92 | internal int PropertyCount 93 | { 94 | get 95 | { 96 | GatherPropertiesIfNeeded(); 97 | return m_ConstrainProperties == null ? 0 : m_ConstrainProperties.Count; 98 | } 99 | } 100 | 101 | public override bool CanProcess( AssetImporter item ) 102 | { 103 | if( m_ImporterReference == null || ReferenceAssetImporter == item ) 104 | return false; 105 | 106 | return item.GetType() == ReferenceAssetImporter.GetType(); 107 | } 108 | 109 | public override int Version 110 | { 111 | get { return 0; } 112 | } 113 | 114 | public override bool GetSearchFilter( out string searchFilter, List ignoreAssetPaths ) 115 | { 116 | searchFilter = null; 117 | if( m_ImporterReference != null ) 118 | { 119 | switch( GetAssetType() ) 120 | { 121 | case AssetType.Texture: 122 | searchFilter = "t:Texture"; 123 | break; 124 | case AssetType.Model: 125 | searchFilter = "t:GameObject"; 126 | break; 127 | case AssetType.Audio: 128 | searchFilter = "t:AudioClip"; 129 | break; 130 | case AssetType.Folder: 131 | break; 132 | case AssetType.Native: 133 | break; 134 | default: 135 | throw new ArgumentOutOfRangeException(); 136 | } 137 | 138 | ignoreAssetPaths.Add( AssetDatabase.GetAssetPath( m_ImporterReference ) ); 139 | return true; 140 | } 141 | 142 | return false; 143 | } 144 | 145 | public override List GetConformObjects( string asset, ImportDefinitionProfile profile ) 146 | { 147 | AssetImporter assetImporter = AssetImporter.GetAtPath( asset ); 148 | if( m_ImporterReference == null ) 149 | return new List(0); 150 | 151 | SerializedObject assetImporterSO = new SerializedObject( assetImporter ); 152 | SerializedObject profileImporterSO = new SerializedObject( ReferenceAssetImporter ); 153 | 154 | // TODO Performance: if there are any. check to make sure these are valid constraints, if not, don't include them in the count 155 | if( m_ConstrainProperties.Count == 0 ) 156 | { 157 | return CompareSerializedObject( profileImporterSO, assetImporterSO ); 158 | } 159 | 160 | List infos = new List(); 161 | 162 | for( int i = 0; i < m_ConstrainProperties.Count; ++i ) 163 | { 164 | SerializedProperty assetRuleSP = profileImporterSO.FindProperty( m_ConstrainProperties[i] ); 165 | if( assetRuleSP == null ) 166 | continue; // could be properties from another Object 167 | SerializedProperty foundAssetSP = assetImporterSO.FindProperty( m_ConstrainProperties[i] ); 168 | 169 | PropertyConformObject conformObject = new PropertyConformObject( m_ConstrainProperties[i] ); 170 | conformObject.SetSerializedProperties( assetRuleSP, foundAssetSP ); 171 | infos.Add( conformObject ); 172 | } 173 | 174 | return infos; 175 | } 176 | 177 | // TODO no longer used, would this be a good public API? 178 | internal void AddProperty( string propName, bool isRealName = true ) 179 | { 180 | GatherPropertiesIfNeeded(); 181 | 182 | string otherName = isRealName ? GetPropertyDisplayName( propName ) : GetPropertyRealName( propName ); 183 | 184 | if( m_ConstrainProperties == null ) 185 | m_ConstrainProperties = new List(); 186 | if( m_ConstrainPropertiesDisplayNames == null ) 187 | m_ConstrainPropertiesDisplayNames = new List(); 188 | 189 | m_ConstrainProperties.Add( isRealName ? propName : otherName ); 190 | m_ConstrainPropertiesDisplayNames.Add( isRealName ? otherName : propName ); 191 | } 192 | 193 | private void RemoveProperty( string p, bool realName = true ) 194 | { 195 | GatherPropertiesIfNeeded(); 196 | 197 | List list = realName ? m_ConstrainProperties : m_ConstrainPropertiesDisplayNames; 198 | for( int i = 0; i < list.Count; ++i ) 199 | { 200 | if( list[i] == p ) 201 | { 202 | RemoveProperty( i ); 203 | return; 204 | } 205 | } 206 | } 207 | 208 | private void RemoveProperty( int i ) 209 | { 210 | GatherPropertiesIfNeeded(); 211 | 212 | m_ConstrainProperties.RemoveAt( i ); 213 | m_ConstrainPropertiesDisplayNames.RemoveAt( i ); 214 | } 215 | 216 | internal string GetPropertyDisplayName( int i ) 217 | { 218 | GatherPropertiesIfNeeded(); 219 | 220 | return m_ConstrainPropertiesDisplayNames[i]; 221 | } 222 | 223 | internal string GetPropertyDisplayName( string realName ) 224 | { 225 | GatherPropertiesIfNeeded(); 226 | 227 | foreach( SerializedProperty property in m_SerialisedProperties ) 228 | { 229 | if( property.name == realName ) 230 | { 231 | return property.displayName; 232 | } 233 | } 234 | 235 | return string.Empty; 236 | } 237 | 238 | internal string GetPropertyRealName( string displayName ) 239 | { 240 | GatherPropertiesIfNeeded(); 241 | 242 | foreach( SerializedProperty property in m_SerialisedProperties ) 243 | { 244 | if( property.displayName == displayName ) 245 | return property.name; 246 | } 247 | 248 | return string.Empty; 249 | } 250 | 251 | internal void GatherPropertiesIfNeeded() 252 | { 253 | if( m_HasProperties == 0 || m_ConstrainProperties.Count != m_ConstrainPropertiesDisplayNames.Count ) 254 | GatherProperties(); 255 | } 256 | 257 | internal void GatherProperties() 258 | { 259 | if( m_ImporterReference == null ) 260 | return; 261 | 262 | SerializedObject so = new SerializedObject( GetAssetImporter() ); 263 | SerializedProperty iter = so.GetIterator(); 264 | 265 | List props = new List(); 266 | 267 | m_SerialisedProperties.Clear(); 268 | iter.NextVisible( true ); 269 | 270 | do 271 | { 272 | props.Add( iter.name ); 273 | } while( iter.NextVisible( false ) ); 274 | 275 | foreach( string s in props ) 276 | { 277 | m_SerialisedProperties.Add( so.FindProperty( s ) ); 278 | } 279 | 280 | GatherDisplayNames(); 281 | 282 | // TODO check if properties exist in active m_Importer 283 | 284 | 285 | m_HasProperties = m_ConstrainProperties.Count > 0 ? 1 : -1; 286 | } 287 | 288 | internal void GatherDisplayNames() 289 | { 290 | m_ConstrainPropertiesDisplayNames.Clear(); 291 | for( int i=0; i= 0 ) 298 | m_ConstrainPropertiesDisplayNames[i] = property.displayName; 299 | } 300 | } 301 | 302 | private static List CompareSerializedObject( SerializedObject template, SerializedObject asset ) 303 | { 304 | SerializedProperty templateIter = template.GetIterator(); 305 | SerializedProperty assetIter = asset.GetIterator(); 306 | assetIter.NextVisible( true ); 307 | templateIter.NextVisible( true ); 308 | 309 | List infos = new List(); 310 | 311 | do 312 | { 313 | if( assetIter.name == "m_UserData" ) 314 | { 315 | templateIter.NextVisible( false ); 316 | continue; 317 | } 318 | 319 | // TODO Performance: Is there a better way to find the properties? 320 | // possibly could use utility method to get all properties in one loop?? (this may not work, NextVisible will not work this way) 321 | PropertyConformObject conformObject = new PropertyConformObject( templateIter.name ); 322 | conformObject.SetSerializedProperties( template.FindProperty( templateIter.name ), asset.FindProperty( assetIter.name ) ); 323 | infos.Add( conformObject ); 324 | 325 | templateIter.NextVisible( false ); 326 | } while( assetIter.NextVisible( false ) ); 327 | 328 | return infos; 329 | } 330 | 331 | public override bool Apply( ImportContext context, ImportDefinitionProfile fromProfile ) 332 | { 333 | if( CanProcess( context.Importer ) == false ) 334 | return false; 335 | 336 | if( m_ConstrainProperties.Count > 0 ) 337 | { 338 | SerializedObject profileSerializedObject = new SerializedObject( ReferenceAssetImporter ); 339 | SerializedObject assetImporterSO = new SerializedObject( context.Importer ); 340 | CopyConstrainedProperties( assetImporterSO, profileSerializedObject ); 341 | } 342 | else 343 | { 344 | EditorUtility.CopySerialized( ReferenceAssetImporter, context.Importer ); 345 | } 346 | 347 | return true; 348 | } 349 | 350 | private void CopyConstrainedProperties( SerializedObject affectedAssetImporterSO, SerializedObject templateImporterSO ) 351 | { 352 | foreach( string property in m_ConstrainProperties ) 353 | { 354 | SerializedProperty templateSerialisedProperty = templateImporterSO.FindProperty( property ); 355 | affectedAssetImporterSO.CopyFromSerializedProperty( templateSerialisedProperty ); 356 | } 357 | 358 | if( ! affectedAssetImporterSO.ApplyModifiedProperties() ) 359 | Debug.LogError( "copy failed" ); 360 | } 361 | 362 | public override void DrawGUI( ControlRect layout ) 363 | { 364 | if( m_Inspector == null ) 365 | m_Inspector = new ImporterPropertiesImportTaskInspector(); 366 | 367 | m_Inspector.Draw( SelfSerializedObject, layout ); 368 | SelfSerializedObject.ApplyModifiedProperties(); 369 | } 370 | 371 | public override int GetHashCode() 372 | { 373 | return (m_ConstrainProperties.GetHashCode() * 31) + 374 | ((m_ImporterReference != null ? m_ImporterReference.GetHashCode() : 0) * 31) + 375 | (ImportTaskName.GetHashCode() * 31); 376 | } 377 | } 378 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Importer Properties/ImporterPropertiesImportTask.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5313fd8897b914b80bd06434514b403c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Importer Properties/ImporterPropertiesImportTaskInspector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using AssetTools.GUIUtility; 4 | using UnityEditor; 5 | using UnityEngine; 6 | using UnityEngine.Assertions; 7 | 8 | namespace AssetTools 9 | { 10 | public class ImporterPropertiesImportTaskInspector 11 | { 12 | private ImporterPropertiesImportTask m_ImportTask; 13 | 14 | private SerializedProperty m_ImporterReferenceSerializedProperty; 15 | private SerializedProperty m_ConstrainPropertiesSerializedProperty; 16 | 17 | public void Draw( SerializedObject importTaskObject, ControlRect layout ) 18 | { 19 | if( m_ImportTask == null ) 20 | { 21 | m_ImportTask = importTaskObject.targetObject as ImporterPropertiesImportTask; 22 | if( m_ImportTask == null ) 23 | { 24 | Debug.LogError( "SerializedObject must be of type ImporterPropertiesImportTask" ); 25 | return; 26 | } 27 | } 28 | 29 | if( m_ImporterReferenceSerializedProperty == null || m_ConstrainPropertiesSerializedProperty == null ) 30 | { 31 | m_ImporterReferenceSerializedProperty = importTaskObject.FindProperty( "m_ImporterReference" ); 32 | m_ConstrainPropertiesSerializedProperty = importTaskObject.FindProperty( "m_ConstrainProperties" ); 33 | if( m_ImporterReferenceSerializedProperty == null || m_ConstrainPropertiesSerializedProperty == null ) 34 | { 35 | Debug.LogError( "Invalid properties for ImporterPropertiesImportTask" ); 36 | return; 37 | } 38 | } 39 | 40 | if( m_ImporterReferenceSerializedProperty != null ) 41 | ReferenceObjectGUI( layout ); 42 | 43 | if( m_ImporterReferenceSerializedProperty.objectReferenceValue != null ) 44 | { 45 | ConstrainToPropertiesGUI( layout ); 46 | } 47 | else 48 | { 49 | Rect r = layout.Get( 25 ); 50 | EditorGUI.HelpBox( r, "No template Object to constrain properties on.", MessageType.Warning ); 51 | } 52 | } 53 | 54 | private void ReferenceObjectGUI( ControlRect layout ) 55 | { 56 | using( var check = new EditorGUI.ChangeCheckScope() ) 57 | { 58 | EditorGUI.PropertyField( layout.Get(), m_ImporterReferenceSerializedProperty, new GUIContent( "Importer Template" ), false ); 59 | if( check.changed ) 60 | { 61 | m_ImportTask.m_AssetImporter = null; 62 | m_ImportTask.GatherProperties(); 63 | } 64 | } 65 | } 66 | 67 | private void ConstrainToPropertiesGUI( ControlRect layout ) 68 | { 69 | layout.Space( 10 ); 70 | EditorGUI.LabelField( layout.Get(), "Constrain to Properties:" ); 71 | 72 | Rect boxAreaRect = layout.Get( Mathf.Max( (m_ImportTask.PropertyCount * layout.layoutHeight) + 6, 20 ) ); 73 | GUI.Box( boxAreaRect, GUIContent.none ); 74 | 75 | ControlRect subLayout = new ControlRect( boxAreaRect.x + 3, boxAreaRect.y + 3, boxAreaRect.width - 6, layout.layoutHeight ) 76 | { 77 | padding = 0 78 | }; 79 | 80 | // list all of the displayNames 81 | for( int i = 0; i < m_ImportTask.PropertyCount; ++i ) 82 | { 83 | EditorGUI.LabelField( subLayout.Get(), m_ImportTask.GetPropertyDisplayName( i ) ); 84 | } 85 | 86 | Rect layoutRect = layout.Get(); 87 | layoutRect.x = layoutRect.x + (layoutRect.width - 100); 88 | layoutRect.width = 100; 89 | if( GUI.Button( layoutRect, "Edit Selection" ) ) 90 | { 91 | string[] propertyNamesForReference = GetPropertyNames( new SerializedObject( m_ImportTask.ReferenceAssetImporter ) ); 92 | 93 | m_ImportTask.GatherPropertiesIfNeeded(); 94 | GenericMenu menu = new GenericMenu(); 95 | foreach( string propertyName in propertyNamesForReference ) 96 | { 97 | // we do not want UserData to be included. We are required to use this in order to save information about 98 | // how the Asset is imported, to generate a different hash for the cache server 99 | if( propertyName.Contains( "m_UserData" ) ) 100 | continue; 101 | 102 | bool isPropertySelected = m_ImportTask.m_ConstrainProperties.Contains( propertyName ); 103 | string propertyDisplayName = m_ImportTask.GetPropertyDisplayName( propertyName ); 104 | menu.AddItem( new GUIContent( propertyDisplayName ), isPropertySelected, TogglePropertyConstraintSelected, propertyDisplayName ); 105 | } 106 | 107 | menu.ShowAsContext(); 108 | } 109 | } 110 | 111 | private void TogglePropertyConstraintSelected( object selectedObject ) 112 | { 113 | string propertyName = selectedObject as string; 114 | Assert.IsNotNull( propertyName ); 115 | 116 | for( int i = 0; i < m_ImportTask.m_ConstrainPropertiesDisplayNames.Count; ++i ) 117 | { 118 | if( m_ImportTask.m_ConstrainPropertiesDisplayNames[i].Equals( propertyName ) ) 119 | { 120 | m_ConstrainPropertiesSerializedProperty.DeleteArrayElementAtIndex( i ); 121 | return; 122 | } 123 | } 124 | 125 | m_ConstrainPropertiesSerializedProperty.arraySize += 1; 126 | m_ConstrainPropertiesSerializedProperty.GetArrayElementAtIndex( m_ConstrainPropertiesSerializedProperty.arraySize-1 ).stringValue = m_ImportTask.GetPropertyRealName( propertyName ) ; 127 | m_ImportTask.GatherDisplayNames(); 128 | } 129 | 130 | private static string[] GetPropertyNames( SerializedObject serializedObject ) 131 | { 132 | SerializedProperty soIter = serializedObject.GetIterator(); 133 | 134 | List propNames = new List(); 135 | 136 | soIter.NextVisible(true); 137 | do 138 | { 139 | propNames.Add(soIter.name); 140 | } while (soIter.NextVisible(false)); 141 | 142 | return propNames.ToArray(); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Importer Properties/ImporterPropertiesImportTaskInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9ca6d2d6d26ec43cf85949251fb76c57 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Importer Properties/PropertyConformObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using UnityEngine; 5 | using UnityEditor; 6 | 7 | namespace AssetTools 8 | { 9 | 10 | public class PropertyConformObject : IConformObject 11 | { 12 | public bool Conforms 13 | { 14 | get 15 | { 16 | if( m_AssetSerializedProperty.propertyType != SerializedPropertyType.Generic ) 17 | return m_Conforms; 18 | 19 | for( int i = 0; i < m_SubObjects.Count; ++i ) 20 | { 21 | if( !m_SubObjects[i].Conforms ) 22 | return false; 23 | } 24 | return m_Conforms; 25 | } 26 | set { m_Conforms = value; } 27 | } 28 | 29 | public List SubObjects 30 | { 31 | get { return m_SubObjects; } 32 | set { m_SubObjects = value; } 33 | } 34 | 35 | public string Name 36 | { 37 | get { return m_PropertyName; } 38 | set { m_PropertyName = value; } 39 | } 40 | 41 | private bool m_Conforms = true; 42 | private List m_SubObjects = new List(); 43 | private string m_PropertyName; 44 | 45 | private SerializedProperty m_TemplateSerializedProperty; 46 | private SerializedProperty m_AssetSerializedProperty; 47 | 48 | public PropertyConformObject( string name ) 49 | { 50 | m_PropertyName = name; 51 | } 52 | 53 | private PropertyConformObject( string name, SerializedProperty template, SerializedProperty asset ) 54 | { 55 | m_PropertyName = name; 56 | SetSerializedProperties( template, asset ); 57 | } 58 | 59 | public void SetSerializedProperties( SerializedProperty template, SerializedProperty asset ) 60 | { 61 | m_TemplateSerializedProperty = template; 62 | m_AssetSerializedProperty = asset; 63 | Conforms = CompareSerializedProperty( asset, template ); 64 | } 65 | 66 | public SerializedProperty TemplateSerializedProperty 67 | { 68 | get { return m_TemplateSerializedProperty; } 69 | } 70 | 71 | // TODO cache these values?? 72 | public string TemplateValue 73 | { 74 | get { return GetValue( m_TemplateSerializedProperty ); } 75 | } 76 | 77 | public string AssetValue 78 | { 79 | get { return GetValue( m_AssetSerializedProperty ); } 80 | } 81 | 82 | public SerializedPropertyType TemplateType 83 | { 84 | get { return m_TemplateSerializedProperty.propertyType; } 85 | } 86 | 87 | public SerializedProperty AssetSerializedProperty 88 | { 89 | get { return m_AssetSerializedProperty; } 90 | } 91 | 92 | public string ActualValue 93 | { 94 | get 95 | { 96 | if( AssetSerializedProperty.propertyType != SerializedPropertyType.Generic ) 97 | return AssetValue; 98 | return ""; 99 | } 100 | } 101 | 102 | public string ExpectedValue 103 | { 104 | get 105 | { 106 | if( AssetSerializedProperty.propertyType != SerializedPropertyType.Generic ) 107 | return TemplateValue; 108 | return ""; 109 | } 110 | } 111 | 112 | public bool Apply( SerializedObject toObject ) 113 | { 114 | toObject.CopyFromSerializedProperty( TemplateSerializedProperty ); 115 | if( !toObject.ApplyModifiedProperties() ) 116 | { 117 | Debug.LogError( "Copying of SerialisedProperty failed for - " + toObject.targetObject.name ); 118 | return false; 119 | } 120 | 121 | return true; 122 | } 123 | 124 | public void AddTreeViewItems( int parentId, ConformObjectTreeViewItem parent, AssetsTreeViewItem assetsTreeItem, int depth, int arrayIndex = -1 ) 125 | { 126 | string extra = arrayIndex >= 0 ? arrayIndex.ToString() : ""; 127 | int hashCodeForID = parentId + (Name + extra).GetHashCode() * 31; 128 | ConformObjectTreeViewItem conformObjectTree = new ConformObjectTreeViewItem( hashCodeForID, depth, this ) 129 | { 130 | AssetsTreeViewItem = assetsTreeItem 131 | }; 132 | parent.AddChild( conformObjectTree ); 133 | 134 | for( int i=0; i SubObjects 31 | { 32 | get { return m_SubObjects; } 33 | set { m_SubObjects = value; } 34 | } 35 | 36 | public string Name 37 | { 38 | get { return m_MethodName; } 39 | set { m_MethodName = value; } 40 | } 41 | 42 | private List m_SubObjects = new List(); 43 | 44 | private string m_MethodName; 45 | private readonly int m_MethodVersion; 46 | private int m_ImportedVersion = Int32.MinValue; 47 | 48 | public string ActualValue 49 | { 50 | get 51 | { 52 | if( m_ImportedVersion == Int32.MinValue ) 53 | return "None"; 54 | return m_ImportedVersion.ToString(); 55 | } 56 | } 57 | 58 | public string ExpectedValue 59 | { 60 | get { return m_MethodVersion.ToString(); } 61 | } 62 | 63 | public bool Apply( SerializedObject toObject ) 64 | { 65 | return false; 66 | } 67 | 68 | public void AddTreeViewItems( int parentId, ConformObjectTreeViewItem parent, AssetsTreeViewItem assetsTreeItem, int depth, int arrayIndex = -1 ) 69 | { 70 | int activePath = parentId + (Name.GetHashCode()*31); 71 | ConformObjectTreeViewItem conformObjectTree = new ConformObjectTreeViewItem( activePath, depth, this ) 72 | { 73 | AssetsTreeViewItem = assetsTreeItem 74 | }; 75 | parent.AddChild( conformObjectTree ); 76 | 77 | for( int i=0; i m_Methods; 14 | 15 | public static List Methods 16 | { 17 | get 18 | { 19 | if( m_Methods == null ) 20 | { 21 | m_Methods = new List(); 22 | FindMethods(); 23 | } 24 | return m_Methods; 25 | } 26 | } 27 | 28 | /// 29 | /// Brute force get all the methods that implement IPostprocessor 30 | /// May not want this as it limits the ability to move over to MonoScript dependency in AssetDatabaseV2 31 | /// 32 | private static void FindMethods() 33 | { 34 | Type p = typeof(IPostprocessor); 35 | 36 | m_Methods.Clear(); 37 | Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); 38 | for( int i=0; i( importedAssets[i] ); 73 | // if( s == null ) 74 | // continue; 75 | // Type t = s.GetClass(); 76 | // if( t.IsInterface ) 77 | // continue; 78 | // if( !typeof(IPostprocessor).IsAssignableFrom( t ) ) 79 | // continue; 80 | // 81 | // 82 | // } 83 | // 84 | // for( int i = 0; i < deletedAssets.Length; ++i ) 85 | // { 86 | // } 87 | // } 88 | } 89 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Postprocessor/PostprocessorImplementorCache.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 570b78265f4204e01937baf4cb1bc639 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Postprocessor/PostprocessorImportTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using AssetTools.GUIUtility; 4 | using UnityEditor; 5 | using UnityEngine; 6 | 7 | namespace AssetTools 8 | { 9 | [Serializable] 10 | public class PostprocessorImportTask : BaseImportTask 11 | { 12 | /// 13 | /// TODO If any of this is changed, do the Assets imported by it need to be reimported? 14 | /// 15 | 16 | [SerializeField] private string m_MethodString = ""; 17 | [SerializeField] private string m_Data = ""; 18 | 19 | private PostprocessorImportTaskInspector m_Inspector = null; 20 | internal ProcessorMethodInfo m_ProcessorMethodInfo; 21 | 22 | public string methodString 23 | { 24 | get { return m_MethodString; } 25 | } 26 | 27 | public override Type GetConformObjectType() 28 | { 29 | return typeof(PostprocessorConformObject); 30 | } 31 | 32 | public override ProcessingType TaskProcessType 33 | { 34 | get { return ProcessingType.Post; } 35 | } 36 | 37 | public override string ImportTaskName 38 | { 39 | get { return "PostprocessorImportTask"; } 40 | } 41 | 42 | 43 | public override int Version 44 | { 45 | get { return Method == null ? 0 : Method.Version; } 46 | } 47 | 48 | public override int MaximumCount 49 | { 50 | get { return -1; } 51 | } 52 | 53 | public override string AssetMenuFixString 54 | { 55 | get 56 | { 57 | return Method == null ? "None Selected" : "Import using " + Method.TypeName; 58 | } 59 | } 60 | 61 | public override bool CanProcess( AssetImporter item ) 62 | { 63 | return true; 64 | } 65 | 66 | public override List GetConformObjects( string asset, ImportDefinitionProfile profile ) 67 | { 68 | // postcessor versionCode comparison 69 | // will need someway to store this. It could not work well if imported not using it 70 | // 1: add it to meta data. Only option is userData, which could conflict with other code packages. This would make it included in the hash for cache server. Which would be required. 71 | // 2: store a databaseof imported version data. Could be tricky to keep in sync 72 | // 3: AssetDatabaseV2 supports asset dependencies 73 | 74 | List infos = new List(); 75 | 76 | if( Method == null ) 77 | { 78 | PostprocessorConformObject conformObject = new PostprocessorConformObject( "None Selected", 0 ); 79 | infos.Add( conformObject ); 80 | return infos; 81 | } 82 | 83 | UserDataSerialization userData = UserDataSerialization.Get( asset ); 84 | List data = userData.GetProcessedMethodsData(); 85 | string profileGuid = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( profile ) ); 86 | 87 | if( data != null ) 88 | { 89 | for( int i = 0; i < data.Count; ++i ) 90 | { 91 | if( data[i].moduleName != ImportTaskName || 92 | data[i].typeName != Method.TypeName || 93 | data[i].assemblyName != Method.AssemblyName || 94 | data[i].importDefinitionGUID != profileGuid ) 95 | continue; 96 | 97 | infos.Add( new PostprocessorConformObject( Method.TypeName, Method.Version, data[i].version ) ); 98 | break; 99 | } 100 | } 101 | 102 | if( infos.Count == 0 ) 103 | infos.Add( new PostprocessorConformObject( Method.TypeName, Method.Version ) ); 104 | 105 | return infos; 106 | } 107 | 108 | public override void PreprocessTask( ImportContext context, ImportDefinitionProfile profile ) 109 | { 110 | UserDataSerialization data = UserDataSerialization.Get( context.AssetPath ); 111 | string profileGuid = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( profile ) ); 112 | data.UpdateProcessing( new UserDataSerialization.PostprocessorData( profileGuid, ImportTaskName, Method.AssemblyName, Method.TypeName, Method.Version ) ); 113 | } 114 | 115 | public override bool Apply( ImportContext context, ImportDefinitionProfile fromProfile ) 116 | { 117 | if( string.IsNullOrEmpty( m_MethodString ) == false ) 118 | { 119 | if( Method != null ) 120 | { 121 | object returnValue = Method.Invoke( context, m_Data ); 122 | if( returnValue != null ) 123 | return (bool) returnValue; 124 | } 125 | } 126 | return false; 127 | } 128 | 129 | private ProcessorMethodInfo Method 130 | { 131 | get 132 | { 133 | if( m_ProcessorMethodInfo == null && string.IsNullOrEmpty( m_MethodString ) == false ) 134 | { 135 | string assemblyName; 136 | string typeString; 137 | GetMethodStrings( out assemblyName, out typeString ); 138 | if( string.IsNullOrEmpty( typeString ) ) 139 | { 140 | Debug.LogError( "Error collecting method from " + m_MethodString ); 141 | return null; 142 | } 143 | 144 | List methods = PostprocessorImplementorCache.Methods; 145 | for( int i = 0; i < methods.Count; ++i ) 146 | { 147 | if( assemblyName != null && methods[i].AssemblyName.StartsWith( assemblyName ) == false ) 148 | continue; 149 | 150 | if( methods[i].TypeName.EndsWith( typeString ) ) 151 | { 152 | // make sure its correct? 153 | m_ProcessorMethodInfo = methods[i]; 154 | break; 155 | } 156 | } 157 | } 158 | 159 | return m_ProcessorMethodInfo; 160 | } 161 | } 162 | 163 | private void GetMethodStrings( out string assemblyName, out string typeString ) 164 | { 165 | int commaIndex = m_MethodString.IndexOf( ',' ); 166 | if( commaIndex > 0 ) 167 | { 168 | assemblyName = m_MethodString.Substring( commaIndex + 2 ); 169 | typeString = m_MethodString.Substring( 0, commaIndex ); 170 | } 171 | else 172 | { 173 | assemblyName = ""; 174 | typeString = m_MethodString; 175 | } 176 | } 177 | 178 | public override void DrawGUI( ControlRect layout ) 179 | { 180 | if( m_Inspector == null ) 181 | m_Inspector = new PostprocessorImportTaskInspector(); 182 | 183 | m_Inspector.Draw( SelfSerializedObject, layout ); 184 | SelfSerializedObject.ApplyModifiedProperties(); 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Postprocessor/PostprocessorImportTask.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c8cc2cda92f5145939433d9f202bd4d1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Postprocessor/PostprocessorImportTaskInspector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AssetTools.GUIUtility; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace AssetTools 7 | { 8 | public class PostprocessorImportTaskInspector 9 | { 10 | private PostprocessorImportTask m_ImportTask; 11 | 12 | private SerializedProperty m_MethodSerializedProperty; 13 | private SerializedProperty m_DataSerializedProperty; 14 | 15 | public void Draw( SerializedObject moduleObject, ControlRect layout ) 16 | { 17 | if( m_ImportTask == null ) 18 | { 19 | m_ImportTask = moduleObject.targetObject as PostprocessorImportTask; 20 | if( m_ImportTask == null ) 21 | { 22 | Debug.LogError( "SerializedObject must be of type PostprocessorImportTask" ); 23 | return; 24 | } 25 | } 26 | 27 | if( m_MethodSerializedProperty == null || m_DataSerializedProperty == null ) 28 | { 29 | List propertyNames = new List {"m_MethodString", "m_Data"}; 30 | List properties = SerializationUtilities.GetSerialisedPropertyCopiesForObject( moduleObject, propertyNames ); 31 | m_MethodSerializedProperty = properties[0]; 32 | m_DataSerializedProperty = properties[1]; 33 | if( m_MethodSerializedProperty == null || m_DataSerializedProperty == null ) 34 | { 35 | Debug.LogError( "Invalid properties for PostprocessorImportTask" ); 36 | return; 37 | } 38 | } 39 | 40 | List methods = PostprocessorImplementorCache.Methods; 41 | GUIContent[] contents = new GUIContent[methods.Count+1]; 42 | contents[0] = new GUIContent("None Selected"); 43 | 44 | int selectedMethod = 0; 45 | for( int i=1; i= 0 ) 70 | { 71 | m_MethodSerializedProperty.stringValue = methods[id].TypeName + ", " + methods[id].AssemblyName; 72 | m_ImportTask.m_ProcessorMethodInfo = null; 73 | } 74 | } 75 | } 76 | 77 | EditorGUI.PropertyField( layout.Get(), m_DataSerializedProperty ); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Postprocessor/PostprocessorImportTaskInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f02bd28b8b33a4624ad9414e9ee6c646 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 98f20f840880b40ac8a51736bf189e79 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor/IPreprocessor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace AssetTools 7 | { 8 | 9 | public interface IPreprocessor 10 | { 11 | bool OnPreprocessAsset( ImportContext context, string data ); 12 | int GetVersion(); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor/IPreprocessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ef45db9b651684271930bb2bcd4bf051 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor/PreprocessorConformObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using UnityEngine; 5 | using UnityEditor; 6 | 7 | namespace AssetTools 8 | { 9 | public class PreprocessorConformObject : IConformObject 10 | { 11 | public bool Conforms 12 | { 13 | get 14 | { 15 | for( int i = 0; i < m_SubObjects.Count; ++i ) 16 | { 17 | if( !m_SubObjects[i].Conforms ) 18 | return false; 19 | } 20 | 21 | return m_ImportedVersion == m_MethodVersion; 22 | } 23 | set 24 | { 25 | if( value ) 26 | m_ImportedVersion = m_MethodVersion; 27 | } 28 | } 29 | 30 | public List SubObjects 31 | { 32 | get { return m_SubObjects; } 33 | set { m_SubObjects = value; } 34 | } 35 | 36 | public string Name 37 | { 38 | get { return m_MethodName; } 39 | set { m_MethodName = value; } 40 | } 41 | 42 | private List m_SubObjects = new List(); 43 | 44 | private string m_MethodName; 45 | private readonly int m_MethodVersion; 46 | private int m_ImportedVersion = Int32.MinValue; 47 | 48 | public string ActualValue 49 | { 50 | get 51 | { 52 | if( m_ImportedVersion == Int32.MinValue ) 53 | return "None"; 54 | return m_ImportedVersion.ToString(); 55 | } 56 | } 57 | 58 | public string ExpectedValue 59 | { 60 | get { return m_MethodVersion.ToString(); } 61 | } 62 | 63 | public bool Apply( SerializedObject toObject ) 64 | { 65 | return false; 66 | } 67 | 68 | public void AddTreeViewItems( int parentId, ConformObjectTreeViewItem parent, AssetsTreeViewItem assetsTreeItem, int depth, int arrayIndex = -1 ) 69 | { 70 | int activePath = parentId + (Name.GetHashCode()*31); 71 | ConformObjectTreeViewItem conformObjectTree = new ConformObjectTreeViewItem( activePath, depth, this ) 72 | { 73 | AssetsTreeViewItem = assetsTreeItem 74 | }; 75 | parent.AddChild( conformObjectTree ); 76 | 77 | for( int i=0; i m_Methods; 68 | 69 | public static List Methods 70 | { 71 | get 72 | { 73 | if( m_Methods == null ) 74 | { 75 | m_Methods = new List(); 76 | FindMethods(); 77 | } 78 | return m_Methods; 79 | } 80 | } 81 | 82 | /// 83 | /// Brute force get all the methods that implement IPreprocessor 84 | /// May not want this as it limits the ability to move over to MonoScript dependency in AssetDatabaseV2 85 | /// 86 | private static void FindMethods() 87 | { 88 | Type p = typeof(IPreprocessor); 89 | 90 | m_Methods.Clear(); 91 | Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); 92 | for( int i=0; i( importedAssets[i] ); 127 | // if( s == null ) 128 | // continue; 129 | // Type t = s.GetClass(); 130 | // if( t.IsInterface ) 131 | // continue; 132 | // if( !typeof(IPreprocessor).IsAssignableFrom( t ) ) 133 | // continue; 134 | // 135 | // 136 | // } 137 | // 138 | // for( int i = 0; i < deletedAssets.Length; ++i ) 139 | // { 140 | // } 141 | // } 142 | } 143 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor/PreprocessorImplementorCache.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 92d5f0960acd448a7bf72ce2cc96dde7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor/PreprocessorImportTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using AssetTools.GUIUtility; 4 | using UnityEditor; 5 | using UnityEngine; 6 | 7 | namespace AssetTools 8 | { 9 | [Serializable] 10 | public class PreprocessorImportTask : BaseImportTask 11 | { 12 | /// 13 | /// TODO If any of this is changed, do the Assets imported by it need to be reimported? 14 | /// 15 | 16 | [SerializeField] private string m_MethodString = ""; 17 | [SerializeField] private string m_Data = ""; 18 | 19 | private PreprocessorImportTaskInspector m_Inspector = null; 20 | internal ProcessorMethodInfo m_ProcessorMethodInfo; 21 | 22 | public string methodString 23 | { 24 | get { return m_MethodString; } 25 | } 26 | 27 | public override Type GetConformObjectType() 28 | { 29 | return typeof(PreprocessorConformObject); 30 | } 31 | 32 | public override ProcessingType TaskProcessType 33 | { 34 | get { return ProcessingType.Pre; } 35 | } 36 | 37 | public override string ImportTaskName 38 | { 39 | get { return "PreprocessorImportTask"; } 40 | } 41 | 42 | public override int Version 43 | { 44 | get { return Method == null ? 0 : Method.Version; } 45 | } 46 | 47 | public override int MaximumCount 48 | { 49 | get { return -1; } 50 | } 51 | 52 | public override string AssetMenuFixString 53 | { 54 | get 55 | { 56 | return Method == null ? "None Selected" : "Import using " + Method.TypeName; 57 | } 58 | } 59 | 60 | public override bool CanProcess( AssetImporter item ) 61 | { 62 | return true; 63 | } 64 | 65 | public override List GetConformObjects( string asset, ImportDefinitionProfile profile ) 66 | { 67 | // Preprocessor versionCode comparison 68 | // will need someway to store this. It could not work well if imported not using it 69 | // 1: add it to meta data. Only option is userData, which could conflict with other code packages. This would make it included in the hash for cache server. Which would be required. 70 | // 2: store a database of imported version data. Could be tricky to keep in sync 71 | // 3: AssetDatabaseV2 supports asset dependencies 72 | 73 | List infos = new List(); 74 | 75 | if( Method == null ) 76 | { 77 | PreprocessorConformObject conformObject = new PreprocessorConformObject( "None Selected", 0 ); 78 | infos.Add( conformObject ); 79 | return infos; 80 | } 81 | 82 | UserDataSerialization userData = UserDataSerialization.Get( asset ); 83 | List data = userData.GetProcessedMethodsData(); 84 | string profileGuid = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( profile ) ); 85 | 86 | if( data != null ) 87 | { 88 | for( int i = 0; i < data.Count; ++i ) 89 | { 90 | if( data[i].moduleName != ImportTaskName || 91 | data[i].typeName != Method.TypeName || 92 | data[i].assemblyName != Method.AssemblyName || 93 | data[i].importDefinitionGUID != profileGuid ) 94 | continue; 95 | 96 | PreprocessorConformObject conformObject = new PreprocessorConformObject( Method.TypeName, Method.Version, data[i].version ); 97 | infos.Add( conformObject ); 98 | break; 99 | } 100 | } 101 | else 102 | { 103 | PreprocessorConformObject conformObject = new PreprocessorConformObject( Method.TypeName, Method.Version ); 104 | infos.Add( conformObject ); 105 | } 106 | return infos; 107 | } 108 | 109 | public override void PreprocessTask( ImportContext context, ImportDefinitionProfile profile ) 110 | { 111 | UserDataSerialization data = UserDataSerialization.Get( context.AssetPath ); 112 | string profileGuid = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( profile ) ); 113 | data.UpdateProcessing( new UserDataSerialization.PostprocessorData( profileGuid, ImportTaskName, Method.AssemblyName, Method.TypeName, Method.Version ) ); 114 | } 115 | 116 | public override bool Apply( ImportContext context, ImportDefinitionProfile fromProfile ) 117 | { 118 | if( string.IsNullOrEmpty( m_MethodString ) == false ) 119 | { 120 | if( Method != null ) 121 | { 122 | object returnValue = Method.Invoke( context, m_Data ); 123 | if( returnValue != null ) 124 | return (bool) returnValue; 125 | } 126 | } 127 | return false; 128 | } 129 | 130 | private ProcessorMethodInfo Method 131 | { 132 | get 133 | { 134 | if( m_ProcessorMethodInfo == null && string.IsNullOrEmpty( m_MethodString ) == false ) 135 | { 136 | string assemblyName; 137 | string typeString; 138 | GetMethodStrings( out assemblyName, out typeString ); 139 | if( string.IsNullOrEmpty( typeString ) ) 140 | { 141 | Debug.LogError( "Error collecting method from " + m_MethodString ); 142 | return null; 143 | } 144 | 145 | List methods = PreprocessorImplementorCache.Methods; 146 | for( int i = 0; i < methods.Count; ++i ) 147 | { 148 | if( assemblyName != null && methods[i].AssemblyName.StartsWith( assemblyName ) == false ) 149 | continue; 150 | 151 | if( methods[i].TypeName.EndsWith( typeString ) ) 152 | { 153 | m_ProcessorMethodInfo = methods[i]; 154 | break; 155 | } 156 | } 157 | } 158 | 159 | return m_ProcessorMethodInfo; 160 | } 161 | } 162 | 163 | private void GetMethodStrings( out string assemblyName, out string typeString ) 164 | { 165 | int commaIndex = m_MethodString.IndexOf( ',' ); 166 | if( commaIndex > 0 ) 167 | { 168 | assemblyName = m_MethodString.Substring( commaIndex + 2 ); 169 | typeString = m_MethodString.Substring( 0, commaIndex ); 170 | } 171 | else 172 | { 173 | assemblyName = ""; 174 | typeString = m_MethodString; 175 | } 176 | } 177 | 178 | public override void DrawGUI( ControlRect layout ) 179 | { 180 | if( m_Inspector == null ) 181 | m_Inspector = new PreprocessorImportTaskInspector(); 182 | 183 | m_Inspector.Draw( SelfSerializedObject, layout ); 184 | SelfSerializedObject.ApplyModifiedProperties(); 185 | } 186 | 187 | public override int GetHashCode() 188 | { 189 | return (m_MethodString.GetHashCode() * 31) + (m_Data.GetHashCode() * 31) + (ImportTaskName.GetHashCode() * 31); 190 | } 191 | } 192 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor/PreprocessorImportTask.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b547c4b62368f42778107dc9e16faf9f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor/PreprocessorImportTaskInspector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AssetTools.GUIUtility; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace AssetTools 7 | { 8 | public class PreprocessorImportTaskInspector 9 | { 10 | private PreprocessorImportTask m_ImportTask; 11 | 12 | private SerializedProperty m_MethodSerializedProperty; 13 | private SerializedProperty m_DataSerializedProperty; 14 | 15 | public void Draw( SerializedObject moduleObject, ControlRect layout ) 16 | { 17 | if( m_ImportTask == null ) 18 | { 19 | m_ImportTask = moduleObject.targetObject as PreprocessorImportTask; 20 | if( m_ImportTask == null ) 21 | { 22 | Debug.LogError( "SerializedObject must be of type PreprocessorImportTask" ); 23 | return; 24 | } 25 | } 26 | 27 | if( m_MethodSerializedProperty == null || m_DataSerializedProperty == null ) 28 | { 29 | List propertyNames = new List {"m_MethodString", "m_Data"}; 30 | List properties = SerializationUtilities.GetSerialisedPropertyCopiesForObject( moduleObject, propertyNames ); 31 | m_MethodSerializedProperty = properties[0]; 32 | m_DataSerializedProperty = properties[1]; 33 | if( m_MethodSerializedProperty == null || m_DataSerializedProperty == null ) 34 | { 35 | Debug.LogError( "Invalid properties for PreprocessorImportTask" ); 36 | return; 37 | } 38 | } 39 | 40 | List methods = PreprocessorImplementorCache.Methods; 41 | GUIContent[] contents = new GUIContent[methods.Count+1]; 42 | contents[0] = new GUIContent("None Selected"); 43 | 44 | int selectedMethod = 0; 45 | for( int i=1; i= 0 ) 70 | { 71 | m_MethodSerializedProperty.stringValue = methods[id].TypeName + ", " + methods[id].AssemblyName; 72 | m_ImportTask.m_ProcessorMethodInfo = null; 73 | } 74 | } 75 | } 76 | 77 | EditorGUI.PropertyField( layout.Get(), m_DataSerializedProperty ); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /Asset Processors/Import Tasks/Preprocessor/PreprocessorImportTaskInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: efc9a079b56f44e35a3d905cabcd5ad0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/ImportDefinitionProfileAssetPostprocessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Runtime.CompilerServices; 5 | using UnityEditor; 6 | using UnityEngine; 7 | 8 | namespace AssetTools 9 | { 10 | public class ImportDefinitionProfileAssetPostprocessor : AssetPostprocessor 11 | { 12 | private static Dictionary m_AssetProcessingContext = new Dictionary(); 13 | 14 | 15 | 16 | private static ImportContext GetContext( string path ) 17 | { 18 | ImportContext ctx; 19 | if( !m_AssetProcessingContext.TryGetValue( path, out ctx ) ) 20 | { 21 | ctx = new ImportContext(); 22 | ctx.Importer = AssetImporter.GetAtPath( path ); 23 | m_AssetProcessingContext.Add( path, ctx ); 24 | } 25 | 26 | return ctx; 27 | } 28 | 29 | private ImportContext GetContext( ) 30 | { 31 | ImportContext ctx; 32 | if( !m_AssetProcessingContext.TryGetValue( assetPath, out ctx ) ) 33 | { 34 | ctx = new ImportContext(); 35 | ctx.Importer = assetImporter; 36 | m_AssetProcessingContext.Add( assetPath, ctx ); 37 | } 38 | 39 | return ctx; 40 | } 41 | 42 | private static ImportContext SetContext( string path, Texture2D texture ) 43 | { 44 | ImportContext ctx = GetContext( path ); 45 | ctx.PostprocessingTexture = texture; 46 | return ctx; 47 | } 48 | 49 | private ImportContext SetContext( Texture2D texture ) 50 | { 51 | ImportContext ctx = GetContext(); 52 | ctx.PostprocessingTexture = texture; 53 | return ctx; 54 | } 55 | 56 | 57 | private static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths ) 58 | { 59 | // Debug.Log( "OnPostprocessAllAssets" ); 60 | 61 | 62 | // TODO process any changes needed by Profiles being chnaged 63 | // for any Profiles (that auto import is set set), imported / deleted / moved 64 | 65 | // get any Assets that may have been affected. 66 | 67 | // get the userData for the Asset 68 | 69 | 70 | // calculate what it should be 71 | 72 | // if it is different, then the asset needs to be changed to what it should be, and reimported. 73 | 74 | 75 | 76 | 77 | // List defs = ImportDefinitionProfileCache.Profiles; 78 | // foreach( string asset in importedAssets ) 79 | // { 80 | // // TODO anything that does not have a specialised Processing method process them 81 | // } 82 | 83 | 84 | 85 | 86 | 87 | m_AssetProcessingContext.Clear(); 88 | } 89 | 90 | private void OnPreprocessAsset() 91 | { 92 | ImportContext context = GetContext( ); 93 | // TODO optimise this 94 | List defs = ImportDefinitionProfileCache.Profiles; 95 | // Any profiles can interact with the Asset, so we need to check all 96 | for( int i = 0; i < defs.Count; ++i ) 97 | { 98 | defs[i].m_ImportDefinitionProfile.PreprocessAsset( context ); 99 | } 100 | } 101 | 102 | /// 103 | /// To properly post process Assets, we need to use the methods individually 104 | /// 105 | 106 | private void OnPostprocessTexture( Texture2D texture ) 107 | { 108 | ImportContext context = SetContext( texture ); 109 | 110 | // TODO optimise this 111 | List defs = ImportDefinitionProfileCache.Profiles; 112 | // Any profiles can interact with the Asset, so we need to check all 113 | for( int i = 0; i < defs.Count; ++i ) 114 | { 115 | defs[i].m_ImportDefinitionProfile.PostprocessAsset( context ); 116 | } 117 | } 118 | 119 | #if ! UNITY_2018_1_OR_NEWER 120 | private void OnPreprocessAudio() 121 | { 122 | OnPreprocessAsset(); 123 | } 124 | 125 | private void OnPreprocessModel() 126 | { 127 | OnPreprocessAsset(); 128 | } 129 | 130 | private void OnPreprocessTexture() 131 | { 132 | OnPreprocessAsset(); 133 | } 134 | #endif 135 | } 136 | 137 | } -------------------------------------------------------------------------------- /Asset Processors/ImportDefinitionProfileAssetPostprocessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8654e54c95b414173aedf473b71604e8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: '"ImportDefinitionFiles": {"ImportDefinitionFiles": { {"assetProcessedWith":[{"importDefintionPath":"Assets/asset-auditing-tools/Auditor/MetaData/AssetPostprocessorUserDataSerialisation.cs0","moduleName":"PreprocessorModule","assemblyName":"blah 10 | blah vhfs.dshh v4592","methodName":"methodName","version":2},{"importDefintionPath":"Assets/asset-auditing-tools/Auditor/MetaData/AssetPostprocessorUserDataSerialisation.cs1","moduleName":"PreprocessorModule","assemblyName":"blah 11 | blah vhfs.dshh v4592","methodName":"methodName","version":2},{"importDefintionPath":"Assets/asset-auditing-tools/Auditor/MetaData/AssetPostprocessorUserDataSerialisation.cs2","moduleName":"PreprocessorModule","assemblyName":"blah 12 | blah vhfs.dshh v4592","methodName":"methodName","version":2}]} }' 13 | assetBundleName: 14 | assetBundleVariant: 15 | -------------------------------------------------------------------------------- /Asset Processors/Interfaces.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff38d3a3a1b944ebb9a3b1880a3ce6a5 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Asset Processors/Interfaces/IConformObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | namespace AssetTools 7 | { 8 | public interface IConformObject 9 | { 10 | string Name { get; set; } 11 | 12 | List SubObjects { get; set; } 13 | 14 | bool Conforms { get; set; } 15 | 16 | string ActualValue { get; } 17 | 18 | string ExpectedValue { get; } 19 | 20 | bool Apply( SerializedObject toObject ); 21 | 22 | void AddTreeViewItems( int parentId, ConformObjectTreeViewItem parent, AssetsTreeViewItem assetsTreeItem, int depth, int arrayIndex = -1 ); 23 | } 24 | } -------------------------------------------------------------------------------- /Asset Processors/Interfaces/IConformObject.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38e856aff09aa4d2984ccaa8a2208a86 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/Interfaces/IImportTask.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEditor.IMGUI.Controls; 5 | using UnityEngine; 6 | 7 | namespace AssetTools 8 | { 9 | public interface IImportTask 10 | { 11 | string ImportTaskName { get; } 12 | 13 | int Version { get; } 14 | 15 | /// 16 | /// UI 17 | /// 18 | 19 | bool GetSearchFilter( out string searchFilter, List ignoreAssetPaths ); 20 | 21 | string AssetMenuFixString 22 | { 23 | get; 24 | } 25 | 26 | /// 27 | /// Checking conformity 28 | /// 29 | 30 | List GetConformObjects( string asset, ImportDefinitionProfile profile ); 31 | 32 | System.Type GetConformObjectType(); 33 | 34 | /// 35 | /// Processing 36 | /// 37 | 38 | bool CanProcess( AssetImporter item ); 39 | 40 | bool IsManuallyProcessing( AssetImporter item ); 41 | 42 | void SetManuallyProcessing( List assetPaths, bool value ); 43 | 44 | void PreprocessTask( ImportContext context, ImportDefinitionProfile profile ); 45 | 46 | bool Apply( ImportContext context, ImportDefinitionProfile profile ); 47 | } 48 | } -------------------------------------------------------------------------------- /Asset Processors/Interfaces/IImportTask.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b6413ed9c340641879c7a2450199cce6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Asset Processors/UserDataSerialization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Runtime.CompilerServices; 5 | using UnityEditor; 6 | using UnityEngine; 7 | using UnityEngine.Assertions; 8 | using UnityEngine.Serialization; 9 | 10 | namespace AssetTools 11 | { 12 | public class UserDataSerialization 13 | { 14 | // TODO this clears every domain reload. See if we can improve this 15 | private static List m_Cache = new List(); 16 | 17 | 18 | const string searchString = "\"ImportDefinitionFiles\": { "; 19 | 20 | [Serializable] 21 | public struct PostprocessorData 22 | { 23 | /// 24 | /// The guid of the Import Definition File that processed this asset 25 | /// 26 | public string importDefinitionGUID; 27 | 28 | /// 29 | /// The importTask name of the importTask that processed this asset 30 | /// 31 | public string moduleName; 32 | 33 | /// 34 | /// The assembly the Method used to process is contained 35 | /// 36 | public string assemblyName; 37 | 38 | /// 39 | /// The typeName of the class that is Invoked 40 | /// 41 | public string typeName; 42 | 43 | /// 44 | /// The version number of the processing method 45 | /// 46 | public int version; 47 | 48 | public PostprocessorData( string importDefinitionGUID, string moduleName, string assemblyName, string typeName, int version ) 49 | { 50 | this.importDefinitionGUID = importDefinitionGUID; 51 | this.moduleName = moduleName; 52 | this.assemblyName = assemblyName; 53 | this.typeName = typeName; 54 | this.version = version; 55 | } 56 | } 57 | 58 | [Serializable] 59 | public struct ImportTaskData 60 | { 61 | /// 62 | /// The guid of the Import Definition File that processed this asset 63 | /// 64 | public string importDefinitionGUID; 65 | 66 | /// 67 | /// The importTask name of the importTask that processed this asset 68 | /// 69 | public string taskName; 70 | 71 | /// 72 | /// The version number of the task 73 | /// 74 | public int version; 75 | 76 | public ImportTaskData( string importDefinitionGUID, string taskName, int version ) 77 | { 78 | this.importDefinitionGUID = importDefinitionGUID; 79 | this.taskName = taskName; 80 | this.version = version; 81 | } 82 | } 83 | 84 | /// 85 | /// A list of serialised data for the Processing done on the asset 86 | /// 87 | [Serializable] 88 | public struct PostprocessorDataList 89 | { 90 | [FormerlySerializedAs( "assetProcessedWith" )] 91 | public List assetProcessedWithMethods; 92 | 93 | public List assetProcessedWithTasks; 94 | 95 | public void UpdateOrAdd( PostprocessorData d ) 96 | { 97 | if( assetProcessedWithMethods == null ) 98 | assetProcessedWithMethods = new List(); 99 | for( int i = 0; i < assetProcessedWithMethods.Count; ++i ) 100 | { 101 | if( assetProcessedWithMethods[i].importDefinitionGUID == d.importDefinitionGUID && 102 | assetProcessedWithMethods[i].moduleName == d.moduleName ) 103 | { 104 | PostprocessorData p = assetProcessedWithMethods[i]; 105 | p.assemblyName = d.assemblyName; 106 | p.typeName = d.typeName; 107 | p.version = d.version; 108 | assetProcessedWithMethods[i] = p; 109 | return; 110 | } 111 | } 112 | assetProcessedWithMethods.Add( d ); 113 | } 114 | 115 | public void UpdateOrAdd( ImportTaskData d ) 116 | { 117 | if( assetProcessedWithTasks == null ) 118 | assetProcessedWithTasks = new List(); 119 | for( int i = 0; i < assetProcessedWithTasks.Count; ++i ) 120 | { 121 | if( assetProcessedWithTasks[i].importDefinitionGUID == d.importDefinitionGUID && 122 | assetProcessedWithTasks[i].taskName == d.taskName ) 123 | { 124 | ImportTaskData p = assetProcessedWithTasks[i]; 125 | p.version = d.version; 126 | assetProcessedWithTasks[i] = p; 127 | return; 128 | } 129 | } 130 | assetProcessedWithTasks.Add( d ); 131 | } 132 | } 133 | 134 | 135 | private PostprocessorDataList m_ImporterPostprocessorData; 136 | private AssetImporter m_Importer; 137 | private string m_ImporterJson; 138 | 139 | //private int m_UserDataStartIndex; 140 | //private int m_UserDataEndIndex; 141 | 142 | 143 | public List GetProcessedMethodsData() 144 | { 145 | // TODO make a copy? 146 | return m_ImporterPostprocessorData.assetProcessedWithMethods; 147 | } 148 | 149 | public List GetProcessedTasksData() 150 | { 151 | // TODO make a copy? 152 | return m_ImporterPostprocessorData.assetProcessedWithTasks; 153 | } 154 | 155 | public UserDataSerialization( string assetPath ) 156 | { 157 | m_Importer = AssetImporter.GetAtPath( assetPath ); 158 | if( m_Importer == null ) 159 | { 160 | Debug.LogError( "Could not find AssetImporter for " + assetPath ); 161 | return; 162 | } 163 | 164 | ParseMetaFile(); 165 | } 166 | 167 | /// 168 | /// Get a userData representation for processing on the asset at assetPath 169 | /// 170 | /// 171 | /// 172 | public static UserDataSerialization Get( string assetPath ) 173 | { 174 | for( int i = 0; i < m_Cache.Count; ++i ) 175 | { 176 | if( m_Cache[i].m_Importer != null ) 177 | { 178 | if( m_Cache[i].m_Importer.assetPath == assetPath ) 179 | return m_Cache[i]; 180 | } 181 | else 182 | { 183 | Debug.LogError( "AssetImporter is null for Asset - this is unexpect to happen and a bug" ); 184 | } 185 | } 186 | 187 | UserDataSerialization ud = new UserDataSerialization( assetPath ); 188 | if( ud.m_Importer != null ) 189 | { 190 | m_Cache.Add(ud); 191 | return m_Cache[m_Cache.Count - 1]; 192 | } 193 | 194 | return null; 195 | } 196 | 197 | private void ParseMetaFile() 198 | { 199 | GetImporterJson(); 200 | PostprocessorDataList importersPostprocessorData = new PostprocessorDataList(); 201 | if( ! string.IsNullOrEmpty( m_ImporterJson )) 202 | importersPostprocessorData = JsonUtility.FromJson( m_ImporterJson ); 203 | m_ImporterPostprocessorData = importersPostprocessorData; 204 | } 205 | 206 | private void GetImporterJson() 207 | { 208 | Assert.IsNotNull( m_Importer ); 209 | 210 | string userData = m_Importer.userData; 211 | 212 | int idfStartIndex = userData.IndexOf( searchString, StringComparison.Ordinal ); 213 | int idfEndIndex = -1; 214 | if( idfStartIndex != -1 ) 215 | { 216 | idfEndIndex = idfStartIndex + searchString.Length; 217 | int counter = 0; 218 | int startIndex = idfEndIndex; 219 | while( idfEndIndex < userData.Length ) 220 | { 221 | if( userData[idfEndIndex] == '{' ) 222 | counter++; 223 | else if( userData[idfEndIndex] == '}' ) 224 | counter--; 225 | 226 | if( counter == -1 ) 227 | break; 228 | ++idfEndIndex; 229 | } 230 | Assert.AreEqual( -1, counter ); 231 | 232 | int length = idfEndIndex - startIndex; 233 | m_ImporterJson = userData.Substring( startIndex, length ); 234 | if( m_ImporterJson.Length > 0 ) 235 | { 236 | for( int i = m_ImporterJson.Length - 1; i >= 0; --i ) 237 | { 238 | if( m_ImporterJson[i] == ' ' ) 239 | m_ImporterJson = m_ImporterJson.Remove( i ); 240 | else 241 | break; 242 | } 243 | } 244 | } 245 | } 246 | 247 | public void UpdateProcessing( PostprocessorData d ) 248 | { 249 | m_ImporterPostprocessorData.UpdateOrAdd( d ); 250 | } 251 | 252 | public void UpdateProcessing( ImportTaskData d ) 253 | { 254 | m_ImporterPostprocessorData.UpdateOrAdd( d ); 255 | } 256 | 257 | public void SaveMetaData() 258 | { 259 | //GetImporterJson(); // use this when testing 260 | string json = JsonUtility.ToJson( m_ImporterPostprocessorData ); 261 | if( string.Equals( json, m_ImporterJson ) ) 262 | return; 263 | 264 | string importDefinitionFileUserData = "\"ImportDefinitionFiles\": { " + json + " }"; 265 | 266 | string userData = m_Importer.userData; 267 | 268 | int idfStartIndex = userData.IndexOf( searchString, StringComparison.Ordinal ); 269 | int idfEndIndex = -1; 270 | if( idfStartIndex != -1 ) 271 | { 272 | idfEndIndex = idfStartIndex + searchString.Length; 273 | int counter = 0; 274 | int startIndex = idfEndIndex; 275 | while( idfEndIndex < userData.Length ) 276 | { 277 | if( userData[idfEndIndex] == '{' ) 278 | counter++; 279 | else if( userData[idfEndIndex] == '}' ) 280 | counter--; 281 | 282 | if( counter == -1 ) 283 | break; 284 | ++idfEndIndex; 285 | } 286 | Assert.AreEqual( -1, counter ); 287 | } 288 | 289 | if( idfStartIndex >= 0 && idfEndIndex > idfStartIndex ) 290 | { 291 | int length = idfEndIndex - idfStartIndex; 292 | if( userData.Length < idfStartIndex + length ) 293 | { 294 | Debug.LogError( "Problem setting user data" ); 295 | } 296 | 297 | if( importDefinitionFileUserData == userData.Substring( idfStartIndex, length ) ) 298 | { 299 | Debug.LogError( "Bad checks" ); 300 | return; // 301 | } 302 | 303 | userData = userData.Remove( idfStartIndex, (idfEndIndex - idfStartIndex)+1 ); 304 | } 305 | else 306 | idfStartIndex = 0; 307 | 308 | m_ImporterJson = json; 309 | m_Importer.userData = userData.Insert( idfStartIndex, importDefinitionFileUserData ); 310 | } 311 | } 312 | 313 | } -------------------------------------------------------------------------------- /Asset Processors/UserDataSerialization.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b131bc1564ba642249e827d926d47cab 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: '"ImportDefinitionFiles": {"ImportDefinitionFiles": { {"assetProcessedWith":[{"importDefintionPath":"Assets/asset-auditing-tools/Auditor/MetaData/AssetPostprocessorUserDataSerialisation.cs0","moduleName":"PreprocessorModule","assemblyName":"blah 10 | blah vhfs.dshh v4592","methodName":"methodName","version":2},{"importDefintionPath":"Assets/asset-auditing-tools/Auditor/MetaData/AssetPostprocessorUserDataSerialisation.cs1","moduleName":"PreprocessorModule","assemblyName":"blah 11 | blah vhfs.dshh v4592","methodName":"methodName","version":2},{"importDefintionPath":"Assets/asset-auditing-tools/Auditor/MetaData/AssetPostprocessorUserDataSerialisation.cs2","moduleName":"PreprocessorModule","assemblyName":"blah 12 | blah vhfs.dshh v4592","methodName":"methodName","version":2}]} }' 13 | assetBundleName: 14 | assetBundleVariant: 15 | -------------------------------------------------------------------------------- /Auditor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0bba4c69657a147308d7bc5e4e01b91e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Auditor/Assets Tree.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe4912eced2c34c8bbdb13440a059bb9 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Auditor/Assets Tree/AssetsTreeView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEditor.IMGUI.Controls; 5 | using UnityEngine; 6 | 7 | namespace AssetTools 8 | { 9 | public class AssetsTreeView : HierarchyTreeView 10 | { 11 | private class AssetViewItemMenuContext 12 | { 13 | public List m_Modules; 14 | public List m_Items; 15 | 16 | public AssetViewItemMenuContext( List modules, List items ) 17 | { 18 | m_Modules = modules; 19 | m_Items = items; 20 | } 21 | } 22 | 23 | private static readonly Color k_ConformFailColor = new Color( 1f, 0.5f, 0.5f ); 24 | 25 | public ImportDefinitionProfile m_Profile; 26 | private readonly List m_SelectedItems = new List(); 27 | internal ModularDetailTreeView m_ModularTreeView; 28 | 29 | public AssetsTreeView( TreeViewState state ) : base( state ) 30 | { 31 | showBorder = true; 32 | } 33 | 34 | protected override TreeViewItem BuildRoot() 35 | { 36 | TreeViewItem root = new TreeViewItem( -1, -1 ) 37 | { 38 | children = new List() 39 | }; 40 | 41 | if( m_Profile != null ) 42 | GenerateTreeElements( m_Profile, root ); 43 | else 44 | Debug.LogError( "Must set a Profile before Building the AssetsTreeView" ); 45 | return root; 46 | } 47 | 48 | private void GenerateTreeElements( ImportDefinitionProfile profile, TreeViewItem root ) 49 | { 50 | List associatedAssets = GetFilteredAssets( profile ); 51 | 52 | // early out if there are no affected assets 53 | if( associatedAssets.Count == 0 ) 54 | { 55 | Debug.Log( "No matching assets found for " + profile.name ); 56 | return; 57 | } 58 | 59 | string activePath = "Assets"; 60 | AssetsTreeViewItem assetsesTreeFolder = new AssetsTreeViewItem( activePath.GetHashCode(), 0, activePath, true ); 61 | if( assetsesTreeFolder.children == null ) 62 | assetsesTreeFolder.children = new List(); 63 | root.AddChild( assetsesTreeFolder ); 64 | 65 | Dictionary items = new Dictionary 66 | { 67 | {activePath.GetHashCode(), assetsesTreeFolder} 68 | }; 69 | 70 | foreach( var assetPath in associatedAssets ) 71 | { 72 | // split the path to generate folder structure 73 | string path = assetPath.Substring( 7 ); 74 | var strings = path.Split( new[] {'/'}, StringSplitOptions.None ); 75 | activePath = "Assets"; 76 | 77 | AssetsTreeViewItem active = assetsesTreeFolder; 78 | 79 | List conformData = profile.GetConformData( assetPath ); 80 | bool result = true; 81 | for( int i = 0; i < conformData.Count; ++i ) 82 | { 83 | if( conformData[i].Conforms == false ) 84 | { 85 | result = false; 86 | break; 87 | } 88 | } 89 | 90 | if( !result && assetsesTreeFolder.conforms ) 91 | assetsesTreeFolder.conforms = false; 92 | 93 | AssetImporter assetImporter = AssetImporter.GetAtPath( assetPath ); 94 | SerializedObject assetImporterSO = new SerializedObject( assetImporter ); 95 | 96 | // the first entries have lower depth 97 | for( int i = 0; i < strings.Length; i++ ) 98 | { 99 | activePath += "/" + strings[i]; 100 | int id = activePath.GetHashCode(); 101 | 102 | if( i == strings.Length - 1 ) 103 | { 104 | AssetsTreeViewItem item = new AssetsTreeViewItem( id, i + 1, strings[i], result ) 105 | { 106 | icon = AssetDatabase.GetCachedIcon( assetPath ) as Texture2D, 107 | path = activePath, 108 | conformData = conformData, 109 | assetObject = assetImporterSO, 110 | isAsset = true 111 | }; 112 | active.AddChild( item ); 113 | active = item; 114 | if( items.ContainsKey( id )) 115 | Debug.LogError( "id already in for " + activePath ); 116 | items.Add( id, item ); 117 | } 118 | else 119 | { 120 | AssetsTreeViewItem item; 121 | if( !items.TryGetValue( id, out item ) ) 122 | { 123 | item = new AssetsTreeViewItem( id, i + 1, strings[i], result ) 124 | { 125 | path = activePath, 126 | icon = AssetDatabase.GetCachedIcon( activePath ) as Texture2D 127 | }; 128 | active.AddChild( item ); 129 | items.Add( id, item ); 130 | } 131 | else if( result == false ) 132 | { 133 | if( item.conforms ) 134 | item.conforms = false; 135 | } 136 | 137 | active = item; 138 | } 139 | } 140 | } 141 | } 142 | 143 | private List GetFilteredAssets( ImportDefinitionProfile profile ) 144 | { 145 | List associatedAssets = new List(); 146 | List ignorePaths = new List(); 147 | string searchFilter; 148 | 149 | List GUIDs; 150 | 151 | // TODO this is probably inefficient, profile and see if there is a more efficient design needed 152 | if( m_Profile.m_ImportTasks.Count > 0 ) 153 | { 154 | m_Profile.m_ImportTasks[0].GetSearchFilter( out searchFilter, ignorePaths ); 155 | GUIDs = new List( AssetDatabase.FindAssets( searchFilter ) ); 156 | 157 | for( int i = 1; i < m_Profile.m_ImportTasks.Count; ++i ) 158 | { 159 | m_Profile.m_ImportTasks[i].GetSearchFilter( out searchFilter, ignorePaths ); 160 | string[] moduleGUIDs = AssetDatabase.FindAssets( searchFilter ); 161 | for( int m = GUIDs.Count - 1; m >= 0; --m ) 162 | { 163 | bool guidInModule = false; 164 | foreach( string moduleGuiD in moduleGUIDs ) 165 | { 166 | if( GUIDs[m] == moduleGuiD ) 167 | { 168 | guidInModule = true; 169 | break; 170 | } 171 | } 172 | if( guidInModule == false ) 173 | GUIDs.RemoveAt( m ); 174 | } 175 | } 176 | } 177 | else 178 | { 179 | GUIDs = new List( AssetDatabase.FindAssets( "" ) ); 180 | } 181 | 182 | //string[] GUIDs = AssetDatabase.FindAssets( typeFilter ); 183 | foreach( var assetGUID in GUIDs ) 184 | { 185 | string assetPath = AssetDatabase.GUIDToAssetPath( assetGUID ); 186 | 187 | // some items may appear twice, due to sub assets, e.g. Sprite 188 | if( ignorePaths.Contains( assetPath ) || associatedAssets.Contains( assetPath ) ) 189 | continue; 190 | 191 | if( Filter.Conforms( assetPath, profile.GetFilters() ) ) 192 | associatedAssets.Add( assetPath ); 193 | } 194 | 195 | return associatedAssets; 196 | } 197 | 198 | protected override void RowGUI( RowGUIArgs args ) 199 | { 200 | AssetsTreeViewItem item = args.item as AssetsTreeViewItem; 201 | if( item != null ) 202 | { 203 | float num = GetContentIndent( item ) + extraSpaceBeforeIconAndLabel; 204 | 205 | Rect r = args.rowRect; 206 | if( args.item.icon != null ) 207 | { 208 | r.xMin += num; 209 | r.width = r.height; 210 | GUI.DrawTexture( r, args.item.icon ); 211 | } 212 | 213 | Color old = GUI.color; 214 | if( item.conforms == false ) 215 | GUI.color = k_ConformFailColor; 216 | 217 | r = args.rowRect; 218 | r.xMin += num; 219 | if( args.item.icon != null ) 220 | { 221 | r.x += r.height + 2f; 222 | r.width -= r.height + 2f; 223 | } 224 | 225 | EditorGUI.LabelField( r, args.label ); 226 | GUI.color = old; 227 | } 228 | else 229 | { 230 | base.RowGUI( args ); 231 | } 232 | } 233 | 234 | protected override void SelectionChanged( IList selectedIds ) 235 | { 236 | base.SelectionChanged( selectedIds ); 237 | SetupSelection( selectedIds ); 238 | } 239 | 240 | internal void SetupSelection( IList selectedIds ) 241 | { 242 | m_SelectedItems.Clear(); 243 | 244 | for( int i = 0; i < selectedIds.Count; ++i ) 245 | { 246 | AssetsTreeViewItem item = FindItem( selectedIds[i], rootItem ) as AssetsTreeViewItem; 247 | if( item != null ) 248 | { 249 | m_SelectedItems.Add( item ); 250 | } 251 | } 252 | 253 | m_ModularTreeView.SetSelectedAssetItems( m_SelectedItems ); 254 | } 255 | 256 | protected override void DoubleClickedItem( int id ) 257 | { 258 | base.DoubleClickedItem( id ); 259 | AssetsTreeViewItem pathItem = FindItem( id, rootItem ) as AssetsTreeViewItem; 260 | if( pathItem == null ) 261 | return; 262 | 263 | Selection.activeObject = AssetDatabase.LoadAssetAtPath( pathItem.path ); 264 | EditorGUIUtility.PingObject( Selection.activeObject ); 265 | } 266 | 267 | protected override void ContextClickedItem( int id ) 268 | { 269 | AssetsTreeViewItem contextItem = FindItem( id, rootItem ) as AssetsTreeViewItem; 270 | if( contextItem == null ) 271 | { 272 | Debug.LogError( "ContextMenu on unknown ID" ); 273 | return; 274 | } 275 | 276 | List selectedItems = new List{ contextItem }; 277 | for( int i = 0; i < m_SelectedItems.Count; ++i ) 278 | { 279 | if( id == m_SelectedItems[i].id ) 280 | { 281 | selectedItems = new List( m_SelectedItems ); 282 | break; 283 | } 284 | } 285 | 286 | for( int i = selectedItems.Count - 1; i >= 0; --i ) 287 | { 288 | if( selectedItems[i].conforms ) 289 | selectedItems.RemoveAt( i ); 290 | } 291 | 292 | if( selectedItems.Count == 0 ) 293 | return; 294 | 295 | GenericMenu menu = new GenericMenu(); 296 | List all = new List(m_Profile.m_ImportTasks.Count); 297 | for( int i = 0; i < m_Profile.m_ImportTasks.Count; ++i ) 298 | { 299 | menu.AddItem( new GUIContent( m_Profile.m_ImportTasks[i].AssetMenuFixString ), false, ContextMenuSelectionCallback, 300 | new AssetViewItemMenuContext( new List{ m_Profile.m_ImportTasks[i] }, selectedItems ) ); 301 | all.Add( m_Profile.m_ImportTasks[i] ); 302 | } 303 | menu.AddItem( new GUIContent( "All" ), false, ContextMenuSelectionCallback, 304 | new AssetViewItemMenuContext( all, selectedItems ) ); 305 | menu.ShowAsContext(); 306 | } 307 | 308 | private void ContextMenuSelectionCallback( object ctx ) 309 | { 310 | AssetViewItemMenuContext menuContext = ctx as AssetViewItemMenuContext; 311 | if( menuContext == null ) 312 | { 313 | Debug.LogError( "Incorrect context received from AssetTreeViewItem context menu" ); 314 | return; 315 | } 316 | 317 | List assets = new List(); 318 | List folders = new List(); 319 | for( int i = 0; i < menuContext.m_Items.Count; ++i ) 320 | { 321 | if( menuContext.m_Items[i].isAsset == false ) 322 | { 323 | GetAssetItems( menuContext.m_Items[i], assets, folders ); 324 | if( folders.Contains( menuContext.m_Items[i] ) == false ) 325 | folders.Add( menuContext.m_Items[i] ); 326 | } 327 | else if( menuContext.m_Items[i].conforms == false && assets.Contains( menuContext.m_Items[i] ) == false ) 328 | assets.Add( menuContext.m_Items[i] ); 329 | } 330 | 331 | List assetPaths = new List(assets.Count); 332 | for( int i = 0; i < assets.Count; ++i ) 333 | assetPaths.Add( assets[i].path ); 334 | 335 | for( int i = 0; i < menuContext.m_Modules.Count; ++i ) 336 | { 337 | if( menuContext.m_Modules[i] == null ) 338 | { 339 | Debug.LogError( "Module became null during window use, report issue" ); 340 | continue; 341 | } 342 | 343 | menuContext.m_Modules[i].SetManuallyProcessing( assetPaths, true ); 344 | } 345 | 346 | // reimport all the assets -> triggering the modules to process them (including any that profiles always do) 347 | AssetDatabase.StartAssetEditing(); 348 | for( int i=0; i checkFoldersForConform = new List(); 372 | 373 | for( int i = 0; i < assets.Count; ++i ) 374 | { 375 | assets[i].Refresh(); 376 | AssetsTreeViewItem parent = assets[i].parent as AssetsTreeViewItem; 377 | if( parent != null & checkFoldersForConform.Contains( parent ) == false ) 378 | checkFoldersForConform.Add( parent ); 379 | } 380 | 381 | for( int i = 0; i < folders.Count; ++i ) 382 | { 383 | if( folders[i].conformData == null ) 384 | { 385 | folders[i].conforms = true; 386 | folders[i].Refresh(); 387 | AssetsTreeViewItem parent = folders[i].parent as AssetsTreeViewItem; 388 | if( parent != null && parent.conforms == false && checkFoldersForConform.Contains( parent ) == false ) 389 | checkFoldersForConform.Add( parent ); 390 | } 391 | } 392 | 393 | while( checkFoldersForConform.Count > 0 ) 394 | { 395 | bool conforms = true; 396 | for( int i = 0; i < checkFoldersForConform[0].children.Count; ++i ) 397 | { 398 | AssetsTreeViewItem item = checkFoldersForConform[0].children[i] as AssetsTreeViewItem; 399 | if( item != null && item.conforms == false ) 400 | conforms = false; 401 | } 402 | 403 | if( conforms ) 404 | { 405 | checkFoldersForConform[0].conforms = true; 406 | AssetsTreeViewItem parent = checkFoldersForConform[0].parent as AssetsTreeViewItem; 407 | if( parent != null & checkFoldersForConform.Contains( parent ) == false ) 408 | checkFoldersForConform.Add( parent ); 409 | } 410 | 411 | checkFoldersForConform.RemoveAt( 0 ); 412 | } 413 | 414 | m_ModularTreeView.Reload(); 415 | } 416 | 417 | private void SetConformObjectRecursive( IConformObject obj, bool value, Type restrictToType ) 418 | { 419 | obj.Conforms = value; 420 | foreach( IConformObject data in obj.SubObjects ) 421 | { 422 | if( data.GetType() == restrictToType ) 423 | SetConformObjectRecursive( data, value, restrictToType ); 424 | } 425 | } 426 | 427 | static void GetAssetItems( AssetsTreeViewItem item , List assets, List folders ) 428 | { 429 | for( int i = 0; i < item.children.Count; ++i ) 430 | { 431 | AssetsTreeViewItem avi = item.children[i] as AssetsTreeViewItem; 432 | if( avi.isAsset == false && folders.Contains( avi ) == false ) 433 | { 434 | folders.Add( avi ); 435 | GetAssetItems( avi, assets, folders ); 436 | } 437 | else if( avi.conforms == false && assets.Contains( avi ) == false ) 438 | assets.Add( avi ); 439 | } 440 | } 441 | } 442 | 443 | } -------------------------------------------------------------------------------- /Auditor/Assets Tree/AssetsTreeView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b6bfcfd488ac541e084d961eb6dde522 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Auditor/Assets Tree/AssetsTreeViewItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor; 3 | using UnityEditor.IMGUI.Controls; 4 | using UnityEngine; 5 | 6 | namespace AssetTools 7 | { 8 | public class AssetsTreeViewItem : TreeViewItem 9 | { 10 | internal bool conforms { get; set; } 11 | 12 | public string path = ""; 13 | public bool isAsset; 14 | 15 | public List conformData; 16 | public SerializedObject assetObject; 17 | 18 | 19 | private AssetImporter m_AssetImporter; 20 | internal AssetImporter AssetImporter 21 | { 22 | get 23 | { 24 | if( m_AssetImporter == null ) 25 | m_AssetImporter = AssetImporter.GetAtPath( path ); 26 | return m_AssetImporter; 27 | } 28 | } 29 | 30 | internal AssetsTreeViewItem( int id, int depth, string displayName, bool assetConforms ) : base( id, depth, displayName ) 31 | { 32 | conforms = assetConforms; 33 | } 34 | 35 | // This is done after setting manually. But if running through the modules during import pipeline. Then perhaps simply reimport and let it handle it. 36 | // if manually done (window view) then add to a list of force runOnImport and reset at PostProcessAllAssets 37 | public void ReimportAsset() 38 | { 39 | if( !isAsset ) 40 | return; 41 | 42 | EditorUtility.SetDirty( AssetImporter ); 43 | AssetImporter.SaveAndReimport(); 44 | } 45 | 46 | public void Refresh() 47 | { 48 | icon = AssetDatabase.GetCachedIcon( path ) as Texture2D; 49 | 50 | if( conformData != null ) 51 | { 52 | conforms = true; 53 | for( int i = 0; i < conformData.Count; ++i ) 54 | { 55 | if( conformData[i].Conforms == false ) 56 | { 57 | conforms = false; 58 | break; 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Auditor/Assets Tree/AssetsTreeViewItem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 93fda1034ef8a4cf58431629209103c7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Auditor/AuditWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEditor.IMGUI.Controls; 5 | using UnityEngine; 6 | 7 | namespace AssetTools 8 | { 9 | public class AuditWindow : EditorWindow 10 | { 11 | private TreeViewState m_AssetListState; 12 | private AssetsTreeView m_AssetList; 13 | 14 | private SearchField m_SearchFieldInternal; 15 | 16 | private TreeViewState m_PropertyListState; 17 | private ModularDetailTreeView m_ModularTreeView; 18 | 19 | private List profiles; 20 | private List profileNames; 21 | 22 | private int selected; 23 | private bool editSelective; 24 | private int editSelectiveProp; 25 | 26 | private float m_SplitterPercent = 0.7f; 27 | private bool m_ResizingSplitter; 28 | 29 | private const float horizontalBuffer = 5f; 30 | private const float k_toolBarY = 0; 31 | private const float k_toolBarHeight = 17f; 32 | 33 | [SerializeField] 34 | MultiColumnHeaderState m_ConformTreeHeaderState; 35 | 36 | private float ContentX 37 | { 38 | get { return horizontalBuffer; } 39 | } 40 | private float ContentWidth 41 | { 42 | get { return position.width - ContentX - horizontalBuffer; } 43 | } 44 | 45 | private Rect ProfileDropDownRect 46 | { 47 | get { return new Rect( ContentX, k_toolBarY, 150, k_toolBarHeight ); } 48 | } 49 | private Rect ProfileSelectRect 50 | { 51 | get { return new Rect( ContentX+ProfileDropDownRect.width+3, k_toolBarY, 60, k_toolBarHeight ); } 52 | } 53 | private Rect RefreshRect 54 | { 55 | get { return new Rect( (ContentX+ContentWidth) - 50, k_toolBarY, 50, k_toolBarHeight ); } 56 | } 57 | private Rect SearchBarRect 58 | { 59 | get 60 | { 61 | float x = ProfileSelectRect.x + ProfileSelectRect.width + 3+5; 62 | return new Rect( x, k_toolBarY+1, (RefreshRect.x-3-5)-x, k_toolBarHeight-1 ); 63 | } 64 | } 65 | 66 | private void OnEnable() 67 | { 68 | m_SearchFieldInternal = new SearchField(); 69 | } 70 | 71 | [MenuItem( "Window/Asset Auditor", priority = 2055)] 72 | public static AuditWindow GetWindow() 73 | { 74 | AuditWindow window = GetWindow(); 75 | window.minSize = new Vector2(350, 350); 76 | window.titleContent = new GUIContent( "Auditor" ); 77 | return window; 78 | } 79 | 80 | private void RefreshData() 81 | { 82 | profiles = new List(); 83 | profileNames = new List(); 84 | GetAuditorProfiles( ); 85 | 86 | if( m_AssetListState == null ) 87 | m_AssetListState = new TreeViewState(); 88 | 89 | IList selection = null; 90 | if( m_AssetList != null ) 91 | selection = m_AssetList.GetSelection(); 92 | 93 | m_AssetList = new AssetsTreeView( m_AssetListState ); 94 | 95 | m_AssetList.SearchStyle = (HierarchyTreeView.SearchType)EditorPrefs.GetInt( "AuditWindow.AssetsTreeView.SearchStyle", 1 ); 96 | m_AssetList.SearchCaseSensitive = EditorPrefs.GetBool( "AuditWindow.AssetsTreeView.SearchCaseSensitivity", false ); 97 | 98 | if( profiles.Count > 0 && selected < profiles.Count ) 99 | m_AssetList.m_Profile = profiles[selected]; 100 | m_AssetList.Reload(); 101 | 102 | if( m_PropertyListState == null ) 103 | m_PropertyListState = new TreeViewState(); 104 | if( m_ModularTreeView == null ) 105 | { 106 | MultiColumnHeaderState headerState = CreateDefaultMultiColumnHeaderState(); 107 | if( MultiColumnHeaderState.CanOverwriteSerializedFields( m_ConformTreeHeaderState, headerState ) ) 108 | MultiColumnHeaderState.OverwriteSerializedFields( m_ConformTreeHeaderState, headerState ); 109 | m_ConformTreeHeaderState = headerState; 110 | m_ModularTreeView = new ModularDetailTreeView( m_PropertyListState, m_ConformTreeHeaderState ); 111 | } 112 | m_AssetList.m_ModularTreeView = m_ModularTreeView; 113 | m_ModularTreeView.Reload(); 114 | 115 | if( selection != null ) 116 | m_AssetList.SetupSelection( selection ); 117 | } 118 | 119 | internal static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState() 120 | { 121 | var retVal = new MultiColumnHeaderState.Column[4]; 122 | retVal[0] = new MultiColumnHeaderState.Column(); 123 | retVal[0].headerContent = new GUIContent( EditorGUIUtility.FindTexture("FilterSelectedOnly"), "Does this element conform to the expected?"); 124 | retVal[0].minWidth = 20; 125 | retVal[0].width = 20; 126 | retVal[0].maxWidth = 20; 127 | retVal[0].headerTextAlignment = TextAlignment.Left; 128 | retVal[0].canSort = true; 129 | retVal[0].autoResize = true; 130 | 131 | retVal[1] = new MultiColumnHeaderState.Column(); 132 | retVal[1].headerContent = new GUIContent("Property Name", "Name of the Property or Parent to Property"); 133 | retVal[1].minWidth = 150; 134 | retVal[1].width = 300; 135 | retVal[1].maxWidth = 1000; 136 | retVal[1].headerTextAlignment = TextAlignment.Left; 137 | retVal[1].canSort = true; 138 | retVal[1].autoResize = true; 139 | 140 | retVal[2] = new MultiColumnHeaderState.Column(); 141 | retVal[2].headerContent = new GUIContent("Expected Value", "The value expected"); 142 | retVal[2].minWidth = 100; 143 | retVal[2].width = 150; 144 | retVal[2].maxWidth = 400; 145 | retVal[2].headerTextAlignment = TextAlignment.Left; 146 | retVal[2].canSort = false; 147 | retVal[2].autoResize = true; 148 | 149 | retVal[3] = new MultiColumnHeaderState.Column(); 150 | retVal[3].headerContent = new GUIContent("Actual Value", "The actual value reported"); 151 | retVal[3].minWidth = 100; 152 | retVal[3].width = 150; 153 | retVal[3].maxWidth = 400; 154 | retVal[3].headerTextAlignment = TextAlignment.Left; 155 | retVal[3].canSort = false; 156 | retVal[3].autoResize = true; 157 | 158 | return new MultiColumnHeaderState(retVal); 159 | } 160 | 161 | void GetAuditorProfiles() 162 | { 163 | string[] auditorProfileGUIDs = AssetDatabase.FindAssets( "t:ImportDefinitionProfile" ); 164 | 165 | profiles.Clear(); 166 | foreach( string asset in auditorProfileGUIDs ) 167 | { 168 | string guidToAssetPath = AssetDatabase.GUIDToAssetPath( asset ); 169 | ImportDefinitionProfile profile = AssetDatabase.LoadAssetAtPath( guidToAssetPath ); 170 | if( profile != null ) 171 | profiles.Add( profile ); 172 | } 173 | 174 | profileNames.Clear(); 175 | foreach( ImportDefinitionProfile assetRule in profiles ) 176 | profileNames.Add( assetRule.name ); 177 | } 178 | 179 | void OnGUI() 180 | { 181 | HandleResize(); 182 | 183 | if( m_AssetList == null || m_ModularTreeView == null ) 184 | RefreshData(); 185 | 186 | ToolbarGUI( new Rect( 0, 0, position.width, k_toolBarHeight) ); 187 | 188 | float viewY = k_toolBarY + k_toolBarHeight; 189 | m_AssetList.OnGUI( new Rect( 0, viewY, position.width, (int)(position.height * m_SplitterPercent) - viewY ) ); 190 | 191 | viewY = (int)(position.height * m_SplitterPercent); 192 | m_ModularTreeView.OnGUI( new Rect( 0, viewY, position.width, position.height - viewY ) ); 193 | 194 | if( m_ResizingSplitter ) 195 | Repaint(); 196 | } 197 | 198 | private void ToolbarGUI( Rect toolbarPos ) 199 | { 200 | GUI.Box( toolbarPos, GUIContent.none, EditorStyles.toolbar ); 201 | 202 | string[] popupList = new string[profileNames.Count + 3]; 203 | for( int i = 0; i < profileNames.Count; ++i ) 204 | popupList[i] = profileNames[i]; 205 | 206 | int preSelected = selected; 207 | selected = EditorGUI.Popup( ProfileDropDownRect, selected, popupList, EditorStyles.toolbarPopup ); 208 | if( selected != preSelected && selected < profileNames.Count ) 209 | RefreshData(); 210 | 211 | if( GUI.Button( ProfileSelectRect, "Select", Styles.button ) && selected >= 0 && selected < profiles.Count && profiles[selected] != null ) 212 | { 213 | Selection.activeObject = profiles[selected]; 214 | EditorGUIUtility.PingObject( profiles[selected] ); 215 | } 216 | 217 | if( m_SearchFieldInternal == null ) 218 | m_SearchFieldInternal = new SearchField(); 219 | OnSearchGUI( SearchBarRect ); 220 | 221 | if( GUI.Button( RefreshRect, "Refresh", Styles.button ) ) 222 | RefreshData(); 223 | } 224 | 225 | private void OnSearchGUI( Rect barPosition ) 226 | { 227 | string text = m_AssetList.CustomSearch; 228 | 229 | Rect popupPosition = new Rect( barPosition.x, barPosition.y, 20, 20 ); 230 | if (Event.current.type == EventType.MouseDown && popupPosition.Contains(Event.current.mousePosition)) 231 | { 232 | var menu = new GenericMenu(); 233 | menu.AddItem(new GUIContent("Hierarchical Search"), m_AssetList.SearchStyle == HierarchyTreeView.SearchType.Hierarchy, () => SetSearchMode( HierarchyTreeView.SearchType.Hierarchy )); 234 | menu.AddItem(new GUIContent("Hierarchical Search (Assets Only)"), m_AssetList.SearchStyle == HierarchyTreeView.SearchType.HierarchyLeafOnly, () => SetSearchMode( HierarchyTreeView.SearchType.HierarchyLeafOnly )); 235 | menu.AddItem(new GUIContent("Flat Search"), m_AssetList.SearchStyle == HierarchyTreeView.SearchType.Flat, () => SetSearchMode( HierarchyTreeView.SearchType.Flat )); 236 | menu.AddItem(new GUIContent("Flat Search (Assets Only)"), m_AssetList.SearchStyle == HierarchyTreeView.SearchType.FlatLeafOnly, () => SetSearchMode( HierarchyTreeView.SearchType.FlatLeafOnly )); 237 | menu.AddSeparator( "" ); 238 | menu.AddItem( new GUIContent( "Case Sensitive" ), m_AssetList.SearchCaseSensitive, () => 239 | { 240 | m_AssetList.SearchCaseSensitive = !m_AssetList.SearchCaseSensitive; 241 | EditorPrefs.SetBool( "AuditWindow.AssetsTreeView.SearchCaseSensitivity", m_AssetList.SearchCaseSensitive ); 242 | m_AssetList.Reload(); 243 | } ); 244 | menu.DropDown(popupPosition); 245 | } 246 | else 247 | { 248 | var searchString = m_SearchFieldInternal.OnGUI(barPosition, text, Styles.searchField, Styles.searchFieldCancelButton, Styles.searchFieldCancelButtonEmpty); 249 | 250 | if (text != searchString) 251 | { 252 | text = searchString; 253 | 254 | m_AssetList.CustomSearch = text; 255 | m_AssetList.Reload(); 256 | 257 | if( String.IsNullOrEmpty( text ) ) 258 | m_AssetList.ExpandForSelection(); 259 | } 260 | } 261 | } 262 | 263 | private void SetSearchMode( HierarchyTreeView.SearchType searchType ) 264 | { 265 | if( m_AssetList.SearchStyle == searchType ) 266 | return; 267 | 268 | m_AssetList.SearchStyle = searchType; 269 | EditorPrefs.SetInt( "AuditWindow.AssetsTreeView.SearchStyle", (int)searchType ); 270 | m_AssetList.Reload(); 271 | } 272 | 273 | private void HandleResize() 274 | { 275 | Rect splitterRect = new Rect(0, (int)(position.height * m_SplitterPercent), position.width, 3); 276 | 277 | EditorGUIUtility.AddCursorRect(splitterRect, MouseCursor.ResizeVertical); 278 | if (Event.current.type == EventType.MouseDown && splitterRect.Contains(Event.current.mousePosition)) 279 | m_ResizingSplitter = true; 280 | 281 | if (m_ResizingSplitter) 282 | { 283 | m_SplitterPercent = Mathf.Clamp(Event.current.mousePosition.y / position.height, 0.1f, 0.9f); 284 | splitterRect.y = (int)(position.height * m_SplitterPercent); 285 | } 286 | 287 | if (Event.current.type == EventType.MouseUp) 288 | m_ResizingSplitter = false; 289 | } 290 | private static class Styles 291 | { 292 | public static readonly GUIStyle searchField = "ToolbarSeachTextFieldPopup"; 293 | public static readonly GUIStyle searchFieldCancelButton = "ToolbarSeachCancelButton"; 294 | public static readonly GUIStyle searchFieldCancelButtonEmpty = "ToolbarSeachCancelButtonEmpty"; 295 | public static readonly GUIStyle button = "ToolbarButton"; 296 | } 297 | 298 | } 299 | } -------------------------------------------------------------------------------- /Auditor/AuditWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c3cf55b48c197bb40a09696dead1d7c2 3 | timeCreated: 1467106475 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Auditor/Conform Tree.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c48248f11a30449ff9649eb014d6b93a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Auditor/Conform Tree/ConformObjectTreeViewItem.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor.IMGUI.Controls; 2 | using UnityEngine; 3 | using Assert = UnityEngine.Assertions.Assert; 4 | 5 | namespace AssetTools 6 | { 7 | public class ConformObjectTreeViewItem : TreeViewItem 8 | { 9 | private bool m_Conforms = false; 10 | internal bool conforms 11 | { 12 | get 13 | { 14 | return conformObject != null ? conformObject.Conforms : m_Conforms; 15 | } 16 | set { m_Conforms = value; } 17 | } 18 | internal IConformObject conformObject { get; set; } 19 | internal AssetsTreeViewItem AssetsTreeViewItem { get; set; } 20 | 21 | internal ConformObjectTreeViewItem( int id, int depth, string displayName, bool propertyConforms ) : base( id, depth, displayName ) 22 | { 23 | m_Conforms = propertyConforms; 24 | } 25 | 26 | internal ConformObjectTreeViewItem( int id, int depth, IConformObject conformObject ) 27 | { 28 | base.id = id; 29 | base.depth = depth; 30 | this.conformObject = conformObject; 31 | base.displayName = conformObject.Name; 32 | } 33 | 34 | public void ApplyConform() 35 | { 36 | if( conformObject.Apply( AssetsTreeViewItem.assetObject ) ) 37 | { 38 | conformObject.Conforms = true; 39 | m_Conforms = true; 40 | displayName = conformObject.Name; 41 | AssetsTreeViewItem.ReimportAsset(); 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Auditor/Conform Tree/ConformObjectTreeViewItem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a07f239c762c348ca85fcc2934708eec 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Auditor/Conform Tree/ModularDetailTreeView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEditor.IMGUI.Controls; 5 | using UnityEngine; 6 | using Assert = UnityEngine.Assertions.Assert; 7 | 8 | namespace AssetTools 9 | { 10 | internal class ModularDetailTreeView : TreeView 11 | { 12 | internal class ItemTree 13 | { 14 | private ConformObjectTreeViewItem item; 15 | private List children; 16 | 17 | public int Depth 18 | { 19 | get { return item == null ? -1 : item.depth; } 20 | } 21 | 22 | public ItemTree( ConformObjectTreeViewItem i ) 23 | { 24 | item = i; 25 | children = new List(); 26 | } 27 | 28 | public void AddChild( ItemTree item ) 29 | { 30 | children.Add( item ); 31 | } 32 | 33 | public void Sort( int[] columnSortOrder, bool[] isColumnAscending ) 34 | { 35 | children.Sort( delegate( ItemTree a, ItemTree b ) 36 | { 37 | int rtn = 0; 38 | for( int i = 0; i < columnSortOrder.Length; i++ ) 39 | { 40 | if( columnSortOrder[i] == 0 ) 41 | { 42 | rtn = isColumnAscending[i] ? a.item.conforms.CompareTo( b.item.conforms ) 43 | : b.item.conforms.CompareTo( a.item.conforms ); 44 | if( rtn == 0 ) 45 | continue; 46 | return rtn; 47 | } 48 | else if( columnSortOrder[i] == 1 ) 49 | { 50 | rtn = isColumnAscending[i] ? string.Compare( a.item.displayName, b.item.displayName, StringComparison.Ordinal ) 51 | : string.Compare( b.item.displayName, a.item.displayName, StringComparison.Ordinal ); 52 | if( rtn == 0 ) 53 | continue; 54 | return rtn; 55 | } 56 | } 57 | 58 | return rtn; 59 | }); 60 | 61 | foreach( ItemTree child in children ) 62 | child.Sort( columnSortOrder, isColumnAscending ); 63 | } 64 | 65 | public void ToList(List list) 66 | { 67 | // TODO be good to optimise this, rarely used, so not required 68 | if( item != null ) 69 | list.Add( item ); 70 | foreach( ItemTree child in children ) 71 | child.ToList( list ); 72 | } 73 | } 74 | 75 | private static readonly Color k_ConformFailColor = new Color( 1f, 0.5f, 0.5f ); 76 | 77 | private List m_SelectedItems; 78 | private Texture2D m_UnconformedTexture; 79 | 80 | public ModularDetailTreeView( TreeViewState state, MultiColumnHeaderState headerState ) : base( state, new MultiColumnHeader( headerState ) ) 81 | { 82 | showBorder = true; 83 | showAlternatingRowBackgrounds = true; 84 | columnIndexForTreeFoldouts = 1; 85 | m_UnconformedTexture = EditorGUIUtility.FindTexture( "LookDevClose@2x" ); 86 | multiColumnHeader.sortingChanged += OnSortingChanged; 87 | } 88 | 89 | public void SetSelectedAssetItems( List selection ) 90 | { 91 | m_SelectedItems = selection; 92 | Reload(); 93 | if( selection.Count > 0 ) 94 | { 95 | // TODO expand so many, but not all if multi selected 96 | SetExpanded( new int[] {(selection[0].displayName + ":").GetHashCode()} ); 97 | } 98 | } 99 | 100 | protected override TreeViewItem BuildRoot() 101 | { 102 | var root = new TreeViewItem( -1, -1 ) 103 | { 104 | children = new List() 105 | }; 106 | 107 | // TODO need to display multiple selection (include folders in that) 108 | if( m_SelectedItems != null && m_SelectedItems.Count > 0 && m_SelectedItems[0].isAsset ) 109 | GenerateTreeElements( m_SelectedItems[0], root ); 110 | return root; 111 | } 112 | 113 | private static void GenerateTreeElements( AssetsTreeViewItem assetsTreeItem, TreeViewItem root ) 114 | { 115 | string activePath = assetsTreeItem.displayName + ":"; 116 | 117 | // create root object for the Asset 118 | ConformObjectTreeViewItem conformObjectAssetRoot = new ConformObjectTreeViewItem( activePath.GetHashCode(), 0, activePath, true ) 119 | { 120 | icon = assetsTreeItem.icon 121 | }; 122 | if( conformObjectAssetRoot.children == null ) 123 | conformObjectAssetRoot.children = new List(); 124 | root.AddChild( conformObjectAssetRoot ); 125 | 126 | List data = assetsTreeItem.conformData; 127 | 128 | for( int i = 0; i < data.Count; ++i ) 129 | { 130 | if( data[i].ConformObjects.Count == 0 ) 131 | continue; 132 | 133 | int moduleHash = data[i].ImportTask.GetHashCode() + i; 134 | ConformObjectTreeViewItem conformObjectModuleRoot = new ConformObjectTreeViewItem( moduleHash, 1, data[i].ImportTask.GetType().Name, true ); 135 | if( conformObjectModuleRoot.children == null ) 136 | conformObjectModuleRoot.children = new List(); 137 | conformObjectAssetRoot.AddChild( conformObjectModuleRoot ); 138 | 139 | if( data[i].Conforms == false ) 140 | { 141 | conformObjectModuleRoot.conforms = false; 142 | conformObjectAssetRoot.conforms = false; 143 | } 144 | 145 | foreach( var conformObject in data[i].ConformObjects ) 146 | { 147 | conformObject.AddTreeViewItems( moduleHash, conformObjectModuleRoot, assetsTreeItem, 2 ); 148 | } 149 | } 150 | } 151 | 152 | protected override IList BuildRows(TreeViewItem root) 153 | { 154 | var rows = base.BuildRows (root); 155 | SortIfNeeded(root, rows); 156 | return rows; 157 | } 158 | 159 | void OnSortingChanged (MultiColumnHeader multiColumnHeader) 160 | { 161 | if( multiColumnHeader.sortedColumnIndex == -1 ) 162 | return; 163 | SortIfNeeded(rootItem, GetRows()); 164 | } 165 | void SortIfNeeded( TreeViewItem root, IList rows ) 166 | { 167 | if(multiColumnHeader.sortedColumnIndex == -1) 168 | return; 169 | SortByMultipleColumns(rows); 170 | Repaint(); 171 | } 172 | 173 | void SortByMultipleColumns( IList rows ) 174 | { 175 | int[] sortedColumns = multiColumnHeader.state.sortedColumns; 176 | if (sortedColumns.Length == 0) 177 | return; 178 | 179 | bool[] columnAscending = new bool[sortedColumns.Length]; 180 | for( int i = 0; i < sortedColumns.Length; i++ ) 181 | columnAscending[i] = multiColumnHeader.IsSortedAscending( sortedColumns[i] ); 182 | 183 | ItemTree root = new ItemTree(null); 184 | Stack stack = new Stack(); 185 | stack.Push( root ); 186 | foreach( TreeViewItem row in rows ) 187 | { 188 | ConformObjectTreeViewItem r = row as ConformObjectTreeViewItem; 189 | if( r == null ) 190 | continue; 191 | int activeParentDepth = stack.Peek().Depth; 192 | 193 | while( row.depth <= activeParentDepth ) 194 | { 195 | stack.Pop(); 196 | activeParentDepth = stack.Peek().Depth; 197 | } 198 | 199 | if( row.depth > activeParentDepth ) 200 | { 201 | ItemTree t = new ItemTree(r); 202 | stack.Peek().AddChild(t); 203 | stack.Push(t); 204 | } 205 | } 206 | 207 | root.Sort( sortedColumns, columnAscending ); 208 | 209 | // convert back to rows 210 | List newRows = new List(rows.Count); 211 | root.ToList( newRows ); 212 | rows.Clear(); 213 | foreach( TreeViewItem treeViewItem in newRows ) 214 | rows.Add( treeViewItem ); 215 | } 216 | 217 | protected override void RowGUI( RowGUIArgs args ) 218 | { 219 | ConformObjectTreeViewItem item = args.item as ConformObjectTreeViewItem; 220 | if( item == null ) 221 | { 222 | Debug.LogWarning( "Unknown TreeViewItem for conform tree" ); 223 | return; 224 | } 225 | 226 | for (int i = 0; i < args.GetNumVisibleColumns (); ++i) 227 | { 228 | if( i == 0 ) 229 | ConformsGUI(args.GetCellRect(i), item); 230 | else if( i == 1 ) 231 | PropertyNameGUI(args.GetCellRect(i), item, ref args); 232 | else if( i == 2 && item.conformObject != null ) 233 | ExpectedValueGUI(args.GetCellRect(i), item); 234 | else if( i == 3 && item.conformObject != null ) 235 | ActualValueGUI(args.GetCellRect(i), item); 236 | 237 | } 238 | } 239 | 240 | void ConformsGUI( Rect cellRect, ConformObjectTreeViewItem item ) 241 | { 242 | if( !item.conforms ) 243 | { 244 | Color old = GUI.color; 245 | GUI.color = k_ConformFailColor * 0.8f; 246 | GUI.DrawTexture( cellRect, m_UnconformedTexture ); 247 | GUI.color = old; 248 | } 249 | } 250 | 251 | void ActualValueGUI( Rect cellRect, ConformObjectTreeViewItem item ) 252 | { 253 | Rect labelRect = cellRect; 254 | if( item.conforms == false ) 255 | { 256 | Color old = GUI.color; 257 | GUI.color = k_ConformFailColor; 258 | EditorGUI.LabelField( labelRect, item.conformObject.ActualValue ); 259 | GUI.color = old; 260 | } 261 | else 262 | EditorGUI.LabelField( labelRect, item.conformObject.ActualValue ); 263 | } 264 | 265 | void ExpectedValueGUI( Rect cellRect, ConformObjectTreeViewItem item ) 266 | { 267 | Rect labelRect = cellRect; 268 | if( item.conforms == false ) 269 | { 270 | Color old = GUI.color; 271 | GUI.color = k_ConformFailColor; 272 | EditorGUI.LabelField( labelRect, item.conformObject.ExpectedValue ); 273 | GUI.color = old; 274 | } 275 | else 276 | EditorGUI.LabelField( labelRect, item.conformObject.ExpectedValue ); 277 | } 278 | 279 | void PropertyNameGUI( Rect cellRect, ConformObjectTreeViewItem item, ref RowGUIArgs args ) 280 | { 281 | float indent = GetContentIndent( item ) + extraSpaceBeforeIconAndLabel; 282 | 283 | Rect labelRect = cellRect; 284 | labelRect.xMin += indent; 285 | Rect iconRect = cellRect; 286 | if( args.item.icon != null ) 287 | { 288 | iconRect.xMin += indent; 289 | iconRect.width = iconRect.height; 290 | GUI.DrawTexture( iconRect, args.item.icon ); 291 | labelRect.x += 18; 292 | labelRect.width -= 18; 293 | } 294 | 295 | if( item.conforms == false ) 296 | { 297 | Color old = GUI.color; 298 | GUI.color = k_ConformFailColor; 299 | EditorGUI.LabelField( labelRect, args.label ); 300 | GUI.color = old; 301 | } 302 | else 303 | EditorGUI.LabelField( labelRect, args.label ); 304 | } 305 | 306 | protected override void ContextClickedItem( int id ) 307 | { 308 | ConformObjectTreeViewItem item = FindItem( id, rootItem ) as ConformObjectTreeViewItem; 309 | Assert.IsNotNull( item ); 310 | if( item.conforms ) 311 | return; 312 | 313 | // TODO ask the ConformObject to do this 314 | PropertyConformObject propertyConformObject = item.conformObject as PropertyConformObject; 315 | if( propertyConformObject != null ) 316 | { 317 | GenericMenu menu = new GenericMenu(); 318 | if( propertyConformObject.TemplateType == SerializedPropertyType.Generic ) 319 | { 320 | menu.AddItem( new GUIContent( "Set children to Template Values" ), false, FixCallback, item ); 321 | } 322 | else if( propertyConformObject.TemplateType == SerializedPropertyType.ArraySize ) 323 | { 324 | menu.AddDisabledItem( new GUIContent( "Cannot set array size" ) ); 325 | } 326 | else 327 | { 328 | menu.AddItem( new GUIContent( "Set to " + propertyConformObject.TemplateValue ), false, FixCallback, item ); 329 | } 330 | 331 | menu.ShowAsContext(); 332 | } 333 | } 334 | 335 | private static void FixCallback( object context ) 336 | { 337 | if( context == null ) 338 | return; 339 | 340 | // TODO multi-select 341 | ConformObjectTreeViewItem selectedNodes = context as ConformObjectTreeViewItem; 342 | Assert.IsNotNull( selectedNodes, "Context must be a ConformObjectTreeViewItem" ); 343 | selectedNodes.ApplyConform(); 344 | } 345 | } 346 | 347 | } -------------------------------------------------------------------------------- /Auditor/Conform Tree/ModularDetailTreeView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c72fec662670d4b88988c5163c82981c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Auditor/GUIUtility.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e3241edbc124b42849ae3f24e007ee01 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Auditor/GUIUtility/ControlRect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor; 3 | using UnityEngine; 4 | 5 | namespace AssetTools.GUIUtility 6 | { 7 | public static class SerializationUtilities 8 | { 9 | public static List GetSerialisedPropertyCopiesForObject( SerializedProperty classProp, IList targets ) 10 | { 11 | SerializedProperty copy = classProp.Copy(); 12 | List properties = new List(targets.Count); 13 | for( int i=0; i GetSerialisedPropertyCopiesForObject( SerializedObject classObj, IList targets ) 29 | { 30 | SerializedProperty copy = classObj.GetIterator().Copy(); 31 | List properties = new List(targets.Count); 32 | for( int i=0; i 1 ) 113 | { 114 | Debug.LogError( "Cannot begin area multiple times" ); 115 | return; 116 | } 117 | areaStartY = currentY; 118 | areaXPadding = xPadding; 119 | areaYPadding = yPadding; 120 | Space( areaYPadding ); 121 | } 122 | 123 | public Rect EndArea() 124 | { 125 | Space( areaYPadding ); 126 | Rect r = new Rect(x, areaStartY, width, currentY-areaStartY); 127 | 128 | areaStartY = 0; 129 | areaXPadding = 0; 130 | areaYPadding = 0; 131 | return r; 132 | } 133 | } 134 | 135 | } -------------------------------------------------------------------------------- /Auditor/GUIUtility/ControlRect.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cda36075f1729406cb8010a224bf5853 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Auditor/GUIUtility/HierarchyTreeView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEditor.IMGUI.Controls; 5 | using UnityEngine; 6 | 7 | namespace AssetTools 8 | { 9 | public abstract class HierarchyTreeView : TreeView 10 | { 11 | public enum SearchType 12 | { 13 | Hierarchy = 0, 14 | HierarchyLeafOnly, 15 | Flat, 16 | FlatLeafOnly, 17 | Standard, 18 | } 19 | 20 | private string m_CustomSearch; 21 | private SearchType m_SearchType; 22 | private bool m_CaseSensitive; 23 | 24 | public string CustomSearch 25 | { 26 | get { return m_CustomSearch; } 27 | set { m_CustomSearch = value; } 28 | } 29 | 30 | public SearchType SearchStyle 31 | { 32 | get { return m_SearchType; } 33 | set { m_SearchType = value; } 34 | } 35 | 36 | public bool SearchCaseSensitive 37 | { 38 | get { return m_CaseSensitive; } 39 | set { m_CaseSensitive = value; } 40 | } 41 | 42 | protected HierarchyTreeView( TreeViewState state ) : base( state ) 43 | { 44 | } 45 | 46 | protected override bool CanChangeExpandedState( TreeViewItem item ) 47 | { 48 | if( string.IsNullOrEmpty(m_CustomSearch) || m_SearchType == SearchType.Standard ) 49 | return base.CanChangeExpandedState( item ); 50 | return false; 51 | } 52 | 53 | protected override IList BuildRows(TreeViewItem root) 54 | { 55 | if( !string.IsNullOrEmpty(m_CustomSearch) ) 56 | { 57 | List searchedRows = new List( 100 ); 58 | switch( m_SearchType ) 59 | { 60 | case SearchType.HierarchyLeafOnly: 61 | BuildSearchTreeRecursive( root, searchedRows, true ); 62 | break; 63 | case SearchType.Hierarchy: 64 | BuildSearchTreeRecursive( root, searchedRows, false ); 65 | break; 66 | case SearchType.FlatLeafOnly: 67 | BuildSearchFlat( root, searchedRows, true ); 68 | break; 69 | case SearchType.Flat: 70 | BuildSearchFlat( root, searchedRows, false ); 71 | break; 72 | } 73 | return searchedRows; 74 | } 75 | 76 | return base.BuildRows(root); 77 | } 78 | 79 | // TODO optimise this recursiveness 80 | 81 | private void BuildSearchFlat( TreeViewItem item, List items, bool leafOnly ) 82 | { 83 | if( item.hasChildren ) 84 | { 85 | foreach( TreeViewItem child in item.children ) 86 | { 87 | if( IsInSearch( m_CustomSearch, child, leafOnly ) ) 88 | { 89 | if( child.hasChildren == false ) 90 | { 91 | items.Add( child ); 92 | child.depth = 0; 93 | } 94 | BuildSearchFlat( child, items, leafOnly ); 95 | } 96 | } 97 | } 98 | } 99 | 100 | private void BuildSearchTreeRecursive( TreeViewItem item, List items, bool leafOnly ) 101 | { 102 | if( item.hasChildren ) 103 | { 104 | foreach( TreeViewItem child in item.children ) 105 | { 106 | if( IsInSearch( m_CustomSearch, child, leafOnly ) ) 107 | { 108 | items.Add( child ); 109 | BuildSearchTreeRecursive( child, items, leafOnly ); 110 | } 111 | } 112 | } 113 | } 114 | 115 | private bool IsInSearch( string search, TreeViewItem item, bool leafOnly ) 116 | { 117 | bool includedInSearch = false; 118 | if( item.hasChildren ) 119 | { 120 | if( !leafOnly ) 121 | includedInSearch = item.displayName.IndexOf( search, m_CaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase ) >= 0; 122 | if( !includedInSearch ) 123 | { 124 | foreach( TreeViewItem t in item.children ) 125 | { 126 | if( IsInSearch( search, t, leafOnly ) ) 127 | { 128 | includedInSearch = true; 129 | break; 130 | } 131 | } 132 | } 133 | } 134 | else 135 | includedInSearch = item.displayName.IndexOf( search, m_CaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase ) >= 0; 136 | 137 | return includedInSearch; 138 | } 139 | 140 | public void ExpandForSelection( ) 141 | { 142 | List toExpand = new List(GetExpanded()); 143 | IList selection = GetSelection(); 144 | 145 | foreach( int selectedId in selection ) 146 | { 147 | TreeViewItem selectedItem = FindItem( selectedId, rootItem ); 148 | if( selectedItem == null ) 149 | continue; 150 | 151 | while( selectedItem.parent != null ) 152 | { 153 | selectedItem = selectedItem.parent; 154 | if( rootItem != selectedItem && ! toExpand.Contains( selectedItem.id )) 155 | toExpand.Add( selectedItem.id ); 156 | } 157 | } 158 | 159 | SetExpanded( toExpand ); 160 | } 161 | } 162 | 163 | } -------------------------------------------------------------------------------- /Auditor/GUIUtility/HierarchyTreeView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 64af1e161818842c89317aefe82774cd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes will be documented in this file. 3 | 4 | ## [0.0.X] - 2019-XX-XX 5 | - Changed search for have multiple search types 6 | - Changed search to case-insensitive by default, adding option is search options 7 | - Changed top bar to use toolbar visuals 8 | - Fixed move up / down options for Import tasks 9 | - Added multicolumn view for Conform tree view 10 | 11 | ## [0.0.5] - 2019-10-09 12 | - Refactored the project and renaming the tools to better describe the functionality 13 | - note: Profiles made in the old naming will need to be reimported 14 | - Added PostProcessing for Textures 15 | - Added ImportContext to hold data as it goes through the import tasks 16 | - Fixed bugs is writing to userData 17 | - Added processing type, pre/post 18 | 19 | ## [0.0.4] - 2019-05-01 20 | - Added multi-select within the window 21 | - Added ability to Add specific modules 22 | 23 | ## [0.0.3] - 2019-04-01 24 | - Added Preprocessor module 25 | - Added Profile sorting 26 | - Added Lock to directory for Profile filters 27 | 28 | ## [0.0.2] - 2018-11-07 29 | - Removed ImporterType from list of filter Targets 30 | - Added labels as a filter Target 31 | - Filters are matched by OrdinalIgnoreCase where possible 32 | 33 | ## [0.0.1] - 2018-11-06 34 | - Initial submission 35 | -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a2b44ffce52f74680a9ab0785ad0efa6 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Documentation.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3ea7b7c464874ca48ef8194bd357331 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Documentation/AssetAuditor.md: -------------------------------------------------------------------------------- 1 | # Asset Auditor tool 2 | 3 | ### Requires Unity 2017+ 4 | 5 | Use this tool to audit assets within your project and ensure Assets have the correct properties. It can be dropped into any Unity project with a version of 2017.1 or greater. It will create a new menu item in __Window__ > __Asset Auditor__. 6 | 7 | This window provides an explorer like interface to managing and modifying assets in your project. When first opened, it may look empty. First you must create a Profile to view in the window. 8 | 9 | # Usage 10 | 11 | ## Profile's 12 | 13 | Create a new Profile within the Project window context menu __Create__ > __Asset Tools__ > __New Profile__. 14 | 15 | ![BroserConfigure](Images/ProfileInspector.png) 16 | 17 | #### Template: 18 | 19 | The Template can be any Unity Object. This determines what Properties can be applied to Assets. Only Assets that match the Template Object type will match the filter. 20 | 21 | __Supported Templates:__ 22 | * Texture 23 | * Model 24 | * Audio 25 | 26 | #### Search Filter's: 27 | 28 | Click Add to add a new search filter to the filters list. Press the right button with "-" to remove one. 29 | All filters must evaluate to true for an Asset to match search conditions. 30 | 31 | Filter Target, Indicates what you want to search on: 32 | * *Filename* - Just the full filepath filename and Extension. 33 | * *FullFilename* - Includes the Filename, Folder Path and the extension. 34 | * *FolderName* - Just the folder that contains the Asset. 35 | * *Directory* - The full Folder Path to the Asset (Not including the Asset). 36 | * *Extension* - Just the extension of the Asset. 37 | * *AssetBundle* - The AssetBundle name the Asset is assigned to. 38 | * *FileSize* - The size on disk of the Asset (Not imported size). 39 | * *Labels* - Labels assigned to the Asset. (Only supports Contains, DoesNotContain and Equals). Full string of the label must be included and be a comma separated per label without spaces. 40 | 41 | Filter Condition, Indicates how you want to see if the Target is a match: 42 | * *Contains* - Target can contain the wildcard anywhere. 43 | * *Equals* - Target must match the wildcard precisely. 44 | * *DoesNotContain* - Target cannot contain the wildcard string. 45 | * *Regex* - Use a Regular Expression as the wildcard. 46 | * *StartsWith* - Target starts with the wildcard. 47 | * *EndsWith* - Target ends with the wildcard. 48 | * *GreaterThan* - Used when Target is a number, true when greater than the wildcard. 49 | * *GreaterThanEqual* - Used when Target is a number, true when greater than or equal to the wildcard. 50 | * *LessThan* - Used when Target is a number, true when less than the wildcard. 51 | * *LessThanEqual* - Used when Target is a number, true when less than or equal to the wildcard. 52 | 53 | Wildcard string: This is the value to Filter against. 54 | 55 | #### Constrain to Propertie's: 56 | 57 | Click on __"Edit Selection"__ to open a drop down list containing properties available for importing Assets associated with the Template. 58 | 59 | ![BroserConfigure](Images/ConstrainToProperties.png) 60 | 61 | ## Fixing Asset's 62 | 63 | Assets that do not conform to the Template properties are displayed with a red font. 64 | 65 | Right clicking on an Asset that does not conform to list options to fix the Asset. 66 | 67 | ![BroserConfigure](Images/Rightclick.png) 68 | 69 | #### TODO Tasks 70 | * Multi-Select of Asset files 71 | * Selecting a folder lists Assets under 72 | * Work with NativeFormatImporter types 73 | * Allow specification of post/pre processor methods to use on asset 74 | * Add labels as a search option 75 | Add user-data as a feature? 76 | if this is valid, then would be good to have contain to properties as a reverse so can easily disable user-data 77 | or automatically ignore user-data 78 | * Implement TestRunner tests 79 | * Conformity check - A way to check all profiles and see which have Assets that do not conform 80 | * Try out property view with a multicolumnheader -------------------------------------------------------------------------------- /Documentation/AssetAuditor.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3d7406792b3a465ea3438b5f6d8e12d 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Documentation/Images.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 63768d3a8057043538b304b152b1fad3 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Documentation/Images/ConstrainToProperties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-Technologies/asset-auditing-tools/3a0c5c5080a086a4a43c72a5702c1d70d69bf8f9/Documentation/Images/ConstrainToProperties.png -------------------------------------------------------------------------------- /Documentation/Images/ConstrainToProperties.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 302827f09b4f84da5ac9ad1582eecb4b 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 4 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | grayScaleToAlpha: 0 25 | generateCubemap: 6 26 | cubemapConvolution: 0 27 | seamlessCubemap: 0 28 | textureFormat: 1 29 | maxTextureSize: 2048 30 | textureSettings: 31 | serializedVersion: 2 32 | filterMode: -1 33 | aniso: -1 34 | mipBias: -1 35 | wrapU: -1 36 | wrapV: -1 37 | wrapW: -1 38 | nPOTScale: 1 39 | lightmap: 0 40 | compressionQuality: 50 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spritePixelsToUnits: 100 47 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 48 | spriteGenerateFallbackPhysicsShape: 1 49 | alphaUsage: 1 50 | alphaIsTransparency: 0 51 | spriteTessellationDetail: -1 52 | textureType: 0 53 | textureShape: 1 54 | maxTextureSizeSet: 0 55 | compressionQualitySet: 0 56 | textureFormatSet: 0 57 | platformSettings: 58 | - buildTarget: DefaultTexturePlatform 59 | maxTextureSize: 2048 60 | resizeAlgorithm: 0 61 | textureFormat: -1 62 | textureCompression: 1 63 | compressionQuality: 50 64 | crunchedCompression: 0 65 | allowsAlphaSplitting: 0 66 | overridden: 0 67 | androidETC2FallbackOverride: 0 68 | spriteSheet: 69 | serializedVersion: 2 70 | sprites: [] 71 | outline: [] 72 | physicsShape: [] 73 | spritePackingTag: 74 | userData: 75 | assetBundleName: 76 | assetBundleVariant: 77 | -------------------------------------------------------------------------------- /Documentation/Images/ProfileInspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-Technologies/asset-auditing-tools/3a0c5c5080a086a4a43c72a5702c1d70d69bf8f9/Documentation/Images/ProfileInspector.png -------------------------------------------------------------------------------- /Documentation/Images/ProfileInspector.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2db38aa0c1090462098d3bb32e20a11d 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 4 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | grayScaleToAlpha: 0 25 | generateCubemap: 6 26 | cubemapConvolution: 0 27 | seamlessCubemap: 0 28 | textureFormat: 1 29 | maxTextureSize: 2048 30 | textureSettings: 31 | serializedVersion: 2 32 | filterMode: -1 33 | aniso: -1 34 | mipBias: -1 35 | wrapU: -1 36 | wrapV: -1 37 | wrapW: -1 38 | nPOTScale: 1 39 | lightmap: 0 40 | compressionQuality: 50 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spritePixelsToUnits: 100 47 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 48 | spriteGenerateFallbackPhysicsShape: 1 49 | alphaUsage: 1 50 | alphaIsTransparency: 0 51 | spriteTessellationDetail: -1 52 | textureType: 0 53 | textureShape: 1 54 | maxTextureSizeSet: 0 55 | compressionQualitySet: 0 56 | textureFormatSet: 0 57 | platformSettings: 58 | - buildTarget: DefaultTexturePlatform 59 | maxTextureSize: 2048 60 | resizeAlgorithm: 0 61 | textureFormat: -1 62 | textureCompression: 1 63 | compressionQuality: 50 64 | crunchedCompression: 0 65 | allowsAlphaSplitting: 0 66 | overridden: 0 67 | androidETC2FallbackOverride: 0 68 | spriteSheet: 69 | serializedVersion: 2 70 | sprites: [] 71 | outline: [] 72 | physicsShape: [] 73 | spritePackingTag: 74 | userData: 75 | assetBundleName: 76 | assetBundleVariant: 77 | -------------------------------------------------------------------------------- /Documentation/Images/Rightclick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-Technologies/asset-auditing-tools/3a0c5c5080a086a4a43c72a5702c1d70d69bf8f9/Documentation/Images/Rightclick.png -------------------------------------------------------------------------------- /Documentation/Images/Rightclick.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d67bc8f3b910a4cd1bdda9b586bb046f 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 4 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | grayScaleToAlpha: 0 25 | generateCubemap: 6 26 | cubemapConvolution: 0 27 | seamlessCubemap: 0 28 | textureFormat: 1 29 | maxTextureSize: 2048 30 | textureSettings: 31 | serializedVersion: 2 32 | filterMode: -1 33 | aniso: -1 34 | mipBias: -1 35 | wrapU: -1 36 | wrapV: -1 37 | wrapW: -1 38 | nPOTScale: 1 39 | lightmap: 0 40 | compressionQuality: 50 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spritePixelsToUnits: 100 47 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 48 | spriteGenerateFallbackPhysicsShape: 1 49 | alphaUsage: 1 50 | alphaIsTransparency: 0 51 | spriteTessellationDetail: -1 52 | textureType: 0 53 | textureShape: 1 54 | maxTextureSizeSet: 0 55 | compressionQualitySet: 0 56 | textureFormatSet: 0 57 | platformSettings: 58 | - buildTarget: DefaultTexturePlatform 59 | maxTextureSize: 2048 60 | resizeAlgorithm: 0 61 | textureFormat: -1 62 | textureCompression: 1 63 | compressionQuality: 50 64 | crunchedCompression: 0 65 | allowsAlphaSplitting: 0 66 | overridden: 0 67 | androidETC2FallbackOverride: 0 68 | spriteSheet: 69 | serializedVersion: 2 70 | sprites: [] 71 | outline: [] 72 | physicsShape: [] 73 | spritePackingTag: 74 | userData: 75 | assetBundleName: 76 | assetBundleVariant: 77 | -------------------------------------------------------------------------------- /Import Definition Files.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8086c3042a9c84e0e8dc42e3a623770c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Import Definition Files/ConformData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using UnityEngine; 5 | using UnityEditor; 6 | using UnityEngine.Serialization; 7 | 8 | namespace AssetTools 9 | { 10 | public struct ConformData 11 | { 12 | private readonly IImportTask m_ImportTask; 13 | public IImportTask ImportTask 14 | { 15 | get { return m_ImportTask; } 16 | } 17 | 18 | private readonly List m_ConformObjects; 19 | public List ConformObjects 20 | { 21 | get { return m_ConformObjects; } 22 | } 23 | 24 | public bool Conforms 25 | { 26 | get 27 | { 28 | for( int i = 0; i < ConformObjects.Count; ++i ) 29 | { 30 | if( ConformObjects[i].Conforms == false ) 31 | return false; 32 | } 33 | 34 | return true; 35 | } 36 | } 37 | 38 | public ConformData( IImportTask importTask, ImportDefinitionProfile forProfile, string assetPath ) 39 | { 40 | m_ImportTask = importTask; 41 | m_ConformObjects = m_ImportTask.GetConformObjects( assetPath, forProfile ); 42 | } 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /Import Definition Files/ConformData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 351cc9b924b8d41d1b6a7882d1217340 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Import Definition Files/Filter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Text.RegularExpressions; 6 | using UnityEditor; 7 | using UnityEngine; 8 | using UnityEngine.Assertions; 9 | 10 | namespace AssetTools 11 | { 12 | 13 | [System.Serializable] 14 | public class Filter 15 | { 16 | public enum ConditionTarget 17 | { 18 | Filename = 0, 19 | FullFilename, 20 | FolderName, 21 | Directory, 22 | Extension, 23 | AssetBundleName, 24 | FileSize, 25 | ImporterType, 26 | // TODO remove labels?? 27 | Labels // Labels are a bad use-case if doing Preprocessors step - They cannot be obtained until after the import. It is possible to read the meta file. But if it is not Text based or formatting changes it could run into trouble 28 | 29 | // TODO Can get original Texture width/height via reflection, test if can get it during preimport 30 | } 31 | 32 | public enum Condition 33 | { 34 | Contains = 0, 35 | Equals, 36 | Regex, 37 | DoesNotContain, 38 | StartsWith, 39 | EndsWith, 40 | GreaterThan, 41 | GreaterThanEqual, 42 | LessThan, 43 | LessThanEqual 44 | } 45 | 46 | public ConditionTarget m_Target; 47 | public Condition m_Condition; 48 | public string m_Wildcard; 49 | 50 | public Filter() 51 | { 52 | m_Target = ConditionTarget.Filename; 53 | m_Condition = Condition.Contains; 54 | m_Wildcard = ""; 55 | 56 | } 57 | 58 | public Filter( ConditionTarget target, Condition condition, string wildcard ) 59 | { 60 | m_Target = target; 61 | m_Condition = condition; 62 | m_Wildcard = wildcard; 63 | } 64 | 65 | public static bool Conforms( string path, IList filters ) 66 | { 67 | AssetImporter importerForPath = AssetImporter.GetAtPath( path ); 68 | if( importerForPath == null ) 69 | { 70 | Debug.LogError( "Could not find AssetImporter for " + path ); 71 | return false; 72 | } 73 | return Conforms( importerForPath, filters ); 74 | } 75 | 76 | public static bool Conforms( AssetImporter importer, IList filters ) 77 | { 78 | if( importer == null || filters == null || filters.Count == 0 ) 79 | return true; 80 | 81 | FileInfo fi = new FileInfo( importer.assetPath ); 82 | DirectoryInfo di = new DirectoryInfo( importer.assetPath ); 83 | 84 | Assert.IsTrue( fi.Exists || di.Exists ); 85 | 86 | for( int i = 0; i < filters.Count; ++i ) 87 | { 88 | switch( filters[i].m_Target ) 89 | { 90 | case ConditionTarget.Filename: 91 | string name = fi.Name; 92 | if( ! string.IsNullOrEmpty( fi.Extension ) ) 93 | name = name.Remove( name.Length - fi.Extension.Length ); 94 | if( !Target( name, filters[i] ) ) 95 | return false; 96 | break; 97 | case ConditionTarget.FullFilename: 98 | if( !Target( importer.assetPath, filters[i] ) ) 99 | return false; 100 | break; 101 | case ConditionTarget.FolderName: 102 | if( fi.Directory == null || !Target( fi.Directory.Name, filters[i] ) ) 103 | return false; 104 | break; 105 | case ConditionTarget.Directory: 106 | string path = importer.assetPath; 107 | if( !Target( path.Remove( path.Length - fi.Name.Length - 1 ), filters[i] ) ) 108 | return false; 109 | break; 110 | case ConditionTarget.Extension: 111 | if( !Target( fi.Extension, filters[i] ) ) 112 | return false; 113 | break; 114 | case ConditionTarget.FileSize: 115 | if( !Target( fi.Length, filters[i] ) ) 116 | return false; 117 | break; 118 | case ConditionTarget.AssetBundleName: 119 | if( !Target( importer.assetBundleName, filters[i] ) ) 120 | return false; 121 | break; 122 | case ConditionTarget.Labels: 123 | string[] wildLabels = filters[i].m_Wildcard.Split( ',' ); 124 | string[] labels = AssetDatabase.GetLabels( AssetDatabase.LoadAssetAtPath( importer.assetPath ) ); 125 | switch( filters[i].m_Condition ) 126 | { 127 | case Condition.Equals: 128 | if( wildLabels.Length != labels.Length ) 129 | return false; 130 | for( int wlId = 0; wlId < wildLabels.Length; ++wlId ) 131 | { 132 | bool contains = false; 133 | for( int lId = 0; lId < labels.Length; ++lId ) 134 | { 135 | if( wildLabels[wlId].Equals( labels[lId], StringComparison.OrdinalIgnoreCase ) ) 136 | { 137 | contains = true; 138 | break; 139 | } 140 | } 141 | if( contains == false ) 142 | return false; 143 | } 144 | return true; 145 | case Condition.Contains: 146 | for (int wlId = 0; wlId < wildLabels.Length; ++wlId) 147 | { 148 | bool contains = false; 149 | for (int lId = 0; lId < labels.Length; ++lId) 150 | { 151 | if( wildLabels[wlId].Equals(labels[lId], StringComparison.OrdinalIgnoreCase) ) 152 | { 153 | contains = true; 154 | break; 155 | } 156 | } 157 | if( contains == false ) 158 | return false; 159 | } 160 | return true; 161 | case Condition.DoesNotContain: 162 | for (int wlId = 0; wlId < wildLabels.Length; ++wlId) 163 | { 164 | for (int lId = 0; lId < labels.Length; ++lId) 165 | { 166 | if (wildLabels[wlId].Equals(labels[lId], StringComparison.OrdinalIgnoreCase)) 167 | return false; 168 | } 169 | } 170 | return true; 171 | } 172 | 173 | break; 174 | case ConditionTarget.ImporterType: 175 | if( !Target( importer.GetType().Name, filters[i] ) ) 176 | return false; 177 | break; 178 | default: 179 | throw new ArgumentOutOfRangeException(); 180 | } 181 | } 182 | 183 | return true; 184 | } 185 | 186 | private static bool Target( string target, Filter filter ) 187 | { 188 | Assert.IsNotNull( target ); 189 | Assert.IsNotNull( filter ); 190 | Assert.IsNotNull( filter.m_Wildcard ); 191 | 192 | switch( filter.m_Condition ) 193 | { 194 | case Condition.Equals: 195 | return target.Equals( filter.m_Wildcard, StringComparison.OrdinalIgnoreCase ); 196 | case Condition.Contains: 197 | return target.Contains( filter.m_Wildcard ); 198 | case Condition.DoesNotContain: 199 | return !target.Contains( filter.m_Wildcard ); 200 | case Condition.EndsWith: 201 | return target.EndsWith( filter.m_Wildcard, StringComparison.OrdinalIgnoreCase ); 202 | case Condition.StartsWith: 203 | return target.StartsWith( filter.m_Wildcard, StringComparison.OrdinalIgnoreCase ); 204 | case Condition.Regex: 205 | return Regex.IsMatch( target, filter.m_Wildcard ); 206 | default: 207 | throw new Exception( ); 208 | } 209 | } 210 | 211 | private static bool Target( long target, Filter filter ) 212 | { 213 | Assert.IsNotNull( filter ); 214 | Assert.IsNotNull( filter.m_Wildcard ); 215 | 216 | int wildcard; 217 | if( int.TryParse( filter.m_Wildcard, out wildcard ) == false ) 218 | { 219 | Debug.LogError( string.Format( "Wildcard as number is not long parsable \"{0}\"", filter.m_Wildcard ) ); 220 | return false; 221 | } 222 | 223 | switch( filter.m_Condition ) 224 | { 225 | case Condition.Equals: 226 | return target.Equals( wildcard ); 227 | case Condition.LessThan: 228 | return target < wildcard; 229 | case Condition.LessThanEqual: 230 | return target <= wildcard; 231 | case Condition.GreaterThan: 232 | return target > wildcard; 233 | case Condition.GreaterThanEqual: 234 | return target >= wildcard; 235 | default: 236 | throw new ArgumentOutOfRangeException(); 237 | } 238 | } 239 | } 240 | 241 | } -------------------------------------------------------------------------------- /Import Definition Files/Filter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 378e7b102e998458cb2eecf16de8b63d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Import Definition Files/ImportDefinitionProfile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using UnityEngine; 5 | using UnityEditor; 6 | using UnityEngine.Serialization; 7 | 8 | namespace AssetTools 9 | { 10 | 11 | 12 | [CreateAssetMenu(fileName = "New Import Definition", menuName = "Asset Tools/New Import Definition", order = 0)] 13 | public class ImportDefinitionProfile : ScriptableObject, IComparable 14 | { 15 | public bool m_RunOnImport = false; 16 | public bool m_FilterToFolder = true; 17 | public int m_SortIndex = 0; 18 | 19 | [SerializeField] private List m_Filters; 20 | 21 | public List Filters 22 | { 23 | get{ return m_Filters == null ? new List() : new List(m_Filters); } 24 | } 25 | 26 | private string m_DirectoryPath = null; 27 | 28 | [FormerlySerializedAs( "m_Modules" )] 29 | public List m_ImportTasks = new List(); 30 | 31 | internal string DirectoryPath 32 | { 33 | get 34 | { 35 | if( string.IsNullOrEmpty( m_DirectoryPath ) ) 36 | m_DirectoryPath = Path.GetDirectoryName( AssetDatabase.GetAssetPath( this ) ); 37 | return m_DirectoryPath; 38 | } 39 | set { m_DirectoryPath = null; } 40 | } 41 | 42 | public List GetFilters( bool userFiltersOnly = false ) 43 | { 44 | List filters =Filters; 45 | if( m_FilterToFolder ) 46 | filters.Add( new Filter( Filter.ConditionTarget.Directory, Filter.Condition.StartsWith, DirectoryPath ) ); 47 | return filters; 48 | } 49 | 50 | public List GetConformData( string asset ) 51 | { 52 | List data = new List(); 53 | for( int i = 0; i < m_ImportTasks.Count; ++i ) 54 | { 55 | if( m_ImportTasks[i] != null ) 56 | { 57 | ConformData d = new ConformData( m_ImportTasks[i], this, asset ); 58 | data.Add( d ); 59 | } 60 | } 61 | return data; 62 | } 63 | 64 | public void PreprocessAsset( ImportContext context, bool checkForConformity = true ) 65 | { 66 | if( checkForConformity ) 67 | { 68 | if( m_FilterToFolder ) 69 | { 70 | List filters = Filters; 71 | filters.Add( new Filter( Filter.ConditionTarget.Directory, Filter.Condition.StartsWith, DirectoryPath ) ); 72 | if( Filter.Conforms( context.Importer, filters ) == false ) 73 | return; 74 | } 75 | else if( Filter.Conforms( context.Importer, m_Filters ) == false ) 76 | return; 77 | } 78 | 79 | bool saveMeta = false; 80 | 81 | if( m_RunOnImport ) 82 | { 83 | for( int i = 0; i < m_ImportTasks.Count; ++i ) 84 | { 85 | if( m_ImportTasks[i] != null ) 86 | { 87 | m_ImportTasks[i].PreprocessTask( context, this ); 88 | saveMeta = true; 89 | if( m_ImportTasks[i].TaskProcessType == BaseImportTask.ProcessingType.Pre ) 90 | { 91 | m_ImportTasks[i].Apply( context, this ); 92 | m_ImportTasks[i].SetManuallyProcessing( context.AssetPath, false ); 93 | } 94 | } 95 | } 96 | } 97 | else 98 | { 99 | for( int i = 0; i < m_ImportTasks.Count; ++i ) 100 | { 101 | if( m_ImportTasks[i] != null && m_ImportTasks[i].IsManuallyProcessing( context.Importer ) ) 102 | { 103 | m_ImportTasks[i].PreprocessTask( context, this ); 104 | saveMeta = true; 105 | if( m_ImportTasks[i].TaskProcessType == BaseImportTask.ProcessingType.Pre ) 106 | { 107 | m_ImportTasks[i].Apply( context, this ); 108 | m_ImportTasks[i].SetManuallyProcessing( context.AssetPath, false ); 109 | } 110 | } 111 | } 112 | } 113 | 114 | if( saveMeta ) 115 | { 116 | UserDataSerialization data = UserDataSerialization.Get( context.AssetPath ); 117 | if( data != null ) 118 | data.SaveMetaData(); 119 | } 120 | } 121 | 122 | public void PostprocessAsset( ImportContext context, bool checkForConformity = true ) 123 | { 124 | if( checkForConformity ) 125 | { 126 | if( m_FilterToFolder ) 127 | { 128 | List filters = Filters; 129 | filters.Add( new Filter( Filter.ConditionTarget.Directory, Filter.Condition.StartsWith, DirectoryPath ) ); 130 | if( Filter.Conforms( context.Importer, filters ) == false ) 131 | return; 132 | } 133 | else if( Filter.Conforms( context.Importer, m_Filters ) == false ) 134 | return; 135 | } 136 | 137 | if( m_RunOnImport ) 138 | { 139 | for( int i = 0; i < m_ImportTasks.Count; ++i ) 140 | { 141 | if( m_ImportTasks[i] != null && m_ImportTasks[i].TaskProcessType == BaseImportTask.ProcessingType.Post ) 142 | { 143 | m_ImportTasks[i].Apply( context, this ); 144 | m_ImportTasks[i].SetManuallyProcessing( context.AssetPath, false ); 145 | } 146 | } 147 | } 148 | else 149 | { 150 | for( int i = 0; i < m_ImportTasks.Count; ++i ) 151 | { 152 | if( m_ImportTasks[i] != null && m_ImportTasks[i].IsManuallyProcessing( context.Importer ) && m_ImportTasks[i].TaskProcessType == BaseImportTask.ProcessingType.Post ) 153 | { 154 | m_ImportTasks[i].Apply( context, this ); 155 | m_ImportTasks[i].SetManuallyProcessing( context.AssetPath, false ); 156 | } 157 | } 158 | } 159 | } 160 | 161 | public BaseImportTask AddTask( Type type ) 162 | { 163 | if (type == null) 164 | { 165 | Debug.LogWarning("Cannot remove schema with null type."); 166 | return null; 167 | } 168 | if (!typeof(BaseImportTask).IsAssignableFrom(type)) 169 | { 170 | Debug.LogWarningFormat("Invalid Schema type {0}. Schemas must inherit from AddressableAssetGroupSchema.", type.FullName); 171 | return null; 172 | } 173 | 174 | BaseImportTask importTaskInstance = (BaseImportTask)CreateInstance( type ); 175 | if( importTaskInstance.MaximumCount > 0 ) 176 | { 177 | int sameType = 0; 178 | foreach( BaseImportTask task in m_ImportTasks ) 179 | { 180 | if( task.GetType() == type ) 181 | { 182 | sameType++; 183 | if( sameType >= importTaskInstance.MaximumCount ) 184 | break; 185 | } 186 | } 187 | 188 | if( sameType >= importTaskInstance.MaximumCount ) 189 | { 190 | DestroyImmediate( importTaskInstance ); 191 | Debug.LogError( "Task count exceeded" ); 192 | return null; 193 | } 194 | } 195 | 196 | if( importTaskInstance != null ) 197 | { 198 | importTaskInstance.name = type.Name; 199 | try 200 | { 201 | importTaskInstance.hideFlags |= HideFlags.HideInHierarchy; 202 | AssetDatabase.AddObjectToAsset( importTaskInstance, this ); 203 | } 204 | catch( Exception e ) 205 | { 206 | Console.WriteLine( e ); 207 | throw; 208 | } 209 | 210 | m_ImportTasks.Add( importTaskInstance ); 211 | EditorUtility.SetDirty( this ); 212 | } 213 | 214 | return importTaskInstance; 215 | } 216 | 217 | public bool RemoveTask( int index ) 218 | { 219 | if( index < 0 || index >= m_ImportTasks.Count ) 220 | return false; 221 | if( m_ImportTasks[index] == null ) 222 | return true; 223 | 224 | #if UNITY_2018_3_OR_NEWER 225 | AssetDatabase.RemoveObjectFromAsset( m_ImportTasks[index] ); 226 | #else 227 | DestroyImmediate( m_ImportTasks[index], true ); 228 | #endif 229 | m_ImportTasks.RemoveAt( index ); 230 | EditorUtility.SetDirty( this ); 231 | return true; 232 | } 233 | 234 | public int CompareTo( ImportDefinitionProfile other ) 235 | { 236 | if( other == null ) 237 | return 1; 238 | 239 | int s = m_SortIndex.CompareTo( other.m_SortIndex ); 240 | if( s == 0 ) 241 | { 242 | // if in same index, sort by shortest path length first 243 | int lengthCompare = DirectoryPath.Length.CompareTo( other.DirectoryPath.Length ); 244 | return lengthCompare; 245 | } 246 | return s; 247 | } 248 | } 249 | } -------------------------------------------------------------------------------- /Import Definition Files/ImportDefinitionProfile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dcb9553626d5040429c9154380d4abd0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Import Definition Files/ImportDefinitionProfileCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEditor; 5 | using UnityEngine; 6 | 7 | namespace AssetTools 8 | { 9 | struct ProfileData : IComparer, IComparable 10 | { 11 | public string m_AssetPath; 12 | public ImportDefinitionProfile m_ImportDefinitionProfile; 13 | public int Compare( ProfileData x, ProfileData y ) 14 | { 15 | return x.m_ImportDefinitionProfile.CompareTo( y.m_ImportDefinitionProfile ); 16 | } 17 | 18 | public int CompareTo( ProfileData other ) 19 | { 20 | return m_ImportDefinitionProfile.CompareTo( other.m_ImportDefinitionProfile ); 21 | } 22 | } 23 | 24 | public class ImportDefinitionProfileCache : AssetPostprocessor 25 | { 26 | private static List s_Profiles = new List(); 27 | 28 | /// 29 | /// A List of AuditProfiles within the project and their locations 30 | /// 31 | internal static List Profiles 32 | { 33 | get 34 | { 35 | return s_Profiles; 36 | } 37 | } 38 | 39 | /// 40 | /// Whenever there is a domain reload fine all assets of type profile 41 | /// 42 | [InitializeOnLoadMethod] 43 | private static void RefreshCacheObjects() 44 | { 45 | s_Profiles.Clear(); 46 | string[] guids = AssetDatabase.FindAssets( "t:ImportDefinitionProfile" ); 47 | for( int i = 0; i < guids.Length; ++i ) 48 | { 49 | string path = AssetDatabase.GUIDToAssetPath( guids[i] ); 50 | ImportDefinitionProfile profile = AssetDatabase.LoadAssetAtPath( path ); 51 | s_Profiles.Add( new ProfileData 52 | { 53 | m_AssetPath = path, 54 | m_ImportDefinitionProfile = profile 55 | } ); 56 | } 57 | s_Profiles.Sort(); 58 | } 59 | 60 | /// 61 | /// Keep the profile list in sync with the project. 62 | /// TODO Could keep track of profiles without [InitialiseOnLoadMethod] with validation instead? 63 | /// 64 | /// 65 | /// 66 | /// 67 | /// 68 | private static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedToAssetPaths, string[] movedFromAssetPaths ) 69 | { 70 | for( int i = 0; i < movedFromAssetPaths.Length; ++i ) 71 | { 72 | for( int d = 0; d < s_Profiles.Count; ++d ) 73 | { 74 | if( s_Profiles[d].m_AssetPath == movedFromAssetPaths[i] ) 75 | { 76 | ProfileData def = s_Profiles[d]; 77 | def.m_AssetPath = movedToAssetPaths[i]; 78 | def.m_ImportDefinitionProfile.DirectoryPath = null; 79 | s_Profiles[d] = def; 80 | break; 81 | } 82 | } 83 | } 84 | 85 | for( int i = 0; i < importedAssets.Length; ++i ) 86 | { 87 | if( importedAssets[i].EndsWith( ".asset" ) == false ) 88 | continue; 89 | ImportDefinitionProfile profile = AssetDatabase.LoadAssetAtPath( importedAssets[i] ); 90 | if( profile == null ) 91 | continue; 92 | 93 | bool isInCache = false; 94 | for( int d = 0; d < s_Profiles.Count; ++d ) 95 | { 96 | if( s_Profiles[d].m_AssetPath == importedAssets[i] ) 97 | { 98 | isInCache = true; 99 | break; 100 | } 101 | } 102 | 103 | if( !isInCache ) 104 | { 105 | ProfileData item = new ProfileData(); 106 | item.m_AssetPath = importedAssets[i]; 107 | item.m_ImportDefinitionProfile = profile; 108 | profile.DirectoryPath = null; 109 | s_Profiles.Add( item ); 110 | } 111 | } 112 | 113 | for( int i = 0; i < deletedAssets.Length; ++i ) 114 | { 115 | for( int d = 0; d < s_Profiles.Count; ++d ) 116 | { 117 | if( s_Profiles[d].m_AssetPath == deletedAssets[i] ) 118 | { 119 | s_Profiles.RemoveAt( d ); 120 | break; 121 | } 122 | } 123 | } 124 | } 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | /// 137 | /// Get all types that can be assigned to type T 138 | /// 139 | /// The class type to use as the base class or interface for all found types. 140 | /// A list of types that are assignable to type T. The results are cached. 141 | public static List GetTypes() 142 | { 143 | return TypeManager.Types; 144 | } 145 | 146 | /// 147 | /// Get all types that can be assigned to type rootType. 148 | /// 149 | /// The class type to use as the base class or interface for all found types. 150 | /// A list of types that are assignable to type T. The results are not cached. 151 | public static List GetTypes(Type rootType) 152 | { 153 | return TypeManager.GetManagerTypes(rootType); 154 | } 155 | 156 | class TypeManager 157 | { 158 | public static List GetManagerTypes(Type rootType) 159 | { 160 | var types = new List(); 161 | try 162 | { 163 | foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) 164 | { 165 | #if NET_4_6 166 | if (a.IsDynamic) 167 | continue; 168 | foreach (var t in a.ExportedTypes) 169 | #else 170 | foreach (var t in a.GetExportedTypes()) 171 | #endif 172 | { 173 | if (t != rootType && rootType.IsAssignableFrom(t) && !t.IsAbstract) 174 | types.Add(t); 175 | } 176 | } 177 | } 178 | catch (Exception) 179 | { 180 | // ignored 181 | } 182 | 183 | return types; 184 | } 185 | } 186 | 187 | class TypeManager : TypeManager 188 | { 189 | // ReSharper disable once StaticMemberInGenericType 190 | static List s_Types; 191 | public static List Types 192 | { 193 | get 194 | { 195 | if (s_Types == null) 196 | s_Types = GetManagerTypes(typeof(T)); 197 | 198 | return s_Types; 199 | } 200 | } 201 | } 202 | } 203 | } -------------------------------------------------------------------------------- /Import Definition Files/ImportDefinitionProfileCache.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bda6e3cca10be4dffa4ca70ce82c8636 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Import Definition Files/ImportDefinitionProfileInspector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | using System.IO; 4 | using System.Reflection; 5 | using UnityEngine; 6 | using UnityEditor; 7 | using AssetTools.GUIUtility; 8 | using UnityEngine.Assertions; 9 | using Object = System.Object; 10 | 11 | namespace AssetTools 12 | { 13 | [CustomEditor( typeof(ImportDefinitionProfile) )] 14 | public class ImportDefinitionProfileInspector : Editor 15 | { 16 | private ImportDefinitionProfile m_Profile; 17 | 18 | private SerializedProperty m_ProcessOnImport; 19 | private SerializedProperty m_FolderOnly; 20 | private SerializedProperty m_FiltersListProperty; 21 | private SerializedProperty m_Tasks; 22 | private SerializedProperty m_SortIndex; 23 | 24 | List m_ImportTaskTypes; 25 | 26 | // TODO serialiseFoldoutState?? 27 | List m_ImportTaskFoldoutStates = new List(); 28 | 29 | void OnEnable() 30 | { 31 | m_ProcessOnImport = serializedObject.FindProperty( "m_RunOnImport" ); 32 | m_FolderOnly = serializedObject.FindProperty( "m_FilterToFolder" ); 33 | m_FiltersListProperty = serializedObject.FindProperty( "m_Filters" ); 34 | m_Tasks = serializedObject.FindProperty( "m_ImportTasks" ); 35 | m_SortIndex = serializedObject.FindProperty( "m_SortIndex" ); 36 | 37 | m_ImportTaskTypes = ImportDefinitionProfileCache.GetTypes(); 38 | } 39 | 40 | public override void OnInspectorGUI() 41 | { 42 | if( m_Profile == null ) 43 | m_Profile = (ImportDefinitionProfile) target; 44 | 45 | //while asset is being created, path can't be determined, which throws an exception when setting up the filters 46 | if(string.IsNullOrEmpty(AssetDatabase.GetAssetPath(m_Profile))) 47 | return; 48 | 49 | Rect viewRect = GUILayoutUtility.GetRect( EditorGUIUtility.currentViewWidth-23, 0 ); 50 | ControlRect layout = new ControlRect( viewRect.x, viewRect.y, viewRect.width ); 51 | 52 | layout.Space( 10 ); 53 | EditorGUI.PropertyField( layout.Get(), m_FolderOnly, new GUIContent("Lock to folder", "Include a filter to limit this profile to this profile only")); 54 | EditorGUI.PropertyField( layout.Get(), m_ProcessOnImport, new GUIContent("Process On Import")); 55 | EditorGUI.PropertyField( layout.Get(), m_SortIndex, new GUIContent("Sort Index")); 56 | layout.Space( 10 ); 57 | 58 | EditorGUI.LabelField( layout.Get(), "Search Filter's" ); 59 | 60 | List filters = m_Profile.GetFilters( true ); 61 | if( filters == null ) 62 | filters = new List(); 63 | 64 | int filterCount = filters.Count; 65 | if( m_FolderOnly.boolValue ) 66 | filterCount++; 67 | 68 | Rect boxAreaRect = layout.Get( (16 * filterCount) + (3 * filterCount) + 6 ); 69 | GUI.Box( boxAreaRect, GUIContent.none ); 70 | 71 | ControlRect subLayout = new ControlRect( boxAreaRect.x + 3, boxAreaRect.y + 3, boxAreaRect.width - 6, 16 ); 72 | subLayout.padding = 3; 73 | 74 | int removeAt = -1; 75 | int size = m_FiltersListProperty.arraySize; 76 | if( m_FolderOnly.boolValue ) 77 | size++; 78 | 79 | for( int i = 0; i < size; ++i ) 80 | { 81 | Rect segmentRect = subLayout.Get(); 82 | segmentRect.x += 3; 83 | segmentRect.width -= 6; 84 | 85 | float segWidth = (segmentRect.width - segmentRect.height) / 3; 86 | segmentRect.width = segWidth - 3; 87 | float startX = segmentRect.x; 88 | 89 | if( m_FolderOnly.boolValue && i == size-1 ) 90 | { 91 | EditorGUI.BeginDisabledGroup( true ); 92 | EditorGUI.EnumPopup( segmentRect, Filter.ConditionTarget.Directory ); 93 | segmentRect.x = startX + segWidth; 94 | EditorGUI.EnumPopup( segmentRect, Filter.Condition.StartsWith ); 95 | segmentRect.x = startX + (segWidth * 2); 96 | EditorGUI.TextField( segmentRect, m_Profile.DirectoryPath ); 97 | EditorGUI.EndDisabledGroup(); 98 | } 99 | else 100 | { 101 | SerializedProperty filterProperty = m_FiltersListProperty.GetArrayElementAtIndex( i ); 102 | filterProperty.NextVisible( true ); 103 | do 104 | { 105 | if( filterProperty.propertyType == SerializedPropertyType.Enum && filterProperty.name == "m_Target" ) 106 | { 107 | segmentRect.x = startX; 108 | EditorGUI.PropertyField( segmentRect, filterProperty, GUIContent.none ); 109 | } 110 | else if( filterProperty.propertyType == SerializedPropertyType.Enum && filterProperty.name == "m_Condition" ) 111 | { 112 | segmentRect.x = startX + segWidth; 113 | EditorGUI.PropertyField( segmentRect, filterProperty, GUIContent.none ); 114 | } 115 | else if( filterProperty.propertyType == SerializedPropertyType.String && filterProperty.name == "m_Wildcard" ) 116 | { 117 | segmentRect.x = startX + (segWidth * 2); 118 | EditorGUI.PropertyField( segmentRect, filterProperty, GUIContent.none ); 119 | } 120 | } while( filterProperty.NextVisible( false ) ); 121 | 122 | segmentRect.x = startX + (segWidth * 3); 123 | segmentRect.width = segmentRect.height; 124 | if( GUI.Button( segmentRect, "-" ) ) 125 | removeAt = i; 126 | } 127 | } 128 | 129 | if( removeAt >= 0 ) 130 | m_FiltersListProperty.DeleteArrayElementAtIndex( removeAt ); 131 | 132 | Rect layoutRect = layout.Get(); 133 | layoutRect.x = layoutRect.x + (layoutRect.width - 40); 134 | layoutRect.width = 40; 135 | if( GUI.Button( layoutRect, "Add" ) ) 136 | { 137 | m_FiltersListProperty.arraySize += 1; 138 | } 139 | 140 | layout.Space( 20 ); 141 | 142 | EditorGUI.LabelField( layout.Get(), "", UnityEngine.GUI.skin.horizontalSlider); 143 | 144 | size = m_Tasks.arraySize; 145 | for( int i = 0; i < size; ++i ) 146 | { 147 | if( m_ImportTaskFoldoutStates.Count-1 < i ) 148 | m_ImportTaskFoldoutStates.Add( true ); 149 | 150 | SerializedProperty taskProperty = m_Tasks.GetArrayElementAtIndex( i ); 151 | BaseImportTask importTask = taskProperty.objectReferenceValue as BaseImportTask; 152 | if( importTask == null ) 153 | continue; 154 | 155 | if( i > 0 ) 156 | layout.Space( 10 ); 157 | 158 | Rect headerRect = layout.Get( 20 ); 159 | m_ImportTaskFoldoutStates[i] = EditorGUI.Foldout( headerRect, m_ImportTaskFoldoutStates[i], importTask.name, true ); 160 | 161 | Event current = Event.current; 162 | if( headerRect.Contains( current.mousePosition ) ) 163 | { 164 | if( (current.type == EventType.MouseDown && current.button == 1) || current.type == EventType.ContextClick ) 165 | { 166 | GenericMenu menu = new GenericMenu(); 167 | if( i == 0 ) 168 | menu.AddDisabledItem( new GUIContent( "Move Up" ) ); 169 | else 170 | menu.AddItem( new GUIContent( "Move Up" ), false, MoveTaskUpCallback, i ); 171 | if( i == size-1 ) 172 | menu.AddDisabledItem( new GUIContent( "Move Down" ) ); 173 | else 174 | menu.AddItem( new GUIContent( "Move Down" ), false, MoveTaskDownCallback, i ); 175 | 176 | menu.AddSeparator( "" ); 177 | menu.AddItem( new GUIContent( "Delete Import Task" ), false, RemoveTaskCallback, i ); 178 | menu.ShowAsContext(); 179 | current.Use(); 180 | } 181 | else if( current.type == EventType.MouseDown && current.button == 0 ) 182 | { 183 | m_ImportTaskFoldoutStates[i] = !m_ImportTaskFoldoutStates[i]; 184 | } 185 | } 186 | 187 | if( m_ImportTaskFoldoutStates[i] ) 188 | { 189 | layout.BeginArea( 5, 5 ); 190 | 191 | importTask.DrawGUI( layout ); 192 | 193 | GUI.depth = GUI.depth - 1; 194 | GUI.Box( layout.EndArea(), "" ); 195 | GUI.depth = GUI.depth + 1; 196 | } 197 | } 198 | 199 | if( size > 0 ) 200 | EditorGUI.LabelField( layout.Get(), "", UnityEngine.GUI.skin.horizontalSlider); 201 | 202 | layoutRect = layout.Get(); 203 | if( layoutRect.width > 120 ) 204 | { 205 | layoutRect.x = layoutRect.x + (layoutRect.width - 120); 206 | layoutRect.width = 120; 207 | } 208 | 209 | if (EditorGUI.DropdownButton( layoutRect, new GUIContent("Add Import Task", "Add new Task to this Definition."), FocusType.Keyboard)) 210 | { 211 | var menu = new GenericMenu(); 212 | for (int i = 0; i < m_ImportTaskTypes.Count; i++) 213 | { 214 | var type = m_ImportTaskTypes[i]; 215 | menu.AddItem(new GUIContent(type.Name, ""), false, OnAddImportTask, type); 216 | } 217 | 218 | menu.ShowAsContext(); 219 | } 220 | 221 | GUILayoutUtility.GetRect( viewRect.width, layoutRect.y + layoutRect.height ); 222 | serializedObject.ApplyModifiedProperties(); 223 | } 224 | 225 | void RemoveTaskCallback( object context ) 226 | { 227 | int index = (int) context; 228 | // Ask first? 229 | if( m_Profile.RemoveTask( index ) ) 230 | { 231 | m_Tasks.DeleteArrayElementAtIndex( index ); 232 | m_Tasks.MoveArrayElement( index, m_Tasks.arraySize - 1 ); 233 | m_Tasks.arraySize = m_Tasks.arraySize - 1; 234 | 235 | m_ImportTaskFoldoutStates.RemoveAt( index ); 236 | 237 | EditorUtility.SetDirty( m_Profile ); 238 | Repaint(); 239 | } 240 | } 241 | 242 | void MoveTaskUpCallback( object context ) 243 | { 244 | int index = (int) context; 245 | m_Tasks.MoveArrayElement( index, index-1 ); 246 | Repaint(); 247 | } 248 | 249 | void MoveTaskDownCallback( object context ) 250 | { 251 | int index = (int) context; 252 | m_Tasks.MoveArrayElement( index, index+1 ); 253 | Repaint(); 254 | } 255 | 256 | void OnAddImportTask(object context) 257 | { 258 | Type t = context as Type; 259 | Assert.IsNotNull( t, "Null ImportTask Type" ); 260 | 261 | BaseImportTask addedImportTask = m_Profile.AddTask( t ); 262 | if( addedImportTask != null ) 263 | { 264 | // keep the serialised property in sync 265 | m_Tasks.arraySize++; 266 | m_Tasks.GetArrayElementAtIndex( m_Tasks.arraySize-1 ).objectReferenceValue = addedImportTask; 267 | m_ImportTaskFoldoutStates.Add( true ); 268 | 269 | EditorUtility.SetDirty( m_Profile ); 270 | Repaint(); 271 | } 272 | } 273 | 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /Import Definition Files/ImportDefinitionProfileInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 594c554a559ba49e1b5d8e2ee007fe87 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repo is deprecated and development has stopped on this 2 | 3 | # Asset Auditing Tools 4 | As a Unity project gets bigger, it can get hard to maintain the many Assets within a Project. 5 | This tool is designed to alleviate the common use of hard coding lots AssetPostprocessor classes. 6 | 7 | ### Important note 8 | In order for Pre and Post processing tasks to work in this system, editing the AssetImporter.userData of assets is required. 9 | This is also tricky for possible custom tasks. 10 | 11 | When using the cache server it stores the imported asset by information must be known before importing. 12 | Such as the GetVersion of the AssetPostprocessor classes and binary of the file + meta file. For our ImportTasks to work correctly with the cache server. 13 | The tasks write to the .userData of the meta file changing its hash for storing. Allowing it to work nicely with the cache server. 14 | 15 | I really do not like this, as other code may overwrite the .userData destroying the usage of this tool. 16 | New features are coming to Unity in future versions to get around this. 17 | 18 | ## Import Definition Profiles 19 | Import Definition Profiles are a way of defining how Assets are imported. Using a set of filters to determine if the profile relates to those Assets. 20 | A set of Import Tasks that can be added onto profiles to define how the filtered Assets should be imported. 21 | 22 | Some basic Import Tasks will be included. Design is intended for custom import tasks to be implemented for specific project needs. 23 | 24 | [Manual Page](Documentation/ImportDefinitionProfiles.md) (TODO) 25 | 26 | ## Auditor Window 27 | This tool is intended as a way to search through your project for Assets. Comparing them against Import Definition Profiles within your project. 28 | Through this window you can easily see an Asset that has not been imported in a way you expect and correct. 29 | 30 | ImportDefinitionProfiles are used to determine which profiles are displayed in the view. If run on import is enabled, then it is expected that 31 | all the Asset will automatically be in the correct state. 32 | 33 | The Auditor Window can be used for finer control and to observe where changes are occuring. 34 | 35 | [Manual Page](Documentation/AssetAuditor.md) (Out of date) 36 | 37 | ## Disclaimer 38 | This is a project developed by a member of the Enterprise Support team and not maintained by Unity's roadmap. (I update and maintain when I can) 39 | Feedback and requests are more than welcome, email: andrewmi@unity3d.com 40 | 41 | ## Road Map 42 | 43 | General 44 | - Branch out 2017 and 2018 versions, ready for utilising new ADV2 feature 45 | - Setup for package manager 46 | 47 | Import Definition Profiles 48 | - New Import Tasks 49 | - 2018+ new task "PresetPropertiesImportTask", this will be good for both importer properties and other objects 50 | - Look into if MonoScript cache should be used for IPreprocessor. Paving the path to no longer being reliant on AssetImporter.userData for importer versioning. With AssetDatabase V2 we can set custom dependencies for Assets. Allowing an Asset to have a dependency on the MonoScript. 51 | - This would still need a way of saving what versions they were previously imported with for auditing. 52 | - Track movement and changes to profiles to reimport any Assets where necessary. 53 | - Look into custom Importer for the ScriptableObject for IDF. So it can Apply -> Then allowing reimport any required Assets on Apply. 54 | - Need to investigate import order and if this is needed to effect that 55 | - Order task into Pre and Post import sections 56 | - rework UX of the profile inspector gui 57 | 58 | Auditor Window 59 | - Multiselect and fix working 60 | - Ability to fix a folder 61 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2f1dabfeddb0d404ca842b6b82c36088 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Unity.AssetTools.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Unity.AssetTools.Editor", 3 | "references": [], 4 | "includePlatforms": [ 5 | "Editor" 6 | ], 7 | "excludePlatforms": [] 8 | } -------------------------------------------------------------------------------- /Unity.AssetTools.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c713531c016f4fdeb91b1027c6f70fa 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------