Unity导入STL格式模型(二)
发表于2018-03-01
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