Add main mesh fixes

This commit is contained in:
Toomas Tamm
2021-02-23 02:13:14 +02:00
parent c89f326c37
commit 989424e659
139 changed files with 8101 additions and 2033 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cbedea52d931409a8b62256873637225
timeCreated: 1596214557

View File

@@ -0,0 +1,94 @@
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace Asset_Cleaner {
class AufWindow : EditorWindow {
[SerializeField] PersistentUndoRedoState _persistentUndo;
[MenuItem("Window/- Asset Cleaner %L")]
static void OpenActiveWindow() {
GetWindow<AufWindow>();
}
// restore window state after recompilation
void OnEnable() {
var wd = Globals<WindowData>.Value = new WindowData();
wd.Window = this;
var firstTime = _persistentUndo == null;
Globals<PersistentUndoRedoState>.Value = _persistentUndo ?? new PersistentUndoRedoState();
Globals<BacklinkStore>.Value = new BacklinkStore();
var config = Globals<Config>.Value = new Config();
PersistenceUtils.Load(ref config);
if (firstTime || !config.RebuildCacheOnDemand)
Globals<BacklinkStore>.Value.Init();
EditorApplication.update += Upd;
EditorApplication.projectWindowItemOnGUI += ProjectViewGui.OnProjectWindowItemOnGui;
AufCtx.TryInitWorld();
// need to close window in case of Asset Cleaner uninstalled
if (!CleanerStyleAsset.Style.TryFindSelf(out wd.Style))
ForceClose();
}
void OnGUI() {
var store = Globals<BacklinkStore>.Value;
if (!store.Initialized) {
// prevent further window GUI rendering
if (!GUILayout.Button("Initialize Cache")) return;
var stopwatch = new Stopwatch();
stopwatch.Start();
store.Init();
stopwatch.Stop();
Globals<Config>.Value.InitializationTime = $"Initialized in {stopwatch.Elapsed.TotalSeconds:N} s";
AufCtx.World.NewEntityWith(out RequestRepaintEvt _);
}
AufCtx.OnGuiGroup.Run();
}
static void Upd() {
if (AufCtx.World == null) {
AufCtx.DestroyWorld();
return;
}
if (!Globals<BacklinkStore>.Value.Initialized) return;
AufCtx.UpdateGroup.Run();
}
bool _closing;
void ForceClose() {
if (_closing) return;
_closing = true;
Close();
EditorWindow.DestroyImmediate(this);
}
void OnDisable() {
if (AufCtx.Destroyed) return;
_persistentUndo = Globals<PersistentUndoRedoState>.Value;
AufCtx.UpdateGroup.Destroy();
AufCtx.OnGuiGroup.Destroy();
AufCtx.DestroyWorld();
Globals<Config>.Value = default;
Globals<PersistentUndoRedoState>.Value = default;
Globals<WindowData>.Value = default;
EditorApplication.update -= Upd;
EditorApplication.projectWindowItemOnGUI -= ProjectViewGui.OnProjectWindowItemOnGui;
// need to close window in case of Asset Cleaner uninstalled
if (!CleanerStyleAsset.Style.TryFindSelf(out _))
ForceClose();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44ebd7bbb2bb41b4ba0187901e8d583f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using UnityEditor;
namespace Asset_Cleaner {
class ProcessAllAssets : AssetPostprocessor {
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) {
if (!AufCtx.InitStarted) return;
if (!Globals<BacklinkStore>.Value.Initialized) return;
var store = Globals<BacklinkStore>.Value;
var length = movedAssets.Length;
for (var i = 0; i < length; i++)
store.Replace(movedFromAssetPaths[i], movedAssets[i]);
foreach (var path in deletedAssets)
store.Remove(path);
foreach (var path in importedAssets)
store.RebuildFor(path, true);
store.UpdateUnusedAssets();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17c1f845fa88d38448c4cf65e9745f30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using UnityEditor;
using UnityEngine;
namespace Asset_Cleaner {
static class ProjectViewGui {
static CleanerStyleAsset.Style _style = Globals<WindowData>.Value.Style;
public static void OnProjectWindowItemOnGui(string guid, Rect rect) {
if (!Globals<Config>.Value.MarkRed) return;
var store = Globals<BacklinkStore>.Value;
if (!store.Initialized) return;
var path = AssetDatabase.GUIDToAssetPath(guid);
ShowRowQuantity(rect, path, store);
long size = 0;
var _ = store.UnusedFiles.TryGetValue(path, out size) || store.UnusedScenes.TryGetValue(path, out size);
if (SearchUtils.IsUnused(path)) {
var buf = GUI.color;
{
GUI.color = _style.RedHighlight;
GUI.Box(rect, string.Empty);
}
GUI.color = buf;
GUI.Label(rect, CommonUtils.BytesToString(size), _style.ProjectViewCounterLabel);
}
}
static void ShowRowQuantity(Rect rect, string path, BacklinkStore backlinkStore) {
if (!AssetDatabase.IsValidFolder(path))
return;
backlinkStore.FoldersWithQty.TryGetValue(path, out var folderWithQty);
var cntFiles = folderWithQty?.UnusedFilesQty ?? 0;
var cntScenes = folderWithQty?.UnusedScenesQty ?? 0;
long size = folderWithQty?.UnusedSize ?? 0;
if (cntFiles == 0 && cntScenes == 0) return;
var countStr = cntFiles + cntScenes > 0 ? $"{cntFiles} | {cntScenes} ({CommonUtils.BytesToString(size)})" : "";
GUI.Label(rect, countStr, _style.ProjectViewCounterLabel);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a17718cc645348b0bc4ed6d7e514ab30
timeCreated: 1589032770

View File

@@ -0,0 +1,56 @@
using System;
using Leopotam.Ecs;
using UnityEngine;
using static Asset_Cleaner.AufCtx;
namespace Asset_Cleaner {
class SysProcessSearch : IEcsRunSystem {
EcsFilter<SelectionChanged> _from = null;
EcsFilter<Result, SearchResultGui, InSceneResult> SceneResultRows = null;
EcsFilter<SceneResult, SceneDetails> ScenePaths = null;
EcsFilter<SearchArg>.Exclude<InSceneResult> SearchArgMain = null;
EcsFilter<Result, SearchResultGui, FileResultTag> FileResultRows = null;
public void Run() {
if (_from.IsEmpty())
return;
SearchArgMain.AllDestroy();
ScenePaths.AllDestroy();
FileResultRows.AllDestroy();
SceneResultRows.AllDestroy();
var wd = Globals<WindowData>.Value;
if (wd.Window)
wd.Window.Repaint();
foreach (var i in _from.Out(out var get1, out _)) {
var t1 = get1[i];
if (!t1.Target) continue;
wd.FindFrom = t1.From;
try {
switch (t1.From) {
case FindModeEnum.Scene:
World.NewEntityWith(out SearchArg st);
SearchUtils.Init(st, t1.Target, t1.Scene);
SearchUtils.InScene(st, t1.Scene);
break;
case FindModeEnum.File:
World.NewEntityWith(out SearchArg arg);
SearchUtils.Init(arg, t1.Target);
SearchUtils.FilesThatReference(arg);
SearchUtils.ScenesThatContain(t1.Target);
break;
}
}
catch (Exception e) {
Debug.LogException(e);
}
}
_from.AllUnset<SelectionChanged>();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: afd561b0c1384bcc82976f2624afa895
timeCreated: 1577287503

View File

@@ -0,0 +1,30 @@
using Leopotam.Ecs;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Asset_Cleaner {
class RequestRepaintEvt { }
class SysRepaintWindow : IEcsRunSystem, IEcsInitSystem {
EcsFilter<RequestRepaintEvt> Repaint = null;
public void Init() {
var wd = Globals<WindowData>.Value;
wd.SceneFoldout = new GUIContent(AssetPreview.GetMiniTypeThumbnail(typeof(SceneAsset)));
wd.ExpandScenes = true;
wd.ExpandFiles = true;
wd.ScrollPos = Vector2.zero;
wd.Window.titleContent = new GUIContent("Asset Cleaner v1.0");
}
public void Run() {
var wd = Globals<WindowData>.Value;
if (Repaint.IsEmpty()) return;
wd.Window.Repaint();
InternalEditorUtility.RepaintAllViews();
Repaint.AllDestroy();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 673a2486762b4e3da784f1657b90bb93
timeCreated: 1577288020

View File

@@ -0,0 +1,44 @@
using Leopotam.Ecs;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
namespace Asset_Cleaner {
class SceneToClose : IEcsAutoReset {
public Scene Scene;
public int SelectionId;
public bool ForceClose;
public void Reset() {
ForceClose = default;
Scene = default;
SelectionId = default;
}
}
class SysSceneCleanup : IEcsRunSystem, IEcsDestroySystem {
EcsFilter<SceneToClose> ScenesToClose = default;
public void Run() {
if (ScenesToClose.IsEmpty()) return;
var selectionId = Globals<PersistentUndoRedoState>.Value.Id;
foreach (var i in ScenesToClose.Out(out var g1, out var entities)) {
var s = g1[i].Scene;
if (g1[i].SelectionId == selectionId && !g1[i].ForceClose) continue;
if (Selection.activeGameObject && Selection.activeGameObject.scene == s) continue;
if (s.isLoaded) EditorSceneManager.CloseScene(s, removeScene: true);
entities[i].Destroy();
}
}
// close scenes on window close
public void Destroy() {
foreach (var i in ScenesToClose.Out(out var g1, out _)) {
var s = g1[i].Scene;
if (s.isLoaded) EditorSceneManager.CloseScene(s, removeScene: true);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d42cae4aa3ff4500948ddd623a270120
timeCreated: 1596142629

View File

@@ -0,0 +1,190 @@
using System.Linq;
using System.Runtime.InteropServices;
using Leopotam.Ecs;
using UnityEditor;
using UnityEngine;
using static Asset_Cleaner.AufCtx;
namespace Asset_Cleaner {
class CleanupPrevArg { }
class UndoEvt { }
class RedoEvt { }
class SysUndoRedoSelection : IEcsRunSystem, IEcsInitSystem, IEcsDestroySystem {
EcsFilter<UndoEvt> UndoEvt = default;
EcsFilter<RedoEvt> RedoEvt = default;
bool _preventHistoryInsert;
bool _preventSelectionSet;
public void Init() {
Undo.undoRedoPerformed += OnUndoRedoPerformed;
Selection.selectionChanged += OnSelectionChanged;
Globals<UndoRedoState>.Value = new UndoRedoState();
if (Globals<PersistentUndoRedoState>.Value.History.Count > 0)
_preventHistoryInsert = true;
OnSelectionChanged(); //init selection
}
public void Destroy() {
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
Selection.selectionChanged -= OnSelectionChanged;
Globals<UndoRedoState>.Value = default;
}
public void Run() {
MouseInput();
if (UndoEvt.IsEmpty() && RedoEvt.IsEmpty()) return;
Counters(undo: !UndoEvt.IsEmpty(), redo: !RedoEvt.IsEmpty(), false);
_preventHistoryInsert = true;
if (!_preventSelectionSet) {
(var history, var id) = Globals<PersistentUndoRedoState>.Value;
SelectionEntry entry = history[id];
if (entry.Valid())
Selection.objects = entry.IsGuids
? entry.Guids.Select(AssetDatabase.GUIDToAssetPath).Select(AssetDatabase.LoadAssetAtPath<Object>).Where(obj => obj).ToArray()
: entry.SceneObjects;
}
_preventSelectionSet = false;
UndoEvt.AllDestroy();
RedoEvt.AllDestroy();
}
static void Counters(bool undo, bool redo, bool insertToHistory) {
var state = Globals<PersistentUndoRedoState>.Value;
World.NewEntityWith(out RequestRepaintEvt _);
const int MinId = 0;
if (insertToHistory) {
var entry = new SelectionEntry();
var count = state.History.Count - 1 - state.Id;
if (count > 0)
state.History.RemoveRange(state.Id + 1, count);
state.History.Add(entry);
state.Id = MaxId();
if (Selection.assetGUIDs.Length > 0) {
entry.IsGuids = true;
entry.Guids = Selection.assetGUIDs;
}
else {
entry.SceneObjects = Selection.objects;
}
}
if (undo) {
// loop to skip invalid
while (true) {
state.Id -= 1;
if (state.Id < MinId) break;
if (state.History[state.Id].Valid()) break;
}
}
if (redo) {
// loop to skip invalid
while (true) {
state.Id += 1;
if (state.Id > MaxId()) break;
if (state.History[state.Id].Valid()) break;
}
}
state.Id = Mathf.Clamp(state.Id, MinId, MaxId());
var undoRedoState = Globals<UndoRedoState>.Value;
undoRedoState.UndoEnabled = state.Id != MinId;
undoRedoState.RedoEnabled = state.Id != MaxId();
int MaxId() => Mathf.Max(0, state.History.Count - 1);
}
void OnSelectionChanged() {
World.NewEntityWith(out RequestRepaintEvt _);
if (Globals<Config>.Value.Locked) return;
Counters(undo: false, redo: false, insertToHistory: !_preventHistoryInsert);
_preventHistoryInsert = false;
World.NewEntityWith(out SelectionChanged comp);
World.NewEntityWith(out CleanupPrevArg _);
var go = Selection.activeGameObject;
if (go && go.scene.IsValid()) {
comp.From = FindModeEnum.Scene;
comp.Target = go;
comp.Scene = go.scene;
}
else {
var guids = Selection.assetGUIDs;
// comp.Guids = Selection.assetGUIDs;
bool any = guids != null && guids.Length > 0;
if (any) {
comp.From = FindModeEnum.File;
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
comp.Target = AssetDatabase.LoadAssetAtPath<Object>(path);
}
else {
comp.From = FindModeEnum.None;
comp.Target = null;
}
}
}
// prevents selection history flooding
void OnUndoRedoPerformed() {
// below is a hackish way to catch Undo/Redo from editor
if (!Undo.GetCurrentGroupName().Equals("Selection Change")) return;
var evt = Event.current;
if (evt == null) return;
if (evt.rawType != EventType.KeyDown) return;
switch (evt.keyCode) {
case KeyCode.Z:
World.NewEntityWith(out UndoEvt _);
_preventSelectionSet = true; // prevent manual Selection set
break;
case KeyCode.Y:
World.NewEntityWith(out RedoEvt _);
_preventSelectionSet = true;
break;
}
}
void MouseInput() {
if (_nextClick > EditorApplication.timeSinceStartup) return;
var any = false;
if (Pressed(0x5)) {
World.NewEntityWith(out UndoEvt _);
any = true;
}
if (Pressed(0x6)) {
World.NewEntityWith(out RedoEvt _);
any = true;
}
if (any)
_nextClick = EditorApplication.timeSinceStartup + 0.25;
}
#if UNITY_EDITOR_WIN
[DllImport("USER32.dll")]
static extern short GetKeyState(int keycode);
#else
static short GetKeyState(int keycode) => 0;
#endif
double _nextClick;
// 5 back, 6 fw
static bool Pressed(int keyCode) => (GetKeyState(keyCode) & 0x100) != 0;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6e6a2f49ce174cabbc560ea8e3e9975c
timeCreated: 1596008857

View File

@@ -0,0 +1,970 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Leopotam.Ecs;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
namespace Asset_Cleaner {
class FileResultTag { }
enum TargetTypeEnum {
File = 0,
Directory = 1,
Scene = 2,
ObjectInScene = 3,
ObjectInStage = 4
}
class SysWindowGui : IEcsRunSystem, IEcsInitSystem {
EcsFilter<Result, SearchResultGui, InSceneResult> SceneResultRows = null;
EcsFilter<SceneResult, SceneDetails> ScenePaths = null;
EcsFilter<SearchArg>.Exclude<InSceneResult> SearchArgMain = null;
EcsFilter<Result, SearchResultGui, FileResultTag> FileResultRows = null;
public void Init() {
BacklinkStoreDirty(true);
VisualSettingDirty(true);
}
public void Run() {
var windowData = Globals<WindowData>.Value;
_toolbarSelection = GUILayout.Toolbar(_toolbarSelection, _toolbarStrings, GUILayout.ExpandWidth(false));
var conf = Globals<Config>.Value;
switch (_toolbarSelection) {
case 0: {
ShowTabMain(conf, windowData);
break;
}
case 1: {
ShowTabSettings(conf);
break;
}
}
}
string[] _toolbarStrings = {"Main", "Settings"};
const int _progressBarShowFromLevel = 10;
int _toolbarSelection = 0;
int _settingIgnoredPathsHash1;
bool BacklinkStoreDirty(bool set) {
var res = Hash() != _settingIgnoredPathsHash1;
if (set) _settingIgnoredPathsHash1 = Hash();
return res;
int Hash() {
var conf = Globals<Config>.Value;
return DirtyUtils.HashCode(conf.IgnorePathContainsCombined,
conf.IgnoreMaterial,
conf.IgnoreScriptable);
}
}
int _settingCodeHash1;
bool VisualSettingDirty(bool set) {
var res = Hash() != _settingCodeHash1;
if (set) _settingCodeHash1 = Hash();
return res;
int Hash() {
var conf = Globals<Config>.Value;
return DirtyUtils.HashCode(
conf.MarkRed,
conf.ShowInfoBox,
conf.RebuildCacheOnDemand);
}
}
void ShowTabSettings(Config conf) {
using (new EditorGUILayout.VerticalScope()) {
EditorGUILayout.Space();
var enabled = GUI.enabled;
GUI.enabled = true;
using (new EditorGUILayout.VerticalScope()) {
conf.MarkRed = GUILayout.Toggle(conf.MarkRed, "Display counters and red overlay in Project View");
conf.ShowInfoBox = GUILayout.Toggle(conf.ShowInfoBox, "Help suggestions");
conf.RebuildCacheOnDemand = GUILayout.Toggle(conf.RebuildCacheOnDemand, "Rebuild cache on demand (when scripts are updated often)");
EditorGUILayout.Space();
conf.IgnoreMaterial = GUILayout.Toggle(conf.IgnoreMaterial, "Ignore Materials");
conf.IgnoreScriptable = GUILayout.Toggle(conf.IgnoreScriptable, "Ignore ScriptableObjects");
}
EditorGUILayout.Space();
GUI.enabled = enabled;
EditorGUILayout.LabelField("Ignore Path(s) contains:");
conf.IgnorePathContainsCombined = GUILayout.TextArea(conf.IgnorePathContainsCombined);
using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(false))) {
EditorGUILayout.Space();
var previous = GUI.enabled;
GUI.enabled = BacklinkStoreDirty(false) || VisualSettingDirty(false);
if (GUILayout.Button("Apply")) {
conf.IgnorePathContains = conf.IgnorePathContainsCombined.Split(';')
.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
Apply();
}
GUI.enabled = previous;
var selectedGuids = Selection.assetGUIDs;
var assetPaths = new List<string>();
if (selectedGuids.Length > 0) {
foreach (var guid in selectedGuids) {
var realAssetPath = AssetDatabase.GUIDToAssetPath(guid);
var obj = AssetDatabase.LoadAssetAtPath<Object>(realAssetPath);
var assetPath = realAssetPath.Replace("Assets/", string.Empty);
if (obj is DefaultAsset &&
!conf.IgnorePathContains.Any(p => (StringComparer.Ordinal.Equals(p, assetPath)))) {
assetPaths.Add(assetPath);
}
}
}
GUI.enabled = (assetPaths.Count > 0);
var foldersList = string.Join(", ", assetPaths);
if (GUILayout.Button("Add Selected Path")) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to add these folder(s) to ignored list: \"{foldersList}\"?",
ok: "Ignore",
cancel: "Cancel");
if (choice) {
conf.IgnorePathContainsCombined += $"{foldersList};";
conf.IgnorePathContains = conf.IgnorePathContainsCombined.Split(';')
.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
Apply();
}
}
GUI.enabled = true;
if (GUILayout.Button("Reset")) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to reset to the factory settings?",
ok: "Reset",
cancel: "Cancel");
if (choice) {
var serializable = AufSerializableData.Default();
AufSerializableData.OnDeserialize(in serializable, ref conf);
Apply();
}
}
GUI.enabled = previous;
}
EditorGUILayout.Space();
EditorGUILayout.LabelField(conf.InitializationTime);
var buf = GUI.enabled;
GUI.enabled = Selection.objects.Length > 0;
if (GUILayout.Button("Reserialize selected assets", GUILayout.ExpandWidth(false))) {
var paths = Selection.objects.Select(AssetDatabase.GetAssetPath);
AssetDatabase.ForceReserializeAssets(paths);
EditorApplication.ExecuteMenuItem("File/Save Project");
AssetDatabase.Refresh();
}
GUI.enabled = buf;
EditorGUILayout.Space();
}
void Apply() {
var rebuild = BacklinkStoreDirty(true);
VisualSettingDirty(true);
PersistenceUtils.Save(in conf);
AufCtx.World.NewEntityWith(out RequestRepaintEvt _);
if (rebuild)
Globals<BacklinkStore>.Value.UpdateUnusedAssets();
InternalEditorUtility.RepaintAllViews();
}
}
void ShowTabMain(Config conf, WindowData windowData) {
var store = Globals<BacklinkStore>.Value;
EditorGUIUtility.labelWidth = windowData.Window.position.width * .7f;
int Hash() => DirtyUtils.HashCode(conf.Locked);
var active = SearchArgMain.Get1[0];
if (conf.Locked && (windowData.FindFrom == FindModeEnum.File &&
(active == null || active.Main == null || !AssetDatabase.Contains(active.Main)))) {
conf.Locked = false;
AufCtx.World.NewEntityWith(out RequestRepaintEvt _);
}
var style = windowData.Style;
var hash = Hash();
if (hash != Hash()) {
PersistenceUtils.Save(in conf);
AufCtx.World.NewEntityWith(out RequestRepaintEvt _);
}
// if (Globals<WindowData>.Get() == null) return;
EditorGUILayout.Space();
SearchArg arg = default;
foreach (var i in SearchArgMain) {
arg = SearchArgMain.Get1[i];
if (arg != null && arg.Main != null) {
break;
}
}
if (arg == default)
return;
var targetTypeEnum = GetTargetType(windowData, arg?.Main);
BacklinkStore.UnusedQty unusedQty = new BacklinkStore.UnusedQty(0, 0, 0);
using (new EditorGUILayout.HorizontalScope()) {
var enabledBuf = GUI.enabled;
var selectedGuids = Selection.assetGUIDs;
var undoRedoState = Globals<UndoRedoState>.Value;
GUI.enabled = selectedGuids != null && !conf.Locked && undoRedoState.UndoEnabled;
if (GUILayout.Button(style.ArrowL, style.ArrowBtn)) {
AufCtx.World.NewEntityWith(out UndoEvt _);
}
GUI.enabled = selectedGuids != null && !conf.Locked && undoRedoState.RedoEnabled;
if (GUILayout.Button(style.ArrowR, style.ArrowBtn)) {
AufCtx.World.NewEntityWith(out RedoEvt _);
}
GUI.enabled = enabledBuf;
if (conf.Locked) {
if (GUILayout.Button(style.Lock, style.LockBtn)) {
AufCtx.World.NewEntityWith(out SelectionChanged selectionChanged);
conf.Locked = false;
if (Selection.activeObject != arg.Target) {
selectionChanged.From = FindModeEnum.Scene;
selectionChanged.Scene = SceneManager.GetActiveScene();
selectionChanged.Target = Selection.activeObject;
}
else if (Selection.assetGUIDs is string[] guids) {
// todo show info box multiple selection is unsupported
if (guids.Length > 0) {
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
selectionChanged.Target = AssetDatabase.LoadAssetAtPath<Object>(path);
switch (Selection.selectionChanged.Target) {
case DefaultAsset _:
selectionChanged.From = FindModeEnum.File;
break;
case GameObject go when go.scene.isLoaded:
selectionChanged.From = FindModeEnum.Scene;
selectionChanged.Scene = SceneManager.GetActiveScene();
break;
default:
selectionChanged.From = FindModeEnum.File;
break;
}
}
else if (Selection.activeObject is GameObject go && go.scene.isLoaded) {
selectionChanged.From = FindModeEnum.Scene;
selectionChanged.Target = Selection.activeObject;
selectionChanged.Scene = SceneManager.GetActiveScene();
}
}
}
}
else {
var enabled = GUI.enabled;
GUI.enabled = selectedGuids != null && selectedGuids.Length == 1;
if (GUILayout.Button(style.Unlock, style.UnlockBtn)) {
conf.Locked = true;
}
GUI.enabled = enabled;
}
unusedQty = ShowObjectName(store, windowData, targetTypeEnum, arg, selectedGuids);
}
bool isMultiSelect = Selection.assetGUIDs != null && Selection.assetGUIDs.Length > 1;
if (conf.ShowInfoBox) {
if (isMultiSelect && (unusedQty.UnusedFilesQty + unusedQty.UnusedScenesQty > 0)) {
var msgUnusedFiles = (unusedQty.UnusedFilesQty > 0)
? $"unused files ({unusedQty.UnusedFilesQty}),"
: "";
var msgUnusedScenes = (unusedQty.UnusedScenesQty > 0)
? $"unused scenes ({unusedQty.UnusedScenesQty}),"
: "";
var msgMultiSelect = $"This multi-selection contains: " +
msgUnusedFiles + msgUnusedScenes +
$"\nYou could delete them pressing corresponding button to the right.";
EditorGUILayout.HelpBox(msgMultiSelect, MessageType.Info);
}
else if (TryGetHelpInfo(arg, out var msg, out var msgType)) {
EditorGUILayout.HelpBox(msg, msgType);
}
}
if (targetTypeEnum != TargetTypeEnum.Directory && !isMultiSelect) {
var windowData2 = Globals<WindowData>.Value;
EditorGUILayout.BeginVertical();
{
windowData2.ScrollPos = EditorGUILayout.BeginScrollView(windowData2.ScrollPos);
{
RenderRows(windowData2); //TODO?
EditorGUILayout.Space();
}
EditorGUILayout.EndScrollView();
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
}
}
static bool TryGetHelpInfo(SearchArg arg, out string msg, out MessageType msgType) {
msgType = MessageType.Info;
if (arg == null) {
msg = default;
return false;
}
var path = arg.FilePath;
if (string.IsNullOrEmpty(path)) {
msg = default;
return false;
}
if (SearchUtils.IgnoredPaths(path, out var subPath)) {
msg = $"Paths containing '{subPath}' are ignored. You could add or remove it in Settings tab";
return true;
}
if (SearchUtils.IgnoredNonAssets(path) && !path.Eq("Assets")) {
msg = $"Asset is outside of Assets folder";
return true;
}
if (IgnoreTypes.Check(path, out var type)) {
if (AssetDatabase.IsValidFolder(path)) {
var scenes = arg.UnusedScenesFiltered?.Count;
var files = arg.UnusedAssetsFiltered?.Count;
if (scenes == 0 && files == 0) {
msg = default;
return false;
}
var b = new StringBuilder();
b.Append("This directory contains: ");
var any = false;
if (files > 0) {
any = true;
b.Append($"unused files ({files})");
}
if (scenes > 0) {
if (any)
b.Append(", ");
b.Append($"unused scenes ({scenes})");
}
b.Append(
".\nYou could delete them pressing corresponding button to the right.\nIf you don't want it to be inspected, please add it to Ignore list in the Settings tab");
msg = b.ToString();
return true;
}
msg = $"Assets of type '{type.Name}' are ignored. Please contact support if you need to change it";
return true;
}
// if (Filters.ScenePaths.GetEntitiesCount() == 0 && Filters.FileResultRows.GetEntitiesCount() == 0) {
if (SearchUtils.IsUnused(path)) {
type = AssetDatabase.GetMainAssetTypeAtPath(path);
var name = type.IsAssignableFromInverse(typeof(SceneAsset)) ? "scene" : "file";
msg =
$"This {name} has no explicit serialized usages and potentially could be removed. If you don't want it to be inspected, please add the containing folder to ignore list";
return true;
}
msgType = default;
msg = default;
return false;
}
static TargetTypeEnum GetTargetType(WindowData windowData1, Object obj) {
if (obj == null) return TargetTypeEnum.File;
var targetTypeEnum = TargetTypeEnum.Directory;
var path = AssetDatabase.GetAssetPath(obj);
switch (windowData1.FindFrom) {
case FindModeEnum.File when obj is DefaultAsset:
targetTypeEnum = TargetTypeEnum.Directory;
break;
case FindModeEnum.File when path.LastIndexOf(".unity", StringComparison.Ordinal) != -1:
targetTypeEnum = TargetTypeEnum.Scene;
break;
case FindModeEnum.File:
targetTypeEnum = TargetTypeEnum.File;
break;
case FindModeEnum.Scene:
targetTypeEnum = TargetTypeEnum.ObjectInScene;
break;
case FindModeEnum.Stage:
targetTypeEnum = TargetTypeEnum.ObjectInStage;
break;
}
return targetTypeEnum;
}
GUIContent _contentBuf = new GUIContent();
GUIContent _buf2 = new GUIContent();
BacklinkStore.UnusedQty ShowObjectName(BacklinkStore store, WindowData windowData, TargetTypeEnum targetTypeEnum, SearchArg arg, string[] selectedGuids) {
float TextWidth() {
_buf2.text = _contentBuf.text;
return 20f + GUI.skin.button.CalcSize(_buf2).x;
}
if (arg == null || arg.Main == null) return new BacklinkStore.UnusedQty();
bool isMultiSelect = selectedGuids != null && selectedGuids.Length > 1;
if (_contentBuf == null) {
_contentBuf = new GUIContent {tooltip = $"Click to ping"};
}
_contentBuf.image = isMultiSelect
? windowData.Style.MultiSelect.image
: AssetPreview.GetMiniThumbnail(arg.Target);
_contentBuf.text = string.Empty;
_contentBuf.tooltip = string.Empty;
if (!isMultiSelect) {
switch (targetTypeEnum) {
case TargetTypeEnum.Directory:
if (!SearchArgMain.IsEmpty()) {
_contentBuf.text = $"{arg.Main.name} (Folder)";
if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn,
GUILayout.MinWidth(TextWidth()))) {
EditorGUIUtility.PingObject(arg.Main);
}
if (AskDeleteUnusedFiles(arg, arg.UnusedAssetsFiltered, windowData))
return new BacklinkStore.UnusedQty();
if (AskDeleteUnusedScenes(arg, arg.UnusedScenesFiltered, windowData))
return new BacklinkStore.UnusedQty();
}
break;
case TargetTypeEnum.File:
_contentBuf.text = $"{arg.Main.name} (File Asset)";
if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn,
GUILayout.MinWidth(TextWidth()))) {
EditorGUIUtility.PingObject(arg.Main);
}
bool hasUnusedFile = SearchUtils.IsUnused(arg.FilePath);
var previous = GUI.enabled;
GUI.enabled = hasUnusedFile;
if (GUILayout.Button(windowData.Style.RemoveFile,
windowData.Style.RemoveUnusedBtn)) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to remove file: \"{arg.Main.name}\"?",
ok: "Remove",
cancel: "Cancel");
if (choice) {
EditorApplication.ExecuteMenuItem("File/Save Project");
DeleteWithMeta(arg.FilePath);
AssetDatabase.Refresh();
SearchUtils.Upd(arg);
}
}
GUI.enabled = previous;
break;
case TargetTypeEnum.Scene:
_contentBuf.text = $"{arg.Main.name} (Scene)";
if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn,
GUILayout.MinWidth(TextWidth()))) {
EditorGUIUtility.PingObject(arg.Main);
}
bool hasUnusedScene = SearchUtils.IsUnused(arg.FilePath);
previous = GUI.enabled;
GUI.enabled = hasUnusedScene;
if (GUILayout.Button(windowData.Style.RemoveScene,
windowData.Style.RemoveUnusedBtn)) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to remove scene: {arg.Main.name}?",
ok: "Remove",
cancel: "Cancel");
if (choice) {
EditorApplication.ExecuteMenuItem("File/Save Project");
DeleteWithMeta(arg.FilePath);
AssetDatabase.Refresh();
SearchUtils.Upd(arg);
}
}
GUI.enabled = previous;
break;
case TargetTypeEnum.ObjectInScene:
_contentBuf.text = $"{arg.Main.name} (Object in Scene)";
if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn,
GUILayout.MinWidth(TextWidth()))) {
EditorGUIUtility.PingObject(arg.Main);
}
break;
case TargetTypeEnum.ObjectInStage:
_contentBuf.image = AssetPreview.GetMiniThumbnail(arg.Target);
_contentBuf.text = $"{arg.Main.name} (Object in Staging)";
if (GUILayout.Button(_contentBuf,
windowData.Style.RemoveUnusedBtn)) {
EditorGUIUtility.PingObject(arg.Main);
}
break;
default:
if (GUILayout.Button($"{arg.Main.name} (Unknown Object Type)",
windowData.Style.RemoveUnusedBtn)) {
EditorGUIUtility.PingObject(arg.Main);
}
break;
}
}
else {
var unusedAssets = new List<string>();
var unusedScenes = new List<string>();
foreach (var guid in selectedGuids) {
var path = AssetDatabase.GUIDToAssetPath(guid);
if (store.UnusedFiles.TryGetValue(path, out _))
unusedAssets.Add(path);
else if (store.UnusedScenes.TryGetValue(path, out _))
unusedScenes.Add(path);
if (store.FoldersWithQty.TryGetValue(path, out _)) {
SearchArg searchArg = new SearchArg() {
FilePath = path,
Target = AssetDatabase.LoadAssetAtPath<DefaultAsset>(path),
Main = AssetDatabase.LoadAssetAtPath<DefaultAsset>(path)
};
SearchUtils.Upd(searchArg);
foreach (var unusedAssetPath in searchArg.UnusedAssetsFiltered)
if (store.UnusedFiles.TryGetValue(unusedAssetPath, out _))
unusedAssets.Add(unusedAssetPath);
foreach (var unusedScenePath in searchArg.UnusedScenesFiltered)
if (store.UnusedScenes.TryGetValue(unusedScenePath, out _))
unusedScenes.Add(unusedScenePath);
}
}
unusedAssets = unusedAssets.Distinct().ToList();
unusedScenes = unusedScenes.Distinct().ToList();
var assetSize = unusedAssets.Sum(CommonUtils.Size);
var sceneSize = unusedScenes.Sum(CommonUtils.Size);
_contentBuf.text =
$"Assets: {unusedAssets.Count} ({CommonUtils.BytesToString(assetSize)}), Scenes: {unusedScenes.Count} ({CommonUtils.BytesToString(sceneSize)})";
;
GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn, GUILayout.MinWidth(TextWidth()));
if (AskDeleteUnusedFiles(arg, unusedAssets, windowData))
return new BacklinkStore.UnusedQty();
if (AskDeleteUnusedScenes(arg, unusedScenes, windowData))
return new BacklinkStore.UnusedQty();
return new BacklinkStore.UnusedQty(unusedAssets.Count, unusedScenes.Count, assetSize + sceneSize);
}
return new BacklinkStore.UnusedQty();
}
static bool AskDeleteUnusedFiles(SearchArg arg, List<string> unusedAssets, WindowData windowData) {
if (arg == null || unusedAssets == null) return false;
var hasUnusedAssets = unusedAssets.Count > 0;
var previous = GUI.enabled;
GUI.enabled = hasUnusedAssets;
var guiContentRemoveAssets = windowData.Style.RemoveFile;
if (GUILayout.Button(guiContentRemoveAssets,
windowData.Style.RemoveUnusedBtn)) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to remove {unusedAssets.Count} asset(s)?",
ok: "Remove",
cancel: "Cancel");
if (choice) {
EditorApplication.ExecuteMenuItem("File/Save Project");
var i = 0f;
var total = (float) unusedAssets.Count;
foreach (var f in unusedAssets) {
var path = Application.dataPath.Replace("Assets", f);
DeleteWithMeta(path);
var percent = i * 100 / total;
if (total >= _progressBarShowFromLevel) {
if (Math.Abs(percent % 5f) < 0.01f) {
if (EditorUtility.DisplayCancelableProgressBar(
"Please wait...",
"Deleting assets...", percent))
throw new Exception("Deleting aborted");
}
i++;
}
}
if (total >= _progressBarShowFromLevel) {
EditorUtility.ClearProgressBar();
}
AssetDatabase.Refresh();
SearchUtils.Upd(arg);
}
GUI.enabled = previous;
return true;
}
GUI.enabled = previous;
return false;
}
static void DeleteWithMeta(string path) {
FileUtil.DeleteFileOrDirectory(path);
var metaPath = AssetDatabase.GetTextMetaFilePathFromAssetPath(path);
if (!string.IsNullOrEmpty(metaPath))
FileUtil.DeleteFileOrDirectory(metaPath);
}
static bool AskDeleteUnusedScenes(SearchArg arg, List<string> unusedScenes, WindowData windowData) {
if (arg == null || unusedScenes == null) return false;
var hasUnusedScenes = unusedScenes.Count > 0;
var previous = GUI.enabled;
GUI.enabled = hasUnusedScenes;
var guiContentRemoveScenes = windowData.Style.RemoveScene;
if (GUILayout.Button(guiContentRemoveScenes,
windowData.Style.RemoveUnusedBtn)) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to remove {unusedScenes.Count} scene(s)?",
ok: "Remove",
cancel: "Cancel");
if (choice) {
EditorApplication.ExecuteMenuItem("File/Save Project");
var i = 0f;
var total = (float) unusedScenes.Count;
foreach (var scene in unusedScenes) {
var path = Application.dataPath.Replace("Assets", scene);
DeleteWithMeta(path);
if (total >= _progressBarShowFromLevel) {
var percent = i * 100 / total;
if (Math.Abs(percent % 5f) < 0.01f) {
if (EditorUtility.DisplayCancelableProgressBar(
"Please wait...",
"Deleting scenes...", percent))
throw new Exception("Deleting aborted");
}
i++;
}
}
if (total >= _progressBarShowFromLevel) {
EditorUtility.ClearProgressBar();
}
AssetDatabase.Refresh();
SearchUtils.Upd(arg);
}
GUI.enabled = previous;
return true;
}
GUI.enabled = previous;
return false;
}
void RenderRows(WindowData windowData) {
// todo show spinner until scene is loaded,
if (FileResultRows.GetEntitiesCount() > 0) {
windowData.ExpandFiles =
EditorGUILayout.Foldout(windowData.ExpandFiles,
$"Usages in Project: {FileResultRows.GetEntitiesCount()}");
}
if (SearchArgMain.IsEmpty())
return;
if (windowData.ExpandFiles && windowData.FindFrom == FindModeEnum.File)
foreach (var i in FileResultRows.Out(out var get1, out var get2, out _, out _))
DrawRowFile(get1[i], get2[i], windowData);
var sceneMessage = $"Usages in Scenes: {ScenePaths.GetEntitiesCount()}";
if (ScenePaths.GetEntitiesCount() > 0) {
windowData.ExpandScenes =
EditorGUILayout.Foldout(windowData.ExpandScenes, sceneMessage);
}
if (!windowData.ExpandScenes) return;
if (windowData.ExpandScenes && windowData.FindFrom == FindModeEnum.Scene) {
foreach (var (grp, indices) in SceneResultRows.Out(out _, out var get2, out _, out _)
.GroupBy1(ResultComp.Instance)) {
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
var count = 0;
foreach (var i in indices) {
if (count++ == 0)
if (GUILayout.Button(get2[i].Label, windowData.Style.RowMainAssetBtn)) {
if (windowData.Click.IsDoubleClick(grp.RootGo)) {
// _selectionChangedByArrows = false;
Selection.activeObject = grp.RootGo;
}
else
EditorGUIUtility.PingObject(grp.RootGo);
windowData.Click = new PrevClick(grp.RootGo);
}
DrawRowScene(get2[i]);
}
}
}
}
using (new GUILayout.HorizontalScope()) {
GUILayout.Space(windowData.Style.SceneIndent1);
using (new EditorGUILayout.VerticalScope()) {
foreach (var i1 in ScenePaths.Out(out var get1, out var get2, out _)) {
windowData.SceneFoldout.text = get1[i1].PathNicified;
var details = get2[i1];
details.SearchRequested = details.Scene.isLoaded;
details.SearchRequested = EditorGUILayout.Foldout(details.SearchRequested,
windowData.SceneFoldout, EditorStyles.foldout);
if (details.SearchRequested && details.Scene.isLoaded && !details.SearchDone) {
var mainArg = SearchArgMain.GetSingle();
mainArg.Scene = SceneManager.GetSceneByPath(details.Scene.path);
SearchUtils.InScene(mainArg, details.Scene);
details.SearchDone = true;
}
if (!details.SearchRequested) {
if (!details.Scene.isLoaded) continue;
if (!details.WasOpened) {
// to clean up on selection change
AufCtx.World.NewEntityWith(out SceneToClose comp);
comp.Scene = details.Scene;
comp.ForceClose = true;
}
foreach (var row in SceneResultRows.Out(out _, out _, out var get3, out var entities)) {
if (!get3[row].ScenePath.Eq(details.Path))
continue;
entities[row].Destroy();
}
details.SearchDone = false;
}
else {
if (!details.Scene.isLoaded) {
details.Scene = EditorSceneManager.OpenScene(details.Path, OpenSceneMode.Additive);
// to clean up on selection change
AufCtx.World.NewEntityWith(out SceneToClose comp);
comp.Scene = details.Scene;
comp.SelectionId = Globals<PersistentUndoRedoState>.Value.Id;
#if UNITY_2019_1_OR_NEWER
EditorSceneManager.SetSceneCullingMask(details.Scene, 0);
#endif
details.SearchRequested = true;
// todo Scope component
#if later
if (details.Scene.isLoaded)
EditorSceneManager.CloseScene(details.Scene, false);
#endif
}
else if (SceneResultRows.IsEmpty())
EditorGUILayout.LabelField("No in-scene dependencies found.");
else
using (new GUILayout.HorizontalScope()) {
GUILayout.Space(windowData.Style.SceneIndent2);
using (new EditorGUILayout.VerticalScope())
foreach (var (grp, indices) in SceneResultRows
.Out(out var g1, out var g2, out var g3, out _)
.GroupBy1(ResultComp.Instance)) {
var any = false;
foreach (var i3 in indices) {
if (!g3[i3].ScenePath.Eq(details.Path))
continue;
any = true;
break;
}
if (!any)
continue;
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
var count = 0;
foreach (var i2 in indices) {
if (!g3[i2].ScenePath.Eq(details.Path))
continue;
if (count++ == 0) {
Result comp = g1[i2];
if (GUILayout.Button(g2[i2].Label,
windowData.Style.RowMainAssetBtn)) {
if (windowData.Click.IsDoubleClick(grp.RootGo)) {
// _selectionChangedByArrows = false;
Selection.activeObject = comp.RootGo;
}
else
EditorGUIUtility.PingObject(comp.RootGo);
windowData.Click = new PrevClick(comp.RootGo);
}
}
DrawRowScene(g2[i2]);
}
}
}
}
}
}
}
}
}
class ResultComp : IEqualityComparer<Result> {
public static ResultComp Instance { get; } = new ResultComp();
public bool Equals(Result x, Result y) => GetHashCode(x) == GetHashCode(y);
public int GetHashCode(Result obj) => obj.RootGo.GetInstanceID();
}
static void DrawRowScene(SearchResultGui gui) {
EditorGUI.BeginChangeCheck();
// if (data.TargetGo || data.TargetComponent)
foreach (var prop in gui.Properties) {
{
var locked = prop.Property.objectReferenceValue is MonoScript;
var f = GUI.enabled;
if (locked) GUI.enabled = false;
EditorGUILayout.PropertyField(prop.Property, prop.Content, false);
if (locked) GUI.enabled = f;
}
}
if (EditorGUI.EndChangeCheck())
gui.SerializedObject.ApplyModifiedProperties();
}
static void DrawRowFile(Result data, SearchResultGui gui, WindowData windowData) {
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
var buf = GUI.color;
var pingGo = data.MainFile == null ? data.RootGo : data.MainFile;
if (GUILayout.Button(gui.Label, windowData.Style.RowMainAssetBtn)) {
if (windowData.Click.IsDoubleClick(pingGo)) {
// _selectionChangedByArrows = false;
Selection.activeObject = pingGo;
}
else {
EditorGUIUtility.PingObject(pingGo);
}
windowData.Click = new PrevClick(pingGo);
}
GUI.color = buf;
EditorGUI.BeginChangeCheck();
if (data.File) {
foreach (var prop in gui.Properties) {
using (new EditorGUILayout.HorizontalScope()) {
var locked = prop.Property.objectReferenceValue is MonoScript;
var f = GUI.enabled;
if (locked) GUI.enabled = false;
EditorGUILayout.PropertyField(prop.Property, prop.Content, false);
if (locked) GUI.enabled = f;
}
}
}
if (EditorGUI.EndChangeCheck()) {
gui.SerializedObject.ApplyModifiedProperties();
// dependency.SerializedObject.Update();
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87ac41283aa347c4aece096c90076c7f
timeCreated: 1596213420