Irrlicht Engine - кроссплатформенный графический 3D движок с открытым исходным кодом. Полностью бесплатен как для коммерческого, так и для некоммерческого использования. Основной автор движка, Nikolaus Gebhardt, разрабатывает его с 2002 года. Движок сочетает в себе универсальность, переносимость и широкую функциональность. Официальный сайт проекта: http://irrlicht.sourceforge.net/
Иррлихт – это кроссплатформенная библиотека. Имеется поддержка большей части операционных систем семейства Windows, а также Linux, MacOS и Solaris. Для успешного рендеринга на всех названных платформах, Иррлихт поддерживает несколько видов графического API: DirectX 8.1, DirectX 9.0, OpenGL (до версии 2.0) и имеет два собственных софтварных рендера.
Краткий список возможностей
Поддержка 8ми форматов текстур и около 2х десятков форматов мешей.
Поддержка пиксельных и вершинных шейдеров до версии 3.0. Поддержка HLSL и GLSL.
Particle systems и Billboards.
Bump и Parallax mapping.
Динамическое освещение и динамические тени.
Light maps.
Туман.
Terrain и water surfaces.
Развитая система GUI, включающая до 2х десятков элементов.
Встроенный XML парсер.
Работа с zip и pak архивами, а также многое другое.
Иррлихт был написан на С++, однако уже имеются порты на .Net и Java. На движке уже выпушено большое количество как коммерческих, так и некоммерческих игр, а также проектов другого типа.
Главное достоинство Иррлихт - простота его интерфейса, а также возможность работы с ним без знания тех разделов математики, которые обычно необходимы при программировании 3D графики. Для работы необходимо скачать SDK с официального сайта. Последняя версия на 25.12.07 - 1.4.
Имеет Иррлихт также и один недостаток: полигоны мешей не хранятся в вершинных буферах в видеопамяти, а каждый раз при отрисовке загружаются в неё по шине из оперативной памяти, поэтому движок не тянет сильно нагруженные сцены вне зависимости от «крутизны» видеокарты. Сгладить этот недостаток можно, например, определяя вручную, какие меши видны данной камере, а остальные делая «невидимыми». Такой способ хорошо подходит к играм с камерой «от третьего лица сверху» (например, рпг и стратегии).
Для начала посмотрим основные моменты работы с Иррлихт. Каждый кусок кода самостоятелен, его можно сразу скомпилировать и посмотреть результат.
/* Начало работы с Irrlicht Engine 1.4
Все подробности есть в справке в папке irrlicht-1.4\doc в SDK
Подключается единственный необходимый хедер */
#include
// irr:: - основное пространство имен, в котором лежит все (!)
using namespace irr;
/* остальные пространства имен.
в ядре лежит математика, контейнеры.
в сцене - все, что связано с 3д сценой.
в гуй - весь гуй
в видео - работа с выводом графики
в ио - файловый ввод-вывод, в том числе XML и работа с файловой системой */
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
// поключаем библиотеку
#pragma comment(lib, "Irrlicht.lib")
/* Очень удобно работать с Ирлихтом вместе с консольным окном,
т.к. туда кидается большое число полезных логов. */
int main()
{
/* Первый шаг - инициализация движка.
Функция вернет 0, если не удалось.
Существует два способа инициализации: простой и расширенный (больше параметров).
Здесь рассматривается простой способ.
Параметры функции:
1 - video::E_DRIVER_TYPE deviceType - АПИ для вывода графики.
Поддерживаются такие: DirectX 9, DirectX 8, OpenGL и два софтварных рендера,
(The Burning's Software Renderer - получше будет)
2 - const core::dimension2d< s32 > & windowSize - размер создаваемого окна.
3 - u32 bits - "битность" цветов в полноэкранном режиме, если он выбран.
4 - bool fullscreen - флаг включения полноэкранного режима
5 - bool stencilbuffer - флаг, который используется, если необходимо рисовать тени в 3д
6 - bool vsync - вертикальная синхронизация (для 3д)
7 - IEventReceiver * receiver - указатель на объектный callback,
в который будут скидываться все сообщения ввода */
IrrlichtDevice *device = createDevice( video::EDT_SOFTWARE, dimension2d(640, 480), 16,
false, false, false, 0);
/* С помощью этой функции устанавливается заголовок окна.
Внимание: все строки, которые выводятся на экран, принимаются движком только в Юникоде */
device->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");
/* Ниже - основные инструменты.
Видео драйвер отвечает непосредственно за отрисовку на экран
Менеджер сцены - за расположение и работу с объектами 3д сцены
Гуй менеджер - за управление элементами гуй */
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* guienv = device->getGUIEnvironment();
/* Простой пример создание гуй-контрола. Такие функции возвращают
указатель на контрол, с помощью которого им можно управлять
Здесь создается Label с текстовой строкой,
в заданном с помощью прямоугольника месте. */
IGUIStaticText* text = guienv->addStaticText(L"Hello World! This is the Irrlicht Software renderer!",
rect(10,10,260,22), true);
/* а здесь задается цвет текста. По умолчанию используется стандартный шрифт, но его можно сменить см. IGUIFont */
text->setOverrideColor(SColor(128,255,0,0));
/* Теперь дело за 3д графикой.
Меш - объект, который хранит информацию о 3д модели. Треугольники, координаты текстур и т.д.
Нод - объект сцены. Существует большое количество типов нодов, данный нод управляет выводом конкретного меша на экран.
Один меш может выводиться по-разному разными нодами. */
IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
/* Установка некоторых параметров нода
1 - Реакция на динамическое освещение (отключена)
2 - Установка проигрываемой анимации. Для мд2 формата есть свои константы,
а вообще она устанавливается по кадрам:
node->setFrameLoop(0,310);
node->setLoopMode(true); - установка зацикленности анимации
setAnimationEndCallback (IAnimationEndCallBack *callback) - эта функция задает объектный callback, который вызывается после окончания анимации в том случае, если она НЕ зациклена.
3 - наложение текстуры на меш, первый параметр обычно всегда равен нулю */
if (node)
{
node->setMaterialFlag(EMF_LIGHTING, false);
node->setMD2Animation ( scene::EMAT_STAND );
node->setMaterialTexture( 0, driver->getTexture("../../media/sydney.bmp") );
}
/* Создание простейшей камеры.
Первый параметр - указание на "родительский" нод, обычно это делают чтобы привязать к нему камеру
Второй - позиция камеры
Третий - точка, куда она смотрит. Эти два параметра могут меняться в процессе, т.к. функция возвращает указатель на камеру */
smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));
// Основной цикл приложения. Функция run() вернет false, если пользователь закроет окно
while(device->run())
{
/* Функция, которая подготавливает сцену для отрисовки, вызывается обязательно. Здесь устанавливаются следующие параметры
1 - очистка заднего фона (третий параметр - цвет очистки)
2 - чистка z-буфера. Необходимо для 3д графики */
driver->beginScene(true, true, SColor(255,100,101,140));
// Рисуем сцену и гуй
smgr->drawAll();
guienv->drawAll();
// конец отрисовки и вывод на экран
driver->endScene();
}
// Все объекты иррлихт унаследованы от IReferenceCounted,
// который считает ссылки и удаляет объект, если на него нет ссылок
// grab() и drop() - "++" и "--" для ссылок
device->drop();
return 0;
}
На рисунке показан скриншот результат работы. Позади него консольное окно с логами, очень полезное при отладке.
В качестве более сложного примера использования библиотеки ниже рассматривается создание просмотрщика уровней Квейка.
#include
#include
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")
int main()
{
// здесь пользователь может выбрать понравившиеся ему АПИ
video::E_DRIVER_TYPE driverType;
printf("Please select the driver you want for this example:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 1;
}
// инициализируется движок
IrrlichtDevice *device =
createDevice(driverType, core::dimension2d(640, 480));
// проверка, все ли в порядке
if (device == 0)
return 1;
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
/* Иррлихт позволяет работать с zip архивами, Данной строчкой он добавляется в "видимость" для ирлихтовского контроллера файловой системы. */
device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");
// Теперь файлы из архива доступны
scene::IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
scene::ISceneNode* node = 0;
/* Здесь создается ОктТри нод. Этот нод будет выводиться на экран с оптимизацией по восьмеричному дереву - это необходимо для больших объектов */
if (mesh)
node = smgr->addOctTreeSceneNode(mesh->getMesh(0), 0, -1, 128);
// Изменяется позицию нода, также можно его вращать и масштабировать:
// SetRotation и SetScale
if (node)
node->setPosition(core::vector3df(-1300,-144,-1249));
/* Этот вид камеры автоматически подключен к клавиатуре (стрелки) и мышке,
с помощью которых ей можно управлять, что очень удобно для тестов */
smgr->addCameraSceneNodeFPS();
/* А тут задаются параметры курсора мыши,
кроме определения его видимости, можно спрашивать и задавать его расположение */
device->getCursorControl()->setVisible(false);
/* В основной цикл, в отличие от прошлого примера, добавлен код отображения
текущего АПИ и ФПС - количество отрендеренных кадров в секунду */
int lastFPS = -1;
while(device->run())
if (device->isWindowActive()) // проверка, активно ли окно
{
driver->beginScene(true, true, video::SColor(0,200,200,200));
smgr->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Irrlicht Engine - Quake 3 Map example [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}
Скриншот выглядит уже почти как фрагмент полноценной игры.
Впрочем, с помощью Иррлихта можно создавать не только игры, но и «обычные» приложения, что и демонстрирует следующий пример.
#include
#include
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#pragma comment(lib, "Irrlicht.lib")
IrrlichtDevice *device = 0;
s32 cnt = 0;
IGUIListBox* listbox = 0;
class MyEventReceiver : public IEventReceiver
{
public:
virtual bool OnEvent(const SEvent& event)
{
// ресивер может также принимать сообщения от собственного гуи
if (event.EventType == EET_GUI_EVENT)
{
// каждому элементу можно и нужно задать свое числовое ид
// здесь мы получаем ид гуй, который среагировал на действия пользователя
s32 id = event.GUIEvent.Caller->getID();
IGUIEnvironment* env = device->getGUIEnvironment();
// можно определить тип события, которое произошло с контролом
switch(event.GUIEvent.EventType)
{
// например был прокручен скроллбар
case EGET_SCROLL_BAR_CHANGED:
if (id == 104)
{
// выясняется позиция бегунка
s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
// и изменяется альфа канал у всех элементов активного скина
// скин отвечает за многие параметры (вообще оформление)
// навешенных на него гуи. Можно создавать разные скины
// и активизировать по мере надобности.
for (u32 i=0; igetSkin()->getColor((EGUI_DEFAULT_COLOR)i);
col.setAlpha(pos);
env->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
}
}
break;
// клик по кнопке
case EGET_BUTTON_CLICKED:
if (id == 101)
{
// таким способом можно заставить device->run() вернуть false
device->closeDevice();
return true;
}
if (id == 102)
{
// Добавить строку в листбокс
listbox->addItem(L"Window created");
cnt += 30;
if (cnt > 200)
cnt = 0;
// создается "дочернее" (не Windows) окошко
IGUIWindow* window = env->addWindow(
rect(100 + cnt, 100 + cnt, 300 + cnt, 200 + cnt),
false, // модальность
L"Test window"); // заголовок
// Label
env->addStaticText(L"Please close me",
rect(35,35,140,50),
true, // border
false, // wordwrap
window);
return true;
}
if (id == 103)
{
listbox->addItem(L"File open");
// Можно пользоваться стандартными диалогами открытия файла
env->addFileOpenDialog(L"Please choose a file.");
return true;
}
break;
default:
break;
}
}
return false;
}
};
int main()
{
video::E_DRIVER_TYPE driverType;
printf("Please select the driver you want for this example:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 1;
}
device = createDevice(driverType, core::dimension2d(640, 480));
if (device == 0)
return 1;
MyEventReceiver receiver;
device->setEventReceiver(&receiver);
device->setWindowCaption(L"Irrlicht Engine - User Interface Demo");
video::IVideoDriver* driver = device->getVideoDriver();
IGUIEnvironment* env = device->getGUIEnvironment();
// а здесь загружается новый шрифт, он должен быть создан с помощью специального инструмента
// FontTool из SDK, и состоит как минимум из xml и одной - нескольких текстур
IGUISkin* skin = env->getSkin();
IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
if (font)
skin->setFont(font);
skin->setFont(env->getBuiltInFont(), EGDF_TOOLTIP);
// Здесь создаются кнопки. Обязательно нужно задавать для кнопок id, чтобы потом ловить их в ресивере
// кнопки могут быть двух видов Pressed и не Pressed,
// не Pressed обладает обычным поведением, как у виндовской кнопки
// Pressed напоминает checkbox, после нажатия она остается в таком состоянии до повторного клика.
// Для кнопок можно устанавливать картинки (2 шт. - нажатое и отжатое состояние)
env->addButton(rect(10,240,110,240 + 32), 0, 101, L"Quit", L"Exits Program");
env->addButton(rect(10,280,110,280 + 32), 0, 102, L"New Window", L"Launches a new Window");
env->addButton(rect(10,320,110,320 + 32), 0, 103, L"File Open", L"Opens a file");
env->addStaticText(L"Transparent Control:", rect(150,20,350,40), true);
// создание скроллбара и установка максимального значения
IGUIScrollBar* scrollbar = env->addScrollBar(true, rect(150, 45, 350, 60), 0, 104);
scrollbar->setMax(255);
// управление скроллбаром
scrollbar->setPos(env->getSkin()->getColor(EGDC_WINDOW).getAlpha());
env->addStaticText(L"Logging ListBox:", rect(50,110,250,130), true);
listbox = env->addListBox(rect(50, 140, 250, 210));
// есть, конечно, и editbox
env->addEditBox(L"Editable Text", rect(350, 80, 550, 100));
env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),position2d(10,10));
while(device->run() && driver)
if (device->isWindowActive())
{
driver->beginScene(true, true, SColor(0,200,200,200));
env->drawAll();
driver->endScene();
}
device->drop();
return 0;
}
Авторские права: весь код, использованный в данной статье, взят из Irrlicht SDK и принадлежит его создателям. Автор только изменил комментарии в коде.