809 lines
30 KiB
C#
809 lines
30 KiB
C#
|
using UnityEngine;
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using DigitalOpus.MB.Core;
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
using UnityEditor;
|
|||
|
#endif
|
|||
|
|
|||
|
public class MB3_MeshBakerGrouper : MonoBehaviour, MB_IMeshBakerSettingsHolder
|
|||
|
{
|
|||
|
public enum ClusterType
|
|||
|
{
|
|||
|
none,
|
|||
|
grid,
|
|||
|
pie,
|
|||
|
agglomerative,
|
|||
|
}
|
|||
|
|
|||
|
public MB3_MeshBakerGrouperCore grouper;
|
|||
|
public ClusterType clusterType = ClusterType.none;
|
|||
|
public GrouperData data = new GrouperData();
|
|||
|
|
|||
|
//these are for getting a resonable bounds in which to draw gizmos.
|
|||
|
[HideInInspector] public Bounds sourceObjectBounds = new Bounds(Vector3.zero, Vector3.one);
|
|||
|
|
|||
|
public MB3_MeshCombinerSettings meshBakerSettingsAsset;
|
|||
|
public MB3_MeshCombinerSettingsData meshBakerSettings;
|
|||
|
|
|||
|
public MB_IMeshBakerSettings GetMeshBakerSettings()
|
|||
|
{
|
|||
|
if (meshBakerSettingsAsset == null)
|
|||
|
{
|
|||
|
return meshBakerSettings;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return meshBakerSettingsAsset.GetMeshBakerSettings();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
public SerializedProperty GetMeshBakerSettingsAsSerializedProperty()
|
|||
|
{
|
|||
|
if (meshBakerSettingsAsset == null)
|
|||
|
{
|
|||
|
UnityEditor.SerializedObject so = new UnityEditor.SerializedObject(this);
|
|||
|
return so.FindProperty("meshBakerSettings");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
UnityEditor.SerializedObject so = new UnityEditor.SerializedObject(meshBakerSettingsAsset);
|
|||
|
return so.FindProperty("data");
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
void OnDrawGizmosSelected()
|
|||
|
{
|
|||
|
if (grouper == null)
|
|||
|
{
|
|||
|
grouper = CreateGrouper(clusterType, data);
|
|||
|
}
|
|||
|
if (grouper.d == null)
|
|||
|
{
|
|||
|
grouper.d = data;
|
|||
|
}
|
|||
|
grouper.DrawGizmos(sourceObjectBounds);
|
|||
|
}
|
|||
|
|
|||
|
public MB3_MeshBakerGrouperCore CreateGrouper(ClusterType t, GrouperData data)
|
|||
|
{
|
|||
|
if (t == ClusterType.grid) grouper = new MB3_MeshBakerGrouperGrid(data);
|
|||
|
if (t == ClusterType.pie) grouper = new MB3_MeshBakerGrouperPie(data);
|
|||
|
if (t == ClusterType.agglomerative)
|
|||
|
{
|
|||
|
MB3_TextureBaker tb = GetComponent<MB3_TextureBaker>();
|
|||
|
List<GameObject> gos;
|
|||
|
if (tb != null)
|
|||
|
{
|
|||
|
gos = tb.GetObjectsToCombine();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
gos = new List<GameObject>();
|
|||
|
}
|
|||
|
grouper = new MB3_MeshBakerGrouperCluster(data, gos);
|
|||
|
}
|
|||
|
if (t == ClusterType.none) grouper = new MB3_MeshBakerGrouperNone(data);
|
|||
|
return grouper;
|
|||
|
}
|
|||
|
|
|||
|
public void DeleteAllChildMeshBakers()
|
|||
|
{
|
|||
|
MB3_MeshBakerCommon[] mBakers = GetComponentsInChildren<MB3_MeshBakerCommon>();
|
|||
|
for (int i = 0; i < mBakers.Length; i++)
|
|||
|
{
|
|||
|
MB3_MeshBakerCommon mb = mBakers[i];
|
|||
|
GameObject resultGameObject = mb.meshCombiner.resultSceneObject;
|
|||
|
MB_Utility.Destroy(resultGameObject);
|
|||
|
MB_Utility.Destroy(mb.gameObject);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
namespace DigitalOpus.MB.Core
|
|||
|
{
|
|||
|
/// all properties go here so that settings are remembered as user switches between cluster types
|
|||
|
[Serializable]
|
|||
|
public class GrouperData
|
|||
|
{
|
|||
|
public bool clusterOnLMIndex;
|
|||
|
public bool clusterByLODLevel;
|
|||
|
public Vector3 origin;
|
|||
|
|
|||
|
//Normally these properties would be in the subclasses but putting them here makes writing the inspector much easier
|
|||
|
//for grid
|
|||
|
public Vector3 cellSize;
|
|||
|
|
|||
|
//for pie
|
|||
|
public int pieNumSegments = 4;
|
|||
|
public Vector3 pieAxis = Vector3.up;
|
|||
|
public float ringSpacing = 100f;
|
|||
|
public bool combineSegmentsInInnermostRing = false;
|
|||
|
|
|||
|
//for clustering
|
|||
|
public int height = 1;
|
|||
|
public float maxDistBetweenClusters = 1f;
|
|||
|
public bool includeCellsWithOnlyOneRenderer = true;
|
|||
|
}
|
|||
|
|
|||
|
[Serializable]
|
|||
|
public abstract class MB3_MeshBakerGrouperCore
|
|||
|
{
|
|||
|
|
|||
|
public GrouperData d;
|
|||
|
public abstract Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection);
|
|||
|
public abstract void DrawGizmos(Bounds sourceObjectBounds);
|
|||
|
public void DoClustering(MB3_TextureBaker tb, MB3_MeshBakerGrouper grouper)
|
|||
|
{
|
|||
|
//todo warn for no objects and no Texture Bake Result
|
|||
|
Dictionary<string, List<Renderer>> cell2objs = FilterIntoGroups(tb.GetObjectsToCombine());
|
|||
|
|
|||
|
if (d.clusterOnLMIndex)
|
|||
|
{
|
|||
|
Dictionary<string, List<Renderer>> cell2objsNew = new Dictionary<string, List<Renderer>>();
|
|||
|
foreach (string key in cell2objs.Keys)
|
|||
|
{
|
|||
|
List<Renderer> gaws = cell2objs[key];
|
|||
|
Dictionary<int, List<Renderer>> idx2objs = GroupByLightmapIndex(gaws);
|
|||
|
foreach (int keyIdx in idx2objs.Keys)
|
|||
|
{
|
|||
|
string keyNew = key + "-LM-" + keyIdx;
|
|||
|
cell2objsNew.Add(keyNew, idx2objs[keyIdx]);
|
|||
|
}
|
|||
|
}
|
|||
|
cell2objs = cell2objsNew;
|
|||
|
}
|
|||
|
if (d.clusterByLODLevel)
|
|||
|
{
|
|||
|
//visit each cell
|
|||
|
//visit each renderer
|
|||
|
//check if that renderer is a child of an LOD group
|
|||
|
// visit each LOD level check if this renderer is in that list.
|
|||
|
// if not add it to LOD0 for that cell
|
|||
|
// otherwise add it to LODX for that cell creating LODs as necessary
|
|||
|
Dictionary<string, List<Renderer>> cell2objsNew = new Dictionary<string, List<Renderer>>();
|
|||
|
foreach (string key in cell2objs.Keys)
|
|||
|
{
|
|||
|
List<Renderer> gaws = cell2objs[key];
|
|||
|
foreach (Renderer r in gaws)
|
|||
|
{
|
|||
|
if (r == null) continue;
|
|||
|
bool foundInLOD = false;
|
|||
|
LODGroup lodg = r.GetComponentInParent<LODGroup>();
|
|||
|
if (lodg != null)
|
|||
|
{
|
|||
|
LOD[] lods = lodg.GetLODs();
|
|||
|
for (int i = 0; i < lods.Length; i++)
|
|||
|
{
|
|||
|
LOD lod = lods[i];
|
|||
|
if (Array.Find<Renderer>(lod.renderers, x => x == r) != null)
|
|||
|
{
|
|||
|
foundInLOD = true;
|
|||
|
List<Renderer> rs;
|
|||
|
string newKey = String.Format("{0}_LOD{1}", key, i);
|
|||
|
if (!cell2objsNew.TryGetValue(newKey, out rs))
|
|||
|
{
|
|||
|
rs = new List<Renderer>();
|
|||
|
cell2objsNew.Add(newKey, rs);
|
|||
|
}
|
|||
|
if (!rs.Contains(r)) rs.Add(r);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (!foundInLOD)
|
|||
|
{
|
|||
|
List<Renderer> rs;
|
|||
|
string newKey = String.Format("{0}_LOD0", key);
|
|||
|
if (!cell2objsNew.TryGetValue(newKey, out rs))
|
|||
|
{
|
|||
|
rs = new List<Renderer>();
|
|||
|
cell2objsNew.Add(newKey, rs);
|
|||
|
}
|
|||
|
if (!rs.Contains(r)) rs.Add(r);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
cell2objs = cell2objsNew;
|
|||
|
}
|
|||
|
int clustersWithOnlyOneRenderer = 0;
|
|||
|
foreach (string key in cell2objs.Keys)
|
|||
|
{
|
|||
|
List<Renderer> gaws = cell2objs[key];
|
|||
|
if (gaws.Count > 1 || grouper.data.includeCellsWithOnlyOneRenderer)
|
|||
|
{
|
|||
|
AddMeshBaker(grouper, tb, key, gaws);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
clustersWithOnlyOneRenderer++;
|
|||
|
}
|
|||
|
}
|
|||
|
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));
|
|||
|
}
|
|||
|
|
|||
|
Dictionary<int, List<Renderer>> GroupByLightmapIndex(List<Renderer> gaws)
|
|||
|
{
|
|||
|
Dictionary<int, List<Renderer>> idx2objs = new Dictionary<int, List<Renderer>>();
|
|||
|
for (int i = 0; i < gaws.Count; i++)
|
|||
|
{
|
|||
|
List<Renderer> objs = null;
|
|||
|
if (idx2objs.ContainsKey(gaws[i].lightmapIndex))
|
|||
|
{
|
|||
|
objs = idx2objs[gaws[i].lightmapIndex];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
objs = new List<Renderer>();
|
|||
|
idx2objs.Add(gaws[i].lightmapIndex, objs);
|
|||
|
}
|
|||
|
objs.Add(gaws[i]);
|
|||
|
}
|
|||
|
return idx2objs;
|
|||
|
}
|
|||
|
|
|||
|
void AddMeshBaker(MB3_MeshBakerGrouper grouper, MB3_TextureBaker tb, string key, List<Renderer> gaws)
|
|||
|
{
|
|||
|
int numVerts = 0;
|
|||
|
for (int i = 0; i < gaws.Count; i++)
|
|||
|
{
|
|||
|
Mesh m = MB_Utility.GetMesh(gaws[i].gameObject);
|
|||
|
if (m != null)
|
|||
|
numVerts += m.vertexCount;
|
|||
|
}
|
|||
|
|
|||
|
GameObject nmb = new GameObject("MeshBaker-" + key);
|
|||
|
nmb.transform.position = Vector3.zero;
|
|||
|
MB3_MeshBakerCommon newMeshBaker;
|
|||
|
if (numVerts >= 65535)
|
|||
|
{
|
|||
|
newMeshBaker = nmb.AddComponent<MB3_MultiMeshBaker>();
|
|||
|
newMeshBaker.useObjsToMeshFromTexBaker = false;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
newMeshBaker = nmb.AddComponent<MB3_MeshBaker>();
|
|||
|
newMeshBaker.useObjsToMeshFromTexBaker = false;
|
|||
|
}
|
|||
|
|
|||
|
newMeshBaker.textureBakeResults = tb.textureBakeResults;
|
|||
|
newMeshBaker.transform.parent = tb.transform;
|
|||
|
newMeshBaker.meshCombiner.settingsHolder = grouper;
|
|||
|
for (int i = 0; i < gaws.Count; i++)
|
|||
|
{
|
|||
|
newMeshBaker.GetObjectsToCombine().Add(gaws[i].gameObject);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Serializable]
|
|||
|
public class MB3_MeshBakerGrouperNone : MB3_MeshBakerGrouperCore
|
|||
|
{
|
|||
|
public MB3_MeshBakerGrouperNone(GrouperData d)
|
|||
|
{
|
|||
|
this.d = d;
|
|||
|
}
|
|||
|
|
|||
|
public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
|
|||
|
{
|
|||
|
Debug.Log("Filtering into groups none");
|
|||
|
|
|||
|
Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
|
|||
|
|
|||
|
List<Renderer> rs = new List<Renderer>();
|
|||
|
for (int i = 0; i < selection.Count; i++)
|
|||
|
{
|
|||
|
if (selection[i] != null)
|
|||
|
{
|
|||
|
rs.Add(selection[i].GetComponent<Renderer>());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cell2objs.Add("MeshBaker", rs);
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
public override void DrawGizmos(Bounds sourceObjectBounds)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Serializable]
|
|||
|
public class MB3_MeshBakerGrouperGrid : MB3_MeshBakerGrouperCore
|
|||
|
{
|
|||
|
public MB3_MeshBakerGrouperGrid(GrouperData d)
|
|||
|
{
|
|||
|
this.d = d;
|
|||
|
}
|
|||
|
|
|||
|
public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
|
|||
|
{
|
|||
|
Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
|
|||
|
if (d.cellSize.x <= 0f || d.cellSize.y <= 0f || d.cellSize.z <= 0f)
|
|||
|
{
|
|||
|
Debug.LogError("cellSize x,y,z must all be greater than zero.");
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
Debug.Log("Collecting renderers in each cell");
|
|||
|
foreach (GameObject t in selection)
|
|||
|
{
|
|||
|
if (t == null)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
GameObject go = t;
|
|||
|
Renderer mr = go.GetComponent<Renderer>();
|
|||
|
if (mr is MeshRenderer || mr is SkinnedMeshRenderer)
|
|||
|
{
|
|||
|
//get the cell this gameObject is in
|
|||
|
Vector3 gridVector = mr.bounds.center;
|
|||
|
gridVector.x = Mathf.Floor((gridVector.x - d.origin.x) / d.cellSize.x) * d.cellSize.x;
|
|||
|
gridVector.y = Mathf.Floor((gridVector.y - d.origin.y) / d.cellSize.y) * d.cellSize.y;
|
|||
|
gridVector.z = Mathf.Floor((gridVector.z - d.origin.z) / d.cellSize.z) * d.cellSize.z;
|
|||
|
List<Renderer> objs = null;
|
|||
|
string gridVectorStr = gridVector.ToString();
|
|||
|
if (cell2objs.ContainsKey(gridVectorStr))
|
|||
|
{
|
|||
|
objs = cell2objs[gridVectorStr];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
objs = new List<Renderer>();
|
|||
|
cell2objs.Add(gridVectorStr, objs);
|
|||
|
}
|
|||
|
|
|||
|
if (!objs.Contains(mr))
|
|||
|
{
|
|||
|
//Debug.Log("Adding " + mr + " todo " + gridVectorStr);
|
|||
|
objs.Add(mr);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
public override void DrawGizmos(Bounds sourceObjectBounds)
|
|||
|
{
|
|||
|
Vector3 cs = d.cellSize;
|
|||
|
if (cs.x <= .00001f || cs.y <= .00001f || cs.z <= .00001f) return;
|
|||
|
Vector3 p = sourceObjectBounds.center - sourceObjectBounds.extents;
|
|||
|
Vector3 offset = d.origin;
|
|||
|
offset.x = offset.x % cs.x;
|
|||
|
offset.y = offset.y % cs.y;
|
|||
|
offset.z = offset.z % cs.z;
|
|||
|
//snap p to closest cell center
|
|||
|
Vector3 start;
|
|||
|
p.x = Mathf.Round((p.x) / cs.x) * cs.x + offset.x;
|
|||
|
p.y = Mathf.Round((p.y) / cs.y) * cs.y + offset.y;
|
|||
|
p.z = Mathf.Round((p.z) / cs.z) * cs.z + offset.z;
|
|||
|
if (p.x > sourceObjectBounds.center.x - sourceObjectBounds.extents.x) p.x = p.x - cs.x;
|
|||
|
if (p.y > sourceObjectBounds.center.y - sourceObjectBounds.extents.y) p.y = p.y - cs.y;
|
|||
|
if (p.z > sourceObjectBounds.center.z - sourceObjectBounds.extents.z) p.z = p.z - cs.z;
|
|||
|
start = p;
|
|||
|
int numcells = Mathf.CeilToInt(sourceObjectBounds.size.x / cs.x + sourceObjectBounds.size.y / cs.y + sourceObjectBounds.size.z / cs.z);
|
|||
|
if (numcells > 200)
|
|||
|
{
|
|||
|
Gizmos.DrawWireCube(d.origin + cs / 2f, cs);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
for (; p.x < sourceObjectBounds.center.x + sourceObjectBounds.extents.x; p.x += cs.x)
|
|||
|
{
|
|||
|
p.y = start.y;
|
|||
|
for (; p.y < sourceObjectBounds.center.y + sourceObjectBounds.extents.y; p.y += cs.y)
|
|||
|
{
|
|||
|
p.z = start.z;
|
|||
|
for (; p.z < sourceObjectBounds.center.z + sourceObjectBounds.extents.z; p.z += cs.z)
|
|||
|
{
|
|||
|
Gizmos.DrawWireCube(p + cs / 2f, cs);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Serializable]
|
|||
|
public class MB3_MeshBakerGrouperPie : MB3_MeshBakerGrouperCore
|
|||
|
{
|
|||
|
public MB3_MeshBakerGrouperPie(GrouperData data)
|
|||
|
{
|
|||
|
d = data;
|
|||
|
}
|
|||
|
|
|||
|
public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
|
|||
|
{
|
|||
|
Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
|
|||
|
if (d.pieNumSegments == 0)
|
|||
|
{
|
|||
|
Debug.LogError("pieNumSegments must be greater than zero.");
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
if (d.pieAxis.magnitude <= .000001f)
|
|||
|
{
|
|||
|
Debug.LogError("Pie axis vector is too short.");
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
if (d.ringSpacing <= .000001f)
|
|||
|
{
|
|||
|
Debug.LogError("Ring spacing is too small.");
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
d.pieAxis.Normalize();
|
|||
|
Quaternion pieAxis2yIsUp = Quaternion.FromToRotation(d.pieAxis, Vector3.up);
|
|||
|
|
|||
|
Debug.Log("Collecting renderers in each cell");
|
|||
|
foreach (GameObject t in selection)
|
|||
|
{
|
|||
|
if (t == null)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
GameObject go = t;
|
|||
|
Renderer mr = go.GetComponent<Renderer>();
|
|||
|
if (mr is MeshRenderer || mr is SkinnedMeshRenderer)
|
|||
|
{
|
|||
|
//get the cell this gameObject is in
|
|||
|
Vector3 origin2obj = mr.bounds.center - d.origin;
|
|||
|
origin2obj = pieAxis2yIsUp * origin2obj;
|
|||
|
Vector2 origin2Obj2D = new Vector2(origin2obj.x, origin2obj.z);
|
|||
|
float radius = origin2Obj2D.magnitude;
|
|||
|
origin2obj.Normalize();
|
|||
|
|
|||
|
float deg_aboutY = 0f;
|
|||
|
if (Mathf.Abs(origin2obj.x) < 10e-5f && Mathf.Abs(origin2obj.z) < 10e-5f)
|
|||
|
{
|
|||
|
deg_aboutY = 0f;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
deg_aboutY = Mathf.Atan2(origin2obj.x, origin2obj.z) * Mathf.Rad2Deg;
|
|||
|
if (deg_aboutY < 0f) deg_aboutY = 360f + deg_aboutY;
|
|||
|
}
|
|||
|
|
|||
|
// Debug.Log ("Obj " + mr + " angle " + d_aboutY);
|
|||
|
int segment = Mathf.FloorToInt(deg_aboutY / 360f * d.pieNumSegments);
|
|||
|
int ring = Mathf.FloorToInt(radius / d.ringSpacing);
|
|||
|
if (ring == 0 && d.combineSegmentsInInnermostRing)
|
|||
|
{
|
|||
|
segment = 0;
|
|||
|
}
|
|||
|
|
|||
|
List<Renderer> objs = null;
|
|||
|
string segStr = "seg_" + segment + "_ring_" + ring;
|
|||
|
if (cell2objs.ContainsKey(segStr))
|
|||
|
{
|
|||
|
objs = cell2objs[segStr];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
objs = new List<Renderer>();
|
|||
|
cell2objs.Add(segStr, objs);
|
|||
|
}
|
|||
|
|
|||
|
if (!objs.Contains(mr))
|
|||
|
{
|
|||
|
objs.Add(mr);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
public override void DrawGizmos(Bounds sourceObjectBounds)
|
|||
|
{
|
|||
|
if (d.pieAxis.magnitude < .1f) return;
|
|||
|
if (d.pieNumSegments < 1) return;
|
|||
|
float rad = sourceObjectBounds.extents.magnitude;
|
|||
|
|
|||
|
int numRings = Mathf.CeilToInt(rad / d.ringSpacing);
|
|||
|
numRings = Mathf.Max(1, numRings);
|
|||
|
for (int i = 0; i < numRings; i++)
|
|||
|
{
|
|||
|
DrawCircle(d.pieAxis.normalized, d.origin, d.ringSpacing * (i + 1), 24);
|
|||
|
}
|
|||
|
|
|||
|
Gizmos.color = Color.white;
|
|||
|
Quaternion yIsUp2PieAxis = Quaternion.FromToRotation(Vector3.up, d.pieAxis);
|
|||
|
Quaternion rStep = Quaternion.AngleAxis(180f / d.pieNumSegments, Vector3.up);
|
|||
|
Vector3 r = Vector3.forward;
|
|||
|
for (int i = 0; i < d.pieNumSegments; i++)
|
|||
|
{
|
|||
|
Vector3 rr = yIsUp2PieAxis * r;
|
|||
|
Vector3 origin = d.origin;
|
|||
|
int nr = numRings;
|
|||
|
if (d.combineSegmentsInInnermostRing)
|
|||
|
{
|
|||
|
origin = d.origin + rr.normalized * d.ringSpacing;
|
|||
|
nr = numRings - 1;
|
|||
|
}
|
|||
|
|
|||
|
if (nr == 0) break;
|
|||
|
|
|||
|
Gizmos.DrawLine(origin, origin + nr * d.ringSpacing * rr.normalized);
|
|||
|
r = rStep * r;
|
|||
|
r = rStep * r;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static int MaxIndexInVector3(Vector3 v)
|
|||
|
{
|
|||
|
int idx = 0;
|
|||
|
float val = v.x;
|
|||
|
if (v.y > val)
|
|||
|
{
|
|||
|
idx = 1;
|
|||
|
val = v.y;
|
|||
|
}
|
|||
|
if (v.z > val)
|
|||
|
{
|
|||
|
idx = 2;
|
|||
|
val = v.z;
|
|||
|
}
|
|||
|
return idx;
|
|||
|
}
|
|||
|
|
|||
|
public static void DrawCircle(Vector3 axis, Vector3 center, float radius, int subdiv)
|
|||
|
{
|
|||
|
Quaternion q = Quaternion.AngleAxis(360 / subdiv, axis);
|
|||
|
int maxIdx = MaxIndexInVector3(axis);
|
|||
|
int otherIdx = maxIdx == 0 ? maxIdx + 1 : maxIdx - 1;
|
|||
|
Vector3 r = axis; //r construct a vector perpendicular to axis
|
|||
|
float temp = r[maxIdx];
|
|||
|
r[maxIdx] = r[otherIdx];
|
|||
|
r[otherIdx] = -temp;
|
|||
|
r = Vector3.ProjectOnPlane(r, axis);
|
|||
|
r.Normalize();
|
|||
|
r *= radius;
|
|||
|
for (int i = 0; i < subdiv + 1; i++)
|
|||
|
{
|
|||
|
Vector3 r2 = q * r;
|
|||
|
Gizmos.color = Color.white;
|
|||
|
Gizmos.DrawLine(center + r, center + r2);
|
|||
|
r = r2;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
[Serializable]
|
|||
|
public class MB3_MeshBakerGrouperKMeans : MB3_MeshBakerGrouperCore
|
|||
|
{
|
|||
|
public int numClusters = 4;
|
|||
|
public Vector3[] clusterCenters = new Vector3[0];
|
|||
|
public float[] clusterSizes = new float[0];
|
|||
|
|
|||
|
public MB3_MeshBakerGrouperKMeans(GrouperData data)
|
|||
|
{
|
|||
|
d = data;
|
|||
|
}
|
|||
|
|
|||
|
public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
|
|||
|
{
|
|||
|
Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
|
|||
|
List<GameObject> validObjs = new List<GameObject>();
|
|||
|
int numClusters = 20;
|
|||
|
foreach (GameObject t in selection)
|
|||
|
{
|
|||
|
if (t == null)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
GameObject go = t;
|
|||
|
Renderer mr = go.GetComponent<Renderer>();
|
|||
|
if (mr is MeshRenderer || mr is SkinnedMeshRenderer)
|
|||
|
{
|
|||
|
//get the cell this gameObject is in
|
|||
|
validObjs.Add(go);
|
|||
|
}
|
|||
|
}
|
|||
|
if (validObjs.Count > 0 && numClusters > 0 && numClusters < validObjs.Count)
|
|||
|
{
|
|||
|
MB3_KMeansClustering kmc = new MB3_KMeansClustering(validObjs, numClusters);
|
|||
|
kmc.Cluster();
|
|||
|
clusterCenters = new Vector3[numClusters];
|
|||
|
clusterSizes = new float[numClusters];
|
|||
|
for (int i = 0; i < numClusters; i++)
|
|||
|
{
|
|||
|
List<Renderer> lr = kmc.GetCluster(i, out clusterCenters[i], out clusterSizes[i]);
|
|||
|
if (lr.Count > 0)
|
|||
|
{
|
|||
|
cell2objs.Add("Cluster_" + i, lr);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//todo error messages
|
|||
|
}
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
public override void DrawGizmos(Bounds sceneObjectBounds)
|
|||
|
{
|
|||
|
if (clusterCenters != null && clusterSizes != null && clusterCenters.Length == clusterSizes.Length)
|
|||
|
{
|
|||
|
for (int i = 0; i < clusterSizes.Length; i++)
|
|||
|
{
|
|||
|
Gizmos.DrawWireSphere(clusterCenters[i], clusterSizes[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Serializable]
|
|||
|
public class MB3_MeshBakerGrouperCluster : MB3_MeshBakerGrouperCore
|
|||
|
{
|
|||
|
|
|||
|
public MB3_AgglomerativeClustering cluster;
|
|||
|
float _lastMaxDistBetweenClusters;
|
|||
|
public float _ObjsExtents = 10f;
|
|||
|
public float _minDistBetweenClusters = .001f;
|
|||
|
List<MB3_AgglomerativeClustering.ClusterNode> _clustersToDraw = new List<MB3_AgglomerativeClustering.ClusterNode>();
|
|||
|
float[] _radii;
|
|||
|
|
|||
|
public MB3_MeshBakerGrouperCluster(GrouperData data, List<GameObject> gos)
|
|||
|
{
|
|||
|
d = data;
|
|||
|
}
|
|||
|
|
|||
|
public override Dictionary<string, List<Renderer>> FilterIntoGroups(List<GameObject> selection)
|
|||
|
{
|
|||
|
Dictionary<string, List<Renderer>> cell2objs = new Dictionary<string, List<Renderer>>();
|
|||
|
for (int i = 0; i < _clustersToDraw.Count; i++)
|
|||
|
{
|
|||
|
MB3_AgglomerativeClustering.ClusterNode node = _clustersToDraw[i];
|
|||
|
List<Renderer> rrs = new List<Renderer>();
|
|||
|
for (int j = 0; j < node.leafs.Length; j++)
|
|||
|
{
|
|||
|
Renderer r = cluster.clusters[node.leafs[j]].leaf.go.GetComponent<Renderer>();
|
|||
|
if (r is MeshRenderer || r is SkinnedMeshRenderer)
|
|||
|
{
|
|||
|
rrs.Add(r);
|
|||
|
}
|
|||
|
}
|
|||
|
cell2objs.Add("Cluster_" + i, rrs);
|
|||
|
}
|
|||
|
return cell2objs;
|
|||
|
}
|
|||
|
|
|||
|
public void BuildClusters(List<GameObject> gos, ProgressUpdateCancelableDelegate progFunc)
|
|||
|
{
|
|||
|
if (gos.Count == 0)
|
|||
|
{
|
|||
|
Debug.LogWarning("No objects to cluster. Add some objects to the list of Objects To Combine.");
|
|||
|
return;
|
|||
|
}
|
|||
|
if (cluster == null) cluster = new MB3_AgglomerativeClustering();
|
|||
|
List<MB3_AgglomerativeClustering.item_s> its = new List<MB3_AgglomerativeClustering.item_s>();
|
|||
|
for (int i = 0; i < gos.Count; i++)
|
|||
|
{
|
|||
|
if (gos[i] != null && its.Find(x => x.go == gos[i]) == null)
|
|||
|
{
|
|||
|
Renderer mr = gos[i].GetComponent<Renderer>();
|
|||
|
if (mr != null && (mr is MeshRenderer || mr is SkinnedMeshRenderer))
|
|||
|
{
|
|||
|
MB3_AgglomerativeClustering.item_s ii = new MB3_AgglomerativeClustering.item_s();
|
|||
|
ii.go = gos[i];
|
|||
|
ii.coord = mr.bounds.center;
|
|||
|
its.Add(ii);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
cluster.items = its;
|
|||
|
//yield return cluster.agglomerate();
|
|||
|
cluster.agglomerate(progFunc);
|
|||
|
if (!cluster.wasCanceled)
|
|||
|
{
|
|||
|
float smallest, largest;
|
|||
|
_BuildListOfClustersToDraw(progFunc, out smallest, out largest);
|
|||
|
d.maxDistBetweenClusters = Mathf.Lerp(smallest, largest, .9f);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void _BuildListOfClustersToDraw(ProgressUpdateCancelableDelegate progFunc, out float smallest, out float largest)
|
|||
|
{
|
|||
|
_clustersToDraw.Clear();
|
|||
|
if (cluster.clusters == null)
|
|||
|
{
|
|||
|
smallest = 1f;
|
|||
|
largest = 10f;
|
|||
|
return;
|
|||
|
}
|
|||
|
if (progFunc != null) progFunc("Building Clusters To Draw A:", 0);
|
|||
|
List<MB3_AgglomerativeClustering.ClusterNode> removeMe = new List<MB3_AgglomerativeClustering.ClusterNode>();
|
|||
|
largest = 1f;
|
|||
|
smallest = 10e6f;
|
|||
|
for (int i = 0; i < cluster.clusters.Length; i++)
|
|||
|
{
|
|||
|
MB3_AgglomerativeClustering.ClusterNode node = cluster.clusters[i];
|
|||
|
//don't draw clusters that were merged too far apart and only want leaf nodes
|
|||
|
if (node.distToMergedCentroid <= d.maxDistBetweenClusters /*&& node.leaf == null*/)
|
|||
|
{
|
|||
|
if (d.includeCellsWithOnlyOneRenderer)
|
|||
|
{
|
|||
|
_clustersToDraw.Add(node);
|
|||
|
}
|
|||
|
else if (node.leaf == null)
|
|||
|
{
|
|||
|
_clustersToDraw.Add(node);
|
|||
|
}
|
|||
|
}
|
|||
|
if (node.distToMergedCentroid > largest)
|
|||
|
{
|
|||
|
largest = node.distToMergedCentroid;
|
|||
|
}
|
|||
|
if (node.height > 0 && node.distToMergedCentroid < smallest)
|
|||
|
{
|
|||
|
smallest = node.distToMergedCentroid;
|
|||
|
}
|
|||
|
}
|
|||
|
if (progFunc != null) progFunc("Building Clusters To Draw B:", 0);
|
|||
|
for (int i = 0; i < _clustersToDraw.Count; i++)
|
|||
|
{
|
|||
|
removeMe.Add(_clustersToDraw[i].cha);
|
|||
|
removeMe.Add(_clustersToDraw[i].chb);
|
|||
|
}
|
|||
|
|
|||
|
for (int i = 0; i < removeMe.Count; i++)
|
|||
|
{
|
|||
|
_clustersToDraw.Remove(removeMe[i]);
|
|||
|
}
|
|||
|
_radii = new float[_clustersToDraw.Count];
|
|||
|
if (progFunc != null) progFunc("Building Clusters To Draw C:", 0);
|
|||
|
for (int i = 0; i < _radii.Length; i++)
|
|||
|
{
|
|||
|
MB3_AgglomerativeClustering.ClusterNode n = _clustersToDraw[i];
|
|||
|
Bounds b = new Bounds(n.centroid, Vector3.one);
|
|||
|
for (int j = 0; j < n.leafs.Length; j++)
|
|||
|
{
|
|||
|
Renderer r = cluster.clusters[n.leafs[j]].leaf.go.GetComponent<Renderer>();
|
|||
|
if (r != null)
|
|||
|
{
|
|||
|
b.Encapsulate(r.bounds);
|
|||
|
}
|
|||
|
}
|
|||
|
_radii[i] = b.extents.magnitude;
|
|||
|
}
|
|||
|
if (progFunc != null) progFunc("Building Clusters To Draw D:", 0);
|
|||
|
_ObjsExtents = largest + 1f;
|
|||
|
_minDistBetweenClusters = Mathf.Lerp(smallest, 0f, .9f);
|
|||
|
|
|||
|
if (_ObjsExtents < 2f) _ObjsExtents = 2f;
|
|||
|
}
|
|||
|
|
|||
|
public override void DrawGizmos(Bounds sceneObjectBounds)
|
|||
|
{
|
|||
|
if (cluster == null || cluster.clusters == null)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
if (_lastMaxDistBetweenClusters != d.maxDistBetweenClusters)
|
|||
|
{
|
|||
|
float s, l;
|
|||
|
_BuildListOfClustersToDraw(null, out s, out l);
|
|||
|
_lastMaxDistBetweenClusters = d.maxDistBetweenClusters;
|
|||
|
}
|
|||
|
for (int i = 0; i < _clustersToDraw.Count; i++)
|
|||
|
{
|
|||
|
Gizmos.color = Color.white;
|
|||
|
MB3_AgglomerativeClustering.ClusterNode node = _clustersToDraw[i];
|
|||
|
Gizmos.DrawWireSphere(node.centroid, _radii[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|