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; } Урок закончен. Приступайте к практическим упражнениям!
|
|
| |