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

Перевод Example 012 Terrain Rendering(Рендеринг Территории) + мой тутор как генерировать карты высот

Автор:

Тутор на тему "как создать простую сцену".

По прочтению перевода у вас может возникнуть 2 вопроса.
1. Где взять текстуры для скибокса?
2. Где взять генератор Heightmap"(карт высот) которая является всего лишь gray scale(градиентом серого)".

Текстуры для скибокса можно сделать самому посредством фотошопа или 3D Studio Max-а, а можно взять с сайта, подобного этому:
http://www.alusion-fr.com/fr1ffa.htm

Как генерировать карты высот?
Идём сюда и смотрим:
http://gcup.ru/forum/58-4206-1
Качаем L3DT по ссылке.
То, что выделено зелёным выполняем до шага 9, 9 шаг нам не нужен.
Единственное примечание, чтобы "гористость" была близка тому как в примере Иррлихта на шаге 4 выбираем гористость повыше. Остальное по вкусу. Итак, карта готова. Нам из этого понадобятся 2 вещи:
а) Карта высот. В L3DT она называется Heightfield. Перед тем как экспортировать её надо сжать, изначально она имеет размер 1024х1024. А нам надо как в примере Иррлихта 256х256, так и гористость будет та, что надо. А если брать в оригинальном размере, то плосковато получится. Operations->Heightfield->Resize Heightfield->256х256. Далее, после сжатия, выбираем закладку Heightfield. Ждём пока загрузится. Нажимаем правой кнопкой на той же вкладке Heightfield->Export layer. Выбираем формат Heightmap файла, я выбрал *.jpg. Имя файла по вкусу и место куда его писать. Ok. Готово.
б) Карта текстур. В L3DT она называется Texture Map. Я её взял в том размере, в котором она и была: 1024x1024. Аналогично пункту "а". Выделить закладку, подождать когда загрузится, правой кнопкой->Export layer...

Ах, да. И ещё один важный момент. Чтобы скибокс отображался корректно главный девайс иррлихта надо создавать как в этом примере(012 Terrain Rendering). Если его создать так, как написано в начальных туториалах скибокс может отображаться некорректно. Т.е. создавать надо примерно так:

  irr::SIrrlichtCreationParameters params;
  params.DriverType=driverType;
  params.WindowSize=core::dimension2d<u32>(640, 480);
  IrrlichtDevice* device = createDeviceEx(params);

Далее.
Встраиваем карту высот в Иррлихт, надеваем текстуру. Детализирующую текстуру можно оставить Иррлихтовскую - результат (у меня по крайней мере) столь же замечательный.

Встраиваем свой скибокс. Масштабируем террэин по вкусу. Наслаждаемся результатом...

Что? Вы не знаете как встраивать в Иррлихт всё это, как масштабировать террэин?

Тогда читайте:

/** Example 012 Terrain Rendering(Пример 012 Рендеринг Территории)

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

Обратите внимание Terrain Renderer(рендерринг территории) в Иррлихт 
основывается на Spintz' GeoMipMapSceneNode, очень благодарен ему. 
DeusXL представил новое элегантное и простое решение для построения
больших площадей на небольших heightmaps -> terrain smoothing
(heightmaps = карты высот, terrain smoothing = сглаживание территории).

Поначалу здесь нет ничего особенного. Мы извлекаем необходимые 
файлы заголовков и создаём список событий, чтобы фиксировать 
нажатия пользователем клавиш: клавиша 'W' для переключения в режим 
каркаса, клавиша 'P' переключает в режим pointcloud(облако точек), 
и клавиша 'D' переключает между режимами твёрдого и 
детализированного отображения материала.

(при переводе использовалась информация:
http://ru.wikipedia.org/wiki/LOD)
*/
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

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


class MyEventReceiver : public IEventReceiver
{
public:

  MyEventReceiver(scene::ISceneNode* terrain, scene::ISceneNode* skybox, scene::ISceneNode* skydome) :
    Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true)
  {
    Skybox->setVisible(true);
    Skydome->setVisible(false);
  }

  bool OnEvent(const SEvent& event)
  {
    // проверить нажал ли пользователь искомые клавиши
    if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
    {
      switch (event.KeyInput.Key)
      {
      case irr::KEY_KEY_W: // Перекоючатель в каркасный режим
        Terrain->setMaterialFlag(video::EMF_WIREFRAME,
            !Terrain->getMaterial(0).Wireframe);
        Terrain->setMaterialFlag(video::EMF_POINTCLOUD, false);
        return true;
      case irr::KEY_KEY_P: // Перекоючатель в "облако точек"
        Terrain->setMaterialFlag(video::EMF_POINTCLOUD,
            !Terrain->getMaterial(0).PointCloud);
        Terrain->setMaterialFlag(video::EMF_WIREFRAME, false);
        return true;
      case irr::KEY_KEY_D: // режимы детализации карты
        Terrain->setMaterialType(
          Terrain->getMaterial(0).MaterialType == video::EMT_SOLID ?
          video::EMT_DETAIL_MAP : video::EMT_SOLID);
        return true;
      case irr::KEY_KEY_S: // переключить небо
        showBox=!showBox;
        Skybox->setVisible(showBox);
        Skydome->setVisible(!showBox);
        return true;
      default:
        break;
      }
    }

    return false;
  }

private:
  scene::ISceneNode* Terrain;
  scene::ISceneNode* Skybox;
  scene::ISceneNode* Skydome;
  bool showBox;
};


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

  //создать устройство с полной гибкостью над заданием параметров
  //вы можете добавить больше параметров если пожелаете, используя:
  //irr::SIrrlichtCreationParameters
  irr::SIrrlichtCreationParameters params;
  params.DriverType=driverType;
  params.WindowSize=core::dimension2d<u32>(640, 480);
  IrrlichtDevice* device = createDeviceEx(params);

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

  
  /*
  Перавым делом, мы добавляем стандартьные элементы сцены: 
  Симпатичный логотип движка иррлихт, маленький текст справки,
  контролируемую пользователем камеру, и мы запрещаем 
  курсор мыши.
  */

  video::IVideoDriver* driver = device->getVideoDriver();
  scene::ISceneManager* smgr = device->getSceneManager();
  gui::IGUIEnvironment* env = device->getGUIEnvironment();

  driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);

  //добавить логотип иррлихта
  env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
    core::position2d<s32>(10,10));

  //установить другой шрифт
  env->getSkin()->setFont(env->getFont("../../media/fontlucida.png"));

  //добавить текст справки
  env->addStaticText(
    L"Press 'W' to change wireframe mode\nPress 'D' to toggle detail map\nPress 'S' to toggle skybox/skydome",
    core::rect<s32>(10,421,250,475), true, true, 0, -1, true);

  //добавить камеру
  scene::ICameraSceneNode* camera =
    smgr->addCameraSceneNodeFPS(0,100.0f,1.2f);

  camera->setPosition(core::vector3df(2700*2,255*2,2600*2));
  camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
  camera->setFarValue(42000.0f);

  //запретить курсор мыши
  device->getCursorControl()->setVisible(false);

  /*
  А вот и узел сцены визуализации территории: Мы добавляем его как
  и любой другой узел сцены с помощью
  ISceneManager::addTerrainSceneNode(). 
  Единственный параметр, который мы используем, - это
  имя файла heightmap(карты высот), которую потребно использовать. 
  Heightmap(карта высот) является всего лишь gray scale(градиент серого)
  текстурой. Terrain renderer(рендеринг территорий) загружает её и создаёт
  3D terrain(трёхмерную территорию) из неё.

  Для того чтобы сделать территорию выгдядщей побольше, мы изменим 
  коэффициент масштабирования на (40, 4.4, 40). Так как у нас нет 
  динамического освещения в сцене мы, отключаем освещение, и 
  устанавливаем terrain-texture.jpg как текстуру для территории и 
  detailmap3.jpg как вторичную текстуру, делающую карту более 
  детальной(detail map). И наконец, мы устанавливаем  коэффициент 
  масштабирования для текстуры: Первая текстура будет повторяться 
  только 1 раз(растянута на всю территорию), а вторая(detail map) 20 раз.
  */

  //добавить узел сцены территории
  scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
    "../../media/terrain-heightmap.bmp",
    0,        //parent node(корневой узел)
    -1,        //node id(идентификатор узла)
    core::vector3df(0.f, 0.f, 0.f),    //position(позиция)
    core::vector3df(0.f, 0.f, 0.f),    //rotation(поворот, ротация)
    core::vector3df(40.f, 4.4f, 40.f),  //scale(масштабирование)
    video::SColor ( 255, 255, 255, 255 ),  //vertexColor(цвет вершин)
    5,        //maxLOD (максимальный уровень детализации)
    scene::ETPS_17,      //patchSize(размер куска карты?)
    4        //smoothFactor(фактор сглаживания)
    );

  terrain->setMaterialFlag(video::EMF_LIGHTING, false);

  terrain->setMaterialTexture(0,
      driver->getTexture("../../media/terrain-texture.jpg"));
  terrain->setMaterialTexture(1,
      driver->getTexture("../../media/detailmap3.jpg"));
  
  terrain->setMaterialType(video::EMT_DETAIL_MAP);

  terrain->scaleTexture(1.0f, 20.0f);
  //terrain->setDebugDataVisible ( true );

  /*
  Для того, чтобы фиксировать столкновения с территорией, мы 
  создадим селектор треугольников.
  Если  вы хотите знать, что делают селекторы треугольников, просто 
  посмотрите туториал по столкновениям. Terrain triangle 
  selector(Территориальный селектор треугольников) работает совместно 
  с территорией. Для демонстрации этого, мы создадим аниматор 
  реакции на столкновения и прикрепим его к камере, так что камера не сможет 
  пролетать сквозь землю.
  */

  //создать селектор треугольников для территории  
  scene::ITriangleSelector* selector
    = smgr->createTerrainTriangleSelector(terrain, 0);
  terrain->setTriangleSelector(selector);

  //создать аниматор реакции на столкновение и присоединить его к камере
  scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
    selector, camera, core::vector3df(60,100,60),
    core::vector3df(0,0,0),
    core::vector3df(0,50,0));
  selector->drop();
  camera->addAnimator(anim);
  anim->drop();

  /* Если вы нуждаетесь в доступе к данным территории 
  вы можете сделать это прямо с помощью следующего 
  фрагмента кода.
  */
  scene::CDynamicMeshBuffer* buffer = new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
  terrain->getMeshBufferForLOD(*buffer, 0);
  video::S3DVertex2TCoords* data = (video::S3DVertex2TCoords*)buffer->getVertexBuffer().getData();
  //Работайте с переменной data или получите IndexBuffer аналогичным способом.
  buffer->drop(); //Когда работа выполнена освободить буфер.

  /*
  Для того, чтобы сделать, чтобы пользователь был в состоянии переключаться 
  между нормаьным и каркасным режимом, мы создадим event 
  reciever(получальщик событий) и позволим Иррлихту узнать об этом. 
  В добавок, мы добавим skybox(звёздный ящик) который мы уже использовали 
  во многих примерах Иррлихта и skydome(звёздный купол), который
  видим взаимноисключающе с skybox. Для переключения со skybox на
  skydome надо нажать 'S'.
  */

  //создать skybox и skydome
  driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

  scene::ISceneNode* skybox=smgr->addSkyBoxSceneNode(
    driver->getTexture("../../media/irrlicht2_up.jpg"),
    driver->getTexture("../../media/irrlicht2_dn.jpg"),
    driver->getTexture("../../media/irrlicht2_lf.jpg"),
    driver->getTexture("../../media/irrlicht2_rt.jpg"),
    driver->getTexture("../../media/irrlicht2_ft.jpg"),
    driver->getTexture("../../media/irrlicht2_bk.jpg"));
  scene::ISceneNode* skydome=smgr->addSkyDomeSceneNode(
  driver->getTexture("../../media/skydome.jpg"),16,8,0.95f,2.0f);

  driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);

  //создать event receiver(получальщиек событий)
  MyEventReceiver receiver(terrain, skybox, skydome);
  device->setEventReceiver(&receiver);

  /*
  Вот и всё, отрисовываем всё.
  */

  int lastFPS = -1;

  while(device->run())
  if (device->isWindowActive())
  {
    driver->beginScene(true, true, 0 );

    smgr->drawAll();
    env->drawAll();

    driver->endScene();

    //Отобразить кадры в секунду(FPS) в заголовке окна
    int fps = driver->getFPS();
    if (lastFPS != fps)
    {
      core::stringw str = L"Terrain Renderer - Irrlicht Engine [";
      str += driver->getName();
      str += "] FPS:";
      str += fps;
      //Кроме того напечатаем высоту территории согласно текущей позиции камеры
      //Мы можем использовать позицию камеры потому,
      //что территория находится в начале координат
      str += " Height: ";
      str += terrain->getHeight(camera->getAbsolutePosition().X,
          camera->getAbsolutePosition().Z);

      device->setWindowCaption(str.c_str());
      lastFPS = fps;
    }
  }

  device->drop();
  
  return 0;
}

/*
Теперь вы знаете, как использовать территории в Иррлихте.
**/

12 августа 2010