Unity合并Mesh并保存在本地

// Easy Mesh Combiner
// [email protected]
// Originally written at Jan 2022
// Used the below link for combining algorithm (user @Bunzaga):
// https://answers.unity.com/questions/196649/combinemeshes-with-different-materials.html

#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;

public class Easy_MeshCombineTool : EditorWindow
{

// Options
public bool generateLightmapUV = true;
public bool optimizeMeshBuffer = true;
public bool backUpOriginalOjects = true;
public bool addMeshCollider = false;

// Use 32 bit mesh index for too many vertex models (not optimized for mobile)
public bool use32BitIndex = false;
public string combinedName = "Combined_Models";

// internal variables
GameObject[] selectedOjects;
bool vertexLimitError;
int selectedVertexCounts = 0;
int selectedTrinaglesCount = 0;
public MeshFilter[] mf;

[MenuItem("Window/Easy Mesh Combine Tool")]
static void Init()
{
    // Display window
    Easy_MeshCombineTool window = (Easy_MeshCombineTool)EditorWindow.GetWindow(typeof(Easy_MeshCombineTool));
    window.Show();
}

[MenuItem("Window/SaveMesh")]
static void SaveMesh()
{
    GameObject selectObj = Selection.activeGameObject;
    Mesh ml = selectObj.GetComponent<MeshFilter>().sharedMesh;
    AssetDatabase.CreateAsset(ml, "Assets/"+ml.name+".asset");
}

void OnGUI()
{
    EditorGUILayout.Space();

    // Top label ( window title and version name)
    GUILayout.Label("Easy Mesh Combiner - Update 1.1 Jan 2022", EditorStyles.helpBox);

    EditorGUILayout.Space();

    generateLightmapUV = EditorGUILayout.Toggle("Generate Lightmap UV", generateLightmapUV);

    optimizeMeshBuffer = EditorGUILayout.Toggle("Optimize Mesh Buffer", optimizeMeshBuffer);
    
    EditorGUILayout.Space();

    backUpOriginalOjects = EditorGUILayout.Toggle("Backup Original Objects", backUpOriginalOjects);
    
    addMeshCollider = EditorGUILayout.Toggle("Add Mesh Collider", addMeshCollider);

    EditorGUILayout.Space();
    EditorGUILayout.Space();

    use32BitIndex = EditorGUILayout.Toggle("Use 32 Bit Mesh Index", use32BitIndex);
   
    EditorGUILayout.Space();
   
    combinedName = EditorGUILayout.TextField("Enter Name", combinedName);
    
    EditorGUILayout.Space();
    EditorGUILayout.Space();

    // Show error box when the selected game objects has higher than 65535 vertexs
    if (vertexLimitError)
        EditorGUILayout.HelpBox("Reached to the 16 bit vertex count limit : 65535 \n You can use 32 bit mesh index format \n 16 bit is better for mobile platforms", MessageType.Error);
    else
    {
        if (GUILayout.Button("Make Group and Combine"))
            Combine_Meshes();
    }

    // Actions when selecting game object in the scene/hierarchy
    if (Selection.activeGameObject)
    {

        EditorGUILayout.Space();
        EditorGUILayout.Space();
        EditorGUILayout.Space();

        // Display vertex and triangles count
        EditorGUILayout.HelpBox("Vertex count : " + selectedVertexCounts.ToString(), MessageType.None);
        EditorGUILayout.HelpBox("Trinagles count : " + selectedTrinaglesCount.ToString(), MessageType.None);

        GameObject[] selectedObjects = Selection.gameObjects;
        List<GameObject> allObjects = new List<GameObject>();
        allObjects.Clear();

        // Read all selected game objects and its childs
        for (int a = 0;a< selectedObjects.Length;a++)
        {
            Transform[] childs = selectedObjects[a].GetComponentsInChildren<Transform>();
            for (int c = 0; c < childs.Length; c++)
            { 
                if(childs[c].gameObject.GetComponent<MeshFilter>())
                   allObjects.Add(childs[c].gameObject);
            }
        }

        mf = new MeshFilter[allObjects.Count];

        for (int d = 0; d < allObjects.Count; d++)
        {
            if(allObjects[d].GetComponent<MeshFilter>())
               mf[d] = allObjects[d].GetComponent<MeshFilter>();
        }

        // Display vertex counts
        int vertexCounts = 0;
        int trianglesCount = 0;

        for (int vl = 0; vl < mf.Length; vl++)
        {
            vertexCounts += mf[vl].sharedMesh.vertexCount;
            trianglesCount += mf[vl].sharedMesh.triangles.Length / 3;
        }

        if (vertexCounts > 65535 && !use32BitIndex)
            vertexLimitError = true;
        else
            vertexLimitError = false;

        // Save the vertexs and tringles count
        selectedVertexCounts = vertexCounts;
        selectedTrinaglesCount = trianglesCount;
    }

    
    
}

// Group game objects before combine
void Group_Objects(Transform targetParent)
{
    selectedOjects = Selection.gameObjects;
    for(int a = 0;a< selectedOjects.Length;a++)
    {
        selectedOjects[a].transform.parent = targetParent;

        // Disconnect prefabs before combine
        try
        {
            if (PrefabUtility.IsPartOfAnyPrefab(selectedOjects[a]))
                PrefabUtility.UnpackPrefabInstance(selectedOjects[a], PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
        }
        catch { }
        }
}

// Main mesh combine function
void Combine_Meshes()
{
    if (vertexLimitError)
        return;

    // Create the group parent
    GameObject target = new GameObject();
    target.name = "Back Up";
    // Group selected game objects before combine
    Group_Objects(target.transform);
    //________________________________________________________________
    // Keep the original model or destroy
    if (backUpOriginalOjects)
    { 
        GameObject keepOriginal;

        keepOriginal = Instantiate(target) as GameObject;

        keepOriginal.name = target.name;
        keepOriginal.transform.position = target.transform.position;
        keepOriginal.transform.rotation = target.transform.rotation;
        keepOriginal.transform.localScale = target.transform.localScale;
        keepOriginal.SetActive(false);                
    }
    //________________________________________________________________
    // Save initialize data
    string originalName = "default";
    originalName = target.GetComponent<Transform>().name;

    //________________________________________________________________
    #region Combine
    // This code is originalay copied from below link and modified by me (user @Bunzaga):
    // https://answers.unity.com/questions/196649/combinemeshes-with-different-materials.html
    ArrayList materials = new ArrayList();
    ArrayList combineInstanceArrays = new ArrayList();
    MeshFilter[] meshFilters = target.GetComponentsInChildren<MeshFilter>();

    foreach (MeshFilter meshFilter in meshFilters)
    {
        MeshRenderer meshRenderer = meshFilter.GetComponent<MeshRenderer>();

        if (!meshRenderer ||
            !meshFilter.sharedMesh ||
            meshRenderer.sharedMaterials.Length != meshFilter.sharedMesh.subMeshCount)
        {
            continue;
        }

        for (int s = 0; s < meshFilter.sharedMesh.subMeshCount; s++)
        {
            int materialArrayIndex = Contains(materials, meshRenderer.sharedMaterials[s].name);
            if (materialArrayIndex == -1)
            {
                materials.Add(meshRenderer.sharedMaterials[s]);
                materialArrayIndex = materials.Count - 1;
            }
            combineInstanceArrays.Add(new ArrayList());

            CombineInstance combineInstance = new CombineInstance();
            combineInstance.transform = meshRenderer.transform.localToWorldMatrix;
            combineInstance.subMeshIndex = s;
            combineInstance.mesh = meshFilter.sharedMesh;
            (combineInstanceArrays[materialArrayIndex] as ArrayList).Add(combineInstance);
        }
    }

    // Get / Create mesh filter & renderer
    MeshFilter meshFilterCombine = target.GetComponent<MeshFilter>();
    if (meshFilterCombine == null)
    {
        meshFilterCombine = target.AddComponent<MeshFilter>();
    }
    MeshRenderer meshRendererCombine = target.GetComponent<MeshRenderer>();
    if (meshRendererCombine == null)
    {
        meshRendererCombine = target.AddComponent<MeshRenderer>();
    }

    
    // Combine by material index into per-material meshes
    // also, Create CombineInstance array for next step
    Mesh[] meshes = new Mesh[materials.Count];
    CombineInstance[] combineInstances = new CombineInstance[materials.Count];

    for (int m = 0; m < materials.Count; m++)
    {
        CombineInstance[] combineInstanceArray = (combineInstanceArrays[m] as ArrayList).ToArray(typeof(CombineInstance)) as CombineInstance[];
        meshes[m] = new Mesh();

        //________________________________________________________________
        // use 32 bit mesh index format for when combined mesh's vertex counts is higher than 65535
        if (use32BitIndex && selectedVertexCounts > 65535)
            meshes[m].indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; // 4 billions vertex counts limit
        else
            meshes[m].indexFormat = UnityEngine.Rendering.IndexFormat.UInt16; // 65535 vertex counts limit

        meshes[m].CombineMeshes(combineInstanceArray, true, true);

        combineInstances[m] = new CombineInstance();
        combineInstances[m].mesh = meshes[m];
        combineInstances[m].subMeshIndex = 0;
    }
            
    // Combine into one
    meshFilterCombine.sharedMesh = new Mesh();
    //________________________________________________________________
    // use 32 bit mesh index format for when combined mesh's vertex counts is higher than 65535
    if (use32BitIndex && selectedVertexCounts > 65535)
        meshFilterCombine.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; // 4 billions vertex counts limit
    else
        meshFilterCombine.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt16; // 65535 vertex counts limit

    meshFilterCombine.sharedMesh.CombineMeshes(combineInstances, false, false);

    // Destroy other meshes
    foreach (Mesh oldMesh in meshes)
    {
        oldMesh.Clear();
        DestroyImmediate(oldMesh);
    }

    // Assign materials
    Material[] materialsArray = materials.ToArray(typeof(Material)) as Material[];
    meshRendererCombine.materials = materialsArray;

    // Destroy original mesh models
    while (target.transform.childCount > 0)
    {
        foreach (Transform child in target.transform)
        {
            DestroyImmediate(child.gameObject);
        }
    }

    #endregion
    //________________________________________________________________
    // Assign the combined game object's name
    target.transform.GetComponent<MeshFilter>().sharedMesh.name = combinedName;
    target.name = combinedName;

    //________________________________________________________________
    // Optimize mesh buffer
    if (optimizeMeshBuffer)
        MeshUtility.Optimize(target.GetComponent<MeshFilter>().sharedMesh);

    // Generate lightmap uv
    if (generateLightmapUV)
        Unwrapping.GenerateSecondaryUVSet(target.transform.GetComponent<MeshFilter>().sharedMesh);

    // Add Mesh Collider
    if(addMeshCollider)
        target.AddComponent<MeshCollider>();

    //________________________________________________________________

    // Use the original position, rotation and scale fro the new combined mesh
    target.transform.position = Vector3.zero;
    target.transform.rotation = Quaternion.identity;
    target.transform.localScale = new Vector3(1,1,1);


}
// Used for materials combination
private int Contains(ArrayList searchList, string searchName)
{
    for (int i = 0; i < searchList.Count; i++)
    {
        if (((Material)searchList[i]).name == searchName)
        {
            return i;
        }
    }
    return -1;
}

}
#endif

猜你喜欢

转载自blog.csdn.net/weixin_43780907/article/details/130727469