2011年2月27日 星期日

OpenGL 發射火焰的效果

光看靜止的圖片有時候還是看不出個所以然,所以這次我用CamStudio來抓取影像,
只是效果不是是很好.頓頓的,有空的話,再試試別種軟體來抓影片.
下面就是音速小子發射火焰槍的效果:

我把重點程式碼貼上,火焰槍就是這樣做出來的:

Flamethrower.h

struct Flame //火的資料(很多的火組成火焰)
{
Vector Position; //位置
Vector ViewPoint;//方向
int Life;//生命
    float Size; //大小
    float Rotate; //不斷的旋轉角度,使火焰看起來較活
};
const int MAX_FLAME_COUNT = 1000;

class Flamethrower
{
public:
    Flamethrower();
    ~Flamethrower();
    bool Initial(char *texture);
    void Fire(Vector Position,
              Vector ViewPoint,
              int Life);//發射火焰
    void UpData(Vector CameraPosition, int time);
    void Draw(Vector up, Vector right);
    CTexture Texture;
protected:
    Flame FlamePool[MAX_FLAME_COUNT];//一個火焰"坑"(就是存放很多的火)
    int SortIndex[MAX_FLAME_COUNT]; //排序的索引陣列
    int Count; //火的數量
    int time1,time2;//更新的變數
};

這個類別應該是很簡單,不用多做解釋,直接看Flamethrower.cpp
Flamethrower::Flamethrower()
{
   Count = 0;
   time1 = 0;
}

Flamethrower::~Flamethrower()
{

}

bool Flamethrower::Initial(char *texture)
{
    srand(GetTickCount());
    if( !Texture.LoadTGA(texture) )
       return false;
    return true;
}

void Flamethrower::Fire(Vector Position, Vector ViewPoint, int Life)
{
    int i,temp;
    if (Count < MAX_FLAME_COUNT) //若沒有超過限制數量
    {
        temp = Count;
        for(i=0;i<Count;i++)
        {
            if(FlamePool[i].Life == 0)
            {
                temp = i;
                break;
            }
        }
        if(temp==MAX_FLAME_COUNT)
            return;
        FlamePool[temp].Position = Position;
//配合音速小子攻擊時槍會"上下"震動
if(rand()%100 < 50)
        FlamePool[temp].Position[1] += rand()%3;
else
   FlamePool[temp].Position[1] -= rand()%3;
//
        FlamePool[temp].ViewPoint = ViewPoint;
        //往x軸擴展出去的效果
        if(rand()%100<50)
            FlamePool[temp].ViewPoint[0] += (float)((rand()%100)/500.00);
        else
            FlamePool[temp].ViewPoint[0] -= (float)((rand()%100)/500.00);

        //往y軸上升的效果
        FlamePool[temp].ViewPoint[1] += (float)((rand()%100)/90.00);

        //往z軸擴展出去的效果
        if(rand()%100<50)
            FlamePool[temp].ViewPoint[2] += (float)((rand()%100)/500.00);
        else
            FlamePool[temp].ViewPoint[2] -= (float)((rand()%100)/500.00);

        FlamePool[temp].Life = Life;
        FlamePool[temp].Size = 1;
        FlamePool[temp].Rotate = rand()%360;
        SortIndex[temp] = temp;

        if (temp == Count && Count < MAX_FLAME_COUNT-1) //若沒有超過限制數量
            Count++;
    }
}

void Flamethrower::UpData(Vector CameraPosition, int time)
{
    int i,j;

    if(time-time1 > 1)
    {
        time1 = time;
        for (i=0;i<Count;i++)
        {
            if (FlamePool[i].Life > 0)
            {
                Vector delta(FlamePool[i].ViewPoint[0]-FlamePool[i].Position[0],
                             0,
                             FlamePool[i].ViewPoint[2]-FlamePool[i].Position[2]);
                FlamePool[i].Position += delta * 6;
                FlamePool[i].ViewPoint += delta * 6;
                FlamePool[i].Life--;
                FlamePool[i].Size += .6;
                FlamePool[i].Rotate += 4+rand()%8;
                if( FlamePool[i].Rotate > 360 )
                    FlamePool[i].Rotate = 1;
            }
        }

        //做排序(離Camera越遠,就要越先畫出來)
        for (i=Count-1;i>0;i--)
        {
            for (j=1;j<=i;j++)
            {
                Vector v1 = FlamePool[SortIndex[j-1]].Position - CameraPosition;
                Vector v2 = FlamePool[SortIndex[j]].Position - CameraPosition;
                double length1 = (v1[0] * v1[0]) + (v1[1] * v1[1]) + (v1[2] * v1[2]);
                double length2 = (v2[0] * v2[0]) + (v2[1] * v2[1]) + (v2[2] * v2[2]);
                if ( length1 < length2 ) //v2距離較遠,與v1交換
                {
                    int temp = SortIndex[j-1];
                    SortIndex[j-1] = SortIndex[j];
                    SortIndex[j] = temp;
                }
            }
        }
    }
}

void Flamethrower::Draw(Vector up, Vector right)
{
    int i,j;
    Vector v;
    for (i=0;i<Count;i++)
    {
        if (FlamePool[SortIndex[i]].Life > 0
   && SphereTest(FlamePool[SortIndex[i]].Position[0], FlamePool[SortIndex[i]].Position[1], FlamePool[SortIndex[i]].Position[2], FlamePool[SortIndex[i]].Size))
        {
            glPushMatrix();
                glTranslatef(FlamePool[SortIndex[i]].Position[0], FlamePool[SortIndex[i]].Position[1], FlamePool[SortIndex[i]].Position[2]);
                glColor4f(1.0f,1.0f,1.0f,FlamePool[SortIndex[i]].Life/96.00);
                if( up[1] == 1 ) //正上方是頭頂,所以方塊對z軸旋轉
                    glRotatef(FlamePool[SortIndex[i]].Rotate,0,0,1);
                if( up[2] == -1 )//正上方是眼前,所以方塊對y軸旋轉
                    glRotatef(FlamePool[SortIndex[i]].Rotate,0,1,0);
   glBegin(GL_QUADS);
   v = up*(-FlamePool[SortIndex[i]].Size) + right*(-FlamePool[SortIndex[i]].Size);
       glTexCoord2f(0.0f, 0.0f);
   glVertex3fv(v);
   v = up*(-FlamePool[SortIndex[i]].Size) + right*FlamePool[SortIndex[i]].Size;
     glTexCoord2f(1.0f, 0.0f);
   glVertex3fv(v);
                    v = up*(FlamePool[SortIndex[i]].Size) + right*(FlamePool[SortIndex[i]].Size);
                    glTexCoord2f(1,1);
                    glVertex3fv(v);
   v = up*(FlamePool[SortIndex[i]].Size) + right*(-FlamePool[SortIndex[i]].Size);
     glTexCoord2f(0, 1);
   glVertex3fv(v);
   glEnd();
            glPopMatrix();
        }
    }
}

做出來的效果雖不是最佳,但也有幾分樣了,而且沒有加入讓火焰貼圖面向攝影機的功能,所以實用性質不大,(那....為什麼放上來啊?....)

沒有留言:

張貼留言