stl文件介绍及读取

STL文件规则

(1)共顶点规则
每一个三角面片必须与其相邻的每一个面片共两个顶点 ,即一个三角面片的顶点不能落在相邻的任何三角面片的边上;
(2)取向规则
单个面片法向量符合右手法(ccw)则且其法向量必须指向实体外面;
(3)充满规则
小三角面片必须布满三维模型的所有表面,不得有任何遗漏;
(4)取值规则
每个顶点的坐标值必须为非负 ,即 STL 文件的实体应该在坐标系的第一象限.

二进制STL文件

二进制STL文件用固定的字节数来给出三角面片的几何信息。
【80】文件起始的80个字节是文件头,用于存贮文件名
【4】紧接着用 4 个字节的整数来描述模型的三角面片个数(小端存储)
【50*n】后面逐个给出每个三角面片的几何信息。每个三角面片占用固定的50个字节(小端存储),依次是:
【12】3个4字节浮点数(角面片的法矢量)
【12】3个4字节浮点数(1个顶点的坐标)
【12】3个4字节浮点数(2个顶点的坐标)
【12】3个4字节浮点数(3个顶点的坐标)个
【2】三角面片的最后2个字节用来描述三角面片的属性信息。

ASCII文件格式

ASCII码格式的STL文件逐行给出三角面片的几何信息,每一行以1个或2个关键字开头。在STL文件中的三角面片的信息单元 facet 是一个带矢量方向的三角面片,STL 三维模型就是由一系列这样的三角面片构成。整个STL文件首行给出了文件路径及文件名。在一个 STL 文件中,每一个 facet 由 7 行数据组成,facet normal是三角面片指向实体外部的法矢量坐标,outer loop说明随后的3行数据分别是三角面片的 3 个顶点坐标,3 顶点沿指向实体外部的法矢量方向逆时针排列。

solid filename stl //自定义文件头
facet normal x y z //三角面片法向量的3个
outer loop
vertex x y z //三角面片第一个顶点坐标
vertex x y z //三角面片第二个顶点坐标
vertex x y z //三角面片第三个顶点坐标
endloop
endfacet //完成一个三角面片定义
……
endsolid filename stl ∥整个STL文件定义结束

注意

stl文件包含了一些错误信息

判断stl文件并读取

这里学习消化了MeshLab中的代码,MeshLab也使用了别的库,记不清是看的别的库还是MeshLab自己实现的代码了。

判断stl文件是二进制还是ASCII码

bool isSTLBinary(const char *filename)
{
    bool isBinary = false;//return value

    FILE *fp = nullptr;
    int errorCode = fopen_s(&fp,filename, "r");
    if (errorCode==0)//成功打开文件
    {
        //确定文件实际大小
        fseek(fp, 0, SEEK_END);//将fp移动到文件尾部
        int fileSize = ftell(fp);//返回文档首部到fp位置大小(bytes)
        int facetNum;
        //计算标准stl二进制文件的大小
        fseek(fp, STL_LABEL_SIZE, SEEK_SET);//跳过文档开头的注释
        fread(&facetNum, sizeof(int), 1, fp);//读取facet的数目并保存在facetNum中
        int standardBinaryFileSize = 80 + sizeof(int)+facetNum * 50;
        //判断是否是标准stl文件
        if (fileSize==standardBinaryFileSize)
        {
            isBinary = true;        
        }
        //判断是否是非标准stl文件
        unsigned char tmpbuf[128];//如果文件过短,这里会有Bug
        fread(tmpbuf, sizeof(tmpbuf), 1, fp);//读取128个char大小的数据
        for (unsigned int i = 0;i<sizeof(tmpbuf);i++)
        {
            //char类型取值范围是-128~127,如果是ASCII格式,应该全是char
            if (tmpbuf[i]>127)
            {
                isBinary = true;
                break;
            }
        }

        fclose(fp);
    }
    return isBinary;
}

读取二进制stl文件

先定义一个表示stl文件的类。这个类其实可以不要,可以按照自己的实际需求把从stl文件中读取的数据组织起来。比如说可以只读取其中的顶点信息而忽略法向量信息,我这里没有读取属性信息。

class STLDocument
{
public:
    class STLPoint
    {
    public:
        double x;
        double y;
        double z;
        explicit STLPoint()
        {
            x = y = z = 0;
        }
        explicit STLPoint(float *p_Array)
        {
            x = p_Array[0];
            y = p_Array[1];
            z = p_Array[2];
        }
        STLPoint& operator=(float *p_Array)
        {
            x = p_Array[0];
            y = p_Array[1];
            z = p_Array[2];
            return *this;
        }
    };
    class STLFacet
    {
    public:
        STLPoint m_PointList[3];//三个顶点
        STLPoint m_Normal;//法向量
    };

    vector<STLPoint> m_VertexList;
    vector<STLFacet> m_FacetList;
    void clear();  //清除数据
};//end class 

读取二进制stl文件,并将数据存在上面定义的类里面。

const int STL_LABEL_SIZE = 80;
bool openBinary(const std::string &p_FileName, STLDocument &p_STLDocument)
{
    p_STLDocument.clear();
    FILE *fp;
    int numFacet;
    int error= fopen_s(&fp,p_FileName.c_str(), "rb");
    float normal[3];
    float point1[3];
    STLDocument::STLPoint stlPoint;
    STLDocument::STLFacet stlFacet;
    if (0==error)//成功打开文件
    {
        fseek(fp, STL_LABEL_SIZE, SEEK_SET);//跳过开头
        fread(&numFacet, sizeof(int), 1, fp);//读取facet的数目
        for (int facetIndex = 0; facetIndex < numFacet;++facetIndex)
        {
            unsigned short attr; //用来储存属性,实际上把这个值丢弃了
            fread(normal, sizeof(float), 3, fp);//读取facet的法向量
            stlFacet.m_Normal = normal;
            for (int i = 0; i < 3;++i)
            {
                fread(point1, sizeof(float), 3, fp);//读取vertex
                stlFacet.m_PointList[i] = point1;
                stlPoint = point1;
                p_STLDocument.m_VertexList.push_back(stlPoint);
            }
            fread(&attr, sizeof(unsigned short), 1, fp);//读取属性
            p_STLDocument.m_FacetList.push_back(stlFacet);
        }
    }
    fclose(fp);
    return true;
}

读取ASCII码的stl文件

bool openASCII(const std::string &p_FileName, STLDocument &p_STLDocument)
{
    p_STLDocument.clear();
    ifstream ifs;
    ifs.open(p_FileName, ifstream::in);
    if (ifs.is_open())
    {
        /*略去第一行*/
        string line;
        getline(ifs, line);

        /*读取facet*/
        int readCounter = 0;//记录每行读取成功的数据项
        int lineCounter = 0;
        while (!ifs.eof())
        {
            STLDocument::STLFacet facet;
            STLDocument::STLPoint point;
            /*读取facet normal ni nj nk*/
            getline(ifs, line);
            readCounter = sscanf(line.c_str(), "%*s %*s %lf %lf %lf\n", &facet.m_Normal.x, &facet.m_Normal.y, &facet.m_Normal.z);
            if (readCounter!=3)
            {
                // we could be in the case of a multiple solid object, where after a endfaced instead of another facet we have to skip two lines:
                //     endloop
                //   endfacet
                //endsolid     <- continue on ret==0 will skip this line
                //solid ascii  <- and this one.
                //   facet normal 0.000000e+000 7.700727e-001 -6.379562e-001
                lineCounter++;
                continue;
            }//end if


            getline(ifs, line);//读取outer loop
            /*读取三个顶点坐标vertex v1x v1y v1z*/
            for (int i = 0; i < 3;i++)
            {
                getline(ifs, line);
                readCounter=sscanf(line.c_str(),"%*s %lf %lf %lf", &point.x, &point.y, &point.z);
                p_STLDocument.m_VertexList.push_back(point);
                facet.m_PointList[i] = point;           
            }
            p_STLDocument.m_FacetList.push_back(facet);
            getline(ifs, line);//读取“endloop”
            getline(ifs, line);//读取"endfacet"
            lineCounter += 7;
        }// end while (ifs.eof())
    }//end if (ifs.is_open())
    else
    {
        cout << "Error Opening file!"<<endl;
        return false;
    }
    ifs.close();
    return true;
}
  • 9
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值