2011年6月21日 星期二

Irrlicht Collision Performance Tuning


Dear All:
這陣子因為工作上的需要開始看Irrlicht這套Open Source的Engine,就使用上來說,真的不得不佩服它直覺與乾淨的API介面,比起OGRE你會感覺OGRE是Ogre。當然Irrlicht也不是沒缺點,最嚴重就是Animation 、Collision與Rendering這三個系統。首先,Animation沒有GPU的Skin mesh,一切交由CPU、Collision System效能過慢,而Rendering 架構上沒特別為Shader做設計,還停留在Fixed Render Pipeline時代。
OK!今天要記錄的Tuning在Collision System,重點如下:
問題:
Irrlicht的Collision在每次碰撞前,都從Selector裡把碰撞層的所有三角形拷貝出來,過程中順便做Local to World Space的轉換,若面數量少還好,要是以上千面來算就可觀了!小弟用Intel Core2 i3做實驗,每個Frame對一個1250面的模型做50次的線段碰撞,FPS竟然低於100以下,這對於要拿Irrlicht寫遊戲的人來說,根本就不堪使用。

解法:
關鍵就發生在CSceneCollisionManager::getCollisionPoint這個函式,這函式做了以上所描述的事情,那該怎麼改呢?其實不難,眼尖的你因該已經心裡有譜了,問題出在“拷貝”與“空間轉換”,上千個頂點的模型來說這算量是非常大的,所以就是要tuning這兩點。
  1. 空間轉換:我們只需要把線段轉換至Model的Local Sapce,去做碰撞就可解決將所有點轉換至World Space的問題了!
  2. 拷貝:我們只是單純的對碰撞層的Model做碰撞,並不會修改到資料本身,因此是可以保證資料不被修改的安全性。
根據以上兩點,我們對函式修改如下:(特別注意標記為紅色的代碼)
bool getCollisionPoint(const core::line3d & ray, const core::matrix4& matWorld, ITriangleSelector* selector, core::vector3df& outIntersection, core::triangle3df& outTriangle, ISceneNode*& outNode)
{
    if (!selector)
    {
        _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
        return false;
    }

    s32 cnt = selector->getTriangleCount();

    const core::vector3df linevect = ray.getVector().normalize();
    core::vector3df intersection;
    f32 nearest = FLT_MAX;
    bool found = false;
    const f32 raylength = ray.getLengthSQ();

    const f32 minX = core::min_(ray.start.X, ray.end.X);
    const f32 maxX = core::max_(ray.start.X, ray.end.X);
    const f32 minY = core::min_(ray.start.Y, ray.end.Y);
    const f32 maxY = core::max_(ray.start.Y, ray.end.Y);
    const f32 minZ = core::min_(ray.start.Z, ray.end.Z);
    const f32 maxZ = core::max_(ray.start.Z, ray.end.Z);

    for (s32 i=0; i          const core::triangle3df& triangle = (*selector->getTriangles())[i];

        if(minX > triangle.pointA.X && minX > triangle.pointB.X && minX > triangle.pointC.X)
            continue;
        if(maxX < triangle.pointA.X && maxX < triangle.pointB.X && maxX < triangle.pointC.X)
            continue;
        if(minY > triangle.pointA.Y && minY > triangle.pointB.Y && minY > triangle.pointC.Y)
            continue;
        if(maxY < triangle.pointA.Y && maxY < triangle.pointB.Y && maxY < triangle.pointC.Y)
            continue;
        if(minZ > triangle.pointA.Z && minZ > triangle.pointB.Z && minZ > triangle.pointC.Z)
            continue;
        if(maxZ < triangle.pointA.Z && maxZ < triangle.pointB.Z && maxZ < triangle.pointC.Z)
            continue;

        if (triangle.getIntersectionWithLine(ray.start, linevect, intersection))
        {
            const f32 tmp = intersection.getDistanceFromSQ(ray.start);
            const f32 tmp2 = intersection.getDistanceFromSQ(ray.end);

            if (tmp < raylength && tmp2 < raylength && tmp < nearest)
            {
                nearest = tmp;
                outTriangle = triangle;
                outIntersection = intersection;
                outNode = selector->getSceneNodeForTriangle(i);
                found = true;
            }
        }
    }

    if( found )
    {
        matWorld.transformVect( outTriangle.pointA, outTriangle.pointA );
        matWorld.transformVect( outTriangle.pointB, outTriangle.pointB );
        matWorld.transformVect( outTriangle.pointC, outTriangle.pointC );
        matWorld.transformVect( outIntersection, outIntersection );
    }

    _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
    return found;
}

沒有留言:

張貼留言

LinkWithin

Related Posts Plugin for WordPress, Blogger...