Шаг 6

Сегодняшний шаг будет дальнейшим развитием предыдущего Шага 5. На предыдущем шаге мы создали каркас OpenGL приложения. Сейчас самое время наполнить его чем-нибудь красивым. В качестве красивого я выбрал урок №6 "Наложение текстуры" известного Nehe (русский перевод его уроков здесь).
Цель этого укрока следующая - максимально точно воспроизвести С++ код урока Nehe на Lua. На всякий случай еще раз: полного биндинга OpenGL к Lua мы здесь делать не будем, в Lua будут экспортироваться только те функции OpenGL, которые нужны для урока (остальные вы легко сможете приделать сами :).
Если посмотреть на код урока Nehe, то мы увидим, что в нем используется большое количество констант OpenGL. Чтобы не передавать из Lua в С непонятные числа со значениями этих констант, нам нужно зарегистрировать константы в нашей библиотеке с очевидными именами. Напишем небольшой макрос, который упростит нашу задачу:

// stack: opengl
#define glConst(L, name) lua_pushnumber(L, GL_##name);\ \\ stack: opengl GL_name
                         lua_setfield(L, -2, #name)\ \\ stack: opengl


Что делает этот макрос? Для начала мы предполагаем, что при вызове макроса на вершине стека Lua находится таблица нашей библиотеки. На вершину стека мы кладем константу OpenGL, которая имеет вид GL_name. Склейка GL_ и имени переменной производится препроцессором С++ при помощи оператора ##. Затем мы устанавливаем в таблице, находящейся в стеке по индексу -2 (вторая от вершины стека, на самую вершину стека мы только что положили число) поле с ключем name. Параметр макроса name препроцессор С++ превращает в символьную константу "name" при помощи оператора #. Функция Lua lua_setfield()удаляет из стека верхний элемент, поэтому после выполнения макроса на вершине стека опять окажется таблица нашей библиотеки. Следующий вызов:

glConst(L, 
TEXTURE_2D);

сформирует две строки кода С++:

lua_pushnumber(L, GL_TEXTURE_2D); \\ stack: opengl GL_name
lua_setfield(L, -2,
"TEXTURE_2D"); \\ stack: opengl

Из Lua обращаться к этой константе будет очень просто:

opengl.TEXTURE_2D

Переходим к функциям OpenGL. Чтобы код Lua выглядел похоже на код С, мы будем регистрировать функции OpenGL в нашей библиотеке без приставки gl. Тогда, загрузив в Lua нашу библиотеку вызовом:

local gl = require('opengl')

вызов функции (на С):

glEnable(GL_TEXTURE_2D);

будет выглядеть как:

gl.Enable(gl.TEXTURE_2D)

Кажется, весьма похоже!
Весь код библиотеки приводить здесь смысла нет, поясню только основные моменты.
Для простоты, вызов макроса glConst() со всеми константами OpenGL 1.1 я поместил в отдельный файл. Сделал я это в Notepad++ очень просто: скопировал в него все константы из файла glew.h, затем заменил все "#define GL_" на "glConst(L, " (все без кавычек), затем в диалоге замены поставил галочку "Регулярное выражение" и заменил " 0x.*$" на ");" (тоже все без кавычек). Файл с константами я сохранил в папке modules под именем glconst.inl и в функции registerConstants() включил его includ'ом.
Все функции, если в них передается неправильный аргумент генерируют ошибку Lua, после чего Lua печатает состояние своего стека и аварийно завершается. Это весьма удобно при отладке, поскольку Lua печатает номер строки, в которой произошла ошибка.
Функция возврата ошибок OpenGL возвращает массив строк с описанием ошибок (для этого использовалась функция из утилит OpenGL gluErrorString()) .
Теперь о загрузке изображений. В уроке Nehe использовалась библиотека утилит OpenGL glaux, которая умеет загружать только изображения .BMP. Но мы-то хочем много и сразу :) На работе я использую библиотеку DevIL, однако ее бинарники подключить к проекту в Code::Blocks мне не удалось. Code::Blocks упорно делал вид, что никаких библиотек я к нему не подключал и отчаянно ругался. Поиск решения проблемы в Google тоже ничего не дал. Ну да ладно, на этой библиотеке свет клином не сошелся. В Google я ввел "opengl image library". И мне на удочку попалась чудесная библиотека SOIL, предназначенная специально для загрузки избражений в OpenGL (но в данном уроке эти ее способности нам не пригодятся).
Библиотека SOIL понимает несколько распространенных форматов изображений (взято с сайта SOIL):
Для наших нужд вполне достаточно. Сама библиотека скомпилирована в статическую библиотеку, которую нужно подключить так:



Внимание! Порядок подключения библиотеки важен! Она должна быть включена перед библиотекой opengl32.a.
Функция  loadImageData() принимает на вход имя файла, и в случае удачи возвращает четыре значения:
  1. указатель на массив unsigned char* (в Lua передается как lightuserdata, фактически указатель на void*)
  2. ширина изображения
  3. высота изображения
  4. количество байт на пиксель
Эти данные затем используются в функции texImage2D().
Также был изменен код текстурирования кубика. Дело в том, что в своем уроке Nehe использовал текстуру BMP, а в ней начало текстуры находится в левом нижнем углу. В текстурах других форматов начало текстуры обычно находится в левом верхнем углу.
Также в Lua я приделал небольшой счетчик FPS (на моем чахлом ноутбуке получилось около 500 кадров в секунду).
Результат сегодняшнего урока у меня получился такой:



Что будет дальше, пока не знаю, но на сегодня все.
До встречи!

Файлы урока можно взять здесь.

Назад
Hosted by uCoz