요새 각광받은 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번 키를 누를때 마다 모델파일을 새로 로드하도록 구현되었다.