본문 바로가기

Robotics/Software Tech.

MFC에서 OpenGL 사용하기


MFC에서 OpenGL 사용하는 방법에 대한 설명입니다.
설명과 함께 샘플 파일을 업로드 합니다.



MainFrame Class
Step 1: In the Mainframe Class override the initial dimensions of the default Window. Using the CREATESTRUCT we change the dimensions of the window to 400 x 400

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
  // TODO: Modify the Window class or styles here by modifying
  //  the CREATESTRUCT cs

  cs.cx=400;
  cs.cy=400;

  return CFrameWnd::PreCreateWindow(cs);
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
  // TODO: Modify the Window class or styles here by modifying
  //  the CREATESTRUCT cs

  cs.cx=400;
  cs.cy=400;

  return CFrameWnd::PreCreateWindow(cs);
}
BOOL CMainFrame::OnQueryNewPalette()
{
  // TODO: Add your message handler code here and/or call default
  CView *pView=GetActiveView();
  pView->Invalidate(FALSE);
  return CFrameWnd::OnQueryNewPalette();
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
  // TODO: Modify the Window class or styles here by modifying
  // the CREATESTRUCT cs
 
  cs.cx=400;
  cs.cy=400;

  return CFrameWnd::PreCreateWindow(cs);
}

View Class
Step 1: Step up the pixel format and create a rendering context

int CMfc_cubeView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CView::OnCreate(lpCreateStruct) == -1)
    return -1;
 
  // TODO: Add your specialized creation code here
  PIXELFORMATDESCRIPTOR pfd=
  {
    sizeof(PIXELFORMATDESCRIPTOR),
      1,
      PFD_DRAW_TO_WINDOW|
      PFD_SUPPORT_OPENGL|
      PFD_DOUBLEBUFFER,
      PFD_TYPE_RGBA,
      24,          //24-bit color
      0,0,0,0,0,0,
      0,0,0,0,0,0,0,
      32,          //32 bit depth buffer
      0,0,
      PFD_MAIN_PLANE,    //Main layer type
      0,
      0,0,0
  };
  CClientDC clientDC(this);
  /*
  ChoosePixelFormat
  Requests a pixel-format index for a pixel format that most closely
  matches the format requested.   This function's two arguments are a
  handle to the DC for which to select the pixel format and the
  address of the PIXELFORMATDESCRIPTOR structure that holds the attributes
  of the requested pixel format
  */
  int pixelFormat =ChoosePixelFormat(clientDC.m_hDC,&pfd);
  BOOL success =   SetPixelFormat(clientDC.m_hDC,pixelFormat,&pfd);
  /*
  DescribePixelFormat
  Fills a PIXELFORMATDESCRIPTOR  structure with information about the given
  pixel format.  This function's four arguments are a handle to the DC, the
  pixel index to examine, and the size and address of PIXELFORMATDESCRIPTOR structure.
  */
  DescribePixelFormat(clientDC.m_hDC,pixelFormat, sizeof(pfd),&pfd);

  if (pfd.dwFlags & PFD_NEED_PALETTE)
      SetupLogicalPalette();

  /*
  wglCreateContext
  Creates a rendering context compatible with the given DC.
  */
  m_hRC=wglCreateContext(clientDC.m_hDC);
  /*
  wglMakeCurrent
  Makes a rendering context current, which binds the rendering
  context to the given DC.  This function's two arguments
  are a handle to a DC and the handle to the rendering context.
  */
  wglMakeCurrent(clientDC.m_hDC,m_hRC);

  //Texture Mapping
  //Once you have your DIB loaded and its color tables created, you can
  // actually start thinking about your texture mapping
  //GL_TEXTURE_2D equates to a two-dimensional texture
  //GL_CLAMP for a single image
  //GL_REPEAT for a repeating pattern
  //GL_TEXTURE_MAG_FILTER determines how a texture is magnified when
  // the destination is larger than the texture.
  //GL_TEXTURE_MIN_FILTER determines how a texture is reduced
  //glTexEnvi set the texturing environment

  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
 
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
  glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);

  glEnable(GL_DEPTH);
  glEnable(GL_TEXTURE_2D);

  glClearColor(1.0f,1.0f,1.0f,1.0f);

  wglMakeCurrent(clientDC.m_hDC,NULL);

  /* CDib is a class used to read Device Independant Bitmaps */
  m_pDib = new CDib("snake.BMP");
  CreateColorTables(m_pDib);
  SetupColorTables();
  /* The timer determines the rendering refresh rate in milliseconds*/
  SetTimer(1,1,0);
  return 0;
}

Step 2: Cleanup resources: Bitmaps, Timers, and device contexts

void CMfc_cubeView::OnDestroy()
{
  CView::OnDestroy();
 
  // TODO: Add your message handler code here
  KillTimer(1);
  delete m_pDib;
  /*Frees the Rendering Context.  Cleanup of resources*/
  wglDeleteContext(m_hRC);
  if (m_hPalette)
     DeleteObject(m_hPalette);

}

Step 3: Modify the Class style

BOOL CMfc_cubeView::PreCreateWindow(CREATESTRUCT& cs)
{
  // TODO: Modify the Window class or styles here by modifying
  // the CREATESTRUCT cs
  cs.style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

  return CView::PreCreateWindow(cs);
}

Step 4: When Invalidate(TRUE) is invoked the OnDraw event is fired. The OnDraw method contains the rendering code portion.

void CMfc_cubeView::OnDraw(CDC* pDC)
{
  CMfc_cubeDoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);

  // TODO: add draw code for native data here
  if (m_hPalette)
  {
    SelectPalette(pDC->m_hDC,m_hPalette, FALSE);
    RealizePalette(pDC->m_hDC);
  }
  wglMakeCurrent(pDC->m_hDC,m_hRC);
  DrawWithOpenGL();
  SwapBuffers(pDC->m_hDC);
  wglMakeCurrent(pDC->m_hDC,NULL);
}

Step 5: The OnSize method defines the current viewport, defines the PROJECTION Model and Object Model, and light sources.

void CMfc_cubeView::OnSize(UINT nType, int cx, int cy)
{
  CView::OnSize(nType, cx, cy);
 
  // TODO: Add your message handler code here
  CClientDC clientDC(this);
  wglMakeCurrent(clientDC.m_hDC,m_hRC);
  glViewport(0,0,cx,cy);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(-1.0,1.0,-1.0,1.0,2.0,9.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  GLfloat light0Ambient[]={0.0f,0.0f,0.0f,1.0f};
  GLfloat light0Diffuse[]={1.0f,1.0f,1.0f,1.0f};
  GLfloat light0Position[]={0.0f,0.0f,0.0f,1.0f};

  glLightfv(GL_LIGHT0,GL_AMBIENT,light0Ambient);
  glLightfv(GL_LIGHT0,GL_DIFFUSE, light0Diffuse);
  glLightfv(GL_LIGHT0,GL_POSITION,light0Position);

  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);

  glTranslatef(0.0f,0.0f,-6.0f);
  wglMakeCurrent(NULL,NULL);
}

Step 6: On Timer event is invoke every X predefine miliseconds

void CMfc_cubeView::OnTimer(UINT nIDEvent)
{
  // TODO: Add your message handler code here and/or call default

  if (m_cube_animate==1 )
  {
    m_cube_spin+=2;
  }
  if (m_icosa_animate==1)
  {
    m_icosa_spin+=2;
  }
  Invalidate(FALSE);

  CView::OnTimer(nIDEvent);
}

Step 7: Your Rendering Code

void CMfc_cubeView::DrawWithOpenGL()
{
  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);
  glClearColor(0.0f,0.0f,0.0f,1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  AnimateCube();
}

Step 8: The Animated Cube Example

void CMfc_cubeView::AnimateCube()
{
  GLfloat glfMaterialColor[]={0.2f,0.8f,0.5f,1.0f};
  GLvoid* pTextureBits = (GLvoid*) m_pDib->GetDibBitsPtr();
  GLint width = m_pDib->GetDibWidth();
  GLint height = m_pDib->GetDibHeight();

  glPushMatrix();
  glTexImage2D(GL_TEXTURE_2D,0,3,width,height,0,GL_COLOR_INDEX,
               GL_UNSIGNED_BYTE,pTextureBits);
  glClear(GL_COLOR_INDEX|GL_DEPTH_BUFFER_BIT);

  glRotatef(m_cube_spin,1.0,1.0,1.0);
  glScalef (0.5,0.5,0.5);   /* modeling transformation */
   
  //glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,glfMaterialColor);

  glBegin(GL_POLYGON);
  //glNormal3f(0.0f,0.0f,1.0f);
  //vertice 1
  glTexCoord2f(0.0f,1.0f);
  glVertex3f(1.0f,1.0f,1.0f);
  //vertice 2
  glTexCoord2f(0.0f,0.0f);
  glVertex3f(-1.0f,1.0f,1.0f);
  //vertice 3
  glTexCoord2f(1.0f,0.0f);
  glVertex3f(-1.0f,-1.0f,1.0f);
  //vertice 4
  glTexCoord2f(1.0f,1.0f);
  glVertex3f(1.0f,-1.0f,1.0f);
  glEnd();
   
  glBegin(GL_POLYGON);
  //glNormal3f(0.0f,0.0f,-1.0f);
  //vertice 1
  glTexCoord2f(0.0f,1.0f);
  glVertex3f(1.0f,1.0f,-1.0f);
  //vertice 2
  glTexCoord2f(0.0f,0.0f);
  glVertex3f(1.0f,-1.0f,-1.0f);
  //vertice 3
  glTexCoord2f(1.0f,0.0f);
  glVertex3f(-1.0f,-1.0f,-1.0f);
  //vertice 4
  glTexCoord2f(0.0f,0.0f);
  glVertex3f(-1.0f,1.0f,-1.0f);
  glEnd();

  glBegin(GL_POLYGON);
  //glNormal3f(-1.0f,0.0f,0.0f);
  //vertice 1
  glTexCoord2f(0.0f,1.0f);
  glVertex3f(-1.0f,1.0f,1.0f);
  //vertice 2
  glTexCoord2f(0.0f,0.0f);
  glVertex3f(-1.0f,1.0f,-1.0f);
  //vertice 3
  glTexCoord2f(1.0f,0.0f);
  glVertex3f(-1.0f,-1.0f,-1.0f);
  //vertice 4
  glTexCoord2f(1.0f,1.0f);
  glVertex3f(-1.0f,-1.0f,1.0f);
  glEnd();

  glBegin(GL_POLYGON);
  //glNormal3f(1.0f,0.0f,0.0f);
  //vertice 1
  glTexCoord2f(0.0f,1.0f);
  glVertex3f(1.0f,1.0f,1.0f);
  //vertice 2
  glTexCoord2f(0.0f,0.0f);
  glVertex3f(1.0f,-1.0f,1.0f);
  //vertice 3
  glTexCoord2f(1.0f,0.0f);
  glVertex3f(1.0f,-1.0f,-1.0f);
  //vertice 4
  glTexCoord2f(1.0f,1.0f);
  glVertex3f(1.0f,1.0f,-1.0f);
  glEnd();

  glBegin(GL_POLYGON);
  //glNormal3f(0.0f,1.0f,0.0f);
  //vertice 1
  glTexCoord2f(0.0f,1.0f);
  glVertex3f(-1.0f,1.0f,-1.0f);
  //vertice 2
  glTexCoord2f(0.0f,0.0f);
  glVertex3f(-1.0f,1.0f,1.0f);
  //vertice 3
  glTexCoord2f(1.0f,0.0f);
  glVertex3f(1.0f,1.0f,1.0f);
  //vertice 4
  glTexCoord2f(1.0f,1.0f);
  glVertex3f(1.0f,1.0f,-1.0f);
  glEnd();

  glBegin(GL_POLYGON);
  //glNormal3f(0.0f,-1.0f,0.0f);
  //vertice 1
  glTexCoord2f(0.0f,1.0f);
  glVertex3f(-1.0f,-1.0f,-1.0f);
  //vertice 2
  glTexCoord2f(0.0f,0.0f);
  glVertex3f(1.0f,-1.0f,-1.0f);
  //vertice 3
  glTexCoord2f(1.0f,0.0f);
  glVertex3f(1.0f,-1.0f,1.0f);
  //vertice 4
  glTexCoord2f(1.0f,1.0f);
  glVertex3f(-1.0f,-1.0f,1.0f);
  glEnd();

  glPopMatrix();
}

void CMfc_cubeView::CreateColorTables(CDib *pDib)
{
  LPRGBQUAD pColorTable = pDib->GetDibRGBTablePtr();
  for(UINT i=0; i<256; ++i)
  {
    m_red[i]=(GLfloat) pColorTable[i].rgbRed/255;
    m_green[i]=(GLfloat) pColorTable[i].rgbGreen/255;
    m_blue[i]=(GLfloat) pColorTable[i].rgbBlue/255;
  }
}

void CMfc_cubeView::SetupColorTables()
{
  CClientDC clientDC(this);
  wglMakeCurrent(clientDC.m_hDC,m_hRC);
  glPixelMapfv(GL_PIXEL_MAP_I_TO_R,256,m_red);
  glPixelMapfv(GL_PIXEL_MAP_I_TO_G,256,m_green);
  glPixelMapfv(GL_PIXEL_MAP_I_TO_B,256,m_blue);
  glPixelTransferi(GL_MAP_COLOR,TRUE);
  wglMakeCurrent(clientDC.m_hDC,m_hRC);
}

void CMfc_cubeView::SetupLogicalPalette()
{
  struct
  {
    WORD Version;
    WORD NumberOfEntries;
    PALETTEENTRY aEntries[256];
  }logicalPalette={0x300,256};

  BYTE reds[]={0,36,72,109,145,182,218,255};
  BYTE greens[]={0,36,72,109,145,182,218,255};
  BYTE blues[]={0,85,170,255};

  for(int colorNum=0; colorNum<256; ++colorNum)
  {
    logicalPalette.aEntries[colorNum].peRed=reds[colorNum&0x07];
    logicalPalette.aEntries[colorNum].peGreen=greens[(colorNum>>0x03)&0x07];
    logicalPalette.aEntries[colorNum].peBlue=blues[(colorNum>>0x06)&0x03];
    logicalPalette.aEntries[colorNum].peFlags=0;
  }
  m_hPalette=CreatePalette ((LOGPALETTE*)&logicalPalette);
}