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
