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