MegascansTerrainTools.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEditor;
  4. using System;
  5. using System.Linq;
  6. #if UNITY_EDITOR
  7. namespace Quixel
  8. {
  9. public class MegascansTerrainTools
  10. {
  11. /*
  12. (Verify that the pipeline is HDRP.)
  13. * *******************************************************************************
  14. * ********************** Unity 2018.1 and 2018.2 ********************************
  15. * *******************************************************************************
  16. Terrain material blend setup steps:
  17. 1. Get selected materials and make a terrain LayeredLit Material.
  18. 2. Set Splat Prop types for the terrain.
  19. 3. Set created materail to the terrain.
  20. 4. Enable splat maps for the terrain.
  21. 5. Set Layer Mask property of the terrain material to use Splat Alpha Map.
  22. 6. Voila start painting!
  23. * *******************************************************************************
  24. * ********************** Unity 2018.3 onwards ***********************************
  25. * *******************************************************************************
  26. * Terrain material blend setup steps:
  27. 1. Get selected materials and make Terrain Layers from them.
  28. 2. Assign newly created terrain layers to terrain data.
  29. 3. Voila start painting!
  30. */
  31. static int maxMaterialAllowed = 4;
  32. public static void SetupTerrain()
  33. {
  34. string materialName = EditorPrefs.GetString("QuixelDefaultMaterialName", "Terrain Material");
  35. string materialPath = EditorPrefs.GetString("QuixelDefaultMaterialPath", "Quixel/");
  36. string tilingNumber = EditorPrefs.GetString("QuixelDefaultTiling", "10");
  37. string[] versionParts = Application.unityVersion.Split('.');
  38. int majorVersion = int.Parse(versionParts[0]);
  39. if (majorVersion < 2018)
  40. {
  41. Debug.Log("This Unity version doesn't support this feature.");
  42. return;
  43. }
  44. else
  45. {
  46. float tiling;
  47. try
  48. {
  49. tiling = float.Parse(tilingNumber);
  50. }
  51. catch (Exception ex)
  52. {
  53. Debug.Log("Exception: " + ex.ToString());
  54. tiling = 1f;
  55. }
  56. maxMaterialAllowed = MegascansUtilities.isLegacy() ? 4 : 8;
  57. List<Material> sourceMaterials = new List<Material>();
  58. Terrain terrain = null;
  59. try
  60. {
  61. //Get currently highlighted materials in the project window.
  62. sourceMaterials = GetSelectedMaterialsInProjectHierarchy();
  63. //Verify two or more materails were seleted.
  64. if (sourceMaterials.Count < 2 && MegascansUtilities.isLegacy())
  65. {
  66. Debug.Log("Not enough materials to create a blend material. Please select more materials.");
  67. return;
  68. }
  69. //Get selected terrain in the scene view.
  70. terrain = getCurrentlySelectedTerrain();
  71. //Verify a terrain is selected.
  72. if (terrain == null)
  73. {
  74. Debug.Log("No terrain selected. Please select a terrain.");
  75. //return;
  76. }
  77. }
  78. catch (Exception ex)
  79. {
  80. Debug.Log("Error getting select terrain/materials.");
  81. Debug.Log(ex);
  82. }
  83. if (MegascansUtilities.isLegacy())
  84. {
  85. try
  86. {
  87. //Set up the material here.
  88. Material terrainMaterial = SetupTerrainMaterialDeprecated(sourceMaterials, materialPath, materialName, tiling);
  89. //Get terrain data for splat maps.
  90. TerrainData terrainData = terrain.terrainData;
  91. //Get the textures from source materials to add to the painting.
  92. if (terrainData)
  93. {
  94. #pragma warning disable
  95. terrainData.splatPrototypes = getMaterialTexturesForSplatMap(sourceMaterials, tiling);
  96. }
  97. terrain.materialType = Terrain.MaterialType.Custom;
  98. terrain.materialTemplate = terrainMaterial;
  99. EnableSplatmaps(terrainData);
  100. Texture2D alphaMap = terrainData.alphamapTextures[0];
  101. terrainMaterial.SetTexture("_LayerMaskMap", alphaMap);
  102. Debug.Log("Terrain blend material successfully created!");
  103. }
  104. catch (Exception ex)
  105. {
  106. Debug.Log("Error Generating terrain blend material!");
  107. Debug.Log(ex);
  108. }
  109. }
  110. else
  111. {
  112. #if UNITY_2018_3 || UNITY_2018_4 || UNITY_2019 || UNITY_2020 || UNITY_2021
  113. //Create the material here.
  114. //Material terrainMaterial = SetupTerrainMaterial(materialPath, materialName);
  115. List<TerrainLayer> terrainLayers = new List<TerrainLayer>();
  116. foreach (TerrainLayer tl in terrain.terrainData.terrainLayers)
  117. {
  118. terrainLayers.Add(tl);
  119. }
  120. // Unity's terrain system uses the second terrain layer as default material on terrain.
  121. // To counter this we swap the position of the materials in our list.
  122. if (sourceMaterials.Count > 1 && terrainLayers.Count == 0)
  123. {
  124. Material tempMat = sourceMaterials[0];
  125. sourceMaterials[0] = sourceMaterials[1];
  126. sourceMaterials[1] = tempMat;
  127. }
  128. foreach (Material mat in sourceMaterials)
  129. {
  130. if (terrainLayers.Count <= 8)
  131. {
  132. terrainLayers.Add(createTerrainLayer(mat, tiling));
  133. }
  134. }
  135. if (terrainLayers.Count > 0)
  136. {
  137. terrain.terrainData.terrainLayers = terrainLayers.ToArray();
  138. }
  139. #endif
  140. }
  141. }
  142. }
  143. public static Material CreateMaterial(string mPath, string name, string shaderName)
  144. {
  145. string path = MegascansUtilities.FixPath(mPath);
  146. /// Unity doesn't allow you to create objects in directories which don't exist.
  147. /// So in this function, we create any and all necessary subdirectories that are required.
  148. /// We return the final subdirectory, which is used later in the asset creation too.
  149. //first, create the user specified path from the importer settings.
  150. string[] pathParts = MegascansUtilities.FixSlashes(path).Split('/');
  151. string defPath = "Assets";
  152. if (pathParts.Length > 0)
  153. {
  154. for (int i = 0; i < pathParts.Length; ++i)
  155. {
  156. defPath = MegascansUtilities.ValidateFolderCreate(defPath, pathParts[i]);
  157. }
  158. }
  159. defPath = defPath + "/" + name + ".mat";
  160. Material terrainMaterial = new Material(Shader.Find(shaderName));
  161. AssetDatabase.CreateAsset(terrainMaterial, defPath);
  162. AssetDatabase.Refresh();
  163. return terrainMaterial;
  164. }
  165. //This method sets the right parameters depending upon the selected settings.
  166. public static Material SetupTerrainMaterial(string materialPath, string materialName)
  167. {
  168. Material terrainMaterial = CreateMaterial(materialPath, materialName, "HDRenderPipeline/TerrainLit");
  169. terrainMaterial.enableInstancing = true;
  170. return terrainMaterial;
  171. }
  172. public static Material SetupTerrainMaterialDeprecated(List<Material> sourceMaterials, string materialPath, string materialName, float tiling)
  173. {
  174. int numberOfLayers = Mathf.Clamp(sourceMaterials.Count + 1, 2, 4);
  175. Material terrainMaterial = CreateMaterial(materialPath, materialName, "HDRenderPipeline/LayeredLit");
  176. terrainMaterial.SetFloat("_LayerCount", numberOfLayers);
  177. terrainMaterial.EnableKeyword("_LAYEREDLIT_" + numberOfLayers.ToString() + "_LAYERS");
  178. numberOfLayers--;
  179. if (numberOfLayers > 3)
  180. {
  181. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[3], "0", tiling);
  182. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[0], "1", tiling);
  183. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[1], "2", tiling);
  184. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[2], "3", tiling);
  185. }
  186. else if (numberOfLayers > 2)
  187. {
  188. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[2], "0", tiling);
  189. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[0], "1", tiling);
  190. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[1], "2", tiling);
  191. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[2], "3", tiling);
  192. }
  193. else if (numberOfLayers > 1)
  194. {
  195. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[1], "0", tiling);
  196. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[0], "1", tiling);
  197. SetFloatsAndTexturesDeprecated(terrainMaterial, sourceMaterials[1], "2", tiling);
  198. }
  199. return terrainMaterial;
  200. }
  201. public static void SetFloatsAndTexturesDeprecated(Material terrainMaterial, Material sourceMaterial, string suffix, float tiling)
  202. {
  203. terrainMaterial.EnableKeyword("_MASKMAP" + suffix);
  204. terrainMaterial.EnableKeyword("_NORMALMAP" + suffix);
  205. terrainMaterial.EnableKeyword("_NORMALMAP_TANGENT_SPACE" + suffix);
  206. terrainMaterial.EnableKeyword("_MASKMAP" + suffix);
  207. terrainMaterial.SetTexture("_BaseColorMap" + suffix, sourceMaterial.mainTexture);
  208. terrainMaterial.SetTexture("_MaskMap" + suffix, sourceMaterial.GetTexture("_MaskMap"));
  209. terrainMaterial.SetTexture("_NormalMap" + suffix, sourceMaterial.GetTexture("_NormalMap"));
  210. terrainMaterial.SetFloat("_NormalMapSpace" + suffix, 0f);
  211. terrainMaterial.SetFloat("_Metallic" + suffix, 1f);
  212. terrainMaterial.SetTextureScale("_BaseColorMap" + suffix, new Vector2(tiling, tiling));
  213. terrainMaterial.SetTextureScale("_MaskMap" + suffix, new Vector2(tiling, tiling));
  214. terrainMaterial.SetTextureScale("_NormalMap" + suffix, new Vector2(tiling, tiling));
  215. }
  216. public static List<Material> GetSelectedMaterialsInProjectHierarchy()
  217. {
  218. List<Material> selectedMaterials = new List<Material>();
  219. List<string> selectedMaterialPaths = new List<string>();
  220. List<UnityEngine.Object> selection = new List<UnityEngine.Object>();
  221. foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
  222. {
  223. if (obj.GetType() == typeof(Material) && selectedMaterials.Count <= maxMaterialAllowed)
  224. {
  225. selectedMaterials.Add((Material)obj);
  226. }
  227. else
  228. {
  229. selection.Add(obj);
  230. }
  231. }
  232. List<string> selectedFolders = MegascansUtilities.GetSelectedFolders(selection);
  233. foreach (string folder in selectedFolders)
  234. {
  235. selectedMaterialPaths = selectedMaterialPaths.Concat(MegascansUtilities.GetFiles(folder, ".mat")).ToList();
  236. }
  237. foreach (string mPath in selectedMaterialPaths)
  238. {
  239. Material mat = (Material)AssetDatabase.LoadAssetAtPath(mPath, typeof(Material));
  240. if (mat != null && selectedMaterials.Count <= maxMaterialAllowed) //4 is the max number of layers currently allowed in the HDRenderPipeline/LayeredLit shader.
  241. {
  242. selectedMaterials.Add(mat);
  243. }
  244. }
  245. return selectedMaterials;
  246. }
  247. public static SplatPrototype[] getMaterialTexturesForSplatMap(List<Material> materials, float tiling)
  248. {
  249. SplatPrototype[] splatPropsTypes = new SplatPrototype[materials.Count];
  250. for (int i = 0; i < materials.Count; i++)
  251. {
  252. SplatPrototype prop = new SplatPrototype();
  253. prop.texture = (Texture2D)materials[i].mainTexture;
  254. prop.normalMap = (Texture2D)materials[i].GetTexture("_NormalMap");
  255. prop.tileSize = new Vector2(tiling, tiling);
  256. splatPropsTypes[i] = prop;
  257. }
  258. return splatPropsTypes;
  259. }
  260. public static Terrain getCurrentlySelectedTerrain()
  261. {
  262. foreach (UnityEngine.Object obj in Selection.objects)
  263. {
  264. if (obj.GetType() == typeof(GameObject))
  265. {
  266. GameObject terrain = (GameObject)obj;
  267. if (terrain.GetComponent<Terrain>())
  268. {
  269. return terrain.GetComponent<Terrain>();
  270. }
  271. }
  272. }
  273. return null;
  274. }
  275. public static void EnableSplatmaps(TerrainData terrainData)
  276. {
  277. UnityEngine.Object[] data = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(terrainData));
  278. foreach (UnityEngine.Object o in data)
  279. {
  280. if (o is Texture2D)
  281. {
  282. (o as Texture2D).hideFlags = HideFlags.None;
  283. AssetDatabase.SaveAssets();
  284. AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(o));
  285. }
  286. }
  287. }
  288. #if UNITY_2018_3 || UNITY_2018_4 || UNITY_2019 || UNITY_2020 || UNITY_2021
  289. public static TerrainLayer createTerrainLayer(Material mat, float tiling)
  290. {
  291. TerrainLayer terrainLayer = new TerrainLayer();
  292. string path = AssetDatabase.GetAssetPath(mat);
  293. path = path.Replace(".mat", ".terrainlayer");
  294. AssetDatabase.CreateAsset(terrainLayer, path);
  295. AssetDatabase.Refresh();
  296. terrainLayer.diffuseTexture = (Texture2D)mat.mainTexture;
  297. //attempt to auto-detect a settings file for Lightweight or HD pipelines
  298. switch (MegascansUtilities.getCurrentPipeline())
  299. {
  300. case Pipeline.HDRP:
  301. terrainLayer.normalMapTexture = (Texture2D)mat.GetTexture("_NormalMap");
  302. if (mat.GetFloat("_MaterialID") == 4)
  303. {
  304. terrainLayer.maskMapTexture = (Texture2D)mat.GetTexture("_SpecularColorMap");
  305. terrainLayer.metallic = 1.0f;
  306. }
  307. else if (mat.GetFloat("_MaterialID") == 1)
  308. {
  309. terrainLayer.maskMapTexture = (Texture2D)mat.GetTexture("_MaskMap");
  310. terrainLayer.specular = new Color(1.0f, 1.0f, 1.0f);
  311. }
  312. break;
  313. case Pipeline.LWRP:
  314. terrainLayer.normalMapTexture = (Texture2D)mat.GetTexture("_BumpMap");
  315. if (mat.GetFloat("_WorkflowMode") == 1)
  316. {
  317. terrainLayer.maskMapTexture = (Texture2D)mat.GetTexture("_MetallicGlossMap");
  318. terrainLayer.specular = new Color(1.0f, 1.0f, 1.0f);
  319. }
  320. else
  321. {
  322. terrainLayer.maskMapTexture = (Texture2D)mat.GetTexture("_SpecGlossMap");
  323. terrainLayer.metallic = 1.0f;
  324. }
  325. break;
  326. case Pipeline.Standard:
  327. terrainLayer.normalMapTexture = (Texture2D)mat.GetTexture("_BumpMap");
  328. if (mat.shader.ToString() == "Standard (Specular setup)")
  329. {
  330. terrainLayer.maskMapTexture = (Texture2D)mat.GetTexture("_SpecGlossMap");
  331. terrainLayer.metallic = 1.0f;
  332. }
  333. else
  334. {
  335. terrainLayer.maskMapTexture = (Texture2D)mat.GetTexture("_MetallicGlossMap");
  336. terrainLayer.specular = new Color(1.0f, 1.0f, 1.0f);
  337. }
  338. break;
  339. }
  340. terrainLayer.tileSize = new Vector2(tiling, tiling);
  341. return terrainLayer;
  342. }
  343. public static void CreateTerrainLayerFromMat()
  344. {
  345. try {
  346. Material selectedMat = MegascansUtilities.GetSelectedMaterial();
  347. if (!selectedMat)
  348. return;
  349. Debug.Log("Here");
  350. createTerrainLayer(selectedMat, 1f);
  351. Debug.Log("Successfully created the terrain layer.");
  352. }
  353. catch (Exception ex)
  354. {
  355. Debug.Log("Exception::MegascansImageUtils::Flip Green Channel:: " + ex.ToString());
  356. MegascansUtilities.HideProgressBar();
  357. }
  358. }
  359. #endif
  360. }
  361. }
  362. #endif