Иллюстрированный самоучитель по Visual Studio.Net

         

Введение обработчиков сообщений Windows


Наш объект, будучи активизирован в рамках окна контейнера, будет реагировать на сообщения Windows. Он должен управляться мышью, поддерживать вращение с помощью таймера, устанавливать нужный формат при создании своего окна и т. д. Введите в класс copenGL способность реагировать на следующие сообщения:

WM_ERASEBKGND, WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_LBUTTONUP, WM_RBUTTONUP, WM_MOUSEMOVE, WM_CREATE, WM_DESTROY, WM_SIZE, WM_TIMER.

Для этого:

  • Поставьте курсор на строку с именем класса COpenGL в окне ClassView и дайте команду Properties из контекстного меню.

  • Нажмите кнопку Messages на панели инструментов окна Properties.

  • Для того чтобы после введения обработчика окно свойств не убегало, переведите его в режим Floating и оттащите в сторону. В окне Class View должен быть выбран класс COpenGL

  • По очереди для всех перечисленных сообщений укажите действие <Add> в правом столбце таблицы Properties.
  • Обработчик сообщения OnEraseBkgnd вызывается операционной системой в те моменты, когда фон окна должен быть стерт, например при изменении размеров окна. Родительская версия этой функции или обработка по умолчанию использует для стирания (закрашивания) кисть, указанную в структуре WNDCLASS при ее регистрации. Если надо отменить стирание фона, то наша версия функции обработки должна установить специальный флаг, который говорит о том, что сообщение обработано, иначе окно останется помеченным как нуждающееся в стирании фона. Введите в файл реализации класса COpenGL код обработки сообщения:

    LRESULT COpenGL::OnEraseBkgnd(UINT /*uMsg*/, WPARAM

    /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)

    {

    //====== Устанавливаем флаг завершения обработки

    bHandled = TRUE;

    return 0;

    }



    Отметьте, что прототип функции обработки отличается от того, который принят в MFC. Там он имеет вид af x_msg BOOL OnEraseBkgnd(CDC* pDC); и определен в классе CWnd. Наш класс COpenGL среди своих многочисленных предков имеет класс CComControl, который происходит от класса CWindowlmpl, а тот, в свою очередь, является потомком класса cwindow.
    Последний выполняет в ATL ту же роль, что и класс cwnd в MFC, но не несет с собой бремени наследования от CObject. Это в основном и ускоряет функционирование ATL-приложений.

    Примечание
    В заготовке тела функций обработки все параметры закомментированы. Это сделано для того, чтобы упростить работу компилятору, так как далеко не все параметры задействованы постоянно. Если параметр необходимее его нужно сделать видимым для компилятора, убрав знаки комментария. Сделайте это для параметра bHandled.
    Теперь введите в класс обработчик сообщения WM_CREATE и заполните его кодами, которые готовят окно и устанавливают некоторые параметры OpenGL:

    LRESULT COpenGL::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/,'LPARAM /*lParam*/, BOOL& bHandled)

    //======= Описатель формата окна OpenGL

    PIXELFORMATDESCRIPTOR pfd =

    {

    sizeof(PIXELFORMATDESCRIPTOR),
    // Размер структуры
    1,
    // Номер версии
    PFD_DRAW_TO_WINDOW |
    // Поддержка
    GDI PFD_SUPPORT_OPENGL |
    // Поддержка OpenGL
    PFD_DOUBLEBUFFER,
    // Двойная буферизация
    PFD_TYPE_RGBA,
    // Формат RGBA, не палитра
    24,
    // Количество плоскостей

    // в каждом буфере цвета

    24, 0,
    // Для компонента Red
    24, 0,
    // Для компонента Green
    24, 0,
    // Для компонента Blue
    24, 0,
    // Для компонента Alpha
    0,
    // Количество плоскостей

    // буфера Accumulation

    0,
    // То же для компонента Red
    0,
    // для компонента Green
    0,
    // для компонента Blue
    0, // для компонента Alpha
    32, // Глубина Z-буфера
    0, // Глубина буфера Stencil
    0, // Глубина буфера Auxiliary
    0, // Теперь игнорируется
    0, // Количество плоскостей
    0, // Теперь игнорируется
    0, // Цвет прозрачной маски
    0 // Теперь игнорируется

    };

    // Добываем дежурный контекст и просим выбрать ближайший

    m_hdc = GetDCO ;

    int iD = ChoosePixelFormat(m_hdc, &pfd) ;

    if ( !ID )
    {

    ATLASSERT(FALSE);

    return -1;
    }

    //====== Пытаемся установить этот формат

    if ( ISetPixelFormat (m_hdc, iD, &pfd))
    {

    ATLASSERT(FALSE);

    return -1;
    }

    //====== Пытаемся создать контекст передачи OpenGL



    if ( !(m_hRC = wglCreateContext (m_hdc)))
    {

    ATLASSERT(FALSE);

    return -1;
    }

    //====== Пытаемся выбрать его в качестве текущего

    if ( !wglMakeCurrent (m_hdc, m_hRC))
    {

    ATLASSERT(FALSE);

    return -1;
    }

    //====== Теперь можно посылать команды OpenGL

    glEnable (GL_LIGHTING) ;
    // Будет освещение
    glEnable (GL_LIGHTO) ;
    // Только 1 источник
    glEnable (GL_DEPTH_TEST) ;
    // Учитывать глубину (ось Z)

    //====== Учитывать цвет материала поверхности

    glEnable (GL_COLOR_MATERIAL) ;
    //====== Устанавливаем цвет фона

    SetBkColor () ;
    bHandled = TRUE;
    return 0;
    }

    Класс copenGL должен реагировать на сообщение WM_SIZE и корректировать видимый объем сцены. Мы будем использовать режим просмотра с учетом перспективы. Его определяет функция gluPerspective. Введите в класс copenGL обработку WM_SIZE и вставьте в нее следующие коды:

    LRESULT COpenGL: :OnSize(UINT /*uMsg*/, WPARAM /*wParam*/,

    LPARAM IParam, BOOL& bHandled)
    {

    // Распаковываем длинный параметр и узнаем размеры окна

    UINT сх = LOWORD ( IParam) , су = HIWORD (IParam) ;

    //====== Вычисляем максимальные диспропорции окна

    double dAspect = cx<=cy ? double (су) /сх

    : double (сх) /су;

    //==== Задаем тип текущей матрицы (матрица проекции)

    glMatrixMode (GL_PROJECTION) ;

    //====== Приравниваем ее к единичной диагональной

    glLoadldentity () ;

    //== Параметры перспективы (45 градусов - угол обзора)
    gluPerspective (45., dAspect, 1., 10000.);
    glViewport (0, 0, сх, су); DrawScene () ;
    bHandled = TRUE;
    return 0;
    }

    Функция glViewport, как вы помните, задает прямоугольник просмотра. При закрытии окна внедренного объекта необходимо освободить память, занимаемую контекстом передачи, и отказаться от услуг таймера, с помощью которого мы будем производить анимацию вращения изображения. Введите в класс обработчик сообщения WM_DESTROY и измените ее стартовый код, как показано ниже:

    LRESULT COpenGL: :OnDestroy (UINT /*uMsg*/, WPARAM

    /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)

    {
    KillTimer(l);


    if (m_hRC)

    {

    wglDeleteContext(m_hRC); m_hRC = 0;
    }

    bHandled = TRUE;
    return 0;
    }

    Инициализация переменных

    В конструктор класса вставьте код установки начальных значений переменных, с помощью которых пользователь сможет управлять сценой Open GL:

    COpenGL: : COpenGL()
    {

    //====== Контекст передачи пока отсутствует

    m_hRC = 0;

    //====== Начальный разворот изображения

    m_AngleX = 35. f;
    m_AngleY = 20. f;

    //====== Угол зрения для матрицы проекции

    m_AngleView = 45. f;

    //====== Начальный цвет фона

    m_clrFillColor = RGB (255,245,255);

    //====== Начальный режим заполнения

    //====== внутренних точек полигона

    m_FillMode = GL_FILL;

    //====== Подготовка графика по умолчанию

    DefaultGraphic ();

    //=== Начальное смещение относительно центра сцены
    //=== Сдвиг назад на полуторный размер объекта
    m_zTrans = -1.5f*m_fRangeX;
    m_xTrans = m_yTrans = 0.f ;

    // Начальные значения квантов смещения (для анимации)

    m_dx = m_dy = 0.f;

    //=== Мыть не захвачена

    m_bCaptured = false;

    //=== Правая кнопка не была нажата

    m_bRightButton = false;

    //=== Рисуем четырехугольниками m_bQuad = true;

    //====== Начальный значения параметров освещения

    m_LightParam[OJ = 50; // X position
    m_LightParam[l] = 80; // Y position
    m_LightParam[2] = 100; // Z position

    m_LightParam[3] = 15; // Ambient light

    m_LightPararn[4] = 70; // Diffuse light

    m_LightParam[5] = 100; // Specular light

    m_LightParam[6] = 100; // Ambient material

    m_LightParam[7] = 100; // Diffuse material

    m_LightParam[8] = 40; // Specular material

    m_LightParam[9] = 70; // Shininess material

    m_LightParam[10] = 0; // Emission material
    }
    Функция перерисовки

    Перерисовка изображения OpenGL состоит в том, что обнуляется буфер цвета и буфер глубины — буфер третьей координаты. Затем в матрицу моделирования (GL_MODELVIEW), которая уже выбрана в качестве текущей, загружается единичная матрица (glLoadldentity). После этого происходит установка освещения, с тем чтобы на него не действовали преобразования сдвига и вращения.


    Лишь после этого матрица моделирования домножается на матрицу трансляции и матрицу вращений. Чтобы рассмотреть изображение, достаточно иметь возможность вращать его вокруг двух осей (X и Y). Поэтому мы домножаем матрицу моделирования на две матрицы вращений (glRotatef). Сначала вращаем вокруг оси X, затем вокруг оси Y:

    HRESULT COpenGL: :OnDraw (ATL_DRAWINFO& di)

    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadldentity{);

    //====== Установка параметров освещения

    SetLight ();

    //====== Формирование матрицы моделирования

    glTranslatef(m_xTrans,m_yTrans,m_zTrans);
    glRotatef (m_AngleX, l.0f, 0.0f, 0.0f );
    glRotatef (m_AngleY, 0.0f, l.0f, 0.0f );

    //====== Вызов рисующих команд из списка

    glCallList(1);

    //====== Переключение буферов

    SwapBuffers(m_hdc);
    return S_OK;

    }

    Содержание раздела