天天看點

Irrlicht學習備忘錄——16 Quake3MapShader

16 Quake3MapShader

    官方代碼($sdk)\examples\16.Quake3MapShader

Irrlicht學習備忘錄——16 Quake3MapShader

    這個例子很像第2個官方例子,使用的場景模型也跟那例子用的。唯一的差別是,這個例子中增加了對Quake3壓縮地圖的場景進行更進階點的渲染,使它看起來比第2個例子更好。下面看具體代碼。

#include <irrlicht.h>

#include "driverChoice.h"

//下面的宏用來定義Quake3允許載入的級别

#define IRRLICHT_QUAKE3_ARENA

//#define ORIGINAL_QUAKE3_ARENA

//#define CUSTOM_QUAKE3_ARENA

//#define SHOW_SHADER_NAME

//共有2個級别,例子裡預設使用的是IRRLICHT_QUAKE3_ARENA,它是irr的Quake3渲染級别。另外一個是Quake3競技場原始效果級别ORIGINAL_QUAKE3_ARENA。使用Quake3競技場原始效果級别時,可以開啟使用者自定義效果CUSTOM_QUAKE3_ARENA宏。通過注釋和取消注釋來編譯,可以看到不同級别的渲染效果。SHOW_SHADER_NAME宏用來開啟關閉顯示場景中用到的quake3 shader的名字。

//如果定義了ORIGINAL_QUAKE3_ARENA

#ifdef ORIGINAL_QUAKE3_ARENA

    //定義QUAKE3_STORAGE_FORMAT宏

   #define QUAKE3_STORAGE_FORMAT   addFolderFileArchive

    //定義QUAKE3_STORAGE_1宏

   #define QUAKE3_STORAGE_1   "/baseq3/"

    //如果定義了CUSTOM_QUAKE3_ARENA

   #ifdef CUSTOM_QUAKE3_ARENA

       //定義QUAKE3_STORAGE_2宏

       #define QUAKE3_STORAGE_2   "/cf/"

       //定義QUAKE3_MAP_NAME宏

       #define QUAKE3_MAP_NAME   "maps/cf.bsp"

   #else

    //定義QUAKE3_MAP_NAME宏

   #define QUAKE3_MAP_NAME   "maps/q3dm8.bsp"

   #endif

#endif

//如果定義了IRRLICHT_QUAKE3_ARENA宏

#ifdef IRRLICHT_QUAKE3_ARENA

    //定義QUAKE3_STORAGE_FORMAT宏

   #define QUAKE3_STORAGE_FORMAT   addFileArchive

    //定義QUAKE3_STORAGE_1宏

   #define QUAKE3_STORAGE_1   "../../media/map-20kdm2.pk3"

    //定義QUAKE3_MAP_NAME宏

   #define QUAKE3_MAP_NAME   "maps/20kdm2.bsp"

#endif

using namespace irr;

using namespace scene;

#ifdef _MSC_VER

#pragma comment(lib,"Irrlicht.lib")

#endif

//截屏類。這個類繼承IEventReceiver事件接收接口,用來檢查使用者操作按鍵情況,在按下F9鍵時進行螢幕截屏。

class CScreenShotFactory : publicIEventReceiver

{

public:

   //device irr裝置、templateName截屏臨時名字、node場景節點(本例中沒用到)

   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)

    {

       //檢查是否是鍵盤按下事件

       if ((event.EventType == EET_KEY_INPUT_EVENT) && event.KeyInput.PressedDown)

       {

           //F9鍵按下,截屏并儲存為jpg檔案

           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();

               }

           }

           //按下F8鍵,打開或關閉Debug資料顯示

           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;

    u32Number;

   core::stringc FilenameTemplate;

   ISceneNode* Node;

};

int IRRCALLCONV main(int argc, char* argv[])

{

    //選擇使用的irr裝置

   video::E_DRIVER_TYPE driverType=driverChoiceConsole();

    if(driverType==video::EDT_COUNT)

       return 1;

    //建立irr裝置,失敗則退出程式

   const core::dimension2du videoDim(800,600);

   IrrlichtDevice *device = createDevice(driverType, videoDim, 32, false );

    if(device == 0)

       return 1;

    //如果有指令行參數提供的地圖檔案名,就用它做場景。否則就用預設的QUAKE3_MAP_NAME檔案名。

   const char* mapname=0;

    if(argc>2)

       mapname = argv[2];

   else

       mapname = QUAKE3_MAP_NAME;

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

   scene::ISceneManager* smgr = device->getSceneManager();

   gui::IGUIEnvironment* gui = device->getGUIEnvironment();

    //添加自己的私有資源檔案工作目錄

   device->getFileSystem()->addFileArchive("../../media/");

    //為了顯示QUAKE3的地圖,首先需要讀取它。Quake3地圖被打包在.pk3檔案中,是以irr的檔案系統需要加載.pk3封包件,在加載它之後,還需要從封包件中對其進行讀取。

    //這裡QUAKE3_STORAGE_FORMAT宏,根據前面的顯示級别定義,被設成了函數名addFolderFileArchive或addFileArchive。

    //根據有無指令行參數設定地圖工作檔案夾或壓縮包

    if(argc>2)

       device->getFileSystem()->QUAKE3_STORAGE_FORMAT(argv[1]);

   else

       device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_1);

    //如果設定了QUAKE3_STORAGE_2宏,就加載相關的檔案(對QUAKE3地圖格式為也不清楚,從代碼隻能知道,加載了QUAKE3_STORAGE_2級别的模型,畫質會提高,但對渲染的要求也有提高)

   #ifdef QUAKE3_STORAGE_2

       device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_2);

   #endif

    //設定允許Quake3着色器控制ZWRITE

   smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT,true);

   scene::IQ3LevelMesh* const mesh =(scene::IQ3LevelMesh*)smgr->getMesh(mapname);

    //更改Mesh的結構類型,以擷取更快的渲染速度。添加幾何網格到場景。幾何網格是被優化過的,能有更快的繪制速度。

   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);

    //現在,為每個場景節點建立自己的Shader。這些Shaders目标存儲在QuakeMesh場景quake3::E_Q3_MESH_ITEMS中,Shaders的ID存儲在材質參數中。大多數看起來很暗,像骨頭、移動的岩漿和閃爍的燭光。

    if( mesh )

    {

       //這些額外的Mesh會非常龐大

       const scene::IMesh * const additional_mesh =mesh->getMesh(quake3::E_Q3_MESH_ITEMS);

       //如果定義了SHOW_SHADER_NAME宏,設定字型

       #ifdef SHOW_SHADER_NAME

       gui::IGUIFont *font =device->getGUIEnvironment()->getFont("../../media/fontlucida.png");

    #endif

       u32 count = 0;     

       for ( u32 i = 0; i!= additional_mesh->getMeshBufferCount(); ++i )

       {

           const IMeshBuffer* meshBuffer = additional_mesh->getMeshBuffer(i);

           const video::SMaterial& material = meshBuffer->getMaterial();

           //Shaders索引值儲存在材質參數中

           const s32 shaderIndex = (s32) material.MaterialTypeParam2;

           //普通的附加Mesh可以不需要額外支援的進行渲染,但是火焰Shader不行,它需要特殊支援

           const quake3::IShader *shader = mesh->getShader(shaderIndex);

           if (0 == shader)

           {

                continue;

           }

           //通過管理器對每個MeshBuffer提供一個正确的Shader,将Shader綁定入場景Mesh

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

           //如果定義了SHOW_SHADER_NAME宏,為場景節點添加一個文章告示闆顯示shader名。

           #ifdef 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

       }

    }

   scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();

    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);

    //加載一個Irr引擎LOGO

   gui->addImage(driver->getTexture("irrlichtlogo2.png"),core::position2d<s32>(10,10));

    //根據裝置不同添加不同的裝置Logo

   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;

    }

    intlastFPS = -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;

           #ifdef _IRR_SCENEMANAGER_DEBUG          

                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");

           #endif

           device->setWindowCaption(str.c_str());

            lastFPS = fps;

       }

    }

    //最後,删除渲染裝置

   device->drop();

   return 0;

}

    這個例子中,使用了很多quake3專有的東西,在引擎中這部分增加了quake3名字空間。對其它格式的場景模型不适用。我找了很多工具,專門的quake3模組化工具沒找到,隻找到常見的3d max和maya的quake3模型導出插件。沒法弄清到底是哪些東西導出後,能成為這例子中用得到的東西,隻好不詳細研究這例子了。沒搞遊戲的專業人員指點,隻靠自己網上搜資料,好多東西要弄清楚是有困難的。

繼續閱讀