NeHe Tutorials Народный учебник по OpenGL
Урок X3. OpenGL

Не так давно я обнаружил этот сайт и взялся за изучение OpenGL с его помощью. Народный учебник по OpenGL - очень хорошая идея. Я надеюсь, что этот урок поможет многим пользователям в освоении OpenGL. Я пооследовательно адаптировал коды этих уроков к Microsoft Visual Studio , и в процессе возникла идея объединения их с тем, чтобы использовать для компиляции собственных программ.

 

 Уже после того, как это было сделано, я обнаружил, что на сайте NeHe GameDev имеется адаптация всех уроков к  MS Studio .NET (Grant James), но не в MFC. По моему мнению, в MFC гораздо больше возможностей для разработки приложений. Я адаптировал  почти все уроки к MFC в директории LessonsGLCode и свел их в одну общую программу GLSummary. В процессе адаптации я столкнулся с тем, что возникают конфликты при переводе проектов из одной модификации Microsoft Visual Studio в другую. В конце концов, в демонстрационных целях я остановился на следующих вариантах (ссылки на меня приветствуются, но не обязательны):

-        GLSummaryCode_2010 – один проект, обобщающий все уроки;

-        LessonsGLCode_2010 – все проекты, но из каждого можно вызвать любой другой.

 

Здесь 2010 означает, что проекты предназначены для Microsoft Visual Studio Professional 2010. Впрочем, они безболезненно адаптируются к версии 2012, но для нее нужен уже Windows 7, версия 2010 работает и там, и в Windows XP.

 

Эти коды могут также здорово помочь тем, кто начинает изучать Visual C++, однако в дальнейшем изложении я предполагаю, что у читащего этот текст имеются базовые навыки работы с этим компиллятором. Прилагаемые коды можно сразу компиллировать в указанной среде, и, если они запущены в соответствующей версии MS VS, то должны сработать с одного пинка.

 

Изложение этого урока относится к GLSummaryCode_2010. Впрочем, в версии 2012 особых отличий не будет.

 

            Коды адаптированных мною проектов собраны в директории GLSummaryCode_2010. Основной проект для данного урока GLSummary. Все иcходные данные для этого проекта и всех прочих находятся в папке Data. Для редактирования текстов запускается GLSummary.sln. Даже если у вас нет большого опыта использования Visual C++, можете начать его изучение с запуска этого (или любого другого прилагаемого) проекта. Общие для всех проектов подпрограммы расположены в папке GlobUse.

 

Список подпрограмм, которые задействованы в проекте, находятся по закладке Обозреватель решений(Solution Explorer (меню View->Solution Explorer).) Выбрав любой *.h или *.cpp файл, можно его просмотреть и(или) редактировать. На первый взгляд, файлов этих очень много, но при ближайшем рассмотрении все оказывается довольно простым.

 

Скомпиллируйте программу(меню Build->Build Solution) и запустите программу (меню Debud->Start Without Debugging). Если все правильно сделано, то будет запущена демонстрация одного из уроков (того, чьё название хранится в файле LastNum.txt в папке LessonsGL/GLSummary). Для демонстрации любого другого урока обратитесь в меню File->Lesson... Появится диалоговое окно:

 

 

Вы можете выбрать любой другой урок из списка для демонстрации.

 

      Для формирования самостоятельного приложения:

-        создайте отдельную папку (например, GLTest);

-        скопируйте  в  эту новую папку программу  GLSummary.exe,  которая находится в папке ../GLSummary/Release;

-        скопируйте в эту же папку папки HelpText, Data, Art и BoxData, и приложение готово.

 

Файлы GLSummary.cpp, GLSummaryDoc.cpp,GLSummaryView.cpp, GLSummary.rc, resource.h были созданы компиллятором Visual C++ 7.0 при создании проекта. Mainfrm.cpp первоначально тоже создавалась компиллятором, но затем она была перенесена в папку GlobUse для использования всеми проектами в директории LessonsGL.

 

В файле GLSummaryView.cpp и и во всех других проектах  в *View.cpp  процедура CreateGLWindow вызывается по прерыванию OnCreate.

 

int CGLSummaryView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

       if (CView::OnCreate(lpCreateStruct) == -1)

             return -1;

       CreateGLWindow(this,32);

       return 0;

}

 

Процедура инициализации InitGLn вызывается в OnInitialUpdate:

 

void CGLSummaryView::OnInitialUpdate()

{

       CView::OnInitialUpdate();

       FILE * f = fopen("LastNum.txt","rt");   //Get last lesson name

    if(f != NULL)

       {

             fgets(str, 256, f);

             fclose(f);

             CString tString = str;

             m_strNum = strtok(str, ". _-:");

             m_strLesson = tString;

       m_pDlgTitle = new CDlgTitle;           

       m_pDlgTitle->SetTitle(tString);

       } 

       InitGLn();

       GetDocument()->SetTitle(GetLessonName(m_strNum));

}

 

Символьная переменная m_strNum является глобальной и служит идентификатором выбранного урока.

 

Процедура ResizeGLScene вызывается  по прерыванию OnSize:

 

 void CGLSummaryView::OnSize(UINT nType, int cx, int cy)

{

       CView::OnSize(nType, cx, cy);

 

       ReSizeGLScene(cx,cy);

       GetMainFrameGlob->SetTitlePos();

}

 

Собственно изображение осуществляется процедурой DrawGLSceneN по прерыванию OnDraw:

 

void CGLSummaryView::OnDraw(CDC* /*pDC*/)

{

DrawGLSceneN();

}

 

или  по прерыванию OnTimer:

 

void CMainFrame::OnTimer(UINT nIDEvent)

{

             switch(nIDEvent)

{

 case TIMER_PLAY:

       UpdateGLScene();

       DrawGLSceneN();

  break;

}

       CFrameWnd::OnTimer(nIDEvent);

}

 

или при выполнении установленных для пользователя действий.  Смена атрибутов, формирующих изображение, производится процедурой  UpdateGLScene  по прерыванию OnTimer.

 

Процедуры WndProc  и WinMain в MFC не нужны.

 

Для управления изображением вместо WinMain в исходных уроках используется обработка событий, принятая в Visual C++.

 

Например, обработка нажатия клавиши 'L' выполняется процедурой CGLSummaryView::OnVkL.

 

void CGLSummaryView::OnVkL(){ProcessKeyboardN("L");}

 

Организация этой процедуры была произведена при помощи AppStudio(меню View->Resource View). Я не буду подробно описывать процесс создания этой процедуры, он описан в MSDN Library.

 

Переход в полноэкранный режим в отличие от исходных уроков осуществляется одновременным нажатием клавиш Ctrl+Enter и выполняется процедурой CMainFrame::OnVkReturnCtrl:

 

void CMainFrame::OnVkReturnCtrl()

{

       m_bFullScreen = ! m_bFullScreen;

if (m_bFullScreen)

{

    ModifyStyle(WS_CAPTION, 0);

       m_wndToolBar.ShowWindow(SW_HIDE);

       m_wndStatusBar.ShowWindow(SW_HIDE);

       ShowWindow(SW_SHOWMAXIMIZED);

       HideMenu();

}

else

 {

   ModifyStyle(WS_CAPTION, WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE

             | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_MAXIMIZE);

       ShowMenu();

       m_wndToolBar.ShowWindow(SW_SHOWNORMAL);

       m_wndStatusBar.ShowWindow(SW_SHOWNORMAL);

       ShowWindow(SW_SHOWNORMAL);

}//if (m_bFullScreen)

}

 

Клавиша F1 в отличие от исходных уроков задействована по своему стандартному назначению для вызова помощи по программе (меню Help->GL Help Summary) и выполняется процедурой CMainFrame::OnHelp():

 

void CMainFrame::OnHelp()

{

       if(m_pAboutDlg == NULL)

             m_pAboutDlg = new CAboutDlg();

    m_pAboutDlg->ShowWindow(SW_SHOWNORMAL);

}

 

Диалоговое окно m_pAboutDlg является немодальным, что позволяет осуществлять управление программой, как из этого окна, так и другими способами:

 

 

Ещё одно отличие от исходных уроков в том, что все процедуры работы с текстурами собраны в файле GlobGLTexture.cpp в директории GlobUse. Я только добавил в эти процедуру LoadGLTextureImage(CString fName), которая позволяет загрузить в текстуру файл с изображением любого типа, используя класс CImage:

 

int LoadGLTextureImage(CString fName)

{

if(!isFileExist(fName))

{

AfxMessageBox("LoadGLTextureImage\nNo File exist:\n" + fName);

return FALSE;

}

CImage * pImg = new CImage;

pImg->Load(fName);

CBitmap * pBm = GetImgBitmap(pImg);

if(pBm == NULL)

return FALSE;

BITMAP BMP;

pBm->GetBitmap(&BMP);

GLuint nt = LoadGLTexture(BMP.bmWidth, BMP.bmHeight,GL_BGR_EXT, BMP.bmBits);

delete pBm;delete pImg;

pImg = NULL;

return nt;

}

 

Файл GlobGLTexture.cpp можно скопировать и использовать при создании любых приложений, использующих OpenGL.

 

Сами уроки в этом приложении представлены на закладке Solution Explorer  в виде файлов *DrawInit.cpp. Это как раз те коды, которые были любезно предоставлены на NeHe.GameDev, и я старался, насколько это возможно не менять их  в процессе адаптации к MFC.

 

Единственное большое изменение – это то, что я взял на себя смелость исключить из всех проектов библиотеку Glaux.lib. Но, я думаю, авторы оригинальных текстов не будут на меня в обиде. Не могли же они ожидать от разработчиков MS VS такой подлости, как исключение библиотеки Glaux.lib, начиная с 10-й версии. И, если к 10-й версии её ещё можно привязать, то в 11-й она вовсе не работает.

 

Собрание всех основных уроков в отдельную директорию ClobUse позволяет применять и совмещать общие подпрограммы в отдельных уроках, а также разрабатывать собственые приложения путем компиляции отдельных подпрограмм, собранных в LessonsGL. Примеры разработки таких приложений приведены в предлагаемых уроках: Урок X4.Игры с УФО (проект lessonX4/UFOgame) и Урок X5.Боксер (проект lessonX5/BoxMan).

 

Урок X4.Игры с УФО создан из уроков 07, 11, 30, 32, 34. Урок X5.Боксер демонстрирует возможность создания фигур и их движений во внешнем текстовом файле, причем пользователю не нужно знать программистских деталей, достаточно только пространственного воображения.

 

Мне представляется, что знакомство с OpenGL с помощью готовых рабочих кодов вполне вписывается в концепцию Народного учебника по OpenGL. Я не имею большого опыта работы с OpenGL, и некоторые уроки мне не удалось вполне адаптировать к MS VS. Я надеюсь, что уважаемым авторам оригинальных текстов уроков не составит труда подправить мои коды, чтобы в полной мере привести их к их первоначальным замыслам.

 

Код к уроку

 

LessonsCode_2010.7z (5.09 Mb)

© Владимир Петров
petrov@msun.ru

PMG  17 апреля 2013