MB3_MeshBakerGrouper.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. using UnityEngine;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using DigitalOpus.MB.Core;
  6. #if UNITY_EDITOR
  7. using UnityEditor;
  8. #endif
  9. public class MB3_MeshBakerGrouper : MonoBehaviour, MB_IMeshBakerSettingsHolder
  10. {
  11. public enum ClusterType
  12. {
  13. none,
  14. grid,
  15. pie,
  16. agglomerative,
  17. }
  18. public MB3_MeshBakerGrouperCore grouper;
  19. public ClusterType clusterType = ClusterType.none;
  20. public GrouperData data = new GrouperData();
  21. //these are for getting a resonable bounds in which to draw gizmos.
  22. [HideInInspector] public Bounds sourceObjectBounds = new Bounds(Vector3.zero, Vector3.one);
  23. public MB3_MeshCombinerSettings meshBakerSettingsAsset;
  24. public MB3_MeshCombinerSettingsData meshBakerSettings;
  25. public MB_IMeshBakerSettings GetMeshBakerSettings()
  26. {
  27. if (meshBakerSettingsAsset == null)
  28. {
  29. if (meshBakerSettings == null) meshBakerSettings = new MB3_MeshCombinerSettingsData();
  30. return meshBakerSettings;
  31. }
  32. else
  33. {
  34. return meshBakerSettingsAsset.GetMeshBakerSettings();
  35. }
  36. }
  37. public void GetMeshBakerSettingsAsSerializedProperty(out string propertyName, out UnityEngine.Object targetObj)
  38. {
  39. if (meshBakerSettingsAsset == null)
  40. {
  41. targetObj = this;
  42. propertyName = "meshBakerSettings";
  43. }
  44. else
  45. {
  46. targetObj = meshBakerSettingsAsset;
  47. propertyName = "data";
  48. }
  49. }
  50. void OnDrawGizmosSelected()
  51. {
  52. if (grouper == null)
  53. {
  54. grouper = CreateGrouper(clusterType, data);
  55. }
  56. if (grouper.d == null)
  57. {
  58. grouper.d = data;
  59. }
  60. grouper.DrawGizmos(sourceObjectBounds);
  61. }
  62. public MB3_MeshBakerGrouperCore CreateGrouper(ClusterType t, GrouperData data)
  63. {
  64. if (t == ClusterType.grid) grouper = new MB3_MeshBakerGrouperGrid(data);
  65. if (t == ClusterType.pie) grouper = new MB3_MeshBakerGrouperPie(data);
  66. if (t == ClusterType.agglomerative)
  67. {
  68. MB3_TextureBaker tb = GetComponent<MB3_TextureBaker>();
  69. List<GameObject> gos;
  70. if (tb != null)
  71. {
  72. gos = tb.GetObjectsToCombine();
  73. }
  74. else
  75. {
  76. gos = new List<GameObject>();
  77. }
  78. grouper = new MB3_MeshBakerGrouperCluster(data, gos);
  79. }
  80. if (t == ClusterType.none) grouper = new MB3_MeshBakerGrouperNone(data);
  81. return grouper;
  82. }
  83. public void DeleteAllChildMeshBakers()
  84. {
  85. MB3_MeshBakerCommon[] mBakers = GetComponentsInChildren<MB3_MeshBakerCommon>();
  86. for (int i = 0; i < mBakers.Length; i++)
  87. {
  88. MB3_MeshBakerCommon mb = mBakers[i];
  89. GameObject resultGameObject = mb.meshCombiner.resultSceneObject;
  90. MB_Utility.Destroy(resultGameObject);
  91. MB_Utility.Destroy(mb.gameObject);
  92. }
  93. }
  94. }
  95. namespace DigitalOpus.MB.Core
  96. {
  97. /// all properties go here so that settings are remembered as user switches between cluster types
  98. [Serializable]
  99. public class GrouperData
  100. {
  101. public bool clusterOnLMIndex;
  102. public bool clusterByLODLevel;
  103. public Vector3 origin;
  104. //Normally these properties would be in the subclasses but putting them here makes writing the inspector much easier
  105. //for grid
  106. public Vector3 cellSize;
  107. //for pie
  108. public int pieNumSegments = 4;
  109. public Vector3 pieAxis = Vector3.up;
  110. public float ringSpacing = 100f;
  111. public bool combineSegmentsInInnermostRing = false;
  112. //for clustering
  113. public int height = 1;
  114. public float maxDistBetweenClusters = 1f;
  115. public bool includeCellsWithOnlyOneRenderer = true;
  116. }
  117. [Serializable]
  118. public abstract class MB3_MeshBakerGrouperCore
  119. {
  120. public GrouperData d;
  121. public abstract Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection);
  122. public abstract void DrawGizmos(Bounds sourceObjectBounds);
  123. public void DoClustering(MB3_TextureBaker tb, MB3_MeshBakerGrouper grouper)
  124. {
  125. //todo warn for no objects and no Texture Bake Result
  126. Dictionary<string, List<Renderer>> cell2objs = FilterIntoGroups(tb.GetObjectsToCombine());
  127. if (d.clusterOnLMIndex)
  128. {
  129. Dictionary<string, List<Renderer>> cell2objsNew = new Dictionary<string, List<Renderer>>();
  130. foreach (string key in cell2objs.Keys)
  131. {
  132. List<Renderer> gaws = cell2objs[key];
  133. Dictionary<int, List<Renderer>> idx2objs = GroupByLightmapIndex(gaws);
  134. foreach (int keyIdx in idx2objs.Keys)
  135. {
  136. string keyNew = key + "-LM-" + keyIdx;
  137. cell2objsNew.Add(keyNew, idx2objs[keyIdx]);
  138. }
  139. }
  140. cell2objs = cell2objsNew;
  141. }
  142. if (d.clusterByLODLevel)
  143. {
  144. //visit each cell
  145. //visit each renderer
  146. //check if that renderer is a child of an LOD group
  147. // visit each LOD level check if this renderer is in that list.
  148. // if not add it to LOD0 for that cell
  149. // otherwise add it to LODX for that cell creating LODs as necessary
  150. Dictionary<string, List<Renderer>> cell2objsNew = new Dictionary<string, List<Renderer>>();
  151. foreach (string key in cell2objs.Keys)
  152. {
  153. List<Renderer> gaws = cell2objs[key];
  154. foreach (Renderer r in gaws)
  155. {
  156. if (r == null) continue;
  157. bool foundInLOD = false;
  158. LODGroup lodg = r.GetComponentInParent<LODGroup>();
  159. if (lodg != null)
  160. {
  161. LOD[] lods = lodg.GetLODs();
  162. for (int i = 0; i < lods.Length; i++)
  163. {
  164. LOD lod = lods[i];
  165. if (Array.Find<Renderer>(lod.renderers, x => x == r) != null)
  166. {
  167. foundInLOD = true;
  168. List<Renderer> rs;
  169. string newKey = String.Format("{0}_LOD{1}", key, i);
  170. if (!cell2objsNew.TryGetValue(newKey, out rs))
  171. {
  172. rs = new List<Renderer>();
  173. cell2objsNew.Add(newKey, rs);
  174. }
  175. if (!rs.Contains(r)) rs.Add(r);
  176. }
  177. }
  178. }
  179. if (!foundInLOD)
  180. {
  181. List<Renderer> rs;
  182. string newKey = String.Format("{0}_LOD0", key);
  183. if (!cell2objsNew.TryGetValue(newKey, out rs))
  184. {
  185. rs = new List<Renderer>();
  186. cell2objsNew.Add(newKey, rs);
  187. }
  188. if (!rs.Contains(r)) rs.Add(r);
  189. }
  190. }
  191. }
  192. cell2objs = cell2objsNew;
  193. }
  194. int clustersWithOnlyOneRenderer = 0;
  195. foreach (string key in cell2objs.Keys)
  196. {
  197. List<Renderer> gaws = cell2objs[key];
  198. if (gaws.Count > 1 || grouper.data.includeCellsWithOnlyOneRenderer)
  199. {
  200. AddMeshBaker(grouper, tb, key, gaws);
  201. }
  202. else
  203. {
  204. clustersWithOnlyOneRenderer++;
  205. }
  206. }
  207. Debug.Log(String.Format("Found {0} cells with Renderers. Not creating bakers for {1} because there is only one mesh in the cell. Creating {2} bakers.", cell2objs.Count, clustersWithOnlyOneRenderer, cell2objs.Count - clustersWithOnlyOneRenderer));
  208. }
  209. Dictionary<int, List<Renderer>> GroupByLightmapIndex(List<Renderer> gaws)
  210. {
  211. Dictionary<int, List<Renderer>> idx2objs = new Dictionary<int, List<Renderer>>();
  212. for (int i = 0; i < gaws.Count; i++)
  213. {
  214. List<Renderer> objs = null;
  215. if (idx2objs.ContainsKey(gaws[i].lightmapIndex))
  216. {
  217. objs = idx2objs[gaws[i].lightmapIndex];
  218. }
  219. else
  220. {
  221. objs = new List<Renderer>();
  222. idx2objs.Add(gaws[i].lightmapIndex, objs);
  223. }
  224. objs.Add(gaws[i]);
  225. }
  226. return idx2objs;
  227. }
  228. void AddMeshBaker(MB3_MeshBakerGrouper grouper, MB3_TextureBaker tb, string key, List<Renderer> gaws)
  229. {
  230. int numVerts = 0;
  231. for (int i = 0; i < gaws.Count; i++)
  232. {
  233. Mesh m = MB_Utility.GetMesh(gaws[i].gameObject);
  234. if (m != null)
  235. numVerts += m.vertexCount;
  236. }
  237. GameObject nmb = new GameObject("MeshBaker-" + key);
  238. nmb.transform.position = Vector3.zero;
  239. MB3_MeshBakerCommon newMeshBaker;
  240. if (numVerts >= 65535)
  241. {
  242. newMeshBaker = nmb.AddComponent<MB3_MultiMeshBaker>();
  243. newMeshBaker.useObjsToMeshFromTexBaker = false;
  244. }
  245. else
  246. {
  247. newMeshBaker = nmb.AddComponent<MB3_MeshBaker>();
  248. newMeshBaker.useObjsToMeshFromTexBaker = false;
  249. }
  250. newMeshBaker.textureBakeResults = tb.textureBakeResults;
  251. newMeshBaker.transform.parent = tb.transform;
  252. newMeshBaker.meshCombiner.settingsHolder = grouper;
  253. for (int i = 0; i < gaws.Count; i++)
  254. {
  255. newMeshBaker.GetObjectsToCombine().Add(gaws[i].gameObject);
  256. }
  257. }
  258. }
  259. [Serializable]
  260. public class MB3_MeshBakerGrouperNone : MB3_MeshBakerGrouperCore
  261. {
  262. public MB3_MeshBakerGrouperNone(GrouperData d)
  263. {
  264. this.d = d;
  265. }
  266. public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
  267. {
  268. Debug.Log("Filtering into groups none");
  269. Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
  270. List<Renderer> rs = new List<Renderer>();
  271. for (int i = 0; i < selection.Count; i++)
  272. {
  273. if (selection[i] != null)
  274. {
  275. rs.Add(selection[i].GetComponent<Renderer>());
  276. }
  277. }
  278. cell2objs.Add("MeshBaker", rs);
  279. return cell2objs;
  280. }
  281. public override void DrawGizmos(Bounds sourceObjectBounds)
  282. {
  283. }
  284. }
  285. [Serializable]
  286. public class MB3_MeshBakerGrouperGrid : MB3_MeshBakerGrouperCore
  287. {
  288. public MB3_MeshBakerGrouperGrid(GrouperData d)
  289. {
  290. this.d = d;
  291. }
  292. public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
  293. {
  294. Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
  295. if (d.cellSize.x <= 0f || d.cellSize.y <= 0f || d.cellSize.z <= 0f)
  296. {
  297. Debug.LogError("cellSize x,y,z must all be greater than zero.");
  298. return cell2objs;
  299. }
  300. Debug.Log("Collecting renderers in each cell");
  301. foreach (GameObject t in selection)
  302. {
  303. if (t == null)
  304. {
  305. continue;
  306. }
  307. GameObject go = t;
  308. Renderer mr = go.GetComponent<Renderer>();
  309. if (mr is MeshRenderer || mr is SkinnedMeshRenderer)
  310. {
  311. //get the cell this gameObject is in
  312. Vector3 gridVector = mr.bounds.center;
  313. gridVector.x = Mathf.Floor((gridVector.x - d.origin.x) / d.cellSize.x) * d.cellSize.x;
  314. gridVector.y = Mathf.Floor((gridVector.y - d.origin.y) / d.cellSize.y) * d.cellSize.y;
  315. gridVector.z = Mathf.Floor((gridVector.z - d.origin.z) / d.cellSize.z) * d.cellSize.z;
  316. List<Renderer> objs = null;
  317. string gridVectorStr = gridVector.ToString();
  318. if (cell2objs.ContainsKey(gridVectorStr))
  319. {
  320. objs = cell2objs[gridVectorStr];
  321. }
  322. else
  323. {
  324. objs = new List<Renderer>();
  325. cell2objs.Add(gridVectorStr, objs);
  326. }
  327. if (!objs.Contains(mr))
  328. {
  329. //Debug.Log("Adding " + mr + " todo " + gridVectorStr);
  330. objs.Add(mr);
  331. }
  332. }
  333. }
  334. return cell2objs;
  335. }
  336. public override void DrawGizmos(Bounds sourceObjectBounds)
  337. {
  338. Vector3 cs = d.cellSize;
  339. if (cs.x <= .00001f || cs.y <= .00001f || cs.z <= .00001f) return;
  340. Vector3 p = sourceObjectBounds.center - sourceObjectBounds.extents;
  341. Vector3 offset = d.origin;
  342. offset.x = offset.x % cs.x;
  343. offset.y = offset.y % cs.y;
  344. offset.z = offset.z % cs.z;
  345. //snap p to closest cell center
  346. Vector3 start;
  347. p.x = Mathf.Round((p.x) / cs.x) * cs.x + offset.x;
  348. p.y = Mathf.Round((p.y) / cs.y) * cs.y + offset.y;
  349. p.z = Mathf.Round((p.z) / cs.z) * cs.z + offset.z;
  350. if (p.x > sourceObjectBounds.center.x - sourceObjectBounds.extents.x) p.x = p.x - cs.x;
  351. if (p.y > sourceObjectBounds.center.y - sourceObjectBounds.extents.y) p.y = p.y - cs.y;
  352. if (p.z > sourceObjectBounds.center.z - sourceObjectBounds.extents.z) p.z = p.z - cs.z;
  353. start = p;
  354. int numcells = Mathf.CeilToInt(sourceObjectBounds.size.x / cs.x + sourceObjectBounds.size.y / cs.y + sourceObjectBounds.size.z / cs.z);
  355. if (numcells > 200)
  356. {
  357. Gizmos.DrawWireCube(d.origin + cs / 2f, cs);
  358. }
  359. else
  360. {
  361. for (; p.x < sourceObjectBounds.center.x + sourceObjectBounds.extents.x; p.x += cs.x)
  362. {
  363. p.y = start.y;
  364. for (; p.y < sourceObjectBounds.center.y + sourceObjectBounds.extents.y; p.y += cs.y)
  365. {
  366. p.z = start.z;
  367. for (; p.z < sourceObjectBounds.center.z + sourceObjectBounds.extents.z; p.z += cs.z)
  368. {
  369. Gizmos.DrawWireCube(p + cs / 2f, cs);
  370. }
  371. }
  372. }
  373. }
  374. }
  375. }
  376. [Serializable]
  377. public class MB3_MeshBakerGrouperPie : MB3_MeshBakerGrouperCore
  378. {
  379. public MB3_MeshBakerGrouperPie(GrouperData data)
  380. {
  381. d = data;
  382. }
  383. public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
  384. {
  385. Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
  386. if (d.pieNumSegments == 0)
  387. {
  388. Debug.LogError("pieNumSegments must be greater than zero.");
  389. return cell2objs;
  390. }
  391. if (d.pieAxis.magnitude <= .000001f)
  392. {
  393. Debug.LogError("Pie axis vector is too short.");
  394. return cell2objs;
  395. }
  396. if (d.ringSpacing <= .000001f)
  397. {
  398. Debug.LogError("Ring spacing is too small.");
  399. return cell2objs;
  400. }
  401. d.pieAxis.Normalize();
  402. Quaternion pieAxis2yIsUp = Quaternion.FromToRotation(d.pieAxis, Vector3.up);
  403. Debug.Log("Collecting renderers in each cell");
  404. foreach (GameObject t in selection)
  405. {
  406. if (t == null)
  407. {
  408. continue;
  409. }
  410. GameObject go = t;
  411. Renderer mr = go.GetComponent<Renderer>();
  412. if (mr is MeshRenderer || mr is SkinnedMeshRenderer)
  413. {
  414. //get the cell this gameObject is in
  415. Vector3 origin2obj = mr.bounds.center - d.origin;
  416. origin2obj = pieAxis2yIsUp * origin2obj;
  417. Vector2 origin2Obj2D = new Vector2(origin2obj.x, origin2obj.z);
  418. float radius = origin2Obj2D.magnitude;
  419. origin2obj.Normalize();
  420. float deg_aboutY = 0f;
  421. if (Mathf.Abs(origin2obj.x) < 10e-5f && Mathf.Abs(origin2obj.z) < 10e-5f)
  422. {
  423. deg_aboutY = 0f;
  424. }
  425. else
  426. {
  427. deg_aboutY = Mathf.Atan2(origin2obj.x, origin2obj.z) * Mathf.Rad2Deg;
  428. if (deg_aboutY < 0f) deg_aboutY = 360f + deg_aboutY;
  429. }
  430. // Debug.Log ("Obj " + mr + " angle " + d_aboutY);
  431. int segment = Mathf.FloorToInt(deg_aboutY / 360f * d.pieNumSegments);
  432. int ring = Mathf.FloorToInt(radius / d.ringSpacing);
  433. if (ring == 0 && d.combineSegmentsInInnermostRing)
  434. {
  435. segment = 0;
  436. }
  437. List<Renderer> objs = null;
  438. string segStr = "seg_" + segment + "_ring_" + ring;
  439. if (cell2objs.ContainsKey(segStr))
  440. {
  441. objs = cell2objs[segStr];
  442. }
  443. else
  444. {
  445. objs = new List<Renderer>();
  446. cell2objs.Add(segStr, objs);
  447. }
  448. if (!objs.Contains(mr))
  449. {
  450. objs.Add(mr);
  451. }
  452. }
  453. }
  454. return cell2objs;
  455. }
  456. public override void DrawGizmos(Bounds sourceObjectBounds)
  457. {
  458. if (d.pieAxis.magnitude < .1f) return;
  459. if (d.pieNumSegments < 1) return;
  460. float rad = sourceObjectBounds.extents.magnitude;
  461. int numRings = Mathf.CeilToInt(rad / d.ringSpacing);
  462. numRings = Mathf.Max(1, numRings);
  463. for (int i = 0; i < numRings; i++)
  464. {
  465. DrawCircle(d.pieAxis.normalized, d.origin, d.ringSpacing * (i + 1), 24);
  466. }
  467. Gizmos.color = Color.white;
  468. Quaternion yIsUp2PieAxis = Quaternion.FromToRotation(Vector3.up, d.pieAxis);
  469. Quaternion rStep = Quaternion.AngleAxis(180f / d.pieNumSegments, Vector3.up);
  470. Vector3 r = Vector3.forward;
  471. for (int i = 0; i < d.pieNumSegments; i++)
  472. {
  473. Vector3 rr = yIsUp2PieAxis * r;
  474. Vector3 origin = d.origin;
  475. int nr = numRings;
  476. if (d.combineSegmentsInInnermostRing)
  477. {
  478. origin = d.origin + rr.normalized * d.ringSpacing;
  479. nr = numRings - 1;
  480. }
  481. if (nr == 0) break;
  482. Gizmos.DrawLine(origin, origin + nr * d.ringSpacing * rr.normalized);
  483. r = rStep * r;
  484. r = rStep * r;
  485. }
  486. }
  487. static int MaxIndexInVector3(Vector3 v)
  488. {
  489. int idx = 0;
  490. float val = v.x;
  491. if (v.y > val)
  492. {
  493. idx = 1;
  494. val = v.y;
  495. }
  496. if (v.z > val)
  497. {
  498. idx = 2;
  499. val = v.z;
  500. }
  501. return idx;
  502. }
  503. public static void DrawCircle(Vector3 axis, Vector3 center, float radius, int subdiv)
  504. {
  505. Quaternion q = Quaternion.AngleAxis(360 / subdiv, axis);
  506. int maxIdx = MaxIndexInVector3(axis);
  507. int otherIdx = maxIdx == 0 ? maxIdx + 1 : maxIdx - 1;
  508. Vector3 r = axis; //r construct a vector perpendicular to axis
  509. float temp = r[maxIdx];
  510. r[maxIdx] = r[otherIdx];
  511. r[otherIdx] = -temp;
  512. r = Vector3.ProjectOnPlane(r, axis);
  513. r.Normalize();
  514. r *= radius;
  515. for (int i = 0; i < subdiv + 1; i++)
  516. {
  517. Vector3 r2 = q * r;
  518. Gizmos.color = Color.white;
  519. Gizmos.DrawLine(center + r, center + r2);
  520. r = r2;
  521. }
  522. }
  523. }
  524. [Serializable]
  525. public class MB3_MeshBakerGrouperKMeans : MB3_MeshBakerGrouperCore
  526. {
  527. public int numClusters = 4;
  528. public Vector3[] clusterCenters = new Vector3[0];
  529. public float[] clusterSizes = new float[0];
  530. public MB3_MeshBakerGrouperKMeans(GrouperData data)
  531. {
  532. d = data;
  533. }
  534. public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
  535. {
  536. Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
  537. List<GameObject> validObjs = new List<GameObject>();
  538. int numClusters = 20;
  539. foreach (GameObject t in selection)
  540. {
  541. if (t == null)
  542. {
  543. continue;
  544. }
  545. GameObject go = t;
  546. Renderer mr = go.GetComponent<Renderer>();
  547. if (mr is MeshRenderer || mr is SkinnedMeshRenderer)
  548. {
  549. //get the cell this gameObject is in
  550. validObjs.Add(go);
  551. }
  552. }
  553. if (validObjs.Count > 0 && numClusters > 0 && numClusters < validObjs.Count)
  554. {
  555. MB3_KMeansClustering kmc = new MB3_KMeansClustering(validObjs, numClusters);
  556. kmc.Cluster();
  557. clusterCenters = new Vector3[numClusters];
  558. clusterSizes = new float[numClusters];
  559. for (int i = 0; i < numClusters; i++)
  560. {
  561. List<Renderer> lr = kmc.GetCluster(i, out clusterCenters[i], out clusterSizes[i]);
  562. if (lr.Count > 0)
  563. {
  564. cell2objs.Add("Cluster_" + i, lr);
  565. }
  566. }
  567. }
  568. else
  569. {
  570. //todo error messages
  571. }
  572. return cell2objs;
  573. }
  574. public override void DrawGizmos(Bounds sceneObjectBounds)
  575. {
  576. if (clusterCenters != null && clusterSizes != null && clusterCenters.Length == clusterSizes.Length)
  577. {
  578. for (int i = 0; i < clusterSizes.Length; i++)
  579. {
  580. Gizmos.DrawWireSphere(clusterCenters[i], clusterSizes[i]);
  581. }
  582. }
  583. }
  584. }
  585. [Serializable]
  586. public class MB3_MeshBakerGrouperCluster : MB3_MeshBakerGrouperCore
  587. {
  588. public MB3_AgglomerativeClustering cluster;
  589. float _lastMaxDistBetweenClusters;
  590. public float _ObjsExtents = 10f;
  591. public float _minDistBetweenClusters = .001f;
  592. List<MB3_AgglomerativeClustering.ClusterNode> _clustersToDraw = new List<MB3_AgglomerativeClustering.ClusterNode>();
  593. float[] _radii;
  594. public MB3_MeshBakerGrouperCluster(GrouperData data, List<GameObject> gos)
  595. {
  596. d = data;
  597. }
  598. public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
  599. {
  600. Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
  601. for (int i = 0; i < _clustersToDraw.Count; i++)
  602. {
  603. MB3_AgglomerativeClustering.ClusterNode node = _clustersToDraw[i];
  604. List<Renderer> rrs = new List<Renderer>();
  605. for (int j = 0; j < node.leafs.Length; j++)
  606. {
  607. Renderer r = cluster.clusters[node.leafs[j]].leaf.go.GetComponent<Renderer>();
  608. if (r is MeshRenderer || r is SkinnedMeshRenderer)
  609. {
  610. rrs.Add(r);
  611. }
  612. }
  613. cell2objs.Add("Cluster_" + i, rrs);
  614. }
  615. return cell2objs;
  616. }
  617. public void BuildClusters(List<GameObject> gos, ProgressUpdateCancelableDelegate progFunc)
  618. {
  619. if (gos.Count == 0)
  620. {
  621. Debug.LogWarning("No objects to cluster. Add some objects to the list of Objects To Combine.");
  622. return;
  623. }
  624. if (cluster == null) cluster = new MB3_AgglomerativeClustering();
  625. List<MB3_AgglomerativeClustering.item_s> its = new List<MB3_AgglomerativeClustering.item_s>();
  626. for (int i = 0; i < gos.Count; i++)
  627. {
  628. if (gos[i] != null && its.Find(x => x.go == gos[i]) == null)
  629. {
  630. Renderer mr = gos[i].GetComponent<Renderer>();
  631. if (mr != null && (mr is MeshRenderer || mr is SkinnedMeshRenderer))
  632. {
  633. MB3_AgglomerativeClustering.item_s ii = new MB3_AgglomerativeClustering.item_s();
  634. ii.go = gos[i];
  635. ii.coord = mr.bounds.center;
  636. its.Add(ii);
  637. }
  638. }
  639. }
  640. cluster.items = its;
  641. //yield return cluster.agglomerate();
  642. cluster.agglomerate(progFunc);
  643. if (!cluster.wasCanceled)
  644. {
  645. float smallest, largest;
  646. _BuildListOfClustersToDraw(progFunc, out smallest, out largest);
  647. d.maxDistBetweenClusters = Mathf.Lerp(smallest, largest, .9f);
  648. }
  649. }
  650. void _BuildListOfClustersToDraw(ProgressUpdateCancelableDelegate progFunc, out float smallest, out float largest)
  651. {
  652. _clustersToDraw.Clear();
  653. if (cluster.clusters == null)
  654. {
  655. smallest = 1f;
  656. largest = 10f;
  657. return;
  658. }
  659. if (progFunc != null) progFunc("Building Clusters To Draw A:", 0);
  660. List<MB3_AgglomerativeClustering.ClusterNode> removeMe = new List<MB3_AgglomerativeClustering.ClusterNode>();
  661. largest = 1f;
  662. smallest = 10e6f;
  663. for (int i = 0; i < cluster.clusters.Length; i++)
  664. {
  665. MB3_AgglomerativeClustering.ClusterNode node = cluster.clusters[i];
  666. //don't draw clusters that were merged too far apart and only want leaf nodes
  667. if (node.distToMergedCentroid <= d.maxDistBetweenClusters /*&& node.leaf == null*/)
  668. {
  669. if (d.includeCellsWithOnlyOneRenderer)
  670. {
  671. _clustersToDraw.Add(node);
  672. }
  673. else if (node.leaf == null)
  674. {
  675. _clustersToDraw.Add(node);
  676. }
  677. }
  678. if (node.distToMergedCentroid > largest)
  679. {
  680. largest = node.distToMergedCentroid;
  681. }
  682. if (node.height > 0 && node.distToMergedCentroid < smallest)
  683. {
  684. smallest = node.distToMergedCentroid;
  685. }
  686. }
  687. if (progFunc != null) progFunc("Building Clusters To Draw B:", 0);
  688. for (int i = 0; i < _clustersToDraw.Count; i++)
  689. {
  690. removeMe.Add(_clustersToDraw[i].cha);
  691. removeMe.Add(_clustersToDraw[i].chb);
  692. }
  693. for (int i = 0; i < removeMe.Count; i++)
  694. {
  695. _clustersToDraw.Remove(removeMe[i]);
  696. }
  697. _radii = new float[_clustersToDraw.Count];
  698. if (progFunc != null) progFunc("Building Clusters To Draw C:", 0);
  699. for (int i = 0; i < _radii.Length; i++)
  700. {
  701. MB3_AgglomerativeClustering.ClusterNode n = _clustersToDraw[i];
  702. Bounds b = new Bounds(n.centroid, Vector3.one);
  703. for (int j = 0; j < n.leafs.Length; j++)
  704. {
  705. Renderer r = cluster.clusters[n.leafs[j]].leaf.go.GetComponent<Renderer>();
  706. if (r != null)
  707. {
  708. b.Encapsulate(r.bounds);
  709. }
  710. }
  711. _radii[i] = b.extents.magnitude;
  712. }
  713. if (progFunc != null) progFunc("Building Clusters To Draw D:", 0);
  714. _ObjsExtents = largest + 1f;
  715. _minDistBetweenClusters = Mathf.Lerp(smallest, 0f, .9f);
  716. if (_ObjsExtents < 2f) _ObjsExtents = 2f;
  717. }
  718. public override void DrawGizmos(Bounds sceneObjectBounds)
  719. {
  720. if (cluster == null || cluster.clusters == null)
  721. {
  722. return;
  723. }
  724. if (_lastMaxDistBetweenClusters != d.maxDistBetweenClusters)
  725. {
  726. float s, l;
  727. _BuildListOfClustersToDraw(null, out s, out l);
  728. _lastMaxDistBetweenClusters = d.maxDistBetweenClusters;
  729. }
  730. for (int i = 0; i < _clustersToDraw.Count; i++)
  731. {
  732. Gizmos.color = Color.white;
  733. MB3_AgglomerativeClustering.ClusterNode node = _clustersToDraw[i];
  734. Gizmos.DrawWireSphere(node.centroid, _radii[i]);
  735. }
  736. }
  737. }
  738. }