[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Форум » Игровые движки » IrrLicht Engine » Туториал №16: Уровни Quake 3 с поддержкой шейдеров
Туториал №16: Уровни Quake 3 с поддержкой шейдеров
MerGC_TeamДата: Четверг, 03.04.2014, 17:51 | Сообщение # 1
Веселый админ
Группа: Администраторы
Сообщений: 32
Статус: Оффлайн
Если вы уже создали игру или создаете, то использование шейдеров позволит повысить качественно ее графику. В этом туториале вы узнаете как загрузжать уровни из игры Quake 3 в движок раздельно поместить на сцену его обычную геометрию с оптимизацию на скорость отрисовки и шейдерные объекты и как создать управляемую пользователем камеру и как создавать скриншоты в игре.

Начнем как обычно, подключим заголовочный файл IrrLicht и файл с утилитой для запроса видеодрайвера.
Код
#include < irrlicht.h >
#include "driverChoice.h"


Определим несколько значений влияющих на загрузку уровня Quake3
Код
#define IRRLICHT_QUAKE3_ARENA
//#define ORIGINAL_QUAKE3_ARENA
//#define CUSTOM_QUAKE3_ARENA
//#define SHOW_SHADER_NAME

#ifdef ORIGINAL_QUAKE3_ARENA
         #define QUAKE3_STORAGE_FORMAT   addFolderFileArchive
         #define QUAKE3_STORAGE_1                "/baseq3/"
         #ifdef CUSTOM_QUAKE3_ARENA
                 #define QUAKE3_STORAGE_2        "/cf/"
                 #define QUAKE3_MAP_NAME         "maps/cf.bsp"
         #else
                 #define QUAKE3_MAP_NAME                 "maps/q3dm8.bsp"
         #endif
#endif

#ifdef IRRLICHT_QUAKE3_ARENA
         #define QUAKE3_STORAGE_FORMAT   addZipFileArchive
         #define QUAKE3_STORAGE_1        "media/map-20kdm2.pk3"
         #define QUAKE3_MAP_NAME                 "maps/20kdm2.bsp"
#endif

using namespace irr;
using namespace scene;

Класс для создания скриншотов
Код
class CScreenShotFactory : public IEventReceiver
{
public:

         CScreenShotFactory( IrrlichtDevice *device, const c8 * templateName, ISceneNode* node )
                 : Device(device), Number(0), FilenameTemplate(templateName), Node(node)
         {
                 FilenameTemplate.replace ( '/', '_' );
                 FilenameTemplate.replace ( '', '_' );
         }

         bool OnEvent(const SEvent& event)
         {
                 // проверка нажатия F9
                 if ((event.EventType == EET_KEY_INPUT_EVENT) &&
                    event.KeyInput.PressedDown)
                 {
                         if (event.KeyInput.Key == KEY_F9)
                         {
                    video::IImage* image = Device->getVideoDriver()->createScreenShot();
                    if (image)
                    {
                    c8 buf[256];
                    snprintf(buf, 256, "%s_shot%04d.jpg",
                    FilenameTemplate.c_str(),
                    ++Number);
                    Device->getVideoDriver()->writeImageToFile(image, buf, 85 );
                    image->drop();
                    }
                         }
                         else
                         if (event.KeyInput.Key == KEY_F8)
                         {
                    if (Node->isDebugDataVisible())
                    Node->setDebugDataVisible(scene::EDS_OFF);
                    else
                    Node->setDebugDataVisible(scene::EDS_BBOX_ALL);
                         }
                 }
                 return false;
         }

private:
         IrrlichtDevice *Device;
         u32 Number;
         core::stringc FilenameTemplate;
         ISceneNode* Node;
};


Начнем-с...
Код
int IRRCALLCONV main(int argc, char* argv[])
{
         // запрашиваем видеодрайвер (OpenGL, DirectX и т.д.)
         video::E_DRIVER_TYPE driverType=driverChoiceConsole();
         if (driverType==video::EDT_COUNT) return 1; // выходим если не выбран  
          
         // создаем корневой объект(движок)
         const core::dimension2du videoDim(800,600);
         IrrlichtDevice *device = createDevice(driverType, videoDim, 32, false );
         if (device == 0) return 1; // выходим если не создан.

         const char* mapname=0;
         if (argc>2)
                 mapname = argv[2];
         else
                 mapname = QUAKE3_MAP_NAME;


Указатели на видеодрайвер и т.п. чтобы не "просить" их у движка а-ля device->getVideoDriver().
Код
video::IVideoDriver* driver = device->getVideoDriver();
         scene::ISceneManager* smgr = device->getSceneManager();
         gui::IGUIEnvironment* gui = device->getGUIEnvironment();

         device->getFileSystem()->addFolderFileArchive("media/");


Подготовимся к загрузке уровня Quake 3, который упакован в файл .pk3, который в свою очередь, не что иное а .zip файл. Мы просто добавим этот архив к нашей виртуальной файловой системе FileSystem, а затем запросим файл уровня, как обычный файл с диска, клево!? Ну так! .
Код
if (argc>2)
                 device->getFileSystem()->QUAKE3_STORAGE_FORMAT(argv[1]);
         else
                 device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_1);
#ifdef QUAKE3_STORAGE_2
         device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_2);
#endif

         // Шейдеры Quake3 управляются методом Z-Writing
         smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);


Загружаем модель уровня вызовом getMesh(). В ответ мы получим указатель на IAnimatedMesh. Как известно, уровни Quake 3 не содержат анимаций и представляют собой большую кучу статической геометрии с наложенными на нее материалами. Посколько IAnimatedMesh будет содержать только один единственный фрейм, то мы заберем его и добавим на сцену с помощью addOctreeSceneNode(). Узел(нода) Octree оптимизированна для отрисовки многополигональных моделей и рисует только попадающую в створ камеры геометрию. Альтернанивой Octree может служить AnimatedMeshSceneNode, узел(нода), которая рисуется целиком без оптимизаций. Можете попробовать метод addAnimatedMeshSceneNode вместо addOctreeSceneNode и сравнить количество рисуемой геометрии с помощью метода IVideoDriver::getPrimitiveCountDrawed(), а так же как замена повлияла на быстродействие.
Код
scene::IQ3LevelMesh* const mesh =
                 (scene::IQ3LevelMesh*) smgr->getMesh(mapname);


Значит добавляем узел Octree с уровнем quake3 на сцену.


Код
scene::ISceneNode* node = 0;
         if (mesh)
         {
                 scene::IMesh * const geometry = mesh->getMesh(quake3::E_Q3_MESH_GEOMETRY);
                 node = smgr->addOctreeSceneNode(geometry, 0, -1, 4096);
         }

         // создаем обработчик событий класса для создания скриншотов
         CScreenShotFactory screenshotFactory(device, mapname, node);
         device->setEventReceiver(&screenshotFactory);


Теперь сконструируем узлы для каждого шейдерного объекта уровня, они хранятся в модели уровня помещенные как scene::E_Q3_MESH_ITEMS и ID шейдера хранится в MaterialParameters, в основном эти объекты такие как черепа, лава, зеленые мерцающие трубы
Код
if ( mesh )
         {
                 // набор геометрии уровня предназначенный для шейдерирования  
                 const scene::IMesh * const additional_mesh = mesh->getMesh(quake3::E_Q3_MESH_ITEMS);

#ifdef SHOW_SHADER_NAME
                 gui::IGUIFont *font = device->getGUIEnvironment()->getFont("media/fontlucida.png");
                 u32 count = 0;
#endif

                 for ( u32 i = 0; i!= additional_mesh->getMeshBufferCount(); ++i )
                 {
                         const IMeshBuffer* meshBuffer = additional_mesh->getMeshBuffer(i);
                         const video::SMaterial& material = meshBuffer->getMaterial();

                         // ShaderIndex хранится в параметрах материала
                         const s32 shaderIndex = (s32) material.MaterialTypeParam2;

                         // пропускаем объект если набор вершин не содержит шейдеров
                         const quake3::IShader *shader = mesh->getShader(shaderIndex);
                         if (0 == shader)
                         {
                    continue;
                         }

                         // мы можем вывести дамп шейдеров в консоль целиком
                         // чем переполним консоль, в этом возможно появится  
                         // необходимость при отладке, даелается это методом  
                         // quake3::dumpShader ( Shader );

                         node = smgr->addQuake3SceneNode(meshBuffer, shader);

#ifdef SHOW_SHADER_NAME
                         // при опции SHOW_SHADER_NAME кажем имя шейдера над его объектом
                         count += 1;
                         core::stringw name( node->getName() );
                         node = smgr->addBillboardTextSceneNode(
                    font, name.c_str(), node,
                    core::dimension2d< f32 >(80.0f, 8.0f),
                    core::vector3df(0, 10, 0));
#endif
                 }
         }


Добавим FPS камеру для возможности активно обзревать уровень как в стрелялках от первого лица. Вообще, в IrrLicht есть несколько камер. К примеру камера Maya, которая вращается мышью с прижатой левой кнопкой и маштабирутеся с прижатыми левой и правой, а двигается с прижатой правой. Добавляется на сцену такая методом addCameraSceneNodeMaya().
Код
scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();


положение камеры выберем следующим образом: найдем все объекты уровня, помеченные как точки старта(респауна), т.е. "info_player_deathmatch" и случайным образом выберем одну из них.
Код
if ( mesh )
         {
                 quake3::tQ3EntityList &entityList = mesh->getEntityList();

                 quake3::IEntity search;
                 search.name = "info_player_deathmatch";

                 s32 index = entityList.binary_search(search);
                 if (index >= 0)
                 {
                         s32 notEndList;
                         do
                         {
                    const quake3::SVarGroup *group = entityList[index].getGroup(1);

                    u32 parsepos = 0;
                    const core::vector3df pos =
                    quake3::getAsVector3df(group->get("origin"), parsepos);

                    parsepos = 0;
                    const f32 angle = quake3::getAsFloat(group->get("angle"), parsepos);

                    core::vector3df target(0.f, 0.f, 1.f);
                    target.rotateXZBy(angle);

                    camera->setPosition(pos);
                    camera->setTarget(pos + target);

                    ++index;
                    notEndList = index == 2;
                         } while ( notEndList );
                 }
         }


Прячем указатель мыши, ну и по мелочи...
Код
device->getCursorControl()->setVisible(false);

         // грузим логотип движка
         gui->addImage(driver->getTexture("irrlichtlogo2.png"),
                         core::position2d< s32 >(10, 10));

         // позиция рисования логотипа
         const core::position2di pos(videoDim.Width - 128, videoDim.Height - 64);

         switch ( driverType )
         {
                 case video::EDT_BURNINGSVIDEO:
                         gui->addImage(driver->getTexture("burninglogo.png"), pos);
                         break;
                 case video::EDT_OPENGL:
                         gui->addImage(driver->getTexture("opengllogo.png"), pos);
                         break;
                 case video::EDT_DIRECT3D8:
                 case video::EDT_DIRECT3D9:
                         gui->addImage(driver->getTexture("directxlogo.png"), pos);
                         break;
         }


Приступаем к рисованию, попутно будем выводит статистику в заголовок окна (количество рисуемых примитивов). Строка 'if (device->isWindowActive())' опциональна и всего лишь позволяет приостановить рендеринг, если оно приложения не активно, т.е. вы его свернули или переключились на другое окно.
Код
int lastFPS = -1;

         while(device->run())
         if (device->isWindowActive())
         {
                 driver->beginScene(true, true, video::SColor(255,20,20,40));
                 smgr->drawAll();
                 gui->drawAll();
                 driver->endScene();

                 int fps = driver->getFPS();
                 //if (lastFPS != fps)
                 {
                         io::IAttributes * const attr = smgr->getParameters();
                         core::stringw str = L"Q3 [";
                         str += driver->getName();
                         str += "] FPS:";
                         str += fps;
                         str += " Cull:";
                         str += attr->getAttributeAsInt("calls");
                         str += "/";
                         str += attr->getAttributeAsInt("culled");
                         str += " Draw: ";
                         str += attr->getAttributeAsInt("drawn_solid");
                         str += "/";
                         str += attr->getAttributeAsInt("drawn_transparent");
                         str += "/";
                         str += attr->getAttributeAsInt("drawn_transparent_effect");

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


По окончанию работы, дропаем движок.
Код
device->drop();

         return 0;
}

Урок закончен. Приступайте к практическим упражнениям!
 
Форум » Игровые движки » IrrLicht Engine » Туториал №16: Уровни Quake 3 с поддержкой шейдеров
  • Страница 1 из 1
  • 1
Поиск: