Войти
IrrlichtСтатьи

Перевод примера 005 User Interface(пользовательский интерфейс) и мой вариант перевода примера 002 Quake3Map

Автор:

Один из вариантов перевода 2 примеров из стандартной поставки Иррлихта.

Если заинтересует качество моего перевода могу выложить свой перевод примера №7. Ещё перевёл Example 011 Per-Pixel Lighting, но не до конца отрихтовал на данный момент. В любом случае, можно ещё найти неплохие переводы тут:

Перевод 1 тоториала "Привет мир":
http://irrlicht.at.ua/publ/1-1-0-3

Перевод примера 002 Quake3Map
http://irrlicht.at.ua/publ/1-1-0-7

Ссылку на урок №7 думаю давать излишне, ибо он статьёй тут же.
Почему как статья, а не на форуме? Потому, что в отведённый лимит не умещаются.

Итак:

/** Пример 005 User Interface(юзьверьский интерфейс)

Этот пример покажет как пользоваться встроенным пользовательским интерфейсом
движка Иррлихт. Тут вы получите общее описание и вам будет показано,
как создавать и использовать окна, кнопки, скролл бары(полосы прокруток), статический
текст, и листбоксы.

Как обычно, мы извлечём заголовочные файлы, и используем пространство имён
иррлихта. Мы сохраним указатели на основные устройства иррлихта,
создадим особую переменную для сохранения изменяющейся
позиции созданного окна, и указатель на листбокс.
*/
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#endif

// Объявляем структуру для сохранения контеста 
//event receiver-а(получальщика событий) чтобы
//использовать её для вызова метода OnEvent().
struct SAppContext
{
  IrrlichtDevice *device;
  s32        counter;
  IGUIListBox*  listbox;
};

// Объявим несколько переменных, которые мы используем 
//для идентификации элементов управления GUI.
enum
{
  GUI_ID_QUIT_BUTTON = 101,
  GUI_ID_NEW_WINDOW_BUTTON,
  GUI_ID_FILE_OPEN_BUTTON,
  GUI_ID_TRANSPARENCY_SCROLL_BAR
};

/*
Event Receiver(получальщик событий) не только может получать 
нажатия кнопок на клавиатуре и события от мыши, но так же события 
Графического Пользовательского Интерфейса (GUI). 
Здесь есть почти всё: щелчки кнопок, изменение  выбранного в листбоксе, 
события показывающие, что элемент hovered(выделен, подсвечен(курсор над ним)
и так далее. Чтобы обрести способность реагировать на 
некоторые эти события, мы опишем класс event receiver(получальщик событий).
Далее остаётся только реагировать на события пользовательского интерфейса, 
и если такое событие произошло, мы получаем id(идентификатор) вызвавшего 
событие gui элемента и получаем 
указатель на среду(Environment) gui (т.е. на сам элемент GUI вызвавший событие).
*/
class MyEventReceiver : public IEventReceiver
{
public:
  MyEventReceiver(SAppContext & context) : Context(context) { }

  virtual bool OnEvent(const SEvent& event)
  {
    if (event.EventType == EET_GUI_EVENT)
    {
      s32 id = event.GUIEvent.Caller->getID();
      IGUIEnvironment* env = Context.device->getGUIEnvironment();

      switch(event.GUIEvent.EventType)
      {

      /*
      Если полоса прокрутки зафиксировала изменение положения ползунка, и это
      'наша' полоса прокрутки (только с id равным:
      GUI_ID_TRANSPARENCY_SCROLL_BAR), тогда мы
      установим прозрачность всем GUI элементам. Это очень
      простая задача: Есть текстурированный(skined) объект GUI, 
      в котором сохранены все цветовые установки. Мы просто пройдёмся по всем цветам
      сохраняя изменённую величину их альфа-канала(прозрачности)
      и сохраним новые параметры в шкурке(текстуре) GUI элементов.
      */
      case EGET_SCROLL_BAR_CHANGED:
        if (id == GUI_ID_TRANSPARENCY_SCROLL_BAR)
        {
          s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
          
          for (u32 i=0; i<EGDC_COUNT ; ++i)
          {
            SColor col = env->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);
            col.setAlpha(pos);
            env->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
          }
          
        }
        break;

      /*
      Если кнопка нажата, и это одна из 'наших'
      3 кнопок. То, если она первая - завершаем программу.
      Если это вторая кнопка, мы создаём маленькое окошко с небольшим
      текстом в нём. Мы так же добавляем строку в листбокс чтобы регистрировать
      события GUI. И если это 3 кнопка, мы создаём
      диалог открытия файла, и добавляем так же это событие как строку в листбокс.
      Это всё для event receiver(получальщика событий).
      */
      case EGET_BUTTON_CLICKED:
        switch(id)
        {
        case GUI_ID_QUIT_BUTTON:
          Context.device->closeDevice();
          return true;

        case GUI_ID_NEW_WINDOW_BUTTON:
          {
          Context.listbox->addItem(L"Window created");
          Context.counter += 30;
          if (Context.counter > 200)
            Context.counter = 0;

          IGUIWindow* window = env->addWindow(
            rect<s32>(100 + Context.counter, 100 + Context.counter,
            300 + Context.counter, 200 + Context.counter),
            false, // modal? //модаль блокирует работу с родительским приложением, 
            L"Test window"); //пока пользователь это окно не закроет.

          env->addStaticText(L"Please close me",
            rect<s32>(35,35,140,50),
            true, // border? //рамка?
            false, // wordwrap? //перенос слов?
            window);
          }
          return true;

        case GUI_ID_FILE_OPEN_BUTTON:
          Context.listbox->addItem(L"File open");
          env->addFileOpenDialog(L"Please choose a file.");
          return true;

        default:
          return false;
        }
        break;

      default:
        break;
      }
    }

    return false;
  }

private:
  SAppContext & Context;
};


/*
Ok(Окей), теперь начинается более интересная часть. 
Сперва, создадим устройства иррлихта. Как и в
некоторых прошлых примерах, мы спрашиваем 
пользователя какой драйвер он хочет выбрать 
для этого примера:
*/
int main()
{
  // спросить пользователя про драйвер
  video::E_DRIVER_TYPE driverType=driverChoiceConsole();
  if (driverType==video::EDT_COUNT)
    return 1;

  // создать основное устройство и выйти, если создать его не получилось

  IrrlichtDevice * device = createDevice(driverType, core::dimension2d<u32>(640, 480));

  if (device == 0)
    return 1; // не получилось создать выбранный драйвер.

  /* Создание основного устройство было успешно осуществлено, 
      теперь мы установим event receiver(получальщик событий) и
    сохраним указатель на драйвер и на среду gui . */

  device->setWindowCaption(L"Irrlicht Engine - User Interface Demo");
  device->setResizable(true);

  video::IVideoDriver* driver = device->getVideoDriver();
  IGUIEnvironment* env = device->getGUIEnvironment();

  /*
  Чтобы сделать шрифт немного посимпатичнее, мы загрузим внешний шрифт из файла
  и установим его как новый шрифт по умолчанию для шкурки(skin) GUI объекта.
  Чтобы наш шрифт был основным инструментом вывода, мы установим его
  как встроенный по умолчанию шрифт.
  */

  IGUISkin* skin = env->getSkin();
  IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
  if (font)
    skin->setFont(font);

  skin->setFont(env->getBuiltInFont(), EGDF_TOOLTIP);

  /*
  Мы добавляем 3 кнопки. Первая осуществляет выход из программы. Вторая
  создаёт окно и третья открывает диалог открытия файла. Третий
  параметр это id(идентификатор) кнопки, с помощью которого 
  мы можем легко опознать кнопку в event receiver(получальщике свойств).
  */  

  env->addButton(rect<s32>(10,240,110,240 + 32), 0, GUI_ID_QUIT_BUTTON,
      L"Quit", L"Exits Program");
  env->addButton(rect<s32>(10,280,110,280 + 32), 0, GUI_ID_NEW_WINDOW_BUTTON,
      L"New Window", L"Launches a new Window");
  env->addButton(rect<s32>(10,320,110,320 + 32), 0, GUI_ID_FILE_OPEN_BUTTON,
      L"File Open", L"Opens a file");

  /*
  Теперь, мы добавляем статический текст и скроллбар(полосу прокрутки), 
  которая изменяет
  прозрачность всех gui элементов. Мы установим максимальное значение для
  скроллбара в 255, поскольку это максимальная величина цвета.
  Затем мы создадим статический текст и листбоксе.
  */

  env->addStaticText(L"Transparent Control:", rect<s32>(150,20,350,40), true);
  IGUIScrollBar* scrollbar = env->addScrollBar(true,
      rect<s32>(150, 45, 350, 60), 0, GUI_ID_TRANSPARENCY_SCROLL_BAR);
  scrollbar->setMax(255);

  // установить позицию скроллбара в альфазначение произвольного элемента
  scrollbar->setPos(env->getSkin()->getColor(EGDC_WINDOW).getAlpha());

  env->addStaticText(L"Logging ListBox:", rect<s32>(50,110,250,130), true);
  IGUIListBox * listbox = env->addListBox(rect<s32>(50, 140, 250, 210));
  env->addEditBox(L"Editable Text", rect<s32>(350, 80, 550, 100));

  // Сохранить необходимые данные в контекстной структуре.
  SAppContext context;
  context.device = device;
  context.counter = 0;
  context.listbox = listbox;

  // Затем создаём event receiver(получальщик свойств), 
  //передаём ему эту контекстную структуру.
  MyEventReceiver receiver(context);

  // И прикажем устройству использовать наш event receiver(получальщик свойств).
  device->setEventReceiver(&receiver);


  /*
  И наконец, мы создаём симпатичный логотип движка Иррлихт в верхнем левом углу. 
  */
  env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
      position2d<int>(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;
}

Ну и пример №2:

/** Пример 002 Quake3Map(карта Quake3)

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

Пожалуйста обратите внимание, что вы длжны знать базу движка перед тем как
перейти к данному туториалу. Просто бросте коротки взгляд на первый туториал, 
Если вы этого ещё не сделали это здесь: http://irrlicht.sourceforge.net/tut001.html

Начнём как и в примере "Привет мир": Мы извлекаем заголовки иррлихта
и дополнительные заголовки для обеспечения возможности 
задания пользователем типа драйвера в консоли.
*/
#include <irrlicht.h>
#include <iostream>

/*
Как уже было написано в примере HelloWorld, всё необходимое для работы 
движка Иррлихт можно найти в  пространстве имён 'irr'. Чтобы избавиться от 
конструкции irr:: перед именем каждого класса, мы говорим компилятору, что 
будем сейчас использовать это пространство имён, и теперь не нужно постоянно 
писать 'irr::'. Есть ещё 5 подпространств имён, 
это: 'core', 'scene', 'video', 'io' and 
'gui'. В отличие от примера HelloWorld, мы не объявляем 'using namespace' для
этих 5 подпространств, потому, что вы таким образом увидите в каком 
подпространстве имён могут быть найдены те или иные классы или переменные.
Но если вы находите это более удобным, то можете включить все подпространства
имён, как в прошлом примере.
*/
using namespace irr;

/*
Опять же, для того, чтобы использовать Irrlicht.DLL файл, нам нужно 
слинковать  с проектом Irrlicht.lib. Мы могли бы сделать это в опциях в 
project settings(в установках проекта), но это слишком просто, мы же 
используем директиву препроцессора pragma comment lib, 
так более наглядно:
*/
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

/*
Ok, позвольте начать. Опять же, мы используем 
метод main() для старта, а не WinMain().
*/
int main()
{
  /*
  Как и в примере HelloWorld, мы создаём IrrlichtDevice с помощью
  createDevice(). Разница теперь в том, что мы просим пользователя 
  выбрать какой видео драйвер использовать. Мы делаем это не 
  только для удовольствия, программное обеспечение устройства 
  может быть слишком медленным для  прорисовки огромной карты 
  Quake 3, но можно сделать эту фишку и для удовольствия тоже.
  Вместо того, чтобы копипровать весь этот код в приложение вы 
  можете просто извлеч driverChoice.h из директоии include Irrlicht-а. 
  Функция driverChoiceConsole делает то же самое.
  */

  // спрашиваем пользователя о драйвере

  video::E_DRIVER_TYPE driverType;

  printf("Please select the driver you want for this example:\n"\
    " (a) OpenGL 1.5\n (b) Direct3D 9.0c\n (c) Direct3D 8.1\n"\
    " (d) Burning's Software Renderer\n (e) Software Renderer\n"\
    " (f) NullDevice\n (otherKey) exit\n\n");

  char i;
  std::cin >> i;

  switch(i)
  {
    case 'a': driverType = video::EDT_OPENGL;   break;
    case 'b': driverType = video::EDT_DIRECT3D9;break;
    case 'c': driverType = video::EDT_DIRECT3D8;break;
    case 'd': driverType = video::EDT_BURNINGSVIDEO;break;
    case 'e': driverType = video::EDT_SOFTWARE; break;
    case 'f': driverType = video::EDT_NULL;     break;
    default: return 1;
  }

  // создать устройтсво и выйти, если создать не удалось

  IrrlichtDevice *device =
    createDevice(driverType, core::dimension2d<u32>(640, 480));

  if (device == 0)
    return 1; // не удалось создать выбранный драйвер

  /*
  Получить указатель на видеодрайвер и SceneManager так, чтобы
  нам не нужно было постоянно обращаться к ним так: 
  irr::IrrlichtDevice::getVideoDriver() и так:
  irr::IrrlichtDevice::getSceneManager().
  */
  video::IVideoDriver* driver = device->getVideoDriver();
  scene::ISceneManager* smgr = device->getSceneManager();

  /*
  Для отображения карты Quake 3, мы первым делом должны 
  загрузить её. Карта Quake 3 упакована в .pk3 файлы, которвые 
  являются ничем иным как .zip файлами. Такми образом, мы должны 
  добавить .pk3 файл к нашей  irr::io::IFileSystem. После добавления, 
  мы можем читать файлы из архива как если они сохранены в 
  директории на диске.
  */
  device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");

  /*
  Теперь мы можем загрузить сетку вызвав:
  irr::scene::ISceneManager::getMesh(). Мы получим возвращаемую 
  ссылку на irr::scene::IAnimatedMesh. Как вы вероятно знаете, 
  карта Quake 3 не очень оживлена(не имеет анимации), она является  
  лишь огромным куском статической геометрии с прилагающимися 
  текстурами. Поэтому IAnimatedMesh состоит лишь из 1 
  фрейма(кадра), так что мы получаем лишь "first frame"(первый 
  фрейм) от "анимации", который является нашим уровнем квейка и
  создаём Octree(октановое дерево) узла сцены с этой сеткой карты, 
  используем для всего этого:
  irr::scene::ISceneManager::addOctreeSceneNode().
  Octree optimizes(оптимизация посредством октановых деревьев) 
  немного оптимизирует сцену, пытаясь отрисовывать только ту 
  геометрию, которая видна в текущий момент. Альтернативой Octree 
  может быть: irr::scene::IMeshSceneNode, которые всегда 
  отрисовывают полную геометрию сетки, без оптимизации. 
  Попробуйте сделать так: Используйте:
  irr::scene::ISceneManager::addMeshSceneNode() вместо:
  addOctreeSceneNode() и сравните отрисовку примитивов для видео
  драйвера. (Существует метод:
  irr::video::IVideoDriver::getPrimitiveCountDrawn()
  класса irr::video::IVideoDriver). Заметим, что эта 
  Octree(Октадеревянная) оптимизация полезна только при 
  использовании огромных сеток состоящих из множественной 
  геометрии.
  */
  scene::IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
  scene::ISceneNode* node = 0;

  if (mesh)
    node = smgr->addOctreeSceneNode(mesh->getMesh(0), 0, -1, 1024);
//    node = smgr->addMeshSceneNode(mesh->getMesh(0));

  /*
  Из-за того, что уровень небыл смоделирован чётко вокруг 
  координат центра(0,0,0), мы  немного смещаем уровень. 
  Это может быть сделано для уровня в:
  irr::scene::ISceneNode с использовнаие методов:
  irr::scene::ISceneNode::setPosition() (в этом случае),
  irr::scene::ISceneNode::setRotation(), и
  irr::scene::ISceneNode::setScale().
  */
  if (node)
    node->setPosition(core::vector3df(-1300,-144,-1249));

  /*
  Теперь мы нуждаемся лишь в камере, чтобы осмотреть карту Quake 3.
  Мы желаем создать контролируемую пользователем камеру. В 
  движке иррлихт есть несколько разновидностей камер. Например,
  MayaCamera, которой можно управлять как камерой из Maya:
  Поворот при зажатой левой кнопке мыши, Zoom(приближение) по зажатию 
  обоих кнопок, translate(перемещение) по зажатию правой кнопки мыши. 
  Камера может быть создана с помощью:
  irr::scene::ISceneManager::addCameraSceneNodeMaya(). Но для этого
  примера, мы хотим создать камеру которая ведёт себя как в
  first person shooter games(стрелялках от первого лица) (FPS) и 
  следовательно мы используем:
  irr::scene::ISceneManager::addCameraSceneNodeFPS().
  */
  smgr->addCameraSceneNodeFPS();

  /*
  Курсор мыши при такой камере не нужен, так что мы спрячем его
  irr::IrrlichtDevice::ICursorControl.
  */
  device->getCursorControl()->setVisible(false);

  /*
  Мы сделали всё необходимое, так что осталось только нарисовать всё. 
  Ещё мы отобразим счётчик кадров в секунду(frames per second=FPS) 
  (не путать со стрелялками от первого лица, тоже FPS)
  и отрисуем текст вывода FPS как примитив в заголовке окна.
  Тест на irr::IrrlichtDevice::isWindowActive() (сворачивание окна) не является 
  обязательным, но позволяет движку перехватывать курсор мыши после 
  переключения задач между окнами, после активации других оконных 
  приложений. 
  Вызов: irr::IrrlichtDevice::yield() (yield выход)позволяет избежать 
  зацикливания для всех процессов, когда окно не активно.
  */
  int lastFPS = -1;

  while(device->run())
  {
    if (device->isWindowActive())
    {
      driver->beginScene(true, true, video::SColor(255,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;
      }
    }
    else
      device->yield();
  }

  /*
  В конце, удаляем основное устройство Иррлихта.
  */
  device->drop();
  return 0;
}

/*
Вот и всё. Откомпилируйте и поиграйте с программой.
**/

9 августа 2010