MB3_MeshBakerEditorWindowAnalyseSceneTab.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. //----------------------------------------------
  2. // MeshBaker
  3. // Copyright © 2011-2012 Ian Deane
  4. //----------------------------------------------
  5. using UnityEditor;
  6. using UnityEngine;
  7. using System;
  8. using System.Reflection;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using DigitalOpus.MB.Core;
  12. namespace DigitalOpus.MB.MBEditor
  13. {
  14. public class MB3_MeshBakerEditorWindowAnalyseSceneTab
  15. {
  16. const int NUM_FILTERS = 5;
  17. bool writeReportFile = false;
  18. bool splitAtlasesSoMeshesFit = false;
  19. int atlasSize = 4096;
  20. string generate_AssetsFolder = "";
  21. List<List<GameObjectFilterInfo>> sceneAnalysisResults = new List<List<GameObjectFilterInfo>>();
  22. bool[] sceneAnalysisResultsFoldouts = new bool[0];
  23. int[] groupByFilterIdxs = new int[NUM_FILTERS];
  24. string[] groupByOptionNames;
  25. IGroupByFilter[] groupByOptionFilters;
  26. IGroupByFilter[] filters;
  27. Vector2 scrollPos2 = Vector2.zero;
  28. GUIContent gc_atlasSize = new GUIContent("Max Atlas Size", "");
  29. GUIContent gc_splitAtlasesSoMeshesFit = new GUIContent("Split Groups If Textures Would Exceed Atlas Size (beta)", "If combining the textures into a single atlas would exceed the maximum atlas size then create multiple atlases. Othersize texture sizes are reduced.");
  30. public static bool InterfaceFilter(Type typeObj, System.Object criteriaObj)
  31. {
  32. return typeObj.ToString() == criteriaObj.ToString();
  33. }
  34. void populateGroupByFilters()
  35. {
  36. string qualifiedInterfaceName = "DigitalOpus.MB.Core.IGroupByFilter";
  37. var interfaceFilter = new TypeFilter(InterfaceFilter);
  38. List<Type> types = new List<Type>();
  39. foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
  40. {
  41. System.Collections.IEnumerable typesIterator = null;
  42. try
  43. {
  44. typesIterator = ass.GetTypes();
  45. }
  46. catch (Exception e)
  47. {
  48. //Debug.Log("The assembly that I could not read types for was: " + ass.GetName());
  49. //suppress error
  50. e.Equals(null);
  51. }
  52. if (typesIterator != null)
  53. {
  54. foreach (Type ty in ass.GetTypes())
  55. {
  56. var myInterfaces = ty.FindInterfaces(interfaceFilter, qualifiedInterfaceName);
  57. if (myInterfaces.Length > 0)
  58. {
  59. types.Add(ty);
  60. }
  61. }
  62. }
  63. }
  64. List<string> filterNames = new List<string>();
  65. List<IGroupByFilter> filters = new List<IGroupByFilter>();
  66. filterNames.Add("None");
  67. filters.Add(null);
  68. foreach (Type tt in types)
  69. {
  70. if (!tt.IsAbstract && !tt.IsInterface)
  71. {
  72. IGroupByFilter instance = (IGroupByFilter)System.Activator.CreateInstance(tt);
  73. filterNames.Add(instance.GetName());
  74. filters.Add(instance);
  75. }
  76. }
  77. groupByOptionNames = filterNames.ToArray();
  78. groupByOptionFilters = filters.ToArray();
  79. }
  80. public void drawTabAnalyseScene(Rect position)
  81. {
  82. //first time we are displaying collect the filters
  83. if (groupByOptionNames == null || groupByOptionNames.Length == 0)
  84. {
  85. //var types = AppDomain.CurrentDomain.GetAssemblies()
  86. // .SelectMany(s => s.GetTypes())
  87. // .Where(p => type.IsAssignableFrom(p));
  88. populateGroupByFilters();
  89. //set filter initial values
  90. for (int i = 0; i < groupByOptionFilters.Length; i++)
  91. {
  92. if (groupByOptionFilters[i] is GroupByShader)
  93. {
  94. groupByFilterIdxs[0] = i;
  95. break;
  96. }
  97. }
  98. for (int i = 0; i < groupByOptionFilters.Length; i++)
  99. {
  100. if (groupByOptionFilters[i] is GroupByStatic)
  101. {
  102. groupByFilterIdxs[1] = i;
  103. break;
  104. }
  105. }
  106. for (int i = 0; i < groupByOptionFilters.Length; i++)
  107. {
  108. if (groupByOptionFilters[i] is GroupByRenderType)
  109. {
  110. groupByFilterIdxs[2] = i;
  111. break;
  112. }
  113. }
  114. for (int i = 0; i < groupByOptionFilters.Length; i++)
  115. {
  116. if (groupByOptionFilters[i] is GroupByOutOfBoundsUVs)
  117. {
  118. groupByFilterIdxs[3] = i;
  119. break;
  120. }
  121. }
  122. groupByFilterIdxs[4] = 0; //none
  123. }
  124. if (groupByFilterIdxs == null || groupByFilterIdxs.Length < NUM_FILTERS)
  125. {
  126. groupByFilterIdxs = new int[]{
  127. 0,0,0,0,0
  128. };
  129. }
  130. EditorGUILayout.HelpBox("List shaders in scene prints a report to the console of shaders and which objects use them. This is useful for planning which objects to combine.", UnityEditor.MessageType.None);
  131. groupByFilterIdxs[0] = EditorGUILayout.Popup("Group By:", groupByFilterIdxs[0], groupByOptionNames);
  132. for (int i = 1; i < NUM_FILTERS; i++)
  133. {
  134. groupByFilterIdxs[i] = EditorGUILayout.Popup("Then Group By:", groupByFilterIdxs[i], groupByOptionNames);
  135. }
  136. EditorGUILayout.BeginHorizontal();
  137. float oldLabelWidth = EditorGUIUtility.labelWidth;
  138. EditorGUIUtility.labelWidth = 300;
  139. splitAtlasesSoMeshesFit = EditorGUILayout.Toggle(gc_splitAtlasesSoMeshesFit, splitAtlasesSoMeshesFit);
  140. EditorGUIUtility.labelWidth = oldLabelWidth;
  141. bool enableAtlasField = true;
  142. if (splitAtlasesSoMeshesFit)
  143. {
  144. enableAtlasField = false;
  145. }
  146. EditorGUI.BeginDisabledGroup(enableAtlasField);
  147. atlasSize = EditorGUILayout.IntField(gc_atlasSize, atlasSize);
  148. EditorGUI.EndDisabledGroup();
  149. EditorGUILayout.EndHorizontal();
  150. EditorGUILayout.BeginHorizontal();
  151. if (GUILayout.Button("Select Folder For Combined Material Assets"))
  152. {
  153. generate_AssetsFolder = EditorUtility.SaveFolderPanel("Create Combined Material Assets In Folder", "", "");
  154. generate_AssetsFolder = "Assets" + generate_AssetsFolder.Replace(Application.dataPath, "") + "/";
  155. }
  156. EditorGUILayout.LabelField("Folder: " + generate_AssetsFolder);
  157. EditorGUILayout.EndHorizontal();
  158. EditorGUILayout.BeginHorizontal();
  159. if (GUILayout.Button("List Shaders In Scene"))
  160. {
  161. EditorUtility.DisplayProgressBar("Analysing Scene", "", .05f);
  162. try
  163. {
  164. listMaterialsInScene();
  165. }
  166. catch (Exception ex)
  167. {
  168. Debug.LogError(ex.StackTrace);
  169. }
  170. finally
  171. {
  172. EditorUtility.ClearProgressBar();
  173. }
  174. }
  175. if (GUILayout.Button("Bake Every MeshBaker In Scene"))
  176. {
  177. try
  178. {
  179. MB3_TextureBaker[] texBakers = (MB3_TextureBaker[]) GameObject.FindObjectsOfType(typeof(MB3_TextureBaker));
  180. for (int i = 0; i < texBakers.Length; i++)
  181. {
  182. texBakers[i].CreateAtlases(updateProgressBar, true, new MB3_EditorMethods());
  183. }
  184. MB3_MeshBakerCommon[] mBakers = (MB3_MeshBakerCommon[]) GameObject.FindObjectsOfType(typeof(MB3_MeshBakerCommon));
  185. bool createTempMaterialBakeResult;
  186. for (int i = 0; i < mBakers.Length; i++)
  187. {
  188. if (mBakers[i].textureBakeResults != null)
  189. {
  190. MB3_MeshBakerEditorFunctions.BakeIntoCombined(mBakers[i], out createTempMaterialBakeResult);
  191. }
  192. }
  193. }
  194. catch (Exception e)
  195. {
  196. Debug.LogError(e);
  197. }
  198. finally
  199. {
  200. EditorUtility.ClearProgressBar();
  201. }
  202. }
  203. EditorGUILayout.EndHorizontal();
  204. if (sceneAnalysisResults.Count > 0)
  205. {
  206. float height = position.height - 150f;
  207. if (height < 500f) height = 500f;
  208. MB_EditorUtil.DrawSeparator();
  209. scrollPos2 = EditorGUILayout.BeginScrollView(scrollPos2, false, true); //(scrollPos2,, GUILayout.Width(position.width - 20f), GUILayout.Height(height));
  210. EditorGUILayout.LabelField("Shaders In Scene", EditorStyles.boldLabel);
  211. for (int i = 0; i < sceneAnalysisResults.Count; i++)
  212. {
  213. List<GameObjectFilterInfo> gows = sceneAnalysisResults[i];
  214. EditorGUILayout.BeginHorizontal();
  215. if (GUILayout.Button("Generate Baker", GUILayout.Width(200)))
  216. {
  217. createAndSetupBaker(gows, generate_AssetsFolder);
  218. }
  219. if (GUILayout.Button("Select", GUILayout.Width(200)))
  220. {
  221. UnityEngine.Object[] selected = new UnityEngine.Object[gows.Count];
  222. for (int j = 0; j < gows.Count; j++)
  223. {
  224. selected[j] = gows[j].go;
  225. }
  226. Selection.objects = selected;
  227. SceneView.lastActiveSceneView.FrameSelected();
  228. }
  229. string descr = String.Format("Objs={0} AtlasIndex={1} {2}", gows.Count, gows[0].atlasIndex, gows[0].GetDescription(filters, gows[0]));
  230. EditorGUILayout.LabelField(descr, EditorStyles.wordWrappedLabel);
  231. EditorGUILayout.EndHorizontal();
  232. sceneAnalysisResultsFoldouts[i] = EditorGUILayout.Foldout(sceneAnalysisResultsFoldouts[i], "");
  233. if (sceneAnalysisResultsFoldouts[i])
  234. {
  235. EditorGUI.indentLevel += 1;
  236. for (int j = 0; j < gows.Count; j++)
  237. {
  238. if (gows[j].go != null)
  239. {
  240. EditorGUILayout.LabelField(gows[j].go.name + " " + gows[j].GetDescription(filters, gows[j]));
  241. }
  242. }
  243. EditorGUI.indentLevel -= 1;
  244. }
  245. }
  246. EditorGUILayout.EndScrollView();
  247. MB_EditorUtil.DrawSeparator();
  248. }
  249. }
  250. int GetLODLevelForRenderer(Renderer r)
  251. {
  252. if (r != null)
  253. {
  254. LODGroup lodGroup = r.GetComponentInParent<LODGroup>();
  255. if (lodGroup != null)
  256. {
  257. LOD[] lods = lodGroup.GetLODs();
  258. for (int lodIdx = 0; lodIdx < lods.Length; lodIdx++)
  259. {
  260. Renderer[] rs = lods[lodIdx].renderers;
  261. for (int j = 0; j < rs.Length; j++)
  262. {
  263. if (rs[j] == r)
  264. {
  265. return lodIdx;
  266. }
  267. }
  268. }
  269. }
  270. }
  271. return 0;
  272. }
  273. void listMaterialsInScene()
  274. {
  275. if (!ValidateGroupByFields()) return;
  276. if (groupByOptionFilters == null)
  277. {
  278. populateGroupByFilters();
  279. }
  280. List<IGroupByFilter> gbfs = new List<IGroupByFilter>();
  281. for (int i = 0; i < groupByFilterIdxs.Length; i++)
  282. {
  283. if (groupByFilterIdxs[i] != 0)
  284. {
  285. gbfs.Add(groupByOptionFilters[groupByFilterIdxs[i]]);
  286. }
  287. }
  288. filters = gbfs.ToArray();
  289. //Get All Objects Already In a list of objects to be combined
  290. MB3_MeshBakerRoot[] allBakers = GameObject.FindObjectsOfType<MB3_MeshBakerRoot>();
  291. HashSet<GameObject> objectsAlreadyIncludedInBakers = new HashSet<GameObject>();
  292. for (int i = 0; i < allBakers.Length; i++)
  293. {
  294. List<GameObject> objsToCombine = allBakers[i].GetObjectsToCombine();
  295. for (int j = 0; j < objsToCombine.Count; j++)
  296. {
  297. if (objsToCombine[j] != null) objectsAlreadyIncludedInBakers.Add(objsToCombine[j]);
  298. }
  299. }
  300. //collect all renderers in scene
  301. List<GameObjectFilterInfo> gameObjects = new List<GameObjectFilterInfo>();
  302. Renderer[] rs = (Renderer[]) GameObject.FindObjectsOfType(typeof(Renderer));
  303. // Profile.StartProfile("listMaterialsInScene1");
  304. EditorUtility.DisplayProgressBar("Analysing Scene", "Collecting Renderers", .25f);
  305. for (int i = 0; i < rs.Length; i++)
  306. {
  307. Renderer r = rs[i];
  308. if (r is MeshRenderer || r is SkinnedMeshRenderer)
  309. {
  310. if (r.GetComponent<TextMesh>() != null)
  311. {
  312. continue; //don't add TextMeshes
  313. }
  314. GameObjectFilterInfo goaw = new GameObjectFilterInfo(r.gameObject, objectsAlreadyIncludedInBakers, filters);
  315. if (goaw.materials.Length > 0) //don't consider renderers with no materials
  316. {
  317. gameObjects.Add(goaw);
  318. EditorUtility.DisplayProgressBar("Analysing Scene", "Collecting Renderer For " + r.name, .1f);
  319. }
  320. }
  321. }
  322. //analyse meshes
  323. Dictionary<int, MB_Utility.MeshAnalysisResult> meshAnalysisResultCache = new Dictionary<int, MB_Utility.MeshAnalysisResult>();
  324. int totalVerts = 0;
  325. for (int i = 0; i < gameObjects.Count; i++)
  326. {
  327. string rpt = String.Format("Processing {0} [{1} of {2}]", gameObjects[i].go.name, i, gameObjects.Count);
  328. EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " A", .6f);
  329. Mesh mm = MB_Utility.GetMesh(gameObjects[i].go);
  330. int nVerts = 0;
  331. if (mm != null)
  332. {
  333. nVerts += mm.vertexCount;
  334. MB_Utility.MeshAnalysisResult mar;
  335. if (!meshAnalysisResultCache.TryGetValue(mm.GetInstanceID(), out mar))
  336. {
  337. EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Check Out Of Bounds UVs", .6f);
  338. MB_Utility.hasOutOfBoundsUVs(mm, ref mar);
  339. //Rect dummy = mar.uvRect;
  340. MB_Utility.doSubmeshesShareVertsOrTris(mm, ref mar);
  341. meshAnalysisResultCache.Add(mm.GetInstanceID(), mar);
  342. }
  343. if (mar.hasOutOfBoundsUVs)
  344. {
  345. int w = (int)mar.uvRect.width;
  346. int h = (int)mar.uvRect.height;
  347. gameObjects[i].outOfBoundsUVs = true;
  348. gameObjects[i].warning += " [WARNING: has uvs outside the range (0,1) tex is tiled " + w + "x" + h + " times]";
  349. }
  350. if (mar.hasOverlappingSubmeshVerts)
  351. {
  352. gameObjects[i].submeshesOverlap = true;
  353. gameObjects[i].warning += " [WARNING: Submeshes share verts or triangles. 'Multiple Combined Materials' feature may not work.]";
  354. }
  355. }
  356. totalVerts += nVerts;
  357. EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Validate OBuvs Multi Material", .6f);
  358. Renderer mr = gameObjects[i].go.GetComponent<Renderer>();
  359. if (!MB_Utility.AreAllSharedMaterialsDistinct(mr.sharedMaterials))
  360. {
  361. 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.]";
  362. }
  363. }
  364. List<GameObjectFilterInfo> objsNotAddedToBaker = new List<GameObjectFilterInfo>();
  365. Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap = sortIntoBakeGroups3(gameObjects, objsNotAddedToBaker, filters, splitAtlasesSoMeshesFit, atlasSize);
  366. sceneAnalysisResults = new List<List<GameObjectFilterInfo>>();
  367. foreach (GameObjectFilterInfo gow in gs2bakeGroupMap.Keys)
  368. {
  369. List<List<GameObjectFilterInfo>> gows = gs2bakeGroupMap[gow];
  370. for (int i = 0; i < gows.Count; i++) //if split atlases by what fits in atlas
  371. {
  372. sceneAnalysisResults.Add(gows[i]);
  373. }
  374. }
  375. sceneAnalysisResultsFoldouts = new bool[sceneAnalysisResults.Count];
  376. for (int i = 0; i < sceneAnalysisResults.Count; i++) { sceneAnalysisResultsFoldouts[i] = true; }
  377. if (writeReportFile)
  378. {
  379. string fileName = Application.dataPath + "/MeshBakerSceneAnalysisReport.txt";
  380. try
  381. {
  382. System.IO.File.WriteAllText(fileName, generateSceneAnalysisReport(gs2bakeGroupMap, objsNotAddedToBaker));
  383. Debug.Log(String.Format("Wrote scene analysis file to '{0}'. This file contains a list of all renderers and the materials/shaders that they use. It is designed to be opened with a spreadsheet.", fileName));
  384. }
  385. catch (Exception e)
  386. {
  387. e.GetHashCode(); //supress compiler warning
  388. Debug.Log("Failed to write file: " + fileName);
  389. }
  390. }
  391. }
  392. string generateSceneAnalysisReport(Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap, List<GameObjectFilterInfo> objsNotAddedToBaker)
  393. {
  394. string outStr = "(Click me, if I am too big copy and paste me into a spreadsheet or text editor)\n";// Materials in scene " + shader2GameObjects.Keys.Count + " and the objects that use them:\n";
  395. outStr += "\t\tOBJECT NAME\tLIGHTMAP INDEX\tSTATIC\tOVERLAPPING SUBMESHES\tOUT-OF-BOUNDS UVs\tNUM MATS\tMATERIAL\tWARNINGS\n";
  396. int totalVerts = 0;
  397. string outStr2 = "";
  398. foreach (List<List<GameObjectFilterInfo>> goss in gs2bakeGroupMap.Values)
  399. {
  400. for (int atlasIdx = 0; atlasIdx < goss.Count; atlasIdx++)
  401. {
  402. List<GameObjectFilterInfo> gos = goss[atlasIdx];
  403. outStr2 = "";
  404. totalVerts = 0;
  405. gos.Sort();
  406. for (int i = 0; i < gos.Count; i++)
  407. {
  408. totalVerts += gos[i].numVerts;
  409. string matStr = "";
  410. Renderer mr = gos[i].go.GetComponent<Renderer>();
  411. foreach (Material mmm in mr.sharedMaterials)
  412. {
  413. matStr += "[" + mmm + "] ";
  414. }
  415. outStr2 += "\t\t" + gos[i].go.name + " (" + gos[i].numVerts + " verts)\t" + gos[i].lightmapIndex + "\t" + gos[i].isStatic + "\t" + gos[i].submeshesOverlap + "\t" + gos[i].outOfBoundsUVs + "\t" + gos[i].numMaterials + "\t" + matStr + "\t" + gos[i].warning + "\n";
  416. }
  417. outStr2 = "\t" + gos[0].shaderName + " (" + totalVerts + " verts): \n" + outStr2;
  418. outStr += outStr2;
  419. }
  420. }
  421. if (objsNotAddedToBaker.Count > 0)
  422. {
  423. outStr += "Other objects\n";
  424. string shaderName = "";
  425. totalVerts = 0;
  426. List<GameObjectFilterInfo> gos1 = objsNotAddedToBaker;
  427. gos1.Sort();
  428. outStr2 = "";
  429. for (int i = 0; i < gos1.Count; i++)
  430. {
  431. if (!shaderName.Equals(objsNotAddedToBaker[i].shaderName))
  432. {
  433. outStr2 += "\t" + gos1[0].shaderName + "\n";
  434. shaderName = objsNotAddedToBaker[i].shaderName;
  435. }
  436. totalVerts += gos1[i].numVerts;
  437. string matStr = "";
  438. Renderer mr = gos1[i].go.GetComponent<Renderer>();
  439. foreach (Material mmm in mr.sharedMaterials)
  440. {
  441. matStr += "[" + mmm + "] ";
  442. }
  443. outStr2 += "\t\t" + gos1[i].go.name + " (" + gos1[i].numVerts + " verts)\t" + gos1[i].lightmapIndex + "\t" + gos1[i].isStatic + "\t" + gos1[i].submeshesOverlap + "\t" + gos1[i].outOfBoundsUVs + "\t" + gos1[i].numMaterials + "\t" + matStr + "\t" + gos1[i].warning + "\n";
  444. }
  445. outStr += outStr2;
  446. }
  447. return outStr;
  448. }
  449. bool MaterialsAreTheSame(GameObjectFilterInfo a, GameObjectFilterInfo b)
  450. {
  451. HashSet<Material> aMats = new HashSet<Material>();
  452. for (int i = 0; i < a.materials.Length; i++) aMats.Add(a.materials[i]);
  453. HashSet<Material> bMats = new HashSet<Material>();
  454. for (int i = 0; i < b.materials.Length; i++) bMats.Add(b.materials[i]);
  455. return aMats.SetEquals(bMats);
  456. }
  457. bool ShadersAreTheSame(GameObjectFilterInfo a, GameObjectFilterInfo b)
  458. {
  459. HashSet<Shader> aMats = new HashSet<Shader>();
  460. for (int i = 0; i < a.shaders.Length; i++) aMats.Add(a.shaders[i]);
  461. HashSet<Shader> bMats = new HashSet<Shader>();
  462. for (int i = 0; i < b.shaders.Length; i++) bMats.Add(b.shaders[i]);
  463. return aMats.SetEquals(bMats);
  464. }
  465. public static Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> sortIntoBakeGroups3(List<GameObjectFilterInfo> gameObjects, List<GameObjectFilterInfo> objsNotAddedToBaker, IGroupByFilter[] filters, bool splitAtlasesSoMeshesFit, int atlasSize)
  466. {
  467. Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap = new Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>>();
  468. List<GameObjectFilterInfo> gos = gameObjects;
  469. if (gos.Count < 1) return gs2bakeGroupMap;
  470. gos.Sort();
  471. List<List<GameObjectFilterInfo>> l = null;
  472. GameObjectFilterInfo key = gos[0];
  473. for (int i = 0; i < gos.Count; i++)
  474. {
  475. GameObjectFilterInfo goaw = gos[i];
  476. //compare with key and decide if we need a new list
  477. for (int j = 0; j < filters.Length; j++)
  478. {
  479. if (filters[j] != null && filters[j].Compare(key, goaw) != 0) l = null;
  480. }
  481. if (l == null)
  482. {
  483. l = new List<List<GameObjectFilterInfo>>();
  484. l.Add(new List<GameObjectFilterInfo>());
  485. gs2bakeGroupMap.Add(gos[i], l);
  486. key = gos[i];
  487. }
  488. l[0].Add(gos[i]);
  489. }
  490. //now that objects have been grouped by the sort criteria we can see how many atlases are needed
  491. Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap2 = new Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>>();
  492. if (splitAtlasesSoMeshesFit)
  493. {
  494. foreach (GameObjectFilterInfo k in gs2bakeGroupMap.Keys)
  495. {
  496. List<GameObjectFilterInfo> vs = gs2bakeGroupMap[k][0];
  497. List<GameObject> objsInGroup = new List<GameObject>();
  498. for (int i = 0; i < vs.Count; i++)
  499. {
  500. objsInGroup.Add(vs[i].go);
  501. }
  502. MB3_TextureCombiner tc = new MB3_TextureCombiner();
  503. tc.maxAtlasSize = atlasSize;
  504. tc.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker;
  505. tc.LOG_LEVEL = MB2_LogLevel.warn;
  506. List<AtlasPackingResult> packingResults = new List<AtlasPackingResult>();
  507. Material tempResMat = k.materials[0]; //we don't write to the materials so can use this as the result material
  508. if (tc.CombineTexturesIntoAtlases(null, null, tempResMat, objsInGroup, null, null, packingResults,
  509. onlyPackRects:true, splitAtlasWhenPackingIfTooBig:false))
  510. {
  511. List<List<GameObjectFilterInfo>> atlasGroups = new List<List<GameObjectFilterInfo>>();
  512. for (int i = 0; i < packingResults.Count; i++)
  513. {
  514. List<GameObjectFilterInfo> ngos = new List<GameObjectFilterInfo>();
  515. List<MB_MaterialAndUVRect> matsData = (List<MB_MaterialAndUVRect>)packingResults[i].data;
  516. for (int j = 0; j < matsData.Count; j++)
  517. {
  518. for (int kk = 0; kk < matsData[j].objectsThatUse.Count; kk++)
  519. {
  520. GameObjectFilterInfo gofi = vs.Find(x => x.go == matsData[j].objectsThatUse[kk]);
  521. //Debug.Assert(gofi != null);
  522. ngos.Add(gofi);
  523. }
  524. }
  525. ngos[0].atlasIndex = (short)i;
  526. atlasGroups.Add(ngos);
  527. }
  528. gs2bakeGroupMap2.Add(k, atlasGroups);
  529. }
  530. else
  531. {
  532. gs2bakeGroupMap2.Add(k, gs2bakeGroupMap[k]);
  533. }
  534. }
  535. }
  536. else
  537. {
  538. gs2bakeGroupMap2 = gs2bakeGroupMap;
  539. }
  540. return gs2bakeGroupMap2;
  541. }
  542. void createBakers(Dictionary<GameObjectFilterInfo, List<GameObjectFilterInfo>> gs2bakeGroupMap, List<GameObjectFilterInfo> objsNotAddedToBaker)
  543. {
  544. string s = "";
  545. int numBakers = 0;
  546. int numObjsAdded = 0;
  547. if (generate_AssetsFolder == null || generate_AssetsFolder == "")
  548. {
  549. Debug.LogError("Need to choose a folder for saving the combined material assets.");
  550. return;
  551. }
  552. List<GameObjectFilterInfo> singletonObjsNotAddedToBaker = new List<GameObjectFilterInfo>();
  553. foreach (List<GameObjectFilterInfo> gaw in gs2bakeGroupMap.Values)
  554. {
  555. if (gaw.Count > 1)
  556. {
  557. numBakers++;
  558. numObjsAdded += gaw.Count;
  559. createAndSetupBaker(gaw, generate_AssetsFolder);
  560. s += " Created meshbaker for shader=" + gaw[0].shaderName + " lightmap=" + gaw[0].lightmapIndex + " OBuvs=" + gaw[0].outOfBoundsUVs + "\n";
  561. }
  562. else
  563. {
  564. singletonObjsNotAddedToBaker.Add(gaw[0]);
  565. }
  566. }
  567. s = "Created " + numBakers + " bakers. Added " + numObjsAdded + " objects\n" + s;
  568. Debug.Log(s);
  569. s = "Objects not added=" + objsNotAddedToBaker.Count + " objects that have unique material=" + singletonObjsNotAddedToBaker.Count + "\n";
  570. for (int i = 0; i < objsNotAddedToBaker.Count; i++)
  571. {
  572. s += " " + objsNotAddedToBaker[i].go.name +
  573. " isStatic=" + objsNotAddedToBaker[i].isStatic +
  574. " submeshesOverlap" + objsNotAddedToBaker[i].submeshesOverlap +
  575. " numMats=" + objsNotAddedToBaker[i].numMaterials + "\n";
  576. }
  577. for (int i = 0; i < singletonObjsNotAddedToBaker.Count; i++)
  578. {
  579. s += " " + singletonObjsNotAddedToBaker[i].go.name + " single\n";
  580. }
  581. Debug.Log(s);
  582. }
  583. void createAndSetupBaker(List<GameObjectFilterInfo> gaws, string pthRoot)
  584. {
  585. for (int i = gaws.Count - 1; i >= 0; i--)
  586. {
  587. if (gaws[i].go == null) gaws.RemoveAt(i);
  588. }
  589. if (gaws.Count < 1)
  590. {
  591. Debug.LogError("No game objects.");
  592. return;
  593. }
  594. if (pthRoot == null || pthRoot == "")
  595. {
  596. Debug.LogError("Folder for saving created assets was not set.");
  597. return;
  598. }
  599. int numVerts = 0;
  600. for (int i = 0; i < gaws.Count; i++)
  601. {
  602. if (gaws[i].go != null)
  603. {
  604. numVerts = gaws[i].numVerts;
  605. }
  606. }
  607. GameObject newMeshBaker = null;
  608. if (numVerts >= 65535)
  609. {
  610. newMeshBaker = MB3_MultiMeshBakerEditor.CreateNewMeshBaker();
  611. }
  612. else
  613. {
  614. newMeshBaker = MB3_MeshBakerEditor.CreateNewMeshBaker();
  615. }
  616. newMeshBaker.name = ("MeshBaker-" + gaws[0].shaderName + "-LM" + gaws[0].lightmapIndex).ToString().Replace("/", "-");
  617. MB3_TextureBaker tb = newMeshBaker.GetComponent<MB3_TextureBaker>();
  618. MB3_MeshBakerCommon mb = tb.GetComponentInChildren<MB3_MeshBakerCommon>();
  619. tb.GetObjectsToCombine().Clear();
  620. for (int i = 0; i < gaws.Count; i++)
  621. {
  622. if (gaws[i].go != null && !tb.GetObjectsToCombine().Contains(gaws[i].go))
  623. {
  624. tb.GetObjectsToCombine().Add(gaws[i].go);
  625. }
  626. }
  627. if (splitAtlasesSoMeshesFit)
  628. {
  629. tb.maxAtlasSize = atlasSize;
  630. }
  631. if (gaws[0].numMaterials > 1)
  632. {
  633. string pthMat = AssetDatabase.GenerateUniqueAssetPath(pthRoot + newMeshBaker.name + ".asset");
  634. MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(tb, pthMat);
  635. tb.doMultiMaterial = true;
  636. SerializedObject tbr = new SerializedObject(tb);
  637. SerializedProperty resultMaterials = tbr.FindProperty("resultMaterials");
  638. MB_TextureBakerEditorConfigureMultiMaterials.ConfigureMutiMaterialsFromObjsToCombine2(tb, resultMaterials, tbr);
  639. }
  640. else
  641. {
  642. string pthMat = AssetDatabase.GenerateUniqueAssetPath(pthRoot + newMeshBaker.name + ".asset");
  643. MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(tb, pthMat);
  644. }
  645. if (gaws[0].isMeshRenderer)
  646. {
  647. mb.meshCombiner.settings.renderType = MB_RenderType.meshRenderer;
  648. }
  649. else
  650. {
  651. mb.meshCombiner.settings.renderType = MB_RenderType.skinnedMeshRenderer;
  652. }
  653. }
  654. void bakeAllBakersInScene()
  655. {
  656. MB3_MeshBakerRoot[] bakers = (MB3_MeshBakerRoot[]) GameObject.FindObjectsOfType(typeof(MB3_MeshBakerRoot));
  657. for (int i = 0; i < bakers.Length; i++)
  658. {
  659. if (bakers[i] is MB3_TextureBaker)
  660. {
  661. MB3_TextureBaker tb = (MB3_TextureBaker)bakers[i];
  662. tb.CreateAtlases(updateProgressBar, true, new MB3_EditorMethods());
  663. }
  664. }
  665. EditorUtility.ClearProgressBar();
  666. }
  667. public void updateProgressBar(string msg, float progress)
  668. {
  669. EditorUtility.DisplayProgressBar("Combining Meshes", msg, progress);
  670. }
  671. bool ValidateGroupByFields()
  672. {
  673. bool foundNone = false;
  674. for (int i = 0; i < groupByFilterIdxs.Length; i++)
  675. {
  676. if (groupByFilterIdxs[i] == 0) foundNone = true; //zero is the none selection
  677. if (foundNone && groupByFilterIdxs[i] != 0)
  678. {
  679. Debug.LogError("All non-none values must be at the top of the group by list");
  680. return false;
  681. }
  682. }
  683. for (int i = 0; i < groupByFilterIdxs.Length; i++)
  684. {
  685. for (int j = i + 1; j < groupByFilterIdxs.Length; j++)
  686. {
  687. if (groupByFilterIdxs[i] == groupByFilterIdxs[j] && groupByFilterIdxs[i] != 0)
  688. {
  689. Debug.LogError("Two of the group by options are the same.");
  690. return false;
  691. }
  692. }
  693. }
  694. return true;
  695. }
  696. }
  697. }