OpenGL MDI 应用开发指南

在本文中,将探讨如何将一个简单的OpenGL应用程序扩展为一个标准的多文档界面(MDI)应用程序。每个MDI子窗口都将包含第一部分中单一应用程序的功能。

在第一部分中,有一个单一的渲染上下文,它属于唯一的应用程序窗口。现在,将把许多功能从应用程序窗口转移到MDI子窗口。因此,将同时运行多个渲染上下文,在第一课中所做的组织将成为关键。在后续课程中,将添加线程,绘图复杂性将进一步提高。

理解MDI应用程序及其行为

理解本课程的关键部分是理解MDI应用程序及其行为。MDI应用程序基于一个不可见或透明的窗口类,该类存在于正常应用程序窗口绘制的区域中。这个特殊的窗口类被称为MDIClient,尽管它是不可见的,但它参与控制插入其中的MDI子窗口的行为。

MDIClient负责所有那些MDI特殊的事情,比如最小化和最大化行为、平铺和级联以及其他许多独特的功能。如果想使用这种类型的应用程序,值得阅读一些相关文章。对于MDI应用程序,将制作OpenGL窗口并将它们插入到MDIClient中,并允许标准行为起作用。

使用代码

从第一课中保留的伪代码没有改变,只是从应用程序窗口转移到了MDI子窗口,并且在关闭时添加了一个清理步骤。因此,每个MDI子窗口运行以下序列:

1. 初始化OpenGL窗口(仅调用一次) 2. 缩放OpenGL视口(初始调用) 重复 3. 绘制场景 4. 将场景传输到屏幕 直到窗口关闭 5. 窗口关闭时清理OpenGL内存和其他使用的东西

注意:第2步的缩放过程也会在窗口大小改变时被调用。

数据结构

使用与第一课相同的结构来保存数据,只是这次每个MDI子窗口创建一个结构,并且它附加到每个MDI子窗口上。第1步的初始化返回一个特定于MDI子窗口的渲染上下文,因此每个MDI子窗口都有自己的渲染上下文。

MDI子窗口处理程序成为所有OpenGL调用的场所。创建MDI子窗口将导致创建一个新的渲染上下文,它将存储在窗口本身的自己的数据库结构中。因此,每个MDI子窗口将有自己的数据结构和消息,创建操作的窗口变得独特于附加到每个MDI子窗口的数据结构。因此,每个MDI子窗口可以执行不同的操作,而不需要跟踪OpenGL系统本身的复杂性。

在第一课中看起来有点复杂的数据持有安排使MDI中的数据管理变得明显和简单。还有其他更快的方法将数据附加到窗口,但它们比SetProp/GetProp更复杂,但这对初学者的目标受众来说是最简单。稍后,当进入游戏和更快的渲染情况时,将讨论其他处理方法。

MDI子窗口处理程序

下面的MDI子窗口是所有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 }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485