前言:
3D 的stl 模型文件分为两种 二进制和Ascii
明码的Ascii 内容清晰可以打开看,但是文件比较大
二进制的文件 看不到内容 但是占用空间小
我是用 qt + opengl 加载 stl 模型
本章写 qt c++ 加载 ascii 格式 和 二进制格式的 stl 文件
ASCII 格式:
/* ASCII STL 格式
*
*
* solid
* facet normal +0.0000000E+00 +1.0000000E+00 +0.0000000E+00
outer loop
vertex +8.0000000E+01 +1.0000000E+02 +6.0000000E+01
vertex +8.0000000E+01 +1.0000000E+02 +0.0000000E+00
vertex +0.0000000E+00 +1.0000000E+02 +0.0000000E+00
endloop
endfacet
......
endsolid
*/
文件头 solid 后面跟着的是文件描述
下面的7行 是一个三角形的描述
有多少个三角形就有多少个7行
然后包含1行法向量信息
3行顶点信息
文件结尾 endsolid
读取ASCII 格式代码
void loadAsciiSTL(std::string path,std::vector<FACET> &facetVec);
void Widget::loadAsciiSTL(std::string path, std::vector<FACET> &facetVec)
{
/* ASCII STL 格式
*
*
* solid
* facet normal +0.0000000E+00 +1.0000000E+00 +0.0000000E+00
outer loop
vertex +8.0000000E+01 +1.0000000E+02 +6.0000000E+01
vertex +8.0000000E+01 +1.0000000E+02 +0.0000000E+00
vertex +0.0000000E+00 +1.0000000E+02 +0.0000000E+00
endloop
endfacet
......
endsolid
* 共计7行
*
*
*
*
*
* */
FILE* file = fopen(path.c_str(),"r");
//把文件指针移动到文件末尾,并计算文件大小
fseek(file,0L,SEEK_END);
long fileSize = ftell(file);
fclose(file);
file = fopen(path.c_str(),"r");
//计算文件有多少行
int fileLines = 0;
for(long i = 0;i<fileSize;i++)
//一个字符一个字符的读取 当读到换行符说明读到了一行 直到结束
if(getc(file) == '\n')
fileLines++;
//计算三角形的个数
int triangleCount = 0;
//一段三角形的描述是7行 stl文件格式决定的
triangleCount = fileLines / 7;
//文件指针移到文件头
rewind(file);
//跳过文件头 solid
while(getc(file) != '\n');
for(auto i = 0; i< triangleCount;i++)
{
FACET temp;
//读法向量
fscanf(file,"%*s %*s %f %f %f\n",&temp.normal[0],&temp.normal[1],&temp.normal[2]);
//跳过 outer loop
fscanf(file,"%*s %*s");
//读顶点数据
fscanf(file,"%*s %f %f %f\n",&temp.vertex[0][0],&temp.vertex[0][1],&temp.vertex[0][2]);
fscanf(file,"%*s %f %f %f\n",&temp.vertex[1][0],&temp.vertex[1][1],&temp.vertex[1][2]);
fscanf(file,"%*s %f %f %f\n",&temp.vertex[2][0],&temp.vertex[2][1],&temp.vertex[2][2]);
//跳过 endloop
fscanf(file,"%*s");
//跳过 endfacet
fscanf(file,"%*s");
facetVec.push_back(temp);
}
//跳过文件尾部 endsolid
while(getc(file) != '\n');
fclose(file);
delete file;
}
二进制文件的格式
起始是80个字节的 头
下面是 UINT32 三角形的个数 4字节
三角形信息部分
法向量 float3 4x3 12字节字节
顶点1 float3 4x3 12字节
顶点2 float3 4x3 12字节
顶点3 float3 4x3 12字节
uint16 三角形的属性信息没有用 2字节
共计 12*4 +2 = 50字节
读取二进制文件代码
void loadBinarySTL(std::string path,std::vector<FACET> &facetVec);
void Widget::loadBinarySTL(std::string path, std::vector<FACET> &facetVec)
{
FILE* file = fopen(path.c_str(),"r");
/*80字节文件头*/
char header[80];
fread(header,80,1,file);
uint32_t triangleNum;
fread(&triangleNum,sizeof(uint32_t),1,file);
for(auto i = 0; i< triangleNum;i++)
{
/*12字节法向量*/
FACET temp;
fread(&temp.normal[0],sizeof(float),1,file);
fread(&temp.normal[1],sizeof(float),1,file);
fread(&temp.normal[2],sizeof(float),1,file);
/*36字节顶点*/
for(auto j = 0; j< 3;j++)
{
fread(&temp.vertex[j][0],sizeof(float),1,file);
fread(&temp.vertex[j][1],sizeof(float),1,file);
fread(&temp.vertex[j][2],sizeof(float),1,file);
}
/*2字节三角型面片的属性信息*/
char c[2];
fread(c,2,1,file);
facetVec.push_back(temp);
}
fclose(file);
delete file;
}
注意我们要先判断是Ascii还是二进制然后在确定调用哪个接口
我这里写了一个函数来判断是什么格式
bool isAsciiFileType(QString path);
bool Widget::isAsciiFileType(QString path)
{
QFile file(path);
char ch[80];
if(file.open(QIODevice::ReadOnly))
{
file.read(ch,80);
file.close();
//读取80个字节 如果是ascii 就会换行了 如果是二进制的不会有换行
for(auto i = 0; i< 80;i++)
{
if(ch[i] == '\n')
{
qDebug()<<"this is ascii";
return true;
}
}
return false;
}
qDebug()<<"Open File Failed!";
return false;
}
因为2进制的 第一行是80字节 但是ascii 第一行没有80字节
所以我们读取80字节 如果包含换行符说明就是ASCII 因为它换行了
使用的话就这样
if(isAsciiFileType(""))
loadAsciiSTL()
else
loadBinarySTL()