Unity导入STL格式模型(二)

发表于2018-03-01
评论0 3.8k浏览
接上篇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组件使用,方便了很多。

github源码链接:https://github.com/SaiTingHu/ImportSTL
来自:http://blog.csdn.net/qq992817263/article/details/72738344

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引