STL三维模型切片(一)——文件导入

STL格式的三维模型由大量三角形面片构成,文件中只记录了每个三角形的基本信息,包括三个顶点及其法向量(表示内外)。在对该模型进行处理之前,首先必须将其导入进来。STL格式的有两种记录形式:二进制及文本方式,二者没有本质区别,都只是记录了三角形的信息。先放代码,再逐个功能进行分析。

private MeshGeometry3D GetMeshModelFromSTLFile(string path)
{

    StreamReader srFirstLine = new StreamReader(path);
    string fileType = srFirstLine.ReadLine().Substring(0, 5);
    string secondLine = srFirstLine.ReadLine().Trim().Substring(0, 5);
    srFirstLine.Close();

    MeshGeometry3D mesh3D = new MeshGeometry3D();
    int indexOfTriangles = 0;
    if (fileType == "solid" && secondLine == "facet") //ASCII type
    {
        StreamReader sr = new StreamReader(path);
        sr.ReadLine();//remove first line
        while (sr.ReadLine() != null) //remove facet normal line
        {
            if (sr.ReadLine() == null)
            {
                break;
            }//remove outer loop line

            mesh3D.Positions.Add(StringToPoint3D(sr.ReadLine()));
            mesh3D.TriangleIndices.Add(indexOfTriangles++);

            mesh3D.Positions.Add(StringToPoint3D(sr.ReadLine()));
            mesh3D.TriangleIndices.Add(indexOfTriangles++);

            mesh3D.Positions.Add(StringToPoint3D(sr.ReadLine()));
            mesh3D.TriangleIndices.Add(indexOfTriangles++);

            sr.ReadLine(); //remove endloop
            sr.ReadLine(); //remove endfacet

        }
        sr.Close();
        return mesh3D;

        Point3D StringToPoint3D(string vertexStr)
        {
            var tt = vertexStr.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            return new Point3D
            {
                X = Convert.ToDouble(tt[1]),
                Y = Convert.ToDouble(tt[2]),
                Z = Convert.ToDouble(tt[3])
            };
        }
    }
    else //Binary type
    {
        BinaryReader br = new BinaryReader(File.OpenRead(path));
        br.ReadBytes(80);//去掉80字节
        var count = (int)br.ReadUInt32();//三角形数目
        for (int i = 0; i < count; i++)
        {
            br.ReadBytes(12);//三个4字节表示三角形法向量,不需要
            mesh3D.Positions.Add(new Point3D { X = br.ReadSingle(), Y = br.ReadSingle(), Z = br.ReadSingle() });
            mesh3D.TriangleIndices.Add(indexOfTriangles++);

            mesh3D.Positions.Add(new Point3D { X = br.ReadSingle(), Y = br.ReadSingle(), Z = br.ReadSingle() });
            mesh3D.TriangleIndices.Add(indexOfTriangles++);

            mesh3D.Positions.Add(new Point3D { X = br.ReadSingle(), Y = br.ReadSingle(), Z = br.ReadSingle() });
            mesh3D.TriangleIndices.Add(indexOfTriangles++);

            br.ReadBytes(2);//去掉最后两个字节
        }
        br.Close();
        return mesh3D;
    }

}

首先,我们需要判断STL文件是二进制还是文本格式。一般来说,文本格式第一行以solid开头,即当第一行前5个字符为solid时,可以认为该文件为文本格式。经过大量检验,发现该方法是可行的。直到有一次发现某个二进制格式的STL文件开头5个字符也是solid,说明该方法不能百分之百保证判断准确。于是,添加第二行判断,文本格式的STL文件第二行以facet开头,两者都满足的情况下,基本可以确定该文件为文本格式,否则为二进制格式。

随后,确定格式后,分别按其格式字义逐行(字)读取,并添加到Mesh对象中,这样就将STL文件读取进来了。