在WC_TABCONTROL中渲染OpenGL画布

时间:2017-07-15 03:58:09

标签: c++ winapi opengl

我的目标是使用包含用于呈现OpenGL的画布的Win32 API创建基本选项卡控件。我的选项卡包含用于渲染OpenGL的静态控件。但是,我可以让画布显示在GUI中的唯一方法是排除选项卡控件(在我的示例中注释掉CREATE_TAB_PANE宏来执行此操作)。

我的例子如下:

// OpenGlTabWin32.cpp
// NOTE: canvas displays fine if TabPane creation is commented out

#include <windows.h>
#include <commctrl.h>
#include <gl/GL.h>

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")

#define CREATE_TAB_PANE

enum { IDC_TAB = 200, IDC_CANVAS = 201 };

static HWND TabPaneId;
static HWND CanvasId;

static WNDPROC CanvasWndProc;
static HGLRC CanvasRc;
static HDC CanvasDc;

////////////////////////////////////////////////////////////////////////////////
// WndProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WndProc(HWND hwnd,
                                UINT msg,
                                WPARAM wParam,
                                LPARAM lParam) {

  switch (msg) {
    case WM_CREATE:
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// OpenGlCanvasProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK OpenGlCanvasProc(HWND hwnd,
                                         UINT msg,
                                         WPARAM wParam,
                                         LPARAM lParam) {

  if (msg == WM_PAINT) {
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    EndPaint(hwnd, &ps);

    wglMakeCurrent(CanvasDc, CanvasRc);
    SwapBuffers(CanvasDc);

    return 0;
  }

  return CallWindowProc(CanvasWndProc, hwnd, msg, wParam, lParam);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasInit
////////////////////////////////////////////////////////////////////////////////
static void CanvasInit() {

  glClearColor(0.0, 0.0, 0.0, 0.0);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasResize
////////////////////////////////////////////////////////////////////////////////
static void CanvasResize(int width, int height) {

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(0, 1, 0, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

////////////////////////////////////////////////////////////////////////////////
// CanvasDisplay
////////////////////////////////////////////////////////////////////////////////
static void CanvasDisplay() {

  glClear(GL_COLOR_BUFFER_BIT);

  glLoadIdentity();
  glColor3d(1, 0, 0);

  glBegin(GL_LINE_STRIP);
    glVertex2d(0.2, 0.2);
    glVertex2d(0.8, 0.8);
  glEnd();

  SwapBuffers(CanvasDc);
}

////////////////////////////////////////////////////////////////////////////////
// WinMain
////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd) {

  // Create window

  const char *className = "OpenGlTab";

  WNDCLASSEX wc;
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = 0;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = className;
  wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

  if (!RegisterClassEx(&wc)) {
    exit(0);
  }

  HWND winId = CreateWindowEx(0,
                              className,
                              "Tab Pane w/ OpenGL",
                              WS_OVERLAPPEDWINDOW,
                              100, 100, 600, 400,
                              0,
                              0,
                              hInstance,
                              0);
  if (!winId) {
    exit(0);
  }

  ShowWindow(winId, nShowCmd);
  UpdateWindow(winId);

#ifdef CREATE_TAB_PANE

  // Create tab pane

  TabPaneId = CreateWindow(WC_TABCONTROL,
                           0,
                           WS_CHILD | WS_VISIBLE,
                           10, 10, 566, 343,
                           winId,
                           HMENU(IDC_TAB),
                           hInstance,
                           0);

  TCITEM tabItem = {0};
  tabItem.mask = TCIF_TEXT;
  tabItem.pszText = "Tab";

  SendMessage(TabPaneId, TCM_INSERTITEM, 0, LPARAM(&tabItem));

#endif

  // Create OpenGL canvas

  int w = 200;
  int h = 200;

  CanvasId = CreateWindow("static",
                          "",
                          WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |  WS_CLIPSIBLINGS,
                          20, 100, w, h,
                          winId,
                          HMENU(IDC_CANVAS),
                          hInstance,
                          0);

  CanvasWndProc = (WNDPROC)SetWindowLongPtr(CanvasId,
                                            GWLP_WNDPROC,
                                            (LONG_PTR)OpenGlCanvasProc);

  CanvasDc = GetDC(CanvasId);

  static PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),
    1, // version
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    32, // color depth
    0, 0, 0, 0, 0, 0,
    0, 0,
    0, 0, 0, 0, 0,
    16, // depth buffer
    0,
    0,
    0,
    0,
    0, 0, 0
  };

  int pixelFormat = ChoosePixelFormat(CanvasDc, &pfd);
  SetPixelFormat(CanvasDc, pixelFormat, &pfd);

  CanvasRc = wglCreateContext(CanvasDc);

  // Render OpenGL canvas

  wglMakeCurrent(CanvasDc, CanvasRc);
  CanvasResize(w, h);
  CanvasInit();
  CanvasDisplay();
  SwapBuffers(CanvasDc);

  // Execute GUI

  MSG msg;

  while (GetMessage(&msg, 0, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return 0;
}

我更新的示例包含每个Chris的更改:

// OpenGlTabWin32.cpp
// NOTE: canvas displays fine if TabPane creation is commented out

#include <windows.h>
#include <commctrl.h>
#include <gl/GL.h>

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")

#define CREATE_TAB_PANE

enum { IDC_TAB = 200, IDC_CANVAS = 201 };

static HWND TabPaneId;
static HWND CanvasId;

static WNDPROC CanvasWndProc;
static HGLRC CanvasRc;

////////////////////////////////////////////////////////////////////////////////
// WndProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WndProc(HWND hwnd,
                                UINT msg,
                                WPARAM wParam,
                                LPARAM lParam) {

  switch (msg) {
    case WM_CREATE:
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// CanvasInit
////////////////////////////////////////////////////////////////////////////////
static void CanvasInit() {

  glClearColor(0.0, 0.0, 0.0, 0.0);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasResize
////////////////////////////////////////////////////////////////////////////////
static void CanvasResize(int width, int height) {

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(0, 1, 0, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

////////////////////////////////////////////////////////////////////////////////
// CanvasDisplay
////////////////////////////////////////////////////////////////////////////////
static void CanvasDisplay() {

  glClear(GL_COLOR_BUFFER_BIT);

  glLoadIdentity();
  glColor3d(1, 0, 0);

  glBegin(GL_LINE_STRIP);
    glVertex2d(0.2, 0.2);
    glVertex2d(0.8, 0.8);
  glEnd();
}

////////////////////////////////////////////////////////////////////////////////
// OpenGlCanvasProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK OpenGlCanvasProc(HWND hwnd,
                                         UINT msg,
                                         WPARAM wParam,
                                         LPARAM lParam) {

  switch (msg) {
    case WM_PAINT:
    {
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd, &ps);

      wglMakeCurrent(hdc, CanvasRc);
      CanvasDisplay();
      SwapBuffers(hdc);
      wglMakeCurrent(NULL, NULL);
      ReleaseDC(hwnd, hdc);

      EndPaint(hwnd, &ps);

      return 0;
    }

    case WM_SIZE:
    {
      HDC hdc = GetDC(hwnd);
      wglMakeCurrent(hdc, CanvasRc);
      CanvasResize(LOWORD(lParam), HIWORD(lParam));
      wglMakeCurrent(NULL, NULL);
      ReleaseDC(hwnd, hdc);

      return 0;
    }
  }

  return CallWindowProc(CanvasWndProc, hwnd, msg, wParam, lParam);
}

////////////////////////////////////////////////////////////////////////////////
// WinMain
////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd) {

  // Create window

  const char *className = "OpenGlTab";

  WNDCLASSEX wc;
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = 0;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = className;
  wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

  if (!RegisterClassEx(&wc)) {
    exit(0);
  }

  HWND winId = CreateWindowEx(0,
                              className,
                              "Tab Pane w/ OpenGL",
                              WS_OVERLAPPEDWINDOW,
                              100, 100, 600, 400,
                              0,
                              0,
                              hInstance,
                              0);
  if (!winId) {
    exit(0);
  }

  ShowWindow(winId, nShowCmd);
  UpdateWindow(winId);

#ifdef CREATE_TAB_PANE

  // Create tab pane

  TabPaneId = CreateWindow(WC_TABCONTROL,
                           0,
                           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
                           10, 10, 566, 343,
                           winId,
                           HMENU(IDC_TAB),
                           hInstance,
                           0);

  TCITEM tabItem = {0};
  tabItem.mask = TCIF_TEXT;
  tabItem.pszText = "Tab";

  SendMessage(TabPaneId, TCM_INSERTITEM, 0, LPARAM(&tabItem));

#endif

  // Create OpenGL canvas

  int w = 200;
  int h = 200;

  CanvasId = CreateWindow("static",
                          "",
                          WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                          20, 100, w, h,
                          winId,
                          HMENU(IDC_CANVAS),
                          hInstance,
                          0);

  CanvasWndProc = (WNDPROC)SetWindowLongPtr(CanvasId,
                                            GWLP_WNDPROC,
                                            (LONG_PTR)OpenGlCanvasProc);

  HDC hdc = GetDC(CanvasId);

  static PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),
    1, // version
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    32, // color depth
    0, 0, 0, 0, 0, 0,
    0, 0,
    0, 0, 0, 0, 0,
    16, // depth buffer
    0,
    0,
    0,
    0,
    0, 0, 0
  };

  int pixelFormat = ChoosePixelFormat(hdc, &pfd);
  SetPixelFormat(hdc, pixelFormat, &pfd);

  CanvasRc = wglCreateContext(hdc);

  // Render OpenGL canvas

  wglMakeCurrent(hdc, CanvasRc);
  CanvasResize(w, h);
  CanvasInit();
  CanvasDisplay();
  SwapBuffers(hdc);
  wglMakeCurrent(NULL, NULL);
  ReleaseDC(CanvasId, hdc);

  // Execute GUI

  MSG msg;

  while (GetMessage(&msg, 0, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return 0;
}

1 个答案:

答案 0 :(得分:3)

此示例中有许多事情可能有所作为:

  • 最直接的问题可能只是选项卡窗口覆盖了画布窗口,而您实际上并没有绘制任何东西来响应WM_PAINT。一旦作为Windows无效,选项卡控件将在画布上绘制,通常允许子窗口相互绘制;因此,将WS_CLIPSIBLINGS添加到选项卡控件可能会有所帮助。

  • 您正在将HDC抓取到静态控件并在将其与当前wgl上下文关联后保留它。你不应该真的这样做,除非你使用CS_OWNDC的窗口类,特别是那个可能有CS_PARENTDC的窗口类(因为那时,只要父窗口或不同的子窗口绘制,DC就会重新关联一个从未将SetPixelFormat与之关联的窗口。)

  • 您只是将您的opengl上下文设置为当前,并期望稍后设置它。这很好 - 假设你有一个带有HDC的CS_OWNDC窗口,你可以抢先一步并保持不变 - 并且假设你永远不想因任何原因创建第二个GL上下文。

因此,当您在不控制窗口类样式的应用程序中执行OpenGL时(或者可能存在多个OpenGL上下文),您需要确保始终清除当前上下文并在您发布DC时立即释放DC完成它。

例如,您的CanvasWindowProc看起来应该更像这样:

case WM_PAINT:
  {
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd,&ps);
    wglMakeCurrent(glrc,hdc);
    CanvasDisplay();
    SwapBuffers();
    wglMakeCurrent(NULL,NULL);
    EndPaint(&ps);
  }
  return 0;
case WM_SIZE:
  {
    HDC hdc = GetDC(hwnd);
    wglMakeCurrent(glrc,hdc);
    CanvasResize(LOWORD(lParam),HIWORD(lParam));
    wglMakeCurrent(NULL,NULL);
    ReleaseDC(hwnd,hdc);
  }
  break;