FR2_Export.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. //#define REPLACE_SAME_TYPE
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using UnityEditor;
  6. using UnityEngine;
  7. namespace vietlabs.fr2
  8. {
  9. public class FR2_Export
  10. {
  11. // private static int processIndex;
  12. private const int maxThread = 5;
  13. //[MenuItem("Assets/FR2/Tools/Merge Duplicates")]
  14. private static Dictionary<string, ProcessReplaceData> listReplace;
  15. private static HashSet<string> cacheSelection;
  16. private static List<Thread> lstThreads;
  17. public static bool IsMergeProcessing { get; private set; }
  18. public static void ExportCSV(FR2_Ref[] csvSource)
  19. {
  20. var result = FR2_CSV.GetCSVRows(csvSource);
  21. if (result.Length > 0)
  22. {
  23. EditorGUIUtility.systemCopyBuffer = result;
  24. Debug.Log("[FR2] CSV file content (" + csvSource.Length + " assets) copied to clipboard!");
  25. }
  26. else
  27. {
  28. Debug.LogWarning("[FR2] Nothing to export!");
  29. }
  30. }
  31. [MenuItem("Assets/FR2/Toggle Ignore", false, 19)]
  32. private static void Ignore()
  33. {
  34. if (!FR2_Cache.isReady)
  35. {
  36. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  37. return;
  38. }
  39. Object[] actives = Selection.objects;
  40. for (var i = 0; i < actives.Length; i++)
  41. {
  42. string path = AssetDatabase.GetAssetPath(actives[i]);
  43. if (path.Equals(FR2_Cache.DEFAULT_CACHE_PATH))
  44. {
  45. continue;
  46. }
  47. if (FR2_Setting.IgnoreAsset.Contains(path))
  48. {
  49. FR2_Setting.RemoveIgnore(path);
  50. }
  51. else
  52. {
  53. FR2_Setting.AddIgnore(path);
  54. }
  55. }
  56. }
  57. [MenuItem("Assets/FR2/Copy GUID", false, 20)]
  58. private static void CopyGUID()
  59. {
  60. EditorGUIUtility.systemCopyBuffer = AssetDatabase.AssetPathToGUID(
  61. AssetDatabase.GetAssetPath(Selection.activeObject)
  62. );
  63. }
  64. [MenuItem("Assets/FR2/Export Selection", false, 21)]
  65. private static void ExportSelection()
  66. {
  67. if (!FR2_Cache.isReady)
  68. {
  69. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  70. return;
  71. }
  72. FR2_Unity.ExportSelection();
  73. }
  74. [MenuItem("Assets/FR2/Select Dependencies (assets I use)", false, 22)]
  75. private static void SelectDependencies_wtme()
  76. {
  77. if (!FR2_Cache.isReady)
  78. {
  79. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  80. return;
  81. }
  82. SelectDependencies(false);
  83. }
  84. [MenuItem("Assets/FR2/Refresh")]
  85. public static void ForceRefreshSelection()
  86. {
  87. var guids = Selection.assetGUIDs;
  88. if (!FR2_Cache.isReady) return; // cache not ready!
  89. for (var i = 0; i < guids.Length; i++)
  90. {
  91. string guid = guids[i];
  92. if (guid == FR2_Cache.CachePath)
  93. {
  94. continue;
  95. }
  96. if (!FR2_Asset.IsValidGUID(guid))
  97. {
  98. continue;
  99. }
  100. if (FR2_Cache.Api.AssetMap.ContainsKey(guid))
  101. {
  102. FR2_Cache.Api.RefreshAsset(guid, true);
  103. #if FR2_DEBUG
  104. UnityEngine.Debug.Log("Changed : " + guids[i]);
  105. #endif
  106. continue;
  107. }
  108. FR2_Cache.Api.AddAsset(guid);
  109. }
  110. FR2_Cache.Api.Check4Work();
  111. }
  112. [MenuItem("Assets/FR2/Select Dependencies included me", false, 23)]
  113. private static void SelectDependencies_wme()
  114. {
  115. if (!FR2_Cache.isReady)
  116. {
  117. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  118. return;
  119. }
  120. SelectDependencies(true);
  121. }
  122. //[MenuItem("Assets/FR2/Select")]
  123. [MenuItem("Assets/FR2/Select Used (assets used me)", false, 24)]
  124. private static void SelectUsed_wtme()
  125. {
  126. if (!FR2_Cache.isReady)
  127. {
  128. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  129. return;
  130. }
  131. SelectUsed(false);
  132. }
  133. [MenuItem("Assets/FR2/Select Used included me", false, 25)]
  134. private static void SelectUsed_wme()
  135. {
  136. if (!FR2_Cache.isReady)
  137. {
  138. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  139. return;
  140. }
  141. SelectUsed(true);
  142. }
  143. [MenuItem("Assets/FR2/Export Dependencies", false, 40)]
  144. private static void ExportDependencies()
  145. {
  146. if (!FR2_Cache.isReady)
  147. {
  148. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  149. return;
  150. }
  151. var deps = GetSelectionDependencies();
  152. if (deps == null) return;
  153. Selection.objects = deps.ToArray();
  154. FR2_Unity.ExportSelection();
  155. }
  156. [MenuItem("Assets/FR2/Export Assets (no scripts)", false, 41)]
  157. private static void ExportAsset()
  158. {
  159. if (!FR2_Cache.isReady)
  160. {
  161. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  162. return;
  163. }
  164. List<Object> list = GetSelectionDependencies();
  165. for (int i = list.Count - 1; i >= 0; i--)
  166. {
  167. if (list[i] is MonoScript)
  168. {
  169. list.RemoveAt(i);
  170. }
  171. }
  172. //Debug.Log(i + ":" + list[i] + ":" + list[i].GetType());
  173. Selection.objects = list.ToArray();
  174. FR2_Unity.ExportSelection();
  175. }
  176. public static void MergeDuplicate(string guid_file)
  177. {
  178. // for (int i = 0; i < Selection.objects.Length; i++)
  179. // {
  180. // Object item = Selection.objects[i];
  181. // Debug.Log(item.name);
  182. // }
  183. //string guid_file = EditorGUIUtility.systemCopyBuffer;
  184. long toFileId = 0;
  185. var string_arr = guid_file.Split ('/');
  186. if (string_arr.Length > 1) {
  187. toFileId = long.Parse(string_arr[1]);
  188. }
  189. string guid = string_arr[0];
  190. // var wat = new System.Diagnostics.Stopwatch();
  191. // wat.Start();
  192. //validate clipboard guid
  193. string gPath = AssetDatabase.GUIDToAssetPath(guid);
  194. if (string.IsNullOrEmpty(gPath) || !gPath.StartsWith("Assets/"))
  195. {
  196. Debug.LogWarning("Invalid guid <" + guid + "> in clipboard, can not replace !");
  197. return;
  198. }
  199. var temp = FR2_Unity.Selection_AssetGUIDs;//cheat refresh selection, DO NOT delete
  200. HashSet<string> guids_files = FR2_Unity._Selection_AssetGUIDs;
  201. var realKey = "";
  202. foreach (var item in guids_files) {
  203. if (item.StartsWith (guid_file, System.StringComparison.Ordinal)) {
  204. realKey = item;
  205. }
  206. }
  207. if (string.IsNullOrEmpty(realKey))
  208. {
  209. Debug.LogWarning("Clipboard guid <" + guid +
  210. "> not found in Selection, you may not intentionally replace selection assets by clipboard guid");
  211. // foreach (var item in guids_files) {
  212. // Debug.Log ("item: " + item);
  213. // }
  214. return;
  215. }
  216. guids_files.Remove(realKey);
  217. cacheSelection = new HashSet<string>();
  218. foreach(var item in cacheSelection)
  219. {
  220. cacheSelection.Add(item);
  221. }
  222. if (guids_files.Count == 0)
  223. {
  224. Debug.LogWarning("No new asset selected to replace, must select all duplications to replace");
  225. return;
  226. }
  227. //check asset type, only replace same type
  228. #if REPLACE_SAME_TYPE
  229. var type1 = AssetDatabase.GetMainAssetTypeAtPath(gPath);
  230. var importType1 = AssetImporter.GetAtPath(gPath);
  231. #endif
  232. List<FR2_Asset> assetList = new List<FR2_Asset>();
  233. var lstFind = new List<string>();
  234. foreach (var item in guids_files)
  235. {
  236. var arr = item.Split('/');
  237. string g = arr[0];
  238. #if REPLACE_SAME_TYPE
  239. var p2 = AssetDatabase.GUIDToAssetPath(g);
  240. var type2 = AssetDatabase.GetMainAssetTypeAtPath(p2);
  241. if(type1 != type2)
  242. {
  243. Debug.LogWarning("Cannot replace asset: " + p2 + " becase difference type");
  244. continue;
  245. }
  246. if(type1 == typeof(UnityEngine.Texture2D))
  247. {
  248. var importType2 = AssetImporter.GetAtPath(p2) as TextureImporter;
  249. var textureImportType1 = importType1 as TextureImporter;
  250. if (importType2 == null || textureImportType1 == null)
  251. {
  252. Debug.LogWarning("Cannot replace asset: " + p2 + " becase difference type");
  253. continue;
  254. }
  255. if(textureImportType1.textureType != importType2.textureType)
  256. {
  257. Debug.LogWarning("Cannot replace asset: " + p2 + " becase difference type");
  258. continue;
  259. }
  260. if (textureImportType1.textureType == TextureImporterType.Sprite)
  261. {
  262. if (textureImportType1.spriteImportMode != importType2.spriteImportMode)
  263. {
  264. Debug.LogWarning("Cannot replace asset: " + p2 + " becase difference type");
  265. continue;
  266. }
  267. }
  268. //Debug.Log("import type " + mainImportType);
  269. }
  270. //Debug.Log("type: " + mainType);
  271. #endif
  272. lstFind.Add(g);
  273. // if (arr.Length > 1)
  274. // {
  275. // long file = long.Parse(arr[1]);
  276. //
  277. // }
  278. }
  279. if (lstFind.Count == 0)
  280. {
  281. Debug.LogWarning("No new asset selected to replace, must select all duplications to replace");
  282. return;
  283. }
  284. assetList = FR2_Cache.Api.FindAssets(lstFind.ToArray(), false);
  285. //replace one by one
  286. listReplace = new Dictionary<string, ProcessReplaceData>();
  287. lstThreads = new List<Thread>();
  288. for (int i = assetList.Count - 1; i >= 0; i--)
  289. {
  290. //Debug.Log("FR2 Replace GUID : " + assetList[i].guid + " ---> " + guid + " : " + assetList[i].UsedByMap.Count + " assets updated");
  291. string fromId = assetList[i].guid;
  292. List<FR2_Asset> arr = assetList[i].UsedByMap.Values.ToList();
  293. for (var j = 0; j < arr.Count; j++)
  294. {
  295. FR2_Asset a = arr[j];
  296. if (!listReplace.ContainsKey(a.assetPath))
  297. {
  298. listReplace.Add(a.assetPath, new ProcessReplaceData());
  299. }
  300. listReplace[a.assetPath].datas.Add(new ReplaceData
  301. {
  302. from = fromId,
  303. to = guid,
  304. asset = a,
  305. toFileId = toFileId
  306. });
  307. }
  308. }
  309. foreach (KeyValuePair<string, ProcessReplaceData> item in listReplace)
  310. {
  311. item.Value.processIndex = item.Value.datas.Count - 1;
  312. }
  313. IsMergeProcessing = true;
  314. EditorApplication.update -= ApplicationUpdate;
  315. EditorApplication.update += ApplicationUpdate;
  316. // for (var i = assetList.Count - 1; i >= 0; i--)
  317. // {
  318. // // Debug.Log("FR2 Replace GUID : " + assetList[i].guid + " ---> " + guid + " : " + assetList[i].UsedByMap.Count + " assets updated");
  319. // var from = assetList[i].guid;
  320. // var arr = assetList[i].UsedByMap.Values.ToList();
  321. // for (var j = 0; j < arr.Count; j ++)
  322. // {
  323. // var a = arr[j];
  324. // var result = a.ReplaceReference(from, guid);
  325. // if (result && !dictAsset.ContainsKey(a.guid))
  326. // {
  327. // dictAsset.Add(a.guid, 1);
  328. // }
  329. // }
  330. // }
  331. // Debug.Log("Time replace guid " + wat.ElapsedMilliseconds);
  332. // wat = new System.Diagnostics.Stopwatch();
  333. // wat.Start();
  334. // var listRefresh = dictAsset.Keys.ToList();
  335. // for (var i = 0; i < listRefresh.Count; i++)
  336. // {
  337. // FR2_Cache.Api.RefreshAsset(listRefresh[i], true);
  338. // }
  339. // FR2_Cache.Api.RefreshSelection();
  340. // FR2_Cache.Api.Check4Usage();
  341. // AssetDatabase.Refresh();
  342. // Debug.Log("Time replace guid " + wat.ElapsedMilliseconds);
  343. }
  344. private static void ApplicationUpdate()
  345. {
  346. bool notComplete = listReplace.Where(x => x.Value.processIndex >= 0).Count() > 0;
  347. if (lstThreads.Count <= 0 && notComplete)
  348. {
  349. foreach (KeyValuePair<string, ProcessReplaceData> item in listReplace)
  350. {
  351. if (item.Value.processIndex >= 0)
  352. {
  353. ReplaceData a = item.Value.datas[item.Value.processIndex--];
  354. a.isTerrian = a.asset.type == FR2_AssetType.TERRAIN;
  355. if (a.isTerrian)
  356. {
  357. a.terrainData =
  358. AssetDatabase.LoadAssetAtPath(a.asset.assetPath, typeof(Object)) as TerrainData;
  359. }
  360. a.isSucess = a.asset.ReplaceReference(a.from, a.to, a.toFileId, a.terrainData);
  361. }
  362. }
  363. }
  364. for (int i = lstThreads.Count - 1; i >= 0; i--)
  365. {
  366. if (!lstThreads[i].IsAlive)
  367. {
  368. lstThreads.RemoveAt(i);
  369. }
  370. }
  371. //if (lstThreads.Count <= 0 && !notComplete) //complete
  372. {
  373. foreach (KeyValuePair<string, ProcessReplaceData> item in listReplace)
  374. {
  375. List<ReplaceData> lst = item.Value.datas;
  376. for (var i = 0; i < lst.Count; i++)
  377. {
  378. ReplaceData data = lst[i];
  379. if (!data.isUpdated && data.isSucess)
  380. {
  381. data.isUpdated = true;
  382. if (data.isTerrian)
  383. {
  384. EditorUtility.SetDirty(data.terrainData);
  385. AssetDatabase.SaveAssets();
  386. data.terrainData = null;
  387. FR2_Unity.UnloadUnusedAssets();
  388. }
  389. else
  390. {
  391. AssetDatabase.ImportAsset(data.asset.assetPath, ImportAssetOptions.Default);
  392. }
  393. }
  394. }
  395. }
  396. var guidsRefreshed = new HashSet<string>();
  397. EditorApplication.update -= ApplicationUpdate;
  398. foreach (KeyValuePair<string, ProcessReplaceData> item in listReplace)
  399. {
  400. List<ReplaceData> lst = item.Value.datas;
  401. for (var i = 0; i < lst.Count; i++)
  402. {
  403. ReplaceData data = lst[i];
  404. if (data.isSucess && !guidsRefreshed.Contains(data.asset.guid))
  405. {
  406. guidsRefreshed.Add(data.asset.guid);
  407. FR2_Cache.Api.RefreshAsset(data.asset.guid, true);
  408. }
  409. }
  410. }
  411. lstThreads = null;
  412. listReplace = null;
  413. FR2_Cache.Api.RefreshSelection();
  414. FR2_Cache.Api.Check4Work();
  415. AssetDatabase.Refresh();
  416. IsMergeProcessing = false;
  417. }
  418. }
  419. //[MenuItem("Assets/FR2/Tools/Fix Model Import Material")]
  420. //public static void FixModelImportMaterial(){
  421. // if (Selection.activeObject == null) return;
  422. // CreatePrefabReplaceModel((GameObject)Selection.activeObject);
  423. //}
  424. //[MenuItem("GameObject/FR2/Paste Materials", false, 10)]
  425. //public static void PasteMaterials(){
  426. // if (Selection.activeObject == null) return;
  427. // var r = Selection.activeGameObject.GetComponent<Renderer>();
  428. // Undo.RecordObject(r, "Replace Materials");
  429. // r.materials = model_materials;
  430. // EditorUtility.SetDirty(r);
  431. //}
  432. //[MenuItem("GameObject/FR2/Copy Materials", false, 10)]
  433. //public static void CopyMaterials(){
  434. // if (Selection.activeObject == null) return;
  435. // var r = Selection.activeGameObject.GetComponent<Renderer>();
  436. // if (r == null) return;
  437. // model_materials = r.sharedMaterials;
  438. //}
  439. //-------------------------- APIs ----------------------
  440. private static void SelectDependencies(bool includeMe)
  441. {
  442. List<FR2_Asset> list = FR2_Cache.Api.FindAssets(FR2_Unity.Selection_AssetGUIDs, false);
  443. var dict = new Dictionary<string, Object>();
  444. if (includeMe)
  445. {
  446. AddToDict(dict, list.ToArray());
  447. }
  448. for (var i = 0; i < list.Count; i++)
  449. {
  450. AddToDict(dict, FR2_Asset.FindUsage(list[i]).ToArray());
  451. }
  452. Selection.objects = dict.Values.ToArray();
  453. }
  454. private static void SelectUsed(bool includeMe)
  455. {
  456. List<FR2_Asset> list = FR2_Cache.Api.FindAssets(FR2_Unity.Selection_AssetGUIDs, false);
  457. var dict = new Dictionary<string, Object>();
  458. if (includeMe)
  459. {
  460. AddToDict(dict, list.ToArray());
  461. }
  462. for (var i = 0; i < list.Count; i++)
  463. {
  464. AddToDict(dict, list[i].UsedByMap.Values.ToArray());
  465. }
  466. Selection.objects = dict.Values.ToArray();
  467. }
  468. //-------------------------- UTILS ---------------------
  469. internal static void AddToDict(Dictionary<string, Object> dict, params FR2_Asset[] list)
  470. {
  471. for (var j = 0; j < list.Length; j++)
  472. {
  473. string guid = list[j].guid;
  474. if (!dict.ContainsKey(guid))
  475. {
  476. string assetPath = AssetDatabase.GUIDToAssetPath(guid);
  477. dict.Add(guid, FR2_Unity.LoadAssetAtPath<Object>(assetPath));
  478. }
  479. }
  480. }
  481. private static List<Object> GetSelectionDependencies()
  482. {
  483. if (!FR2_Cache.isReady)
  484. {
  485. Debug.LogWarning("FR2 cache not yet ready, please open Window > FR2_Window and hit scan project!");
  486. return null;
  487. }
  488. return FR2_Cache.FindUsage(FR2_Unity.Selection_AssetGUIDs).Select(
  489. guid =>
  490. {
  491. string assetPath = AssetDatabase.GUIDToAssetPath(guid);
  492. return FR2_Unity.LoadAssetAtPath<Object>(assetPath);
  493. }
  494. ).ToList();
  495. }
  496. private class ProcessReplaceData
  497. {
  498. public readonly List<ReplaceData> datas = new List<ReplaceData>();
  499. public int processIndex;
  500. }
  501. private class ReplaceData
  502. {
  503. public FR2_Asset asset;
  504. public string from;
  505. public bool isSucess;
  506. public bool isTerrian;
  507. public bool isUpdated;
  508. public TerrainData terrainData;
  509. public string to;
  510. public long toFileId;
  511. }
  512. // AssetDatabase.ImportAsset(oAssetPath, ImportAssetOptions.Default);
  513. // importer.importMaterials = false;
  514. // var importer = AssetImporter.GetAtPath(oAssetPath) as ModelImporter;
  515. // var nModel = AssetDatabase.LoadAssetAtPath<GameObject>(oAssetPath);
  516. // // Reimport model with importMaterial = false
  517. // var extension = Path.GetExtension(oAssetPath);
  518. // model_materials = model.GetComponent<Renderer>().sharedMaterials;
  519. // var oGUID = AssetDatabase.AssetPathToGUID(oAssetPath);
  520. // var oAssetPath = AssetDatabase.GetAssetPath(model);
  521. // if (model == null) return;
  522. //{
  523. //static void CreatePrefabReplaceModel(GameObject model)
  524. //static Material[] model_materials;
  525. // //create prefab from new model
  526. // var prefabPath = oAssetPath.Replace(extension, ".prefab");
  527. // var clone = (GameObject)Object.Instantiate(nModel);
  528. // clone.GetComponent<Renderer>().sharedMaterials = model_materials;
  529. // PrefabUtility.CreatePrefab(prefabPath, clone, ReplacePrefabOptions.ReplaceNameBased);
  530. // AssetDatabase.SaveAssets();
  531. // GameObject.DestroyImmediate(clone);
  532. //}
  533. }
  534. }