在wiki上有对STL图形文件格式的详细描述。
http://en.wikipedia.org/wiki/STL_(file_format)
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个字节是存放的三角片的个数,这个是ASCII格式所没有 的。
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
读取STL文件,首先,程序要区别读取的是那种格式的STL。然后根据格式,来读取数据。
先定义如下数据类型:
template <int D, class T = float>
class Vec {
private:
T v[D];
public:
...
};
typedef Vec<3,float> Vec3f;
用于存放点坐标数据和法向量数据,这里将点坐标和法向量都读到一个数组中。
- bool ReadSTLFile(const char *cfilename)
- {
- if (cfilename == NULL)
- {
- return false;
- }
- ifstream in(cfilename, ios::in);
- if (!in)
- {
- return false;
- }
- string headStr;
- getline(in, headStr, ' ');
- in.close();
- if (headStr.empty())
- {
- return false;
- }
- if (headStr[0] == 's')
- {
- ReadASCII(cfilename);
- }
- else
- {
- ReadBinary(cfilename);
- }
- return true;
- }
bool ReadSTLFile(const char *cfilename)
{
if (cfilename == NULL)
{
return false;
}
ifstream in(cfilename, ios::in);
if (!in)
{
return false;
}
string headStr;
getline(in, headStr, ' ');
in.close();
if (headStr.empty())
{
return false;
}
if (headStr[0] == 's')
{
ReadASCII(cfilename);
}
else
{
ReadBinary(cfilename);
}
return true;
}
- bool ReadASCII(const char *cfilename)
- {
- int i=0,j=0,cnt=0 ,pCnt=4;
- char a[100];
- char str[100];
- double x=0,y=0,z=0;
- Vec3f tPoint;
- Vector<Vec3f> pointList; // todo: 可以预先计算出pointList的大小,节省空间
- ifstream in;
- in.open(cfilename, ios::in);
- if (!in)
- {
- return false;
- }
- do
- {
- i=0;
- cnt=0;
- in.getline(a,100, '\n');
- while(a[i]!='\0')
- {
- if (!islower((int)a[i]) && !isupper((int)a[i]) && a[i]!=' ')
- break;
- cnt++;
- i++;
- }
- while(a[cnt]!='\0')
- {
- str[j]=a[cnt];
- cnt++;
- j++;
- }
- str[j]='\0';
- j=0;
- if (sscanf(str,"%lf%lf%lf",&x,&y,&z)==3)
- {
- tPoint.SetParam(x,y,z);
- pointList.push_back(tPoint);
- }
- pCnt++;
- }while(!in.eof());
- return true;
- }
- bool ReadBinary(const char *cfilename)
- {
- char str[80];
- ifstream in;
- //三角形数目
- int unTriangles(0);
- in.open(cfilename, ios::in);
- if (!in)
- {
- return false;
- }
- in.read(str, 80);
- in >> unTriangles;
- if(unTriangles==0)
- {
- return false;
- }
- Vec3f tPoint;
- vector<Vec3f> pointList(unTriangles); // 预留足够的空间
- float x(0.f), y(0.f), z(0.f);
- char unusedByte;
- //Binary
- for(int i=0;i<(int)unTriangles;i++)
- {
- for (int pointIdx=0; pointIdx<4; pointIdx++)
- {
- in >> x >> y >> z;
- tPoint.SetParam(x, y, z);
- pointList.push_back(tPoint);
- }
- in >> unusedByte >> unusedByte;
- }
- in.close();
- return true;
- }
bool ReadASCII(const char *cfilename)
{
int i=0,j=0,cnt=0 ,pCnt=4;
char a[100];
char str[100];
double x=0,y=0,z=0;
Vec3f tPoint;
Vector<Vec3f> pointList; // todo: 可以预先计算出pointList的大小,节省空间
ifstream in;
in.open(cfilename, ios::in);
if (!in)
{
return false;
}
do
{
i=0;
cnt=0;
in.getline(a,100, '\n');
while(a[i]!='\0')
{
if (!islower((int)a[i]) && !isupper((int)a[i]) && a[i]!=' ')
break;
cnt++;
i++;
}
while(a[cnt]!='\0')
{
str[j]=a[cnt];
cnt++;
j++;
}
str[j]='\0';
j=0;
if (sscanf(str,"%lf%lf%lf",&x,&y,&z)==3)
{
tPoint.SetParam(x,y,z);
pointList.push_back(tPoint);
}
pCnt++;
}while(!in.eof());
return true;
}
bool ReadBinary(const char *cfilename)
{
char str[80];
ifstream in;
//三角形数目
int unTriangles(0);
in.open(cfilename, ios::in);
if (!in)
{
return false;
}
in.read(str, 80);
in >> unTriangles;
if(unTriangles==0)
{
return false;
}
Vec3f tPoint;
vector<Vec3f> pointList(unTriangles); // 预留足够的空间
float x(0.f), y(0.f), z(0.f);
char unusedByte;
//Binary
for(int i=0;i<(int)unTriangles;i++)
{
for (int pointIdx=0; pointIdx<4; pointIdx++)
{
in >> x >> y >> z;
tPoint.SetParam(x, y, z);
pointList.push_back(tPoint);
}
in >> unusedByte >> unusedByte;
}
in.close();
return true;
}