Files
FueraDeEscala/Assets/Game Kit Controller/Scripts/Others/combineMeshSystem.cs
Robii Aragon 779f2c8b20 add ckg
plantilla base para movimiento básico
2026-02-05 05:07:55 -08:00

523 lines
20 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
[RequireComponent (typeof (MeshFilter))]
[RequireComponent (typeof (MeshRenderer))]
public class combineMeshSystem : MonoBehaviour
{
[Header ("Main Settings")]
[Space]
public bool useMeshFilterList;
public List<MeshFilter> listMeshFilter;
[Space]
public bool useCustomParentListToGetMeshFilterList;
public List<Transform> customParentListToGetMeshFilterList;
[Space]
[SerializeField]
[Tooltip ("MeshFilters with Meshes which we don't want to combine into one Mesh.")]
private MeshFilter [] meshFiltersToSkip = new MeshFilter [0];
[Space]
[Header ("Other Settings")]
[Space]
public bool createMultiMaterialMesh = false;
public bool combineInactiveChildren = false;
public bool deactivateCombinedChildren = true;
public bool deactivateCombinedChildrenMeshRenderers = false;
public bool generateUVMap = false;
public bool destroyCombinedChildren = false;
[Space]
[Header ("Debug")]
[Space]
public bool showDebugPrint;
[Space]
[Header ("Events Settings")]
[Space]
public bool sendCombinedObjectOnEvent;
public eventParameters.eventToCallWithGameObject eventToSendCombinedObject;
private const int Mesh16BitBufferVertexLimit = 65535;
public bool CreateMultiMaterialMesh
{
get { return createMultiMaterialMesh; }
set { createMultiMaterialMesh = value; }
}
public bool CombineInactiveChildren
{
get { return combineInactiveChildren; }
set { combineInactiveChildren = value; }
}
public bool DeactivateCombinedChildren
{
get { return deactivateCombinedChildren; }
set
{
deactivateCombinedChildren = value;
CheckDeactivateCombinedChildren ();
}
}
public bool DeactivateCombinedChildrenMeshRenderers
{
get { return deactivateCombinedChildrenMeshRenderers; }
set
{
deactivateCombinedChildrenMeshRenderers = value;
CheckDeactivateCombinedChildren ();
}
}
public bool GenerateUVMap
{
get { return generateUVMap; }
set { generateUVMap = value; }
}
public bool DestroyCombinedChildren
{
get { return destroyCombinedChildren; }
set
{
destroyCombinedChildren = value;
CheckDestroyCombinedChildren ();
}
}
private void CheckDeactivateCombinedChildren ()
{
if (deactivateCombinedChildren || deactivateCombinedChildrenMeshRenderers) {
destroyCombinedChildren = false;
}
}
private void CheckDestroyCombinedChildren ()
{
if (destroyCombinedChildren) {
deactivateCombinedChildren = false;
deactivateCombinedChildrenMeshRenderers = false;
}
}
public void CombineMeshes (bool showCreatedMeshInfo)
{
#region Save our parent scale and our Transform and reset it temporarily:
// When we are unparenting and get parent again then sometimes scale is a little bit different so save scale before unparenting:
Vector3 oldScaleAsChild = transform.localScale;
// If we have parent then his scale will affect to our new combined Mesh scale so unparent us:
int positionInParentHierarchy = transform.GetSiblingIndex ();
Transform parent = transform.parent;
transform.parent = null;
// Thanks to this the new combined Mesh will have same position and scale in the world space like its children:
Quaternion oldRotation = transform.rotation;
Vector3 oldPosition = transform.position;
Vector3 oldScale = transform.localScale;
transform.rotation = Quaternion.identity;
transform.position = Vector3.zero;
transform.localScale = Vector3.one;
#endregion Save Transform and reset it temporarily.
#region Combine Meshes into one Mesh:
if (!createMultiMaterialMesh) {
CombineMeshesWithSingleMaterial (showCreatedMeshInfo);
} else {
CombineMeshesWithMutliMaterial (showCreatedMeshInfo);
}
#endregion Combine Meshes into one Mesh.
#region Set old Transform values:
// Bring back the Transform values:
transform.rotation = oldRotation;
transform.position = oldPosition;
transform.localScale = oldScale;
// Get back parent and same hierarchy position:
transform.parent = parent;
transform.SetSiblingIndex (positionInParentHierarchy);
// Set back the scale value as child:
transform.localScale = oldScaleAsChild;
if (sendCombinedObjectOnEvent) {
eventToSendCombinedObject.Invoke (gameObject);
}
if (showDebugPrint) {
print ("objects meshes combined");
}
#endregion Set old Transform values.
}
void checkNullElementsOnListMeshFilter ()
{
for (int i = listMeshFilter.Count - 1; i >= 0; i--) {
if (listMeshFilter [i] == null) {
listMeshFilter.RemoveAt (i);
}
}
}
private MeshFilter [] GetMeshFiltersToCombine ()
{
// Get all MeshFilters belongs to this GameObject and its children:
MeshFilter [] meshFilters = new MeshFilter [0];
if (useMeshFilterList) {
checkNullElementsOnListMeshFilter ();
int listMeshFilterCount = listMeshFilter.Count + 1;
meshFilters = new MeshFilter [listMeshFilterCount];
MeshFilter mainMeshFilter = gameObject.GetComponent<MeshFilter> ();
meshFilters [0] = mainMeshFilter;
for (int i = 0; i < listMeshFilter.Count; i++) {
meshFilters [i + 1] = listMeshFilter [i];
}
} else {
meshFilters = GetComponentsInChildren<MeshFilter> (combineInactiveChildren);
}
// Delete first MeshFilter belongs to this GameObject in meshFiltersToSkip array:
meshFiltersToSkip = meshFiltersToSkip.Where ((meshFilter) => meshFilter != meshFilters [0]).ToArray ();
// Delete null values in meshFiltersToSkip array:
meshFiltersToSkip = meshFiltersToSkip.Where ((meshFilter) => meshFilter != null).ToArray ();
for (int i = 0; i < meshFiltersToSkip.Length; i++) {
meshFilters = meshFilters.Where ((meshFilter) => meshFilter != meshFiltersToSkip [i]).ToArray ();
}
return meshFilters;
}
private void CombineMeshesWithSingleMaterial (bool showCreatedMeshInfo)
{
// Get all MeshFilters belongs to this GameObject and its children:
MeshFilter [] meshFilters = GetMeshFiltersToCombine ();
// First MeshFilter belongs to this GameObject so we don't need it:
CombineInstance [] combineInstances = new CombineInstance [meshFilters.Length - 1];
// If it will be over 65535 then use the 32 bit index buffer:
long verticesLength = 0;
for (int i = 0; i < meshFilters.Length - 1; i++) // Skip first MeshFilter belongs to this GameObject in this loop.
{
combineInstances [i].subMeshIndex = 0;
combineInstances [i].mesh = meshFilters [i + 1].sharedMesh;
combineInstances [i].transform = meshFilters [i + 1].transform.localToWorldMatrix;
verticesLength += combineInstances [i].mesh.vertices.Length;
}
// Set Material from child:
MeshRenderer [] meshRenderers = new MeshRenderer [0];
if (useMeshFilterList) {
checkNullElementsOnListMeshFilter ();
meshRenderers = new MeshRenderer [listMeshFilter.Count];
int meshRenderersLength = meshRenderers.Length;
for (int i = 0; i < meshRenderersLength; i++) {
meshRenderers [i] = listMeshFilter [i].GetComponent<MeshRenderer> ();
}
MeshRenderer mainMeshRenderer = gameObject.GetComponent<MeshRenderer> ();
meshRenderers = meshRenderers.Append (mainMeshRenderer).ToArray ();
} else {
meshRenderers = GetComponentsInChildren<MeshRenderer> (combineInactiveChildren);
}
if (meshRenderers.Length >= 2) {
meshRenderers [0].sharedMaterials = new Material [1];
meshRenderers [0].sharedMaterial = meshRenderers [1].sharedMaterial;
} else {
meshRenderers [0].sharedMaterials = new Material [0]; // Reset the MeshRenderer's Materials array.
}
// Create Mesh from combineInstances:
Mesh combinedMesh = new Mesh ();
combinedMesh.name = name;
#if UNITY_2017_3_OR_NEWER
if (verticesLength > Mesh16BitBufferVertexLimit) {
combinedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; // Only works on Unity 2017.3 or higher.
}
combinedMesh.CombineMeshes (combineInstances);
GenerateUV (combinedMesh);
meshFilters [0].sharedMesh = combinedMesh;
DeactivateCombinedGameObjects (meshFilters);
if (showCreatedMeshInfo) {
if (verticesLength <= Mesh16BitBufferVertexLimit) {
Debug.Log ("<color=#00cc00><b>Mesh \"" + name + "\" was created from " + combineInstances.Length + " children meshes and has " + verticesLength
+ " vertices.</b></color>");
} else {
Debug.Log ("<color=#ff3300><b>Mesh \"" + name + "\" was created from " + combineInstances.Length + " children meshes and has " + verticesLength
+ " vertices. Some old devices, like Android with Mali-400 GPU, do not support over 65535 vertices.</b></color>");
}
}
#else
if(verticesLength <= Mesh16BitBufferVertexLimit)
{
combinedMesh.CombineMeshes(combineInstances);
GenerateUV(combinedMesh);
meshFilters[0].sharedMesh = combinedMesh;
DeactivateCombinedGameObjects(meshFilters);
if(showCreatedMeshInfo)
{
Debug.Log("<color=#00cc00><b>Mesh \""+name+"\" was created from "+combineInstances.Length+" children meshes and has "+verticesLength
+" vertices.</b></color>");
}
}
else if(showCreatedMeshInfo)
{
Debug.Log("<color=red><b>The mesh vertex limit is 65535! The created mesh had "+verticesLength+" vertices. Upgrade Unity version to"
+" 2017.3 or higher to avoid this limit (some old devices, like Android with Mali-400 GPU, do not support over 65535 vertices).</b></color>");
}
#endif
}
private void CombineMeshesWithMutliMaterial (bool showCreatedMeshInfo)
{
#region Get MeshFilters, MeshRenderers and unique Materials from all children:
MeshFilter [] meshFilters = GetMeshFiltersToCombine ();
MeshRenderer [] meshRenderers = new MeshRenderer [meshFilters.Length];
meshRenderers [0] = GetComponent<MeshRenderer> (); // Our (parent) MeshRenderer.
List<Material> uniqueMaterialsList = new List<Material> ();
for (int i = 0; i < meshFilters.Length - 1; i++) {
meshRenderers [i + 1] = meshFilters [i + 1].GetComponent<MeshRenderer> ();
if (meshRenderers [i + 1] != null) {
Material [] materials = meshRenderers [i + 1].sharedMaterials; // Get all Materials from child Mesh.
for (int j = 0; j < materials.Length; j++) {
if (!uniqueMaterialsList.Contains (materials [j])) // If Material doesn't exists in the list then add it.
{
uniqueMaterialsList.Add (materials [j]);
}
}
}
}
#endregion Get MeshFilters, MeshRenderers and unique Materials from all children.
#region Combine children Meshes with the same Material to create submeshes for final Mesh:
List<CombineInstance> finalMeshCombineInstancesList = new List<CombineInstance> ();
// If it will be over 65535 then use the 32 bit index buffer:
long verticesLength = 0;
for (int i = 0; i < uniqueMaterialsList.Count; i++) // Create each Mesh (submesh) from Meshes with the same Material.
{
List<CombineInstance> submeshCombineInstancesList = new List<CombineInstance> ();
for (int j = 0; j < meshFilters.Length - 1; j++) // Get only childeren Meshes (skip our Mesh).
{
if (meshRenderers [j + 1] != null && meshFilters [j + 1].sharedMesh != null) {
Material [] submeshMaterials = meshRenderers [j + 1].sharedMaterials; // Get all Materials from child Mesh.
for (int k = 0; k < submeshMaterials.Length; k++) {
// If Materials are equal, combine Mesh from this child:
if (uniqueMaterialsList [i] == submeshMaterials [k]) {
CombineInstance combineInstance = new CombineInstance ();
combineInstance.subMeshIndex = k; // Mesh may consist of smaller parts - submeshes.
// Every part have different index. If there are 3 submeshes
// in Mesh then MeshRender needs 3 Materials to render them.
combineInstance.mesh = meshFilters [j + 1].sharedMesh;
combineInstance.transform = meshFilters [j + 1].transform.localToWorldMatrix;
submeshCombineInstancesList.Add (combineInstance);
verticesLength += combineInstance.mesh.vertices.Length;
}
}
}
}
// Create new Mesh (submesh) from Meshes with the same Material:
Mesh submesh = new Mesh ();
#if UNITY_2017_3_OR_NEWER
if (verticesLength > Mesh16BitBufferVertexLimit) {
submesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; // Only works on Unity 2017.3 or higher.
}
submesh.CombineMeshes (submeshCombineInstancesList.ToArray (), true);
#else
// Below Unity 2017.3 if vertices count is above the limit then an error appears in the console when we use the below method.
// Anyway we don't stop the algorithm here beacuse we want to count the entire number of vertices in the children meshes:
if(verticesLength <= Mesh16BitBufferVertexLimit)
{
submesh.CombineMeshes(submeshCombineInstancesList.ToArray(), true);
}
#endif
CombineInstance finalCombineInstance = new CombineInstance ();
finalCombineInstance.subMeshIndex = 0;
finalCombineInstance.mesh = submesh;
finalCombineInstance.transform = Matrix4x4.identity;
finalMeshCombineInstancesList.Add (finalCombineInstance);
}
#endregion Combine submeshes (children Meshes) with the same Material.
#region Set Materials array & combine submeshes into one multimaterial Mesh:
meshRenderers [0].sharedMaterials = uniqueMaterialsList.ToArray ();
Mesh combinedMesh = new Mesh ();
combinedMesh.name = name;
#if UNITY_2017_3_OR_NEWER
if (verticesLength > Mesh16BitBufferVertexLimit) {
combinedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; // Only works on Unity 2017.3 or higher.
}
combinedMesh.CombineMeshes (finalMeshCombineInstancesList.ToArray (), false);
GenerateUV (combinedMesh);
meshFilters [0].sharedMesh = combinedMesh;
DeactivateCombinedGameObjects (meshFilters);
if (showCreatedMeshInfo) {
if (verticesLength <= Mesh16BitBufferVertexLimit) {
Debug.Log ("<color=#00cc00><b>Mesh \"" + name + "\" was created from " + (meshFilters.Length - 1) + " children meshes and has "
+ finalMeshCombineInstancesList.Count + " submeshes, and " + verticesLength + " vertices.</b></color>");
} else {
Debug.Log ("<color=#ff3300><b>Mesh \"" + name + "\" was created from " + (meshFilters.Length - 1) + " children meshes and has "
+ finalMeshCombineInstancesList.Count + " submeshes, and " + verticesLength
+ " vertices. Some old devices, like Android with Mali-400 GPU, do not support over 65535 vertices.</b></color>");
}
}
#else
if(verticesLength <= Mesh16BitBufferVertexLimit)
{
combinedMesh.CombineMeshes(finalMeshCombineInstancesList.ToArray(), false);
GenerateUV(combinedMesh);
meshFilters[0].sharedMesh = combinedMesh;
DeactivateCombinedGameObjects(meshFilters);
if(showCreatedMeshInfo)
{
Debug.Log("<color=#00cc00><b>Mesh \""+name+"\" was created from "+(meshFilters.Length-1)+" children meshes and has "
+finalMeshCombineInstancesList.Count+" submeshes, and "+verticesLength+" vertices.</b></color>");
}
}
else if(showCreatedMeshInfo)
{
Debug.Log("<color=red><b>The mesh vertex limit is 65535! The created mesh had "+verticesLength+" vertices. Upgrade Unity version to"
+" 2017.3 or higher to avoid this limit (some old devices, like Android with Mali-400 GPU, do not support over 65535 vertices).</b></color>");
}
#endif
#endregion Set Materials array & combine submeshes into one multimaterial Mesh.
}
private void DeactivateCombinedGameObjects (MeshFilter [] meshFilters)
{
for (int i = 0; i < meshFilters.Length - 1; i++) // Skip first MeshFilter belongs to this GameObject in this loop.
{
if (!destroyCombinedChildren) {
if (deactivateCombinedChildren) {
meshFilters [i + 1].gameObject.SetActive (false);
}
if (deactivateCombinedChildrenMeshRenderers) {
MeshRenderer meshRenderer = meshFilters [i + 1].gameObject.GetComponent<MeshRenderer> ();
if (meshRenderer != null) {
meshRenderer.enabled = false;
}
}
} else {
DestroyImmediate (meshFilters [i + 1].gameObject);
}
}
}
private void GenerateUV (Mesh combinedMesh)
{
#if UNITY_EDITOR
if (generateUVMap) {
UnityEditor.UnwrapParam unwrapParam = new UnityEditor.UnwrapParam ();
UnityEditor.UnwrapParam.SetDefaults (out unwrapParam);
UnityEditor.Unwrapping.GenerateSecondaryUVSet (combinedMesh, unwrapParam);
}
#endif
}
public void getObjectMeshes ()
{
listMeshFilter.Clear ();
if (useCustomParentListToGetMeshFilterList) {
for (int i = 0; i < customParentListToGetMeshFilterList.Count; i++) {
if (customParentListToGetMeshFilterList [i] != null) {
MeshFilter [] meshFilters = customParentListToGetMeshFilterList [i].GetComponentsInChildren<MeshFilter> (combineInactiveChildren);
for (int j = 0; j < meshFilters.Length; j++) {
if (meshFilters [j] != null) {
listMeshFilter.Add (meshFilters [j]);
}
}
}
}
} else {
MeshFilter [] meshFilters = GetComponentsInChildren<MeshFilter> (combineInactiveChildren);
for (int i = 0; i < meshFilters.Length; i++) {
if (meshFilters [i] != null) {
listMeshFilter.Add (meshFilters [i]);
}
}
}
MeshFilter mainMeshFilter = gameObject.GetComponent<MeshFilter> ();
for (int i = listMeshFilter.Count - 1; i >= 0; i--) {
if (listMeshFilter [i].GetComponent<ParticleSystem> () != null) {
listMeshFilter.RemoveAt (i);
}
if (mainMeshFilter != null && mainMeshFilter == listMeshFilter [i]) {
listMeshFilter.RemoveAt (i);
}
}
updateComponent ();
}
public void clearMeshList ()
{
listMeshFilter.Clear ();
updateComponent ();
}
void updateComponent ()
{
GKC_Utils.updateComponent (this);
GKC_Utils.updateDirtyScene ("Update Combine Mesh System", gameObject);
}
}