Add main mesh fixes
This commit is contained in:
34
Assets/Asset Cleaner/Utils/Asr.cs
Normal file
34
Assets/Asset Cleaner/Utils/Asr.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Diagnostics;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
static class FLAGS {
|
||||
// cleanup in release
|
||||
public const string DEBUG = "DEBUG1";
|
||||
public const string M_DISABLE_POOLING = "M_DISABLE_POOLING";
|
||||
}
|
||||
|
||||
static class Asr {
|
||||
#line hidden
|
||||
[Conditional(FLAGS.DEBUG)]
|
||||
public static void AreEqual(int a, int b) {
|
||||
Assert.AreEqual(a, b);
|
||||
}
|
||||
|
||||
[Conditional(FLAGS.DEBUG)]
|
||||
public static void IsTrue(bool b, string format = null) {
|
||||
Assert.IsTrue(b, format);
|
||||
}
|
||||
|
||||
[Conditional(FLAGS.DEBUG)]
|
||||
public static void IsFalse(bool b, string format = null) {
|
||||
Assert.IsFalse(b, format);
|
||||
}
|
||||
|
||||
[Conditional(FLAGS.DEBUG)]
|
||||
public static void IsNotNull(object target, string format = null) {
|
||||
Assert.IsNotNull(target, format);
|
||||
}
|
||||
#line default
|
||||
}
|
||||
}
|
||||
3
Assets/Asset Cleaner/Utils/Asr.cs.meta
Normal file
3
Assets/Asset Cleaner/Utils/Asr.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53c5eeeb89d541a5b26f906f93ffe650
|
||||
timeCreated: 1581424097
|
||||
37
Assets/Asset Cleaner/Utils/AufCtx.cs
Normal file
37
Assets/Asset Cleaner/Utils/AufCtx.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Leopotam.Ecs;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
static class AufCtx {
|
||||
public static EcsWorld World;
|
||||
|
||||
public static EcsSystems UpdateGroup;
|
||||
public static EcsSystems OnGuiGroup;
|
||||
|
||||
internal static bool InitStarted { get; private set; }
|
||||
internal static bool Destroyed { get; private set; }
|
||||
|
||||
internal static void TryInitWorld() {
|
||||
if (InitStarted) return;
|
||||
InitStarted = true;
|
||||
|
||||
World = new EcsWorld();
|
||||
|
||||
(OnGuiGroup = new EcsSystems(World)
|
||||
.Add(new SysWindowGui())).Init();
|
||||
|
||||
(UpdateGroup = new EcsSystems(World)
|
||||
.Add(new SysRepaintWindow())
|
||||
.Add(new SysUndoRedoSelection())
|
||||
.Add(new SysProcessSearch())
|
||||
.Add(new SysSceneCleanup())
|
||||
).Init();
|
||||
}
|
||||
|
||||
internal static void DestroyWorld() {
|
||||
if (!InitStarted) return;
|
||||
InitStarted = false;
|
||||
Destroyed = true;
|
||||
Asr.IsFalse(__GlobalsCounter.HasAnyValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Asset Cleaner/Utils/AufCtx.cs.meta
Normal file
11
Assets/Asset Cleaner/Utils/AufCtx.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 999cc1329c8dd5743b39fe17eb02a7aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Assets/Asset Cleaner/Utils/CommonUtils.cs
Normal file
40
Assets/Asset Cleaner/Utils/CommonUtils.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
static class CommonUtils {
|
||||
static string[] _suffix = {"B", "KB", "MB", "GB", "TB"};
|
||||
|
||||
public static string BytesToString(long byteCount) {
|
||||
if (byteCount == 0)
|
||||
return $"0 {_suffix[0]}";
|
||||
|
||||
var bytes = Math.Abs(byteCount);
|
||||
var place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
|
||||
double num;
|
||||
if (place == 0 || place == 1) { // display B, KB in MB
|
||||
num = Math.Round(bytes / Math.Pow(1024, 2), 4);
|
||||
return $"{Math.Sign(byteCount) * num:N} {_suffix[2]}";
|
||||
}
|
||||
|
||||
num = Math.Round(bytes / Math.Pow(1024, place), 1);
|
||||
return $"{Math.Sign(byteCount) * num:F0} {_suffix[place]}";
|
||||
}
|
||||
|
||||
// todo
|
||||
public static long Size(string path) {
|
||||
return TryGetSize(path, out var res) ? res : 0L;
|
||||
}
|
||||
|
||||
public static bool TryGetSize(string path, out long result) {
|
||||
if (!File.Exists(path)) {
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var fi = new FileInfo(path);
|
||||
result = fi.Length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Asset Cleaner/Utils/CommonUtils.cs.meta
Normal file
3
Assets/Asset Cleaner/Utils/CommonUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a0fa79cb05e495a8dc80a251a33970c
|
||||
timeCreated: 1595072385
|
||||
46
Assets/Asset Cleaner/Utils/DirtyUtils.cs
Normal file
46
Assets/Asset Cleaner/Utils/DirtyUtils.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
static class DirtyUtils {
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int HashCode<T1>(in T1 v1) {
|
||||
var hash = v1.GetHashCode();
|
||||
hash = (hash * 397);
|
||||
return hash;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int HashCode<T1, T2>(in T1 v1, in T2 v2) {
|
||||
var hash = v1.GetHashCode();
|
||||
hash = (hash * 397) ^ v2.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int HashCode<T1, T2, T3>(in T1 v1, in T2 v2, in T3 v3) {
|
||||
var hash = v1.GetHashCode();
|
||||
hash = (hash * 397) ^ v2.GetHashCode();
|
||||
hash = (hash * 397) ^ v3.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int HashCode<T1, T2, T3, T4>(in T1 v1, in T2 v2, in T3 v3, in T4 v4) {
|
||||
var hash = v1.GetHashCode();
|
||||
hash = (hash * 397) ^ v2.GetHashCode();
|
||||
hash = (hash * 397) ^ v3.GetHashCode();
|
||||
hash = (hash * 397) ^ v4.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int HashCode<T1, T2, T3, T4, T5>(in T1 v1, in T2 v2, in T3 v3, in T4 v4, in T5 v5) {
|
||||
var hash = v1.GetHashCode();
|
||||
hash = (hash * 397) ^ v2.GetHashCode();
|
||||
hash = (hash * 397) ^ v3.GetHashCode();
|
||||
hash = (hash * 397) ^ v4.GetHashCode();
|
||||
hash = (hash * 397) ^ v5.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Asset Cleaner/Utils/DirtyUtils.cs.meta
Normal file
3
Assets/Asset Cleaner/Utils/DirtyUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fd8e20db38143e3a3c51ea733bd0bab
|
||||
timeCreated: 1577270369
|
||||
55
Assets/Asset Cleaner/Utils/EcsUtils.cs
Normal file
55
Assets/Asset Cleaner/Utils/EcsUtils.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Leopotam.Ecs;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
static class EcsUtils {
|
||||
public static IEnumerable<(T Group, IEnumerable<int> Indices)> GroupBy1<T, T1, T2>(this EcsFilter<T, T1, T2> f, IEqualityComparer<T> comp)
|
||||
where T : class
|
||||
where T1 : class
|
||||
where T2 : class {
|
||||
foreach (var group in Inner().GroupBy(tuple => tuple.Group, comp))
|
||||
yield return (group.Key, group.Select(g => g.EcsIndex));
|
||||
|
||||
IEnumerable<(T Group, int EcsIndex)> Inner() {
|
||||
var get1 = f.Get1;
|
||||
foreach (var i in f) yield return (get1[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
public static EcsFilter<T> Out<T>(this EcsFilter<T> filter, out T[] get1, out EcsEntity[] entities) where T : class {
|
||||
get1 = filter.Get1;
|
||||
entities = filter.Entities;
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static EcsFilter<T1, T2> Out<T1, T2>(this EcsFilter<T1, T2> filter, out T1[] get1, out T2[] get2, out EcsEntity[] entities)
|
||||
where T1 : class where T2 : class {
|
||||
get1 = filter.Get1;
|
||||
get2 = filter.Get2;
|
||||
entities = filter.Entities;
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static EcsFilter<T1, T2, T3> Out<T1, T2, T3>(this EcsFilter<T1, T2, T3> filter, out T1[] get1, out T2[] get2, out T3[] get3, out EcsEntity[] entities)
|
||||
where T1 : class where T2 : class where T3 : class {
|
||||
get1 = filter.Get1;
|
||||
get2 = filter.Get2;
|
||||
get3 = filter.Get3;
|
||||
entities = filter.Entities;
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static void AllDestroy(this EcsFilter f) {
|
||||
var ecsEntities = f.Entities;
|
||||
foreach (var i in f)
|
||||
ecsEntities[i].Destroy();
|
||||
}
|
||||
|
||||
public static void AllUnset<T>(this EcsFilter f) where T : class {
|
||||
var e = f.Entities;
|
||||
foreach (var i in f)
|
||||
e[i].Unset<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Asset Cleaner/Utils/EcsUtils.cs.meta
Normal file
11
Assets/Asset Cleaner/Utils/EcsUtils.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d81e9bb0744bc4448ddb9bbd93bffd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
Assets/Asset Cleaner/Utils/Ext.cs
Normal file
17
Assets/Asset Cleaner/Utils/Ext.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Leopotam.Ecs;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
static class Ext {
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Eq(this string s1, string s2) => (s1 == s2);
|
||||
// public static bool Eq(this string s1, string s2) => StringComparer.Ordinal.Equals(s1, s2);
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T GetSingle<T>(this EcsFilter<T> f) where T : class {
|
||||
Asr.AreEqual(f.GetEntitiesCount(), 1);
|
||||
return f.Get1[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Asset Cleaner/Utils/Ext.cs.meta
Normal file
11
Assets/Asset Cleaner/Utils/Ext.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66f6b6922016ea04797deac8d082bc80
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Asset Cleaner/Utils/Globals.cs
Normal file
29
Assets/Asset Cleaner/Utils/Globals.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace Asset_Cleaner {
|
||||
static class Globals<T> where T : class {
|
||||
static T _instance;
|
||||
|
||||
public static T Value {
|
||||
get {
|
||||
Asr.IsFalse(_instance == null);
|
||||
return _instance;
|
||||
}
|
||||
set {
|
||||
var was = HasValue();
|
||||
_instance = value;
|
||||
|
||||
// keep counter to check during deinitialization if all Globals are cleared
|
||||
if (was && !HasValue())
|
||||
__GlobalsCounter.Counter -= 1;
|
||||
if (!was && HasValue())
|
||||
__GlobalsCounter.Counter += 1;
|
||||
|
||||
bool HasValue() => _instance != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class __GlobalsCounter {
|
||||
internal static int Counter;
|
||||
public static bool HasAnyValue() => Counter > 0;
|
||||
}
|
||||
}
|
||||
3
Assets/Asset Cleaner/Utils/Globals.cs.meta
Normal file
3
Assets/Asset Cleaner/Utils/Globals.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd0464bfdb1b4253ba131c7140510c81
|
||||
timeCreated: 1596137163
|
||||
124
Assets/Asset Cleaner/Utils/Option.cs
Normal file
124
Assets/Asset Cleaner/Utils/Option.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
readonly struct Option<T> : IEquatable<Option<T>>, IComparable<Option<T>> {
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
static readonly bool IsValueType;
|
||||
|
||||
public bool HasValue { get; }
|
||||
|
||||
T Value { get; }
|
||||
|
||||
public static implicit operator Option<T>(T arg) {
|
||||
if (!IsValueType) return ReferenceEquals(arg, null) ? new Option<T>() : new Option<T>(arg, true);
|
||||
#if M_WARN
|
||||
if (arg.Equals(default(T)))
|
||||
Warn.Warning($"{arg} has default value");
|
||||
#endif
|
||||
return new Option<T>(arg, true);
|
||||
}
|
||||
|
||||
static Option() {
|
||||
IsValueType = typeof(T).IsValueType;
|
||||
}
|
||||
|
||||
public void GetOrFail(out T value) {
|
||||
if (!TryGet(out value))
|
||||
Fail($"Option<{typeof(T).Name}> has no value");
|
||||
}
|
||||
|
||||
public T GetOrFail() {
|
||||
if (!TryGet(out var value))
|
||||
Fail($"Option<{typeof(T).Name}> has no value");
|
||||
return value;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG1")]
|
||||
static void Fail(string format = null) {
|
||||
throw new Exception(format);
|
||||
}
|
||||
|
||||
public bool TryGet(out T value) {
|
||||
if (!HasValue) {
|
||||
value = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
value = Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal Option(T value, bool hasValue) {
|
||||
Value = value;
|
||||
HasValue = hasValue;
|
||||
}
|
||||
|
||||
public T ValueOr(T alternative) {
|
||||
return HasValue ? Value : alternative;
|
||||
}
|
||||
|
||||
// for debug purposes
|
||||
public override string ToString() {
|
||||
if (!HasValue) return "None";
|
||||
|
||||
return Value == null ? "Some(null)" : $"Some({Value})";
|
||||
}
|
||||
|
||||
#region eq comparers boilerplate
|
||||
|
||||
public bool Equals(Option<T> other) {
|
||||
if (!HasValue && !other.HasValue)
|
||||
return true;
|
||||
|
||||
if (HasValue && other.HasValue)
|
||||
return EqualityComparer<T>.Default.Equals(Value, other.Value);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return obj is Option<T> && Equals((Option<T>) obj);
|
||||
}
|
||||
|
||||
public static bool operator ==(Option<T> left, Option<T> right) {
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Option<T> left, Option<T> right) {
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
if (!HasValue) return 0;
|
||||
|
||||
return IsValueType || Value != null ? Value.GetHashCode() : 1;
|
||||
}
|
||||
|
||||
public int CompareTo(Option<T> other) {
|
||||
if (HasValue && !other.HasValue) return 1;
|
||||
if (!HasValue && other.HasValue) return -1;
|
||||
|
||||
return Comparer<T>.Default.Compare(Value, other.Value);
|
||||
}
|
||||
|
||||
public static bool operator <(Option<T> left, Option<T> right) {
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Option<T> left, Option<T> right) {
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Option<T> left, Option<T> right) {
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Option<T> left, Option<T> right) {
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/Asset Cleaner/Utils/Option.cs.meta
Normal file
3
Assets/Asset Cleaner/Utils/Option.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 098c45ccebe14f5caa28635386021c94
|
||||
timeCreated: 1591801098
|
||||
57
Assets/Asset Cleaner/Utils/PersistenceUtils.cs
Normal file
57
Assets/Asset Cleaner/Utils/PersistenceUtils.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
static class PersistenceUtils {
|
||||
public static void Load(ref Config result) {
|
||||
var serializable = Deserialize();
|
||||
AufSerializableData.OnDeserialize(in serializable, ref result);
|
||||
}
|
||||
|
||||
public static void Save(in Config src) {
|
||||
AufSerializableData.OnSerialize(in src, out var serializable);
|
||||
var json = JsonUtility.ToJson(serializable);
|
||||
File.WriteAllText(Path, json);
|
||||
}
|
||||
|
||||
static AufSerializableData Deserialize() {
|
||||
AufSerializableData serializableData;
|
||||
string json;
|
||||
|
||||
if (!File.Exists(Path)) {
|
||||
// not exists - write new
|
||||
serializableData = AufSerializableData.Default();
|
||||
json = JsonUtility.ToJson(serializableData);
|
||||
File.WriteAllText(Path, json);
|
||||
}
|
||||
else {
|
||||
// exists
|
||||
json = File.ReadAllText(Path);
|
||||
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
// but corrupted - overwrite with new
|
||||
serializableData = AufSerializableData.Default();
|
||||
json = JsonUtility.ToJson(serializableData);
|
||||
File.WriteAllText(Path, json);
|
||||
}
|
||||
|
||||
serializableData = JsonUtility.FromJson<AufSerializableData>(json);
|
||||
if (serializableData.Valid())
|
||||
return serializableData;
|
||||
|
||||
serializableData = AufSerializableData.Default();
|
||||
json = JsonUtility.ToJson(serializableData);
|
||||
File.WriteAllText(Path, json);
|
||||
}
|
||||
|
||||
return serializableData;
|
||||
}
|
||||
|
||||
static string Path => $"{Application.temporaryCachePath}/AssetCleaner_{AufSerializableData.CurrentVersion}.json";
|
||||
|
||||
// [MenuItem("Tools/LogPath")]
|
||||
static void Log() {
|
||||
Debug.Log(Application.temporaryCachePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Asset Cleaner/Utils/PersistenceUtils.cs.meta
Normal file
3
Assets/Asset Cleaner/Utils/PersistenceUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 764a6ef52ca2456eb7c276bbcd0929f0
|
||||
timeCreated: 1589466213
|
||||
81
Assets/Asset Cleaner/Utils/Pool.cs
Normal file
81
Assets/Asset Cleaner/Utils/Pool.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
class Pool<T> : IDisposable where T : class {
|
||||
Func<T> _ctor;
|
||||
readonly Stack<T> _stack;
|
||||
|
||||
// todo place asserts on app quit
|
||||
Action<T> _reset;
|
||||
Action<T> _destroy;
|
||||
|
||||
static Action<T> Empty = _ => { };
|
||||
|
||||
public Pool(Func<T> ctor, Action<T> reset, Action<T> destroy = null) {
|
||||
_ctor = ctor;
|
||||
#if !M_DISABLE_POOLING
|
||||
_destroy = destroy ?? Empty;
|
||||
_reset = reset;
|
||||
_stack = new Stack<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
public T Get() {
|
||||
#if M_DISABLE_POOLING
|
||||
return _ctor.Invoke();
|
||||
#else
|
||||
T element;
|
||||
if (_stack.Count == 0) {
|
||||
element = _ctor();
|
||||
}
|
||||
else {
|
||||
element = _stack.Pop();
|
||||
}
|
||||
|
||||
return element;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Release(ref T element) {
|
||||
#if !M_DISABLE_POOLING
|
||||
Asr.IsFalse(_stack.Count > 0 && ReferenceEquals(_stack.Peek(), element),
|
||||
"Internal error. Trying to release object that is already released to pool. ");
|
||||
|
||||
_reset.Invoke(element);
|
||||
_stack.Push(element);
|
||||
#endif
|
||||
|
||||
element = null;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose() {
|
||||
#if !M_DISABLE_POOLING
|
||||
while (_stack.Count > 0) {
|
||||
var t = _stack.Pop();
|
||||
_destroy.Invoke(t);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public _Scope GetScoped(out T tmp) {
|
||||
tmp = Get();
|
||||
return new _Scope(this, ref tmp);
|
||||
}
|
||||
|
||||
public struct _Scope : IDisposable {
|
||||
Pool<T> _pool;
|
||||
T _val;
|
||||
|
||||
internal _Scope(Pool<T> pool, ref T val) {
|
||||
_pool = pool;
|
||||
_val = val;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
_pool.Release(ref _val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Asset Cleaner/Utils/Pool.cs.meta
Normal file
3
Assets/Asset Cleaner/Utils/Pool.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5a618b193c34a5cba77e7cc0df122f1
|
||||
timeCreated: 1591254176
|
||||
458
Assets/Asset Cleaner/Utils/SearchUtils.cs
Normal file
458
Assets/Asset Cleaner/Utils/SearchUtils.cs
Normal file
@@ -0,0 +1,458 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Leopotam.Ecs;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using static Asset_Cleaner.AufCtx;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Asset_Cleaner {
|
||||
static class SearchUtils {
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsAssignableFromInverse(this Type lhs, Type rhs) {
|
||||
if (lhs == null || rhs == null)
|
||||
return false;
|
||||
|
||||
return rhs.IsAssignableFrom(lhs);
|
||||
}
|
||||
|
||||
static Queue<SerializedProperty> _tmp = new Queue<SerializedProperty>();
|
||||
|
||||
public static void Upd(SearchArg arg) {
|
||||
if (arg.Target is DefaultAsset folder) {
|
||||
var path = AssetDatabase.GetAssetPath(folder);
|
||||
var store = Globals<BacklinkStore>.Value;
|
||||
arg.UnusedAssetsFiltered = store.UnusedFiles.Where(p => p.Key.StartsWith(path)).Select(p => p.Key).ToList();
|
||||
arg.UnusedScenesFiltered = store.UnusedScenes.Where(p => p.Key.StartsWith(path)).Select(p => p.Key).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Init(SearchArg arg, Object target, Scene scene = default) {
|
||||
Asr.IsNotNull(target, "Asset you're trying to search is corrupted");
|
||||
|
||||
arg.Target = target;
|
||||
|
||||
arg.FilePath = AssetDatabase.GetAssetPath(arg.Target);
|
||||
if (!scene.IsValid()) {
|
||||
Upd(arg);
|
||||
|
||||
arg.Main = AssetDatabase.LoadMainAssetAtPath(arg.FilePath);
|
||||
if (AssetDatabase.IsSubAsset(arg.Target)) { }
|
||||
else {
|
||||
switch (target) {
|
||||
case SceneAsset _:
|
||||
// todo support cross-scene references?
|
||||
// nested = all assets
|
||||
break;
|
||||
default:
|
||||
// AssetDatabase.IsMainAssetAtPathLoaded()
|
||||
var subAssets = AssetDatabase.LoadAllAssetsAtPath(arg.FilePath).Where(Predicate).ToArray();
|
||||
arg.SubAssets = subAssets.Length == 0 ? default(Option<Object[]>) : subAssets;
|
||||
|
||||
bool Predicate(Object s) {
|
||||
if (!s)
|
||||
return false;
|
||||
return s.GetInstanceID() != arg.Target.GetInstanceID();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (arg.Target) {
|
||||
case GameObject gg:
|
||||
arg.Main = gg;
|
||||
arg.Scene = scene;
|
||||
arg.SubAssets = gg.GetComponents<Component>().OfType<Object>().ToArray();
|
||||
break;
|
||||
case Component component: {
|
||||
// treat like subAsset
|
||||
arg.Main = component.gameObject;
|
||||
arg.Scene = scene;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// project asset such as Material
|
||||
arg.Main = arg.Target;
|
||||
arg.Scene = scene;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool SearchInChildProperties(SearchArg arg, Object suspect, bool scene, out EcsEntity entity) {
|
||||
if (IsTargetOrNested(arg, suspect)) {
|
||||
entity = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!suspect) {
|
||||
entity = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var so = new SerializedObject(suspect);
|
||||
_tmp.Clear();
|
||||
var queue = _tmp;
|
||||
var propIterator = so.GetIterator();
|
||||
|
||||
var prefabInstance = false;
|
||||
|
||||
if (scene && !string.IsNullOrEmpty(arg.FilePath) && PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(suspect) == arg.FilePath) {
|
||||
prefabInstance = true;
|
||||
while (propIterator.NextVisible(true)) {
|
||||
if (propIterator.propertyType != SerializedPropertyType.ObjectReference)
|
||||
continue;
|
||||
if (!IsTargetOrNested(arg, propIterator.objectReferenceValue))
|
||||
continue;
|
||||
|
||||
queue.Enqueue(propIterator.Copy());
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (propIterator.Next(true)) {
|
||||
if (propIterator.propertyType != SerializedPropertyType.ObjectReference)
|
||||
continue;
|
||||
if (!IsTargetOrNested(arg, propIterator.objectReferenceValue))
|
||||
continue;
|
||||
|
||||
queue.Enqueue(propIterator.Copy());
|
||||
}
|
||||
}
|
||||
|
||||
if (queue.Count == 0 && !prefabInstance) {
|
||||
entity = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
entity = World.NewEntityWith(out Result data);
|
||||
|
||||
var gui = entity.Set<SearchResultGui>();
|
||||
gui.Properties = new List<SearchResultGui.PropertyData>();
|
||||
gui.SerializedObject = so;
|
||||
gui.Label = new GUIContent();
|
||||
|
||||
// init header
|
||||
Texture2D miniTypeThumbnail = null;
|
||||
if (scene) {
|
||||
switch (suspect) {
|
||||
case Component component:
|
||||
data.RootGo = component.gameObject;
|
||||
gui.TransformPath = AnimationUtility.CalculateTransformPath(component.transform, null);
|
||||
gui.Label.image = AssetPreview.GetMiniThumbnail(data.RootGo);
|
||||
gui.Label.text = gui.TransformPath;
|
||||
break;
|
||||
case GameObject go:
|
||||
data.RootGo = go;
|
||||
gui.Label.image = AssetPreview.GetMiniThumbnail(data.RootGo);
|
||||
gui.TransformPath = AnimationUtility.CalculateTransformPath(go.transform, null);
|
||||
gui.Label.text = gui.TransformPath;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
miniTypeThumbnail = data.RootGo.GetInstanceID() == suspect.GetInstanceID()
|
||||
? null
|
||||
: AssetPreview.GetMiniThumbnail(suspect);
|
||||
}
|
||||
else {
|
||||
data.File = suspect;
|
||||
data.FilePath = AssetDatabase.GetAssetPath(data.File);
|
||||
data.MainFile = AssetDatabase.LoadMainAssetAtPath(data.FilePath);
|
||||
|
||||
// todo
|
||||
var prefabInstanceStatus = PrefabUtility.GetPrefabInstanceStatus(data.MainFile);
|
||||
switch (prefabInstanceStatus) {
|
||||
case PrefabInstanceStatus.Connected:
|
||||
case PrefabInstanceStatus.Disconnected:
|
||||
switch (data.File) {
|
||||
case Component comp:
|
||||
// transformPath = $"{AnimationUtility.CalculateTransformPath(comp.transform, null)}/".Replace("/", "/\n");
|
||||
gui.TransformPath =
|
||||
$"{AnimationUtility.CalculateTransformPath(comp.transform, null)}";
|
||||
break;
|
||||
case GameObject go:
|
||||
// transformPath = $"{AnimationUtility.CalculateTransformPath(go.transform, null)}/".Replace("/", "/\n");
|
||||
gui.TransformPath =
|
||||
$"{AnimationUtility.CalculateTransformPath(go.transform, null)}";
|
||||
break;
|
||||
default:
|
||||
// Assert.Fail("Not a component"); //todo
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case PrefabInstanceStatus.NotAPrefab:
|
||||
case PrefabInstanceStatus.MissingAsset:
|
||||
if (!AssetDatabase.IsMainAsset(data.File)) {
|
||||
// {row.Main.name}
|
||||
gui.TransformPath = $"/{data.File.name}";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
gui.Label.text = data.FilePath.Replace(AssetsRootPath, string.Empty);
|
||||
gui.Label.image = AssetDatabase.GetCachedIcon(data.FilePath);
|
||||
}
|
||||
|
||||
gui.Label.tooltip = gui.TransformPath;
|
||||
|
||||
|
||||
// init properties (footer)
|
||||
while (queue.Count > 0) {
|
||||
var prop = queue.Dequeue();
|
||||
var targetObject = prop.serializedObject.targetObject;
|
||||
var item = new SearchResultGui.PropertyData {
|
||||
Property = prop,
|
||||
Content = new GUIContent()
|
||||
};
|
||||
item.Content.image = miniTypeThumbnail;
|
||||
item.Content.text = Nicify(prop, targetObject, gui.TransformPath);
|
||||
item.Content.tooltip = gui.TransformPath;
|
||||
var typeName = targetObject.GetType().Name;
|
||||
if (StringComparer.Ordinal.Equals(typeName, targetObject.name))
|
||||
item.Content.tooltip = $"{gui.TransformPath}.{prop.propertyPath}";
|
||||
else
|
||||
item.Content.tooltip = $"{gui.TransformPath}({typeName}).{prop.propertyPath}";
|
||||
gui.Properties.Add(item: item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsFileIgrnoredBySettings(string path) {
|
||||
if (IgnoreTypes.Check(path, out _)) return true;
|
||||
if (IgnoredNonAssets(path)) return true;
|
||||
if (IgnoredPaths(path, out _)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IgnoredPaths(string path, out string str) {
|
||||
var conf = Globals<Config>.Value;
|
||||
foreach (var substr in conf.IgnorePathContains) {
|
||||
Asr.IsNotNull(path);
|
||||
Asr.IsNotNull(substr);
|
||||
if (!path.Contains(substr)) continue;
|
||||
str = substr;
|
||||
return true;
|
||||
}
|
||||
|
||||
str = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IgnoredNonAssets(string path) {
|
||||
return !path.Contains("Assets/");
|
||||
}
|
||||
|
||||
#region Project
|
||||
|
||||
public static bool IsUnused(string path) {
|
||||
if (IsFileIgrnoredBySettings(path))
|
||||
return false;
|
||||
return !AnyDependencies(path);
|
||||
}
|
||||
|
||||
static bool AnyDependencies(string path) {
|
||||
var store = Globals<BacklinkStore>.Value;
|
||||
if (store.UnusedFiles.Select(p => p.Key).Contains(path))
|
||||
return false;
|
||||
if (store.UnusedScenes.Select(p => p.Key).Contains(path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void FilesThatReference(SearchArg arg) {
|
||||
var store = Globals<BacklinkStore>.Value;
|
||||
var path1 = AssetDatabase.GetAssetPath(arg.Target);
|
||||
|
||||
if (!store.Backward.TryGetValue(path1, out var dep))
|
||||
return;
|
||||
|
||||
foreach (var path in dep.Lookup) {
|
||||
var mainAsset = AssetDatabase.GetMainAssetTypeAtPath(path);
|
||||
if (mainAsset.IsAssignableFromInverse(typeof(SceneAsset)))
|
||||
continue;
|
||||
|
||||
var any = false;
|
||||
if (mainAsset.IsAssignableFromInverse(typeof(GameObject))) { }
|
||||
else {
|
||||
var allAssetsAtPath = AssetDatabase.LoadAllAssetsAtPath(path);
|
||||
foreach (var suspect in allAssetsAtPath) {
|
||||
if (suspect is DefaultAsset || suspect is Transform || !suspect) continue;
|
||||
|
||||
if (!SearchInChildProperties(arg, suspect, false, out var entity))
|
||||
continue;
|
||||
|
||||
entity.Set<FileResultTag>();
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (any) continue;
|
||||
|
||||
// failed to find any property - just show main asset
|
||||
var e = World.NewEntity();
|
||||
var gui = e.Set<SearchResultGui>();
|
||||
gui.Properties = new List<SearchResultGui.PropertyData>();
|
||||
var main = AssetDatabase.LoadMainAssetAtPath(path);
|
||||
gui.Label = new GUIContent() {
|
||||
image = AssetPreview.GetMiniThumbnail(main),
|
||||
text = path.Replace(AssetsRootPath, string.Empty)
|
||||
};
|
||||
var res = e.Set<Result>();
|
||||
res.MainFile = main;
|
||||
e.Set<FileResultTag>();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ScenesThatContain(Object activeObject) {
|
||||
var store = Globals<BacklinkStore>.Value;
|
||||
var path1 = AssetDatabase.GetAssetPath(activeObject);
|
||||
if (!store.Backward.TryGetValue(path1, out var dep))
|
||||
return;
|
||||
|
||||
foreach (var path in dep.Lookup) {
|
||||
if (!AssetDatabase.GetMainAssetTypeAtPath(path).IsAssignableFromInverse(typeof(SceneAsset)))
|
||||
continue;
|
||||
|
||||
World.NewEntityWith(out SceneResult sp, out SceneDetails sceneDetails);
|
||||
sp.PathNicified = path.Replace("Assets/", string.Empty);
|
||||
|
||||
// heavy
|
||||
sceneDetails.Path = path;
|
||||
var alreadyOpened = false;
|
||||
for (var i = 0; i < EditorSceneManager.loadedSceneCount; i++) {
|
||||
var cur = SceneManager.GetSceneAt(i);
|
||||
if (!cur.path.Eq(sceneDetails.Path)) continue;
|
||||
alreadyOpened = true;
|
||||
sceneDetails.Scene = cur;
|
||||
}
|
||||
|
||||
sceneDetails.WasOpened = alreadyOpened;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scene
|
||||
|
||||
static Pool<List<Component>> ComponentListPool = new Pool<List<Component>>(() => new List<Component>(), list => list.Clear());
|
||||
|
||||
// todo provide explicit scene arg
|
||||
public static void InScene(SearchArg arg, Scene currentScene) {
|
||||
var rootGameObjects = currentScene.GetRootGameObjects();
|
||||
|
||||
foreach (var suspect in Traverse(rootGameObjects)) {
|
||||
if (!SearchInChildProperties(arg, suspect, scene: true, out var entity))
|
||||
continue;
|
||||
var s = entity.Set<InSceneResult>();
|
||||
s.ScenePath = arg.Scene.path;
|
||||
}
|
||||
|
||||
IEnumerable<Object> Traverse(GameObject[] roots) {
|
||||
foreach (var rootGo in roots)
|
||||
foreach (var comp in GoAndChildComps(rootGo)) {
|
||||
yield return comp;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<Object> GoAndChildComps(GameObject rr) {
|
||||
using (ComponentListPool.GetScoped(out var pooled)) {
|
||||
rr.GetComponents(pooled);
|
||||
foreach (var component in pooled) {
|
||||
if (component is Transform)
|
||||
continue;
|
||||
yield return component;
|
||||
}
|
||||
}
|
||||
|
||||
var transform = rr.transform;
|
||||
var childCount = transform.childCount;
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
var g = transform.GetChild(i).gameObject;
|
||||
foreach (var res in GoAndChildComps(g))
|
||||
yield return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if backup
|
||||
static void InScene(SearchArg arg, Scene currentScene) {
|
||||
var allObjects = currentScene
|
||||
.GetRootGameObjects()
|
||||
.SelectMany(g => g.GetComponentsInChildren<Component>(true)
|
||||
.Where(c => c && !(c is Transform))
|
||||
.Union(Enumerable.Repeat(g as Object, 1))
|
||||
)
|
||||
.ToArray();
|
||||
|
||||
var total = allObjects.Length;
|
||||
for (var i = 0; i < total; i++) {
|
||||
var suspect = allObjects[i];
|
||||
|
||||
if (SearchInChildProperties(arg, suspect, true, out var entity)) {
|
||||
var s = entity.Set<InSceneResult>();
|
||||
s.ScenePath = arg.Scene.path;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool IsTargetOrNested(SearchArg target, Object suspect) {
|
||||
if (!suspect)
|
||||
return false;
|
||||
|
||||
if (target.Target.GetInstanceID() == suspect.GetInstanceID() || target.Main.GetInstanceID() == (suspect).GetInstanceID())
|
||||
return true;
|
||||
|
||||
if (target.SubAssets.TryGet(out var subassets))
|
||||
foreach (var asset in subassets) {
|
||||
if (asset.GetInstanceID() == (suspect).GetInstanceID())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static string Nicify(SerializedProperty sp, Object o, string transformPath) {
|
||||
// return sp.propertyPath;
|
||||
|
||||
string nice;
|
||||
switch (o) {
|
||||
case AnimatorState animatorState:
|
||||
return animatorState.name;
|
||||
case Material material:
|
||||
nice = material.name;
|
||||
break;
|
||||
default: {
|
||||
nice = sp.propertyPath.Replace(".Array.data", string.Empty);
|
||||
if (nice.IndexOf(".m_PersistentCalls.m_Calls", StringComparison.Ordinal) > 0) {
|
||||
nice = nice.Replace(".m_PersistentCalls.m_Calls", string.Empty)
|
||||
.Replace(".m_Target", string.Empty);
|
||||
}
|
||||
|
||||
nice = nice.Split('.').Select(t => ObjectNames.NicifyVariableName(t).Replace(" ", string.Empty))
|
||||
.Aggregate((a, b) => a + "." + b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// nice = $"{transformPath}({o.GetType().Name}).{nice}";
|
||||
nice = $"({o.GetType().Name}).{nice}";
|
||||
return nice;
|
||||
}
|
||||
|
||||
const string AssetsRootPath = "Assets/";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/Asset Cleaner/Utils/SearchUtils.cs.meta
Normal file
3
Assets/Asset Cleaner/Utils/SearchUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66f3f357bedc44e2907d7e5f48de45d3
|
||||
timeCreated: 1576134438
|
||||
Reference in New Issue
Block a user