Trang

26/12/12

Tổng hợp một số thủ thuật của Irrlicht


SƯU TẦM CÁC THỦ THUẬT CỦA IRRLICH ENGINE

------o0o---------

Ta có quy định một số biến chuẩn như sau :

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

//

Irr::IrrlichtDevice *device; //khởi tạo bằng hàm CreateDevice hay  CreateDeviceEx

//các biến quy định sau

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

stringw caption = L“”; //Dùng để hiện lên thanh tiêu đề của cửa sổ Irrlicht

1)      Hiển thị tốc độ khung hình và driver hiện tại :

        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"driver[";
                                str += driver->getName();
                                str += "] FPS:";
                                str += fps;
                                device->setWindowCaption(str.c_str());
                                lastFPS = fps;
                        }
                }
                else
                        device->yield();
        }

2)      Lấy frameDelta time dùng cho chuyển động cố định bất chấp tốc độ khung hình

        u32 then = device->getTimer()->getTime();
        // This is the movemen speed in units per second.
        const f32 MOVEMENT_SPEED = 5.f;
        while(device->run())
        {
           // Work out a frame delta time.
           const u32 now = device->getTimer()->getTime();
           const f32 frameDeltaTime = (f32)(now - then) / 1000.f;//Time in seconds
           then = now;

 

3)      Cách dùng hàm : getCollisionPoint của class IsceneCollisionManager để tìm kiếm va chạm đối với các SceneNode khác :

Cần có các biến thành phần :

line3df ray;

ITriangleSelector * selector; //cái nào tạo qua hàm CreateTriangleSelector() hay createTriangleSelectorFromBoundingBox() của class IsceneManager

Các biến vector3df thì không cần bàn ( quá dễ)

Chú ý :

const ISceneNode * outNode;

Lúc đó ta dùng hàm này như sau :

Smrg->getSceneCollisionManager()->getCollisionPoint  (ray,  selector, outCollisionPoint, outTriangle,&outNode);

4)      Dùng custom font :

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

5)      Tạo ScreenShot được lưu theo thời gian lưu:

Dùng hàm sau đây :

void takeScreenshot(irr::IrrlichtDevice* device)

{

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

   //get image from the last rendered frame

   irr::video::IImage* const image = driver->createScreenShot();

   if (image) //should always be true, but you never know. ;)

   {

      //construct a filename, consisting of local time and file extension

      irr::c8 filename[64];

      snprintf(filename,64,"screenshot_%u.png",device->getTimer()->getRealTime());

      //write screenshot to file

      if (!driver->writeImageToFile(image, filename))

         device->getLogger()->log(L"Failed to take screenshot.", irr::ELL_WARNING);

      //Don't forget to drop image since we don't need it anymore.

      image->drop();

   }

}

6)      Chuyển chuổi thành vector hay float hay int :

- Chuyển chuổi thành vector

      vector3df getVectorFromString(stringw str)\\

      {

                  vector3df vector;

                  swscanf(str.c_str(), L"%f %f %f", &vector.X,&vector.Y,&vector.Z );

                  return vector;

      }

- Chuyển chuổi thành int

      int getIntegerFromString(stringw str)\\

      {

                  int integer;

                  swscanf(str.c_str(),L"%i",&integer);

                  return integer;

      }

- Chuyển chuổi thành dạng float :

      f32 getFloatFromString(stringw str)\\

      {

                  f32 decimal;

                  swscanf(str.c_str(),L"%f",&decimal);

                  return decimal;

      }

7)      Chuyển Texture thành Image và ngược lại :

- Chuyển texture thành image :

      video::IImage* image = driver->createImageFromData (

                  texture->getColorFormat(),

                  texture->getSize(),

                  texture->lock(),

                  false  //copy mem

                  );         

Còn cách khác ;

- Chuyển image thành texture :

      video::ITexture* texture = driver->addTexture(name.c_str(),image);

      texture->grab();

8)      Duyệt qua tòan bộ các SceneNode từ Node gốc:

core::array nodes;

smgr->getSceneNodesFromType(scene::ESNT_ANY, nodes); // Find all nodes

for (u32 i=0; i < nodes.size(); ++i)

{

     scene::ISceneNode * node = nodes[i];

     [….]

}

            Còn một cách khác cũng hay là :

            core::list::Iterator begin = node->getChildren().begin();

            core::list::Iterator end   = node->getChildren().end();

             for (; begin != end; ++begin)

            {

               scene::ISceneNode * node = nodes[i];

               […..]

            }

9)      Không cho hiện cửa sổ command window :

      #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")

 

 

10)   Hãm tốc độ khung hình theo yêu cầu (không dùng Vsync do có thể có máy đặt Vsnyc khác nhau như 60 hay 65 hay 75…)

#include

#include

using namespace irr;

 

struct IrrlichtDelayFPS {

  int maxFPS;

  int lastFPS;

  int yieldCounter;

  IrrlichtDevice *device;

  IrrlichtDelayFPS() { maxFPS = 50;  yieldCounter = 0;  device = 0; }

  void delay() {

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

    int lastFPS = driver->getFPS();

    for( int i = 0 ; i < yieldCounter ; i++ ) {

      device->yield();

    }

    if ( lastFPS >maxFPS ) { yieldCounter++ ; }

    else {

      if (yieldCounter > 0 ) { --yieldCounter; }

      else { yieldCounter = 0 ; }

    }

  }

};

 

/*  cách dùng

  IrrlichtDelayFPS delayFPS;

  delayFPS.maxFPS = 50; //set max FPS

  delayFPS.device = device; //set device

while(device->run()) {

   //your code here

  //delay loop

  delayFPS.delay();

}

*/

11)   Can thiệp trực tiếp vào dữ liệu của Terrain :

Ta có các biến sau :

IMesh* mesh = terrain->getMesh();

IMeshBuffer* buffer = mesh->getMeshBuffer(0); //chỉ cần mesh buffer 0 là đủ

S3DVertex2TCoords* data = (S3DVertex2TCoords*)buffer-> getVertices();

Giả sử ta có tọa độ vector3df pos(x,y,z) thuộc terrain.

Để hiệu chỉnh đỉnh (vertex) tại vị trí này (ví dụ tăng độ cao đỉnh này lên 10) ta làm như sau :

s32 scale = (s32)terrain->getScale().X ; // chú ý phải scale đầy đủ X, và Z bằng nhau

s32 size = heighmapImage->getDimension().Width; // đây là biến lưu Image của HeightMap.

s32 x = (s32)(pos.X / scale);

s32 z = (s32)(pos.Z / scale);

s32 index = x * size + z;

Và sau củng ta có kết quả hiệu chỉnh như sau :

data[index].pos.Y += 10;

12)   Hàm sinh số ngẫu nhiên tìm ở đâu trong Irrlicht ?

Thật ra có đó. Trong tệp tin os.h có tất cả những cái bạn cần. Mình trích ra như thế này ?

class Randomizer

{

public:

            //! resets the randomizer

            static void reset();

            //! generates a pseudo random number

            static s32 rand();

private:

            static s32 seed;

};

13)   Dùng file nén và có mật khẩu trong Irrlicht :

Khi lập trình thì ai cũng muốn đưa sản phẩm của mình ra, nhưng phần code và resource thì chắc chắn ai cũng muốn dấu rồi (kể cả mình).

Irrlicht đả có hàm để thực hiện chức năng này đó là hàm addFileArchive trong filesystem có tham số như sau:

bool addFileArchive(const path& filename, bool ignoreCase=true,bool ignorePaths=true,E_FILE_ARCHIVE_TYPE archiveType=EFAT_UNKNOWN,

const core::stringc& password="") =0;

      + filename : tên file nén (có thể có password)

      + ignoreCase : bỏ qua chữ hoa hay chữ thường (ví dụ : ant.x vàAnt.X cũng giống nhau)

      + ignorePaths : bỏ qua đường dẫn (bạn chỉ cần cho tên là có thể tìm ra nó trong file nén của mình dù nó nằm trong các thư mục khác nhau. Chú ý đều này)

      + archiveType : Irrlicht hỗ trợ các loại file nén sau : pkzip, gzip,folder ảo, pak,npk,tar.

      + password : mật khẩu của tệp tin nén và nó được mã hóa 256 bit.

* Mình dùng như sau : dùng 7Zip nén toàn bộ resource của mình thành một tệp tin nén (loại zip) có password mà mình định trước và theo chuẩn bzip2 và mã hóa theo theo chuẩn AES-256. Vậy là mình tạm thời có thể yên tâm về resource mình kèm theo game rồi !!

14)  Dùng file âm thanh nén với Audiere :

Mặc định Audiere không chấp nhận file nén, nhưng ta có cách khắc phục như sau :

- // them phan nay de chay file Zip

                  irr::io::IFileSystem* fs = m_smgr->getFileSystem();

                  irr::io::IReadFile* rf = fs->createAndOpenFile(filename);

                  if (!rf)

                  {

                              printf("**Not found audio file: %s\n",filename);

                              return;

                  }         

                  //

                  audiere::OutputStreamPtr au_stream = 0;

                  //

                  if (zip_audio) ////dung file audio nen

                  {

                              core::arraybuffer;                  

                              buffer.set_used(rf->getSize());

                              rf->read(buffer.pointer(),rf->getSize());                    

                              audiere::FilePtr mf = audiere::CreateMemoryFile(buffer.pointer(),rf->getSize());               

                              buffer.clear(); 

                              rf->drop();//close file

                              au_stream = audiere::OpenSound(m_audevice,mf,true,audiere::FF_AUTODETECT);            

                  }

                  else //khong nen Audio

                              au_stream = audiere::OpenSound(m_audevice,filename,true,audiere::FF_AUTODETECT);

15)   Custom SceneNode hay Custom Animator có tham chiếu với SceneNode khác :

Giả sử bạn tạo SceneNode trong đó có tham chiếu đến terrain hay SceneNodeAnimator (dĩ niên là phải dính đến scenenode tham chiếu của nó rồi, nhưng ngoài ra nó còn tham chiếu đến địa hình) thì khi lưu và nạp game bạn sẽ gặp phải vấn đề chết người đó là : nó sẽ báo lổi không biết bạn tham chiếu đến sceneNode nào ? (do lúc đó scenenode đó chưa được nạp).

Cách giải quyết thế nào : thứ nhất bạn phải đặt ID cho SceneNode được tham chiếu đó để SceneNode của bạn biết cách mà tìm nó. Và cái thứ 2 quan trọng hơn là bạn hãy thêm vào biến Init như là giá trị khởi tạo. Biến này chỉ là true khi Node đã được tạo hết (khi đến phần render của Node hay animateNode của SceneNodeAnimator). Như vậy vấn đề được giải quyết xong.

16)   Một số các thủ thuật với GUI :

a)      GUI skin : cần nắm vững nếu muốn tối ưu GUI

-          Lấy Skin hiện thời : Guienv->getSkin()

-          Đặt skin làm skin hiện tại cho GUI : gui->setSkin()

b)      Lấy và đổi màu giao diện : skin->getColor() và skin->setColor() : có thể lấy và đỏi màu hay làm trong suốt một phần hay toàn bộ các GUI hiện tại.

c)      Đổi nội dung văn bản mặc đang hiển thị trong GUI: skin->setDefaultText()

- như đổi tên chữ Yes hay No hay Ok hay Cancel trên MsgBox thành Đúng, sai, Đồng ý, Hủy chọn - > vậy là mình có thể việt hóa hoàn toàn GUI

d)     Thay đổi kích thước mắc định của các phần tử trong GUI : độ lớn của Button, độ lớn của ScrollBar…

e)      Còn rất nhiều phần khác trong GUI mà bạn có thể nghiên cứu trong Skin

17)   Làm cho một node canh chỉnh theo Normal map của địa hình

-          Lấy Normal map của địa hình

-          Normal.normalize(); //Chuẩn hóa nó

-          core::matrix4 trans = character->getRelativeTransformation();//lấy ma trận chuyển vị của Node mà mình muốn quay

-          vector3df oldX(1,0,0); //vị trí đi tới mắc định           

-          trans.rotateVect(oldX); //quay vị trí đi tới thành vị trí hiện thời

-          oldX.normalize(); //chuẩn hóa nó

-          core::vector3df nForward = oldX.crossProduct(Normal); //Lấy vector pháp của vector đang đi tới hiện tại với vector Normal

-          if (nForward.getLength()==0) nForward = vector3df (Normal.Y, Normal.X, Normal.Z) ; //Nếu Normal trùng với hướng di chuyển hiện tại

-          core::vector3df nFRote = nForward.getHorizontalAngle(); //lấy gốc quay quanh trục Y của nForward.

-          character->setRotation(nFRote); //quay Node đi theo vector nFRote vậy là xong.

18)  Quay một Node thành BillBoard :

-  // make billboard look to camera

      core::vector3df pos = getAbsolutePosition();

      core::vector3df campos = camera->getAbsolutePosition();

      core::vector3df target = camera->getTarget();

      core::vector3df up = camera->getUpVector();

      core::vector3df view = target - campos;

      view.normalize();

      core::vector3df horizontal = up.crossProduct(view);

      if ( horizontal.getLength() == 0 )

      {

                  horizontal.set(up.Y,up.X,up.Z);

      }

      horizontal.normalize();

      horizontal *= 0.5f * Size.Width;

      core::vector3df vertical = horizontal.crossProduct(view);

      vertical.normalize();

      vertical *= 0.5f * Size.Height;

      view *= -1.0f;

      for (s32 i=0; i<4 i="i" span="span">

                  vertices[i].Normal = view;

 

      vertices[0].Pos = pos + horizontal + vertical;

      vertices[1].Pos = pos + horizontal - vertical;

      vertices[2].Pos = pos - horizontal - vertical;

      vertices[3].Pos = pos - horizontal + vertical;

19)  Sinh Terrain động từ bộ nhớ :

heightMap= myDriver->createImage(video::ECF_A8R8G8B8,dimension2du(257,257));

heightMap->fill(SColor(0,0,0,0));

ge.Generate_LandScape_A(heightMap,52);

ge.FilterToCloud(0,255,3,heightMap);

ge.Emboss(1,5,heightMap);

//dữ liệu trong Memory

c8* imageData=new c8[heightMap->getImageDataSizeInBytes()];

io::IWriteFile* memWriteFile =myDevice->getFileSystem()->createMemoryWriteFile(imageData,heightMap->getImageDataSizeInBytes(),"tempFile.bmp");

//sao chép qua bộ nhớ

myDriver->writeImageToFile(heightMap,memWriteFile);

//lưu lại trên HD để dùng khi nạp lại

myDriver->writeImageToFile(heightMap,"Map.bmp");

//tạo file truy suất đọc trong Memory

io::IReadFile* rf = myDevice->getFileSystem()->createMemoryReadFile(imageData,heightMap->getImageDataSizeInBytes(),"Map.bmp");

//Tạo Terrain như bình thường

if (terrain!=0) terrain->remove();

terrain = mySmgr->addTerrainSceneNode(rf,0,-1,vector3df(0,0,0),vector3df(0,0,0),vector3df(200,50,200),SColor(255,255,255,255));  

20)   CÁC LƯU Ý KHI CHUYỂN SHADER CODE TỪ RENDER MONKEY SANG IRRLICHT

a)      hệ trục tọa độ khác nhau : trục Y và Z sẽ chuyển thành Z và Y

b)      Phép nhân ma trận : các ví dụ mẫu trong RM thì đúng (khi add default) nhưng các ví dụ còn lại sẳn có thì phép nhân ma trận bị sai :

Out.Pos = mul(view_proj_matrix, Pos);

Sẽ thành :

Out.Pos = mul(Pos , view_proj_matrix);  // trong Irrlicht

c)      Ma trận bị Invert : một số ma trận như ViewMatrix hay ViewInvMatrix khi chuyển qua Irrlicht phải bị Invert trước một lần nữa.

d)     3D Noise Texture :

- Do Irrlicht không hỗ trợ định dạng này (3D Texture) mà chỉ có 2D Texture nên phải dùng công thức chuyển đổi (nhưng phải linh hoạt trong nhiều trường hợp)

- Một lệnh Tex3D trong RM sẽ tương ứng với 2 lệnh Tex2D và một lệnh lerp trong Irrlicht.

e)      CUBEMAP : Irrlicht cũng không hỗ trợ định dạng này, nên ta cũng phải thực hiện chuyển đổi (nhất là khi dùng Reflection từ môi trường) bằng cách dùng Liner View. (Xem ví dụ về Refraction để biết)

f)       Hằng số : các hằng số khai trước đều không có tác dụng trong Irrlicht, nên chuyển các hằng số này vào trong thân hàm.


21)  LƯU Ý KHI SCALE NODE LÀ FILE BSP :

      Một SceneNode được tạo nên từ Mesh bsp (như QUAKE) khi dùng lệnh node->setScale() sẽ bị lổi khi render (bị mất đa giác của meshkhi nhìn ở các hướng khác nhau
      Để khắc phục ta không scale từ sceneNode mà trực tiếp từ mesh bằng hàm scale() trong IMeshManipulator* ( từ smgr->getMeshManipulator()) như vậy là thành công không còn lổi render.
 
22) HIỂN THỊ COMPOUND OBJECT TỪ BULLET SANG IRRLICHT:
Với đối tượng là Compound ta dùng ngay emptySceneNode thay thế nó để hiển thị
Các đối tượng khác là child của nó thì dùng bình thường và nó cũng là child (trong sceneNode của Irrlicht) như vậy thì hiển thị SceneNode dạy diện cho các Object của Bullet dạng Compound mới chính xác (Ví dụ : ForkLiftDemo)
23)


 

 

 

Bài đăng phổ biến