说起STL模型,相信使用过CAD三维软件的人都不陌生,
STL = STL文件,一种3D模型文件格式STL(STereo Lithography的缩写)
STL文件格式是由3D SYSTEMS 公司于1988 年制定的一个接口协议,是一种为快速原型制造技术服务的三维图形文件格式。STL 文件由多个三角形面片的定义组成,每个三角形面片的定义包括三角形各个定点的三维坐标及三角形面片的法矢量。三角形顶点的排列顺序遵循右手法则。 STL 文件有2 种类型:文本文件(ASCII格式)和二进制文件(BINARY)。
在此文中我们对ASCII格式的STL文件进行解析,并且用opengl对其进行显示。
STL的ASCII格式如下:
solid filenamestl //文件路径及文件名
facet normal x y z // 三角面片法向量的3个分量值
outer loop
vertex x y z ∥三角面片第一个顶点的坐标
vertex x y z // 三角面片第二个顶点的坐标
vertex x y z ∥三角面片第三个顶点的坐标
endloop
endfacet // 第一个三角面片定义完毕
……
……
endsolid filenamestl ∥整个文件结束
下面来说说处理这个解析的思路,首先我们可以按行一行行读来下,去掉第一行开始一直往下读,每读一行都检查是不是到了最后一行,但是这样显然效率不高,不如我们一开始把三角面片个个数求出来,循环中对每个三角面片进行解析,可以观察出每个三角面片所占的ASCII文件的行数为7行,那么把行数读出来除以七所得的商就是三角面片的个数,那么我们再一一对每个三角面片进行解析,实际上这个工作并不是很难,关键是要知道STL的格式与OPENGLBATCH的接口,OPENGL的接口实际上是一串float的数组,这里面保存了verts即顶点坐标信息,以及norms即法向量信息,那么刚好和我们的stl文件相一致。
以下是我们的核心代码;
#include "windows.h"
#include <GLTools.h> // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#pragma comment(lib,"legacy_stdio_definitions.lib")
#include<stdio.h>
#ifdef __cplusplus
extern"C"
#endif
FILE __iob_func[3] = { __acrt_iob_func(0),__acrt_iob_func(2),__acrt_iob_func(3) };
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <glut.h>
#endif
GLFrame viewFrame;
GLFrustum viewFrustum;
GLBatch triangleBatch;
GLMatrixStack modelViewMatix;
GLMatrixStack projectionMatrix;
GLGeometryTransform transformPipeline;
GLShaderManager shaderManager;
int num;
float* verts;
float* vnorms;
void getstlmodel()
{
int max = 0;
bool isbegin = false;
long size = 0;
int nlines = 0;
int count1 = 0;
int count2 = 0;
FILE* file = fopen("mystl.stl", "r");
fseek(file, 0L, SEEK_END);
size = ftell(file);
fclose(file);
file = fopen("mystl.stl", "r");
for (int i = 0; i<size; i++)
{
if (getc(file) == '\n')
{
nlines++;
}
}
num = nlines / 7;
rewind(file);
while (getc(file) != '\n');
verts = new float[9 * num];
vnorms = new float[9 * num];
for (int i = 0; i<num; i++)
{
char x[200] = "";
char y[200] = "";
char z[200] = "";
if (3 != fscanf(file, "%*s %*s %80s %80s %80s\n", x, y, z))
{
break;
}
vnorms[count1] = vnorms[count1 + 3] = vnorms[count1 + 6] = atof(x);
count1++;
vnorms[count1] = vnorms[count1 + 3] = vnorms[count1 + 6] = atof(y);
count1++;
vnorms[count1] = vnorms[count1 + 3] = vnorms[count1 + 6] = atof(z);
count1 += 7;
fscanf(file, "%*s %*s");
if (3 != fscanf(file, "%*s %80s %80s %80s\n", x, y, z))
{
break;
}
if (isbegin == false)
{
isbegin = true;
max = atof(z);
}
verts[count2] = atof(x);
count2++;
verts[count2] = atof(y);
count2++;
verts[count2] = atof(z);
count2++;
if (3 != fscanf(file, "%*s %80s %80s %80s\n", x, y, z))
{
break;
}
verts[count2] = atof(x);
count2++;
verts[count2] = atof(y);
count2++;
verts[count2] = atof(z);
count2++;
if (3 != fscanf(file, "%*s %80s %80s %80s\n", x, y, z))
{
break;
}
verts[count2] = atof(x);
count2++;
verts[count2] = atof(y);
count2++;
verts[count2] = atof(z);
count2++;
fscanf(file, "%*s");
fscanf(file, "%*s");
}
}
void SetupRC()
{
// Black background
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
shaderManager.InitializeStockShaders();
viewFrame.MoveForward(100.0f);
triangleBatch.Begin(GL_TRIANGLES, num * 3);
triangleBatch.CopyVertexData3f(verts);
triangleBatch.CopyNormalDataf(vnorms);
triangleBatch.End();
// Make the torus
}
void SpecialKeys(int key, int x, int y)
{
if (key == GLUT_KEY_UP)
viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
if (key == GLUT_KEY_DOWN)
viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
if (key == GLUT_KEY_LEFT)
viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
if (key == GLUT_KEY_RIGHT)
viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
// Refresh the Window
glutPostRedisplay();
}
void ChangeSize(int w, int h)
{
// Prevent a divide by zero
if (h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 2000.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
modelViewMatix.PushMatrix(viewFrame);
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
triangleBatch.Draw();
modelViewMatix.PopMatrix();
glutSwapBuffers();
}
int main(int argc, char* argv[])
{
getstlmodel();
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(800, 600);
glutCreateWindow("Geometry Test Program");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}