요새 각광받은 3D프린터 들을 보면 수많은 모델들이 STL (STereoLithography) 이란 파일 포맷으로 제공되고 있다. 이번 데모에서는 이 파일 포맷을 렌더링 해보자. STL 파일 포맷에 대한 정보는 http://en.wikipedia.org/wiki/STL_(file_format) , http://www.eng.nus.edu.sg/LCEL/RP/u21/wwwroot/stl_library.htm#What 에서 참고 바라며, 이번 예제에서는 바이너리 기반의 STL 파일포맷을 대상으로 한다.
이 데모의 실제 동작 화면을 캡춰해 http://www.youtube.com/watch?v=LTGfNCY0wIA 에 올려놓았다.
자 그렇다면 실제 구현에 들어가자. 마찬가지로 App 를 상속받는다.
--------------------------ex3.h
#ifndef EX3_H
#define EX3_H
#include "app.h"
#include <vector>
class Example3 : public App
{
public:
bool OnInit(int argc, TCHAR* argv[]);
void OnEvent(SDL_Event* Event);
void OnLoop();
void OnRender();
void OnCleanUp();
private:
bool LoadSTL(TCHAR* lpszFilename);
#pragma pack(push, 4)
typedef struct
{
float x;
float y;
float z;
}VERTEX;
typedef struct
{
VERTEX normal;
VERTEX v[3];
}FACE;
#pragma pack(pop)
std::vector<face> _faces;
VERTEX _center;
GLuint _listid;
float _scale;
public:
Example3();
virtual ~Example3();
};
#endif
-----------------------------------------ex.cpp#include "ex3.h"
#include "log.h"
// STL 예제 파일들은 인터넷에서 검색하여 구하도록 한다
TCHAR* gszSamples[] = {
_T("samples\\Chest_Back.stl"),
_T("samples\\knot.stl"),
_T("samples\\porsche.stl"),
_T("samples\\pump.stl"),
_T("samples\\tire_v.stl")
};
Example3::Example3()
{
_scale = 1.0f;
}
Example3::~Example3()
{
}
bool Example3::LoadSTL(TCHAR* lpszFilename)
{
FILE* fp = _tfopen( lpszFilename, _T("rb")); //lpszFilename
if (!fp)
{
Log("fail to open the stl file : %s", lpszFilename);
return false;
}
_faces.clear();
glDeleteLists(_listid, 1);
// STL 이진 파일 포맷에 맞게 얻어온다
BYTE header[80];
fread(header, sizeof(header), 1, fp);
UINT32 numOfFacets=0;
fread(&numOfFacets, sizeof(numOfFacets), 1, fp);
Log("face count = %d", numOfFacets);
FACE f;
UINT16 attr;
for (int i=0; i< numOfFacets; i++)
{
fread(&f.normal, sizeof(f.normal), 1, fp);
fread(&f.v, sizeof(f.v), 1, fp);
fread(&attr, 2, 1, fp);
_faces.push_back(f);
}
fclose(fp);
float xmin, xmax;
float ymin, ymax;
float zmin, zmax;
xmin = 9999; xmax = -9999;
ymin = 9999; ymax = -9999;
zmin = 9999; zmax = -9999;
// 크기를 알아내에 화면 중심으로 이동시키기 위해서
for (int i=0; i< _faces.size(); i++)
{
if (_faces[i].v[0].x < xmin)
xmin = _faces[i].v[0].x;
if (_faces[i].v[0].x > xmax)
xmax = _faces[i].v[0].x;
if (_faces[i].v[0].y < ymin)
ymin = _faces[i].v[0].y;
if (_faces[i].v[0].y > ymax)
ymax = _faces[i].v[0].y;
if (_faces[i].v[0].z < zmin)
zmin = _faces[i].v[0].z;
if (_faces[i].v[0].z > zmax)
zmax = _faces[i].v[0].z;
}
_center.x = (xmin+xmax) / 2.0f;
_center.y = (ymin+ymax) / 2.0f;
_center.z = (zmin+zmax) / 2.0f;
// 렌더링 속도 향상을 위해 리스트 버퍼 사용
_listid = glGenLists( 1 );
glNewList(_listid, GL_COMPILE );
glBegin( GL_TRIANGLES );
glColor3f( 0.3, 0.8, 0.1);
for (int i=0; i< _faces.size(); i++)
{
glNormal3fv( (float*)&_faces[i].normal);
glVertex3fv( (float*)&_faces[i].v[0]);
glVertex3fv( (float*)&_faces[i].v[1]);
glVertex3fv( (float*)&_faces[i].v[2]);
}
glEnd();
glEndList();
return true;
}
bool Example3::OnInit(int argc, TCHAR* argv[])
{
// 조명 효과 사용
float ambLight[] = {0.7f, 0.7f, 0.7f, 1.0f};
float specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
float specref[] = {1.0f, 1.0f, 1.0f, 1.0f};
float lightPos[] = {500, 500, 300, 1.0f};
glEnable(GL_LIGHTING);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, ambLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
glEnable(GL_LIGHT0);
return true;
}
void Example3::OnEvent(SDL_Event* e)
{
// q : 줌 인
// z : 줌 아웃
// p : 렌더링 모드 변경
switch( e->type )
{
case SDL_KEYDOWN:
if (e->key.keysym.sym == 'q')
{
_scale *= 1.2;
}
else if (e->key.keysym.sym == 'z')
{
_scale *= 0.8;
}
break;
case SDL_KEYUP:
if (e->key.keysym.sym == 'p')
{
static int mode = 0;
glPolygonMode( GL_FRONT_AND_BACK, GL_POINT+ mode );
mode++;
mode= mode % 3;
}
else if (e->key.keysym.sym >= '1' && e->key.keysym.sym <= '5')
{
int n = e->key.keysym.sym - '1';
this->LoadSTL( gszSamples[n] );
}
break;
default:
break;
}
}
void Example3::OnLoop()
{}
void Example3::OnRender()
{
glClearColor( 0.1f, 0.1f, 0.1f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glLoadIdentity();
gluLookAt(300, 300, 150, 0,0,0, 0,0,1);
static float angle = 0;
glRotatef( angle +=0.5, 0,0,1);
glPushMatrix();
glScalef(_scale, _scale, _scale);
glTranslatef(-_center.x, -_center.y, -_center.z);
glCallList(_listid);
glPopMatrix();
glBegin(GL_LINES);
glColor3f(1, 0, 0);
glVertex3f( 0, 0, 0 );
glVertex3f( 500, 0, 0 );
glColor3f(0, 1, 0);
glVertex3f( 0, 0, 0 );
glVertex3f( 0, 500, 0 );
glColor3f(0, 0, 1);
glVertex3f( 0, 0, 0 );
glVertex3f( 0, 0, 500 );
glEnd();
}
void Example3::OnCleanUp()
{
}
---------------------------------------------------자 그럼 메인 함수를 보자
#include "app.h"
#include "ex3.h"
int _tmain(int argc, TCHAR* args[])
{
App* app = new Example3;
int n = app->Execute(argc, args);
return n;
}
각종 예제 모델 파일들(STL)은 http://www.eng.nus.edu.sg/LCEL/RP/u21/wwwroot/stl_library.htm#Download 에서 다운로드 받은 것들이다. 또한 키보드 1~5번 키를 누를때 마다 모델파일을 새로 로드하도록 구현되었다.





