Repository: MarkUnity/AssetAuditor Branch: master Commit: 6a34b5791429 Files: 36 Total size: 145.1 KB Directory structure: gitextract_7qfqj2k7/ ├── .gitattributes ├── .gitignore ├── Assets/ │ ├── Editor/ │ │ ├── AssetAuditor/ │ │ │ ├── Audio/ │ │ │ │ └── DefaultAudio.wav.meta │ │ │ ├── Audio.meta │ │ │ ├── Models/ │ │ │ │ ├── DefaultAvatar.fbx │ │ │ │ └── DefaultAvatar.fbx.meta │ │ │ ├── Models.meta │ │ │ ├── Scripts/ │ │ │ │ ├── AssetAuditTreeElement.cs │ │ │ │ ├── AssetAuditTreeElement.cs.meta │ │ │ │ ├── AssetAuditTreeView.cs │ │ │ │ ├── AssetAuditTreeView.cs.meta │ │ │ │ ├── AssetAuditor.cs │ │ │ │ ├── AssetAuditor.cs.meta │ │ │ │ ├── AssetAuditorNewRuleWindow.cs │ │ │ │ ├── AssetAuditorNewRuleWindow.cs.meta │ │ │ │ ├── AssetAuditorPreferences.cs │ │ │ │ ├── AssetAuditorPreferences.cs.meta │ │ │ │ ├── AssetAuditorUtilities.cs │ │ │ │ ├── AssetAuditorUtilities.cs.meta │ │ │ │ ├── AssetAuditorWindow.cs │ │ │ │ ├── AssetAuditorWindow.cs.meta │ │ │ │ ├── TreeElement.cs │ │ │ │ ├── TreeElement.cs.meta │ │ │ │ ├── TreeElementUtility.cs │ │ │ │ ├── TreeElementUtility.cs.meta │ │ │ │ ├── TreeModel.cs │ │ │ │ ├── TreeModel.cs.meta │ │ │ │ ├── TreeViewWithTreeModel.cs │ │ │ │ └── TreeViewWithTreeModel.cs.meta │ │ │ ├── Scripts.meta │ │ │ ├── Texture/ │ │ │ │ └── DefaultTexture.jpg.meta │ │ │ └── Texture.meta │ │ └── AssetAuditor.meta │ └── Editor.meta ├── LICENSE └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto ================================================ FILE: .gitignore ================================================ /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ /[Bb]uild/ /[Bb]uilds/ /Assets/AssetStoreTools* /Assets/Editor/AssetAuditor/ProxyAssets .DS_Store # Autogenerated VS/MD/Consulo solution and project files ExportedObj/ .consulo/ *.csproj *.unityproj *.sln *.suo *.tmp *.user *.userprefs *.pidb *.booproj *.svd # Unity3D generated meta files *.pidb.meta # Unity3D Generated File On Crash Reports sysinfo.txt # Builds *.apk *.unitypackage ProjectSettings/ Assets/Plugins\.meta Assets/Plugins/Editor\.meta Assets/Plugins/Editor/JetBrains\.meta Assets/Plugins/Editor/JetBrains/RiderAssetPostprocessor\.cs Assets/Plugins/Editor/JetBrains/RiderAssetPostprocessor\.cs\.meta Assets/Plugins/Editor/JetBrains/RiderPlugin\.cs Assets/Plugins/Editor/JetBrains/RiderPlugin\.cs\.meta Assets/Plugins/Editor/JetBrains/Unity3DRider\.cs Assets/Plugins/Editor/JetBrains/Unity3DRider\.cs\.meta \.idea/\.idea\.AssetAuditor/riderModule\.iml \.idea/\.idea\.AssetAuditor/\.idea/workspace\.xml \.idea/\.idea\.AssetAuditor/\.idea/modules\.xml \.idea/\.idea\.AssetAuditor/\.idea/contentModel\.xml Assets/Editor/AssetAuditor/ProxyAssets\.meta ================================================ FILE: Assets/Editor/AssetAuditor/Audio/DefaultAudio.wav.meta ================================================ fileFormatVersion: 2 guid: d13848c7015ad4078abfd3076f74c106 timeCreated: 1502381708 licenseType: Pro AudioImporter: serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 0 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Audio.meta ================================================ fileFormatVersion: 2 guid: fffabc88153284eda8f44478e35d6337 folderAsset: yes timeCreated: 1502381697 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Models/DefaultAvatar.fbx.meta ================================================ fileFormatVersion: 2 guid: 6ca747ad81483413fa1665afa5a9c79a timeCreated: 1502276181 licenseType: Pro ModelImporter: serializedVersion: 21 fileIDToRecycleName: 100000: Chest 100002: //RootNode 100004: Geo_grp 100006: Head 100008: Hips 100010: Jaw 100012: JawEND 100014: Le_Eye_Mesh 100016: LeftArm 100018: LeftCheek 100020: LeftEye 100022: LeftEyelidLower 100024: LeftEyelidUpper 100026: LeftFoot 100028: LeftForeArm 100030: LeftHand 100032: LeftHandIndex1 100034: LeftHandIndex2 100036: LeftHandIndex3 100038: LeftHandMiddle1 100040: LeftHandMiddle2 100042: LeftHandMiddle3 100044: LeftHandPinky1 100046: LeftHandPinky2 100048: LeftHandPinky3 100050: LeftHandRing1 100052: LeftHandRing2 100054: LeftHandRing3 100056: LeftHandThumb1 100058: LeftHandThumb2 100060: LeftHandThumb3 100062: LeftInnerBrow 100064: LeftIOuterBrow 100066: LeftLeg 100068: LeftLipCorner 100070: LeftLipLower 100072: LeftLipUpper 100074: LeftNostril 100076: LeftShoulder 100078: LeftToes 100080: LeftUpLeg 100082: Lw_Teeth_Mesh 100084: Neck 100086: Reference 100088: Ri_Eye_Mesh 100090: RightArm 100092: RightCheek 100094: RightEye 100096: RightEyelidLower 100098: RightEyelidUpper 100100: RightFoot 100102: RightForeArm 100104: RightHand 100106: RightHandIndex1 100108: RightHandIndex2 100110: RightHandIndex3 100112: RightHandMiddle1 100114: RightHandMiddle2 100116: RightHandMiddle3 100118: RightHandPinky1 100120: RightHandPinky2 100122: RightHandPinky3 100124: RightHandRing1 100126: RightHandRing2 100128: RightHandRing3 100130: RightHandThumb1 100132: RightHandThumb2 100134: RightHandThumb3 100136: RightInnerBrow 100138: RightIOuterBrow 100140: RightLeg 100142: RightLipCorner 100144: RightLipLower 100146: RightLipUpper 100148: RightNostril 100150: RightShoulder 100152: RightToes 100154: RightUpLeg 100156: Spine 100158: TongueBack 100160: TongueTip 100162: Tounge_Mesh 100164: Unity_Body_Mesh 100166: Up_Teeth_Mesh 400000: Chest 400002: //RootNode 400004: Geo_grp 400006: Head 400008: Hips 400010: Jaw 400012: JawEND 400014: Le_Eye_Mesh 400016: LeftArm 400018: LeftCheek 400020: LeftEye 400022: LeftEyelidLower 400024: LeftEyelidUpper 400026: LeftFoot 400028: LeftForeArm 400030: LeftHand 400032: LeftHandIndex1 400034: LeftHandIndex2 400036: LeftHandIndex3 400038: LeftHandMiddle1 400040: LeftHandMiddle2 400042: LeftHandMiddle3 400044: LeftHandPinky1 400046: LeftHandPinky2 400048: LeftHandPinky3 400050: LeftHandRing1 400052: LeftHandRing2 400054: LeftHandRing3 400056: LeftHandThumb1 400058: LeftHandThumb2 400060: LeftHandThumb3 400062: LeftInnerBrow 400064: LeftIOuterBrow 400066: LeftLeg 400068: LeftLipCorner 400070: LeftLipLower 400072: LeftLipUpper 400074: LeftNostril 400076: LeftShoulder 400078: LeftToes 400080: LeftUpLeg 400082: Lw_Teeth_Mesh 400084: Neck 400086: Reference 400088: Ri_Eye_Mesh 400090: RightArm 400092: RightCheek 400094: RightEye 400096: RightEyelidLower 400098: RightEyelidUpper 400100: RightFoot 400102: RightForeArm 400104: RightHand 400106: RightHandIndex1 400108: RightHandIndex2 400110: RightHandIndex3 400112: RightHandMiddle1 400114: RightHandMiddle2 400116: RightHandMiddle3 400118: RightHandPinky1 400120: RightHandPinky2 400122: RightHandPinky3 400124: RightHandRing1 400126: RightHandRing2 400128: RightHandRing3 400130: RightHandThumb1 400132: RightHandThumb2 400134: RightHandThumb3 400136: RightInnerBrow 400138: RightIOuterBrow 400140: RightLeg 400142: RightLipCorner 400144: RightLipLower 400146: RightLipUpper 400148: RightNostril 400150: RightShoulder 400152: RightToes 400154: RightUpLeg 400156: Spine 400158: TongueBack 400160: TongueTip 400162: Tounge_Mesh 400164: Unity_Body_Mesh 400166: Up_Teeth_Mesh 2300000: Le_Eye_Mesh 2300002: Ri_Eye_Mesh 3300000: Le_Eye_Mesh 3300002: Ri_Eye_Mesh 4300000: Le_Eye_Mesh 4300002: Ri_Eye_Mesh 4300004: Unity_Body_Mesh 4300006: Up_Teeth_Mesh 4300008: Lw_Teeth_Mesh 4300010: Tounge_Mesh 9500000: //RootNode 13700000: Lw_Teeth_Mesh 13700002: Tounge_Mesh 13700004: Unity_Body_Mesh 13700006: Up_Teeth_Mesh materials: importMaterials: 0 materialName: 0 materialSearch: 1 animations: legacyGenerateAnimations: 4 bakeSimulation: 0 resampleCurves: 1 optimizeGameObjects: 0 motionNodeName: rigImportErrors: rigImportWarnings: animationImportErrors: animationImportWarnings: animationRetargetingWarnings: animationDoRetargetingWarnings: 0 animationCompression: 3 animationRotationError: 0.5 animationPositionError: 0.5 animationScaleError: 0.5 animationWrapMode: 0 extraExposedTransformPaths: [] extraUserProperties: [] clipAnimations: [] isReadable: 1 meshes: lODScreenPercentages: [] globalScale: 1 meshCompression: 0 addColliders: 0 importVisibility: 1 importBlendShapes: 1 importCameras: 1 importLights: 1 swapUVChannels: 0 generateSecondaryUV: 0 useFileUnits: 1 optimizeMeshForGPU: 1 keepQuads: 0 weldVertices: 1 secondaryUVAngleDistortion: 8 secondaryUVAreaDistortion: 15.000001 secondaryUVHardAngle: 88 secondaryUVPackMargin: 4 useFileScale: 1 tangentSpace: normalSmoothAngle: 60 normalImportMode: 0 tangentImportMode: 3 normalCalculationMode: 4 importAnimation: 1 copyAvatar: 0 humanDescription: serializedVersion: 2 human: - boneName: Hips humanName: Hips limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftUpLeg humanName: LeftUpperLeg limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightUpLeg humanName: RightUpperLeg limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftLeg humanName: LeftLowerLeg limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightLeg humanName: RightLowerLeg limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftFoot humanName: LeftFoot limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightFoot humanName: RightFoot limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: Spine humanName: Spine limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: Chest humanName: Chest limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: Neck humanName: Neck limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: Head humanName: Head limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftShoulder humanName: LeftShoulder limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightShoulder humanName: RightShoulder limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftArm humanName: LeftUpperArm limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightArm humanName: RightUpperArm limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftForeArm humanName: LeftLowerArm limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightForeArm humanName: RightLowerArm limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHand humanName: LeftHand limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHand humanName: RightHand limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftToes humanName: LeftToes limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightToes humanName: RightToes limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftEye humanName: LeftEye limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightEye humanName: RightEye limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: Jaw humanName: Jaw limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandThumb1 humanName: Left Thumb Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandThumb2 humanName: Left Thumb Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandThumb3 humanName: Left Thumb Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandIndex1 humanName: Left Index Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandIndex2 humanName: Left Index Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandIndex3 humanName: Left Index Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandMiddle1 humanName: Left Middle Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandMiddle2 humanName: Left Middle Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandMiddle3 humanName: Left Middle Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandRing1 humanName: Left Ring Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandRing2 humanName: Left Ring Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandRing3 humanName: Left Ring Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandPinky1 humanName: Left Little Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandPinky2 humanName: Left Little Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: LeftHandPinky3 humanName: Left Little Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandThumb1 humanName: Right Thumb Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandThumb2 humanName: Right Thumb Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandThumb3 humanName: Right Thumb Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandIndex1 humanName: Right Index Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandIndex2 humanName: Right Index Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandIndex3 humanName: Right Index Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandMiddle1 humanName: Right Middle Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandMiddle2 humanName: Right Middle Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandMiddle3 humanName: Right Middle Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandRing1 humanName: Right Ring Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandRing2 humanName: Right Ring Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandRing3 humanName: Right Ring Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandPinky1 humanName: Right Little Proximal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandPinky2 humanName: Right Little Intermediate limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 - boneName: RightHandPinky3 humanName: Right Little Distal limit: min: {x: 0, y: 0, z: 0} max: {x: 0, y: 0, z: 0} value: {x: 0, y: 0, z: 0} length: 0 modified: 0 skeleton: - name: DefaultAvatar(Clone) parentName: position: {x: 0, y: 0, z: 0} rotation: {x: 0, y: 0, z: 0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Geo_grp parentName: DefaultAvatar(Clone) position: {x: -0, y: 0, z: 0} rotation: {x: 0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Lw_Teeth_Mesh parentName: Geo_grp position: {x: -0, y: 0, z: 0} rotation: {x: 0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Tounge_Mesh parentName: Geo_grp position: {x: -0, y: 0, z: 0} rotation: {x: 0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Unity_Body_Mesh parentName: Geo_grp position: {x: -0, y: 0, z: 0} rotation: {x: 0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Up_Teeth_Mesh parentName: Geo_grp position: {x: -0, y: 0, z: 0} rotation: {x: 0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Reference parentName: DefaultAvatar(Clone) position: {x: -0, y: 0, z: 0} rotation: {x: 0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Hips parentName: Reference position: {x: -0, y: 0.963794, z: -0.023506777} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Spine parentName: Hips position: {x: -0, y: 0.092263184, z: 0.015771331} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Chest parentName: Spine position: {x: -0, y: 0.16254029, z: 0.021850722} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Neck parentName: Chest position: {x: -0, y: 0.2357239, z: -0.032413255} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Head parentName: Neck position: {x: -0, y: 0.1063558, z: 0.0113267815} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftEye parentName: Head position: {x: -0.020848233, y: 0.0825027, z: 0.055427432} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Le_Eye_Mesh parentName: LeftEye position: {x: -0.0016841149, y: 0.00040588377, z: 0.0053181886} rotation: {x: 0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightEye parentName: Head position: {x: 0.020849999, y: 0.08250283, z: 0.0554274} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Ri_Eye_Mesh parentName: RightEye position: {x: 0.0016618776, y: 0.00038345336, z: 0.0053166724} rotation: {x: 0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftLipUpper parentName: Head position: {x: -0.014501322, y: -0.005111811, z: 0.09461884} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftNostril parentName: Head position: {x: -0.0179, y: 0.026312828, z: 0.0908674} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftCheek parentName: Head position: {x: -0.054244027, y: 0.03370195, z: 0.0594304} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftEyelidUpper parentName: Head position: {x: -0.034406897, y: 0.10060814, z: 0.08020531} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftEyelidLower parentName: Head position: {x: -0.035618957, y: 0.06507366, z: 0.07623474} rotation: {x: -0.0348995, y: -0, z: -0, w: 0.99939084} scale: {x: 1, y: 1.0000005, z: 1.0000005} - name: LeftInnerBrow parentName: Head position: {x: -0.012062691, y: 0.118765265, z: 0.093466826} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftIOuterBrow parentName: Head position: {x: -0.05503987, y: 0.11482529, z: 0.061777398} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightInnerBrow parentName: Head position: {x: 0.012062687, y: 0.118765265, z: 0.093466826} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightIOuterBrow parentName: Head position: {x: 0.055040002, y: 0.11482283, z: 0.061777398} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightEyelidUpper parentName: Head position: {x: 0.03441, y: 0.10061283, z: 0.08020739} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightEyelidLower parentName: Head position: {x: 0.03562, y: 0.06507283, z: 0.0762374} rotation: {x: -0.0348995, y: -0, z: -0, w: 0.99939084} scale: {x: 1, y: 1.0000005, z: 1.0000005} - name: RightCheek parentName: Head position: {x: 0.054239996, y: 0.033702828, z: 0.0594274} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightNostril parentName: Head position: {x: 0.0179, y: 0.026308905, z: 0.09087062} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightLipUpper parentName: Head position: {x: 0.014501322, y: -0.0051071714, z: 0.094617404} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: Jaw parentName: Head position: {x: -0, y: 0.0111267585, z: 0.010327543} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftLipLower parentName: Jaw position: {x: -0.014250817, y: -0.02168876, z: 0.08224063} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: JawEND parentName: Jaw position: {x: -0, y: -0.04828876, z: 0.07185171} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightLipLower parentName: Jaw position: {x: 0.014250817, y: -0.02168876, z: 0.082238786} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightLipCorner parentName: Jaw position: {x: 0.03284, y: -0.01657876, z: 0.066118784} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftLipCorner parentName: Jaw position: {x: -0.032843262, y: -0.01657876, z: 0.066121764} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: TongueBack parentName: Jaw position: {x: -0, y: -0.022869369, z: 0.010095409} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: TongueTip parentName: TongueBack position: {x: -0, y: -0.00040944412, z: 0.0282273} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightShoulder parentName: Chest position: {x: 0.03832851, y: 0.19217674, z: -0.017063085} rotation: {x: 0.228672, y: 0.9715822, z: -0.014005679, w: -0.059507377} scale: {x: 1.0000013, y: 1, z: 1.000001} - name: RightArm parentName: RightShoulder position: {x: -0.08357552, y: 0.0360957, z: -0.000000051557407} rotation: {x: -0.21105212, y: -0.97439414, z: 0.017311702, w: -0.07558775} scale: {x: 1.0000001, y: 1.0000002, z: 1} - name: RightForeArm parentName: RightArm position: {x: 0.25342825, y: 0.006011353, z: -0.016704524} rotation: {x: -0.0006165215, y: 0.022078624, z: -0.01607026, w: 0.99962693} scale: {x: 0.99999994, y: 0.99999976, z: 1.0000001} - name: RightHand parentName: RightForeArm position: {x: 0.2453737, y: 0.021641772, z: 0.005550465} rotation: {x: 8.1490714e-10, y: -0, z: 0.021413714, w: 0.99977076} scale: {x: 1.0000001, y: 1.0000001, z: 1} - name: RightHandThumb1 parentName: RightHand position: {x: 0.014684916, y: -0.011104942, z: 0.025858095} rotation: {x: -0.012817442, y: -0.0032555843, z: 0.031460524, w: 0.99941754} scale: {x: 1.0000004, y: 1, z: 1} - name: RightHandThumb2 parentName: RightHandThumb1 position: {x: 0.016374, y: -0.00529, z: 0.02349136} rotation: {x: -0.026063675, y: -0.09668989, z: -0.003606537, w: 0.9949667} scale: {x: 1.0000002, y: 1.0000001, z: 1} - name: RightHandThumb3 parentName: RightHandThumb2 position: {x: 0.02546, y: -0.00764, z: 0.020833} rotation: {x: 0.000000012019879, y: 9.3132235e-10, z: -0.00000000372546, w: 1} scale: {x: 1.0000002, y: 0.9999999, z: 1} - name: RightHandIndex1 parentName: RightHand position: {x: 0.0747695, y: -0.0012430536, z: 0.034344498} rotation: {x: -0.0021189174, y: 0.080257446, z: 0.017538162, w: 0.9966176} scale: {x: 1, y: 0.99999994, z: 1.0000001} - name: RightHandIndex2 parentName: RightHandIndex1 position: {x: 0.0370584, y: 0.00072612107, z: 0.014538894} rotation: {x: -0.0033265573, y: 0.015931673, z: 0.060633723, w: 0.99802744} scale: {x: 1.0000002, y: 1.0000002, z: 1} - name: RightHandIndex3 parentName: RightHandIndex2 position: {x: 0.025225038, y: -0.0049664653, z: 0.011012146} rotation: {x: -0, y: -0, z: -9.458742e-10, w: 1} scale: {x: 1.0000001, y: 1.0000004, z: 1.0000001} - name: RightHandMiddle1 parentName: RightHand position: {x: 0.075647645, y: 0.0047914027, z: 0.011853182} rotation: {x: -0.0007688713, y: 0.03332108, z: 0.020907532, w: 0.99922574} scale: {x: 0.9999999, y: 0.99999994, z: 1} - name: RightHandMiddle2 parentName: RightHandMiddle1 position: {x: 0.043809064, y: 0.00019418815, z: 0.006454936} rotation: {x: -0.004130707, y: -0.033512067, z: 0.07612222, w: 0.99652666} scale: {x: 1.0000001, y: 1, z: 0.99999994} - name: RightHandMiddle3 parentName: RightHandMiddle2 position: {x: 0.03307247, y: -0.007547537, z: 0.0016898462} rotation: {x: -0, y: -0, z: -0.0000000032414385, w: 1} scale: {x: 1, y: 1.0000001, z: 1} - name: RightHandRing1 parentName: RightHand position: {x: 0.070598476, y: 0.0024570965, z: -0.009821458} rotation: {x: 0.0007108786, y: -0.054342177, z: 0.03494465, w: 0.99791056} scale: {x: 1.0000001, y: 0.99999994, z: 1} - name: RightHandRing2 parentName: RightHandRing1 position: {x: 0.042887185, y: -0.0013753821, z: -0.004945858} rotation: {x: 0.0004845856, y: -0.021289792, z: 0.06986199, w: 0.99732935} scale: {x: 1.0000002, y: 1.0000001, z: 0.99999964} - name: RightHandRing3 parentName: RightHandRing2 position: {x: 0.029500604, y: -0.0076929354, z: -0.004622256} rotation: {x: -0, y: -0, z: 0.0000000013351378, w: 1} scale: {x: 1, y: 0.9999999, z: 0.9999998} - name: RightHandPinky1 parentName: RightHand position: {x: 0.06680334, y: -0.0019941088, z: -0.030756146} rotation: {x: 0.0031760908, y: -0.19200507, z: 0.0451148, w: 0.9803513} scale: {x: 1.0000001, y: 1, z: 0.99999994} - name: RightHandPinky2 parentName: RightHandPinky1 position: {x: 0.028530842, y: -0.001397143, z: -0.011623796} rotation: {x: -0.00017062707, y: -0.009661323, z: -0.0053624124, w: 0.999939} scale: {x: 1, y: 1.0000002, z: 1.0000001} - name: RightHandPinky3 parentName: RightHandPinky2 position: {x: 0.02142686, y: -0.00055350893, z: -0.008516608} rotation: {x: -0, y: 9.3132235e-10, z: -4.2518867e-11, w: 1} scale: {x: 1, y: 1.0000002, z: 1.0000001} - name: LeftShoulder parentName: Chest position: {x: -0.038243506, y: 0.19217809, z: -0.017063085} rotation: {x: -0.01400671, y: -0.05950682, z: 0.22868991, w: 0.97157794} scale: {x: 1, y: 0.99999994, z: 1} - name: LeftArm parentName: LeftShoulder position: {x: -0.08357477, y: 0.036097575, z: 2.8865798e-17} rotation: {x: 0.009464391, y: 0.043691695, z: -0.22304244, w: 0.97378314} scale: {x: 1, y: 1.0000001, z: 0.9999998} - name: LeftForeArm parentName: LeftArm position: {x: -0.2540493, y: 0, z: 0} rotation: {x: -0.0006165216, y: 0.022078618, z: -0.016070211, w: 0.99962693} scale: {x: 1, y: 1, z: 1.0000002} - name: LeftHand parentName: LeftForeArm position: {x: -0.24638927, y: 0, z: 0} rotation: {x: 0.0000000034924592, y: 0.0000000074505797, z: -0.02141356, w: 0.9997707} scale: {x: 0.9999998, y: 1.0000002, z: 0.99999994} - name: LeftHandThumb1 parentName: LeftHand position: {x: -0.014231241, y: -0.012377825, z: 0.025531668} rotation: {x: -0.012314987, y: -0.008526002, z: 0.012583703, w: 0.9998086} scale: {x: 1.0000002, y: 0.99999994, z: 0.99999994} - name: LeftHandThumb2 parentName: LeftHandThumb1 position: {x: -0.016374, y: -0.00529, z: 0.023491409} rotation: {x: -0.026063059, y: 0.09668942, z: 0.0036069017, w: 0.9949668} scale: {x: 1.0000004, y: 1.0000001, z: 0.9999999} - name: LeftHandThumb3 parentName: LeftHandThumb2 position: {x: -0.02546, y: -0.00764, z: 0.020833} rotation: {x: 0.000000042738975, y: 0.0000000037252903, z: 0.0000000018620199, w: 1} scale: {x: 1.0000001, y: 0.9999999, z: 0.9999999} - name: LeftHandIndex1 parentName: LeftHand position: {x: -0.0751258, y: -0.0078414045, z: 0.032652643} rotation: {x: -0.0021189197, y: 0.08025745, z: 0.01753819, w: 0.9966176} scale: {x: 1.0000002, y: 0.9999999, z: 1.0000004} - name: LeftHandIndex2 parentName: LeftHandIndex1 position: {x: -0.03979728, y: 0.000049808405, z: 0.0011857504} rotation: {x: 0.000501889, y: 0.015471162, z: 0.040411938, w: 0.99906325} scale: {x: 1, y: 0.99999994, z: 0.99999976} - name: LeftHandIndex3 parentName: LeftHandIndex2 position: {x: -0.027968477, y: -0.000000006281224, z: -0.00000005171866} rotation: {x: -5.8207654e-11, y: -0, z: 0.000000002835804, w: 1} scale: {x: 0.99999994, y: 1.0000001, z: 1.0000001} - name: LeftHandMiddle1 parentName: LeftHand position: {x: -0.076023825, y: -0.0018851344, z: 0.010141229} rotation: {x: -0.00076887483, y: 0.033321086, z: 0.020907529, w: 0.9992257} scale: {x: 1.0000001, y: 0.9999999, z: 1.0000001} - name: LeftHandMiddle2 parentName: LeftHandMiddle1 position: {x: -0.044280436, y: 0.000004798874, z: -0.00042540013} rotation: {x: -0.0013621139, y: -0.01915252, z: 0.037885293, w: 0.99909765} scale: {x: 1.0000002, y: 1, z: 0.99999994} - name: LeftHandMiddle3 parentName: LeftHandMiddle2 position: {x: -0.033964828, y: -0.000000012197929, z: 0.0000000037564827} rotation: {x: -0, y: -0, z: 4.838512e-10, w: 1} scale: {x: 1, y: 1.0000001, z: 1.0000001} - name: LeftHandRing1 parentName: LeftHand position: {x: -0.07030211, y: -0.0037453093, z: -0.011411792} rotation: {x: -0.00032414307, y: 0.011598279, z: 0.024737397, w: 0.99962664} scale: {x: 0.9999999, y: 0.9999998, z: 1.0000001} - name: LeftHandRing2 parentName: LeftHandRing1 position: {x: -0.043135457, y: -0.000020882308, z: -0.0022351784} rotation: {x: -0.0012033589, y: -0.023113519, z: 0.040983878, w: 0.9988918} scale: {x: 0.99999994, y: 1.0000001, z: 0.99999994} - name: LeftHandRing3 parentName: LeftHandRing2 position: {x: -0.030835565, y: 7.710497e-11, z: -0.00000001649327} rotation: {x: -0, y: -0, z: 0.0000000013897075, w: 1} scale: {x: 0.99999994, y: 1, z: 1} - name: LeftHandPinky1 parentName: LeftHand position: {x: -0.06565995, y: -0.007825106, z: -0.032251246} rotation: {x: -0.00091239274, y: 0.012161445, z: 0.021224977, w: 0.9997003} scale: {x: 1.0000001, y: 0.9999999, z: 1.0000002} - name: LeftHandPinky2 parentName: LeftHandPinky1 position: {x: -0.030805448, y: -0.000030874573, z: -0.0014480775} rotation: {x: -0.00017062433, y: -0.009661346, z: -0.00536237, w: 0.999939} scale: {x: 0.99999994, y: 1.0000001, z: 1.0000001} - name: LeftHandPinky3 parentName: LeftHandPinky2 position: {x: -0.023064027, y: -0.0000064025808, z: 0.000000018332095} rotation: {x: -0, y: 9.3132235e-10, z: -1.9895191e-11, w: 1} scale: {x: 0.9999999, y: 1, z: 0.9999999} - name: RightUpLeg parentName: Hips position: {x: 0.075449534, y: -0.04566399, z: 0} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightLeg parentName: RightUpLeg position: {x: 0.020550467, y: -0.40913, z: 0.0071713654} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightFoot parentName: RightLeg position: {x: 0.0051529994, y: -0.4231559, z: -0.012032089} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: RightToes parentName: RightFoot position: {x: 0.007487, y: -0.0731673, z: 0.1454275} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftUpLeg parentName: Hips position: {x: -0.0754495, y: -0.04566402, z: 0} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftLeg parentName: LeftUpLeg position: {x: -0.020550499, y: -0.40912998, z: 0.0071713654} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftFoot parentName: LeftLeg position: {x: -0.0051529994, y: -0.4231559, z: -0.012032089} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} - name: LeftToes parentName: LeftFoot position: {x: -0.007487, y: -0.0731673, z: 0.14542712} rotation: {x: -0, y: -0, z: -0, w: 1} scale: {x: 1, y: 1, z: 1} armTwist: 0.5 foreArmTwist: 0.5 upperLegTwist: 0.5 legTwist: 0.5 armStretch: 0.05 legStretch: 0.05 feetSpacing: 0 rootMotionBoneName: rootMotionBoneRotation: {x: 0, y: 0, z: 0, w: 1} hasTranslationDoF: 0 hasExtraRoot: 1 skeletonHasParents: 1 lastHumanDescriptionAvatarSource: {instanceID: 0} animationType: 3 humanoidOversampling: 1 additionalBone: 0 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Models.meta ================================================ fileFormatVersion: 2 guid: d1003e9744d57413fad5f07821ef2136 folderAsset: yes timeCreated: 1502276220 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditTreeElement.cs ================================================ using System; namespace UnityAssetAuditor { [Serializable] public class AssetAuditTreeElement : TreeElement { public bool isAsset; public string projectPath; public AssetAuditor.AssetType assetType; internal enum AssetType { Asset, Directory } public bool conforms; public AssetAuditTreeElement (string name,string _projectPath, int depth, int id, bool _isAsset, bool _conforms , AssetAuditor.AssetType _assetType) : base (name, depth, id) { isAsset = _isAsset; projectPath = _projectPath; conforms = _conforms; assetType = _assetType; } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditTreeElement.cs.meta ================================================ fileFormatVersion: 2 guid: f1bafad60d37c484dab3ce5b74c04a6a timeCreated: 1472024032 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditTreeView.cs ================================================ using System; using System.Collections.Generic; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; using UnityEngine.Assertions; namespace UnityAssetAuditor { public class AssetAuditTreeView : TreeViewWithTreeModel { private readonly Action _ruleFixEvent; const float kRowHeights = 20f; const float kIconWidth = 18f; public bool showControls { get; set; } // All columns enum MyColumns { Name, Conforms } public static void TreeToList(TreeViewItem root, IList result) { if (root == null) throw new NullReferenceException("root"); if (result == null) throw new NullReferenceException("result"); result.Clear(); if (root.children == null) return; Stack stack = new Stack(); for (int i = root.children.Count - 1; i >= 0; i--) stack.Push(root.children[i]); while (stack.Count > 0) { TreeViewItem current = stack.Pop(); result.Add(current); if (current.hasChildren && current.children[0] != null) { for (int i = current.children.Count - 1; i >= 0; i--) { stack.Push(current.children[i]); } } } } public AssetAuditTreeView(TreeViewState state, MultiColumnHeader multicolumnHeader, TreeModel model , Action ruleFixEvent) : base(state, multicolumnHeader, model) { _ruleFixEvent = ruleFixEvent; // Custom setup rowHeight = kRowHeights; columnIndexForTreeFoldouts = 0; showAlternatingRowBackgrounds = true; showBorder = true; customFoldoutYOffset = (kRowHeights - EditorGUIUtility.singleLineHeight) * 0.5f; // center foldout in the row since we also center content. See RowGUI extraSpaceBeforeIconAndLabel = kIconWidth; Reload(); } // Note we We only build the visible rows, only the backend has the full tree information. // The treeview only creates info for the row list. protected override IList BuildRows(TreeViewItem root) { var rows = base.BuildRows(root); return rows; } protected override void RowGUI(RowGUIArgs args) { var item = (TreeViewItem)args.item; for (int i = 0; i < args.GetNumVisibleColumns(); ++i) { CellGUI(args.GetCellRect(i), item, (MyColumns)args.GetColumn(i), ref args); } } void CellGUI(Rect cellRect, TreeViewItem item, MyColumns column, ref RowGUIArgs args) { // Center cell rect vertically (makes it easier to place controls, icons etc in the cells) CenterRectUsingSingleLineHeight(ref cellRect); switch (column) { case MyColumns.Name: { // Do toggle Rect iconRect = cellRect; iconRect.x += GetContentIndent(item); iconRect.width = kIconWidth; Texture2D iconTex = null; /* if (item.data.isAsset) { if (!string.IsNullOrEmpty(item.data.projectPath)) { iconTex = AssetPreview.GetMiniThumbnail( AssetDatabase.LoadAssetAtPath( item.data.projectPath.Substring(Application.dataPath.Length - 6))); } }*/ switch (item.data.assetType) { case AssetAuditor.AssetType.Texture: iconTex = AssetPreview.GetMiniThumbnail( AssetDatabase.LoadAssetAtPath( item.data.projectPath)); break; case AssetAuditor.AssetType.Model: iconTex = EditorGUIUtility.FindTexture("PrefabModel Icon"); break; case AssetAuditor.AssetType.Audio: iconTex = EditorGUIUtility.FindTexture("AudioClip Icon"); break; case AssetAuditor.AssetType.Folder: iconTex = EditorGUIUtility.FindTexture("Folder Icon"); break; default: throw new ArgumentOutOfRangeException(); } if (iconRect.xMax < cellRect.xMax) { GUI.DrawTexture(iconRect, iconTex); } // Default icon and label args.rowRect = cellRect; base.RowGUI(args); } break; case MyColumns.Conforms: var conforms = item.data.conforms; if (item.data.isAsset) { if (conforms) { GUI.Label(cellRect , " Settings OK "); } else { if (GUI.Button(cellRect, "Fix")) { _ruleFixEvent.Invoke(item.data); } } } break; } } // Misc //-------- protected override bool CanMultiSelect(TreeViewItem item) { return true; } public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState(float treeViewWidth) { var columns = new[] { new MultiColumnHeaderState.Column { headerContent = new GUIContent("Name"), headerTextAlignment = TextAlignment.Left, width = 0, // adjusted below minWidth = 60, autoResize = true, allowToggleVisibility = false, canSort = false }, new MultiColumnHeaderState.Column { headerContent = new GUIContent("Conforms"), headerTextAlignment = TextAlignment.Left, width = 150, // adjusted below minWidth = 150, autoResize = true, allowToggleVisibility = false, canSort = false }, }; Assert.AreEqual(columns.Length, Enum.GetValues(typeof(MyColumns)).Length, "Number of columns should match number of enum values: You probably forgot to update one of them."); // Set name column width (flexible) int nameColumn = (int)MyColumns.Name; columns[nameColumn].width = treeViewWidth - GUI.skin.verticalScrollbar.fixedWidth; for (int i = 0; i < columns.Length; ++i) if (i != nameColumn) columns[nameColumn].width -= columns[i].width; if (columns[nameColumn].width < 60f) columns[nameColumn].width = 60f; var state = new MultiColumnHeaderState(columns); return state; } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditTreeView.cs.meta ================================================ fileFormatVersion: 2 guid: e00d20ba7c1f4d446a34ea24d8b82c4e timeCreated: 1464348051 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditor.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; using UnityEngine.Collections; namespace UnityAssetAuditor { public delegate void OnQueueComplete(); public class AssetAuditor { public static IEnumerable currentEnumerable; public static IEnumerator currentEnumerator; public static Queue> enumerableQueue; public static event OnQueueComplete queueComplete; private static List foundAssets; public enum WildCardMatchType { NameContains, Regex } public enum AssetType { Texture, Model, Audio, Folder } [Serializable] public struct AssetRule { public string RuleName; public WildCardMatchType WildCardMatchType; public string WildCard; public string AssetGuid; public AssetType assetType; public bool SelectiveMode; public List SelectiveProperties; } static AssetAuditor() { EditorApplication.update += TickEnumerator; enumerableQueue = new Queue>(); } private static void TickEnumerator() { if (currentEnumerable == null) { return; } if (!currentEnumerator.MoveNext()) { currentEnumerable = null; currentEnumerator = null; if (enumerableQueue.Count == 0) { if (queueComplete != null) queueComplete.Invoke(); } else { currentEnumerable = enumerableQueue.Dequeue(); currentEnumerator = currentEnumerable.GetEnumerator(); } } } public static float GetProgress() { return currentEnumerator != null ? currentEnumerator.Current : 0f; } public static void AddEnumerator(IEnumerable enumerator) { enumerableQueue.Enqueue(enumerator); if (currentEnumerable == null) { currentEnumerable = enumerableQueue.Dequeue(); currentEnumerator = currentEnumerable.GetEnumerator(); } } public static void ClearQueue() { enumerableQueue.Clear(); currentEnumerable = null; currentEnumerator = null; } public static Type TypeFromAssetType(AssetType assetType) { switch (assetType) { case AssetType.Texture: return typeof(Texture); case AssetType.Model: return typeof(GameObject); case AssetType.Audio: return typeof(AudioClip); case AssetType.Folder: return null; default: throw new ArgumentOutOfRangeException("assetType", assetType, null); } } public static string[] GetAffectedAssets() { return foundAssets.ToArray(); } public static void UpdateAffectedAssets(AssetRule assetRule) { foundAssets = new List(); switch (assetRule.WildCardMatchType) { case WildCardMatchType.NameContains: ClearQueue(); AddEnumerator(DoNameContainsSearch(foundAssets, assetRule)); break; case WildCardMatchType.Regex: ClearQueue(); AddEnumerator(DoRegexNameSearch(foundAssets, assetRule)); break; default: throw new ArgumentOutOfRangeException(); } } private static IEnumerable DoRegexNameSearch(List foundAssets, AssetRule assetRule) { string type = ""; switch (assetRule.assetType) { case AssetType.Texture: type = "Texture"; break; case AssetType.Model: type = "GameObject"; break; case AssetType.Audio: type = "AudioClip"; break; case AssetType.Folder: break; default: throw new ArgumentOutOfRangeException(); } foreach (var asset in AssetDatabase.FindAssets("t:" + type)) { string guidToAssetPath = AssetDatabase.GUIDToAssetPath(asset); if (guidToAssetPath.Contains(AssetAuditorPreferences.ProxyAssetsDirectory)) continue; if (Regex.IsMatch(guidToAssetPath, assetRule.WildCard)) { foundAssets.Add(guidToAssetPath); } } AssetAuditor.foundAssets = foundAssets; yield return 1f; } public static IEnumerable DoNameContainsSearch(List foundAssets, AssetRule assetRule) { string type = ""; switch (assetRule.assetType) { case AssetType.Texture: type = "Texture"; break; case AssetType.Model: type = "GameObject"; break; case AssetType.Audio: type = "AudioClip"; break; case AssetType.Folder: break; default: throw new ArgumentOutOfRangeException(); } foreach (string findAsset in AssetDatabase.FindAssets("t:" + type + " " + assetRule.WildCard)) { string guidToAssetPath = AssetDatabase.GUIDToAssetPath(findAsset); if(guidToAssetPath.Contains(AssetAuditorPreferences.ProxyAssetsDirectory))continue; foundAssets.Add(guidToAssetPath); } AssetAuditor.foundAssets = foundAssets; yield return 1f; } public static IEnumerable GatherAssetRules(List _assetRules , List _assetRuleNames ) { int progress = 0; string[] foundAssets = AssetDatabase.FindAssets("", new[] {AssetAuditorPreferences.ProxyAssetsDirectory}); // get all assets in the proxyassets folder foreach (string asset in foundAssets) { string guidToAssetPath = AssetDatabase.GUIDToAssetPath(asset); AssetImporter assetImporter = AssetImporter.GetAtPath(guidToAssetPath); AssetRule ar = new AssetRule(); ar = JsonUtility.FromJson(assetImporter.userData); _assetRules.Add(ar); progress++; yield return progress / (float)foundAssets.Length; } _assetRuleNames.Clear(); foreach (AssetRule assetRule in _assetRules) { _assetRuleNames.Add(assetRule.RuleName); } yield return 1f; } public static IEnumerable GatherData(AssetRule assetRule , List elements, int selectedSelective) { int id = -1; // get the affected assets string[] affectedAssets = GetAffectedAssets(); if(affectedAssets == null) Debug.Log(" null affected assets"); // build the directory tree from the affected assets elements.Add(new AssetAuditTreeElement("Root", "", -1, 0, false, false, AssetType.Folder )); // early out if there are no affected assets if (affectedAssets.Length == 0) { Debug.Log("no affected assets "); if (queueComplete != null) queueComplete.Invoke(); yield break; } AssetAuditTreeElement assetsFolder = new AssetAuditTreeElement("Assets", "Assets", 0, id++, false, false, AssetType.Folder ); // add the project root "Assets" folder elements.Add(assetsFolder); if (assetsFolder.children == null) assetsFolder.children = new List(); float progress = 0f; foreach (var affectedAsset in affectedAssets) { // split the path string path = affectedAsset.Substring(7); var strings = path.Split(new[]{'/'}, StringSplitOptions.None); string projectPath = "Assets"; // the first entries have lower depth for(int i = 0 ; i < strings.Length ; i++) { projectPath += "/" + strings[i]; // the last element is the asset itself if (i == strings.Length-1) { var result = CheckAffectedAsset(affectedAsset, assetRule, selectedSelective); var element = new AssetAuditTreeElement(strings[i], projectPath, i + 1, id + 1, true, result, assetRule.assetType); elements.Add(element); id++; } else if (!elements.Exists(element => element.name == strings[i] && element.projectPath == projectPath)) { var assetAuditTreeElement = new AssetAuditTreeElement(strings[i], projectPath, i+1 , id+1, false, false, AssetType.Folder); elements.Add(assetAuditTreeElement); id++; } } progress += 1f; yield return progress / affectedAssets.Length; } } private static bool CheckAffectedAsset(string affectedAsset, AssetRule assetRule, int selectedSelective) { SerializedObject assetImporterSO = null; SerializedObject ruleImporterSO = null; if (TypeFromAssetType(assetRule.assetType) == typeof(Texture)) { TextureImporter assetimporter = AssetImporter.GetAtPath(affectedAsset) as TextureImporter; // this may happen (e.g. render texture) if (assetimporter == null) return false; TextureImporter ruleimporter = AssetImporter.GetAtPath(AssetDatabase.GUIDToAssetPath(assetRule.AssetGuid)) as TextureImporter; if (assetimporter.GetInstanceID() == ruleimporter.GetInstanceID()) return false; // this shouldnt happen but is a nice failsafe assetImporterSO = new SerializedObject(assetimporter); ruleImporterSO = new SerializedObject(ruleimporter); } if (TypeFromAssetType(assetRule.assetType) == typeof(GameObject)) { ModelImporter assetimporter = AssetImporter.GetAtPath(affectedAsset) as ModelImporter; ModelImporter ruleimporter = AssetImporter.GetAtPath(AssetDatabase.GUIDToAssetPath(assetRule.AssetGuid)) as ModelImporter; if (assetimporter.GetInstanceID() == ruleimporter.GetInstanceID()) return false; // this shouldnt happen but is a nice failsafe assetImporterSO = new SerializedObject(assetimporter); ruleImporterSO = new SerializedObject(ruleimporter); } if (TypeFromAssetType(assetRule.assetType) == typeof(AudioClip)) { AudioImporter assetimporter = AssetImporter.GetAtPath(affectedAsset) as AudioImporter; AudioImporter ruleimporter = AssetImporter.GetAtPath(AssetDatabase.GUIDToAssetPath(assetRule.AssetGuid)) as AudioImporter; if (assetimporter.GetInstanceID() == ruleimporter.GetInstanceID()) return false; // this shouldnt happen but is a nice failsafe assetImporterSO = new SerializedObject(assetimporter); ruleImporterSO = new SerializedObject(ruleimporter); } if (assetImporterSO == null || ruleImporterSO == null) return false; // TODO log message here if (!assetRule.SelectiveMode || assetRule.SelectiveProperties.Count <= 0) { return CompareSerializedObject(assetImporterSO, ruleImporterSO); } string property = assetRule.SelectiveProperties[selectedSelective]; string realname = GetPropertyNameFromDisplayName(assetImporterSO, property); SerializedProperty foundAssetSP = assetImporterSO.FindProperty(realname); SerializedProperty assetRuleSP = ruleImporterSO.FindProperty(realname); return CompareSerializedProperty(foundAssetSP, assetRuleSP); } public static bool CompareSerializedProperty(SerializedProperty foundAssetSp, SerializedProperty assetRuleSp) { if (foundAssetSp.propertyPath == "m_FileIDToRecycleName" || foundAssetSp.propertyPath == "m_UserData") return true; // the file ids will always be different so we should skip over this. The user data is where the asset rule info is stored so we dont want to check that switch (foundAssetSp.propertyType) { case SerializedPropertyType.Generic: // this eventually goes down through the data until we get a useable value to compare SerializedProperty foundAssetsSPCopy = foundAssetSp.Copy(); SerializedProperty assetRuleSPCopy = assetRuleSp.Copy(); // we must get the next sibling SerializedProperties to know when to stop the comparison SerializedProperty nextSiblingAssetSP = foundAssetSp.Copy (); SerializedProperty nextSiblingRuleSP = assetRuleSp.Copy (); nextSiblingAssetSP.NextVisible (false); nextSiblingRuleSP.NextVisible (false); bool asset, found; do { if (assetRuleSPCopy.propertyType != foundAssetsSPCopy.propertyType) { return false; // mistmatch in types different serialisation } if (assetRuleSPCopy.propertyType != SerializedPropertyType.Generic) { if (!CompareSerializedProperty(foundAssetsSPCopy, assetRuleSPCopy)) { return false; } } asset = foundAssetsSPCopy.NextVisible(true); found = assetRuleSPCopy.NextVisible(true); } while (found && asset && !SerializedProperty.EqualContents (foundAssetsSPCopy, nextSiblingAssetSP) && !SerializedProperty.EqualContents (assetRuleSPCopy, nextSiblingRuleSP)); return true; case SerializedPropertyType.Integer: return foundAssetSp.intValue == assetRuleSp.intValue; case SerializedPropertyType.Boolean: return foundAssetSp.boolValue == assetRuleSp.boolValue; case SerializedPropertyType.Float: return foundAssetSp.floatValue == assetRuleSp.floatValue; case SerializedPropertyType.String: return foundAssetSp.stringValue == assetRuleSp.stringValue; case SerializedPropertyType.Color: return foundAssetSp.colorValue == assetRuleSp.colorValue; case SerializedPropertyType.ObjectReference: return true;// this is weird on models imports and needs a solution as the exposed transforms reference the model itself case SerializedPropertyType.LayerMask: break; case SerializedPropertyType.Enum: return foundAssetSp.enumValueIndex == assetRuleSp.enumValueIndex; case SerializedPropertyType.Vector2: return foundAssetSp.vector2Value == assetRuleSp.vector2Value; case SerializedPropertyType.Vector3: return foundAssetSp.vector3Value == assetRuleSp.vector3Value; case SerializedPropertyType.Vector4: return foundAssetSp.vector4Value == assetRuleSp.vector4Value; case SerializedPropertyType.Rect: return foundAssetSp.rectValue == assetRuleSp.rectValue; case SerializedPropertyType.ArraySize: if (foundAssetSp.isArray && assetRuleSp.isArray) { Debug.Log(foundAssetSp.arraySize + assetRuleSp.arraySize); return foundAssetSp.arraySize == assetRuleSp.arraySize; } else { return foundAssetSp.intValue == assetRuleSp.intValue; } case SerializedPropertyType.Character: break; case SerializedPropertyType.AnimationCurve: return foundAssetSp.animationCurveValue == assetRuleSp.animationCurveValue; case SerializedPropertyType.Bounds: return foundAssetSp.boundsValue == assetRuleSp.boundsValue; case SerializedPropertyType.Gradient: break; case SerializedPropertyType.Quaternion: return foundAssetSp.quaternionValue == assetRuleSp.quaternionValue; case SerializedPropertyType.ExposedReference: return foundAssetSp.exposedReferenceValue == assetRuleSp.exposedReferenceValue; #if UNITY_2017_1_OR_NEWER case SerializedPropertyType.FixedBufferSize: break; #endif default: throw new ArgumentOutOfRangeException(); } return false; } public static bool CompareSerializedObject(SerializedObject rule, SerializedObject asset) { SerializedProperty ruleIter = rule.GetIterator(); SerializedProperty assetIter = asset.GetIterator(); assetIter.NextVisible(true); ruleIter.NextVisible(true); do { if (!CompareSerializedProperty(ruleIter, assetIter)) { Debug.Log(" failied property " + ruleIter.propertyPath + " " + assetIter.displayName); return false; } ruleIter.NextVisible(false); } while (assetIter.NextVisible(false)); return true; } public static IEnumerable FixAll(AssetAuditTreeView treeView , AssetRule assetRule) { List list = new List(); TreeElementUtility.TreeToList(treeView.treeModel.root, list); float progress = 0f; foreach (AssetAuditTreeElement assetAuditTreeElement in list) { if (assetAuditTreeElement.isAsset && !assetAuditTreeElement.conforms) FixRule(assetAuditTreeElement, assetRule); progress += 1f; yield return progress / list.Count; } } public static void FixRule(AssetAuditTreeElement data , AssetRule assetRule) { string ruleAssetPath = AssetDatabase.GUIDToAssetPath(assetRule.AssetGuid); string affectedAssetPath = data.projectPath; switch (data.assetType) { case AssetType.Texture: TextureImporter ruleTexImporter = AssetImporter.GetAtPath(ruleAssetPath) as TextureImporter; TextureImporter affectedAssetTexImporter = AssetImporter.GetAtPath(affectedAssetPath) as TextureImporter; if (assetRule.SelectiveMode) { SerializedObject ruleImporterSO = new SerializedObject(ruleTexImporter); SerializedObject affectedAssetImporterSO = new SerializedObject(affectedAssetTexImporter); CopySelectiveProperties(affectedAssetImporterSO, ruleImporterSO, assetRule); } else { EditorUtility.CopySerialized(ruleTexImporter, affectedAssetTexImporter); } affectedAssetTexImporter.userData = ""; affectedAssetTexImporter.SaveAndReimport(); break; case AssetType.Model: ModelImporter ruleModelImporter = AssetImporter.GetAtPath(ruleAssetPath) as ModelImporter; ModelImporter affectedAssetModelImporter = AssetImporter.GetAtPath(affectedAssetPath) as ModelImporter; if (assetRule.SelectiveMode) { SerializedObject ruleImporterSO = new SerializedObject(ruleModelImporter); SerializedObject affectedAssetImporterSO = new SerializedObject(affectedAssetModelImporter); CopySelectiveProperties(affectedAssetImporterSO, ruleImporterSO, assetRule); } else { EditorUtility.CopySerialized(ruleModelImporter, affectedAssetModelImporter); } affectedAssetModelImporter.userData = ""; affectedAssetModelImporter.SaveAndReimport(); break; case AssetType.Audio: AudioImporter ruleAudioImporter = AssetImporter.GetAtPath(ruleAssetPath) as AudioImporter; AudioImporter affectedAssetAudioImporter = AssetImporter.GetAtPath(affectedAssetPath) as AudioImporter; if (assetRule.SelectiveMode) { SerializedObject ruleImporterSO = new SerializedObject(ruleAudioImporter); SerializedObject affectedAssetImporterSO = new SerializedObject(affectedAssetAudioImporter); CopySelectiveProperties(affectedAssetImporterSO, ruleImporterSO, assetRule); } else { EditorUtility.CopySerialized(ruleAudioImporter, affectedAssetAudioImporter); } affectedAssetAudioImporter.userData = ""; affectedAssetAudioImporter.SaveAndReimport(); break; case AssetType.Folder: break; default: throw new ArgumentOutOfRangeException(); } data.conforms = true; } private static void CopySelectiveProperties(SerializedObject affectedAssetImporterSO, SerializedObject ruleImporterSO, AssetRule assetRule) { foreach (string property in assetRule.SelectiveProperties) { string realname = GetPropertyNameFromDisplayName(affectedAssetImporterSO, property); SerializedProperty assetRuleSP = ruleImporterSO.FindProperty(realname); affectedAssetImporterSO.CopyFromSerializedProperty(assetRuleSP); bool applyModifiedProperties = affectedAssetImporterSO.ApplyModifiedProperties(); if (!applyModifiedProperties) Debug.Log(" copy failed "); } } public static bool HaveEqualProperties(T rhs, T lhs) { if (rhs != null && lhs != null) { Type type = typeof(T); foreach (PropertyInfo pi in type.GetProperties( BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) { object rhsValue = type.GetProperty(pi.Name).GetValue(rhs, null); object lhsValue = type.GetProperty(pi.Name).GetValue(lhs, null); if (!rhsValue.Equals(lhsValue)) { return false; } } } return true; } public static string GetPropertyNameFromDisplayName(SerializedObject so, string displayName) { SerializedProperty iter = so.GetIterator(); iter.NextVisible(true); do { if (iter.displayName == displayName) return iter.name; } while (iter.NextVisible(false)) ; return null; } public static string[] GetPropertyNames(SerializedObject so) { SerializedProperty soIter = so.GetIterator(); List propNames = new List(); soIter.NextVisible(true); do { propNames.Add(soIter.displayName); } while (soIter.NextVisible(false)); return propNames.ToArray(); } public static void CreateProxyAudio(AssetRule newRule , ref string currentAsset) { string audioProxy = AssetAuditorPreferences.ProxyAudioPath; string ext = audioProxy.Substring( audioProxy.LastIndexOf( '.' ) ); string newAssetPath = AssetAuditorPreferences.ProxyAssetsDirectory + Path.DirectorySeparatorChar + newRule.RuleName + ext; if( !AssetDatabase.CopyAsset( audioProxy, newAssetPath ) ) { Debug.LogWarning( "Failed to copy proxy asset from " + audioProxy ); return; } AssetDatabase.ImportAsset(newAssetPath); WriteUserData(newAssetPath , newRule, ref currentAsset); } public static void CreateProxyModel(AssetRule newRule, ref string currentAsset) { string modelProxy = AssetAuditorPreferences.ProxyModelPath; string ext = modelProxy.Substring( modelProxy.LastIndexOf( '.' ) ); string newAssetPath = AssetAuditorPreferences.ProxyAssetsDirectory + Path.DirectorySeparatorChar + newRule.RuleName + ext; if( !AssetDatabase.CopyAsset( modelProxy, newAssetPath ) ) { Debug.LogWarning( "Failed to copy proxy asset from " + modelProxy ); return; } AssetDatabase.ImportAsset(newAssetPath); WriteUserData(newAssetPath , newRule, ref currentAsset); } public static void CreateProxyTexture(AssetRule newRule, ref string currentAsset) { string textureProxy = AssetAuditorPreferences.ProxyTexturePath; string ext = textureProxy.Substring( textureProxy.LastIndexOf( '.' ) ); string newAssetPath = AssetAuditorPreferences.ProxyAssetsDirectory + Path.DirectorySeparatorChar + newRule.RuleName + ext; if( !AssetDatabase.CopyAsset( textureProxy, newAssetPath ) ) { Debug.LogWarning( "Failed to copy proxy asset from " + textureProxy ); return; } AssetDatabase.ImportAsset(newAssetPath); WriteUserData(newAssetPath , newRule , ref currentAsset); } public static void WriteUserData(string path, AssetRule assetRule, ref string currentAsset) { AssetImporter assetImporter = AssetImporter.GetAtPath(path); assetRule.AssetGuid = AssetDatabase.AssetPathToGUID(assetImporter.assetPath); assetImporter.userData = JsonUtility.ToJson(assetRule); EditorUtility.SetDirty(assetImporter); AssetDatabase.WriteImportSettingsIfDirty(path); AssetDatabase.SaveAssets(); currentAsset = assetRule.AssetGuid; } public static void WriteUserData(string path , AssetRule assetRule) { AssetImporter assetImporter = AssetImporter.GetAtPath(path); assetRule.AssetGuid = AssetDatabase.AssetPathToGUID(assetImporter.assetPath); assetImporter.userData = JsonUtility.ToJson(assetRule); EditorUtility.SetDirty(assetImporter); AssetDatabase.WriteImportSettingsIfDirty(path); AssetDatabase.SaveAssets(); } public static bool RuleExists(AssetRule assetRule) { if (!AssetDatabase.IsValidFolder(AssetAuditorPreferences.ProxyAssetsDirectory)) { string folder = AssetAuditorPreferences.ProxyAssetsDirectory.Split(Path.DirectorySeparatorChar).Last(); string dir = AssetAuditorPreferences.ProxyAssetsDirectory.Substring(0, AssetAuditorPreferences.ProxyAssetsDirectory.Length - folder.Length); AssetDatabase.CreateFolder(dir, folder); } foreach (string asset in AssetDatabase.FindAssets("", new[] {AssetAuditorPreferences.ProxyAssetsDirectory})) { string guidToAssetPath = AssetDatabase.GUIDToAssetPath(asset); AssetImporter assetImporter = AssetImporter.GetAtPath(guidToAssetPath); AssetRule ar = new AssetRule(); ar = JsonUtility.FromJson(assetImporter.userData); if (ar.RuleName == assetRule.RuleName && ar.WildCard == assetRule.WildCard && ar.WildCardMatchType == assetRule.WildCardMatchType) return true; } return false; } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditor.cs.meta ================================================ fileFormatVersion: 2 guid: 75cdde80fe406b14e816e78029e38d15 timeCreated: 1481588091 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditorNewRuleWindow.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using UnityEditor; using UnityEditor.VersionControl; using UnityEngine; using Object = UnityEngine.Object; namespace UnityAssetAuditor { public class AssetAuditorNewRuleWindow : EditorWindow { private static List assetRules; private static AssetAuditor.AssetRule newRule; private static string currentAsset; static int selected = -1; private static string[] affectedAssets; private static AssetAuditorNewRuleWindow window; private static Vector2 scrollPosition; [MenuItem("Asset Auditing/New Audit Rule")] public static void ShowWindow() { window = GetWindow(); window.Show(); window.titleContent = new GUIContent("Asset Auditor Creation"); if (!AssetDatabase.IsValidFolder(AssetAuditorPreferences.ProxyAssetsDirectory)) { string folder = AssetAuditorPreferences.ProxyAssetsDirectory.Split(Path.DirectorySeparatorChar).Last(); string dir = AssetAuditorPreferences.ProxyAssetsDirectory.Substring(0, AssetAuditorPreferences.ProxyAssetsDirectory.Length - folder.Length); AssetDatabase.CreateFolder(dir, folder); } UpdateExistingRules(); scrollPosition = Vector2.zero; AssetAuditor.queueComplete += AffectedAssetSearchComplete; } private static void AffectedAssetSearchComplete() { affectedAssets = AssetAuditor.GetAffectedAssets(); window.Repaint(); AssetAuditor.queueComplete -= AffectedAssetSearchComplete; } void OnGUI() { DoNewRuleGUI(); UpdateSelectedAsset(); UpdateExistingRules(); } private static void UpdateSelectedAsset() { if (selected == -1) return; Selection.activeObject = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(assetRules[selected].AssetGuid), typeof(Object)); } private static void UpdateExistingRules() { // clear current list assetRules = new List(); // get all assets in the proxyassets folder foreach (var asset in AssetDatabase.FindAssets("", new[] {AssetAuditorPreferences.ProxyAssetsDirectory})) { var guidToAssetPath = AssetDatabase.GUIDToAssetPath(asset); var assetImporter = AssetImporter.GetAtPath(guidToAssetPath); AssetAuditor.AssetRule ar = new AssetAuditor.AssetRule(); ar = JsonUtility.FromJson(assetImporter.userData); assetRules.Add(ar); } int i = 0; // make sure that the current asset is selected from assetRules foreach (var assetRule in assetRules) { if (assetRule.AssetGuid == currentAsset) { selected = i; return; } i++; } // if we get to here we couldnt find and asset to be the currently selected // set it to -1 and ignore anything to be currently selected selected = -1; currentAsset = ""; } //done private static void DoNewRuleGUI() { newRule.RuleName = EditorGUILayout.TextField("Rule Name: ", newRule.RuleName); EditorGUI.BeginChangeCheck(); newRule.WildCardMatchType = (AssetAuditor.WildCardMatchType) EditorGUILayout.EnumPopup("Wild Card Matching Type: ", newRule.WildCardMatchType); if (EditorGUI.EndChangeCheck()) { newRule.WildCard = ""; } EditorGUI.BeginChangeCheck(); newRule.WildCard = EditorGUILayout.TextField("Wild Card: ", newRule.WildCard); if (EditorGUI.EndChangeCheck()) { AssetAuditor.queueComplete += AffectedAssetSearchComplete; AssetAuditor.UpdateAffectedAssets(newRule); } newRule.SelectiveMode = EditorGUILayout.Toggle("Selective Mode", newRule.SelectiveMode); if (newRule.SelectiveMode) { if(newRule.SelectiveProperties == null) newRule.SelectiveProperties = new List(); SerializedObject so = GetSerializedObject(newRule.assetType); var propertyNames = AssetAuditor.GetPropertyNames(so); // TODO need to cache this // loop through all the selective properties for (int i = 0 ; i < newRule.SelectiveProperties.Count ; i++) { EditorGUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); newRule.SelectiveProperties[i] = propertyNames[EditorGUILayout.Popup(SelectedFromList(propertyNames , newRule.SelectiveProperties[i]), propertyNames)]; if (EditorGUI.EndChangeCheck()) { AssetAuditor.queueComplete += AffectedAssetSearchComplete; AssetAuditor.UpdateAffectedAssets(newRule); } EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("+", GUILayout.MaxWidth(20))) { AddNewSelectiveRule(); } if (GUILayout.Button("-", GUILayout.MaxWidth(20))) { RemoveLastSelectiveRule(); } EditorGUILayout.LabelField("Add and remove selective property overiding"); EditorGUILayout.EndHorizontal(); } // drop down for type EditorGUI.BeginChangeCheck(); newRule.assetType = (AssetAuditor.AssetType) EditorGUILayout.IntPopup("Rule Type: ", (int)newRule.assetType, new[]{"Texture", "Model","Audio"},new[]{0,1,2} ); if (EditorGUI.EndChangeCheck()) { newRule.SelectiveProperties = new List(); AssetAuditor.queueComplete += AffectedAssetSearchComplete; AssetAuditor.UpdateAffectedAssets(newRule); } if (!AssetAuditor.RuleExists(newRule)) { if (GUILayout.Button("Create New " + newRule.assetType + " Rule")) { switch (newRule.assetType) { case AssetAuditor.AssetType.Texture: AssetAuditor.CreateProxyTexture(newRule , ref currentAsset); break; case AssetAuditor.AssetType.Audio: AssetAuditor.CreateProxyAudio(newRule , ref currentAsset); break; case AssetAuditor.AssetType.Model: AssetAuditor.CreateProxyModel(newRule, ref currentAsset); break; default: throw new ArgumentOutOfRangeException(); } UpdateExistingRules(); AssetAuditor.queueComplete += AffectedAssetSearchComplete; AssetAuditor.UpdateAffectedAssets(assetRules[selected]); } } else { GUILayout.Label("Rule already exists in the project cannot create duplicates"); } GUILayout.Space(20); GUILayout.Label("Affect Assets Preview"); GUILayout.Space(5); Rect rt = GUILayoutUtility.GetRect(5, window ? window.position.width-10 : 100f, 18, 18); EditorGUI.ProgressBar(rt,AssetAuditor.GetProgress(), "Affected Asset Search Progress " + (AssetAuditor.GetProgress() * 100f).ToString("0.00%")); scrollPosition = GUILayout.BeginScrollView(scrollPosition); if (affectedAssets != null) { foreach (string affectedAsset in affectedAssets) { EditorGUILayout.ObjectField( AssetDatabase.LoadAssetAtPath(affectedAsset, AssetAuditor.TypeFromAssetType(newRule.assetType)), AssetAuditor.TypeFromAssetType(newRule.assetType), false); } } GUILayout.EndScrollView(); if (GUILayout.Button("Open Audit View")) { AssetAuditorWindow.GetWindow(); } } private static SerializedObject GetSerializedObject(AssetAuditor.AssetType assetType) { SerializedObject so = null; switch (assetType) { case AssetAuditor.AssetType.Texture: so = new SerializedObject(TextureImporter.GetAtPath(AssetAuditorPreferences.ProxyTexturePath)); break; case AssetAuditor.AssetType.Model: so = new SerializedObject(ModelImporter.GetAtPath(AssetAuditorPreferences.ProxyModelPath)); break; case AssetAuditor.AssetType.Audio: so = new SerializedObject(AudioImporter.GetAtPath(AssetAuditorPreferences.ProxyAudioPath)); break; case AssetAuditor.AssetType.Folder: break; default: throw new ArgumentOutOfRangeException(); } return so; } private static int SelectedFromList(string[] propertyNames , string current) { if (current == "") return 0; // hack to avoid the empty string problem int i = 0; while (propertyNames[i] != current) { i++; } return i; } private static void RemoveLastSelectiveRule() { newRule.SelectiveProperties.RemoveAt(newRule.SelectiveProperties.Count-1); } private static void AddNewSelectiveRule() { newRule.SelectiveProperties.Add(AssetAuditor.GetPropertyNames(GetSerializedObject(newRule.assetType))[0]); } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditorNewRuleWindow.cs.meta ================================================ fileFormatVersion: 2 guid: 9cfdaaf847ee42a46bd18fa749076260 timeCreated: 1479436789 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditorPreferences.cs ================================================ using System.Collections; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace UnityAssetAuditor { public class AssetAuditorPreferences { private static string proxyAssetDir; private const string proxyAssetDirKey = "ProxyAssetDirectory"; private const string proxyAssetDirDefault = "Assets/Editor/AssetAuditor/ProxyAssets"; private static string proxyTexturePath; private const string proxyTexturePathKey = "ProxyTexturePath"; private const string proxyTexturePathDefault = "Assets/Editor/AssetAuditor/Texture/DefaultTexture.jpg"; private static string proxyModelPath; private const string proxyModelPathKey = "ProxyModelPath"; private const string proxyModelPathDefault = "Assets/Editor/AssetAuditor/Models/DefaultAvatar.fbx"; private static string proxyAudioPath; private const string proxyAudioPathKey = "ProxyAudioPath"; private const string proxyAudioPathDefault = "Assets/Editor/AssetAuditor/Audio/DefaultAudio.wav"; static AssetAuditorPreferences() { proxyAssetDir = EditorPrefs.GetString( proxyAssetDirKey, proxyAssetDirDefault ); proxyTexturePath = EditorPrefs.GetString( proxyTexturePathKey, proxyTexturePathDefault ); proxyModelPath = EditorPrefs.GetString( proxyModelPathKey, proxyModelPathDefault ); proxyAudioPath = EditorPrefs.GetString( proxyAudioPathKey, proxyAudioPathDefault ); } public static string ProxyAssetsDirectory { get { createProxyAssetsDirectory(); return proxyAssetDir; } } public static string ProxyTexturePath { get { return proxyTexturePath; } } public static string ProxyModelPath { get { return proxyModelPath; } } public static string ProxyAudioPath { get { return proxyAudioPath; } } private static void createProxyAssetsDirectory() { if( AssetDatabase.IsValidFolder( proxyAssetDir ) ) return; AssetAuditorUtilities.CreatePath.Create( proxyAssetDir ); } [PreferenceItem( "Asset Auditor" )] public static void PreferencesGUI() { EditorGUILayout.LabelField( "Proxy Assets Directory", EditorStyles.boldLabel ); EditorGUILayout.BeginHorizontal(); proxyAssetDir = "Assets/" + EditorGUILayout.TextField( proxyAssetDir.Remove( 0, 7 ) ); if( GUILayout.Button( "Browse", EditorStyles.miniButton ) ) { string path = EditorUtility.OpenFolderPanel( "Select Proxy Assets Directory", proxyAssetDir, "" ); if( !path.Contains( Application.dataPath ) ) { Debug.LogError( "Selected path " + path + " is not a directory within the open project" ); } else if( path.Length > 0 ) { proxyAssetDir = path.Substring( Application.dataPath.Length - 6 ); ; EditorPrefs.SetString( proxyAssetDirKey, proxyAssetDir ); } } EditorGUILayout.EndHorizontal(); if( AssetDatabase.IsValidFolder( proxyAssetDir ) == false ) { EditorGUILayout.HelpBox( "Folder does not exist at given path", MessageType.Warning ); if( GUILayout.Button( "Create Now", EditorStyles.miniButton ) ) { createProxyAssetsDirectory(); } } EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.LabelField( "Proxy Asset Paths", EditorStyles.boldLabel ); EditorGUILayout.Space(); EditorGUILayout.LabelField( "Texture" ); EditorGUILayout.BeginHorizontal(); proxyTexturePath = "Assets/" + EditorGUILayout.TextField( proxyTexturePath.Remove( 0, 7 ) ); if( GUILayout.Button( "Browse", EditorStyles.miniButton ) ) { string path = EditorUtility.OpenFilePanel( "Select Proxy Texture", proxyTexturePath, "jpg,png,bmp,tga" ); if( path.Length > 0 ) { if( !path.Contains( Application.dataPath ) ) { Debug.LogError( "Selected path <" + path + "> is not a directory within the open project" ); } else { proxyTexturePath = path.Substring( Application.dataPath.Length - 6 ); EditorPrefs.SetString( proxyTexturePathKey, proxyTexturePath ); } } } EditorGUILayout.EndHorizontal(); System.Type t = AssetDatabase.GetMainAssetTypeAtPath( proxyTexturePath ); if( t == null || t != typeof(Texture2D) ) EditorGUILayout.HelpBox( "Proxy Texture does not exist at given path", MessageType.Warning ); EditorGUILayout.Space(); EditorGUILayout.LabelField( "Model" ); EditorGUILayout.BeginHorizontal(); proxyModelPath = "Assets/" + EditorGUILayout.TextField( proxyModelPath.Remove( 0, 7 ) ); if( GUILayout.Button( "Browse", EditorStyles.miniButton ) ) { string path = EditorUtility.OpenFilePanel( "Select Proxy Model", proxyModelPath, "fbx,obj,3ds" ); if( path.Length > 0 ) { if( !path.Contains( Application.dataPath ) ) { Debug.LogError( "Selected path <" + path + "> is not a directory within the open project" ); } else { proxyModelPath = path.Substring( Application.dataPath.Length - 6 ); EditorPrefs.SetString( proxyModelPathKey, proxyModelPath ); } } } EditorGUILayout.EndHorizontal(); t = AssetDatabase.GetMainAssetTypeAtPath( proxyModelPath ); if( t == null || t != typeof(GameObject) ) EditorGUILayout.HelpBox( "Proxy Texture does not exist at given path", MessageType.Warning ); EditorGUILayout.Space(); EditorGUILayout.LabelField( "Audio" ); EditorGUILayout.BeginHorizontal(); proxyAudioPath = "Assets/" + EditorGUILayout.TextField( proxyAudioPath.Remove( 0, 7 ) ); if( GUILayout.Button( "Browse", EditorStyles.miniButton ) ) { string path = EditorUtility.OpenFilePanel( "Select Proxy Audio", proxyAudioPath, "wav,mp3,ogg" ); if( path.Length > 0 ) { if( !path.Contains( Application.dataPath ) ) { Debug.LogError( "Selected path <" + path + "> is not a directory within the open project" ); } else { proxyAudioPath = path.Substring( Application.dataPath.Length - 6 ); EditorPrefs.SetString( proxyAudioPathKey, proxyAudioPath ); } } } EditorGUILayout.EndHorizontal(); t = AssetDatabase.GetMainAssetTypeAtPath( proxyAudioPath ); if( t == null || t != typeof(AudioClip) ) EditorGUILayout.HelpBox( "Proxy AudioClip does not exist at given path", MessageType.Warning ); } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditorPreferences.cs.meta ================================================ fileFormatVersion: 2 guid: 940f261582124401db9376c57e2a48c8 timeCreated: 1505468470 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditorUtilities.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.Assertions; namespace UnityAssetAuditor { public class AssetAuditorUtilities { public class CreatePath : IEnumerable { private string[] m_Folders; public CreatePath( string path ) { Assert.IsTrue( path.StartsWith( "Assets/" ) ); m_Folders = path.Split( '/' ); } public CreatePath( string[] folderList ) { Assert.IsTrue( folderList.Length > 1 && folderList[0] == "Assets" ); m_Folders = new string[folderList.Length]; for( int i = 0; i < folderList.Length; i++ ) m_Folders[i] = folderList[i]; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public CreatePathEnumerator GetEnumerator() { return new CreatePathEnumerator( m_Folders ); } public static string Create( string path ) { CreatePathEnumerator e = new CreatePathEnumerator( path ); while( e.MoveNext() ) { // Loop through each folder } return e.Current; } } public class CreatePathEnumerator : IEnumerator { private string[] m_FolderNames; private int m_Position = 0; private string m_CurrentGuid; private string m_CurrentPath; public CreatePathEnumerator( string[] folderList ) { m_FolderNames = folderList; m_CurrentPath = m_FolderNames[0]; } public CreatePathEnumerator( string path ) { Debug.Log( path ); m_FolderNames = path.Split( '/' ); m_CurrentPath = m_FolderNames[0]; } public bool MoveNext() { m_Position++; if( m_Position < m_FolderNames.Length ) { string nextPath = m_CurrentPath + "/" + m_FolderNames[m_Position]; if( AssetDatabase.IsValidFolder( nextPath ) ) m_CurrentGuid = string.Empty; else m_CurrentGuid = AssetDatabase.CreateFolder( m_CurrentPath, m_FolderNames[m_Position] ); m_CurrentPath = nextPath; return true; } return false; } public void Reset() { m_Position = 0; } object IEnumerator.Current { get { return Current; } } public string Current { get { return m_CurrentGuid; } } } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditorUtilities.cs.meta ================================================ fileFormatVersion: 2 guid: 5e05911aedaeb4c9e9bd82b182deab1b timeCreated: 1529319486 licenseType: Pro MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditorWindow.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; namespace UnityAssetAuditor { public delegate void OnGatherAssetRulesComplete(); public delegate void OnGatherDataComplete(); class AssetAuditorWindow : EditorWindow { [SerializeField] static TreeViewState m_TreeViewState; // Serialized in the window layout file so it survives assembly reloading [SerializeField] MultiColumnHeaderState m_MultiColumnHeaderState; static AssetAuditTreeView m_TreeView; private static List assetRules; private List assetRuleNames; private static int selected = 0; private static int selectedSelective = 0; private static bool editSelective; private static int editSelectiveProp = 0; private string[] affectedAssets; private Action act; private OnGatherAssetRulesComplete onGatherAssetRulesComplete; private OnGatherDataComplete onGatherDataComplete; private List elements; private bool gatherAssetsComplete; private bool gatherDataComplete; private List tempElements; private enum State { Uninitialized, GatheringRules, GatheringData, Initialized, NoAssetRules } [NonSerialized] private State state = State.Uninitialized; [MenuItem("Asset Auditing/Auditor View")] public static AssetAuditorWindow GetWindow() { var window = GetWindow(); window.titleContent = new GUIContent("Audit"); return window; } Rect multiColumnTreeViewRect { get { return new Rect(20, 110, position.width - 40, position.height - 130); } } Rect toolbarRect { get { return new Rect(20f, 90f, position.width - 40f, 20f); } } Rect bottomToolbarRect { get { return new Rect(20f, position.height - 18f, position.width - 40f, 16f); } } Rect progressBarRect { get { return new Rect(20f, position.height - 36f, position.width - 40f, 16f); } } Rect ruleSelectRect { get { return new Rect(20f, 10f, position.width - 40, 15); } } Rect wildCardDisplayRect { get { return new Rect(20, 30, position.width - 40f, 15f); } } Rect SelectivePropRect { get { return new Rect(20, 50, position.width - 40f, 15f); } } Rect AddSelectivePropRect { get {return new Rect(20,70,18,18);} } Rect RemoveSelectivePropRect { get { return new Rect(40,70,18,18);} } Rect EditSelectedPropButtonRect { get {return new Rect(60,70, 40,18 );} } Rect EditSelectedPropDropDownRect { get {return new Rect(110,70, 400,18 );} } public AssetAuditTreeView treeView { get { return m_TreeView; } } private void FixRule(AssetAuditTreeElement assetAuditTreeElement) { AssetAuditor.FixRule(assetAuditTreeElement , assetRules[selected]); elements = new List(); AssetAuditor.queueComplete += OnGatherDataComplete; AssetAuditor.AddEnumerator(AssetAuditor.GatherData(assetRules[selected], elements , selectedSelective)); } void GatherAssetRules() { gatherAssetsComplete = false; AssetAuditor.queueComplete += OnGatherAssetRulesComplete; assetRules = new List(); assetRuleNames = new List(); AssetAuditor.ClearQueue(); AssetAuditor.AddEnumerator(AssetAuditor.GatherAssetRules(assetRules,assetRuleNames)); } private void OnGatherAssetRulesComplete() { AssetAuditor.queueComplete -= OnGatherAssetRulesComplete; if (assetRules.Count > 0) { gatherAssetsComplete = true; } else { gatherAssetsComplete = false; state = State.NoAssetRules; } } void GatherData() { gatherDataComplete = false; AssetAuditor.queueComplete += OnGatherDataComplete; elements = new List(); AssetAuditor.ClearQueue(); AssetAuditor.UpdateAffectedAssets(assetRules[selected]); AssetAuditor.AddEnumerator(AssetAuditor.GatherData(assetRules[selected],elements,selectedSelective)); } private void OnGatherDataComplete() { AssetAuditor.queueComplete -= OnGatherDataComplete; // Check if it already exists (deserialized from window layout file or scriptable object) if (m_TreeViewState == null) m_TreeViewState = new TreeViewState(); if (m_TreeView != null && elements.Count > 0) { m_TreeView.treeModel.SetData(elements); m_TreeView.Reload(); } gatherDataComplete = true; } void OnSelectionChange() { // if (!m_Initialized) // return; // m_TreeView.treeModel.SetData(GetData()); // m_TreeView.Reload(); } private void OnFocus() { // this doesn't seem to be needed /*if (m_Initialized) { GatherAssetRules(); m_TreeView.treeModel.SetData(GetData()); m_TreeView.Reload(); }*/ } void OnGUI() { switch (state) { case State.Uninitialized: act = FixRule; GatherAssetRules(); state = State.GatheringRules; break; case State.GatheringRules: if (gatherAssetsComplete) { GatherData(); state = State.GatheringData; } break; case State.GatheringData: if (gatherDataComplete) { // Check if it already exists (deserialized from window layout file or scriptable object) if (m_TreeViewState == null) m_TreeViewState = new TreeViewState(); var headerState = AssetAuditTreeView.CreateDefaultMultiColumnHeaderState(multiColumnTreeViewRect.width); if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_MultiColumnHeaderState, headerState)) MultiColumnHeaderState.OverwriteSerializedFields(m_MultiColumnHeaderState, headerState); m_MultiColumnHeaderState = headerState; var multiColumnHeader = new MultiColumnHeader(headerState); var treeModel = new TreeModel(elements); m_TreeView = new AssetAuditTreeView(m_TreeViewState, multiColumnHeader, treeModel, act); GUILayout.Label(" no asset rules have been found in the project"); state = State.Initialized; } break; case State.Initialized: DoRuleSelectionGUI(); SearchBar(toolbarRect); DoTreeView(multiColumnTreeViewRect); BottomToolBar(bottomToolbarRect); break; case State.NoAssetRules: DoNoAssetRuleGUI(); break; } DoProgressBar(progressBarRect); } private void DoNoAssetRuleGUI() { GUILayout.Label(" No Asset Rules Are Present In The Project "); if (GUILayout.Button("Search Again For Assets ")) /// TODO add directory to string for proxy asset path { GatherAssetRules(); state = State.GatheringRules; } } private void DoProgressBar(Rect rect) { var progress = AssetAuditor.GetProgress(); EditorGUI.ProgressBar(progressBarRect , progress, " Search Progress " + progress.ToString("0.00%")); } private void DoRuleSelectionGUI() { EditorGUI.BeginChangeCheck(); selected = EditorGUI.Popup(ruleSelectRect, "Rule Name", selected, assetRuleNames.ToArray()); if (EditorGUI.EndChangeCheck()) { selectedSelective = 0; GatherData(); } // make wildcard editable and update selection from it if (assetRules != null && selected != -1)// && !string.IsNullOrEmpty(assetRules[selected].WildCard)) { AssetAuditor.AssetRule ar = assetRules[selected]; EditorGUI.BeginChangeCheck(); ar.WildCard = EditorGUI.TextField(wildCardDisplayRect, "WildCard ", ar.WildCard); if (EditorGUI.EndChangeCheck()) { assetRules[selected] = ar; GatherData(); AssetAuditor.WriteUserData(AssetDatabase.GUIDToAssetPath(ar.AssetGuid), ar); } if (ar.SelectiveProperties != null && ar.SelectiveProperties.Count > 0) { selectedSelective = EditorGUI.Popup(SelectivePropRect, "Selective Properties", selectedSelective, ar.SelectiveProperties.ToArray()); } else { EditorGUI.LabelField(SelectivePropRect , " No Selective Properties in the Asset Rule"); } if (GUI.Button(AddSelectivePropRect, "+")) { // add a new selective property if (ar.SelectiveProperties != null && !ar.SelectiveProperties.Contains("Unnasigned property")) ar.SelectiveProperties.Add("Unnasigned property"); assetRules[selected] = ar; GatherData(); AssetAuditor.WriteUserData(AssetDatabase.GUIDToAssetPath(ar.AssetGuid), ar); } if (ar.SelectiveProperties != null && ar.SelectiveProperties.Count > 0) { if (GUI.Button(RemoveSelectivePropRect, "-")) { // remove last selective property ar.SelectiveProperties.RemoveAt(ar.SelectiveProperties.Count - 1); if (ar.SelectiveProperties.Count == 0) ar.SelectiveMode = false; assetRules[selected] = ar; GatherData(); AssetAuditor.WriteUserData(AssetDatabase.GUIDToAssetPath(ar.AssetGuid), ar); } editSelective = GUI.Toggle(EditSelectedPropButtonRect, editSelective, "Edit", "Button"); if (editSelective) { SerializedObject so = new SerializedObject( AssetImporter.GetAtPath(AssetDatabase.GUIDToAssetPath(ar.AssetGuid))); EditorGUI.BeginChangeCheck(); editSelectiveProp = EditorGUI.Popup(EditSelectedPropDropDownRect, editSelectiveProp, AssetAuditor.GetPropertyNames(so)); if (EditorGUI.EndChangeCheck()) { ar.SelectiveProperties[selectedSelective] = AssetAuditor.GetPropertyNames(so)[editSelectiveProp]; assetRules[selected] = ar; GatherData(); AssetAuditor.WriteUserData(AssetDatabase.GUIDToAssetPath(ar.AssetGuid), ar); } } } } } void SearchBar(Rect rect) { if(treeView != null) treeView.searchString = SearchField.OnGUI(rect, treeView.searchString); } void DoTreeView(Rect rect) { if(m_TreeView != null) m_TreeView.OnGUI(rect); } void BottomToolBar(Rect rect) { var style = "miniButton"; if (GUI.Button(new Rect(rect.x, rect.y, rect.width / 3, rect.height), "Expand All", style)) { treeView.ExpandAll(); } if (GUI.Button(new Rect(rect.x + rect.width / 3, rect.y, rect.width / 3, rect.height), "Collapse All", style)) { treeView.CollapseAll(); } if (GUI.Button(new Rect(rect.x + ((rect.width / 3) * 2), rect.y, rect.width / 3, rect.height), "Fix All", style)) { AssetAuditor.AddEnumerator(AssetAuditor.FixAll(m_TreeView , assetRules[selected])); } } } internal static class SearchField { static class Styles { public static GUIStyle searchField = "SearchTextField"; public static GUIStyle searchFieldCancelButton = "SearchCancelButton"; public static GUIStyle searchFieldCancelButtonEmpty = "SearchCancelButtonEmpty"; } public static string OnGUI(Rect position, string text) { // Search field Rect textRect = position; textRect.width -= 15; text = EditorGUI.TextField(textRect, GUIContent.none, text, Styles.searchField); // Cancel button Rect buttonRect = position; buttonRect.x += position.width - 15; buttonRect.width = 15; if (GUI.Button(buttonRect, GUIContent.none, text != "" ? Styles.searchFieldCancelButton : Styles.searchFieldCancelButtonEmpty) && text != "") { text = ""; GUIUtility.keyboardControl = 0; } return text; } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/AssetAuditorWindow.cs.meta ================================================ fileFormatVersion: 2 guid: c3cf55b48c197bb40a09696dead1d7c2 timeCreated: 1467106475 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/TreeElement.cs ================================================ using System; using System.Collections.Generic; using UnityEngine; namespace UnityAssetAuditor { [Serializable] public class TreeElement { [SerializeField] int m_ID; [SerializeField] string m_Name; [SerializeField] int m_Depth; [NonSerialized] TreeElement m_Parent; [NonSerialized] List m_Children; public int depth { get { return m_Depth; } set { m_Depth = value; } } public TreeElement parent { get { return m_Parent; } set { m_Parent = value; } } public List children { get { return m_Children; } set { m_Children = value; } } public bool hasChildren { get { return children != null && children.Count > 0; } } public string name { get { return m_Name; } set { m_Name = value; } } public int id { get { return m_ID; } set { m_ID = value; } } public TreeElement () { } public TreeElement (string name, int depth, int id) { m_Name = name; m_ID = id; m_Depth = depth; } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/TreeElement.cs.meta ================================================ fileFormatVersion: 2 guid: 69be32fe4d27dde489209c5885c1e5dc timeCreated: 1472024155 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/TreeElementUtility.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; using UnityEditor; namespace UnityAssetAuditor { // TreeElementUtility and TreeElement are useful helper classes for backend tree data structures. // See tests at the bottom for examples of how to use. public static class TreeElementUtility { public static void TreeToList(T root, IList result) where T : TreeElement { if (result == null) throw new NullReferenceException("The input 'IList result' list is null"); result.Clear(); Stack stack = new Stack(); stack.Push(root); while (stack.Count > 0) { T current = stack.Pop(); result.Add(current); if (current.children != null && current.children.Count > 0) { for (int i = current.children.Count - 1; i >= 0; i--) { stack.Push((T)current.children[i]); } } } } // Returns the root of the tree parsed from the list (always the first element). // Important: the first item and is required to have a depth value of -1. // The rest of the items should have depth >= 0. public static T ListToTree(IList list) where T : TreeElement { // Validate input ValidateDepthValues (list); // Clear old states foreach (var element in list) { element.parent = null; element.children = null; } // Set child and parent references using depth info for (int parentIndex = 0; parentIndex < list.Count; parentIndex++) { var parent = list[parentIndex]; bool alreadyHasValidChildren = parent.children != null; if (alreadyHasValidChildren) continue; int parentDepth = parent.depth; int childCount = 0; // Count children based depth value, we are looking at children until it's the same depth as this object for (int i = parentIndex + 1; i < list.Count; i++) { if (list[i].depth == parentDepth + 1) childCount++; if (list[i].depth <= parentDepth) break; } // Fill child array List childList = null; if (childCount != 0) { childList = new List(childCount); // Allocate once childCount = 0; for (int i = parentIndex + 1; i < list.Count; i++) { if (list[i].depth == parentDepth + 1) { list[i].parent = parent; childList.Add(list[i]); childCount++; } if (list[i].depth <= parentDepth) break; } } parent.children = childList; } return list[0]; } // Check state of input list public static void ValidateDepthValues(IList list) where T : TreeElement { if (list.Count == 0) throw new ArgumentException("list should have items, count is 0, check before calling ValidateDepthValues", "list"); if (list[0].depth != -1) throw new ArgumentException("list item at index 0 should have a depth of -1 (since this should be the hidden root of the tree). Depth is: " + list[0].depth, "list"); for (int i = 0; i < list.Count - 1; i++) { int depth = list[i].depth; int nextDepth = list[i + 1].depth; if (nextDepth > depth && nextDepth - depth > 1) throw new ArgumentException(string.Format("Invalid depth info in input list. Depth cannot increase more than 1 per row. Index {0} has depth {1} while index {2} has depth {3}", i, depth, i + 1, nextDepth)); } for (int i = 1; i < list.Count; ++i) if (list[i].depth < 0) throw new ArgumentException("Invalid depth value for item at index " + i + ". Only the first item (the root) should have depth below 0."); if (list.Count > 1 && list[1].depth != 0) throw new ArgumentException("Input list item at index 1 is assumed to have a depth of 0", "list"); } // For updating depth values below any given element e.g after reparenting elements public static void UpdateDepthValues(T root) where T : TreeElement { if (root == null) throw new ArgumentNullException("root", "The root is null"); if (!root.hasChildren) return; Stack stack = new Stack(); stack.Push(root); while (stack.Count > 0) { TreeElement current = stack.Pop(); if (current.children != null) { foreach (var child in current.children) { child.depth = current.depth + 1; stack.Push(child); } } } } // Returns true if there is an ancestor of child in the elements list static bool IsChildOf(T child, IList elements) where T : TreeElement { while (child != null) { child = (T)child.parent; if (elements.Contains(child)) return true; } return false; } public static IList FindCommonAncestorsWithinList(IList elements) where T : TreeElement { if (elements.Count == 1) return new List(elements); List result = new List(elements); result.RemoveAll(g => IsChildOf(g, elements)); return result; } } class TreeElementUtilityTests { class TestElement : TreeElement { public TestElement (string name, int depth) { this.name = name; this.depth = depth; } } #region Tests [Test] public static void TestTreeToListWorks() { // Arrange TestElement root = new TestElement("root", -1); root.children = new List(); root.children.Add(new TestElement("A", 0)); root.children.Add(new TestElement("B", 0)); root.children.Add(new TestElement("C", 0)); root.children[1].children = new List(); root.children[1].children.Add(new TestElement("Bchild", 1)); root.children[1].children[0].children = new List(); root.children[1].children[0].children.Add(new TestElement("Bchildchild", 2)); // Test List result = new List(); TreeElementUtility.TreeToList(root, result); // Assert string[] namesInCorrectOrder = { "root", "A", "B", "Bchild", "Bchildchild", "C" }; Assert.AreEqual(namesInCorrectOrder.Length, result.Count, "Result count is not match"); for (int i = 0; i < namesInCorrectOrder.Length; ++i) { Assert.AreEqual(namesInCorrectOrder[i], result[i].name); } TreeElementUtility.ValidateDepthValues(result); } [Test] public static void TestListToTreeWorks() { // Arrange var list = new List(); list.Add(new TestElement("root", -1)); list.Add(new TestElement("A", 0)); list.Add(new TestElement("B", 0)); list.Add(new TestElement("Bchild", 1)); list.Add(new TestElement("Bchildchild", 2)); list.Add(new TestElement("C", 0)); // Test TestElement root = TreeElementUtility.ListToTree(list); // Assert Assert.AreEqual("root", root.name); Assert.AreEqual(3, root.children.Count); Assert.AreEqual("C", root.children[2].name); Assert.AreEqual("Bchildchild", root.children[1].children[0].children[0].name); } [Test] public static void TestListToTreeThrowsExceptionIfRootIsInvalidDepth() { // Arrange var list = new List(); list.Add(new TestElement("root", 0)); list.Add(new TestElement("A", 1)); list.Add(new TestElement("B", 1)); list.Add(new TestElement("Bchild", 2)); // Test bool catchedException = false; try { TreeElementUtility.ListToTree(list); } catch (Exception) { catchedException = true; } // Assert Assert.IsTrue(catchedException, "We require the root.depth to be -1, here it is: " + list[0].depth); } [Test] public static void FindCommonAncestorsWithinListWorks() { // Arrange var list = new List(); list.Add(new TestElement("root", -1)); list.Add(new TestElement("A", 0)); var b0 = new TestElement("B", 0); var b1 = new TestElement("Bchild", 1); var b2 = new TestElement("Bchildchild", 2); list.Add(b0); list.Add(b1); list.Add(b2); var c0 = new TestElement ("C", 0); list.Add(c0); var f0 = new TestElement("F", 0); var f1 = new TestElement("Fchild", 1); var f2 = new TestElement("Fchildchild", 2); list.Add(f0); list.Add(f1); list.Add(f2); // Init tree structure: set children and parent properties TreeElementUtility.ListToTree(list); // Single element TestElement[] input = {b1}; TestElement[] expectedResult = {b1}; var result = TreeElementUtility.FindCommonAncestorsWithinList(input).ToArray(); Assert.IsTrue(ArrayUtility.ArrayEquals(expectedResult, result), "Single input should return single output"); // Single sub tree input = new[] {b1, b2}; expectedResult = new[] {b1}; result = TreeElementUtility.FindCommonAncestorsWithinList (input).ToArray (); Assert.IsTrue(ArrayUtility.ArrayEquals(expectedResult, result), "Common ancestor should only be b1 "); // Multiple sub trees input = new[] { b0, b2, f0, f2, c0 }; expectedResult = new[] { b0, f0, c0 }; result = TreeElementUtility.FindCommonAncestorsWithinList(input).ToArray(); Assert.IsTrue(ArrayUtility.ArrayEquals(expectedResult, result), "Common ancestor should only be b0, f0, c0"); } #endregion } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/TreeElementUtility.cs.meta ================================================ fileFormatVersion: 2 guid: fd65e8f324e17a344a97ddcf5a8d89d2 timeCreated: 1471616285 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/TreeModel.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; namespace UnityAssetAuditor { // The TreeModel is a utility class working on a list of serializable TreeElements where the order and the depth of each TreeElement define // the tree structure. Note that the TreeModel itself is not serializable (in Unity we are currently limited to serializing lists/arrays) but the // input list is. // The tree representation (parent and children references) are then build internally using TreeElementUtility.ListToTree (using depth // values of the elements). // The first element of the input list is required to have depth == -1 (the hiddenroot) and the rest to have // depth >= 0 (otherwise an exception will be thrown) public class TreeModel where T : TreeElement { IList m_Data; T m_Root; int m_MaxID; public T root { get { return m_Root; } set { m_Root = value; } } public event Action modelChanged; public int numberOfDataElements { get { return m_Data.Count; } } public TreeModel (IList data) { SetData (data); } public T Find (int id) { return m_Data.FirstOrDefault (element => element.id == id); } public void SetData (IList data) { Init (data); } void Init (IList data) { if (data == null) throw new ArgumentNullException("data", "Input data is null. Ensure input is a non-null list."); m_Data = data; if (m_Data.Count > 0) m_Root = TreeElementUtility.ListToTree(data); m_MaxID = m_Data.Max(e => e.id); } public int GenerateUniqueID () { return ++m_MaxID; } public IList GetAncestors (int id) { var parents = new List(); TreeElement T = Find(id); if (T != null) { while (T.parent != null) { parents.Add(T.parent.id); T = T.parent; } } return parents; } public IList GetDescendantsThatHaveChildren (int id) { T searchFromThis = Find(id); if (searchFromThis != null) { return GetParentsBelowStackBased(searchFromThis); } return new List(); } IList GetParentsBelowStackBased(TreeElement searchFromThis) { Stack stack = new Stack(); stack.Push(searchFromThis); var parentsBelow = new List(); while (stack.Count > 0) { TreeElement current = stack.Pop(); if (current.hasChildren) { parentsBelow.Add(current.id); foreach (var T in current.children) { stack.Push(T); } } } return parentsBelow; } public void RemoveElements (IList elementIDs) { IList elements = m_Data.Where (element => elementIDs.Contains (element.id)).ToArray (); RemoveElements (elements); } public void RemoveElements (IList elements) { foreach (var element in elements) if (element == m_Root) throw new ArgumentException("It is not allowed to remove the root element"); var commonAncestors = TreeElementUtility.FindCommonAncestorsWithinList (elements); foreach (var element in commonAncestors) { element.parent.children.Remove (element); element.parent = null; } TreeElementUtility.TreeToList(m_Root, m_Data); Changed(); } public void AddElements (IList elements, TreeElement parent, int insertPosition) { if (elements == null) throw new ArgumentNullException("elements", "elements is null"); if (elements.Count == 0) throw new ArgumentNullException("elements", "elements Count is 0: nothing to add"); if (parent == null) throw new ArgumentNullException("parent", "parent is null"); if (parent.children == null) parent.children = new List(); parent.children.InsertRange(insertPosition, elements.Cast ()); foreach (var element in elements) { element.parent = parent; element.depth = parent.depth + 1; TreeElementUtility.UpdateDepthValues(element); } TreeElementUtility.TreeToList(m_Root, m_Data); Changed(); } public void AddRoot (T root) { if (root == null) throw new ArgumentNullException("root", "root is null"); if (m_Data == null) throw new InvalidOperationException("Internal Error: data list is null"); if (m_Data.Count != 0) throw new InvalidOperationException("AddRoot is only allowed on empty data list"); root.id = GenerateUniqueID (); root.depth = -1; m_Data.Add (root); } public void AddElement (T element, TreeElement parent, int insertPosition) { if (element == null) throw new ArgumentNullException("element", "element is null"); if (parent == null) throw new ArgumentNullException("parent", "parent is null"); if (parent.children == null) parent.children = new List (); parent.children.Insert (insertPosition, element); element.parent = parent; TreeElementUtility.UpdateDepthValues(parent); TreeElementUtility.TreeToList(m_Root, m_Data); Changed (); } public void MoveElements(TreeElement parentElement, int insertionIndex, List elements) { if (insertionIndex < 0) throw new ArgumentException("Invalid input: insertionIndex is -1, client needs to decide what index elements should be reparented at"); // Invalid reparenting input if (parentElement == null) return; // We are moving items so we adjust the insertion index to accomodate that any items above the insertion index is removed before inserting if (insertionIndex > 0) insertionIndex -= parentElement.children.GetRange(0, insertionIndex).Count(elements.Contains); // Remove draggedItems from their parents foreach (var draggedItem in elements) { draggedItem.parent.children.Remove(draggedItem); // remove from old parent draggedItem.parent = parentElement; // set new parent } if (parentElement.children == null) parentElement.children = new List(); // Insert dragged items under new parent parentElement.children.InsertRange(insertionIndex, elements); TreeElementUtility.UpdateDepthValues (root); TreeElementUtility.TreeToList (m_Root, m_Data); Changed (); } void Changed () { if (modelChanged != null) modelChanged (); } } #region Tests class TreeModelTests { [Test] public static void TestTreeModelCanAddElements() { var root = new TreeElement {name = "Root", depth = -1}; var listOfElements = new List(); listOfElements.Add(root); var model = new TreeModel(listOfElements); model.AddElement(new TreeElement { name = "Element" }, root, 0); model.AddElement(new TreeElement { name = "Element " + root.children.Count }, root, 0); model.AddElement(new TreeElement { name = "Element " + root.children.Count }, root, 0); model.AddElement(new TreeElement { name = "Sub Element" }, root.children[1], 0); // Assert order is correct string[] namesInCorrectOrder = { "Root", "Element 2", "Element 1", "Sub Element", "Element" }; Assert.AreEqual(namesInCorrectOrder.Length, listOfElements.Count, "Result count does not match"); for (int i = 0; i < namesInCorrectOrder.Length; ++i) Assert.AreEqual(namesInCorrectOrder[i], listOfElements[i].name); // Assert depths are valid TreeElementUtility.ValidateDepthValues(listOfElements); } [Test] public static void TestTreeModelCanRemoveElements() { var root = new TreeElement { name = "Root", depth = -1 }; var listOfElements = new List(); listOfElements.Add(root); var model = new TreeModel(listOfElements); model.AddElement(new TreeElement { name = "Element" }, root, 0); model.AddElement(new TreeElement { name = "Element " + root.children.Count }, root, 0); model.AddElement(new TreeElement { name = "Element " + root.children.Count }, root, 0); model.AddElement(new TreeElement { name = "Sub Element" }, root.children[1], 0); model.RemoveElements(new[] { root.children[1].children[0], root.children[1] }); // Assert order is correct string[] namesInCorrectOrder = { "Root", "Element 2", "Element" }; Assert.AreEqual(namesInCorrectOrder.Length, listOfElements.Count, "Result count does not match"); for (int i = 0; i < namesInCorrectOrder.Length; ++i) Assert.AreEqual(namesInCorrectOrder[i], listOfElements[i].name); // Assert depths are valid TreeElementUtility.ValidateDepthValues(listOfElements); } } #endregion } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/TreeModel.cs.meta ================================================ fileFormatVersion: 2 guid: 6f9fab1cf2636a6439c644bf08108abb timeCreated: 1472122507 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/TreeViewWithTreeModel.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; namespace UnityAssetAuditor { internal class TreeViewItem : TreeViewItem where T : TreeElement { public T data { get; set; } public TreeViewItem (int id, int depth, string displayName, T data) : base (id, depth, displayName) { this.data = data; } } public class TreeViewWithTreeModel : TreeView where T : TreeElement { TreeModel m_TreeModel; readonly List m_Rows = new List(100); public event Action treeChanged; public TreeModel treeModel { get { return m_TreeModel; } } public event Action> beforeDroppingDraggedItems; public TreeViewWithTreeModel (TreeViewState state, TreeModel model) : base (state) { Init (model); } public TreeViewWithTreeModel (TreeViewState state, MultiColumnHeader multiColumnHeader, TreeModel model) : base(state, multiColumnHeader) { Init (model); } void Init (TreeModel model) { m_TreeModel = model; m_TreeModel.modelChanged += ModelChanged; } void ModelChanged () { if (treeChanged != null) treeChanged (); Reload (); } protected override TreeViewItem BuildRoot() { int depthForHiddenRoot = -1; return new TreeViewItem(m_TreeModel.root.id, depthForHiddenRoot, m_TreeModel.root.name); } protected override IList BuildRows (TreeViewItem root) { if (m_TreeModel.root == null) { Debug.LogError ("tree model root is null. did you call SetData()?"); } m_Rows.Clear (); if (!string.IsNullOrEmpty(searchString)) { Search (m_TreeModel.root, searchString, m_Rows); } else { if (m_TreeModel.root.hasChildren) AddChildrenRecursive(m_TreeModel.root, 0, m_Rows); } // We still need to setup the child parent information for the rows since this // information is used by the TreeView internal logic (navigation, dragging etc) SetupParentsAndChildrenFromDepths (root, m_Rows); return m_Rows; } void AddChildrenRecursive (T parent, int depth, IList newRows) { foreach (T child in parent.children) { var item = new TreeViewItem(child.id, depth, child.name, child); newRows.Add(item); if (child.hasChildren) { if (IsExpanded(child.id)) { AddChildrenRecursive (child, depth + 1, newRows); } else { item.children = CreateChildListForCollapsedParent(); } } } } void Search(T searchFromThis, string search, List result) { if (string.IsNullOrEmpty(search)) throw new ArgumentException("Invalid search: cannot be null or empty", "search"); const int kItemDepth = 0; // tree is flattened when searching Stack stack = new Stack(); foreach (var element in searchFromThis.children) stack.Push((T)element); while (stack.Count > 0) { T current = stack.Pop(); // Matches search? if (current.name.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0) { result.Add(new TreeViewItem(current.id, kItemDepth, current.name, current)); } if (current.children != null && current.children.Count > 0) { foreach (var element in current.children) { stack.Push((T)element); } } } SortSearchResult(result); } protected virtual void SortSearchResult (List rows) { rows.Sort (); // sort by displayName by default, can be overriden for multicolumn solutions } protected override IList GetAncestors (int id) { return m_TreeModel.GetAncestors(id); } protected override IList GetDescendantsThatHaveChildren (int id) { return m_TreeModel.GetDescendantsThatHaveChildren(id); } // Dragging //----------- const string k_GenericDragID = "GenericDragColumnDragging"; protected override bool CanStartDrag (CanStartDragArgs args) { return true; } protected override void SetupDragAndDrop(SetupDragAndDropArgs args) { if (hasSearch) return; DragAndDrop.PrepareStartDrag(); var draggedRows = GetRows().Where(item => args.draggedItemIDs.Contains(item.id)).ToList(); DragAndDrop.SetGenericData(k_GenericDragID, draggedRows); DragAndDrop.objectReferences = new UnityEngine.Object[] { }; // this IS required for dragging to work string title = draggedRows.Count == 1 ? draggedRows[0].displayName : "< Multiple >"; DragAndDrop.StartDrag (title); } protected override DragAndDropVisualMode HandleDragAndDrop (DragAndDropArgs args) { // Check if we can handle the current drag data (could be dragged in from other areas/windows in the editor) var draggedRows = DragAndDrop.GetGenericData(k_GenericDragID) as List; if (draggedRows == null) return DragAndDropVisualMode.None; // Parent item is null when dragging outside any tree view items. switch (args.dragAndDropPosition) { case DragAndDropPosition.UponItem: case DragAndDropPosition.BetweenItems: { bool validDrag = ValidDrag(args.parentItem, draggedRows); if (args.performDrop && validDrag) { T parentData = ((TreeViewItem)args.parentItem).data; OnDropDraggedElementsAtIndex(draggedRows, parentData, args.insertAtIndex == -1 ? 0 : args.insertAtIndex); } return validDrag ? DragAndDropVisualMode.Move : DragAndDropVisualMode.None; } case DragAndDropPosition.OutsideItems: { if (args.performDrop) OnDropDraggedElementsAtIndex(draggedRows, m_TreeModel.root, m_TreeModel.root.children.Count); return DragAndDropVisualMode.Move; } default: Debug.LogError("Unhandled enum " + args.dragAndDropPosition); return DragAndDropVisualMode.None; } } public virtual void OnDropDraggedElementsAtIndex (List draggedRows, T parent, int insertIndex) { if (beforeDroppingDraggedItems != null) beforeDroppingDraggedItems (draggedRows); var draggedElements = new List (); foreach (var x in draggedRows) draggedElements.Add (((TreeViewItem) x).data); var selectedIDs = draggedElements.Select (x => x.id).ToArray(); m_TreeModel.MoveElements (parent, insertIndex, draggedElements); SetSelection(selectedIDs, TreeViewSelectionOptions.RevealAndFrame); } bool ValidDrag(TreeViewItem parent, List draggedItems) { TreeViewItem currentParent = parent; while (currentParent != null) { if (draggedItems.Contains(currentParent)) return false; currentParent = currentParent.parent; } return true; } } } ================================================ FILE: Assets/Editor/AssetAuditor/Scripts/TreeViewWithTreeModel.cs.meta ================================================ fileFormatVersion: 2 guid: 68fe63fd42552e7418aac450b41b8afb timeCreated: 1472481611 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Scripts.meta ================================================ fileFormatVersion: 2 guid: d8004df83cf28884ab8e34a091ddd853 folderAsset: yes timeCreated: 1481588073 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Texture/DefaultTexture.jpg.meta ================================================ fileFormatVersion: 2 guid: e762cc062d5c5411a9264437e5c9c09e timeCreated: 1502701589 licenseType: Pro TextureImporter: fileIDToRecycleName: {} serializedVersion: 4 mipmaps: mipMapMode: 0 enableMipMap: 1 sRGBTexture: 1 linearTexture: 0 fadeOut: 0 borderMipMap: 0 mipMapsPreserveCoverage: 0 alphaTestReferenceValue: 0.5 mipMapFadeDistanceStart: 1 mipMapFadeDistanceEnd: 3 bumpmap: convertToNormalMap: 0 externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 isReadable: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 seamlessCubemap: 0 textureFormat: 1 maxTextureSize: 2048 textureSettings: serializedVersion: 2 filterMode: -1 aniso: -1 mipBias: -1 wrapU: -1 wrapV: -1 wrapW: -1 nPOTScale: 1 lightmap: 0 compressionQuality: 50 spriteMode: 0 spriteExtrude: 1 spriteMeshType: 1 alignment: 0 spritePivot: {x: 0.5, y: 0.5} spriteBorder: {x: 0, y: 0, z: 0, w: 0} spritePixelsToUnits: 100 alphaUsage: 1 alphaIsTransparency: 0 spriteTessellationDetail: -1 textureType: 0 textureShape: 1 maxTextureSizeSet: 0 compressionQualitySet: 0 textureFormatSet: 0 platformSettings: - buildTarget: DefaultTexturePlatform maxTextureSize: 2048 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] physicsShape: [] spritePackingTag: userData: '{"RuleName":"nromalmaps","WildCardMatchType":0,"WildCard":"nrm","AssetGuid":"0bc6347fd6d584af3ab1a06182982dad","assetType":0}' assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor/Texture.meta ================================================ fileFormatVersion: 2 guid: 2e33a099c182b4826a46f527b242a2cd folderAsset: yes timeCreated: 1502705399 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor/AssetAuditor.meta ================================================ fileFormatVersion: 2 guid: 36d6f139a16b47a469947e0afd23b31d folderAsset: yes timeCreated: 1479437964 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Editor.meta ================================================ fileFormatVersion: 2 guid: fa69737d10241b54391c55189b99b9c6 folderAsset: yes timeCreated: 1440440842 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: ================================================ FILE: LICENSE ================================================ MIT X11 Copyright (C) 2017 Unity Technologies ApS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL UNITY TECHNOLOGIES APS OR ANY OF ITS AFFILIATES (“UNITY”) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the “Unity” name/mark shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Unity. ================================================ FILE: README.md ================================================ # Asset Auditor The asset auditor tool is designed to allow you to create sets of rules for the import settings for your assets in a Unity project and fix any assets that do not comply to those rules. #### The new audit rule window. This window allows you to create a new rule for you assets. **Rule Name** - The first step is to give the rule a name. This can be whatever you desire it to be and will be name given to the proxy/dummy asset stored in the project. The reason for using a proxy/dummy assets is to provide a full inspector for the asset so that you modify the settings properties in a familiar manner to modify any other asset in your project. **Wild Card Matching Type** - The wild card matching type has two options. **Name Contains** - Name contains works by matching the providing string to any of the asset names in the project that are of the same asset type as the asset rule. So If you set the name contains string to "nrm" on texture based rule. Any texture asset that has nrm in the name will be found by the rule when inspecting in audit view. **Regex** - This work the same as name contains but uses full regex pattern matching. N.B. This is C# regex so there is no need for start and end / **Wild Card** - This is the string that is used in the Wild Card Matching Type for finding assets that the rule should effect. **Selective Mode** - This lets you create a rule that will only overwrite certain settings from a drop down The drop down enabling selective mode creates allows you to add as many selective element as you like and selectwhich rules you would like to override from it. **Rule Type** - This determines what assets the rule should affect. Currently there are three supported asset types. Texture, for all your imported textures, Model, for all your imported models and Audio for the imported audio files. During the creation of the rule new audit rule window will display a list of the assets that will be found by that rule so you can preview your results. #### Audit View The audit view allows you to see the assets that the rule wild card will cover and apply the desired import settings to if desired. It also allows for modification of the wildcard property in case of naming convention changes. **Rule** - this is a drop down list of the available rules in the project. **Wild Card** - This can be used to modify the wild card that has been supplied to original rule. **Selective** - A drop down showing the properties that will be overriden. **Search bar** - This allows for searching of the the assets that are in the tree view. **Tree view** - This shows the subset of the project view that contains the assets that the rule has found through its search based on the wild card and wild card matching type. **Tool Bar** - This contains three options **Expand all** - This expands every element in the tree view **Collapse all** - This collapses the entire tree view **Fix all**- This fixes every asset that has been found to not match rules import settings. ## In Development Improvements to selective mode. UI improvements. Folder level overrides.