unity模型导入

1.导入包来自游戏蛮牛,直接是unitypackage,但是路径不喜欢,自己换模型路径(贴图,材质球,动作),统一缩放值

下面聊一下Mesh、MeshFiler、MeshRenderer、skinMeshRender

mesh Filter是从资源里获取mesh资源,通过meshrender渲染到屏幕

Mesh:村抽三维模型数据(vertices顶点V3[]、triangles 三角形顶点索引 int[]、法线向量数组V3[],uv纹理坐标V2[])

MeshFiler:用于增加mesh属性(可以代码动态生产)

MeshRender:增加材质并渲染出来。

 下面试创建一个Trianglegameobject

     public GameObject GetTriangle ()
        {
            GameObject go = new GameObject ("Triangle");
            MeshFilter filter = go.AddComponent<MeshFilter> ();
            // 构建三角形的三个顶点,并赋值给Mesh.vertices
            Mesh mesh = new Mesh ();
            filter.sharedMesh = mesh;
            mesh.vertices = new Vector3[] {
                new Vector3 (0, 0, 1),
                new Vector3 (0, 2, 0),
                new Vector3 (2, 0, 5),
            };
            // 构建三角形的顶点顺序,因为这里只有一个三角形,
            // 所以只能是(0, 1, 2)这个顺序。
            mesh.triangles = new int[3] {0, 1, 2};
            mesh.RecalculateNormals ();
            mesh.RecalculateBounds ();
            // 使用Shader构建一个材质,并设置材质的颜色。
            Material material = new Material (Shader.Find ("Diffuse"));
            material.SetColor ("_Color", Color.yellow);
            // 构建一个MeshRender并把上面创建的材质赋值给它,
            // 然后使其把上面构造的Mesh渲染到屏幕上。
            MeshRenderer renderer = go.AddComponent<MeshRenderer> ();
            renderer.sharedMaterial = material;
            return go;
        }

 SkinnedMesh:骨骼蒙皮动画(参考 参考

 

 

 

基本原理可概括为:在骨骼控制下,通过顶点混合动态计算蒙皮网格的顶点,而骨骼的运动相对于其父骨骼,并由动画关键帧数据驱动。一个骨骼动画通常包括骨骼层次结构数据,网格(Mesh)数据,网格蒙皮数据(skin info)和骨骼的动画(关键帧)数据

bip001 以及它之下骨骼数53个

换装:参考 这个是unity插件换装

步骤:

    1.替换蒙皮网格(或者直接替换模型换装部位的GameObject,因为合并的时候会合并所有的蒙皮网格,而不会关心它是否属于原来角色身体的一部分,而且如果需要替换的部位有多个配件拥有独立的网格和贴图,那这种方式都可以正常执行。)

    2.合并所有蒙皮网格

    3.刷新骨骼

    4.附加材质(我下面是获取第一个材质作为默认材质)

    5.合并贴图(贴图的宽高最好是2的N次方的值)

    6.重新计算UV

 

using System;
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
public class CharacterCombine : MonoBehaviour
{
    // 目标物体(必须是骨骼的父物体,不然蒙皮失效)
    public GameObject target;
    // 最终材质(合并所有模型后使用的材质)
    public Material material;
    // 物体所有的部分
    private GameObject[] targetParts = new GameObject[9];
    private string[] defaultEquipPartPaths = new string[9];
    void Start()
    {
        // 把FBX的模型按部件分别放入Resources下对应的文件夹里,可以留空,模型需要蒙皮,而且所有模型使用同一骨骼
        // 最后的M是Fbx的模型,需要的Unity3D里设置好材质和贴图,部件贴图要勾选Read/Write Enabled
        defaultEquipPartPaths[0] = "Model/Player/GirlPlayer/Head/Head0000/M";
        defaultEquipPartPaths[1] = "Model/Player/GirlPlayer/Face/Face0000/M";
        defaultEquipPartPaths[2] = "Model/Player/GirlPlayer/Hair/Hair0000/M";
        defaultEquipPartPaths[3] = "";
        defaultEquipPartPaths[4] = "Model/Player/GirlPlayer/Body/Body0000/M";
        defaultEquipPartPaths[5] = "Model/Player/GirlPlayer/Leg/Leg0000/M";
        defaultEquipPartPaths[6] = "Model/Player/GirlPlayer/Hand/Hand0000/M";
        defaultEquipPartPaths[7] = "Model/Player/GirlPlayer/Foot/Foot0000/M";
        defaultEquipPartPaths[8] = "Model/Player/GirlPlayer/Wing/Wing0001/M";

        Destroy(target.GetComponent<SkinnedMeshRenderer>());
        for (int i = 0; i < defaultEquipPartPaths.Length; i++)
        {
            UnityEngine.Object o = Resources.Load(defaultEquipPartPaths[i]);
            if (o)
            {
                GameObject go = Instantiate(o) as GameObject;
                go.transform.parent = target.transform;
                go.transform.localPosition = new Vector3(0, -1000, 0);
                go.transform.localRotation = new Quaternion();
                targetParts[i] = go;
            }
        }

        StartCoroutine(DoCombine());
    }

    /// <summary>
    /// 使用延时,不然某些GameObject还没有创建
    /// </summary>
    /// <returns></returns>
    IEnumerator DoCombine()
    {
        yield return null;
        Combine(target.transform);
    }
    /// <summary>
    /// 合并蒙皮网格,刷新骨骼
    /// 注意:合并后的网格会使用同一个Material
    /// </summary>
    /// <param name="root">角色根物体</param>
    private void Combine(Transform root)
    {
        float startTime = Time.realtimeSinceStartup;

        List<CombineInstance> combineInstances = new List<CombineInstance>();
        List<Transform> boneList = new List<Transform>();
        Transform[] transforms = root.GetComponentsInChildren<Transform>();
        List<Texture2D> textures = new List<Texture2D>();

        int width = 0;
        int height = 0;

        int uvCount = 0;

        List<Vector2[]> uvList = new List<Vector2[]>();

        // 遍历所有蒙皮网格渲染器,以计算出所有需要合并的网格、UV、骨骼的信息
        foreach (SkinnedMeshRenderer smr in root.GetComponentsInChildren<SkinnedMeshRenderer>())
        {
            for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
            {
                CombineInstance ci = new CombineInstance();
                ci.mesh = smr.sharedMesh;
                ci.subMeshIndex = sub;
                combineInstances.Add(ci);
            }
            uvList.Add(smr.sharedMesh.uv);
            uvCount += smr.sharedMesh.uv.Length;

            if (smr.material.mainTexture != null)
            {
                textures.Add(smr.GetComponent<Renderer>().material.mainTexture as Texture2D);
                width += smr.GetComponent<Renderer>().material.mainTexture.width;
                height += smr.GetComponent<Renderer>().material.mainTexture.height;
            }
            foreach (Transform bone in smr.bones)
            {
                foreach (Transform item in transforms)
                {
                    if (item.name != bone.name) continue;
                    boneList.Add(item);
                    break;
                }
            }
        }
        // 获取并配置角色所有的SkinnedMeshRenderer
        SkinnedMeshRenderer tempRenderer = root.gameObject.GetComponent<SkinnedMeshRenderer>();
        if (!tempRenderer)
        {
            tempRenderer = root.gameObject.AddComponent<SkinnedMeshRenderer>();
        }
        tempRenderer.sharedMesh = new Mesh();
        // 合并网格,刷新骨骼,附加材质
        tempRenderer.sharedMesh.CombineMeshes(combineInstances.ToArray(), true, false);
        tempRenderer.bones = boneList.ToArray();
        tempRenderer.material = material;
        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++;
            }
        }
        // 设置贴图和UV
        tempRenderer.material.mainTexture = skinnedMeshAtlas;
        tempRenderer.sharedMesh.uv = atlasUVs;

        // 销毁所有部件
        foreach (GameObject goTemp in targetParts)
        {
            if (goTemp)
            {
                Destroy(goTemp);
            }
        }
        Debug.Log("合并耗时 : " + (Time.realtimeSinceStartup - startTime) * 1000 + " ms");
    }
    /// <summary>
    /// 获取最接近输入值的2的N次方的数,最大不会超过1024,例如输入320会得到512
    /// </summary>
    private int get2Pow(int into)
    {
        int outo = 1;
        for (int i = 0; i < 10; i++)
        {
            outo *= 2;
            if (outo > into)
            {
                break;
            }
        }
        return outo;
    }
}

 

posted @ 2020-10-24 13:47  Elijah_j  阅读(778)  评论(0编辑  收藏  举报