【OpenGL】CPP读取STL文件 含读入优化 二进制 ASCII

参考文献:C++读取STL模型文件

在这里插入图片描述

1. 前言

上一篇博客介绍了如何读取基于ASCII格式储存的STL三维模型文件,并用OpenGL进行了显示。

由于ASCII文件格式有大量用于标记格式的字符串,例如outer loopendloop等等,当三角形面片的个数达到数万个,这样的格式会产生大量的无效信息。因此,不少CAD在导出STL模型文件时,都会默认采用二进制格式

我们再来复习一下二进制格式的STL文件结构:

UINT8//Header//文件头,共80字节,存贮文件名;  
UINT32//Numberoftriangles//4个字节的整数描述三角面片数量  
//foreachtriangle(每个三角面片中占用50字节)  
REAL32[3] //Normalvector//法线矢量,3个4字节浮点数  
REAL32[3] //Vertex1//顶点1坐标,3个4字节浮点数  
REAL32[3] //Vertex2//顶点2坐标,3个4字节浮点数  
REAL32[3] //Vertex3//顶点3坐标,3个4字节浮点数  
UINT16//Attributebytecountend//二个字节,文件属性统计  

2. 程序

2.1 读入优化(先读入内存缓冲区)

这一次,我们希望能够兼顾二进制格式和ASCII格式的STL文件的读取,并加入一定的读入优化,来提高程序运行的速度。把文件读入内存缓冲区是一种常见的优化方法。

void STL_Read()
{
    int stl_i;//STL_Read子函数中的for循环用到的变量
    int triangle_num;//三角形面片的个数
    long fileSize,result;//fileSize==文件大小,result==一个验证读取是否成功的返回值
    char* buffer;//内存缓冲区
    FILE *STL_file;//文件指针

    //STL_file = fopen("1_ASCII.stl","r+");
    STL_file = fopen("1.stl","r+");

    if(STL_file == NULL)
        printf("Fail to read file\n");
    //////////////////////
    fseek(STL_file,0,SEEK_END);
    fileSize = ftell(STL_file);//读取文件大小
    rewind(STL_file);
    ////////////////////////////
    buffer = (char*)malloc(sizeof(char)*fileSize);//申请内存
    if (buffer == NULL)
        printf("Memory error");

    result = fread(buffer,1,fileSize,STL_file);//文件读入buffer
    if (result != fileSize)
    {
        printf("Reading error or ASCII file: ");
        printf("result = %ld  fileSize = %ld\n",result,fileSize);
    }
    fclose(STL_file);
    //////////////////////////////////////上面的代码把文件读入内存(缓冲区)

2.2 判断STL文件格式

在把文件读入内存之后,需要判断STL文件格式是ASCII还是二进制格式。

用Inventor导出的ASCII格式中,最开始出现的字符串是“solid ASCII”,而二进制文件头一般存储的都是文件名,这也就是为什么STL文件不能以包含“ASCII”的字符串命名

//判断STL文件格式是ASCII还是二进制格式
char judger[13]={'s','o','l','i','d',' ','A','S','C','I','I','\n','\0'};
    for(stl_i=0;stl_i<12;stl_i++)
        if(buffer[stl_i]!=judger[stl_i])
            judger[12]='1'; //如果不符合,则说明是二进制文件
                            //注意我这里用的是字符‘1’,其实它只要不是 数值0或者字符‘/0’,都可以进入下面的二进制读取部分

2.3 ASCII格式STL文件的读取

if (judger[12]==0)//判断格式为ASCII
    {
        triangle_num = 0;
        float x,y,z;
        string name,word;
        stringstream ss(buffer);//string stream能够把内存中的内容以类似cin,cout的方式读取,需要引用头文件<sstream>
        ss >> name >> name;
        ss.get();
        do{
            ss>>word;
            if(word != "facet")
                break;
            getline(ss,word);
            getline(ss,word);
            //////////////////////////////////////
            ss >> word >> x >> y >> z;
            vertices[VerticesCnt]=x * 0.1;VerticesCnt++;
            vertices[VerticesCnt]=y * 0.1;VerticesCnt++;
            vertices[VerticesCnt]=z * 0.1;VerticesCnt++;
            vertices[VerticesCnt] = 0.1;VerticesCnt++;
            vertices[VerticesCnt] = 0.4;VerticesCnt++;
            vertices[VerticesCnt] = 0.5;VerticesCnt++;
            //vertex
            ss >> word >> x >> y >> z;
            vertices[VerticesCnt]=x * 0.1;VerticesCnt++;
            vertices[VerticesCnt]=y * 0.1;VerticesCnt++;
            vertices[VerticesCnt]=z * 0.1;VerticesCnt++;
            vertices[VerticesCnt] = 0.5;VerticesCnt++;
            vertices[VerticesCnt] = 0.5;VerticesCnt++;
            vertices[VerticesCnt] = 1.0;VerticesCnt++;
            //vertex
            ss >> word >> x >> y >> z;
            vertices[VerticesCnt]=x * 0.1;VerticesCnt++;
            vertices[VerticesCnt]=y * 0.1;VerticesCnt++;
            vertices[VerticesCnt]=z * 0.1;VerticesCnt++;
            vertices[VerticesCnt] = 1.0;VerticesCnt++;
            vertices[VerticesCnt] = 1.0;VerticesCnt++;
            vertices[VerticesCnt] = 1.0;VerticesCnt++;
            triangle_num++;
            ////////////////////
            getline(ss,word);
            getline(ss,word);
            getline(ss,word);
        }while(1);
    }

2.4 二进制STL文件的读取

(代码直接跟随上一段)

else//二进制格式读取
{
    const char* p = buffer;//p为读取内存的指针
    char name[80];
    float temp;
    int binary_i;
    memcpy(name,p,80);//记录文件名
    p += 80;
    memcpy(&triangle_num,p,4);//记录三角形个数
    p += 4;
    for (binary_i=0;binary_i<triangle_num;binary_i++)
    {
        p += 12;//跳过头部的法向量
        //vertex
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[0]赋值
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[1]赋值
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[2]赋值,VerticesCnt=3
        vertices[VerticesCnt] = 0.1;VerticesCnt++;
        vertices[VerticesCnt] = 0.4;VerticesCnt++;
        vertices[VerticesCnt] = 0.5;VerticesCnt++;
        //vertex
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[0]赋值
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[1]赋值
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[2]赋值,VerticesCnt=3
        vertices[VerticesCnt] = 0.5;VerticesCnt++;
        vertices[VerticesCnt] = 0.5;VerticesCnt++;
        vertices[VerticesCnt] = 1.0;VerticesCnt++;
        //vertex
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[0]赋值
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[1]赋值
        memcpy(&temp,p,4);vertices[VerticesCnt] = temp * 0.1;p+=4;VerticesCnt++;//vertices[2]赋值,VerticesCnt=3
        vertices[VerticesCnt] = 1.0;VerticesCnt++;
        vertices[VerticesCnt] = 1.0;VerticesCnt++;
        vertices[VerticesCnt] = 1.0;VerticesCnt++;
        p += 2;//跳过尾部标志
    }
}

2.5 释放缓冲区内存

然后再把缓冲区的内存释放

free(buffer);
}//void STL_Read函数的结尾

3. 总结

最后,和之前的代码一样,只需要使用STL_Read函数,就可以把顶点数据读入Vertices数组里面,再用OpenGL进行显示就可以了。

觉得有用的话,不要吝惜评论点赞分享哦,希望大家多多包涵,有任何问题欢迎指正、讨论。
本文基于CC-BY-SA 4.0协议,欢迎转载
(博客看累了?去我的B站瞧一瞧?)

猜你喜欢

转载自blog.csdn.net/qq_27133869/article/details/105645644