Unity导入STL格式模型(二)

在群里一位朋友的提点下,以分成多个子网格的方式解决了导入慢以及目标模型顶点过多的问题,在此我要非常诚心的感谢他!


优化之后,目前针对二进制STL文件有比较良好的解析能力。




MeshCompression:是否使用压缩模式,Off为不使用,跳过压缩算法,导入模型的时间可以短到忽略不计。

    On为使用,会为每个子网格单独压缩,也就是删掉所有他认为重复的顶点(位置相等他就认为重复)。

SingleTrianglesNumber:以此数量做为子网格的面数进行拆分,将模型分为多个子网格。

SaveMesh:是否保存模型的所有子网格至本地磁盘?在CreateInstance至Scene中之后。


照例,贴所有核心代码:


    /// <summary>
    /// 创建STL模型实例
    /// </summary>
    private void CreateInstance()
    {
        if (_singleTrianglesNumber < 1000 || _singleTrianglesNumber > 20000)
        {
            Debug.LogError("Single Triangles Number: this value is unreasonable!");
            return;
        }
        if (int.Parse(_trianglescount) > 200000)
        {
            Debug.LogError("Triangles Count: this value is too much!");
            return;
        }

        string fullPath = Path.GetFullPath(AssetDatabase.GetAssetPath(target));

        _total = int.Parse(_trianglescount);
        _number = 0;
        _binaryReader = new BinaryReader(File.Open(fullPath, FileMode.Open));

        //抛弃前84个字节
        _binaryReader.ReadBytes(84);

        _vertices = new List<Vector3>();
        _normals = new List<Vector3>();
        _triangles = new List<int>();

        //读取顶点信息
        Thread t = new Thread(ReadVertex);
        t.Start();

        while (_number < _total)
        {
            EditorUtility.DisplayProgressBar("读取信息", "正在读取顶点信息(" + _number + "/" + _total + ")......", (float)_number / _total);
        }

        CreateGameObject();

        _binaryReader.Close();
        EditorUtility.ClearProgressBar();
    }

    /// <summary>
    /// 读取顶点信息
    /// </summary>
    private void ReadVertex()
    {
        while (_number < _total)
        {
            byte[] bytes;
            bytes = _binaryReader.ReadBytes(50);

            if (bytes.Length < 50)
            {
                _number += 1;
                continue;
            }

            Vector3 vec0 = new Vector3(BitConverter.ToSingle(bytes, 0), BitConverter.ToSingle(bytes, 4), BitConverter.ToSingle(bytes, 8));
            Vector3 vec1 = new Vector3(BitConverter.ToSingle(bytes, 12), BitConverter.ToSingle(bytes, 16), BitConverter.ToSingle(bytes, 20));
            Vector3 vec2 = new Vector3(BitConverter.ToSingle(bytes, 24), BitConverter.ToSingle(bytes, 28), BitConverter.ToSingle(bytes, 32));
            Vector3 vec3 = new Vector3(BitConverter.ToSingle(bytes, 36), BitConverter.ToSingle(bytes, 40), BitConverter.ToSingle(bytes, 44));

            _normals.AddNormal(vec0);
            _triangles.AddTriangle(_vertices.AddGetIndex(vec1), _vertices.AddGetIndex(vec2), _vertices.AddGetIndex(vec3));

            _number += 1;
        }
    }

    /// <summary>
    /// 创建GameObject
    /// </summary>
    private void CreateGameObject()
    {
        string path = AssetDatabase.GetAssetPath(target);
        string fullPath = Path.GetFullPath(path);
        string assetPath = path.Substring(0, path.LastIndexOf("/")) + "/";

        GameObject root = new GameObject(Path.GetFileNameWithoutExtension(fullPath));
        root.transform.localPosition = Vector3.zero;
        root.transform.localScale = Vector3.one;

        int count = _total / _singleTrianglesNumber;
        count += (_total % _singleTrianglesNumber > 0) ? 1 : 0;

        for (int i = 0; i < count; i++)
        {
            GameObject tem = new GameObject(Path.GetFileNameWithoutExtension(fullPath) + "Sub" + i);
            tem.transform.SetParent(root.transform);
            tem.transform.localPosition = Vector3.zero;
            tem.transform.localScale = Vector3.one;

            MeshFilter mf = tem.AddComponent<MeshFilter>();
            MeshRenderer mr = tem.AddComponent<MeshRenderer>();

            int startIndex = i * _singleTrianglesNumber * 3;
            int length = _singleTrianglesNumber * 3;
            if ((startIndex + length) > _vertices.Count)
            {
                length = _vertices.Count - startIndex;
            }

            List<Vector3> vertices = _vertices.GetRange(startIndex, length);
            List<Vector3> normals = _normals.GetRange(startIndex, length);
            List<int> triangles = _triangles.GetRange(0, length);

            //压缩网格
            if (_meshCompression.IsOn())
            {
                MeshCompression(tem.name, vertices, normals, triangles);
            }

            Mesh m = new Mesh();
            m.name = tem.name;
            m.vertices = vertices.ToArray();
            m.normals = normals.ToArray();
            m.triangles = triangles.ToArray();
            m.RecalculateNormals();
            mf.mesh = m;
            mr.material = new Material(Shader.Find("Standard"));

            Debug.Log("Create done! " + tem.name + ": Vertex Number " + m.vertices.Length);
        }
    }

    /// <summary>
    /// 压缩网格
    /// </summary>
    /// <param name="meshName">网格名称</param>
    /// <param name="vertices">需要压缩的网格顶点数组</param>
    /// <param name="normals">与之对应的法线数组</param>
    /// <param name="triangles">与之对应的三角面数组</param>
    private void MeshCompression(string meshName, List<Vector3> vertices, List<Vector3> normals, List<int> triangles)
    {
        //移位补偿,当顶点被标记为待删除顶点时
        int offset = 0;
        //需要删除的顶点索引集合
        List<int> removes = new List<int>();
        for (int i = 0; i < vertices.Count; i++)
        {
            EditorUtility.DisplayProgressBar("压缩网格", "正在压缩网格[ " + meshName + " ](" + i + "/" + vertices.Count + ")......", (float)i / vertices.Count);
            if (removes.Contains(i))
            {
                offset += 1;
                continue;
            }

            triangles[i] = i - offset;
            for (int j = i + 1; j < vertices.Count; j++)
            {
                if (vertices[i] == vertices[j])
                {
                    removes.Add(j);
                    triangles[j] = triangles[i];
                }
            }
        }

        removes.Sort();
        removes.Reverse();

        for (int i = 0; i < removes.Count; i++)
        {
            vertices.RemoveAt(removes[i]);
            normals.RemoveAt(removes[i]);
        }
    }



测试:使用不压缩网格模式,点击CreateInstance,导入的时间快到可以忽略不计,而且目标已被拆分为多个网格,再多顶点的模型也不用担心超过上限:




没有经过压缩的顶点数量分别为:





测试:然后我们使用压缩网格模式,点击CreateInstance,注意,这里勾选SaveMesh,以便于将网格保存在本地,便可重复使用,毕竟压缩模式导入比较慢,不用每次使用都重新压缩网格:




压缩之后的结果:




可以看到顶点几乎被压缩掉了四分之三,如果要考虑性能的话,还是使用压缩网格比较好,而且因为他替换掉的都是位置重复顶点,这些在视觉上是看不出任何差异的,而且我们保存在本地的Mesh文件之后便可以直接通过拖拽给MeshFilter组件使用,方便了很多。



你可能感兴趣的:(unity,STL,网格)