原生 Win32 API OpenGL 教程 - 第 2 部分





5.00/5 (2投票s)
在第二部分中,我们将创建一个 OpenGL MDI 应用程序
源代码和文件是 Visual Studio 2013 格式。
引言
在第二篇文章中,我们将把第一部分的简单 OpenGL 应用程序扩展到一个标准的 MDI 接口应用程序。在这里,每个 MDICHILD
窗口将包含来自第一部分的单个应用程序的功能。
背景
在第一部分中,我们只有一个属于唯一应用程序窗口的渲染上下文。现在我们将做的是将大部分功能从应用程序窗口移动到 MDICHILD
窗口。因此,我们将同时运行多个渲染上下文,并且我们在第 1 课中进行的组织将成为关键。在以后的课程中,我们将添加线程,并且绘制复杂度将提高一个级别。
理解本课的关键是理解 MDI 应用程序和行为。 MDI 应用程序基于一个不可见或透明的 Window
类,该类存在于普通应用程序窗口中绘制的区域中。这个特殊的 Window
类称为 MDIClient
,虽然不可见,但它参与控制插入其中的 MDIChildren
窗口的行为。
MDIClient
负责所有 MDI 的特殊功能,例如最小化和最大化行为、平铺和层叠以及许多其他独特功能。如果你想使用这种应用程序,那么阅读一些关于它们的文章是值得的。对于我们的 MDI 应用程序,我们将创建我们的 OpenGL 窗口并将它们插入到 MDIClient
中,并允许标准行为工作。
Using the Code
我们来自第 1 课的伪代码保持不变,只是它从应用程序窗口移动到 MDIChild
窗口,并且我们在关闭时添加了一个清理步骤。因此,每个 MDIChild
运行以下序列
1.) Initialize OpenGL for window (Called Once)
2.) Scale the OpenGL viewPort (Initial Call)
repeat
3.) Draw the scene
4.) Transfer the scene to screen
until window closes
5.) Window closing cleanup OpenGL memory and stuff used
** Note Item 2) the scale process also gets called if the window changes size
我们使用与第 1 课相同的结构来保存我们的数据,只是这次每个 MDIChild
都创建一个结构,并且它附加到每个 MDI 子级。步骤 1 的初始化返回特定于 MDIChild
的渲染上下文,因此每个 MDIChild
都有自己的渲染上下文。
MDIChild
处理程序成为所有 OpenGL 调用的位置。 MDI 子级的创建将导致创建新的渲染上下文,该上下文将存储在其自身在窗口上的数据库结构中。因此,每个 MDIChild
将具有其自己的数据结构和消息,这些消息会创建对窗口的操作,对于附加到每个 MDIChild
的数据结构而言是唯一的。因此,每个 MDI 子项都可以做不同的事情,而无需跟踪 OpenGL 系统本身的任何复杂性。
在第 1 课中看起来有点复杂的数据保存安排使得我们在 MDI 中的数据管理变得显而易见和简单。还有其他更快的方法可以将数据附加到 Windows,但是它们比 SetProp
/GetProp
更复杂,但这是我们针对初学者受众的最简单的方法。稍后,当我们进入游戏和更快的渲染情况时,我们将讨论处理此问题的其他方法。
对于此应用程序,下面显示的 MDIChild
是所有 OpenGL 被调用和控制的地方,并且值得查看该过程并将其与上面的伪代码等同起来。
static LRESULT CALLBACK OpenGLMDIChildHandler (HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg){
case WM_CREATE: { // WM_CREATE MESSAGE
GLDATABASE* db = (GLDATABASE*) malloc(sizeof(GLDATABASE)); // Allocate structure
db->Rc = InitGL(Wnd); // Initialize OpenGL & get render context
db->glTexture = 0; // Zero the texture
db->xrot = 0.0f; // Zero x rotation
db->yrot = 0.0f; // Zero y rotation
SetProp (Wnd, DATABASE_PROPERTY, (HANDLE) db); // Data structure hold as property
ReSizeGLScene (Wnd); // Rescale the OpenGL window
}
break;
case WM_DESTROY: { // WM_DESTROY MESSAGE
wglMakeCurrent(NULL, NULL); // Make the rendering context not current
GLDATABASE* db = (GLDATABASE*) GetProp(Wnd, DATABASE_PROPERTY);// Get data struct
if (db != 0) {
if (db->Rc != 0) wglDeleteContext(db->Rc); // If valid delete context
if (db->glTexture != 0)
glDeleteTextures(1, &db->glTexture); // If valid delete the texture
free(db); // Release the data structure memory
}
}
break;
case WM_PAINT: { // WM_PAINT MESSAGE
PAINTSTRUCT Ps;
GLDATABASE* db = (GLDATABASE*) GetProp(Wnd, DATABASE_PROPERTY);// Get data struct
BeginPaint (Wnd, &Ps); // Begin paint
DrawGLScene(db, Ps.hdc); // Draw the OpenGL scene
SwapBuffers(Ps.hdc); // Swap buffers
EndPaint(Wnd, &Ps); // End paint
return 0;
}
break;
case WM_TIMER: { // WM_TIMER MESSAGE
GLDATABASE* db = (GLDATABASE*) GetProp(Wnd, DATABASE_PROPERTY);// Get data struct
db->xrot += 1.0f; // Inc x rotation
db->yrot += 1.0f; // Inc y rotation
InvalidateRect(Wnd, 0, TRUE); // Redraw now so invalidate us
}
break;
case WM_WINDOWPOSCHANGED: // WM_WINDOWPOSCHANGED
// Check if window size has changed .. window move doesnt change aspect ratio
if ((lParam == 0) || ((((PWINDOWPOS) lParam)->flags & SWP_NOSIZE) == 0)){
ReSizeGLScene(Wnd); // Rescale the GL window
InvalidateRect(Wnd, 0, TRUE); // We need a redraw now so invalidate us
}
break;
case WM_ERASEBKGND: // WM_ERASEBKGND MESSAGE
return (FALSE);
}
return DefMDIChildProc(Wnd, Msg, wParam, lParam); // Unprocessed messages to DefMDIChildProc
}
历史
- 版本 1.00 - 初始版本
- 版本 1.10 - 修复了 MDIChild 上更改错误纹理的小错误,添加了 MDI DragDrop 功能。