Selasa, 16 Juni 2015

Animasi 3D dengan Objek MD2

Selamat Pagi, salam sejahterah selalu rekan-rekan...

Pagi ini aku akan memulai babak baru, ya animasi 3D, ini adalah artikel pertama tentang animasi 3D dengan objek MD2.


Terus terang untuk menulis artikel ini ogut agak bingung, karena harus memulai dari tahap mana(Klik sini untuk artikel teori animasi 3D). Sebab ada beberapa tahapan yang harus dilewati terlebih dahulu sebelum sampai kesini, tapi kita langsung terabas saja ya, rawe-rawe rantas malang-malang putung (urusan kena tilang polisi ntar belakangan yang penting maju dulu).

Animasi 3D erat berkaitan dengan matematika vektor 3R, pada saat artikel ini ogut buat, belum ada satu artikelpun di blog ini yang mejelaskan tentang vektor 3R, jadi jika reka-rekan agak bingung dengan isi artikel ini jangan takut. Sebab di artikel lain penjelasan cara membuat objek 3D animasi MD2 dan cara membuat program animasinya akan ogut ulang dari level yang paling bawah...hehehe...gimana seneng-kan.

Program kali ini akan menampilkan sebuah objek 3D bernama Yoshi, objek bukan ogut yang buat karena ogut ngak bisa ngutak-ngatik model seperti itu dengan 3ds max. Setelah program rekan-rekan jalankan, gunakan tombol ‘W’ , ‘P’ dan anak panah kiri kanan untuk mengubah animasi Yoshi. Bagi rekan-rekan yang baru bergabung dengan blog ini bisa membaca artikel terdahulu tentang konsep face dan metode 3 daftar, karena berhubungan erat.






Objek Yoshi bisa dibuat dengan menggunakan 3ds max, ogut sih pernah membuat objek animasi seperti Yoshi tapi yang sederhana saja, seperti gambar di bawah ini.


Objek di atas ogut kasih nama MoTer 3D (Mobil Terbang), Moter dibuat dengan menggunakan 3ds max versi 6.0, MoTer dapat bertransformasi dari mobil biasa yang jalan di darat menjadi mobil terbang dengan baling-baling. Source program di blog ini hanya ogut sertakan yang Yoshi saja, sedangkan yang MoTer akan ogut bahas di lain waktu.

Untuk menampilkan Yoshi, caranya load objek 3D Yoshi.

bool Md2Model::LoadModel(char *fileAnimasiName, float ModelSkala, char *SkinFileName)
{
    int i;

    LoadSkinBmp(SkinFileName);
    if ((IDTexture == NULL) || (LebarBmp == 0) || (TinggiBmp == 0))
    {
        MessageBox(NULL, "Error Skin File, program abort", SkinFileName, MB_OK);
        return false;
    }

    FILE *fp = 0;
    errno_t err;

    //fp = fopen(fileAnimasiName, "rb");
    err = fopen_s(&fp, fileAnimasiName, "rb");
  
    //if(!fp)
    if (err != 0)
        return false;

    fseek(fp, 0, SEEK_END);
    int filelength = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    char *buffer = 0;
    buffer = new char[filelength + 1];

    if(!buffer)
        return false;
  
    fread(buffer, sizeof(char), filelength, fp);

    fclose(fp);
 
    MD2Header *pHeader = 0;

    pHeader = (MD2Header*)buffer;

    // Get header data
    ModelMD2Header.magic = pHeader->magic;
    ModelMD2Header.version = pHeader->version;
    ModelMD2Header.skinWidth = pHeader->skinWidth;
    ModelMD2Header.skinHeight = pHeader->skinHeight;
    ModelMD2Header.frameSize = pHeader->frameSize;
    ModelMD2Header.numSkins = pHeader->numSkins;
    ModelMD2Header.numVertices = pHeader->numVertices;
    ModelMD2Header.numTexCoords = pHeader->numTexCoords;
    ModelMD2Header.numTriangles = pHeader->numTriangles;
    ModelMD2Header.numGLCommands = pHeader->numGLCommands;
    ModelMD2Header.numFrames = pHeader->numFrames;
    ModelMD2Header.offsetSkins = pHeader->offsetSkins;
    ModelMD2Header.offsetTexCoords = pHeader->offsetTexCoords;
    ModelMD2Header.offsetTriangles = pHeader->offsetTriangles;
    ModelMD2Header.offsetFrames = pHeader->offsetFrames;
    ModelMD2Header.offsetGLCommands = pHeader->offsetGLCommands;
    ModelMD2Header.offsetEnd = pHeader->offsetEnd;
       
    //this is MD2 file or not
    if(ModelMD2Header.version != 8)
    {
        if(buffer != 0)
        {
            delete[] buffer;
            buffer = 0;
        }
        Release();   
        return false;
    }

    TextKoord = new TK[ModelMD2Header.numTexCoords];

    MD2TK *pTexList;

    //tipe data short
    pTexList = (MD2TK*)&buffer[ModelMD2Header.offsetTexCoords];

    //ubah texture data ke tipe float
    for(int i = 0; i < ModelMD2Header.numTexCoords; i++)
    {
        TextKoord[i].tu = (float)pTexList[i].s;
        TextKoord[i].tv = (float)pTexList[i].t;

        //penyesuaian texture koordinat
        TextKoord[i].tu /= (float)LebarBmp;
        TextKoord[i].tv /= (float)TinggiBmp;
    }


    TriangleList = new MD2Triangle[ModelMD2Header.numTriangles];

    MD2Triangle *pTriList;

    pTriList = (MD2Triangle*)&buffer[ModelMD2Header.offsetTriangles];

   
    for(i = 0; i < ModelMD2Header.numTriangles; i++)
    {
        TriangleList[i].VertexIndex[0] = pTriList[i].VertexIndex[0];
        TriangleList[i].VertexIndex[1] = pTriList[i].VertexIndex[1];
        TriangleList[i].VertexIndex[2] = pTriList[i].VertexIndex[2];
        TriangleList[i].TextKoordIndex[0] = pTriList[i].TextKoordIndex[0];
        TriangleList[i].TextKoordIndex[1] = pTriList[i].TextKoordIndex[1];
        TriangleList[i].TextKoordIndex[2] = pTriList[i].TextKoordIndex[2];
    }

    MD2Animasi *SingleFrame;

    ModelVertex = new VERTEX[ModelMD2Header.numVertices * ModelMD2Header.numFrames];

    int NumKeys = 0;
    int PanjangStr, k;
    char KeyName[16] = {0};
    char LastKeyName[16] = {0};
    char VeryFirstKeyName[16] = {0};


    for(i = 0; i < ModelMD2Header.numFrames; i++)
    {
        SingleFrame = (MD2Animasi*)&buffer[ModelMD2Header.offsetFrames + ModelMD2Header.frameSize * i];

        //hitung total Animasi
        strcpy_s(KeyName, SingleFrame->name);
       
        PanjangStr = strlen(KeyName);       

        for (k = 0; k < PanjangStr; k++)
        {
            if ((KeyName[k] == '0') || (KeyName[k] == '1') || (KeyName[k] == '2') || (KeyName[k] == '3') || (KeyName[k] == '4') || (KeyName[k] == '5') || (KeyName[k] == '6') || (KeyName[k] == '7') || (KeyName[k] == '8') || (KeyName[k] == '9'))
            {
                KeyName[k] = '\0';

                if (strcmp(KeyName, LastKeyName) != 0)
                {
                           
                    strcpy_s(LastKeyName, KeyName);
                    NumKeys++;
                }
                if (strcmp(VeryFirstKeyName, "") == 0)
                    strcpy_s(VeryFirstKeyName, KeyName);
                   
            }
        }
        //hitung total animasi

        for(int j = 0; j < ModelMD2Header.numVertices; j++)
        {
            //tukar Y dan Z agar objek tidak terbalik
            ModelVertex[ModelMD2Header.numVertices * i + j].X = SingleFrame->scale[0] *
                                SingleFrame->AnimasiInfo[j].Vertex[0] +
                                SingleFrame->translate[0];
           
            ModelVertex[ModelMD2Header.numVertices * i + j].Y = SingleFrame->scale[2] *
                                SingleFrame->AnimasiInfo[j].Vertex[2] +
                                SingleFrame->translate[2];   

            ModelVertex[ModelMD2Header.numVertices * i + j].Z = SingleFrame->scale[1] *
                                SingleFrame->AnimasiInfo[j].Vertex[1] +
                                SingleFrame->translate[1];

            //skala MD2 objek
            ModelVertex[ModelMD2Header.numVertices * i + j].X *= ModelSkala;
            ModelVertex[ModelMD2Header.numVertices * i + j].Y *= ModelSkala;
            ModelVertex[ModelMD2Header.numVertices * i + j].Z *= ModelSkala;
        }
    }//for(i = 0; i < ModelMD2Header.numFrames; i++)

  
    //setting frame start dan frame end untuk setiap animasi
    int counterArray = 0;

    ModelAnimasi = new ANIMASI[NumKeys];
    TotalAnimasi = NumKeys;

    strcpy_s(KeyName, "");
    strcpy_s(LastKeyName, VeryFirstKeyName);

    ModelAnimasi[0].FrameStart = 0;
    strcpy_s(ModelAnimasi[0].AnimasiName,  VeryFirstKeyName);

    for(i = 0; i < ModelMD2Header.numFrames; i++)
    {
        SingleFrame = (MD2Animasi*)&buffer[ModelMD2Header.offsetFrames + ModelMD2Header.frameSize * i];

        strcpy_s(KeyName, SingleFrame->name);
        PanjangStr = strlen(KeyName);

        for (k = 0; k < PanjangStr; k++)
        {
            if ((KeyName[k] == '0') || (KeyName[k] == '1') || (KeyName[k] == '2') || (KeyName[k] == '3') || (KeyName[k] == '4') || (KeyName[k] == '5') || (KeyName[k] == '6') || (KeyName[k] == '7') || (KeyName[k] == '8') || (KeyName[k] == '9'))
            {
                KeyName[k] = '\0';

                if (strcmp(KeyName, LastKeyName) != 0)
                {
                    ModelAnimasi[counterArray].FrameEnd = i-1;
                    counterArray++;
                    ModelAnimasi[counterArray].FrameStart = i;

                    strcpy_s(LastKeyName, KeyName);
                           
                    strcpy_s(ModelAnimasi[counterArray].AnimasiName,  KeyName);

                }
                else
                {
                    ModelAnimasi[counterArray].FrameEnd = i;
                }
            }
        }   
    }//for(i = 0; i < ModelMD2Header.numFrames; i++)

    // Delete the data in the buffer.
    if(buffer != 0)
    {
        delete[] buffer;
        buffer = 0;
    }
 
    return true;
}

void Md2Model::LoadSkinBmp(char *FileName)
{
    AUX_RGBImageRec *TempAUX_RGB;

    LebarBmp = 0;
    TinggiBmp = 0;
   
    TempAUX_RGB = auxDIBImageLoad(FileName);
   
    if (TempAUX_RGB != NULL)
    {                  
        glGenTextures(1, &IDTexture);

        glBindTexture(GL_TEXTURE_2D,IDTexture);
        glPixelStorei(GL_UNPACK_ALIGNMENT,1);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);   
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
       
        gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGB,
                            TempAUX_RGB->sizeX,TempAUX_RGB->sizeY,
                            GL_RGB,GL_UNSIGNED_BYTE,
                            TempAUX_RGB->data);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
                            TempAUX_RGB->sizeX, TempAUX_RGB->sizeY,
                            0, GL_RGB, GL_UNSIGNED_BYTE,
                            TempAUX_RGB->data);       
       
        //simpan lebar dan tinggi bmp
        LebarBmp = TempAUX_RGB->sizeX;
        TinggiBmp = TempAUX_RGB->sizeY;

        free(TempAUX_RGB->data);   
        free(TempAUX_RGB);       
    }
    else
        IDTexture = NULL;
}

Lalu tampilkan di layar monitor.

void Render_YOSHI()
{
    float time = (float)timeGetTime();

    SkalaVektor = (time - StartTime) * TimerSpeed;

    if (SkalaVektor >= 1.0f)
    {
        SkalaVektor = 0.0f;
        CurrentIndexAnimasi++;
        NextIndexAnimasi++;

        if (CurrentIndexAnimasi >= Yoshi.ModelAnimasi[IndexAnimasi].FrameEnd)
            CurrentIndexAnimasi = Yoshi.ModelAnimasi[IndexAnimasi].FrameStart;

        if (NextIndexAnimasi >= Yoshi.ModelAnimasi[IndexAnimasi].FrameEnd)
            NextIndexAnimasi = Yoshi.ModelAnimasi[IndexAnimasi].FrameStart;

        // Reset the time
        StartTime = (float)timeGetTime();
    }

    //display md2 objek
    Yoshi.RenderMD2(CurrentIndexAnimasi, NextIndexAnimasi, SkalaVektor);

    //deteksi keyboard
    DelayKeyboard++;
    if (DelayKeyboard > 20)
    {
        if (GetKeyState(VK_RIGHT) & 0x80)
        {
            IndexAnimasi++;
            DelayKeyboard = 0;

            if (IndexAnimasi > (Yoshi.TotalAnimasi - 1))
                IndexAnimasi = 0;

            CurrentIndexAnimasi = Yoshi.ModelAnimasi[IndexAnimasi].FrameStart;
            NextIndexAnimasi = Yoshi.ModelAnimasi[IndexAnimasi].FrameStart + 1;
        }

        if (GetKeyState(VK_LEFT) & 0x80)
        {
            IndexAnimasi--;
            DelayKeyboard = 0;

            if (IndexAnimasi < 0)
                IndexAnimasi = Yoshi.TotalAnimasi - 1;

            CurrentIndexAnimasi = Yoshi.ModelAnimasi[IndexAnimasi].FrameStart;
            NextIndexAnimasi = Yoshi.ModelAnimasi[IndexAnimasi].FrameStart + 1;
        }

        if (GetKeyState('W') & 0x80)
        {
            DelayKeyboard = 0;
            WireFill = !WireFill;

            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        }

        if (GetKeyState('P') & 0x80)
        {
            DelayKeyboard = 0;
            WireFill = !WireFill;

            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        }
    }
}

void Md2Model::RenderMD2(int CurrentFrame, int NextFrame, float SkalaVektor)
{
    glBindTexture(GL_TEXTURE_2D,IDTexture);

    glBegin(GL_TRIANGLES);
  
    for(int i = 0; i < ModelMD2Header.numTriangles; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            //current frame
            v1[j].X  = ModelVertex[TriangleList[i].VertexIndex[j] + ModelMD2Header.numVertices * CurrentFrame].X;
            v1[j].Y  = ModelVertex[TriangleList[i].VertexIndex[j] + ModelMD2Header.numVertices * CurrentFrame].Y;
            v1[j].Z  = ModelVertex[TriangleList[i].VertexIndex[j] + ModelMD2Header.numVertices * CurrentFrame].Z;

            //next frame
            v2[j].X  = ModelVertex[TriangleList[i].VertexIndex[j] + ModelMD2Header.numVertices * NextFrame].X;
            v2[j].Y  = ModelVertex[TriangleList[i].VertexIndex[j] + ModelMD2Header.numVertices * NextFrame].Y;
            v2[j].Z  = ModelVertex[TriangleList[i].VertexIndex[j] + ModelMD2Header.numVertices * NextFrame].Z;

            //ModelVertex yang ditampilkan ke screen
            vt[j].X  = v1[j].X + (v2[j].X - v1[j].X) * SkalaVektor;
            vt[j].Y  = v1[j].Y + (v2[j].Y - v1[j].Y) * SkalaVektor;
            vt[j].Z  = v1[j].Z + (v2[j].Z - v1[j].Z) * SkalaVektor;
        }
                   
        //texture koordinat dan ModelVertex(tanpa normal)
            glTexCoord2f(TextKoord[TriangleList[i].TextKoordIndex[0]].tu, TextKoord[TriangleList[i].TextKoordIndex[0]].tv);
        glVertex3f(vt[0].X, vt[0].Y, vt[0].Z);

            glTexCoord2f(TextKoord[TriangleList[i].TextKoordIndex[1]].tu, TextKoord[TriangleList[i].TextKoordIndex[1]].tv);
            glVertex3f(vt[1].X, vt[1].Y, vt[1].Z);

        glTexCoord2f(TextKoord[TriangleList[i].TextKoordIndex[2]].tu, TextKoord[TriangleList[i].TextKoordIndex[2]].tv);
        glVertex3f(vt[2].X, vt[2].Y, vt[2].Z);
    }
    glEnd();
}

Update 28 Des 2015

Jika rekan-rekan belum jelas tentang membuat animasi 3D silahkan klik link artikel  di bawah ini yang ogut tulis lebih detil.


Klik disini untuk link ke artikel membuat Animasi Hely 3D


Klik disini untuk link ke artikel membuat Multi Animasi MoTer 3D


Klik disini untuk link ke artikel membuat Terrain untuk Hely 3D

Selesai sudah tugas ogut kali ini, sampai jumpa di artikel berikutnya.

Salam Hangat selalu


Heriady
heriady.yoh@gmail.com




Artikel terkait

Membuat Terrain untuk Hely 3D

Program Animasi Game MoTer 3D dengan OpenGL

Membuat Animasi Hely 3D dengan OpenGL

Teori Animasi 3D dengan Vektor R3

Program pertama OpenGL 3D

Konsep face dan metode 3 daftar

Garden 3D yang dibuat dengan 3ds max

Membuat objek 3D dengan 3ds max

Tidak ada komentar:

Posting Komentar