Ввод
новых команд
Вы заметили,
что до сих пор обходились без каких-либо ресурсов. Мы не учитываем традиционный
диалог About, планку меню главного окна, панель инструментов, две таблицы (строк
и ускорителей) и два значка, которые присутствовали в каркасе приложения изначально.
Дальнейшее развитие потребует ввести новые ресурсы. Главным из них будет диалог,
который мы запустим в немодальном режиме и который позволит подробно исследовать
влияние параметров освещения на качество изображения. Начинать, как обычно,
следует с команд меню. Скорректируйте меню главного окна так, чтобы в нем появились
новые команды:
- Edit > Properties
(ID_EDIT_PROPERTIES);
- Edit >
Background (ID_EDIT_BACKGROUND);
- View > Fill
(ID_VIEW_FILL);
- View >
Quad (ID_VIEW_QUAD).
Одновременно
удалите не используемые нами команды: File > New, File > Open, File >
Save, File > Save as, File > Recent File, Edit > Undo, Edit > Cut,
Edit > Copy и Edit > Paste.
Примечание
Вы, конечно, знаете, что
идентификаторы команд можно не задавать. Они генерируются автоматически при
перемещении фокуса от вновь созданной команды к любой другой.
После этого
в классе cocview создайте обработчики всех новых команд с именами по умолчанию
(их предлагает Studio.Net). При создании реакций на эти команды меню (COGView
> Properties > Events) предварительно раскройте все необходимые элементы
в дереве Properties t Commands. Одновременно с функциями обработки типа
COMMAND создайте (для всех команд, кроме Edit > Background) функции обновления
пользовательского интерфейса, то есть функции обработки типа UPDATE_ COMMANDJJI.
Они, как вы помните, следят за состоянием команд меню и соответствующих им кнопок
панели управления, обновляя интерфейс пользователя. Команды становятся доступными
или, наоборот, в зависимости признака, управляемого програмистом.
В обработчике
OnEditBackground мы вызовем стандартный диалог по выбору цвета, сразу открыв
обе его страницы (см. флаг CC_FULLOPEN). С помощью этого диалога пользователь
сможет изменить цвет фона:
void
COGView::OnEditBackground
(void)
{
//======
Создаем объект диалогового класса
CColorDialog
dig(m_BkClr); //====== Устанавливаем бит стиля
dig.m_cc.Flags
|= CC_FULLOPEN;
//======
Запускаем диалог и выбираем результат
if
(cilg.DoModal ()==IDOK)
{
m_BkClr
= dig.m_cc.rgbResuit;
//======
Изменяем цвет фона
SetBkColor();
Invalidate(FALSE);
}
}
Проверьте результат,
запустив приложение и вызвав диалог. При желании создайте глобальный массив
с 16 любимыми цветами и присвойте его адрес переменной lpCustColors, которая
входит в состав полей структуры m_сс, являющейся членом класса CColorDialog.
В этом случае пользователь сможет подобрать и запомнить некоторые цвета.
В обработчик
OnViewQuad введите коды, инвертирующие булевский признак m_bQuad, который мы
используем как флаг необходимости рисования отдельными четырехугольниками (GL_QUADS),
и заново создают изображение. Если признак инвертирован, то мы рисуем полосами
(GL_QUAD_STRIP):
void
COGView::OnViewQuad(void)
{
//
Инвертируем признак стиля задания четырехугольников
m_bQuad
= ! m_bQuad;
//======
Заново создаем изображение
DrawScene
(); Invalidate(FALSE); UpdateWindow();
}
В обработчик
команды обновления интерфейса введите коды, которые обеспечивают появление маркера
выбора рядом с командой меню (или залипания кнопки панели управления):
void
COGView::OnUpdateViewQuad(CCmdUI*
pCmdUI)
{
//======
Вставляем или убираем маркер (пометку)
pCmdUI->SetCheck(m_bQuad==true);
}
Проверьте результат
и попробуйте объяснить зубчатые края поверхности (рис. 7.2). Не знаю, правильно
ли я поступаю, когда по ходу изложения вставляю задачи подобного рода. Но мной
движет желание немного приоткрыть дверь в кухню разработчика и показать, что
все не так уж просто. Искать ошибки в алгоритме, особенно чужом, является очень
кропотливым занятием. Однако совершенно необходимо приобрести этот навык, так
как без него невозможна работа в команде, а также восприятие новых технологий,
раскрываемых в основном посредством анализа содержательных (чужих) примеров
(Samples). Чтобы обнаружить ошибку подобного рода, надо тщательно проанализировать
код, в котором создается изображение (ветвь GL_QUAD_STRIP), и понять, что неправильно
выбран индекс вершины. Замените строку givertex3f (xn, yn, zn); HaglVertexSf
(xi, yi, zi); и вновь проверьте работу приложения. Зубчатость края должна исчезнуть,
но в алгоритме, тем не менее, осталась еще небольшая, слабо заметная неточность.
Ее обнаружение и исправление я оставляю вам, дорогой читатель.
Рис. 7.2.
Вид поверхности при использовании режима GL_QUAD_STRIP
Обработку следующей
команды меню мы проведем в том же стиле, за исключением того, что переменная
m_FillMode не является булевской, хоть и принимает лишь два значения (GL_FILL
и GL_LINE). Из материала предыдущей главы помните, возможен еще одни режим изображения
полигонов — GL_POINT. Логику его реализации при желании вы введете самостоятельно,
а сейчас введите коды двух функции обработки команды меню:
void
COGView::OnViewFill(void)
{
//===
Переключаем режим заполнения четырехугольника
m_FillMode
= m_FillMode==GL_FILL ? GL_LINE : GL__FILL;
//======
Заново создаем изображение
DrawScene();
Invalidate(FALSE);
UpdateWindow()
;
}
void
COGView::OnUpdateViewFill(CCmdUI *pCmdUI)
{
//======
Вставляем или убираем маркер выбора
pCmdUI->SetCheck(m_FillMode==GL_FILL)
;
}
Запустите и
проверьте работу команд меню. Отметьте, что формула учета освещения работает
и в случае каркасного изображения примитивов (рис. 7.3).
Рис. 7.3.
Вид поверхности, созданной в режиме GL_LINE
Для обмена
с диалогом по управлению освещением нам понадобятся две вспомогательные функции
GetLightParams и SetLightParam. Назначение первой из которых заполнить массив
переменных, отражающих текущее состояние параметров освещения сцены OpenGL.
Затем этот массив мы передадим в метод диалогового класса для синхронизации
движков (sliders) управления. Вторая функция позволяет изменить отдельный параметр
и привести его в соответствие с положением движка. Так как мы насчитали 11 параметров,
которыми хотим управлять, то придется ввести в окно диалога 11 регуляторов,
которым соответствует массив m_LightPaxam из 11 элементов. Массив уже помещен
в класс COGView, нам осталось лишь задействовать его:
void
COGView: :GetLightParams (int *pPos)
{
//======
Проход по всем регулировкам
for
(
int i=0; i<ll; i++)
//======
Заполняем транспортный массив pPos
pPos[i]
= m_LightParam[i] ;
void
COGView: :SetLightParam (short Ip, int nPos)
{
//====== Синхронизируем параметр lp и
//======
устанавливаем его в положение nPos
m_LightParam[lp]
= nPos;
//===
Перерисовываем представление с учетом изменений
Invalidate
(FALSE) ;
}