参考文献:C++读取STL模型文件
1. 前言
上一篇博客介绍了如何读取基于ASCII格式储存的STL三维模型文件,并用OpenGL进行了显示。
由于ASCII文件格式有大量用于标记格式的字符串,例如outer loop
、endloop
等等,当三角形面片的个数达到数万个,这样的格式会产生大量的无效信息。因此,不少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站瞧一瞧?)