STL文件格式有两种:ASCII字符格式,及二进制格式。
ASCII字符格式的格式如下:
solid name // 文件名是可选的字符串
facet normal ni nj nk
outer loop
vertex v1x v1y v1z
vertex v2x v2y v2z
vertex v3x v3y v3z
endloop
endfacet
endsolid name //结束行标志
二进制格式数据:
因为字符格式的STL文件比较大,占空间,因为有了二进制格式,且二进制的存储格式与ASCII的格式不同。二进制有80个字节作为文件头,一般都忽略掉,但开头不能使solid,不然就不能与ASCII格式相区分了。 另外,接下来4个字节是存放的三角片的个数。
UINT8[80] – Header
UINT32 – Number of triangles
foreach triangle
REAL32[3] – Normal vector
REAL32[3] – Vertex 1
REAL32[3] – Vertex 2
REAL32[3] – Vertex 3
UINT16 – Attribute byte count
end
读取文件之前需判断文件为哪种格式:
bool isSTLBinary(const char* fileName)
{
bool isBinary = false;//return value
FILE *fp = fopen(fileName, "r");
//int errorCode = fopen_s(&fp, fileName, "r");
// #ifdef __unix
// #define fopen_s(pFile,filename, "r") ((*(pFile))=fopen((fileName), ("r")))==NULL
// #endif
if (fp)//成功打开文件
{
printf("%s isSTLBinary open success\n", fileName);
//确定文件实际大小
fseek(fp, 0, SEEK_END);//将fp移动到文件尾部
int fileSize = ftell(fp);//返回文档首部到fp位置大小(bytes)
int facetNum;
//计算标准stl二进制文件的大小
fseek(fp, 80, SEEK_SET);//跳过文档开头的注释
size_t t = 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
t = 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);
printf("t=%d\n", (int)t);
}
else
{
printf("%s isSTLBinary open file fail\n", fileName);
}
return isBinary;
}
之后再根据文件格式选择读取方法。
读取二进制文件:
void openBinaryFile(std::string fileName, std::vector<float>& positions, std::vector<float>& normals)
{
FILE *fp = fopen(fileName.c_str(), "rb");
int numFacet;
//int error= fopen_s(&fp,fileName, "rb");
float normal[3];
float point1[3];
if (fp)//成功打开文件
{
fseek(fp, 80, SEEK_SET);//跳过开头
size_t t = fread(&numFacet, sizeof(int), 1, fp);//读取facet的数目
for (int facetIndex = 0; facetIndex < numFacet;++facetIndex)
{
unsigned short attr; //用来储存属性,实际上把这个值丢弃了
t = fread(normal, sizeof(float), 3, fp);//读取facet的法向量
for (int i = 0; i < 3;++i)
{
t = fread(point1, sizeof(float), 3, fp);//读取vertex
positions.push_back(point1[0] / 1000);
positions.push_back(point1[1] / 1000);
positions.push_back(point1[2] / 1000);
normals.push_back(point1[0] / 1000);
normals.push_back(point1[1] / 1000);
normals.push_back(point1[2] / 1000);
//printf ("%f, %f %f", point1[0], point1[1], point1[2]);
}
t = fread(&attr, sizeof(unsigned short), 1, fp);//读取属性
}
printf("t=%d\n", (int)t);
}
else
{
printf("%s open fail\n", fileName.c_str());
}
fclose(fp);
}
读取ASCII文件:
void openASCIIFile(std::string fileName, std::vector<float>& positions, std::vector<float>& normals)
{
std::ifstream ifs;
ifs.open(fileName, std::ifstream::in);
if (ifs.is_open())
{
/*略去第一行*/
std::string line;
getline(ifs, line);
/*读取facet*/
int readCounter = 0;//记录每行读取成功的数据项
int lineCounter = 0;
double x,y,z = 0;
double pointX, pointY, pointZ = 0;
while (!ifs.eof())
{
/*读取facet normal ni nj nk*/
getline(ifs, line);
readCounter = sscanf(line.c_str(), "%*s %*s %lf %lf %lf\n", &x, &y, &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", &pointX, &pointY, &pointZ);
positions.push_back(pointX);
positions.push_back(pointY);
positions.push_back(pointZ);
normals.push_back(pointX);
normals.push_back(pointY);
normals.push_back(pointZ);
}
getline(ifs, line);//读取“endloop”
getline(ifs, line);//读取"endfacet"
lineCounter += 7;
}// end while (ifs.eof())
}//end if (ifs.is_open())
else
{
return;
}
ifs.close();
}
STL 类
class CSTL
{
private:
float *vx;
float *vy;
float *vz;
int *triaV1;
int *triaV2;
int *triaV3;
int m_TriaNum;
public:
CSTL();
~CSTL();
public:
bool SetStlInf(float *px,float *py,float *pz,
int *TriaNum1,int *TriaNum2,int *TriaNum3,int TriaTotalNum);
bool SaveStlBinary(char *pathname,char * filename);
bool SaveStlASCII(char *pathname,char * filename);
};
CSTL::CSTL()
{
}
CSTL::~CSTL()
{
}
设置STL信息
bool CSTL::SetStlInf(float *px, float *py, float *pz,
int *TriaNum1, int *TriaNum2, int *TriaNum3, int TriaTotalNum)
{
bool success = false;
vx = px;
vy = py;
vz = pz;
triaV1 = TriaNum1;
triaV2 = TriaNum2;
triaV3 = TriaNum3;
m_TriaNum = TriaTotalNum;
success = true;
return success;
}
保存STL ASCLL 格式文件
bool CSTL::SaveStlASCII(char *pathname,char * filename)
{
bool suc = true;
char *savename = new char[100];
sprintf(savename,"%s%s.ast",pathname,filename);
char *fileInf = new char[200];
sprintf(fileInf,"solid %s.ast %s",filename,"by master");
FILE *fp = fopen(savename,"w");
fprintf(fp,"%s\n",fileInf);
delete []savename;
for(int i=0;i<m_TriaNum;i++)
{
int id= triaV1[i];
float v1x = vx[id];
float v1y = vy[id];
float v1z = vz[id];
id= triaV2[i];
float v2x = vx[id];
float v2y = vy[id];
float v2z = vz[id];
id= triaV3[i];
float v3x = vx[id];
float v3y = vy[id];
float v3z = vz[id];
float nx = (v1y-v3y)*(v2z-v3z)-(v1z-v3z)*(v2y-v3y);
float ny = (v1z-v3z)*(v2x-v3x)-(v2z-v3z)*(v1x-v3x);
float nz = (v1x-v3x)*(v2y-v3y)-(v2x-v3x)*(v1y-v3y);
float nxyz = sqrt(nx*nx+ny*ny+nz*nz);
fprintf(fp,"facet normal %f %f %f\n",nx/nxyz,ny/nxyz,nz/nxyz);
fprintf(fp,"outer loop\n");
fprintf(fp,"vertex %f %f %f\n",v1x,v1y,v1z);
fprintf(fp,"vertex %f %f %f\n",v2x,v2y,v2z);
fprintf(fp,"vertex %f %f %f\n",v3x,v3y,v3z);
fprintf(fp,"endloop\n");
fprintf(fp,"endfacet\n");
}
sprintf(fileInf,"endsolid %s.ast %s",filename,"by master");
fprintf(fp,"%s\n",fileInf);
fclose(fp);
delete []fileInf;
return suc;
}
保存STL 二进制格式文件
bool CSTL::SaveStlBinary(char *pathname,char * filename)
{
bool suc = true;
char *savename = new char[100];
sprintf(savename,"%s%s.stl",pathname,filename);
char *fileInf = new char[200];
sprintf(fileInf,"solid %s.stl %s",filename,"by master");
FILE *fp = fopen(savename,"wb");
delete []savename;
float* dat = new float[12];
fwrite(fileInf,sizeof(char),80,fp);
fwrite(&m_TriaNum,sizeof(int),1,fp);
for(int i=0;i<m_TriaNum;i++)
{
int id= triaV1[i];
float v1x = vx[id];
float v1y = vy[id];
float v1z = vz[id];
id= triaV2[i];
float v2x = vx[id];
float v2y = vy[id];
float v2z = vz[id];
id= triaV3[i];
float v3x = vx[id];
float v3y = vy[id];
float v3z = vz[id];
float nx = (v1y-v3y)*(v2z-v3z)-(v1z-v3z)*(v2y-v3y);
float ny = (v1z-v3z)*(v2x-v3x)-(v2z-v3z)*(v1x-v3x);
float nz = (v1x-v3x)*(v2y-v3y)-(v2x-v3x)*(v1y-v3y);
float nxyz = sqrt(nx*nx+ny*ny+nz*nz);
dat[0] = nx/nxyz;
dat[1] = ny/nxyz;
dat[2] = nz/nxyz;
dat[3] = v1x;
dat[4] = v1y;
dat[5] = v1z;
dat[6] = v2x;
dat[7] = v2y;
dat[8] = v2z;
dat[9] = v3x;
dat[10] = v3y;
dat[11] = v3z;
fwrite(dat,sizeof(float),12,fp);
fwrite("wl",sizeof(char),2,fp);
}
fclose(fp);
delete []dat;
delete []fileInf;
return suc;
}