接上篇Unity导入STL格式模型(一),分成多个子网格的方式解决了导入慢以及目标模型顶点过多的问题,优化之后,目前针对二进制STL文件有比较良好的解析能力。
MeshCompression:是否使用压缩模式
Off为不使用,跳过压缩算法,导入模型的时间可以短到忽略不计。
On为使用,会为每个子网格单独压缩,也就是删掉所有他认为重复的顶点(位置相等他就认为重复)。
SingleTrianglesNumber:以此数量做为子网格的面数进行拆分,将模型分为多个子网格。
SaveMesh:是否保存模型的所有子网格至本地磁盘?在CreateInstance至Scene中之后。
核心代码如下:
///
/// 创建STL模型实例
///
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();
_normals = new List();
_triangles = new List();
//读取顶点信息
Thread t = new Thread(ReadVertex);
t.Start();
while (_number < _total)
{
EditorUtility.DisplayProgressBar("读取信息", "正在读取顶点信息(" + _number + "/" + _total + ")......", (float)_number / _total);
}
CreateGameObject();
_binaryReader.Close();
EditorUtility.ClearProgressBar();
}
///
/// 读取顶点信息
///
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;
}
}
///
/// 创建GameObject
///
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();
MeshRenderer mr = tem.AddComponent();
int startIndex = i * _singleTrianglesNumber * 3;
int length = _singleTrianglesNumber * 3;
if ((startIndex + length) > _vertices.Count)
{
length = _vertices.Count - startIndex;
}
List vertices = _vertices.GetRange(startIndex, length);
List normals = _normals.GetRange(startIndex, length);
List 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);
}
}
///
/// 压缩网格
///
/// 网格名称
/// 需要压缩的网格顶点数组
/// 与之对应的法线数组
/// 与之对应的三角面数组
private void MeshCompression(string meshName, List vertices, List normals, List triangles)
{
//移位补偿,当顶点被标记为待删除顶点时
int offset = 0;
//需要删除的顶点索引集合
List removes = new List();
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源码链接:/SaiTingHu/ImportSTL
来自:/qq992817263/article/details/72738344