MegascansUtilities.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. #if UNITY_EDITOR
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using UnityEditor;
  6. using UnityEngine;
  7. using Newtonsoft.Json.Linq;
  8. namespace Quixel {
  9. public class MegascansUtilities : MonoBehaviour {
  10. #region Other_Utils
  11. /// <summary>
  12. /// Check whether the child folder you're trying to make already exists, if not, create it and return the directory.
  13. /// </summary>
  14. /// <param name="folder"></param>
  15. /// <param name="child"></param>
  16. /// <returns></returns>
  17. public static string ValidateFolderCreate (string parent, string child) {
  18. string tempPath = FixSlashes (Path.Combine (parent, child));
  19. if (!AssetDatabase.IsValidFolder (tempPath)) {
  20. string newPath = AssetDatabase.CreateFolder (parent, child);
  21. return AssetDatabase.GUIDToAssetPath (newPath);
  22. }
  23. return FixSlashes (tempPath);
  24. }
  25. public static string FixSlashes (string txt) {
  26. txt = txt.Replace ("\\", "/");
  27. txt = txt.Replace (@"\\", "/");
  28. return txt;
  29. }
  30. /// <summary>
  31. /// Remove spaces and remove special characters.
  32. /// </summary>
  33. public static string FixPath (string orgPath) {
  34. string path = orgPath.Trim ();
  35. string[] pathFolders = path.Split ('/');
  36. for (int j = 0; j < pathFolders.Length - 1; j++) {
  37. pathFolders[j] = pathFolders[j].Trim ();
  38. }
  39. path = pathFolders[0];
  40. for (int i = 1; i < pathFolders.Length; i++) {
  41. path += "/" + pathFolders[i];
  42. }
  43. return path;
  44. }
  45. static bool isLegacyProject = false;
  46. static bool identifiedPipeline = false;
  47. /// <summary>
  48. /// This function attempts to auto-detect which template the project is using. Defaults to Legacy/Standard if all else fails.
  49. /// </summary>
  50. public static int GetShaderType()
  51. {
  52. int shaderType = EditorPrefs.GetInt("QuixelDefaultShader");
  53. return shaderType == 3 ? (int)getCurrentPipeline() : shaderType;
  54. }
  55. public static bool isLegacy () {
  56. if (!identifiedPipeline) {
  57. string[] versionParts = Application.unityVersion.Split ('.');
  58. int majorVersion = int.Parse (versionParts[0]);
  59. int minorVersion = int.Parse (versionParts[1]);
  60. isLegacyProject = (majorVersion < 2018 || (majorVersion == 2018 && minorVersion < 3));
  61. }
  62. return isLegacyProject;
  63. }
  64. //attempt to auto-detect a settings file for Lightweight or HD pipelines
  65. public static Pipeline getCurrentPipeline () {
  66. Pipeline currentPipeline = Pipeline.HDRP;
  67. //attempt to auto-detect a settings file for Lightweight or HD pipelines
  68. if (AssetDatabase.IsValidFolder ("Assets/Settings")) {
  69. if (AssetDatabase.LoadAssetAtPath ("Assets/Settings/HDRenderPipelineAsset.asset", typeof (ScriptableObject)) ||
  70. AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRP High Quality.asset", typeof(ScriptableObject)) ||
  71. AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRP Low Quality.asset", typeof(ScriptableObject)) ||
  72. AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRP Medium Quality.asset", typeof(ScriptableObject)) ||
  73. AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRPHighQuality.asset", typeof(ScriptableObject)) ||
  74. AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRPLowQuality.asset", typeof(ScriptableObject)) ||
  75. AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRPMediumQuality.asset", typeof(ScriptableObject))) {
  76. currentPipeline = Pipeline.HDRP;
  77. } else if (AssetDatabase.LoadAssetAtPath ("Assets/Settings/Lightweight_RenderPipeline.asset", typeof (ScriptableObject)) ||
  78. AssetDatabase.LoadAssetAtPath ("Assets/Settings/LWRP-HighQuality.asset", typeof (ScriptableObject)) ||
  79. AssetDatabase.LoadAssetAtPath ("Assets/Settings/LWRP-LowQuality.asset", typeof (ScriptableObject)) ||
  80. AssetDatabase.LoadAssetAtPath ("Assets/Settings/LWRP-MediumQuality.asset", typeof (ScriptableObject))) {
  81. currentPipeline = Pipeline.LWRP;
  82. } else if (AssetDatabase.LoadAssetAtPath("Assets/Settings/UniversalRP-HighQuality.asset", typeof(ScriptableObject)) ||
  83. AssetDatabase.LoadAssetAtPath("Assets/Settings/UniversalRP-LowQuality.asset", typeof(ScriptableObject)) ||
  84. AssetDatabase.LoadAssetAtPath("Assets/Settings/UniversalRP-MediumQuality.asset", typeof(ScriptableObject)))
  85. {
  86. currentPipeline = Pipeline.LWRP;
  87. }
  88. else {
  89. currentPipeline = Pipeline.Standard;
  90. }
  91. } else {
  92. if (AssetDatabase.FindAssets ("HDRenderPipelineAsset").Length > 0 ||
  93. AssetDatabase.FindAssets("HDRPHighQuality").Length > 0 ||
  94. AssetDatabase.FindAssets("HDRP High Quality").Length > 0) {
  95. currentPipeline = Pipeline.HDRP;
  96. } else if (AssetDatabase.FindAssets ("Lightweight_RenderPipeline").Length > 0 ||
  97. AssetDatabase.FindAssets ("LWRP-HighQuality").Length > 0 ||
  98. AssetDatabase.FindAssets ("LWRP-LowQuality").Length > 0 ||
  99. AssetDatabase.FindAssets ("LWRP-MediumQuality").Length > 0) {
  100. currentPipeline = Pipeline.LWRP;
  101. } else if (AssetDatabase.FindAssets("UniversalRP-HighQuality").Length > 0 ||
  102. AssetDatabase.FindAssets("UniversalRP-LowQuality").Length > 0 ||
  103. AssetDatabase.FindAssets("UniversalRP-MediumQuality").Length > 0)
  104. {
  105. currentPipeline = Pipeline.LWRP;
  106. } else {
  107. currentPipeline = Pipeline.Standard;
  108. }
  109. }
  110. return currentPipeline;
  111. }
  112. // Tells if an asset type is scatter or not.
  113. public static bool isScatterAsset(JObject assetJson, List<string> importedMeshpaths)
  114. {
  115. try
  116. {
  117. string[] tags = assetJson["tags"].ToObject<string[]>();
  118. string[] categories = assetJson["categories"].ToObject<string[]>();
  119. int childCount = GetMeshChildrenCount(importedMeshpaths);
  120. foreach(string tag in tags)
  121. {
  122. if (tag.ToLower() == "scatter")
  123. {
  124. return (childCount > 1); //Returns false if the is only one variation of asset.
  125. } else if (tag.ToLower() == "cmb_asset")
  126. {
  127. return (childCount > 1); //Returns false if the is only one variation of asset.
  128. }
  129. }
  130. foreach (string category in categories)
  131. {
  132. if (category.ToLower() == "scatter")
  133. {
  134. return (childCount > 1); //Returns false if the is only one variation of asset.
  135. }
  136. else if (category.ToLower() == "cmb_asset")
  137. {
  138. return (childCount > 1); //Returns false if the is only one variation of asset.
  139. }
  140. }
  141. return (childCount > 1);
  142. }
  143. catch (Exception ex)
  144. {
  145. Debug.Log("Exception::MegascansUtilities::IsScatterAsset:: " + ex.ToString());
  146. HideProgressBar();
  147. }
  148. return false;
  149. }
  150. public static int GetMeshChildrenCount(List<string> importedMeshpaths)
  151. {
  152. try
  153. {
  154. if(importedMeshpaths.Count > 0)
  155. {
  156. UnityEngine.Object loadedGeometry = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(importedMeshpaths[0]);
  157. GameObject testGO = (GameObject)Instantiate(loadedGeometry);
  158. int count = testGO.transform.childCount;
  159. DestroyImmediate(testGO);
  160. return count;
  161. }
  162. } catch (Exception ex)
  163. {
  164. Debug.Log("Exception::MegascansUtilities::GetMeshChildrenCount:: " + ex.ToString());
  165. HideProgressBar();
  166. }
  167. return 1;
  168. }
  169. /// <summary>
  170. /// Determine which asset type we're creating. Surfaces, 3D_Assets, 3D_Scatter_Assets, 3D_Plants.
  171. /// </summary>
  172. public static string GetAssetType(string jsonPath)
  173. {
  174. string t = "Surfaces";
  175. if (jsonPath.ToLower().Contains("3d"))
  176. {
  177. t = "3D_Assets";
  178. }
  179. if (jsonPath.ToLower().Contains("debris") ||
  180. jsonPath.ToLower().Contains("dbrs") ||
  181. jsonPath.ToLower().Contains("scatter") ||
  182. jsonPath.ToLower().Contains("sctr"))
  183. {
  184. t = "3D_Scatter_Assets";
  185. }
  186. if (jsonPath.ToLower().Contains("3dplant"))
  187. {
  188. t = "3D_Plants";
  189. }
  190. if (jsonPath.ToLower().Contains("decals") || jsonPath.ToLower().Contains("decal"))
  191. {
  192. t = "Decals";
  193. }
  194. else if (jsonPath.ToLower().Contains("atlas"))
  195. {
  196. t = "Atlases";
  197. }
  198. return t;
  199. }
  200. public static void CopyFileToProject(string sourcePath, string destPath)
  201. {
  202. try
  203. {
  204. if (File.Exists(sourcePath))
  205. {
  206. File.Copy(sourcePath, destPath, true);
  207. AssetDatabase.ImportAsset(destPath);
  208. AssetDatabase.Refresh();
  209. } else
  210. {
  211. Debug.Log("Exception::MegascansImageUtils::ImportTexture:: Source file does not exist.");
  212. }
  213. }
  214. catch (Exception ex)
  215. {
  216. Debug.Log("Exception::MegascansMeshUtils::Importing file to project:: " + ex.ToString());
  217. HideProgressBar();
  218. }
  219. }
  220. #endregion
  221. #region Selection Helpers
  222. /// <summary>
  223. /// Retrieves selected folders in Project view.
  224. /// </summary>
  225. public static List<string> GetSelectedFolders (List<UnityEngine.Object> selections) {
  226. List<string> folders = new List<string> ();
  227. foreach (UnityEngine.Object obj in selections) //Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
  228. {
  229. string path = AssetDatabase.GetAssetPath (obj);
  230. if (!string.IsNullOrEmpty (path)) {
  231. folders.Add (path);
  232. }
  233. }
  234. return folders;
  235. }
  236. /// <summary>
  237. /// Retrieves selected material.
  238. /// </summary>
  239. public static Material GetSelectedMaterial(string fileType = ".mat")
  240. {
  241. foreach (Material obj in Selection.GetFiltered(typeof(Material), SelectionMode.Assets))
  242. {
  243. string path = AssetDatabase.GetAssetPath(obj);
  244. if (path.Contains(fileType))
  245. {
  246. return (Material)AssetDatabase.LoadAssetAtPath(path, typeof(Material));
  247. }
  248. }
  249. return null;
  250. }
  251. /// <summary>
  252. /// Retrieves selected GameObjects with MeshRenderer component in Scene view.
  253. /// </summary>
  254. public static List<MeshRenderer> GetSelectedMeshRenderers()
  255. {
  256. List<MeshRenderer> meshRenderers = new List<MeshRenderer>();
  257. foreach (GameObject g in Selection.gameObjects)
  258. {
  259. if (g.GetComponent<MeshRenderer>() != null)
  260. {
  261. meshRenderers.Add(g.GetComponent<MeshRenderer>());
  262. }
  263. }
  264. return meshRenderers;
  265. }
  266. /// <summary>
  267. /// Recursively gather all files under the given path including all its subfolders.
  268. /// </summary>
  269. public static List<string> GetFiles (string path, string fileType = null) {
  270. List<string> files = new List<string> ();
  271. Queue<string> queue = new Queue<string> ();
  272. queue.Enqueue (path);
  273. while (queue.Count > 0) {
  274. path = queue.Dequeue ();
  275. foreach (string subDir in Directory.GetDirectories (path)) {
  276. queue.Enqueue (subDir);
  277. }
  278. foreach (string s in Directory.GetFiles (path)) {
  279. if (fileType != null && s.Contains (fileType)) {
  280. if (s.Contains (fileType)) {
  281. files.Add (s);
  282. }
  283. } else {
  284. files.Add (s);
  285. }
  286. }
  287. }
  288. return files;
  289. }
  290. #endregion
  291. #region HDRP Features PreDefined Macro Setup
  292. /*
  293. #if (UNITY_2018_2 || UNITY_2018_3 || UNITY_2018_4 || UNITY_2019 || UNITY_2020 || UNITY_2021)
  294. [MenuItem ("Window/Quixel/Enable HDRP Features")]
  295. private static void EnableHDRP () {
  296. Debug.Log ("HDRP enabled.");
  297. AddDefineIfNecessary ("HDRP", EditorUserBuildSettings.selectedBuildTargetGroup);
  298. }
  299. [MenuItem ("Window/Quixel/Disable HDRP Features")]
  300. private static void DisableHDRP () {
  301. Debug.Log ("HDRP disabled.");
  302. RemoveDefineIfNecessary ("HDRP", EditorUserBuildSettings.selectedBuildTargetGroup);
  303. }
  304. #endif
  305. */
  306. public static void AddDefineIfNecessary (string _define, BuildTargetGroup _buildTargetGroup) {
  307. var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup (_buildTargetGroup);
  308. if (defines == null) { defines = _define; } else if (defines.Length == 0) { defines = _define; } else { if (defines.IndexOf (_define, 0) < 0) { defines += ";" + _define; } }
  309. PlayerSettings.SetScriptingDefineSymbolsForGroup (_buildTargetGroup, defines);
  310. }
  311. public static void RemoveDefineIfNecessary (string _define, BuildTargetGroup _buildTargetGroup) {
  312. var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup (_buildTargetGroup);
  313. if (defines.StartsWith (_define + ";")) {
  314. // First of multiple defines.
  315. defines = defines.Remove (0, _define.Length + 1);
  316. } else if (defines.StartsWith (_define)) {
  317. // The only define.
  318. defines = defines.Remove (0, _define.Length);
  319. } else if (defines.EndsWith (";" + _define)) {
  320. // Last of multiple defines.
  321. defines = defines.Remove (defines.Length - _define.Length - 1, _define.Length + 1);
  322. } else {
  323. // Somewhere in the middle or not defined.
  324. var index = defines.IndexOf (_define, 0, System.StringComparison.Ordinal);
  325. if (index >= 0) { defines = defines.Remove (index, _define.Length + 1); }
  326. }
  327. PlayerSettings.SetScriptingDefineSymbolsForGroup (_buildTargetGroup, defines);
  328. }
  329. #endregion
  330. #region Progress Bar Utils
  331. static float maxNumberOfOperations = 0;
  332. static float currentOperationCount = 0;
  333. public static void CalculateNumberOfOperations(JObject assetData, int dispType, int texPack, int shaderType, bool hasBillboardLODOnly)
  334. {
  335. JArray meshComps = (JArray)assetData["meshList"];
  336. int prefabCount = meshComps.Count;
  337. JArray lodList = (JArray)assetData["lodList"];
  338. int meshCount = meshComps.Count;
  339. JArray textureComps = (JArray)assetData["components"];
  340. List<string> texTypes = new List<string>();
  341. for (int i = 0; i < textureComps.Count; ++i)
  342. {
  343. texTypes.Add((string)textureComps[i]["type"]);
  344. }
  345. int texCount = 0;
  346. if (texTypes.Contains("albedo") || texTypes.Contains("diffuse"))
  347. texCount++;
  348. if (texTypes.Contains("normal"))
  349. texCount++;
  350. if (texTypes.Contains("displacement") && dispType != 0)
  351. texCount++;
  352. if (texTypes.Contains("translucency"))
  353. texCount++;
  354. if (texTypes.Contains("ao") && shaderType == 2)
  355. texCount++;
  356. if (texPack == 0)
  357. {
  358. if (texTypes.Contains("metalness") || texTypes.Contains("roughness") || texTypes.Contains("gloss") || texTypes.Contains("ao") || texTypes.Contains("displacement"))
  359. texCount++;
  360. }
  361. else if (texPack == 1)
  362. {
  363. if ((texTypes.Contains("metalness") || texTypes.Contains("roughness") || texTypes.Contains("gloss") || texTypes.Contains("ao") || texTypes.Contains("displacement")) && shaderType == 0)
  364. texCount++;
  365. if (texTypes.Contains("specular"))
  366. texCount++;
  367. }
  368. string type = (string)assetData["type"];
  369. if (type.ToLower().Contains("3dplant") && !hasBillboardLODOnly)
  370. {
  371. texCount *= 2;
  372. }
  373. maxNumberOfOperations = (float)(prefabCount + meshCount + texCount);
  374. maxNumberOfOperations += 1.0f; //For the material
  375. }
  376. public static void UpdateProgressBar(float change = 0, string header = "Import Megascans Asset", string message = "Processing Asset")
  377. {
  378. currentOperationCount += change;
  379. if (currentOperationCount != maxNumberOfOperations)
  380. EditorUtility.DisplayProgressBar(header, message, (currentOperationCount / maxNumberOfOperations));
  381. else
  382. HideProgressBar();
  383. }
  384. public static void HideProgressBar()
  385. {
  386. currentOperationCount = 0;
  387. maxNumberOfOperations = 0;
  388. EditorUtility.ClearProgressBar();
  389. }
  390. public static void PrintProgressBarStats()
  391. {
  392. Debug.Log("Current: " + currentOperationCount);
  393. Debug.Log("Max: " + maxNumberOfOperations);
  394. }
  395. #endregion
  396. #region LOD Heights
  397. public static List<float> getLODHeightList(int numberOfFiles)
  398. {
  399. switch (numberOfFiles)
  400. {
  401. case 1:
  402. return new List<float> { 0.01f };
  403. case 2:
  404. return new List<float> { 0.4f, 0.01f };
  405. case 3:
  406. return new List<float> { 0.5f, 0.2f, 0.01f };
  407. case 4:
  408. return new List<float> { 0.5f, 0.3f, 0.18f, 0.01f };
  409. case 5:
  410. return new List<float> { 0.5f, 0.3f, 0.2f, 0.1f, 0.01f };
  411. case 6:
  412. return new List<float> { 0.55f, 0.35f, 0.24f, 0.15f, 0.07f, 0.01f };
  413. case 7:
  414. return new List<float> { 0.6f, 0.4f, 0.3f, 0.21f, 0.13f, 0.06f, 0.01f };
  415. default:
  416. return new List<float> { 0.65f, 0.45f, 0.35f, 0.26f, 0.18f, 0.11f, 0.06f, 0.01f };
  417. }
  418. }
  419. #endregion
  420. #region Channel packed textures data verification
  421. public static bool AlbedoHasOpacity(JObject channelData)
  422. {
  423. return channelData == null ? false : verifyMapInList((JArray)channelData["Alpha"], "opacity");
  424. }
  425. public static bool SpecularHasGloss(JObject channelData)
  426. {
  427. return channelData == null ? false : verifyMapInList((JArray)channelData["Alpha"], "gloss");
  428. }
  429. public static void MaskMapComponents(JObject channelData, out bool hasMetalness, out bool hasAO, out bool hasGloss)
  430. {
  431. hasMetalness = channelData == null ? false : verifyMapInList((JArray)channelData["Red"], "metalness");
  432. hasAO = channelData == null ? false : verifyMapInList((JArray)channelData["Green"], "ao");
  433. hasGloss = channelData == null ? false : verifyMapInList((JArray)channelData["Alpha"], "gloss");
  434. }
  435. public static bool verifyMapInList(JArray channelData, string texType)
  436. {
  437. for (int i = 0; i < channelData.Count; i++)
  438. {
  439. string packedTex = (string)channelData[i];
  440. if (packedTex.ToLower() == texType.ToLower())
  441. {
  442. return true;
  443. }
  444. }
  445. return false;
  446. }
  447. #endregion
  448. }
  449. }
  450. #endif
  451. public enum Pipeline {
  452. HDRP,
  453. LWRP,
  454. Standard
  455. }