2014년 7월 23일

SDL2 기반의 OPENGL 렌더링 데모 2부

앞선 글에서는 간단한 도형만을 보여주였지만 이번 예제에서는 외부에서 모델 파일을 가져와 좀더 복잡한 형상을 렌더링 해 보자.

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

댓글 2개:

  1. 오늘도 잘보고 갑니다~~~ ^^ 많이 배우고 있네요.. 감사합니다.

    답글삭제
  2. 찾아오시는 분이 있다니 감사드립니다 ~!

    답글삭제

시리우스 라이브러리 홈페이지 오픈

현재 시리우스(Sirius) 라이브러리라는 제품을 개발하고 이를 소개하는 홈페이지를 오픈 하였습니다. 관심있는 분들의 많은 방문 요청드립니다. 앞으로 업데이트 소식및 변경사항은 스파이럴랩 홈페이지를 통해 진행할 예정입니다. 스파이럴랩 홈페이지 :  h...