Шаг 5
Итак, сегодня мы займемся серьезным делом. Сделаем две Lua библиотеки -
sdl и opengl. Первая - это кроссплатформенная библиотека для работы с
окнами, вторая - трехмерная графическая библиотека. Позже, на основе
этого примера, мы сможем соорудить что-нибудь еще более интересное.
На этом шаге будет показано несколько важных вещей. Во-первых, мы
создадим "большой" проект, то есть проект, состоящий из нескольких
подпроектов. Для этого нам нужно будет создать структуру папок, в
которых будут лежать сами проекты и некоторый вспомогательный код.
Во-вторых, будет показано, как готовые библиотеки превращать в
Lua-модули. Вспомогательный код,
облегчающий взаимодействие с Lua, подробно описан в Шаге 4. Вот, вкратце, круг ближайших задач.
Приступим.
Для написания и компиляции кода С++ будем использовать бесплатную IDE
Codeblocks (http://www.codeblocks.org). Для написания кода Lua подойдет
любой текстовый редактор. Я предпочитаю бесплатный Notepad++
(http://notepad-plus.sourceforge.net), который умеет подсвечивать
синтаксис Lua.
Итак, сначала нам нужно организовать наше рабочее место. Создадим папку
для нашего проекта. Я ее назову lualibs.
В ней создадим следующие папки:
- bin, в которой будут лежать все необходимые для запуска
программы бинарные файлы (*.exe, *.dll, и т.д).
- include, в которой будут лежать заголовочные файлы .h, .hpp
для используемых библиотек
- lib, в которой будут лежать файлы .a для
используемых библиотек
- luautil, в которой будут лежать вспомогательные файлы для
работы с Lua в С++
- modules, в которой будут располагаться наши Lua модули.
В папке modules создадим следующие папки:
- sdl, в которой будет находиться код для
модуля библиотеки sdl.
- opengl, в которой будет находиться код для
модуля библиотеки opengl.
Рабочее место подготовили. Теперь нам нужно взять в интернете
библиотеки, которые мы будем использовать. Как минимум, нам понадобится
библиотека Lua. Идем на http://www.lua.org. Далее идем в download ->
binaries-> download. Скачиваем lua5_1_2_Win32_bin.zip.
Распаковываем
его в нашу папку bin. Также скачиваем lua5_1_2_Win32_dll_lib.zip и
распаковываем lua5.1.lib в папку lib, а в нашей папке include создаем
папку lua и в нее распаковываем файлы из папки архива include.
Проверим, все ли правильно мы сделали. Для этого создадим тестовый
консольный проект.
Запускаем Code::blocks. Создаем новое
консольное приложение. Файл проекта console.cbp сохраним в папке
modules. Настроим проект. В меню
Project->Properties->вкладка
Targets. Настроим Output filename так, чтобы получившийся файл .exe
помещался в папку bin. У меня это выглядит так:
Далее нажимаем кнопку Target's build options. Переходим на вкладку
Linker.Добавляем библиотеку lua5.1.lib.
Переходим на вкладку Directories. В ней на вкладке Compiler добавляем
нашу папку include.
Нажимаем Ok. В файл main.cpp добавляем строки:
#include "lua/lua.hpp"
и первой строкой в функции main():
lua_State* L = lua_open();
Запускаем программу на выполнение: меню Build->Run. Должно
напечататься "Hello, world!". Отлично, треть дела сделана!
Теперь сделаем Lua-модуль для библиотеки sdl. Идем на http://www.libsdl.org.
Далее Download->SDL 1.2.В секции Source Code скачиваем
SDL-1.2.12.zip и распаковываем содержимое папки include в нашу папку
include/sdl. В секции Runtime Libraries->Win32 скачиваем
SDL-1.2.12-win32.zip. Распаковываем sdl.dll в папку bin. В секции
Development Libraries->Win32 скачиваем SDL-devel-1.2.12-VC6.zip.
Файлы .lib распаковываем в папку lib.
Идем в Code::blocks. Создаем новый dll проект. Назывем его sdl и сохраняем в папке
modules/sdl. Настраиваем для проекта пути и библиотеки. По аналогии с
тестовым проектом изменяем расположение результирующего dll файла на ../../bin/lua-sdl.dll.
Далее снова кнопка Target's build options. Добавляем в проект библиотеки lua и sdl.
Затем открываем вкладку Directories. Настраиваем пути к заголовочным файлам lua, sdl и утилит lua.
Удалим из проекта созданный по умолчанию файл main.c и создадим
новый файл sdl.cpp. Это будет модуль Lua. Как изготавливать модули
Lua подробно рассказано в Шаге 3. Наш модуль
будет называться sdl и в Lua он будет экспортировать две функции -
Create() и Run(). Для функционирования самого модуля скорее всего
понадобятся еще какие-нибудь функции, но из Lua будут доступны (пока)
только эти две. Позже добавим к ним другие функции.
Создадим "рыбу" модуля, то есть сначала напишем весь необходимый код
для регистрации функций Create() и Run() в Lua, а затем заполним тела
функций реальным содержанием. На этом этапе файл sdl.cpp может
выглядеть так:
#include "lua/lua.hpp"
static int create(lua_State* L)
{
return 0;
}
static int run(lua_State* L)
{
return 0;
}
static const luaL_Reg sdl[] = {
{"Create", create},
{"Run", run},
{0, 0},
};
extern "C" __declspec(dllexport) int luaopen_sdl(lua_State* L)
{
luaL_register(L, "sdl", sdl);
return 1;
}
Компилируем проект. Файл lua-sdl.dll создан успешно. Замечательно. Едем дальше.
Итак, функция Create() инициализирует библиотеку sdl, выполняет
инициализацию OpenGL и создает окно заданного размера. Она принимает на
вход три параметра - ширина окна, высота окна и во весь экран или нет.
Внутри этой функции также выполняется настройка OpenGL. Заведем
несколько статических переменных (они нам могут пригодиться потом):
static SDL_Surface* screen = 0;
static int screenWidth, screenHeight;
static bool fullScreen = false;
static int create(lua_State* L)
{
// stack: width height fullscreen
if(!fromLua(L, 1, screenWidth) ||
!fromLua(L, 2, screenHeight))
{
luaL_error(L, "Invalid arguments!\n");
}
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// включаем поддержку Unicode в sdl
SDL_EnableUNICODE(1);
fromLua(L, 3, fullScreen);
if(fullScreen)
{
screen = SDL_SetVideoMode(screenWidth, screenHeight, 32, SDL_OPENGL | SDL_FULLSCREEN);
}
else
{
screen = SDL_SetVideoMode(screenWidth, screenHeight, 32, SDL_OPENGL | SDL_RESIZABLE);
}
SDL_WM_SetCaption("LUA GUI TEST", "Lua gui test");
return 0;
}
Теперь функция Run(). Она запускает бесконечный главный цикл
программы. Внутри цикла на каждой итерации происходит следующее:
-вызывается глобальная Lua функция idle(), внутри которой производится вся работа по синхронизации состояния программы
-затем вызываются обработчики для внешних воздействий пользователя (клавиатура, мышь и т.д.)
-вызывается глобальная Lua функция draw(), внутри которой должно производиться все рисование.
-вызывается функция SDL_Delay(), которая позволяет операционной системе
обрабатывать другие потоки (иначе даже при ничтожной загрузке в нашем
потоке вся система будет работать очень медленно).
-цикл повторяется.
static int delay = 1;
static int run(lua_State* L)
{
SDL_Event event;
bool done = false;
while(!done)
{
idle(L);
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
done = true;
break;
case SDL_VIDEORESIZE:
break;
case SDL_MOUSEMOTION:
break;
case SDL_MOUSEBUTTONUP:
break;
case SDL_MOUSEBUTTONDOWN:
break;
case SDL_KEYUP:
break;
case SDL_KEYDOWN:
break;
default:
break;
}
}
draw(L);
SDL_GL_SwapBuffers();
SDL_Delay(delay);
}
SDL_Quit();
return 0;
}
Компилируем. На выходе получаем готовый Lua-модуль. Протестируем его.
В папке sdl создадим Lua-скрипт sdltest.lua:
package.path = './?.lua'
package.cpath = '../../bin/lua-?.dll'
local sdl = require('sdl')
sdl.Create(300, 200, false)
sdl.Run()
Создадим там же файл sdltest.bat для запуска скрипта:
..\..\bin\lua5.1.exe sdltest.lua
Запускаем. У меня получилось такое окно:
Мда... Поскольку мы никакого реального рисования пока не выполняем, то
в окне отобразился какой-то мусор от предыдущих запусков OpenGL
приложений.
Переходим к третьей части нашего шага. Изготовим Lua-библиотеку для
OpenGL. Небольшое замечание. Писать полный биндинг OpenGL к Lua у меня
нет ни малейшего желания. Поэтому в Lua будут экспортироваться только
те функции и константы OpenGL, которые нам нужны для решения конкретной
задачи. Поехали. Cоздаем новый .dll проект, который сохраним в папке
modules/opengl. Ставим галочку "Do not create any file" (все
равно файл, сгенерированный по умалчанию, нам не нужен).
Создаем новый файл opengl.cpp и добавляем его в проект. Также добавляем в проект файлы из папки LuaUtil.
Изменим имя результирующей dll:
Настраиваем пути:
Теперь настраиваем библиотеки. Для работы с OpenGL будем использовать библиотеку glew (http://glew.sourceforge.net).
Эта библиотека позволяет прозрачно работать с расширениями OpenGL (если
нам таковые вдруг понадобятся). Скачиваем бинарники для Windows и
распаковываем glew32.dll в нашу папку bin, glew32.lib в папку lib, а
glew.h в папку include.
Подключаем библиотеки:
В файле opengl.cpp пишем "рыбу" с единственной функцией Create():
#include "LuaUtil.h"
#include "glew.h"
#include <iostream>
static int create(lua_State* L)
{
// stack:
glewInit(); // инициализация glew
return 0;
}
static const luaL_Reg opengl[] = {
{"Create", create},
{0, 0},
};
extern "C" __declspec(dllexport) int luaopen_opengl(lua_State* L)
{
luaL_register(L, "opengl", opengl);
return 1;
}
Компилируем. Вроде все компилируется. Добавим функцию Clear():
static int clear(lua_State* L)
{
// stack:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
return 0;
}
static const luaL_Reg opengl[] = {
{"Create", create},
{"Clear", clear},
{0, 0},
};
Пишем тест для проверки. В папке modules создаем файл opengltest.lua:
package.path = './?.lua'
package.cpath = '../bin/lua-?.dll'
local sdl = require('sdl')
local gl = require('opengl')
gl.Create()
sdl.Create(300, 200, false)
function idle()
print('idle')
end
function draw()
gl.Clear()
end
sdl.Run()
Создадим там же файл opengltest.bat для запуска скрипта:
..\bin\lua5.1.exe opengltest.lua
Запускаем.
Отлично! В функции draw(), которая вызывается на каждом кадре,
происходит очистка буферов глубины и цвета, и в результате мы получаем
глубокий черный цвет :)
На сегодня это все. В следущем уроке мы нарисуем что-нибудь более
приятное глазу с анимацией (а на что нам функция idle() по вашему?).
До встречи!
Файлы урока можно взять здесь.
Назад