OpenGL MFC Control

转载:http://blog.sina.com.cn/s/blog_4368081e0100sfrz.html

2010-12-03 AM. 506实验室

Setting Up OpenGL in an MFC Control

FROM: http://www.codeguru.com/cpp/cpp/cpp_mfc/tutorials/print.php/c10975__2/

使用OpenGL在MFC中的picture控件里绘图

第一部分:Creating the Initial OpenGL Window

Step 1: Creating the Project, Microsoft Visual C++ 6.0, File->New

clip_image002

Select MFC AppWizard (exe) from the Templates window and name it "oglMFCDialog".

Where you place it on your hard drive is up to you. Just remember where you saved it.

clip_image004

Click OK,select Dialog

clip_image006

Click Next->Next->Next->Finish->OK

得到如下界面:

clip_image008

Step 2: Creating the Picture Control,add picture control:

clip_image010

Set the following variables:

· Visible: False

· ID: IDC_OPENGL

clip_image012

You may be wondering why you set the Visible attribute to False. When you load the OpenGL rendering context inside of any MFC control, you will just use the control's window rect coordinates to draw the OpenGL stuff. For some odd reason, if the visibility of the picture control is set to True, the control will cover up all the OpenGL you draw inside of the rect.

Step 3: Adding the OpenGL Class

Next, to set up the OpenGL, I opted to keep it nice and neat and add a separate class for it. It's good practice to keep major components of a project in separate classes, so I will keep the OpenGL and the MFC separate.

To add a class, set the following variables:

· Class name: COpenGLControl

· Base class: CWnd

View->Class Wizard

clip_image014

When the Class Wizard pops up,click Add Class->New:

clip_image016

Click the OK button and you will have your new class added to the project.

Step 4: Adding Project Libraries

在设置的Link里加入库opengl32.lib glu32.lib(Because you will be accessing OpenGL's rendering functions, you also will need to add some libraries to link into the project. To do this, right-mouse click on the oglMFCDialog project in the tree once again and select Properties.) ,Project->Setting:

clip_image018

Object/library modules: opengl32.lib glu32.lib,click OK.

clip_image020

Step 5: Setting Up Class Variables

For the duration of the project, some local variables will need to be added to the OpenGLControl.h, both public and private, as well as two #include calls for the OpenGL functions to work. They are as follows:

#include <gl/gl.h>
#include <gl/glu.h>
 
class COpenGLControl : public CWnd
{
   public:
     
     
     
      // Timer
      UINT_PTR m_unpTimer;
 
   private:
     
     
     
      // Window information
      CWnd    *hWnd;
      HDC     hdc;
      HGLRC   hrc;
      int     m_nPixelFormat;
      CRect   m_rect;
      CRect   m_oldWindow;
      CRect   m_originalRect;
   …
   …
   …

Step 6: Adding the oglCreate Function

A new function will need to be added to both the OpenGLControl.h and OpenGLControl.cpp files. The function will be responsible for setting up some basic window variables and function calls important to the MFC; I named this function oglCreate. The portion of code below to be added to the header file can be placed under a new public routine section.

OpenGLControl.h:

void oglCreate(CRect rect, CWnd *parent);

OpenGLControl.cpp:

void COpenGLControl::oglCreate(CRect rect, CWnd *parent)
{
   CString className = AfxRegisterWndClass(CS_HREDRAW |
      CS_VREDRAW | CS_OWNDC, NULL,
      (HBRUSH)GetStockObject(BLACK_BRUSH), NULL);
 
   CreateEx(0, className, "OpenGL", WS_CHILD | WS_VISIBLE |
            WS_CLIPSIBLINGS | WS_CLIPCHILDREN, rect, parent, 0);
 
   // Set initial variables' values
   m_oldWindow    = rect;
   m_originalRect = rect;
 
   hWnd = parent;
}

Step 7: Adding the OnPaint Function

View->Class Wizard, WM_PAINT message add Function

clip_image022

Following is the code that will need to be added into this OnPaint function in the OpenGLControl.cpp file:

void COpenGLControl::OnPaint()

{

//CPaintDC dc(this); // device context for painting

ValidateRect(NULL);

}

Step 8: Adding the OnCreate Function

View->Class Wizard, WM_CREATE message add Function

clip_image024

OpenGLControl.cpp:

int COpenGLControl::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CWnd::OnCreate(lpCreateStruct) == -1)

return -1;

oglInitialize();

return 0;

}

Note: Of course, this new function hasn't been added yet, so if you try to run it you will get a compiler error.

Step 9: Adding the oglInitialize Function

As mentioned in the previous step, the next manually added function, the oglInitialize function, is called when your OpenGL class is created (by means of the OnCreate message). The oglInitialize function will be responsible for setting up all of the information OpenGL needs to render. These include the pixel format, rendering context, and also a clear color to make sure that it's swapping the buffers correctly.

Note: The last line of code in the *.cpp portion below calls an OnDraw function, which has not yet been created. Therefore, another compiler error will occur if you try running this.

OpenGLControl.h:

void oglInitialize(void);

OpenGLControl.cpp:

void COpenGLControl::oglInitialize(void)
{
   // Initial Setup:
   static PIXELFORMATDESCRIPTOR pfd =
   {
      sizeof(PIXELFORMATDESCRIPTOR),
      1,
      PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
      PFD_TYPE_RGBA,
      32,    // bit depth
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      16,    // z-buffer depth
      0, 0, 0, 0, 0, 0, 0,
   };
 
   // Get device context only once.
   hdc = GetDC()->m_hDC;
 
   // Pixel format.
   m_nPixelFormat = ChoosePixelFormat(hdc, &pfd);
   SetPixelFormat(hdc, m_nPixelFormat, &pfd);
 
   // Create the OpenGL Rendering Context.
   hrc = wglCreateContext(hdc);
   wglMakeCurrent(hdc, hrc);
 
   // Basic Setup:
   // Set color to use when clearing the background.
   glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
   glClearDepth(1.0f);
 
   // Turn on backface culling
   glFrontFace(GL_CCW);
   glCullFace(GL_BACK);
 
   // Turn on depth testing
   glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_LEQUAL);
 
   // Send draw request
   OnDraw(NULL);
}

Step 10: Adding the OnDraw Function

Next, the OnDraw function mentioned in the previous step's oglInitialize function needs to be added. This will have to act as a message function, but will need to be added manually. And as you might gather already, the "afx_msg" prefix will need to be added to the function's declaration. You will notice in the *.cpp portion that you don't actually call any procedures yet, just a commented TODO. This will be mentioned later on in the tutorial, for camera controls and such.

Note: If you try running this now, it will compile correctly, but unfortunately still nothing is being drawn into the control. This is due to the change you made in OnPaint.

OpenGLControl.h:

afx_msg void OnDraw(CDC *pDC);

OpenGLControl.cpp:

void COpenGLControl::OnDraw(CDC *pDC)
{
   // TODO: Camera controls.
}

Step 11: Adding the OnTimer Function

View->Class Wizard, WM_TIMER message add Function

clip_image026

OpenGLControl.cpp:

void COpenGLControl::OnTimer(UINT nIDEvent)

{

switch (nIDEvent)

{

case 1:

{

// Clear color and depth buffer bits

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Draw OpenGL scene

// oglDrawScene();

// Swap buffers

SwapBuffers(hdc);

break;

}

default:

break;

}

CWnd::OnTimer(nIDEvent);

}

Step 12: Adding the OnSize Function

View->Class Wizard, WM_SIZE message add Function

clip_image028

The OnSize function is called when the window is resized. To prevent the OpenGL rendering from getting all wacky from resizing the window, a few things such as the perspective and viewport need to be tweaked. In the tutorial, you won't actually resize the control according to the window, but that could be achieved in this function if the need arises.

OpenGLControl.cpp:

void COpenGLControl::OnSize(UINT nType, int cx, int cy)

{

CWnd::OnSize(nType, cx, cy);

if (0 >= cx || 0 >= cy || nType == SIZE_MINIMIZED) return;

// Map the OpenGL coordinates.

glViewport(0, 0, cx, cy);

// Projection view

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

// Set our current view perspective

gluPerspective(35.0f, (float)cx / (float)cy, 0.01f, 2000.0f);

// Model view

glMatrixMode(GL_MODELVIEW);

}

Step 13: Customizing the Main MFC Dialog Class

Your last step is to integrate your OpenGL class into the main MFC dialog. This is done rather painlessly. First, inside the oglMFCDialogDlg.h file you will need to #include the created OpenGL class header file and also create an instance of the COpenGLControl class as a local variable to this class.

oglMFCDialogDlg.h:

#include "OpenGLControl.h"

class CoglMFCDialogDlg : public CDialog

{

private:

COpenGLControl m_oglWindow;

Next, inside of the oglMFCDialogDlg.cpp file, add to the already existing OnInitDialog function before the return statement the following code to create your OpenGL class inside the picture control you created and start up the timer to render the scene.

Note: For initial test purposes, I set the timer to 1 millisecond to call your timer, or a non-clamp.

oglMFCDialogDlg::OnInitDialog:

CRect rect;

// Get size and position of the picture control

GetDlgItem(IDC_OPENGL)->GetWindowRect(rect);

// Convert screen coordinates to client coordinates

ScreenToClient(rect);

// Create OpenGL Control window

m_oglWindow.oglCreate(rect, this);

// Setup the OpenGL Window's timer to render

m_oglWindow.m_unpTimer = m_oglWindow.SetTimer(1, 1, 0);

Step 14: Initial OpenGL Control Results

And there you have it. Now you should have the black clear color constantly being swapped through the buffers! Now, from here you can add a drawing function to do all your rendering work and place that in the OnTimer function where the commented line suggests.

clip_image030

第二部分: Extending the OpenGL Functionality (Extras)

Step 15: Drawing Geometrical Shapes

Anyway, drawing the shapes on this tutorial will be very simple; just create a standard cube by drawing the six sides with quads (4-vertex shapes). If you recall in the OnTimer function, there's a commented-out call to a non-existant oglDrawScene function; you now will create this function.

OpenGLControl.h:

void oglDrawScene(void);

OpenGLControl.cpp:

void COpenGLControl::oglDrawScene(void)

{

// Wireframe Mode

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

glBegin(GL_QUADS);

// Top Side

glVertex3f( 1.0f, 1.0f, 1.0f);

glVertex3f( 1.0f, 1.0f, -1.0f);

glVertex3f(-1.0f, 1.0f, -1.0f);

glVertex3f(-1.0f, 1.0f, 1.0f);

// Bottom Side

glVertex3f(-1.0f, -1.0f, -1.0f);

glVertex3f( 1.0f, -1.0f, -1.0f);

glVertex3f( 1.0f, -1.0f, 1.0f);

glVertex3f(-1.0f, -1.0f, 1.0f);

// Front Side

glVertex3f( 1.0f, 1.0f, 1.0f);

glVertex3f(-1.0f, 1.0f, 1.0f);

glVertex3f(-1.0f, -1.0f, 1.0f);

glVertex3f( 1.0f, -1.0f, 1.0f);

// Back Side

glVertex3f(-1.0f, -1.0f, -1.0f);

glVertex3f(-1.0f, 1.0f, -1.0f);

glVertex3f( 1.0f, 1.0f, -1.0f);

glVertex3f( 1.0f, -1.0f, -1.0f);

// Left Side

glVertex3f(-1.0f, -1.0f, -1.0f);

glVertex3f(-1.0f, -1.0f, 1.0f);

glVertex3f(-1.0f, 1.0f, 1.0f);

glVertex3f(-1.0f, 1.0f, -1.0f);

// Right Side

glVertex3f( 1.0f, 1.0f, 1.0f);

glVertex3f( 1.0f, -1.0f, 1.0f);

glVertex3f( 1.0f, -1.0f, -1.0f);

glVertex3f( 1.0f, 1.0f, -1.0f);

glEnd();

}

Now, one last thing you'll need to do is uncomment the commented-out call to the oglDrawScene function in the OnTimer function.

OpenGLControl::OnTimer:

// Clear color and depth buffer bits
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
// Draw OpenGL scene
oglDrawScene();
 
// Swap buffers
SwapBuffers(hdc);

Step 16: Setting Up a Maya-Style Camera

The Maya-style camera refers to the camera system used in Alias|Wavefront Maya in which the mouse button->drag performs a certain operation: Left: Rotate, Right: Zoom, and Middle: Translate. To do this, you'll need to make an OnMouseMove function call:

Select the WM_MOUSEMOVE message and select from the dropdown, <Add> OnMouseMove, to create the function. Inside this newly created function, you'll need to add the code to perform each of the buttons' given operations. If you recall from early on in the tutorial, you created some member variables for rotate, zoom, and position. You'll use these variables and set their new values according to the position variables passed into the function by Windows.

View->Class Wizard, WM_MOUSEMOVE message add Function

clip_image032

OpenGLControl.h:

#include <gl/gl.h>

#include <gl/glu.h>

// COpenGLControl window

class COpenGLControl : public CWnd

{

// Construction

public:

COpenGLControl();

// View information variables

float m_fLastX;

float m_fLastY;

float m_fPosX;

float m_fPosY;

float m_fZoom;

float m_fRotX;

float m_fRotY;

bool m_bIsMaximized;

OpenGLControl.h:

afx_msg void OnMouseMove(UINT nFlags, CPoint point);

OpenGLControl.cpp:

void COpenGLControl::OnMouseMove(UINT nFlags, CPoint point)
{
   int diffX = (int)(point.x - m_fLastX);
   int diffY = (int)(point.y - m_fLastY);
   m_fLastX  = (float)point.x;
   m_fLastY  = (float)point.y;
 
   // Left mouse button
   if (nFlags & MK_LBUTTON)
   {
      m_fRotX += (float)0.5f * diffY;
 
      if ((m_fRotX > 360.0f) || (m_fRotX < -360.0f))
      {
         m_fRotX = 0.0f;
      }
 
      m_fRotY += (float)0.5f * diffX;
 
      if ((m_fRotY > 360.0f) || (m_fRotY < -360.0f))
      {
         m_fRotY = 0.0f;
      }
   }
 
   // Right mouse button
   else if (nFlags & MK_RBUTTON)
   {
      m_fZoom -= (float)0.1f * diffY;
   }
 
   // Middle mouse button
   else if (nFlags & MK_MBUTTON)
   {
      m_fPosX += (float)0.05f * diffX;
      m_fPosY -= (float)0.05f * diffY;
   }
 
   OnDraw(NULL);
 
   CWnd::OnMouseMove(nFlags, point);
}

Furthermore, I have put a few initial variable settings into the constructor of the COpenGLControl class that help the camera work properly.

OpenGLControl.cpp:

COpenGLControl::COpenGLControl(void)

{

m_fPosX = 0.0f; // X position of model in camera view

m_fPosY = 0.0f; // Y position of model in camera view

m_fZoom = 10.0f; // Zoom on model in camera view

m_fRotX = 0.0f; // Rotation on model in camera view

m_fRotY = 0.0f; // Rotation on model in camera view

}

Now, the last step to do is, if you recall from when you first created the OnDraw function, to change the commented TODO of the camera controls to the following:

OpenGLControl::OnDraw:

glLoadIdentity();
glTranslatef(0.0f, 0.0f, -m_fZoom);
glTranslatef(m_fPosX, m_fPosY, 0.0f);
glRotatef(m_fRotX, 1.0f, 0.0f, 0.0f);
glRotatef(m_fRotY, 0.0f, 1.0f, 0.0f);
Step 17: Resizing the Window Correctly:

Resizing windows in MFC are a rather tedious job, compared to perhaps Visual Basic, but it can be done quite easily; and once you've done it one time, you reuse the code pretty much forever. So, here's the way I had figured it out and still use to this day. It involves both OnSize functions for both MFC and OpenGL.

First, go into your oglMFCDialogDlg message function and create an OnSize function with the same technique you've used throughout this tutorial. This will generate your OnSize for the MFC portion, but you'll need to add some code; the entire OnSize class will now look like this:

clip_image034

oglMFCDialogDlg.cpp:

void CoglMFCDialogDlg::OnSize(UINT nType, int cx, int cy)
{
   CDialog::OnSize(nType, cx, cy);
   switch (nType)
   {
      case SIZE_RESTORED:
      {
         if (m_oglWindow.m_bIsMaximized)
         {
            m_oglWindow.OnSize(nType, cx, cy);
            m_oglWindow.m_bIsMaximized = false;
         }
 
         break;
      }
 
      case SIZE_MAXIMIZED:
      {
         m_oglWindow.OnSize(nType, cx, cy);
         m_oglWindow.m_bIsMaximized = true;
         break;
      }
   }
}

You may have noticed this changes the state of a variable you have not yet created, so you now will do the OpenGL window portion, inside your already created OnSize message function there. So, place the following code right after the code that's already inside the OnSize function:

OpenGLControl.cpp:

switch (nType)

{

// If window resize token is "maximize"

case SIZE_MAXIMIZED:

{

// Get the current window rect

GetWindowRect(m_rect);

// Move the window accordingly

MoveWindow(6, 6, cx - 14, cy - 14);

// Get the new window rect

GetWindowRect(m_rect);

// Store our old window as the new rect

m_oldWindow = m_rect;

break;

}

// If window resize token is "restore"

case SIZE_RESTORED:

{

// If the window is currently maximized

if (m_bIsMaximized)

{

// Get the current window rect

GetWindowRect(m_rect);

// Move the window accordingly (to our stored old window)

MoveWindow(m_oldWindow.left,

m_oldWindow.top - 18,

m_originalRect.Width() - 4,

m_originalRect.Height() - 4);

// Get the new window rect

GetWindowRect(m_rect);

// Store our old window as the new rect

m_oldWindow = m_rect;

}

break;

}

}

效果图:

image
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值