MB_TextureBakerEditorConfigureMultiMaterials.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. // MeshBaker
  2. // Copyright © 2011-2012 Ian Deane
  3. //----------------------------------------------
  4. using UnityEngine;
  5. using System.Collections;
  6. using System.IO;
  7. using System;
  8. using System.Collections.Specialized;
  9. using System.Collections.Generic;
  10. using System.Text.RegularExpressions;
  11. using DigitalOpus.MB.Core;
  12. using UnityEditor;
  13. namespace DigitalOpus.MB.MBEditor
  14. {
  15. public class MB_TextureBakerEditorConfigureMultiMaterials
  16. {
  17. public static void DrawMultipleMaterialsMappings(MB3_TextureBaker momm, SerializedObject textureBaker, MB3_TextureBakerEditorInternal tbEditor)
  18. {
  19. EditorGUILayout.BeginVertical(tbEditor.editorStyles.multipleMaterialBackgroundStyle);
  20. EditorGUILayout.LabelField("Source Material To Combined Mapping", EditorStyles.boldLabel);
  21. float oldLabelWidth = EditorGUIUtility.labelWidth;
  22. EditorGUIUtility.labelWidth = 300;
  23. EditorGUILayout.PropertyField(tbEditor.doMultiMaterialIfOBUVs, MB3_TextureBakerEditorInternal.gc_DoMultiMaterialSplitAtlasesIfOBUVs);
  24. EditorGUILayout.PropertyField(tbEditor.doMultiMaterialSplitAtlasesIfTooBig, MB3_TextureBakerEditorInternal.gc_DoMultiMaterialSplitAtlasesIfTooBig);
  25. EditorGUIUtility.labelWidth = oldLabelWidth;
  26. if (GUILayout.Button(MB3_TextureBakerEditorInternal.configAtlasMultiMatsFromObjsContent))
  27. {
  28. MB_TextureBakerEditorConfigureMultiMaterials.ConfigureMutiMaterialsFromObjsToCombine(momm, tbEditor.resultMaterials, textureBaker);
  29. }
  30. EditorGUILayout.BeginHorizontal();
  31. tbEditor.resultMaterialsFoldout = EditorGUILayout.Foldout(tbEditor.resultMaterialsFoldout, MB3_TextureBakerEditorInternal.combinedMaterialsGUIContent);
  32. if (GUILayout.Button(MB3_TextureBakerEditorInternal.insertContent, EditorStyles.miniButtonLeft, MB3_TextureBakerEditorInternal.buttonWidth))
  33. {
  34. if (tbEditor.resultMaterials.arraySize == 0)
  35. {
  36. momm.resultMaterials = new MB_MultiMaterial[1];
  37. momm.resultMaterials[0].considerMeshUVs = momm.fixOutOfBoundsUVs;
  38. }
  39. else
  40. {
  41. int idx = tbEditor.resultMaterials.arraySize - 1;
  42. tbEditor.resultMaterials.InsertArrayElementAtIndex(idx);
  43. tbEditor.resultMaterials.GetArrayElementAtIndex(idx + 1).FindPropertyRelative("considerMeshUVs").boolValue = momm.fixOutOfBoundsUVs;
  44. }
  45. }
  46. if (GUILayout.Button(MB3_TextureBakerEditorInternal.deleteContent, EditorStyles.miniButtonRight, MB3_TextureBakerEditorInternal.buttonWidth))
  47. {
  48. tbEditor.resultMaterials.DeleteArrayElementAtIndex(tbEditor.resultMaterials.arraySize - 1);
  49. }
  50. EditorGUILayout.EndHorizontal();
  51. if (tbEditor.resultMaterialsFoldout)
  52. {
  53. for (int i = 0; i < tbEditor.resultMaterials.arraySize; i++)
  54. {
  55. EditorGUILayout.Separator();
  56. if (i % 2 == 1)
  57. {
  58. EditorGUILayout.BeginVertical(tbEditor.editorStyles.multipleMaterialBackgroundStyle);
  59. }
  60. else
  61. {
  62. EditorGUILayout.BeginVertical(tbEditor.editorStyles.multipleMaterialBackgroundStyleDarker);
  63. }
  64. string s = "";
  65. if (i < momm.resultMaterials.Length && momm.resultMaterials[i] != null && momm.resultMaterials[i].combinedMaterial != null) s = momm.resultMaterials[i].combinedMaterial.shader.ToString();
  66. EditorGUILayout.BeginHorizontal();
  67. EditorGUILayout.LabelField("---------- submesh:" + i + " " + s, EditorStyles.boldLabel);
  68. if (GUILayout.Button(MB3_TextureBakerEditorInternal.deleteContent, EditorStyles.miniButtonRight, MB3_TextureBakerEditorInternal.buttonWidth))
  69. {
  70. tbEditor.resultMaterials.DeleteArrayElementAtIndex(i);
  71. }
  72. EditorGUILayout.EndHorizontal();
  73. if (i < tbEditor.resultMaterials.arraySize)
  74. {
  75. EditorGUILayout.Separator();
  76. SerializedProperty resMat = tbEditor.resultMaterials.GetArrayElementAtIndex(i);
  77. EditorGUILayout.PropertyField(resMat.FindPropertyRelative("combinedMaterial"));
  78. EditorGUILayout.PropertyField(resMat.FindPropertyRelative("considerMeshUVs"));
  79. SerializedProperty sourceMats = resMat.FindPropertyRelative("sourceMaterials");
  80. EditorGUILayout.PropertyField(sourceMats, true);
  81. }
  82. EditorGUILayout.EndVertical();
  83. }
  84. }
  85. EditorGUILayout.EndVertical();
  86. }
  87. /* tried to see if the MultiMaterialConfig could be done using the GroupBy filters. Saddly it didn't work */
  88. public static void ConfigureMutiMaterialsFromObjsToCombine2(MB3_TextureBaker mom, SerializedProperty resultMaterials, SerializedObject textureBaker)
  89. {
  90. if (mom.GetObjectsToCombine().Count == 0)
  91. {
  92. Debug.LogError("You need to add some objects to combine before building the multi material list.");
  93. return;
  94. }
  95. if (resultMaterials.arraySize > 0)
  96. {
  97. Debug.LogError("You already have some source to combined material mappings configured. You must remove these before doing this operation.");
  98. return;
  99. }
  100. if (mom.textureBakeResults == null)
  101. {
  102. Debug.LogError("Texture Bake Result asset must be set before using this operation.");
  103. return;
  104. }
  105. //validate that the objects to be combined are valid
  106. for (int i = 0; i < mom.GetObjectsToCombine().Count; i++)
  107. {
  108. GameObject go = mom.GetObjectsToCombine()[i];
  109. if (go == null)
  110. {
  111. Debug.LogError("Null object in list of objects to combine at position " + i);
  112. return;
  113. }
  114. Renderer r = go.GetComponent<Renderer>();
  115. if (r == null || (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer)))
  116. {
  117. Debug.LogError("GameObject at position " + i + " in list of objects to combine did not have a renderer");
  118. return;
  119. }
  120. if (r.sharedMaterial == null)
  121. {
  122. Debug.LogError("GameObject at position " + i + " in list of objects to combine has a null material");
  123. return;
  124. }
  125. }
  126. IGroupByFilter[] filters = new IGroupByFilter[3];
  127. filters[0] = new GroupByOutOfBoundsUVs();
  128. filters[1] = new GroupByShader();
  129. filters[2] = new MB3_GroupByStandardShaderType();
  130. List<GameObjectFilterInfo> gameObjects = new List<GameObjectFilterInfo>();
  131. HashSet<GameObject> objectsAlreadyIncludedInBakers = new HashSet<GameObject>();
  132. for (int i = 0; i < mom.GetObjectsToCombine().Count; i++)
  133. {
  134. GameObjectFilterInfo goaw = new GameObjectFilterInfo(mom.GetObjectsToCombine()[i], objectsAlreadyIncludedInBakers, filters);
  135. if (goaw.materials.Length > 0) //don't consider renderers with no materials
  136. {
  137. gameObjects.Add(goaw);
  138. }
  139. }
  140. //analyse meshes
  141. Dictionary<int, MB_Utility.MeshAnalysisResult> meshAnalysisResultCache = new Dictionary<int, MB_Utility.MeshAnalysisResult>();
  142. int totalVerts = 0;
  143. for (int i = 0; i < gameObjects.Count; i++)
  144. {
  145. //string rpt = String.Format("Processing {0} [{1} of {2}]", gameObjects[i].go.name, i, gameObjects.Count);
  146. //EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " A", .6f);
  147. Mesh mm = MB_Utility.GetMesh(gameObjects[i].go);
  148. int nVerts = 0;
  149. if (mm != null)
  150. {
  151. nVerts += mm.vertexCount;
  152. MB_Utility.MeshAnalysisResult mar;
  153. if (!meshAnalysisResultCache.TryGetValue(mm.GetInstanceID(), out mar))
  154. {
  155. //EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Check Out Of Bounds UVs", .6f);
  156. MB_Utility.hasOutOfBoundsUVs(mm, ref mar);
  157. //Rect dummy = mar.uvRect;
  158. MB_Utility.doSubmeshesShareVertsOrTris(mm, ref mar);
  159. meshAnalysisResultCache.Add(mm.GetInstanceID(), mar);
  160. }
  161. if (mar.hasOutOfBoundsUVs)
  162. {
  163. int w = (int)mar.uvRect.width;
  164. int h = (int)mar.uvRect.height;
  165. gameObjects[i].outOfBoundsUVs = true;
  166. gameObjects[i].warning += " [WARNING: has uvs outside the range (0,1) tex is tiled " + w + "x" + h + " times]";
  167. }
  168. if (mar.hasOverlappingSubmeshVerts)
  169. {
  170. gameObjects[i].submeshesOverlap = true;
  171. gameObjects[i].warning += " [WARNING: Submeshes share verts or triangles. 'Multiple Combined Materials' feature may not work.]";
  172. }
  173. }
  174. totalVerts += nVerts;
  175. //EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Validate OBuvs Multi Material", .6f);
  176. Renderer mr = gameObjects[i].go.GetComponent<Renderer>();
  177. if (!MB_Utility.AreAllSharedMaterialsDistinct(mr.sharedMaterials))
  178. {
  179. gameObjects[i].warning += " [WARNING: Object uses same material on multiple submeshes. This may produce poor results when used with multiple materials or fix out of bounds uvs.]";
  180. }
  181. }
  182. List<GameObjectFilterInfo> objsNotAddedToBaker = new List<GameObjectFilterInfo>();
  183. Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap = MB3_MeshBakerEditorWindowAnalyseSceneTab.sortIntoBakeGroups3(gameObjects, objsNotAddedToBaker, filters, false, mom.maxAtlasSize);
  184. mom.resultMaterials = new MB_MultiMaterial[gs2bakeGroupMap.Keys.Count];
  185. string pth = AssetDatabase.GetAssetPath(mom.textureBakeResults);
  186. string baseName = Path.GetFileNameWithoutExtension(pth);
  187. string folderPath = pth.Substring(0, pth.Length - baseName.Length - 6);
  188. int k = 0;
  189. foreach (GameObjectFilterInfo m in gs2bakeGroupMap.Keys)
  190. {
  191. MB_MultiMaterial mm = mom.resultMaterials[k] = new MB_MultiMaterial();
  192. mm.sourceMaterials = new List<Material>();
  193. mm.sourceMaterials.Add(m.materials[0]);
  194. string matName = folderPath + baseName + "-mat" + k + ".mat";
  195. Material newMat = new Material(Shader.Find("Diffuse"));
  196. MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, m.materials[0]);
  197. AssetDatabase.CreateAsset(newMat, matName);
  198. mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material));
  199. k++;
  200. }
  201. MBVersionEditor.UpdateIfDirtyOrScript(textureBaker);
  202. }
  203. //posibilities
  204. // using fixOutOfBoundsUVs or not
  205. //
  206. public static void ConfigureMutiMaterialsFromObjsToCombine(MB3_TextureBaker mom, SerializedProperty resultMaterials, SerializedObject textureBaker)
  207. {
  208. if (mom.GetObjectsToCombine().Count == 0)
  209. {
  210. Debug.LogError("You need to add some objects to combine before building the multi material list.");
  211. return;
  212. }
  213. if (resultMaterials.arraySize > 0)
  214. {
  215. Debug.LogError("You already have some source to combined material mappings configured. You must remove these before doing this operation.");
  216. return;
  217. }
  218. if (mom.textureBakeResults == null)
  219. {
  220. Debug.LogError("Texture Bake Result asset must be set before using this operation.");
  221. return;
  222. }
  223. Dictionary<MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List<List<Material>>> shader2Material_map = new Dictionary<MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List<List<Material>>>();
  224. Dictionary<Material, Mesh> obUVobject2mesh_map = new Dictionary<Material, Mesh>();
  225. //validate that the objects to be combined are valid
  226. for (int i = 0; i < mom.GetObjectsToCombine().Count; i++)
  227. {
  228. GameObject go = mom.GetObjectsToCombine()[i];
  229. if (go == null)
  230. {
  231. Debug.LogError("Null object in list of objects to combine at position " + i);
  232. return;
  233. }
  234. Renderer r = go.GetComponent<Renderer>();
  235. if (r == null || (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer)))
  236. {
  237. Debug.LogError("GameObject at position " + i + " in list of objects to combine did not have a renderer");
  238. return;
  239. }
  240. if (r.sharedMaterial == null)
  241. {
  242. Debug.LogError("GameObject at position " + i + " in list of objects to combine has a null material");
  243. return;
  244. }
  245. }
  246. //first pass put any meshes with obUVs on their own submesh if not fixing OB uvs
  247. if (mom.doMultiMaterialSplitAtlasesIfOBUVs)
  248. {
  249. for (int i = 0; i < mom.GetObjectsToCombine().Count; i++)
  250. {
  251. GameObject go = mom.GetObjectsToCombine()[i];
  252. Mesh m = MB_Utility.GetMesh(go);
  253. MB_Utility.MeshAnalysisResult dummyMar = new MB_Utility.MeshAnalysisResult();
  254. Renderer r = go.GetComponent<Renderer>();
  255. for (int j = 0; j < r.sharedMaterials.Length; j++)
  256. {
  257. if (MB_Utility.hasOutOfBoundsUVs(m, ref dummyMar, j))
  258. {
  259. if (!obUVobject2mesh_map.ContainsKey(r.sharedMaterials[j]))
  260. {
  261. Debug.LogWarning("Object " + go + " submesh " + j + " uses UVs outside the range 0,0..1,1 to generate tiling. This object has been mapped to its own submesh in the combined mesh. It can share a submesh with other objects that use different materials if you use the fix out of bounds UVs feature which will bake the tiling");
  262. obUVobject2mesh_map.Add(r.sharedMaterials[j], m);
  263. }
  264. }
  265. }
  266. }
  267. }
  268. //second pass put other materials without OB uvs in a shader to material map
  269. for (int i = 0; i < mom.GetObjectsToCombine().Count; i++)
  270. {
  271. Renderer r = mom.GetObjectsToCombine()[i].GetComponent<Renderer>();
  272. for (int j = 0; j < r.sharedMaterials.Length; j++)
  273. {
  274. if (!obUVobject2mesh_map.ContainsKey(r.sharedMaterials[j]))
  275. { //if not already added
  276. if (r.sharedMaterials[j] == null) continue;
  277. List<List<Material>> binsOfMatsThatUseShader = null;
  278. MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo newKey = new MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo(r.sharedMaterials[j].shader, r.sharedMaterials[j]);
  279. if (!shader2Material_map.TryGetValue(newKey, out binsOfMatsThatUseShader))
  280. {
  281. binsOfMatsThatUseShader = new List<List<Material>>();
  282. binsOfMatsThatUseShader.Add(new List<Material>());
  283. shader2Material_map.Add(newKey, binsOfMatsThatUseShader);
  284. }
  285. if (!binsOfMatsThatUseShader[0].Contains(r.sharedMaterials[j])) binsOfMatsThatUseShader[0].Add(r.sharedMaterials[j]);
  286. }
  287. }
  288. }
  289. int numResMats = shader2Material_map.Count;
  290. //third pass for each shader grouping check how big the atlas would be and group into bins that would fit in an atlas
  291. if (mom.doMultiMaterialSplitAtlasesIfTooBig)
  292. {
  293. if (mom.packingAlgorithm == MB2_PackingAlgorithmEnum.UnitysPackTextures)
  294. {
  295. Debug.LogWarning("Unity texture packer does not support splitting atlases if too big. Atlases will not be split.");
  296. }
  297. else
  298. {
  299. numResMats = 0;
  300. foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo sh in shader2Material_map.Keys)
  301. {
  302. List<List<Material>> binsOfMatsThatUseShader = shader2Material_map[sh];
  303. List<Material> allMatsThatUserShader = binsOfMatsThatUseShader[0];//at this point everything is in the same list
  304. binsOfMatsThatUseShader.RemoveAt(0);
  305. MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner();
  306. combiner.saveAtlasesAsAssets = false;
  307. if (allMatsThatUserShader.Count > 1) combiner.fixOutOfBoundsUVs = mom.fixOutOfBoundsUVs;
  308. else combiner.fixOutOfBoundsUVs = false;
  309. // Do the texture pack
  310. List<AtlasPackingResult> packingResults = new List<AtlasPackingResult>();
  311. Material tempMat = new Material(sh.shader);
  312. MB_AtlasesAndRects atlasesAndRects = new MB_AtlasesAndRects();
  313. combiner.CombineTexturesIntoAtlases(null, atlasesAndRects, tempMat, mom.GetObjectsToCombine(), allMatsThatUserShader, null, packingResults,
  314. onlyPackRects:true, splitAtlasWhenPackingIfTooBig:true);
  315. for (int i = 0; i < packingResults.Count; i++)
  316. {
  317. List<MB_MaterialAndUVRect> matsData = (List<MB_MaterialAndUVRect>)packingResults[i].data;
  318. List<Material> mats = new List<Material>();
  319. for (int j = 0; j < matsData.Count; j++)
  320. {
  321. Material mat = matsData[j].material;
  322. if (!mats.Contains(mat))
  323. {
  324. mats.Add(mat);
  325. }
  326. }
  327. binsOfMatsThatUseShader.Add(mats);
  328. }
  329. numResMats += binsOfMatsThatUseShader.Count;
  330. }
  331. }
  332. }
  333. //build the result materials
  334. if (shader2Material_map.Count == 0 && obUVobject2mesh_map.Count == 0) Debug.LogError("Found no materials in list of objects to combine");
  335. mom.resultMaterials = new MB_MultiMaterial[numResMats + obUVobject2mesh_map.Count];
  336. string pth = AssetDatabase.GetAssetPath(mom.textureBakeResults);
  337. string baseName = Path.GetFileNameWithoutExtension(pth);
  338. string folderPath = pth.Substring(0, pth.Length - baseName.Length - 6);
  339. int k = 0;
  340. foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo sh in shader2Material_map.Keys)
  341. {
  342. foreach (List<Material> matsThatUse in shader2Material_map[sh])
  343. {
  344. MB_MultiMaterial mm = mom.resultMaterials[k] = new MB_MultiMaterial();
  345. mm.sourceMaterials = matsThatUse;
  346. if (mm.sourceMaterials.Count == 1)
  347. {
  348. mm.considerMeshUVs = false;
  349. }
  350. else
  351. {
  352. mm.considerMeshUVs = mom.fixOutOfBoundsUVs;
  353. }
  354. string matName = folderPath + baseName + "-mat" + k + ".mat";
  355. Material newMat = new Material(Shader.Find("Diffuse"));
  356. if (matsThatUse.Count > 0 && matsThatUse[0] != null)
  357. {
  358. MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, matsThatUse[0]);
  359. }
  360. AssetDatabase.CreateAsset(newMat, matName);
  361. mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material));
  362. k++;
  363. }
  364. }
  365. foreach (Material m in obUVobject2mesh_map.Keys)
  366. {
  367. MB_MultiMaterial mm = mom.resultMaterials[k] = new MB_MultiMaterial();
  368. mm.sourceMaterials = new List<Material>();
  369. mm.sourceMaterials.Add(m);
  370. mm.considerMeshUVs = false;
  371. string matName = folderPath + baseName + "-mat" + k + ".mat";
  372. Material newMat = new Material(Shader.Find("Diffuse"));
  373. MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, m);
  374. AssetDatabase.CreateAsset(newMat, matName);
  375. mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material));
  376. k++;
  377. }
  378. MBVersionEditor.UpdateIfDirtyOrScript(textureBaker);
  379. }
  380. }
  381. }