Net.Like.Xue.Tokyo/Assets/Plugins/MeshCombineStudio/Scripts/Mesh/MeshCombiner.cs

1414 lines
54 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using System;
namespace MeshCombineStudio
{
public enum CombineMode { StaticObjects, DynamicObjects };//, OneDynamicObject };
[ExecuteInEditMode]
public class MeshCombiner : MonoBehaviour
{
public static EventMethod onInit;
public static List<MeshCombiner> instances = new List<MeshCombiner>();
public enum ObjectType { Normal, LodGroup, LodRenderer }
public enum HandleComponent { Disable, Destroy };
public enum ObjectCenter { BoundsCenter, TransformPosition };
public enum BackFaceTriangleMode { Transform, Box, Direction, EulerAngles }
public delegate void EventMethod(MeshCombiner meshCombiner);
public event EventMethod onCombiningStart;
public event EventMethod onCombiningAbort;
public event EventMethod onCombiningReady;
public MeshCombineJobManager.JobSettings jobSettings = new MeshCombineJobManager.JobSettings();
public LODGroupSettings[] lodGroupsSettings;
public ComputeShader computeDepthToArray;
public bool useCustomInstantiatePrefab;
public GameObject instantiatePrefab;
public bool instantiatePrefabValid;
public const int maxLodCount = 8;
public string saveMeshesFolder;
public ObjectOctree.Cell octree;
public List<ObjectOctree.MaxCell> changedCells;
[NonSerialized] public bool octreeContainsObjects;
public bool unitySettingsFoldout = true;
// Search Conditions
public SearchOptions searchOptions;
// Original Objects
public bool useOriginalObjectsHideFlags;
public HideFlags orginalObjectsHideFlags;
// Combine Conditions
public CombineConditionSettings combineConditionSettings;
// Output Settings
public bool outputSettingsFoldout = true;
public CombineMode combineMode;
public int cellSize = 32;
public Vector3 cellOffset;
public int cellCount;
public bool removeOriginalMeshReference = false;
public bool usedRemoveOriginalMeshRederences;
public bool useVertexOutputLimit;
public int vertexOutputLimit = 64000;
public enum RebakeLightingMode { CopyLightmapUvs, RegenarateLightmapUvs };
public RebakeLightingMode rebakeLightingMode;
public bool copyBakedLighting, validCopyBakedLighting;
public bool rebakeLighting, validRebakeLighting;
public float scaleInLightmap = 1;
public bool addMeshColliders = false;
public PhysicMaterial physicsMaterial;
public bool addMeshCollidersInRange = false;
public Bounds addMeshCollidersBounds;
public bool makeMeshesUnreadable = true;
public bool excludeSingleMeshes = false;
public bool removeTrianglesBelowSurface;
public bool noColliders;
public LayerMask surfaceLayerMask;
public float maxSurfaceHeight = 1000;
public bool removeOverlappingTriangles;
public bool removeSamePositionTriangles;
public bool reportFoundObjectsNotOnOverlapLayerMask = true;
public GameObject overlappingCollidersGO;
public LayerMask overlapLayerMask;
public int voxelizeLayer;
public int lodGroupLayer;
public GameObject overlappingNonCombineGO;
public bool disableOverlappingNonCombineGO;
public bool removeBackFaceTriangles;
public BackFaceTriangleMode backFaceTriangleMode;
public Transform backFaceT;
public Vector3 backFaceDirection;
public Vector3 backFaceRotation;
public Bounds backFaceBounds;
#if MCSCaves
public bool useExcludeOverlapRemovalTag;
public string excludeOverlapRemovalTag;
#endif
public bool useExcludeBackfaceRemovalTag;
public string excludeBackfaceRemovalTag;
public bool weldVertices;
public bool weldSnapVertices;
public float weldSnapSize = 0.025f;
public bool weldIncludeNormals;
// Job Settings
public bool jobSettingsFoldout = true;
// Runtime Settings
public bool runtimeSettingsFoldout = true;
public bool combineInRuntime;
public bool combineOnStart = true;
public bool useCombineSwapKey;
public KeyCode combineSwapKey = KeyCode.Tab;
public HandleComponent originalMeshRenderers = HandleComponent.Disable;
public HandleComponent originalLODGroups = HandleComponent.Disable;
// Mesh Save Settings
public bool meshSaveSettingsFoldout = true;
public bool deleteFilesFromSaveFolder;
public Vector3 oldPosition, oldScale;
public LodParentHolder[] lodParentHolders = new LodParentHolder[maxLodCount];
// Is stored in data script to make Inspector faster
[HideInInspector] public List<GameObject> combinedGameObjects = new List<GameObject>();
[HideInInspector] public List<CachedGameObject> foundObjects = new List<CachedGameObject>();
[HideInInspector] public List<CachedLodGameObject> foundLodObjects = new List<CachedLodGameObject>();
[HideInInspector] public List<LODGroup> foundLodGroups = new List<LODGroup>();
[HideInInspector] public List<Collider> foundColliders = new List<Collider>();
public HashSet<LODGroup> uniqueFoundLodGroups = new HashSet<LODGroup>();
public HashSet<Mesh> unreadableMeshes = new HashSet<Mesh>();
public HashSet<Mesh> selectImportSettingsMeshes = new HashSet<Mesh>();
public FoundCombineConditions foundCombineConditions = new FoundCombineConditions();
public HashSet<MeshCombineJobManager.MeshCombineJob> meshCombineJobs = new HashSet<MeshCombineJobManager.MeshCombineJob>();
public int totalMeshCombineJobs;
public int mrDisabledCount = 0;
public bool combined = false;
public bool isCombining = false;
public bool activeOriginal = true;
public bool combinedActive;
public bool drawGizmos = true;
public bool drawMeshBounds = true;
public int originalTotalVertices, originalTotalTriangles;
public int newTotalVertices, newTotalTriangles;
public int originalDrawCalls, newDrawCalls;
public int originalTotalNormalChannels, originalTotalTangentChannels;
public int originalTotalUvChannels, originalTotalUv2Channels, originalTotalUv3Channels, originalTotalUv4Channels;
public int originalTotalColorChannels;
public int newTotalNormalChannels, newTotalTangentChannels;
public int newTotalUvChannels, newTotalUv2Channels, newTotalUv3Channels, newTotalUv4Channels;
public int newTotalColorChannels;
public float combineTime;
[NonSerialized] public MeshCombinerData data;
public FastList<MeshColliderAdd> addMeshCollidersList = new FastList<MeshColliderAdd>();
HashSet<Transform> uniqueLodObjects = new HashSet<Transform>();
[NonSerialized] MeshCombiner thisInstance;
bool hasFoundFirstObject;
Bounds bounds;
[Serializable]
public class SearchOptions
{
public bool foldoutSearchParents = true;
public bool foldoutSearchConditions = true;
public enum ComponentCondition { And, Or, Not };
public enum LODGroupSearchMode { LodGroup, LodRenderers };
public GameObject parent;
public GameObject[] parentGOs;
public ObjectCenter objectCenter;
public LODGroupSearchMode lodGroupSearchMode;
public bool useSearchBox = false;
public Bounds searchBoxBounds;
public bool searchBoxSquare;
public Vector3 searchBoxPivot;
public Vector3 searchBoxSize = new Vector3(25, 25, 25);
public bool useMaxBoundsFactor = true;
public float maxBoundsFactor = 1.5f;
public bool useVertexInputLimit = true;
public int vertexInputLimit = 5000;
public bool useLayerMask;
public LayerMask layerMask = ~0;
public bool useTag;
public string tag;
public bool useNameContains;
public List<string> nameContainList = new List<string>();
public bool onlyActive = true;
#if UNITY_EDITOR
public UnityEditor.StaticEditorFlags editorStatic = UnityEditor.StaticEditorFlags.BatchingStatic;
#endif
public bool onlyStatic = true;
public bool onlyActiveMeshRenderers = true;
public bool useComponentsFilter;
public ComponentCondition componentCondition;
public List<string> componentNameList = new List<string>();
public void GetSearchBoxBounds()
{
searchBoxBounds = new Bounds(searchBoxPivot + new Vector3(0, searchBoxSize.y * 0.5f, 0), searchBoxSize);
}
}
public void AddMeshColliders()
{
try
{
for (int i = 0; i < addMeshCollidersList.Count; i++)
{
MeshColliderAdd mca = addMeshCollidersList.items[i];
var mc = mca.go.AddComponent<MeshCollider>();
mc.sharedMesh = mca.mesh;
mc.material = physicsMaterial;
}
}
catch (Exception e)
{
Debug.LogException(e);
}
finally
{
addMeshCollidersList.Clear();
}
}
public void ExecuteOnCombiningReady()
{
totalMeshCombineJobs = 0;
#if MCSCaves
if (removeOverlappingTriangles)
{
CreateOverlapColliders.DestroyOverlapColliders(overlappingCollidersGO);
if (overlappingNonCombineGO && disableOverlappingNonCombineGO) overlappingNonCombineGO.SetActive(false);
}
#endif
ExecuteHandleObjects(false, HandleComponent.Disable, HandleComponent.Disable);
stopwatch.Stop();
combineTime = (float)stopwatch.ElapsedMilliseconds / 1000;
combinedActive = true;
combined = true;
isCombining = false;
var lodGroupSetups = GetComponentsInChildren<LODGroupSetup>();
if (lodGroupSetups != null)
{
foreach(var lodGroupSetup in lodGroupSetups)
{
// ensure that lod groups are applied after combining
try
{
lodGroupSetup.ApplySetup();
}
catch (Exception e)
{
Debug.LogError("An error occurred applying lod setup");
Debug.LogException(e);
}
}
}
if (onCombiningReady != null) onCombiningReady(this);
}
void Awake()
{
Init();
}
void OnEnable()
{
Init();
}
void Init()
{
if (thisInstance == null)
{
instances.Add(this);
thisInstance = this;
if (onInit != null) onInit(this);
}
}
void OnDisable()
{
thisInstance = null;
instances.Remove(this);
}
public void InitData()
{
if ((searchOptions.parentGOs == null || searchOptions.parentGOs.Length == 0) && searchOptions.parent)
{
searchOptions.parentGOs = new GameObject[] { searchOptions.parent };
}
if (data == null)
{
data = GetComponent<MeshCombinerData>();
if (data == null)
{
data = gameObject.AddComponent<MeshCombinerData>();
data.combinedGameObjects = new List<GameObject>(combinedGameObjects);
data.foundObjects = new List<CachedGameObject>(foundObjects);
data.foundLodObjects = new List<CachedLodGameObject>(foundLodObjects);
data.foundLodGroups = new List<LODGroup>(foundLodGroups);
data.foundColliders = new List<Collider>(foundColliders);
combinedGameObjects.Clear();
foundObjects.Clear();
foundLodObjects.Clear();
foundLodGroups.Clear();
foundColliders.Clear();
}
}
}
void Start()
{
if (Application.isPlaying && !combineInRuntime) return;
InitMeshCombineJobManager();
if (instances[0] == this)
MeshCombineJobManager.instance.SetJobMode(jobSettings);
if (!Application.isPlaying && Application.isEditor) return;
// Debug.Log("Start");
StartRuntime();
}
// ==========================================================================================================================
void OnDestroy()
{
RestoreOriginalRenderersAndLODGroups(true);
thisInstance = null;
instances.Remove(this);
// if (!Application.isPlaying && Application.isEditor) return;
if (instances.Count == 0 && MeshCombineJobManager.instance != null)
{
Methods.Destroy(MeshCombineJobManager.instance.gameObject);
MeshCombineJobManager.instance = null;
}
}
static public MeshCombiner GetInstance(string name)
{
for (int i = 0; i < instances.Count; i++)
{
if (instances[i].gameObject.name == name) return instances[i];
}
return null;
}
public void CopyJobSettingsToAllInstances()
{
for (int i = 0; i < instances.Count; i++) instances[i].jobSettings.CopySettings(jobSettings);
}
public void InitMeshCombineJobManager()
{
if (MeshCombineJobManager.instance == null)
{
MeshCombineJobManager.CreateInstance(this, instantiatePrefab);
}
}
public void CreateLodGroupsSettings()
{
lodGroupsSettings = new LODGroupSettings[maxLodCount];
for (int i = 0; i < lodGroupsSettings.Length; i++) lodGroupsSettings[i] = new LODGroupSettings(i);
}
private void StartRuntime()
{
if (combineInRuntime)
{
if (combineOnStart) CombineAll();
if (useCombineSwapKey && originalMeshRenderers == HandleComponent.Disable && originalLODGroups == HandleComponent.Disable)
{
if (SwapCombineKey.instance == null) gameObject.AddComponent<SwapCombineKey>(); else SwapCombineKey.instance.meshCombinerList.Add(this);
}
}
}
// ==========================================================================================================================
public void DestroyCombinedObjects()
{
AbortAndClearMeshCombineJobs(false);
RestoreOriginalRenderersAndLODGroups(false);
Methods.DestroyChildren(transform);
var combinedGameObjects = data.combinedGameObjects;
for (int i = 0; i < combinedGameObjects.Count; i++)
{
Methods.Destroy(combinedGameObjects[i]);
}
#if UNITY_EDITOR
if (!Application.isPlaying) UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene);
#endif
combinedGameObjects.Clear();
data.ClearAll();
combinedActive = false;
combined = false;
}
public void Reset()
{
DestroyCombinedObjects();
uniqueLodObjects.Clear();
uniqueFoundLodGroups.Clear();
unreadableMeshes.Clear();
foundCombineConditions.combineConditions.Clear();
ResetOctree();
hasFoundFirstObject = false;
bounds.center = bounds.size = Vector3.zero;
if (searchOptions.useSearchBox) searchOptions.GetSearchBoxBounds();
InitAndResetLodParentsCount();
}
public void AbortAndClearMeshCombineJobs(bool executeAbortEvent = true)
{
foreach (var meshCombineJob in meshCombineJobs)
{
meshCombineJob.abort = true;
meshCombineJob.meshCombiner.isCombining = false;
}
ClearMeshCombineJobs(executeAbortEvent);
}
public void ClearMeshCombineJobs(bool executeAbortEvent = true)
{
#if MCSCaves
if (removeOverlappingTriangles)
{
CreateOverlapColliders.DestroyOverlapColliders(overlappingCollidersGO);
if (overlappingNonCombineGO && disableOverlappingNonCombineGO) overlappingNonCombineGO.SetActive(false);
}
#endif
meshCombineJobs.Clear();
totalMeshCombineJobs = 0;
if (executeAbortEvent && onCombiningAbort != null) onCombiningAbort(this);
}
public void AddObjects(Transform rootT, List<Transform> transforms, bool useSearchOptions, bool checkForLODGroups = true)
{
List<LODGroup> lodGroups = new List<LODGroup>();
if (checkForLODGroups)
{
for (int i = 0; i < transforms.Count; i++)
{
LODGroup lodGroup = transforms[i].GetComponent<LODGroup>();
if (lodGroup != null) lodGroups.Add(lodGroup);
}
if (lodGroups.Count > 0) AddLodGroups(rootT, lodGroups.ToArray(), useSearchOptions);
}
AddTransforms(rootT, transforms.ToArray(), useSearchOptions);
}
public void AddObjectsAutomatically(bool useSearchConditions = true)
{
InitData();
Reset();
AddObjectsFromSearchParent(useSearchConditions);
if (combineMode == CombineMode.DynamicObjects && data.foundLodObjects.Count > 0)
{
Debug.Log("(MeshCombineStudio) => Lod Groups don't work yet for dynamic objects (they only work on static objects), this feature will be added in the next update.");
data.foundLodObjects.Clear();
return;
}
AddFoundObjectsToOctree();
if (octreeContainsObjects)
{
octree.SortObjects(this);
CombineCondition.MakeFoundReport(foundCombineConditions);
cellCount = ObjectOctree.MaxCell.maxCellCount;
}
if (Console.instance != null) LogOctreeInfo();
}
public void AddFoundObjectsToOctree()
{
var foundObjects = data.foundObjects;
var foundLodObjects = data.foundLodObjects;
if (foundObjects.Count > 0 || foundLodObjects.Count > 0) octreeContainsObjects = true;
else
{
Debug.Log("(MeshCombineStudio) => No matching GameObjects with chosen search options are found for combining.");
return;
}
CalcOctreeSize(bounds);
ObjectOctree.MaxCell.maxCellCount = 0;
for (int i = 0; i < foundObjects.Count; i++)
{
CachedGameObject foundObject = foundObjects[i];
Vector3 position = (searchOptions.objectCenter == ObjectCenter.TransformPosition ? foundObject.t.position : foundObject.mr.bounds.center);
octree.AddObject(position, this, foundObject, 0, 0);
}
for (int i = 0; i < foundLodObjects.Count; i++)
{
CachedLodGameObject cachedLodGO = foundLodObjects[i];
octree.AddObject(cachedLodGO.center, this, cachedLodGO, cachedLodGO.lodCount, cachedLodGO.lodLevel);
}
}
// ==========================================================================================================================
public void ResetOctree()
{
// Debug.Log("ResetOctree");
octreeContainsObjects = false;
if (octree == null) { octree = new ObjectOctree.Cell(); return; }
BaseOctree.Cell[] cells = octree.cells;
octree.Reset(ref cells);
}
// ==========================================================================================================================
public void CalcOctreeSize(Bounds bounds)
{
float size;
int levels;
Methods.SnapBoundsAndPreserveArea(ref bounds, cellSize, combineMode == CombineMode.StaticObjects ? cellOffset : Vector3.zero);
if (combineMode == CombineMode.StaticObjects)
{
float areaSize = Mathf.Max(Mathw.GetMax(bounds.size), cellSize);
levels = Mathf.CeilToInt(Mathf.Log(areaSize / cellSize, 2));
size = (int)Mathf.Pow(2, levels) * cellSize;
}
else
{
size = Mathw.GetMax(bounds.size);
levels = 0;
}
if (levels == 0 && octree is ObjectOctree.Cell) octree = new ObjectOctree.MaxCell();
else if (levels > 0 && octree is ObjectOctree.MaxCell) octree = new ObjectOctree.Cell();
octree.maxLevels = levels;
octree.bounds = new Bounds(bounds.center, new Vector3(size, size, size));
// Debug.Log("size " + size + " levels " + levels);
}
// ==========================================================================================================================
public void ApplyChanges()
{
validRebakeLighting = rebakeLighting && !validCopyBakedLighting && !Application.isPlaying && Application.isEditor;
for (int i = 0; i < changedCells.Count; i++)
{
ObjectOctree.MaxCell maxCell = changedCells[i];
maxCell.hasChanged = false;
maxCell.ApplyChanges(this);
}
changedCells.Clear();
}
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
public void CombineAll(bool useSearchConditions = true)
{
if (instantiatePrefab == null)
{
Debug.LogError("(MeshCombineStudio) => The `Custom Combined GameObject` is null. Make sure it's assigned in the 'Use Custom Combine GameObject` setting");
return;
}
if (!combineConditionSettings.sameMaterial && combineConditionSettings.material == null)
{
Debug.LogError("(MeshCombineStudio) => You need to assign an output material in 'Combine Conditions' => 'Change Materials'. Keep in mind with this setting you ignore the source materials and combine all meshes into 1 output material.");
return;
}
if (onCombiningStart != null) onCombiningStart(this);
if (removeBackFaceTriangles && backFaceTriangleMode == BackFaceTriangleMode.Transform)
{
if (backFaceT == null)
{
Debug.LogError("(MeshCombineStudio) => You need to assign the BackFace Transform in 'Output Settings'.");
return;
}
backFaceDirection = backFaceT.forward;
}
InitMeshCombineJobManager();
isCombining = true;
stopwatch.Reset();
stopwatch.Start();
#if MCSCaves
RemoveOverlappingTris.triangles.Clear();
#endif
addMeshCollidersList.Clear();
unreadableMeshes.Clear();
selectImportSettingsMeshes.Clear();
AddObjectsAutomatically(useSearchConditions);
if (!octreeContainsObjects) return;
// SetOriginalCollidersActive(false);
#if MCSCaves
if (removeOverlappingTriangles)
{
if (overlappingNonCombineGO) overlappingNonCombineGO.SetActive(true);
if (CreateOverlapColliders.IsAnythingOnFreeLayers(voxelizeLayer, lodGroupLayer)) return;
if (!CreateOverlapColliders.Create(transform, overlapLayerMask, lodGroupLayer, ref overlappingCollidersGO, removeSamePositionTriangles))
{
Debug.LogError("(MeshCombineStudio) => You need to select at least 1 layer in the `Overlap LayerMask` setting.");
return;
}
}
#endif
validRebakeLighting = rebakeLighting && !validCopyBakedLighting && !Application.isPlaying && Application.isEditor;
newTotalVertices = newTotalTriangles = originalTotalVertices = originalTotalTriangles = originalDrawCalls = newDrawCalls = 0;
originalTotalNormalChannels = originalTotalTangentChannels = originalTotalUvChannels = originalTotalUv2Channels = originalTotalUv3Channels = originalTotalUv4Channels = originalTotalColorChannels = 0;
newTotalNormalChannels = newTotalTangentChannels = newTotalUvChannels = newTotalUv2Channels = newTotalUv3Channels = newTotalUv4Channels = newTotalColorChannels = 0;
for (int i = 0; i < lodParentHolders.Length; i++)
{
LodParentHolder lodParentHolder = lodParentHolders[i];
if (!lodParentHolder.found) continue;
if (lodParentHolder.go == null && combineMode != CombineMode.DynamicObjects) lodParentHolder.Create(this, i);
octree.CombineMeshes(this, i);
}
if (MeshCombineJobManager.instance.jobSettings.combineJobMode == MeshCombineJobManager.CombineJobMode.CombineAtOnce) MeshCombineJobManager.instance.ExecuteJobs();
#if UNITY_EDITOR
if (!Application.isPlaying) UnityEditor.EditorUtility.SetDirty(this);
#endif
}
// ==========================================================================================================================
void InitAndResetLodParentsCount()
{
for (int i = 0; i < lodParentHolders.Length; i++)
{
if (lodParentHolders[i].lods == null || lodParentHolders[i].lods.Length != i + 1) lodParentHolders[i].Init(i + 1);
else lodParentHolders[i].Reset();
}
}
public void AddObjectsFromSearchParent(bool useSearchConditions)
{
if (searchOptions.parentGOs == null || searchOptions.parentGOs.Length == 0)
{
Debug.Log("(MeshCombineStudio) => You need to assign at least one Parent GameObject to 'Search Parents' in which meshes will be searched");
return;
}
GameObject[] parentGOs = searchOptions.parentGOs;
for (int i = 0; i < parentGOs.Length; i++)
{
GameObject parentGO = parentGOs[i];
if (parentGO == null) continue;
Transform parentT = parentGO.transform;
LODGroup[] lodGroups = parentGO.GetComponentsInChildren<LODGroup>(true);
AddLodGroups(parentT, lodGroups);
Transform[] transforms = parentGO.GetComponentsInChildren<Transform>(true);
AddTransforms(parentT, transforms, useSearchConditions);
}
var foundObjects = data.foundObjects;
var foundLodGroups = data.foundLodGroups;
var foundColliders = data.foundColliders;
var colliderLookup = data.colliderLookup;
var lodGroupLookup = data.lodGroupLookup;
if (addMeshColliders)
{
for (int i = 0; i < foundObjects.Count; i++)
{
Collider[] colliders = foundObjects[i].go.GetComponentsInChildren<Collider>(false);
for (int j = 0; j < colliders.Length; j++)
{
Collider collider = colliders[j];
if (colliderLookup.ContainsKey(collider)) continue;
foundColliders.Add(collider);
colliderLookup.Add(collider, foundObjects[i]);
}
}
for (int i = 0; i < foundLodGroups.Count; i++)
{
LODGroup lodGroup = foundLodGroups[i];
Collider[] colliders = foundLodGroups[i].gameObject.GetComponentsInChildren<Collider>(false);
for (int j = 0; j < colliders.Length; j++)
{
Collider collider = colliders[j];
if (colliderLookup.ContainsKey(collider)) continue;
foundColliders.Add(collider);
colliderLookup.Add(collider, lodGroupLookup[lodGroup]);
}
}
}
}
// ==========================================================================================================================
void CheckForFoundObjectNotOnOverlapLayerMask(GameObject go)
{
if (!Methods.IsLayerInLayerMask(overlapLayerMask, go.layer))
{
Debug.LogError("(MeshCombineStudio) => " + go.name + " on layer " + LayerMask.LayerToName(go.layer) + " is not part of the Overlap LayerMask", go);
}
}
void AddLodGroups(Transform searchParentT, LODGroup[] lodGroups, bool useSearchOptions = true)
{
List<CachedLodGameObject> cachedLodRenderers = new List<CachedLodGameObject>();
CachedGameObject cachedGODummy = null;
for (int i = 0; i < lodGroups.Length; i++)
{
LODGroup lodGroup = lodGroups[i];
bool validLodGroup;
if (searchOptions.lodGroupSearchMode == SearchOptions.LODGroupSearchMode.LodGroup) validLodGroup = (ValidObject(searchParentT, lodGroup.transform, ObjectType.LodGroup, useSearchOptions, ref cachedGODummy) == 1);
else
{
if (searchOptions.onlyActive && !lodGroup.gameObject.activeInHierarchy) continue;
validLodGroup = true;
}
LOD[] lods = lodGroup.GetLODs();
int lodParentIndex = lods.Length - 1;
if (lodParentIndex <= 0) continue;
if (i == 0) lodGroupsSettings[lodParentIndex].CopyFromLodGroup(lodGroup, lods);
// Debug.Log(lods.Length);
Vector3 center = Vector3.zero;
int rendererCount = 0;
for (int j = 0; j < lods.Length; j++)
{
LOD lod = lods[j];
for (int k = 0; k < lod.renderers.Length; k++)
{
Renderer r = lod.renderers[k];
if (!r) continue;
if (validLodGroup)
{
CachedGameObject cachedGO = null;
int result = ValidObject(searchParentT, r.transform, ObjectType.LodRenderer, useSearchOptions, ref cachedGO);
if (result == -1) continue;
else if (result == -2)
{
cachedLodRenderers.Clear();
goto breakLoop;
}
if (removeOverlappingTriangles && reportFoundObjectsNotOnOverlapLayerMask)
{
CheckForFoundObjectNotOnOverlapLayerMask(cachedGO.go);
}
cachedLodRenderers.Add(new CachedLodGameObject(cachedGO, lodParentIndex, j));
if (searchOptions.objectCenter == ObjectCenter.BoundsCenter)
{
center += cachedGO.mr.bounds.center;
rendererCount++;
}
}
uniqueLodObjects.Add(r.transform);
}
}
breakLoop:
if (cachedLodRenderers.Count > 0)
{
if (searchOptions.objectCenter == ObjectCenter.BoundsCenter) center /= rendererCount;
else center = lodGroup.transform.position;
var foundLodObjects = data.foundLodObjects;
for (int j = 0; j < cachedLodRenderers.Count; j++)
{
CachedLodGameObject cachedLodGO = cachedLodRenderers[j];
if (j == 0)
{
data.lodGroupLookup[lodGroup] = cachedLodGO;
}
cachedLodGO.center = center;
if (!hasFoundFirstObject) { bounds.center = cachedLodGO.mr.bounds.center; hasFoundFirstObject = true; }
bounds.Encapsulate(cachedLodGO.mr.bounds);
foundLodObjects.Add(cachedLodGO);
lodParentHolders[lodParentIndex].found = true;
lodParentHolders[lodParentIndex].lods[cachedLodGO.lodLevel]++;
}
uniqueFoundLodGroups.Add(lodGroup);
cachedLodRenderers.Clear();
}
}
data.foundLodGroups = new List<LODGroup>(uniqueFoundLodGroups);
}
void AddTransforms(Transform searchParentT, Transform[] transforms, bool useSearchConditions = true)
{
int uniqueLodObjectsCount = uniqueLodObjects.Count;
var foundObjects = data.foundObjects;
for (int i = 0; i < transforms.Length; i++)
{
Transform t = transforms[i];
if (uniqueLodObjectsCount > 0 && uniqueLodObjects.Contains(t)) continue;
CachedGameObject cachedGO = null;
if (ValidObject(searchParentT, t, ObjectType.Normal, useSearchConditions, ref cachedGO) == 1)
{
if (removeOverlappingTriangles && reportFoundObjectsNotOnOverlapLayerMask)
{
CheckForFoundObjectNotOnOverlapLayerMask(cachedGO.go);
}
if (!hasFoundFirstObject) { bounds.center = cachedGO.mr.bounds.center; hasFoundFirstObject = true; }
bounds.Encapsulate(cachedGO.mr.bounds);
foundObjects.Add(cachedGO);
lodParentHolders[0].lods[0]++;
}
}
if (foundObjects.Count > 0) lodParentHolders[0].found = true;
// Debug.Log("Count " + count);
// Debug.Log(foundObjects.Count);
}
// ==========================================================================================================================
int ValidObject(Transform searchParentT, Transform t, ObjectType objectType, bool useSearchOptions, ref CachedGameObject cachedGameObject)
{
if (t == null) return -1;
GameObject go = t.gameObject;
MeshRenderer mr = null;
MeshFilter mf = null;
Mesh mesh = null;
if (objectType != ObjectType.LodGroup || searchOptions.lodGroupSearchMode == SearchOptions.LODGroupSearchMode.LodRenderers)
{
mr = t.GetComponent<MeshRenderer>();
if (mr == null || (!mr.enabled && searchOptions.onlyActiveMeshRenderers)) return -1;
mf = t.GetComponent<MeshFilter>();
if (mf == null) return -1;
mesh = mf.sharedMesh;
if (mesh == null) return -1;
if (mesh.vertexCount > 65534) return -2;
}
if (useSearchOptions)
{
if (searchOptions.onlyActive && !go.activeInHierarchy) return -1;
if (objectType != ObjectType.LodRenderer || searchOptions.lodGroupSearchMode == SearchOptions.LODGroupSearchMode.LodRenderers)
{
if (searchOptions.useLayerMask)
{
int layer = 1 << t.gameObject.layer;
if ((searchOptions.layerMask.value & layer) != layer) return -1;
}
#if UNITY_EDITOR
// Debug.Log(go.name + " " + (int)UnityEditor.GameObjectUtility.GetStaticEditorFlags(go) + " " + searchOptions.editorStatic);
if (searchOptions.onlyStatic && !UnityEditor.GameObjectUtility.AreStaticEditorFlagsSet(go, searchOptions.editorStatic)) return -1;
#else
if (searchOptions.onlyStatic && !go.isStatic) return -1;
#endif
if (searchOptions.useTag)
{
if (!t.CompareTag(searchOptions.tag)) return -1;
}
if (searchOptions.useComponentsFilter)
{
if (searchOptions.componentCondition == SearchOptions.ComponentCondition.And)
{
bool pass = true;
for (int j = 0; j < searchOptions.componentNameList.Count; j++)
{
if (t.GetComponent(searchOptions.componentNameList[j]) == null) { pass = false; break; }
}
if (!pass) return -1;
}
else if (searchOptions.componentCondition == SearchOptions.ComponentCondition.Or)
{
bool pass = false;
for (int j = 0; j < searchOptions.componentNameList.Count; j++)
{
if (t.GetComponent(searchOptions.componentNameList[j]) != null) { pass = true; break; }
}
if (!pass) return -1;
}
else
{
bool pass = true;
for (int j = 0; j < searchOptions.componentNameList.Count; j++)
{
if (t.GetComponent(searchOptions.componentNameList[j]) != null) { pass = false; break; }
}
if (!pass) return -1;
}
}
if (searchOptions.useNameContains)
{
bool found = false;
for (int k = 0; k < searchOptions.nameContainList.Count; k++)
{
if (Methods.Contains(t.name, searchOptions.nameContainList[k])) { found = true; break; }
}
if (!found) return -1;
}
if (searchOptions.useSearchBox)
{
if (searchOptions.objectCenter == ObjectCenter.BoundsCenter)
{
if (!searchOptions.searchBoxBounds.Contains(mr.bounds.center)) return -2;
}
else if (!searchOptions.searchBoxBounds.Contains(t.position)) return -2;
}
}
if (objectType != ObjectType.LodGroup)
{
if (searchOptions.useVertexInputLimit && mesh.vertexCount > searchOptions.vertexInputLimit) return -2;
if (useVertexOutputLimit && mesh.vertexCount > vertexOutputLimit) return -2;
if (searchOptions.useMaxBoundsFactor && combineMode == CombineMode.StaticObjects)
{
if (Mathw.GetMax(mr.bounds.size) > cellSize * searchOptions.maxBoundsFactor) return -2;
}
}
}
if ((objectType != ObjectType.LodGroup || searchOptions.lodGroupSearchMode == SearchOptions.LODGroupSearchMode.LodRenderers) && !mesh.isReadable)
{
if (unreadableMeshes.Add(mesh))
{
Debug.LogError("(MeshCombineStudio) => Read/Write is disabled on the mesh on GameObject " + go.name + " and can't be combined. Click the 'Make Meshes Readable' in the MCS Inspector to make it automatically readable in the mesh import settings.");
}
return -1;
}
if (objectType != ObjectType.LodGroup)
{
cachedGameObject = new CachedGameObject(searchParentT, go, t, mr, mf, mesh);
}
return 1;
}
public void RestoreOriginalRenderersAndLODGroups(bool onDestroy)
{
if (activeOriginal) return;
ExecuteHandleObjects(true, HandleComponent.Disable, HandleComponent.Disable, true, onDestroy);
}
public void SwapCombine()
{
if (!combined) { CombineAll(); }
else
{
combinedActive = !combinedActive;
ExecuteHandleObjects(!combinedActive, originalMeshRenderers, originalLODGroups);
}
}
void SetOriginalCollidersActive(bool active, bool onDestroy)
{
if (data == null && !onDestroy) InitData();
if (data == null) return;
var foundColliders = data.foundColliders;
for (int i = 0; i < foundColliders.Count; i++)
{
Collider collider = foundColliders[i];
if (collider)
{
CachedGameObject cachedGO;
data.colliderLookup.TryGetValue(collider, out cachedGO);
if (cachedGO == null || !cachedGO.excludeCombine) collider.enabled = active;
else Methods.ListRemoveAt(foundColliders, i--);
}
else Methods.ListRemoveAt(foundColliders, i--);
}
}
void ExecuteMeshFilter(bool active, CachedGameObject cachedGO)
{
if (active)
{
if (cachedGO.mfr) cachedGO.mfr.RevertMeshFilter(cachedGO.mf);
}
else
{
var mfr = cachedGO.go.AddComponent<MeshFilterRevert>();
if (mfr.DestroyAndReferenceMeshFilter(cachedGO.mf))
{
cachedGO.mfr = mfr;
}
else Methods.Destroy(mfr);
}
}
public void ExecuteHandleObjects(bool active, HandleComponent handleOriginalObjects, HandleComponent handleOriginalLodGroups, bool includeColliders = true, bool onDestroy = false)
{
activeOriginal = active;
Methods.SetChildrenActive(transform, !active);
List<CachedGameObject> foundObjects;
List<CachedLodGameObject> foundLodObjects;
List<LODGroup> foundLodGroups;
List<Collider> foundColliders;
bool useRemoveOriginalMeshReference = !Application.isPlaying && (removeOriginalMeshReference || usedRemoveOriginalMeshRederences);
if (!active)
{
usedRemoveOriginalMeshRederences = useRemoveOriginalMeshReference;
}
else usedRemoveOriginalMeshRederences = false;
if (onDestroy)
{
foundObjects = this.foundObjects;
foundLodObjects = this.foundLodObjects;
foundLodGroups = this.foundLodGroups;
foundColliders = this.foundColliders;
}
else
{
InitData();
if (data == null) return;
foundObjects = data.foundObjects;
foundLodObjects = data.foundLodObjects;
foundLodGroups = data.foundLodGroups;
foundColliders = data.foundColliders;
}
if (handleOriginalObjects == HandleComponent.Disable)
{
if (includeColliders) SetOriginalCollidersActive(active, onDestroy);
for (int i = 0; i < foundObjects.Count; i++)
{
CachedGameObject cachedGO = foundObjects[i];
if (cachedGO.mr && !cachedGO.excludeCombine)
{
cachedGO.mr.enabled = (cachedGO.mrEnabled & active);
if (active) cachedGO.go.hideFlags = HideFlags.None;
else if (useOriginalObjectsHideFlags) cachedGO.go.hideFlags = orginalObjectsHideFlags;
if (useRemoveOriginalMeshReference) ExecuteMeshFilter(active, cachedGO);
}
else Methods.ListRemoveAt(foundObjects, i--);
}
for (int i = 0; i < foundLodObjects.Count; i++)
{
CachedLodGameObject cachedLodGO = foundLodObjects[i];
if (cachedLodGO.mr && !cachedLodGO.excludeCombine)
{
cachedLodGO.mr.enabled = (cachedLodGO.mrEnabled & active);
if (useRemoveOriginalMeshReference) ExecuteMeshFilter(active, cachedLodGO);
}
else Methods.ListRemoveAt(foundLodObjects, i--);
}
}
if (handleOriginalObjects == HandleComponent.Destroy)
{
for (int i = 0; i < foundColliders.Count; i++)
{
Collider collider = foundColliders[i];
if (collider)
{
CachedGameObject cachedGO;
data.colliderLookup.TryGetValue(collider, out cachedGO);
if (cachedGO == null || !cachedGO.excludeCombine) Destroy(collider);
else Methods.ListRemoveAt(foundColliders, i--);
}
else Methods.ListRemoveAt(foundColliders, i--);
}
for (int i = 0; i < foundObjects.Count; i++)
{
bool remove = false;
CachedGameObject cachedGO = foundObjects[i];
if (!cachedGO.excludeCombine)
{
if (cachedGO.mf) Destroy(cachedGO.mf);
else remove = true;
if (cachedGO.mr) Destroy(cachedGO.mr);
else remove = true;
}
else remove = true;
if (remove) Methods.ListRemoveAt(foundObjects, i--);
}
for (int i = 0; i < foundLodObjects.Count; i++)
{
bool remove = false;
CachedGameObject cachedGO = foundLodObjects[i];
if (!cachedGO.excludeCombine)
{
if (cachedGO.mf) Destroy(cachedGO.mf);
else remove = true;
if (cachedGO.mr) Destroy(cachedGO.mr);
else remove = true;
}
else remove = true;
if (remove) Methods.ListRemoveAt(foundLodObjects, i--);
}
}
for (int i = 0; i < foundLodGroups.Count; i++)
{
LODGroup lodGroup = foundLodGroups[i];
if (lodGroup)
{
CachedGameObject cachedGO;
data.lodGroupLookup.TryGetValue(lodGroup, out cachedGO);
if (cachedGO == null || !cachedGO.excludeCombine)
{
if (active) lodGroup.gameObject.hideFlags = HideFlags.None;
else if (useOriginalObjectsHideFlags) lodGroup.gameObject.hideFlags = orginalObjectsHideFlags;
if (handleOriginalLodGroups == HandleComponent.Disable) lodGroup.enabled = active; else Destroy(lodGroup);
}
else Methods.ListRemoveAt(foundLodGroups, i--);
}
else Methods.ListRemoveAt(foundLodGroups, i--);
}
}
void DrawGizmosCube(Bounds bounds, Color color)
{
Gizmos.color = color;
Gizmos.DrawWireCube(bounds.center, bounds.size);
Gizmos.color = new Color(color.r, color.g, color.b, 0.5f);
Gizmos.DrawCube(bounds.center, bounds.size);
Gizmos.color = Color.white;
}
void OnDrawGizmosSelected()
{
if (addMeshColliders && addMeshCollidersInRange)
{
DrawGizmosCube(addMeshCollidersBounds, Color.green);
}
if (removeBackFaceTriangles)
{
if (backFaceTriangleMode == BackFaceTriangleMode.Box)
{
DrawGizmosCube(backFaceBounds, Color.blue);
}
}
if (!drawGizmos) return;
if (octree != null && octreeContainsObjects)
{
octree.Draw(this, true, !searchOptions.useSearchBox);
}
if (searchOptions.useSearchBox)
{
searchOptions.GetSearchBoxBounds();
Gizmos.color = Color.green;
Gizmos.DrawWireCube(searchOptions.searchBoxBounds.center, searchOptions.searchBoxBounds.size);
Gizmos.color = Color.white;
}
}
// ==========================================================================================================================
void LogOctreeInfo()
{
Console.Log("Cells " + ObjectOctree.MaxCell.maxCellCount + " -> Found Objects: ");
LodParentHolder[] lodParentsCount = lodParentHolders;
if (lodParentsCount == null || lodParentsCount.Length == 0) return;
for (int i = 0; i < lodParentsCount.Length; i++)
{
LodParentHolder lodParentCount = lodParentsCount[i];
if (!lodParentCount.found) continue;
string text = "";
text = "LOD Group " + (i + 1) + " |";
int[] lods = lodParentCount.lods;
for (int j = 0; j < lods.Length; j++)
{
text += " " + lods[j].ToString() + " |";
}
Console.Log(text);
}
}
[Serializable]
public class LODGroupSettings
{
public bool animateCrossFading;
public LODFadeMode fadeMode;
public LODSettings[] lodSettings;
public LODGroupSettings(int lodParentIndex)
{
int lodCount = lodParentIndex + 1;
lodSettings = new LODSettings[lodCount];
float percentage = 1.0f / lodCount;
for (int i = 0; i < lodSettings.Length; i++)
{
lodSettings[i] = new LODSettings(1 - (percentage * (i + 1)));
}
}
public void CopyFromLodGroup(LODGroup lodGroup, LOD[] lods)
{
animateCrossFading = lodGroup.animateCrossFading;
fadeMode = lodGroup.fadeMode;
for (int i = 0; i < lods.Length; i++)
{
lodSettings[i].fadeTransitionWidth = lods[i].fadeTransitionWidth;
}
}
public void CopyToLodGroup(LODGroup lodGroup, LOD[] lods)
{
lodGroup.animateCrossFading = animateCrossFading;
lodGroup.fadeMode = fadeMode;
for (int i = 0; i < lods.Length; i++)
{
lods[i].fadeTransitionWidth = lodSettings[i].fadeTransitionWidth;
}
}
}
[Serializable]
public class LODSettings
{
public float screenRelativeTransitionHeight;
public float fadeTransitionWidth;
public LODSettings(float screenRelativeTransitionHeight)
{
this.screenRelativeTransitionHeight = screenRelativeTransitionHeight;
}
}
[Serializable]
public class LodParentHolder
{
public GameObject go;
public Transform t;
public bool found;
public int[] lods;
public void Init(int lodCount)
{
lods = new int[lodCount];
}
public void Create(MeshCombiner meshCombiner, int lodParentIndex)
{
if (meshCombiner.data.foundLodGroups.Count == 0)
{
go = new GameObject(meshCombiner.combineMode == CombineMode.StaticObjects ? "Cells" : "Combine Parent");
}
else
{
go = new GameObject("LODGroup " + (lodParentIndex + 1));
var lodGroupSetup = go.AddComponent<LODGroupSetup>();
lodGroupSetup.Init(meshCombiner, lodParentIndex);
}
t = go.transform;
Transform parentT = t.transform;
parentT.parent = meshCombiner.transform;
}
public void Reset()
{
found = false;
Array.Clear(lods, 0, lods.Length);
}
}
}
public struct MeshColliderAdd
{
public GameObject go;
public Mesh mesh;
public MeshColliderAdd(GameObject go, Mesh mesh)
{
this.go = go;
this.mesh = mesh;
}
}
}