722 lines
37 KiB
C#

#if UNITY_EDITOR
using UnityEditor;
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace MTAssets.EasyMeshCombiner
{
/*
This class is responsible for the functioning of the "Runtime Mesh Combiner" component, and all its functions.
*/
/*
* The Easy Mesh Combiner was developed by Marcos Tomaz in 2019.
* Need help? Contact me (mtassets@windsoft.xyz)
*/
[AddComponentMenu("MT Assets/Easy Mesh Combiner/Runtime Mesh Combiner")] //Add this component in a category of addComponent menu
public class RuntimeMeshCombiner : MonoBehaviour
{
//Private constants
private int MAX_VERTICES_FOR_16BITS_MESH = 50000; //Not change this
//Classes of script
private class GameObjectWithMesh
{
//Class that stores a valid gameobject that contains a mesh
public GameObject gameObject;
public MeshFilter meshFilter;
public MeshRenderer meshRenderer;
public GameObjectWithMesh(GameObject gameObject, MeshFilter meshFilter, MeshRenderer meshRenderer)
{
this.gameObject = gameObject;
this.meshFilter = meshFilter;
this.meshRenderer = meshRenderer;
}
}
private class OriginalGameObjectWithMesh
{
//Class that stores a original GameObject With Mesh data, to restore on undo merge.
public GameObject gameObject;
public bool originalGoState;
public MeshRenderer meshRenderer;
public bool originalMrState;
public OriginalGameObjectWithMesh(GameObject gameObject, bool originalGoState, MeshRenderer meshRenderer, bool originalMrState)
{
this.gameObject = gameObject;
this.originalGoState = originalGoState;
this.meshRenderer = meshRenderer;
this.originalMrState = originalMrState;
}
}
private class SubMeshToCombine
{
//Class that stores a mesh filter/renderer and respective submesh index, to combine
public Transform transform;
public MeshFilter meshFilter;
public MeshRenderer meshRenderer;
public int subMeshIndex;
public SubMeshToCombine(Transform transform, MeshFilter meshFilter, MeshRenderer meshRenderer, int subMeshIndex)
{
this.transform = transform;
this.meshFilter = meshFilter;
this.meshRenderer = meshRenderer;
this.subMeshIndex = subMeshIndex;
}
}
//Enums of script
public enum CombineOnStart
{
Disabled,
OnStart,
OnAwake
}
public enum AfterMerge
{
DisableOriginalMeshes,
DeactiveOriginalGameObjects,
DoNothing
}
//Variables of script
private Vector3 originalPosition = Vector3.zero;
private Vector3 originalEulerAngles = Vector3.zero;
private Vector3 originalScale = Vector3.zero;
private List<OriginalGameObjectWithMesh> originalGameObjectsWithMeshToRestore = new List<OriginalGameObjectWithMesh>();
private bool targetMeshesMerged = false;
//Variables of merge
[HideInInspector]
public AfterMerge afterMerge;
[HideInInspector]
public bool addMeshColliderAfter = true;
[HideInInspector]
public CombineOnStart combineMeshesAtStartUp = CombineOnStart.Disabled;
[HideInInspector]
public bool combineInChildren = false;
[HideInInspector]
public bool combineInactives = false;
[HideInInspector]
public bool recalculateNormals = true;
[HideInInspector]
public bool recalculateTangents = true;
[HideInInspector]
public bool optimizeResultingMesh = false;
[HideInInspector]
public List<GameObject> targetMeshes = new List<GameObject>();
[HideInInspector]
public bool showDebugLogs = true;
[HideInInspector]
public bool garbageCollectorAfterUndo = true;
public UnityEvent onDoneMerge;
public UnityEvent onDoneUnmerge;
//The UI of this component
#if UNITY_EDITOR
//Private variables of Interface
private bool gizmosOfThisComponentIsDisabled = false;
#region INTERFACE_CODE
[UnityEditor.CustomEditor(typeof(RuntimeMeshCombiner))]
public class CustomInspector : UnityEditor.Editor
{
//Private temp variables
public Vector2 targetMeshes_ScrollPos;
public override void OnInspectorGUI()
{
//Start the undo event support, draw default inspector and monitor of changes
RuntimeMeshCombiner script = (RuntimeMeshCombiner)target;
EditorGUI.BeginChangeCheck();
Undo.RecordObject(target, "Undo Event");
script.gizmosOfThisComponentIsDisabled = MTAssetsEditorUi.DisableGizmosInSceneView("RuntimeMeshCombiner", script.gizmosOfThisComponentIsDisabled);
//Support reminder
GUILayout.Space(10);
EditorGUILayout.HelpBox("Remember to read the Easy Mesh Combiner documentation to understand how to use it.\nGet support at: mtassets@windsoft.xyz", MessageType.None);
//Check if the component already have a Mesh Renderer and Mesh Filter components, if already have, show a warning
MeshFilter meshFilter = script.GetComponent<MeshFilter>();
MeshRenderer meshRenderer = script.GetComponent<MeshRenderer>();
if (script.isTargetMeshesMerged() == false)
if (meshFilter != null || meshRenderer != null)
{
GUILayout.Space(10);
EditorGUILayout.HelpBox("It looks like this component already contains a Mesh Filter and/or Mesh Renderer component. The Runtime Mesh Combiner will save the mesh resulting from the merge in this GameObject and therefore it needs a GameObject that does not contain either of these two components. Please remove the Mesh Filter and Mesh Renderer from this GameObject or place the Runtime Mesh Combiner in a new empty GameObject, otherwise the API will not allow merges to be done.", MessageType.Error);
}
//Start of preferences
GUILayout.Space(10);
EditorGUILayout.LabelField("Preferences", EditorStyles.boldLabel);
GUILayout.Space(10);
script.afterMerge = (AfterMerge)EditorGUILayout.EnumPopup(new GUIContent("After Combine",
"What do you do after you complete the merge?\n\nDisable Original Meshes - The original meshes will be deactivated, so all the colliders and other components of the scenario will be kept intact, but the meshes will still be combined!\n\nDeactive Original GameObjects - All original GameObjects will be disabled. When you do not need to keep colliders and other active components in the scene, this is a good option!\n\nRelax, however, it is possible to undo the merge later and re-activate everything again!"),
script.afterMerge);
if (script.afterMerge == AfterMerge.DeactiveOriginalGameObjects)
{
EditorGUI.indentLevel += 1;
script.addMeshColliderAfter = (bool)EditorGUILayout.Toggle(new GUIContent("Add Mesh Collider After",
"Add Mesh Collider to the merge mesh after combining?"),
script.addMeshColliderAfter);
EditorGUI.indentLevel -= 1;
}
script.combineMeshesAtStartUp = (CombineOnStart)EditorGUILayout.EnumPopup(new GUIContent("Combine On Start",
"Here you can enable or disable automatic meshing of meshes, right at the beginning of the execution of this scene in your game." +
"\n\nDisabled - Auto merge will not be performed." +
"\n\nOnAwake - Merging will take place in your game's Awake. Awake is executed before all the Start methods in your scene." +
"\n\nOnStart - The merging will be done at the Start of your game."),
script.combineMeshesAtStartUp);
script.combineInChildren = EditorGUILayout.Toggle(new GUIContent("Combine Childrens Too",
"If this option is enabled, the EMC will combine the GameObjects children of the registered GameObjects for merging, too!"),
script.combineInChildren);
script.combineInactives = EditorGUILayout.Toggle(new GUIContent("Combine Inactives Too",
"If this option is active, the EMC will combine inactive GameObjects as well, even if they are registered in the list of meshes to be merged."),
script.combineInactives);
script.recalculateNormals = EditorGUILayout.Toggle(new GUIContent("Recalculate Normals",
"Enable this and the EMC will recalculate the normals of the mesh resulting from the merge. The EMC will preserve the normal data for the original fabrics if this is disabled."),
script.recalculateNormals);
script.recalculateTangents = EditorGUILayout.Toggle(new GUIContent("Recalculate Tangents",
"Enable this and the EMC will recalculate the tangents of the mesh resulting from the merge. The EMC will preserve the tangent data for the original meshes if this is disabled."),
script.recalculateTangents);
script.optimizeResultingMesh = EditorGUILayout.Toggle(new GUIContent("Optimize Resulting Mesh",
"If this option is enabled, the Runtime Mesh Combiner will optimize the mesh resulting from the merge. This may lead to performance gains in rendering the mesh resulting from the merging, through the mechanism of Unity.\n\nThis can slightly increase the mesh processing time."),
script.optimizeResultingMesh);
script.garbageCollectorAfterUndo = EditorGUILayout.Toggle(new GUIContent("Run GC After Undo",
"Garbage Collector will free up memory that was used by unnecessary assets after undoing a merge, but this can negatively impact your game performance, depending on the complexity of the mesh combined. Run garbage collector after undoing a merge?"),
script.garbageCollectorAfterUndo);
//Start of target meshes
GUILayout.Space(10);
EditorGUILayout.LabelField("Target Meshes", EditorStyles.boldLabel);
GUILayout.Space(10);
Texture2D removeItemIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/Plugins/MT Assets/Easy Mesh Combiner/Editor/Images/Remove.png", typeof(Texture2D));
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Target Meshes To Merge", GUILayout.Width(145));
GUILayout.Space(MTAssetsEditorUi.GetInspectorWindowSize().x - 145);
EditorGUILayout.LabelField("Size", GUILayout.Width(30));
EditorGUILayout.IntField(script.targetMeshes.Count, GUILayout.Width(50));
EditorGUILayout.EndHorizontal();
GUILayout.BeginVertical("box");
targetMeshes_ScrollPos = EditorGUILayout.BeginScrollView(targetMeshes_ScrollPos, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.Width(MTAssetsEditorUi.GetInspectorWindowSize().x), GUILayout.Height(100));
if (script.targetMeshes.Count == 0)
EditorGUILayout.HelpBox("Oops! No GameObjects with meshes was registered to be combined! If you want to subscribe any, click the button below!", MessageType.Info);
if (script.targetMeshes.Count > 0)
for (int i = 0; i < script.targetMeshes.Count; i++)
{
GUILayout.BeginHorizontal();
if (GUILayout.Button(removeItemIcon, GUILayout.Width(25), GUILayout.Height(16)))
script.targetMeshes.RemoveAt(i);
script.targetMeshes[i] = (GameObject)EditorGUILayout.ObjectField(new GUIContent("GameObject " + i.ToString(), "The mesh found in this GameObject will be combined. Click the button to the left if you want to remove this GameObject from the list."), script.targetMeshes[i], typeof(GameObject), true, GUILayout.Height(16));
GUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
GUILayout.EndVertical();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Add New Slot"))
{
script.targetMeshes.Add(null);
targetMeshes_ScrollPos.y += 999999;
}
if (script.targetMeshes.Count > 0)
if (GUILayout.Button("Remove Empty Slots", GUILayout.Width(Screen.width * 0.48f)))
for (int i = script.targetMeshes.Count - 1; i >= 0; i--)
if (script.targetMeshes[i] == null)
script.targetMeshes.RemoveAt(i);
GUILayout.EndHorizontal();
//Merge Events
GUILayout.Space(10);
EditorGUILayout.LabelField("Merge Events", EditorStyles.boldLabel);
GUILayout.Space(10);
DrawDefaultInspector();
//Start of debug
GUILayout.Space(10);
EditorGUILayout.LabelField("Debug", EditorStyles.boldLabel);
GUILayout.Space(10);
EditorGUILayout.Toggle(new GUIContent("Target Meshes Merged",
"Are the target meshes currently combined?"),
script.isTargetMeshesMerged());
if (script.showDebugLogs == true)
EditorGUILayout.HelpBox("Excessive debug logs can cause performance fluctuations in your game. Just enable this function while debugging your merge and game.", MessageType.Warning);
script.showDebugLogs = EditorGUILayout.Toggle(new GUIContent("Show Debug Logs",
"Debug logs notify you if the combiner encounters any invalid or similar mesh, but excessive debug logs can cause performance fluctuations in your game. View logs for debugging?"),
script.showDebugLogs);
//Final space
GUILayout.Space(10);
//Stop paint of GUI, if this gameobject no more exists
if (script == null)
return;
//Apply changes on script, case is not playing in editor
if (GUI.changed == true && Application.isPlaying == false)
{
EditorUtility.SetDirty(script);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(script.gameObject.scene);
}
if (EditorGUI.EndChangeCheck() == true)
{
}
}
}
#endregion
#endif
//Component code
void Awake()
{
//Combine meshes on start
if (combineMeshesAtStartUp == CombineOnStart.OnAwake)
{
if (showDebugLogs == true)
Debug.Log("The merge started in Runtime Combiner \"" + this.gameObject.name + "\".");
CombineMeshes();
}
}
void Start()
{
//Combine meshes on start
if (combineMeshesAtStartUp == CombineOnStart.OnStart)
{
if (showDebugLogs == true)
Debug.Log("The merge started in Runtime Combiner \"" + this.gameObject.name + "\".");
CombineMeshes();
}
}
GameObjectWithMesh[] GetValidatedTargetGameObjects()
{
//Validate the target gameobjects and return a list of valids GameObjects with mesh
//Get all found gameobjects in targets gameobjects
List<Transform> foundGameObjects = new List<Transform>();
for (int i = 0; i < targetMeshes.Count; i++)
{
//Skips if this target mesh is null
if (targetMeshes[i] == null)
{
continue;
}
if (combineInChildren == true)
{
Transform[] childrenGameObjectsInThis = targetMeshes[i].GetComponentsInChildren<Transform>(true);
foreach (Transform trs in childrenGameObjectsInThis)
if (foundGameObjects.Contains(trs) == false) //<-- Check if this GameObject has already been added to the list before adding it, to avoid duplicates
foundGameObjects.Add(trs);
}
if (combineInChildren == false)
{
Transform thisGameObjectTrs = targetMeshes[i].GetComponent<Transform>();
if (foundGameObjects.Contains(thisGameObjectTrs) == false) //<-- Check if this GameObject has already been added to the list before adding it, to avoid duplicates
foundGameObjects.Add(thisGameObjectTrs);
}
}
//Validate each found gameobject and split gameobjects that contains mesh filter or/and mesh renderer, and add to list of valid gameObjects
List<GameObjectWithMesh> gameObjectsWithMesh = new List<GameObjectWithMesh>();
for (int i = 0; i < foundGameObjects.Count; i++)
{
MeshFilter mf = foundGameObjects[i].GetComponent<MeshFilter>();
MeshRenderer mr = foundGameObjects[i].GetComponent<MeshRenderer>();
if (mf != null || mr != null)
{
//If combine inactives is disabled, and mesh filter component is disabled in this object, skips this
if (combineInactives == false && mr.enabled == false)
continue;
if (combineInactives == false && foundGameObjects[i].gameObject.activeSelf == false)
continue;
if (combineInactives == false && foundGameObjects[i].gameObject.activeInHierarchy == false)
continue;
gameObjectsWithMesh.Add(new GameObjectWithMesh(foundGameObjects[i].gameObject, mf, mr));
}
}
//Verify if each gameObject with mesh, is valid and have correct components settings
List<GameObjectWithMesh> validsGameObjectsWithMesh = new List<GameObjectWithMesh>();
for (int i = 0; i < gameObjectsWithMesh.Count; i++)
{
bool canAddToValidGameObjects = true;
//Verify if MeshFilter is null
if (gameObjectsWithMesh[i].meshFilter == null)
{
if (showDebugLogs == true)
{
Debug.LogError("GameObject \"" + gameObjectsWithMesh[i].gameObject.name + "\" does not have the Mesh Filter component, so it is not a valid mesh and will be ignored in the merge process.");
}
canAddToValidGameObjects = false;
}
//Verify if MeshRenderer is null
if (gameObjectsWithMesh[i].meshRenderer == null)
{
if (showDebugLogs == true)
{
Debug.LogError("GameObject \"" + gameObjectsWithMesh[i].gameObject.name + "\" does not have the Mesh Renderer component, so it is not a valid mesh and will be ignored in the merge process.");
}
canAddToValidGameObjects = false;
}
//Verify if SharedMesh is null
if (gameObjectsWithMesh[i].meshFilter != null && gameObjectsWithMesh[i].meshFilter.sharedMesh == null)
{
if (showDebugLogs == true)
{
Debug.LogError("GameObject \"" + gameObjectsWithMesh[i].gameObject.name + "\" does not have a Mesh in Mesh Filter component, so it is not a valid mesh and will be ignored in the merge process.");
}
canAddToValidGameObjects = false;
}
//Verify if count of materials is different of count of submeshes
if (gameObjectsWithMesh[i].meshFilter != null && gameObjectsWithMesh[i].meshRenderer != null && gameObjectsWithMesh[i].meshFilter.sharedMesh != null)
{
if (gameObjectsWithMesh[i].meshFilter.sharedMesh.subMeshCount != gameObjectsWithMesh[i].meshRenderer.sharedMaterials.Length)
{
if (showDebugLogs == true)
{
Debug.LogError("The Mesh Renderer component found in GameObject \"" + gameObjectsWithMesh[i].gameObject.name + "\" has more or less material needed. The mesh that is in this GameObject has " + gameObjectsWithMesh[i].meshFilter.sharedMesh.subMeshCount.ToString() + " submeshes, but has a number of " + gameObjectsWithMesh[i].meshRenderer.sharedMaterials.Length.ToString() + " materials. This mesh will be ignored during the merge process.");
}
canAddToValidGameObjects = false;
}
}
//Verify if has null materials in MeshRenderer
if (gameObjectsWithMesh[i].meshRenderer != null)
{
for (int x = 0; x < gameObjectsWithMesh[i].meshRenderer.sharedMaterials.Length; x++)
{
if (gameObjectsWithMesh[i].meshRenderer.sharedMaterials[x] == null)
{
if (showDebugLogs == true)
{
Debug.LogError("Material " + x.ToString() + " in Mesh Renderer present in component \"" + gameObjectsWithMesh[i].gameObject.name + "\" is null. For the merge process to work well, all materials must be completed. This GameObject will be ignored in the merge process.");
}
canAddToValidGameObjects = false;
}
}
}
//Verify if this gameobject is already merged
if (gameObjectsWithMesh[i].gameObject.GetComponent<CombinedMeshesManager>() != null)
{
if (showDebugLogs == true)
{
Debug.LogError("GameObject \"" + gameObjectsWithMesh[i].gameObject.name + "\" is the result of a previous merge, so it will be ignored by this merge.");
}
canAddToValidGameObjects = false;
}
//If can add to valid GameObjects, add this gameobject
if (canAddToValidGameObjects == true)
{
validsGameObjectsWithMesh.Add(gameObjectsWithMesh[i]);
}
}
return validsGameObjectsWithMesh.ToArray();
}
//API Methods
public bool CombineMeshes()
{
//If meshes already are merged
if (isTargetMeshesMerged() == true)
{
if (showDebugLogs == true)
Debug.Log("The Runtime Combiner \"" + this.gameObject.name + "\" meshes are already combined!");
return true;
}
//If meshes is not merged
if (isTargetMeshesMerged() == false)
{
//If this component already have a Mesh Filter and/or Mesh Renderer, cancel the merge
if (this.gameObject.GetComponent<MeshFilter>() != null || this.gameObject.GetComponent<MeshRenderer>() != null)
{
if (showDebugLogs == true)
Debug.LogError("Unable to merge. Apparently the GameObject \"" + this.gameObject.name + "\" already contains the Mesh Filter and/or Mesh Renderer component. The Runtime Mesh Combiner needs a GameObject that does not contain these two components. Please remove them or place the Runtime Mesh Combiner in a new GameObject and try again.");
return false;
}
//Get the original position, rotation and scale of this GameObject and reset
originalPosition = this.gameObject.transform.position;
originalEulerAngles = this.gameObject.transform.eulerAngles;
originalScale = this.gameObject.transform.lossyScale;
this.gameObject.transform.position = Vector3.zero;
this.gameObject.transform.eulerAngles = Vector3.zero;
this.gameObject.transform.localScale = Vector3.one;
//Get the GameObjectsWithMesh validated
GameObjectWithMesh[] validsGameObjectsWithMesh = GetValidatedTargetGameObjects();
//Verify if has valid gameObjects
if (validsGameObjectsWithMesh.Length == 0)
{
if (showDebugLogs == true)
Debug.LogError("No valid, meshed GameObjects were found in the target GameObjects list. Therefore the merge was interrupted.");
return false;
}
//Separate each submesh according to your material
Dictionary<Material, List<SubMeshToCombine>> subMeshesPerMaterial = new Dictionary<Material, List<SubMeshToCombine>>();
for (int i = 0; i < validsGameObjectsWithMesh.Length; i++)
{
GameObjectWithMesh thisGoWithMesh = validsGameObjectsWithMesh[i];
for (int x = 0; x < thisGoWithMesh.meshFilter.sharedMesh.subMeshCount; x++)
{
Material currentMaterial = thisGoWithMesh.meshRenderer.sharedMaterials[x];
if (subMeshesPerMaterial.ContainsKey(currentMaterial) == true)
{
subMeshesPerMaterial[currentMaterial].Add(new SubMeshToCombine(thisGoWithMesh.gameObject.transform, thisGoWithMesh.meshFilter, thisGoWithMesh.meshRenderer, x));
}
if (subMeshesPerMaterial.ContainsKey(currentMaterial) == false)
{
subMeshesPerMaterial.Add(currentMaterial, new List<SubMeshToCombine>() { new SubMeshToCombine(thisGoWithMesh.gameObject.transform, thisGoWithMesh.meshFilter, thisGoWithMesh.meshRenderer, x) });
}
}
}
//Configure this GameObject
MeshFilter holderMeshFilter = this.gameObject.AddComponent<MeshFilter>();
MeshRenderer holderMeshRenderer = this.gameObject.AddComponent<MeshRenderer>();
//Count the vertex in valids gameobjects
int vertexCountInValidsGameObjects = 0;
foreach (GameObjectWithMesh obj in validsGameObjectsWithMesh)
{
vertexCountInValidsGameObjects += obj.meshFilter.sharedMesh.vertexCount;
}
//Combine the submeshes into one submesh according the material
List<Mesh> combinedSubmehesPerMaterial = new List<Mesh>();
foreach (var key in subMeshesPerMaterial)
{
//Get the submeshes to merge, of current material
List<SubMeshToCombine> subMeshesOfCurrentMaterial = key.Value;
//Combine instances of submeshes from this material
List<CombineInstance> combineInstancesOfCurrentMaterial = new List<CombineInstance>();
//Process each submesh
for (int i = 0; i < subMeshesOfCurrentMaterial.Count; i++)
{
CombineInstance combineInstance = new CombineInstance();
combineInstance.mesh = subMeshesOfCurrentMaterial[i].meshFilter.sharedMesh;
combineInstance.subMeshIndex = subMeshesOfCurrentMaterial[i].subMeshIndex;
combineInstance.transform = subMeshesOfCurrentMaterial[i].transform.localToWorldMatrix;
combineInstancesOfCurrentMaterial.Add(combineInstance);
}
//Create the submesh with all submeshes with current material, and set limitation of vertices
Mesh mesh = new Mesh();
#if UNITY_2017_4 || UNITY_2018_1_OR_NEWER
if (vertexCountInValidsGameObjects <= MAX_VERTICES_FOR_16BITS_MESH)
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt16;
if (vertexCountInValidsGameObjects > MAX_VERTICES_FOR_16BITS_MESH)
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#endif
mesh.CombineMeshes(combineInstancesOfCurrentMaterial.ToArray(), true, true);
//Add to list of combined submeshes per material
combinedSubmehesPerMaterial.Add(mesh);
}
//Process each submeshes per material, creating final combine instances
List<CombineInstance> finalCombineInstances = new List<CombineInstance>();
foreach (Mesh mesh in combinedSubmehesPerMaterial)
{
CombineInstance combineInstanceOfThisSubMesh = new CombineInstance();
combineInstanceOfThisSubMesh.mesh = mesh;
combineInstanceOfThisSubMesh.subMeshIndex = 0;
combineInstanceOfThisSubMesh.transform = Matrix4x4.identity;
finalCombineInstances.Add(combineInstanceOfThisSubMesh);
}
//Create the final mesh that contains all submeshes divided per material
Mesh finalMesh = new Mesh();
#if UNITY_2017_4 || UNITY_2018_1_OR_NEWER
if (vertexCountInValidsGameObjects <= MAX_VERTICES_FOR_16BITS_MESH)
finalMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt16;
if (vertexCountInValidsGameObjects > MAX_VERTICES_FOR_16BITS_MESH)
finalMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#endif
#if !UNITY_2017_4 && !UNITY_2018_1_OR_NEWER
if (vertexCountInValidsGameObjects > MAX_VERTICES_FOR_16BITS_MESH)
Debug.Log("The sum of vertices in the target GameObjects is greater than 65k. The resulting mesh may contain artifacts or be deformed. You can work around this issue only if your version of Unity is 2017.4 or higher.");
#endif
finalMesh.name = this.gameObject.name + " (Temp Merge)";
finalMesh.CombineMeshes(finalCombineInstances.ToArray(), false);
finalMesh.RecalculateBounds();
if (recalculateNormals == true)
finalMesh.RecalculateNormals();
if (recalculateTangents == true)
finalMesh.RecalculateTangents();
if (optimizeResultingMesh == true)
finalMesh.Optimize();
//Polulate this GameObject with the data of combined mesh
holderMeshFilter.sharedMesh = finalMesh;
List<Material> materialsForSubMeshes = new List<Material>();
foreach (var key in subMeshesPerMaterial)
{
materialsForSubMeshes.Add(key.Key);
}
holderMeshRenderer.sharedMaterials = materialsForSubMeshes.ToArray();
//Deactive original GameObjects if is desired
if (afterMerge == AfterMerge.DeactiveOriginalGameObjects)
{
foreach (GameObjectWithMesh obj in validsGameObjectsWithMesh)
{
originalGameObjectsWithMeshToRestore.Add(new OriginalGameObjectWithMesh(obj.gameObject, obj.gameObject.activeSelf, obj.meshRenderer, obj.meshRenderer.enabled));
obj.gameObject.SetActive(false);
}
//Add mesh collider, after merge, if disable gameobjects originals
if (addMeshColliderAfter == true)
{
this.gameObject.AddComponent<MeshCollider>();
}
}
//Disable original mesh filters and renderers if is desired
if (afterMerge == AfterMerge.DisableOriginalMeshes)
{
foreach (GameObjectWithMesh obj in validsGameObjectsWithMesh)
{
originalGameObjectsWithMeshToRestore.Add(new OriginalGameObjectWithMesh(obj.gameObject, obj.gameObject.activeSelf, obj.meshRenderer, obj.meshRenderer.enabled));
obj.meshRenderer.enabled = false;
}
}
//Do nothing if is desired
if (afterMerge == AfterMerge.DoNothing)
{
}
//Apply the original position, rotation and scale of this GameObject again
this.gameObject.transform.position = originalPosition;
this.gameObject.transform.eulerAngles = originalEulerAngles;
this.gameObject.transform.localScale = originalScale;
//Show stats
if (showDebugLogs == true)
Debug.Log("The merge has been successfully completed in Runtime Combiner \"" + this.gameObject.name + "\"!");
//Run events
if (onDoneMerge != null)
{
onDoneMerge.Invoke();
}
targetMeshesMerged = true;
return true;
}
return false;
}
public bool UndoMerge()
{
//If meshes already uncombined
if (isTargetMeshesMerged() == false)
{
if (showDebugLogs == true)
Debug.Log("The Runtime Combiner \"" + this.gameObject.name + "\" meshes are already uncombined!");
return true;
}
//If meshes are merged
if (isTargetMeshesMerged() == true)
{
//Undo the merge according the type of merge
if (afterMerge == AfterMerge.DisableOriginalMeshes)
{
foreach (OriginalGameObjectWithMesh original in originalGameObjectsWithMeshToRestore)
{
//Skip, if is null
if (original.meshRenderer == null)
{
continue;
}
original.meshRenderer.enabled = original.originalMrState;
}
}
if (afterMerge == AfterMerge.DeactiveOriginalGameObjects)
{
foreach (OriginalGameObjectWithMesh original in originalGameObjectsWithMeshToRestore)
{
//Skip, if is null
if (original.gameObject == null)
{
continue;
}
original.gameObject.SetActive(original.originalGoState);
}
if (addMeshColliderAfter == true)
{
MeshCollider meshCollider = this.GetComponent<MeshCollider>();
//Remove the mesh collider
if (meshCollider != null)
{
Destroy(meshCollider);
}
}
}
if (afterMerge == AfterMerge.DoNothing)
{
}
//Reset variables
originalGameObjectsWithMeshToRestore.Clear();
//Remove unecessary components
Destroy(this.GetComponent<MeshRenderer>());
Destroy(this.GetComponent<MeshFilter>());
if (garbageCollectorAfterUndo == true)
{
Resources.UnloadUnusedAssets();
System.GC.Collect();
}
//Show stats
if (showDebugLogs == true)
Debug.Log("The Runtime Combiner \"" + this.gameObject.name + "\" merge was successfully undone!");
//Run events
if (onDoneUnmerge != null)
{
onDoneUnmerge.Invoke();
}
targetMeshesMerged = false;
return true;
}
return false;
}
public bool isTargetMeshesMerged()
{
//Return if the meshes are merged
return targetMeshesMerged;
}
}
}