1. 程式人生 > >Unity人物換裝 合併網格材質骨骼

Unity人物換裝 合併網格材質骨骼

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

struct meshT
{
    public Mesh _mesh;
    public Transform[] _transform;
    public Material _material;

}

/// <summary>
///換裝併合併網格 
/// </summary>
public class MeshMergeDome2 : MonoBehaviour
{
    public static MeshMergeDome2 _intance;
    private void Awake()
    {
        _intance = this;
    }
    public string loadName;
    //生成人物得位置
    private Transform StartPos;
    private GameObject gamePlayerObj;
    Dictionary<string, meshT> meshDic = new Dictionary<string, meshT>();
    Dictionary<string, meshT> meshTs = new Dictionary<string, meshT>();
    private string rootNamne;
    Material[] material;
    Transform[] _transform;


    /// <summary>
    /// 初始化
    /// </summary>
    private void Start()
    {
        //尋找生成位置
        StartPos = GameObject.FindGameObjectWithTag("StartPos").transform;
        //載入人物
        gamePlayerObj = Instantiate(Resources.Load<GameObject>(loadName), StartPos.position, Quaternion.identity);


        SkinnedMeshRenderer[] skinnedMeshRenderers = gamePlayerObj.GetComponentsInChildren<SkinnedMeshRenderer>();


        for (int i = 0; i < skinnedMeshRenderers.Length; i++)
        {
            meshDic.Add(skinnedMeshRenderers[i].name, new meshT() { _mesh = skinnedMeshRenderers[i].sharedMesh, _transform = skinnedMeshRenderers[i].bones, _material = skinnedMeshRenderers[i].sharedMaterial,/*_transform1 = gamePlayerObj.transform.worldToLocalMatrix * skinnedMeshRenderers[i].transform.localToWorldMatrix*/ });


            Destroy(skinnedMeshRenderers[i].gameObject);
        }


        List<Transform> boneList = new List<Transform>();
        Transform[] transforms = gamePlayerObj.transform.Find("Female_Hips").GetComponentsInChildren<Transform>();


        foreach (KeyValuePair<string, meshT> item in meshDic)
        {
            string name = item.Key.Split('-')[0];
            if (name != rootNamne)
            {
                meshTs.Add(name, item.Value);
                for (int i = 0; i < item.Value._transform.Length; i++)
                {
                    boneList.Add(item.Value._transform[i]);
                }
                rootNamne = name;
            }
        }
    CombineInstance[] combineInstances = new CombineInstance[meshTs.Count];






        List<meshT> mesh = new List<meshT>();






        foreach (var item in meshTs)
        {
            mesh.Add(item.Value);
        }




        material = new Material[mesh.Count];
        _transform = new Transform[mesh.Count];




        List<Vector2[]> uvList = new List<Vector2[]>();//uv座標
        int uvCount = 0;


        for (int i = 0; i < mesh.Count; i++)
        {
            uvList.Add(mesh[i]._mesh.uv);//儲存uv座標
            uvCount += mesh[i]._mesh.uv.Length;
            material[i] = mesh[i]._material;
            combineInstances[i].mesh = mesh[i]._mesh;
        }






        List<Texture2D> textures = new List<Texture2D>();


        int width = 0;
        int height = 0;


        //儲存圖片資訊
        foreach (Material item in material)
        {
            if (item.mainTexture != null)
            {
                textures.Add(item.mainTexture as Texture2D);
                width += item.mainTexture.width;
                height += item.mainTexture.height;
            }
        }




        SkinnedMeshRenderer skinned = gamePlayerObj.AddComponent<SkinnedMeshRenderer>();
        // 設定貼圖和UV
        skinned.sharedMesh = new Mesh();
        skinned.sharedMesh.CombineMeshes(combineInstances, true, false); //不在意的矩陣的變換
        skinned.bones = boneList.ToArray();
        skinned.material = material[0];




        //建立一個空的圖片
        Texture2D skinnedMeshAtlas = new Texture2D(get2Pow(width), get2Pow(height));
        Rect[] packingResult = skinnedMeshAtlas.PackTextures(textures.ToArray(), 0);


        Vector2[] atlasUVs = new Vector2[uvCount];


        // 因為將貼圖都整合到了一張圖片上,所以需要重新計算UV
        int j = 0;
        for (int i = 0; i < uvList.Count; i++)
        {
            foreach (Vector2 uv in uvList[i])
            {
                atlasUVs[j].x = Mathf.Lerp(packingResult[i].xMin, packingResult[i].xMax, uv.x);
                atlasUVs[j].y = Mathf.Lerp(packingResult[i].yMin, packingResult[i].yMax, uv.y);
                j++;
            }
        }


        skinned.material.mainTexture = skinnedMeshAtlas;
        skinned.sharedMesh.uv = atlasUVs;


        //MergeMesh(skinned, combineInstances, boneList);
    }


    /// <summary>
    /// 合併網格、材質、骨骼
    /// </summary>
    /// <param name="skinned"></param>
    /// <param name="combineInstances"></param>
    /// <param name="boneList"></param>
    void MergeMesh(SkinnedMeshRenderer skinned, CombineInstance[] combineInstances, List<Transform> boneList)
    {
        skinned.sharedMesh = new Mesh();
        skinned.sharedMesh.CombineMeshes(combineInstances, false, false); //不在意的矩陣的變換
        skinned.materials = material;
        skinned.bones = boneList.ToArray();
    }


    public void UpdateMesh(string meshName)
    {
        string UpdateName = "";
        /// <summary>
        /// 當前的人物換裝資訊
        /// </summary>
        Dictionary<string, meshT> currmesh = meshTs;
        foreach (KeyValuePair<string, meshT> item in currmesh)
        {
            string name = item.Key.Split('-')[0];
            if (name == meshName.Split('-')[0])
            {
                UpdateName = item.Key;
            }
        }


        currmesh[UpdateName] = meshDic[meshName];


        List<Transform> boneList = new List<Transform>();


        foreach (KeyValuePair<string, meshT> item in currmesh)
        {
            for (int i = 0; i < item.Value._transform.Length; i++)
            {
                boneList.Add(item.Value._transform[i]);
            }
        }


        CombineInstance[] combineInstances = new CombineInstance[meshTs.Count];


        List<meshT> mesh = new List<meshT>();


      
        foreach (var item in currmesh)
        {
            mesh.Add(item.Value);
        }


        List<Vector2[]> uvList = new List<Vector2[]>();//uv座標
        int uvCount = 0;


        for (int i = 0; i < mesh.Count; i++)
        {
            uvList.Add(mesh[i]._mesh.uv);//儲存uv座標
            uvCount += mesh[i]._mesh.uv.Length;
            material[i] = mesh[i]._material;
            combineInstances[i].mesh = mesh[i]._mesh;
        }




        material = new Material[mesh.Count];
        _transform = new Transform[mesh.Count];


        


        for (int i = 0; i < mesh.Count; i++)
        {
            material[i] = mesh[i]._material;
            combineInstances[i].mesh = mesh[i]._mesh;
        }


        List<Texture2D> textures = new List<Texture2D>();


        int width = 0;
        int height = 0;


        //儲存圖片資訊
        foreach (Material item in material)
        {
            if (item.mainTexture != null)
            {
                textures.Add(item.mainTexture as Texture2D);
                width += item.mainTexture.width;
                height += item.mainTexture.height;
            }
        }


        SkinnedMeshRenderer skinned = gamePlayerObj.GetComponent<SkinnedMeshRenderer>();
        skinned.sharedMesh = new Mesh();
        skinned.sharedMesh.CombineMeshes(combineInstances, true, false); //不在意的矩陣的變換
        skinned.bones = boneList.ToArray();
        skinned.material = material[0];




        //建立一個空的圖片
        Texture2D skinnedMeshAtlas = new Texture2D(get2Pow(width), get2Pow(height));
        Rect[] packingResult = skinnedMeshAtlas.PackTextures(textures.ToArray(), 0);


        Vector2[] atlasUVs = new Vector2[uvCount];


        // 因為將貼圖都整合到了一張圖片上,所以需要重新計算UV
        int j = 0;
        for (int i = 0; i < uvList.Count; i++)
        {
            foreach (Vector2 uv in uvList[i])
            {
                atlasUVs[j].x = Mathf.Lerp(packingResult[i].xMin, packingResult[i].xMax, uv.x);
                atlasUVs[j].y = Mathf.Lerp(packingResult[i].yMin, packingResult[i].yMax, uv.y);
                j++;
            }
        }


        skinned.material.mainTexture = skinnedMeshAtlas;
        skinned.sharedMesh.uv = atlasUVs;


        //MergeMesh(skinned, combineInstances, boneList);
    }




    /// <summary>
    /// 獲取最接近輸入值的2的N次方的數,最大不會超過1024,例如輸入320會得到512
    /// </summary>
    public int get2Pow(int into)
    {
        int outo = 1;
        for (int i = 0; i < 10; i++)
        {
            outo *= 2;
            if (outo > into)
            {
                break;
            }
        }
        return outo;
    }

}

效果: