Bab 6
Milkshape
3D
Seperti buku bergerak maju ke bab ini, format mulai muncul dalam bab ini. Ini adalah format yang pertma dari format yang lebih rumit. Berakhirlah sudah hari-hari yang sederhana ”beban-dan-siklus” format, seperti format MD2 yang terkenal dan telah dibahas sebelumnya dalam buku ini. Bab ini berisi tentang semua yang anda perlu tau tentang MS3D. Format yang ditampilkan oleh paket, pemodelan sangat bagus dan murah dan dikenal sebagai Milkshape 3D. Milkshape 3D diciptakan oleh chUmbaLum sOft, sebuah perusahan software kecil yang terdiri dari seorsng pendiri yaitu Mete Ciragan. Mete membuat Milksahpe untuk memungkinkan individu untuk membuat model baru untuk Software Valve Half-Life (http:// www.valvesoftware.com) tanpa menggunakan alat permodelan mahal dan profesional.
Sejak pembentukannya, Milkshape teah menjadi salah satu alat pemodelan yang paling populer diantara para pengembang independen ”mods-smaller” permainan dibangun diatas permainan yang sama komersialnya. Sekarang Milkshape mendukung untuk mengimpor dan mengekspor format untuk beberapa permainan populer dan juga mesinnya. Saya sarankan bahwa setiap pengembang permainan pada anggaran singkat lihat program indah ini. Anda dapat memeriksa percobaan 30-hari pada CD. Anada dapat menemukan file setup pada direktori program. Untuk kesenangan anda dapat lihat, gambar 6.1yang menunjukan editor 3D milkshape dengan model yang akan gunakan sebagai contoh dalam halaman –halaman selanjutnya.
Cara Mendapatkan Data
Seperti biasa, anda mulai dengan mendapatkan data dari file. Tidak seperti kebanyakan model format MS3D tidak mengandung informasi tentang jumlah simpul, segitiga, atau apapun pada awal file. Sekarang semua ini adalah header sederhana untuk memverifikasi bahwa file Milkshape tersebut memang valid. Panjang header tepat 14 byte dan berisi identifikasi 10 karakter string dan sebuah nomor versi. Yang 10 byte pertama berisi string “MS3D000000”. String ini mengindentifikasi file sebagai file Milkshape.
Gambar 6.1 Editor 3D Milkshape lengkap dengan model contoh.
Perlu diingat bahwa null dalam string ID karakter tidak memiliki nilai. Pastikan anda mengingat saat anda memuat file. Yang kedua 4 byte berisi bilangan bulat tunggal. Ini adalah nomor versi dari file dan harus berisi nilai 3 atau 4. Format rinci disini adalah untuk kedua versi saja. Sebagai kemajuan dan fersi baru Milkshape yang dirilis, saya akan menempatkan tambahan dan perubahan pada buku situs web.
Simpul
Langsung setelah header maka kita membahas simpul. Seperti bagian lain dari file. Sepotong titik diawali oleh sebuah integer 2 byte yang tidak memiliki tanda yang berisi jumlah simpul yang hadir di dalam model. Simpul yang sedikit berbeda dari apa yang anda gunakan sebelumnya. Bukan hanya X,Y,Z koordinat latar, simpul Milkshape juga mengandung satu byte, integer yang memiliki nomor didalam simpul tersebut ”tulang ”. Nilai -1 berarti titik tidak memiliki tulang yang melekat padanya dan tidak terpengaruh selama animasi.
Berikut adalah struktur simpul yang digunakan untuk loader MS3D :
//----------------------------------------------
//- SMs3dVertex
//- A single vertex
struct SMs3dVertex
{
unsigned char m_ucFlags; //Editor flags, unused for the loader
CVector3 m_vVert; //X,Y,Z coordinates
char m_cBone; //Bone ID (-1 = no bone)
unsigned char m_ucUnused;
};
Ditengah kedua variabel dalam struktur yang dijelaskan sebelumnya, yang hanya menyisakan dua dari satu byte variabel kiri yang tidak diketahui. Yang pertama berisi berbagai flag untuk editor untuk menggunakan titik. Variabel ini sebuah variabel tidak bertanda karakter yang memegang status verteks dalam editor. Jika nilai adalah 0, verteks terlihat, tetapi tidak dipilih. Jika nilai adalah 1, simpulsimpul dipilih dalam editor, dan jika niali adalah 2, verteks tersembunyi dari pandangan di jendela editor . Nilai 3 berarti verteks adalah baik tersembunyi dan dipilih. Meskipun variabel ini tidak diperlukan untuk loading model ke dalam mesin anda, mungkin membantu jika anada menulis importir lain program pemodelan, seperti 3D Studio Max (www.discreet.com) atau Maya (www.aliaswavefront.com). Yang kedua variabel tidak berisi apa-apa, anda dapat melewatinya. Setelah simpul membaca, file tersebut segera bergerak untuk mendapatkan informasi.
Wajah
Wajah untuk format khusus berisi banyak informasi. Tapi, sebelum anda khawatir tentang pemuatan segitiga, anda perlu mencari tahu berapa banyak dari mereka. Seperti dengan potongan titik, wajah atau segitiga dimulai dengan sebuah integer dua byte. Bilangan bulat ini datang setelah titik terakhir adalah membaca dan sebelum data segitiga disimpan. Bilangan dua byte ini berisi jumlah struktur SM3DTriangel untuk membaca dari file. Tepat setelah dua byte senilai data yang menentukan jumlah wajah untuk membaca wajah mereka sendiri. Struktur wajah atau segitiga berisi bendera editor, indeks titik, koordinat tekstur, pengelompokan info dan bahkan informasi yang normal. Mari kita lihat pada struktur SMs3dTriangel untuk melihat apa yang memperkuat model memerlukan :
//--------------------------------------------
//- SMs3dTriangle
//- Triangle data structure
struct SMs3dTriangle
{
unsigned short m_usFlags; //Editor flags
unsigned short m_usVertIndices[3]; //Vertex indexes
CVector3 m_vNormals[3]; //Vertex normals;
float m_fTexCoords[2][3]; //Texture coordinates
unsigned char m_ucSmoothing; //Smoothing group
unsigned char m_ucGroup; //Group index
};
Baik, itu tidak terlalu buruk. Sebanyak enam variabel yang digunakan untuk setiap segitiga dalam model 3D MilkShape. Yang pertama seperti struktur titik, hanya sebuah bendera editor. Seperti simpul, 0 adalah regular, wajah tidak dipilih, 1 berarti wajah dipilih dan 2 berarti wajah yang tersembunyi dari pandangan. Sekali lagi, wajah dapat dipilih dan tersembunyi jika nilainya 3. Perhatikan bahwa variabel bendera disini adalah dua byte, bukan hanya dari 1 seperti dalam struktur simpul. Berikutnya terdapat tiga dari dua byte integer yang tidak bertanda. Ketiga bilangan bulat adalah indeks kedalam array dari simpul yang tercangkup kedalam bagian terakhir. Tiga simpul membentuk segitiga tunggal dalam model. Dan hanya menggunakan data yang dipelajari sejauh ini, adalah mungkin untuk membuat model yang solid. Namun jenis tersebut akan membosankan tanpa tekstur dan pencahayaan. Pindah ke bawah garis, anda datang ke titik normal. Sebuah simpil biasa digunakan untuk pernerangan. Setiap simpul yang normal adalah rata-rata dari semua titik wajah normal. Sebuah wajah yang normal tegak lurus terhadap arah yang terletak pada wajah, titik normal adalah rata-rata dari semua vektor yang tegak lurus. Seorang yang normal, apakah itu sebuah verteks atau sebuah wajah, harus menjadi vektor satuan dengan besarnya 1. Ini disimpan dalam kelas CVektor3. CVektor3 ini dibuat untuk vektor yang terdiri dari tiga variabel floating point, yang mengambil total 12 byte. Keuntungan utama dari menggunakan CVektor3 untuk setiap normal kesetimbang array sederhana mengapung adalah bahwa kelas CVektor3 berisi segudang fungsi. Fungsi ini membuat anda lebih mudah dikemudian hari ketika anda mulai menghidupkan model. Ada tiga normals, satu untuk setiap index verteks.
Berikutnya adalah koordinat tekstur. Koordinat u dan v disimpan di dalam jenis MilkShape. Ada total enam mengapung sepasang koordinat untuk masing-masing tiga simpul yang membentuk wajah. Namun, bukanya disimpan didalam u1,u2,v1,v2dan v3. Jika anda tidak ingat memesan ini, maka akan datang kembali untuk bagian anda. Saya menghabiskan beberapa jam debugging program hanya untuk menemukan ketika saya menggunakan koordinat tekstur yang salah. Dua variable terakhir dengan kelompok milik wajah. Variabel ini tidak terlalu penting karena anda akan melihat di bagian yang akan datang. Kelompok atau jerat dalam mengurus model untuk mengetahui mana wajah masing-masing kelompok.
Jerat
Untuk fleksibilitas maksimum, segitiga Milkshape 3D dikelompokkan menjadi
jerat atau kelompok. Hal ini memungkinkan bagian yang berbeda dari model untuk menggunakan tekstur yang berbeda, bahan, dan bahkan membuat hanya bagian tertentu dari model. Bagian mesh file mengikuti segitiga atau wajah bagian, dan seperti bagian lain didahului oleh sebuah integer dua-byte mengatakan berapa banyak jerat ada. Segera berikut adalah kelompok. Ada 35 byte informasi umum, diikuti dengan nomor dua-byte indeks segitiga. Jumlah indeks tidak konstan; beberapa kelompok mungkin memiliki lebih dari yang lain. Untuk mengkompensasiperbedaan ini,
Anda harus mampu untuk secara dinamis mengalokasikan memori di setiap kelompok untuk menahan indeks ini. Berikut adalah apa yang terlihat seperti struktur.
jerat atau kelompok. Hal ini memungkinkan bagian yang berbeda dari model untuk menggunakan tekstur yang berbeda, bahan, dan bahkan membuat hanya bagian tertentu dari model. Bagian mesh file mengikuti segitiga atau wajah bagian, dan seperti bagian lain didahului oleh sebuah integer dua-byte mengatakan berapa banyak jerat ada. Segera berikut adalah kelompok. Ada 35 byte informasi umum, diikuti dengan nomor dua-byte indeks segitiga. Jumlah indeks tidak konstan; beberapa kelompok mungkin memiliki lebih dari yang lain. Untuk mengkompensasiperbedaan ini,
Anda harus mampu untuk secara dinamis mengalokasikan memori di setiap kelompok untuk menahan indeks ini. Berikut adalah apa yang terlihat seperti struktur.
//------------------------------------------------------------
//- SMs3dMesh
//- Group of triangles in the ms3d file
struct SMs3dMesh
{
unsigned char m_ucFlags; //Editor flags again
char m_cName[32]; //Name of the mesh
unsigned short m_usNumTris;//Number of triangles in the group
unsigned short * m_uspIndices; //Triangle indexes
char m_cMaterial; //Material index, -1 = no material
//Let it clean up after itself like usual
SMs3dMesh()
{
m_uspIndices = 0;
}
~SMs3dMesh()
{
if(m_uspIndices)
{
delete [] m_uspIndices;
m_uspIndices = 0;
}
}
};
Struktur ini membutuhkan sedikit perawatan ketika sedang dibaca masuk m_uspIndices
variabel pointer, yang berarti Anda tidak bisa membaca dalam NumberOfMeshes *
sizeof (SMs3dMesh). Untuk mesh setiap, Anda harus membaca 35 byte pertama yang
terdiri dari beberapa editor bendera-bendera yang sama yang simpul dan
segitiga menggunakan-0 untuk tidak dipilih, 1 untuk dipilih, dan 2 untuk tersembunyi. Anda juga harus membaca 32-karakter nama mesh dan sebuah integer dua-byte yang berisi jumlah segitiga mesh. Menggunakan variabel terakhir,
Anda harus mengalokasikan memori untuk indeks segitiga. M_uspVariable sekarang siap untuk menampung semua dari dua-byte integer diperlukan untuk menunjukkan yang digunakan dalam segitiga mesh. setiap elemen dari array adalah indeks ke dalam array segitiga yang diciptakan dan diisi sebelumnya di urutan beban.
Tepat setelah Anda membaca semua indeks segitiga, ada, tunggal single-byte
variabel yang memegang indeks ke dalam array bahan (yang akan Anda
akan mendapatkan dalam satu detik). Variabel ditandatangani, dan nilai -1
berarti mesh berisi materi tidak ada. Terakhir dari semua, seperti yang Anda lihat, struktur mengurus menghapus sendiri memori, berarti Anda tidak perlu khawatir tentang mengingat untuk jelas itu ketika Anda selesai menggunakannya. Menghapus array dari jerat akan otomatis menghapus segala sesuatu di dalamnya.
variabel pointer, yang berarti Anda tidak bisa membaca dalam NumberOfMeshes *
sizeof (SMs3dMesh). Untuk mesh setiap, Anda harus membaca 35 byte pertama yang
terdiri dari beberapa editor bendera-bendera yang sama yang simpul dan
segitiga menggunakan-0 untuk tidak dipilih, 1 untuk dipilih, dan 2 untuk tersembunyi. Anda juga harus membaca 32-karakter nama mesh dan sebuah integer dua-byte yang berisi jumlah segitiga mesh. Menggunakan variabel terakhir,
Anda harus mengalokasikan memori untuk indeks segitiga. M_uspVariable sekarang siap untuk menampung semua dari dua-byte integer diperlukan untuk menunjukkan yang digunakan dalam segitiga mesh. setiap elemen dari array adalah indeks ke dalam array segitiga yang diciptakan dan diisi sebelumnya di urutan beban.
Tepat setelah Anda membaca semua indeks segitiga, ada, tunggal single-byte
variabel yang memegang indeks ke dalam array bahan (yang akan Anda
akan mendapatkan dalam satu detik). Variabel ditandatangani, dan nilai -1
berarti mesh berisi materi tidak ada. Terakhir dari semua, seperti yang Anda lihat, struktur mengurus menghapus sendiri memori, berarti Anda tidak perlu khawatir tentang mengingat untuk jelas itu ketika Anda selesai menggunakannya. Menghapus array dari jerat akan otomatis menghapus segala sesuatu di dalamnya.
Bahan-bahan
Untuk benar-benar membuat model menonjol dan menambahkan banyak kustomisasi, Anda dapat menggunakan bahan-bahan. Bahan mengontrol cara menangani penyaji.
Para texturing dan pencahayaan model. Dari tekstur, warna, untuk
transparansi, bahan melakukan semuanya.
Struktur bahan yang cukup besar dan berisi banyak data, jadi beruang
dengan saya di sini.
Ingatlah untuk membaca dalam dua-byte variabel Anda yang memberitahu Anda berapa banyak
bahan ada sebelum Anda melompat ke membaca data materi.
Berikut adalah struktur materi:
transparansi, bahan melakukan semuanya.
Struktur bahan yang cukup besar dan berisi banyak data, jadi beruang
dengan saya di sini.
Ingatlah untuk membaca dalam dua-byte variabel Anda yang memberitahu Anda berapa banyak
bahan ada sebelum Anda melompat ke membaca data materi.
Berikut adalah struktur materi:
//------------------------------------------------
//- SMs3dMaterial
//- Material information for the mesh
struct SMs3dMaterial
{
char m_cName[32]; //Material name
float m_fAmbient[4]; //Ambient values
float m_fDiffuse[4]; //Diffuse values
float m_fSpecular[4]; //Specular values
float m_fEmissive[4]; //Emissive values
float m_fShininess; //0 - 128
float m_fTransparency; //0 - 1
char m_cMode; //unused
char m_cTexture[128]; //Texture map file
char m_cAlpha[128]; //Alpha map file
CImage m_Texture;
};
Anda perlu berhati-hati ketika Anda mulai membaca data dari file tersebut.
Alih-alih membaca seluruh kelompok material dalam sekaligus, Anda harus
loop dan membacanya dalam satu waktu. Alasan untuk ini adalah yang terakhir
variabel dalam m_Texture, struktur. m_Texture adalah kelas gambar yang
digunakan untuk menyimpan tekstur untuk materi. Ini menghilangkan mencoba untuk mengurutkan
keluar tekstur nanti saat Anda perlu menggunakan mereka selama program.
Catatan bahwa ini bagian dari struktur sebenarnya tidak terkandung dalam
file, membuat ukuran sebenarnya dari struktur dalam byte file yang 361. para
Variabel CImage adalah kelas khusus yang berada di basecode umum di
file (image.cpp dan image.h). Anda dengan mudah dapat menghapus variabel ini
dan menggantinya dengan sistem anda sendiri gambar-loading.
Variabel pertama dalam struktur adalah 32-karakter array memegang
nama bahan. Ini adalah tidak semua yang penting jika Anda hanya
membuat loader, tapi itu bagus untuk memiliki ketika bekerja dengan model dalam
Milkshape sendiri.
Selanjutnya enam variabel mengandung sifat-sifat bahan. properti ini
menentukan cara pencahayaan adegan akan mempengaruhi model.
■ Para m_fAmbient dan m_fDiffuse setiap toko empat floating-point
nilai-nilai yang mewakili, merah, hijau, biru, dan alpha (RGBA)
nilai-nilai warna. Warna-warna ini membantu menentukan warna
material dan menentukan bagaimana poligon menggunakan bahan ini
akan bereaksi terhadap pencahayaan dalam adegan.
■ Variabel berikutnya, m_fSpecular, juga mengandung warna RGBA.
Sifat material specular menentukan warna specular
menyoroti atau "tempat mengkilap" pada mesh menggunakan materi.
■ Array terakhir mengapung adalah himpunan m_fEmissive. Variabel ini juga
berisi empat mengapung. Properti memancarkan menentukan bagaimana intens
material akan memancarkan cahaya. Semakin tinggi nilai, "terang"
materi akan.
■ Properti material akhir adalah shininess material. para
variabel m_fShininess berisi mengapung tunggal. Variabel ini menentukan
betapa mengkilap yang specular highlights yang. Semakin rendah
nilai, lebih gelap dan kusam menyoroti akan-hanya
berlawanan dengan meningkatnya nilai. Menyoroti lebih gelap dan kusam yang
digunakan untuk bahan seperti kayu dan aspal, sedangkan cerah,
menyoroti shinier yang terbaik untuk logam mengkilap dan bahan buatan.
Setelah semua sifat material datang satu lebih-transparansi
material, yang disimpan dalam m_fTransparency. Meskipun lainnya
materi variabel berurusan dengan warna, transparansi set bagaimana buram atau
"Tembus" mesh. Nilai 1 adalah benar-benar buram dan
Nilai 0 adalah complitely transparan, atau tak terlihat. Karena cara
OpenGL menangani bahan, cara termudah untuk menangani ini adalah untuk mengambil ini
nilai dan hubungkan ke elemen terakhir dari properti menyebar. ini
metode menciptakan mesh transparan cukup bagus.
Setelah ini semua diambil dari perawatan, Anda harus melewatkan byte untuk mengkompensasi
untuk variabel, kecil yang tidak digunakan dalam informasi material. single ini
m_ucMode variabel byte tidak digunakan oleh format untuk saat ini. Hal ini di sana untuk
digunakan dalam versi kemudian.
Kemudian Anda sampai ke tekstur. Nama tekstur disimpan dalam 128-byte
string. Setelah Anda mendapatkan itu, Anda dapat mengirim langsung ke fungsi beban
material, yang disimpan dalam m_fTransparency. Meskipun lainnya
materi variabel berurusan dengan warna, transparansi set bagaimana buram atau
"Tembus" mesh. Nilai 1 adalah benar-benar buram dan
Nilai 0 adalah complitely transparan, atau tak terlihat. Karena cara
OpenGL menangani bahan, cara termudah untuk menangani ini adalah untuk mengambil ini
nilai dan hubungkan ke elemen terakhir dari properti menyebar. ini
metode menciptakan mesh transparan cukup bagus.
Setelah ini semua diambil dari perawatan, Anda harus melewatkan byte untuk mengkompensasi
untuk variabel, kecil yang tidak digunakan dalam informasi material. single ini
m_ucMode variabel byte tidak digunakan oleh format untuk saat ini. Hal ini di sana untuk
digunakan dalam versi kemudian.
Kemudian Anda sampai ke tekstur. Nama tekstur disimpan dalam 128-byte
string. Setelah Anda mendapatkan itu, Anda dapat mengirim langsung ke fungsi beban
dari kelas CImage termasuk dalam struktur. Ini akan mengambil tekstur
dan mengurus loading sehingga siap untuk digunakan ketika Anda mendapatkan ke
render panggung. Nama file yang disimpan dalam file dapat dikirimkan langsung ke
yang CImage:: Load () fungsi atau untuk Anda sendiri tekstur-loading fungsi.
Variabel terakhir memegang nama file dari peta alfa. Karena fakta
bahwa saya tidak dapat menemukan informasi tentang ini pada saat ini ditulis,
penggunaan peta alpha pada model saat ini tidak didukung. Namun,
tetap memeriksa situs Web untuk buku dan segera setelah informasi
tersedia saya akan memperbarui kode dan teks dan posting di sana.
Wah, itu banyak informasi. Namun, Anda sekarang memiliki cukup untuk
membuat model dalam apa yang saya sebut "posisi awal" nya. Ini adalah posisi yang
sebelum setiap animasi diterapkan. Secara umum, posisi ini dioptimalkan
untuk kemudahan mengedit dan mungkin tidak muncul dalam aktual
animasi urutan.
Gambar 6.2 menunjukkan apa model akan terlihat seperti diberikan.
Gambar 6.2 Model diberikan dalam posisi awal dengan tekstur, bahan,dan pencahayaan diaktifkan.
Cukup keren ya? Rendering model ini tidak terlalu keras. Ini pada dasarnya melibatkan
gambar jerat satu per satu, pastikan untuk mengatur sesuai
material dan pencahayaan sifat sebelumnya. Seperti biasa, untuk membuat
kode lebih mudah dibaca dan dikonversi ke bahasa lain dan API, saya menggunakan
langsung modus panggilan yang cukup jelas dalam apa yang mereka lakukan.
Hal pertama yang harus dilakukan adalah informasi material. dalam
OpenGL ini dapat dilakukan dengan menggunakan panggilan glMaterialf dan glMaterialfv.
gambar jerat satu per satu, pastikan untuk mengatur sesuai
material dan pencahayaan sifat sebelumnya. Seperti biasa, untuk membuat
kode lebih mudah dibaca dan dikonversi ke bahasa lain dan API, saya menggunakan
langsung modus panggilan yang cukup jelas dalam apa yang mereka lakukan.
Hal pertama yang harus dilakukan adalah informasi material. dalam
OpenGL ini dapat dilakukan dengan menggunakan panggilan glMaterialf dan glMaterialfv.
for(int x = 0; x < m_usNumMeshes; x++)
{
//Set up materials
if(m_pMeshes[x].m_cMaterial >= 0)
{
SMs3dMaterial * pCurMat = &m_pMaterials[m_pMeshes[x].m_cMaterial];
//Set the alpha for transparency
pCurMat->m_fDiffuse[3] = pCurMat->m_fTransparency;
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pCurMat->m_fAmbient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pCurMat->m_fDiffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pCurMat->m_fSpecular);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, pCurMat->m_fEmissive);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pCurMat->m_fShininess);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//Texture map
pCurMat->m_Texture.Bind();
}
else
glDisable(GL_BLEND);
Ini sedikit kode harus mudah untuk mengikuti. Karena setiap model
dipecah menjadi jerat, Anda harus loop melalui mereka dan menarik mereka satu
pada suatu waktu. Hal pertama yang harus dilakukan adalah memeriksa untuk melihat apakah mesh tidak
memang memiliki material yang melekat padanya. Jika indeks material mesh
bukan -1, sebuah pointer ke materi yang sesuai diperoleh. sekarang Anda
hampir siap untuk mengirim informasi material ke OpenGL, namun,
Anda harus terlebih dahulu mengurus transparansi. Hal ini dilakukan dengan mengambil
variabel transparansi dan menggunakannya untuk menggantikan nilai alpha dari
menyebar properti. Setelah itu selesai operasi sederhana, Anda dapat mengatur sifat material. Setiap properti kecuali shininess menggunakan
glMaterialfv. Hal ini karena semua nilai-nilai lain adalah array dari nilai-nilai,
sedangkan shininess hanyalah sebuah mengapung tunggal.
Bagian berikutnya hanya menyala pencampuran dan set yang sesuai
blending mode. Hal ini menjamin transparansi yang akan bekerja dengan benar dan
akan meminimalkan artefak visual lucu.
Terakhir, menggunakan kelas CImage yang Anda gunakan untuk memuat kulitsebelumnya, Anda
mengikat tekstur untuk mesh.
Jika tidak ada bahan untuk kelompok, Anda harus memastikan untuk mematikan
pencampuran. Kegagalan untuk melakukannya dapat menyebabkan artefak visual yang sangat aneh dan
yang tidak diinginkan grafis Glitches. Anda juga dapat menggunakan glMaterial untuk mengatur
bahan kembali ke default. Nilai default untuk ambient, diffuse,
specular, bahan memancarkan adalah (0,2, 0,2, 0,2, 1,0), (0.8,0.8,0.8,1.0),
(0.0,0.0,0.0,1.0), dan (0.0,0.0,0.0,1.0), masing-masing. shininess yang
material juga set ke 0.
Kode yang mengirim simpul ke sistem rendering berikut:
dipecah menjadi jerat, Anda harus loop melalui mereka dan menarik mereka satu
pada suatu waktu. Hal pertama yang harus dilakukan adalah memeriksa untuk melihat apakah mesh tidak
memang memiliki material yang melekat padanya. Jika indeks material mesh
bukan -1, sebuah pointer ke materi yang sesuai diperoleh. sekarang Anda
hampir siap untuk mengirim informasi material ke OpenGL, namun,
Anda harus terlebih dahulu mengurus transparansi. Hal ini dilakukan dengan mengambil
variabel transparansi dan menggunakannya untuk menggantikan nilai alpha dari
menyebar properti. Setelah itu selesai operasi sederhana, Anda dapat mengatur sifat material. Setiap properti kecuali shininess menggunakan
glMaterialfv. Hal ini karena semua nilai-nilai lain adalah array dari nilai-nilai,
sedangkan shininess hanyalah sebuah mengapung tunggal.
Bagian berikutnya hanya menyala pencampuran dan set yang sesuai
blending mode. Hal ini menjamin transparansi yang akan bekerja dengan benar dan
akan meminimalkan artefak visual lucu.
Terakhir, menggunakan kelas CImage yang Anda gunakan untuk memuat kulitsebelumnya, Anda
mengikat tekstur untuk mesh.
Jika tidak ada bahan untuk kelompok, Anda harus memastikan untuk mematikan
pencampuran. Kegagalan untuk melakukannya dapat menyebabkan artefak visual yang sangat aneh dan
yang tidak diinginkan grafis Glitches. Anda juga dapat menggunakan glMaterial untuk mengatur
bahan kembali ke default. Nilai default untuk ambient, diffuse,
specular, bahan memancarkan adalah (0,2, 0,2, 0,2, 1,0), (0.8,0.8,0.8,1.0),
(0.0,0.0,0.0,1.0), dan (0.0,0.0,0.0,1.0), masing-masing. shininess yang
material juga set ke 0.
Kode yang mengirim simpul ke sistem rendering berikut:
//Draw mesh
glBegin(GL_TRIANGLES);
for(int y = 0; y < m_pMeshes[x].m_usNumTris; y++)
{
//Get a pointer to the current triangle
SMs3dTriangle * pCurTri = &m_pTriangles[m_pMeshes[x].m_uspIndices[y]];
//Send the normal
glNormal3fv(pCurTri->m_vNormals[0].Get());
//Send texture coords
glTexCoord2f(pCurTri->m_fTexCoords[0][0], pCurTri->m_fTexCoords[1][0]);
//Send vertex position
glVertex3fv(m_pVertices[pCurTri->m_usVertIndices[0]].m_vVert.Get());
glNormal3fv(pCurTri->m_vNormals[1].Get());
glTexCoord2f(pCurTri->m_fTexCoords[0][1], pCurTri->m_fTexCoords[1][1]);
glVertex3fv(m_pVertices[pCurTri->m_usVertIndices[1]].m_vVert.Get());
glNormal3fv(pCurTri->m_vNormals[2].Get());
glTexCoord2f(pCurTri->m_fTexCoords[0][2], pCurTri->m_fTexCoords[1][2]);
glVertex3fv(m_pVertices[pCurTri->m_usVertIndices[2]].m_vVert.Get());
}
glEnd();
}
}
Sama seperti Anda loop melalui jaring masing-masing, Anda harus loop melalui masing-masing segitiga dalam jaring itu sendiri. Setelah mendapatkan pointer ke yang sesuai
struktur segitiga dalam array wajah, Anda dapat mengirim normalvektor, koordinat tekstur, dan posisi vertex ke OpenGL. ini harus dilakukan tiga kali untuk setiap segitiga dan sekali untuk setiap vertex.
struktur segitiga dalam array wajah, Anda dapat mengirim normalvektor, koordinat tekstur, dan posisi vertex ke OpenGL. ini harus dilakukan tiga kali untuk setiap segitiga dan sekali untuk setiap vertex.
Animasi
Ini adalah bagian Anda memiliki semua telah menunggu. Bagian pertama yang menggunakan
apa yang Anda pelajari pada Bab 5, "Pengantar Animasi rangka"
(Anda tidak membaca yang tidak Anda?)
Hal pertama yang harus Anda lakukan adalah menyelesaikan loading model. sisanya
bagian dari model berisi (tulang) Data gabungan, termasuk mereka
nama, posisi awal, dan keyframes.
Seperti bagian lain dari file ms3d, struktur sendi didahului
oleh sebuah integer dua-byte yang memberitahu berapa banyak sendi file berisi. lalu
datang data. Sendi dan struktur yang menyertainya cukup
rumit, jadi beruang dengan saya.
Pertama, struktur sederhana. Struktur ini digunakan untuk menyimpan rotasi
dan terjemahan dari keyframes:
apa yang Anda pelajari pada Bab 5, "Pengantar Animasi rangka"
(Anda tidak membaca yang tidak Anda?)
Hal pertama yang harus Anda lakukan adalah menyelesaikan loading model. sisanya
bagian dari model berisi (tulang) Data gabungan, termasuk mereka
nama, posisi awal, dan keyframes.
Seperti bagian lain dari file ms3d, struktur sendi didahului
oleh sebuah integer dua-byte yang memberitahu berapa banyak sendi file berisi. lalu
datang data. Sendi dan struktur yang menyertainya cukup
rumit, jadi beruang dengan saya.
Pertama, struktur sederhana. Struktur ini digunakan untuk menyimpan rotasi
dan terjemahan dari keyframes:
//------------------------------------------------------
//- SMs3dKeyFrame
//- Rotation/Translation information for joints
struct SMs3dKeyFrame
{
float m_fTime;
float m_fParam[3];
};
Ini adalah struktur yang cukup sederhana. Struktur keyframe menyimpan
satu "titik berhenti" atau tengara model. Variabel pertama, fTime,
menyimpan waktu dalam detik keyframe ini akan digunakan untuk mengatur
posisi sendi tertentu. Variabel kedua adalah array dari tiga
mengapung. Mereka menyimpan rotasi di sekitar X, Y, dan sumbu Z, atau mereka mengandung X, Y, dan Z penerjemahan nilai-nilai. Bersama masing-masing berisi satu set
keyframes ini untuk kedua rotasi dan nilai-nilai penerjemahan.
Sekarang, pada struktur sendi. Ini adalah struktur yang cukup besar yang berisi
banyak data. Beberapa itu harus diambil dari file; beberapa itu
diciptakan dari nilai-nilai ini bukannya dimuat langsung dari file tersebut.
Di sini sekali lagi, struktur dalam kode bervariasi dari struktur dalam
file. File berisi bendera editor, nama sendi, sendi orang tua
nama, posisi dan rotasi awal, jumlah keyframes untuk
rotasi dan translasi, dan terjemahan aktual dan rotasi
keyframes. Seperti kasus kembali dalam struktur kelompok bila Anda
dibutuhkan untuk menggunakan sebuah array dinamis dialokasikan untuk menahanindeks, Anda
perlu mengalokasikan memori untuk keyframes sebelum membaca mereka masuk
Brace sendiri. Berikut adalah kode untuk SMs3dJoint, struktur sendi.
keyframes ini untuk kedua rotasi dan nilai-nilai penerjemahan.
Sekarang, pada struktur sendi. Ini adalah struktur yang cukup besar yang berisi
banyak data. Beberapa itu harus diambil dari file; beberapa itu
diciptakan dari nilai-nilai ini bukannya dimuat langsung dari file tersebut.
Di sini sekali lagi, struktur dalam kode bervariasi dari struktur dalam
file. File berisi bendera editor, nama sendi, sendi orang tua
nama, posisi dan rotasi awal, jumlah keyframes untuk
rotasi dan translasi, dan terjemahan aktual dan rotasi
keyframes. Seperti kasus kembali dalam struktur kelompok bila Anda
dibutuhkan untuk menggunakan sebuah array dinamis dialokasikan untuk menahanindeks, Anda
perlu mengalokasikan memori untuk keyframes sebelum membaca mereka masuk
Brace sendiri. Berikut adalah kode untuk SMs3dJoint, struktur sendi.
//------------------------------------------------------
//- SMs3dJoint
//- Bone Joints for animation
struct SMs3dJoint
{
//Data from file
unsigned char m_ucpFlags; //Editor flags
char m_cName[32]; //Bone name
char m_cParent[32]; //Parent name
float m_fRotation[3]; //Starting rotation
float m_fPosition[3]; //Starting position
unsigned short m_usNumRotFrames; //Number of rotation frames
unsigned short m_usNumTransFrames; //Number of translation frames
SMs3dKeyFrame * m_RotKeyFrames; //Rotation keyframes
SMs3dKeyFrame * m_TransKeyFrames; //Translation keyframes
//Data not loaded from file
short m_sParent; //Parent joint index
CMatrix4X4 m_matLocal;
CMatrix4X4 m_matAbs;
CMatrix4X4 m_matFinal;
unsigned short m_usCurRotFrame;
unsigned short m_usCurTransFrame;
};
Mari kita berjalan melalui langkah demi langkah. Seperti semua struktur data dari
MS3D, yang satu ini memiliki nilai satu byte dari bendera editor yang dapat diabaikan.
Setelah itu ada dua karakter 32-string. string ini
terus nama sendi dan nama orang tua sendi itu. Kemudian, Anda akan
pertandingan setiap sendi ke induknya sehingga Anda tidak harus membandingkansemua
string setiap kali posisi sendi perlu menghitung ulang.
Berikutnya datang rotasi awal dan terjemahan sendi. enam
nilai-nilai tiga untuk rotasi dan tiga untuk terjemahan-memberikan awal
posisi sendi.
Lalu datang dua dua-byte bilangan bulat, satu untuk jumlah rotasi
keyframes dan satu untuk jumlah keyframes penerjemahan. ini adalah
digunakan untuk mengalokasikan memori untuk dua variabel berikutnya yang terus aktual
rotasi dan translasi data, masing-masing.
Setelah mengalokasikan memori, Anda dapat membaca data dari file ke dalam
yang baru dibuat array. Ini adalah semua data yang perlu datang dari
file. Anda dapat menutup file dan menyingkirkan setiap buffer sementara yang anda
mungkin telah menciptakan atau dialokasikan.
MS3D, yang satu ini memiliki nilai satu byte dari bendera editor yang dapat diabaikan.
Setelah itu ada dua karakter 32-string. string ini
terus nama sendi dan nama orang tua sendi itu. Kemudian, Anda akan
pertandingan setiap sendi ke induknya sehingga Anda tidak harus membandingkansemua
string setiap kali posisi sendi perlu menghitung ulang.
Berikutnya datang rotasi awal dan terjemahan sendi. enam
nilai-nilai tiga untuk rotasi dan tiga untuk terjemahan-memberikan awal
posisi sendi.
Lalu datang dua dua-byte bilangan bulat, satu untuk jumlah rotasi
keyframes dan satu untuk jumlah keyframes penerjemahan. ini adalah
digunakan untuk mengalokasikan memori untuk dua variabel berikutnya yang terus aktual
rotasi dan translasi data, masing-masing.
Setelah mengalokasikan memori, Anda dapat membaca data dari file ke dalam
yang baru dibuat array. Ini adalah semua data yang perlu datang dari
file. Anda dapat menutup file dan menyingkirkan setiap buffer sementara yang anda
mungkin telah menciptakan atau dialokasikan.
Menemukan Orangtua Hilang
Setiap sendi menyimpan nama induknya. Meskipun nama ini dapat
digunakan, itu adalah sakit untuk menjalankan banyak dari string membandingkan operasi dan pencarian
melalui setiap bersama untuk menemukan yang tepat. Lebih baik bahwa perbandingan ini
dilakukan hanya sekali, selama pemuatan model. itu
mana variabel m_sParent masuk Variabel ini merupakan indeks ke dalam
array sendi. Pada indeks ini adalah orangtua sendi saat ini. Jika sendi adalah
bersama akar, yang berarti telah ada orang tua, ini diatur dengan -1 untuk menghindari kebingungan.
Untuk menghitung variabel ini untuk bersama saat ini, Anda mulai dari awal
dari array dan loop melalui sendi sampai salah satu dari sendi
nama sesuai dengan nama orang tua sendi saat ini ini. Pastikan untuk menentukan
apakah nama orang tua yang kosong pertama. Sebuah nama kosong orangtua
menandakan bahwa bersama telah tidak ada orang tua, dan itu akan membuang-buang waktu untuk
mencari satu.
Persiapan Awal
Sebelum Anda dapat mulai menjiwai, Anda harus melakukan setup sedikit lebih. Setiap
matriks sendi harus diatur dengan rotasi awal dan terjemahan, dan
simpul dan normals menempel pada tulang masing-masing harus diubah
oleh matriks. Lihatlah di CMs3d:: Setup () fungsi. (Karena
fungsi ini terlalu besar untuk dipajang di sini, Anda mungkin ingin merujuk pada
kode. CMs3d:: Setup dapat ditemukan di direktori Code/Chapter6 di
ms3d.cpp. Fungsi dimulai sekitar garis 490.)
Fungsi Pengaturan terdiri dari tiga bagian. Bagian pertama loop melalui
sendi dan menciptakan matriks. Matriks pertama dibuat adalah
relatif matriks, disimpan dalam variabel m_matLocal. Ini adalah rotasi
dan terjemahan dari sendi dengan sendirinya. Menggunakan m_fRotation dan
m_fTranslation array dalam sendi, matriks m_matLocal dapat dibuat
dengan SetRotation dan fungsi SetTranslation anggota
matriks kelas.
Berikutnya datang matriks mutlak. Matriks mutlak hanyalah
relatif matriks (m_matLocal) dikalikan dengan relatif tua itu sendi
matriks. Matriks yang dihasilkan disimpan dalam variabel m_matAbs. Jika
bersama telah tua tidak, matriks mutlak adalah sama dengan relatif atau
lokal matriks.
Matriks variabel ketiga (m_matFinal) adalah transformasi akhir
matriks yang digunakan selama animasi. Untuk saat ini, hanya dapat diatur untuk sama
nilai sebagai matriks mutlak.
Bagian kedua dari fungsi pengaturan melibatkan mengubah
simpul ke dalam apa yang saya sebut "animasi posisi awal". Awal
Posisi animasi hanya posisi diasumsikan oleh model saat
sendi-set menggunakan rotasi menatap dan nilai-terjemahan
nilai-untuk m_fRotation dan m_fTranslation setiap sendi. Posisi ini
mungkin tidak termasuk dalam animasi pertama.
Untuk melakukan transformasi ini, semua yang perlu Anda lakukan adalah mengambil final
matriks dari sendi verteks saat ini terpasang. Simpul
harus diubah oleh invers dari matriks ini. Invers dari
matriks rotasi akan memutar arah berlawanan dari matriks awal. Ini
dapat dilakukan dengan menggunakan fungsi InvRotateVec dan InvTranslateVec
termasuk dalam kelas matriks (matrix.inl).
Terakhir dari semua, Anda harus mengatur normals lanjut untuk memastikan pencahayaan yang tepat.
Karena normals disimpan dalam struktur wajah, Anda mempunyai loop melalui wajah bukan simpul. Untuk mendapatkan yang sesuai
matriks untuk mengubah normals, Anda harus menggunakan indeks titik
struktur wajah untuk mengambil sendi dan matriks digunakan untuk mengubah
puncak, yang normal milik. Setelah Anda memiliki matriks ini,
normal bisa diputar. Seperti simpul, Anda menggunakan InvRotateVec
fungsi. Namun, karena normals pencahayaan vektor-vektor satuan, ada
tidak perlu untuk menerjemahkan mereka juga.
Sekarang, semuanya sudah diatur dan siap untuk menghidupkan.
Animasi dan Interpolasi
Akhirnya Anda sampai ke hal yang baik. Semua simpul di kanan mulai
tempat, semua sendi ditetapkan, dan semua data lainnya dimuat dan
diambil dari perawatan. Sekarang Anda dapat menghidupkan model.
Fungsi Animate dari kelas CMs3d mengambil empat parameter. yang pertama
adalah kecepatan. Kecepatan adalah nilai floating-point yang menetapkan bagaimana
cepat model harus bernyawa. Nilai 1.0f berarti model
harus menghidupkan persis secepat itu dimaksudkan untuk ketika diciptakan. Sebuah
nilai 2.0f akan menghidupkan dua kali lebih cepat seperti aslinya, dan 0.5f akan
Model menghidupkan setengah secepat asli dan sebagainya.
Dua berikutnya parameter, fStartTime dan fEndTime, memberitahu fungsi
bagian mana dari animasi untuk digunakan. Karena setiap sendi dapat memiliki
berbagai jumlah keyframes dan keyframes dari gabungan tidak perlu
terjadi pada interval waktu yang sama, adalah tidak praktis untuk menggunakan memulai dan
akhir keyframe. Para fStartTime dan fEndTime memberikan awal dan akhir
saat segmen animasi. Jika fStartTime adalah 0.3f dan fEndTime adalah 0.9f,
hanya enam persepuluh senilai kedua dari animasi antara dua
kali diambil. Karena tidak ada di file MS3D yang memberitahu Anda
mana animasi terpisah mulai dan berhenti, Anda mungkin merekam memulai
dan waktu akhir dari animasi tertentu. Sebagai contoh, 0,0-0,5 detik
berisi animasi berjalan dan 0,5-1,3 berisi animasi melompat. dengan
memasukkan nilai-nilai ini ke dalam fungsi Animate (), Anda dapat menampilkan hanya
atau hanya menjalankan animasi melompat.
Sekarang Anda perlu menentukan apa bagian dari animasi untuk menampilkan
pada saat saat ini. Tidaklah cukup untuk hanya memilih keyframe dengan
waktu paling dekat dengan waktu saat ini. Melakukan hal ini akan menghasilkan dendeng,
gerak wajar karena jarak antara keyframes dapat
besar dan waktu yang dibutuhkan untuk berpindah dari satu ke yang lain dapat cukup panjang. Sebuah keyframe mungkin hanya ada di awal dan akhir kaki
gerakan, dan hanya bergantian antara dua frame untuk mewakili
berjalan akan terlihat mengerikan. Sebaliknya, Anda perlu interpolasi antara
keyframes.
Menggunakan kelas Timer disertakan dengan kelas CMs3d, Anda mendapatkan waktu
yang telah berlalu dalam hitungan detik sejak frame terakhir. Menambahkan waktu ini untuk
waktu berlalu sejak terakhir kali animasi dimulai kembali, dan
menambahkan hasilnya ke awal waktu yang ditentukan dalam parameter
fungsi harus memberikan nilai waktu untuk digunakan saat menemukan
posisi model.
Berbekal nilai waktu, Anda dapat menemukan "saat ini" dan "sebelumnya"
frame untuk setiap frame. Nilai waktu Anda dihitung dalam sebelumnya
langkah harus jatuh di suatu tempat antara keyframe sebelumnya dan saat ini,
tanpa keyframes di antara mereka. Jika waktu jatuh antara frame 5 dan
6 bingkai, frame sebelumnya adalah 5 dan frame adalah 6.
Mari kita mulai dengan terjemahannya.
Terjemahan
Hal pertama yang Anda lakukan adalah menemukan keyframe saat ini. Hal ini dilakukan dengan menggunakan
loop untuk kenaikan konter bingkai, mulai dari 0, sementara ada
masih keyframes terjemahan, dan sementara nilai waktu untuk itu saat ini
keyframe kurang dari waktu Anda berlalu dihitung.
Ada tiga kemungkinan nilai counter frame, sebagai
berikut:
■ Yang pertama adalah 0. Ini berarti bahwa Anda hanya perlu menyalin
terjemahan nilai untuk terjemahan pertama keyframe ke
variabel temporer memegang terjemahan saat ini.
■ Kemungkinan kedua untuk counter frame yang memegang
jumlah keyframe terakhir. Dalam hal ini, Anda lakukan seperti yang Anda lakukan untuk 0,
kecuali Anda menggunakan frame terakhir dalam array keyframes penerjemahan.
■ Nilai akhir dan kemungkinan besar adalah jumlah keyframe di
tengah dari dua ekstrem. Jika ini terjadi, Anda perlu interpolasi
antara nilai ini terjemahan dari keyframe dan
terjemahan nilai sebelumnya keyframe (framecounter - 1).
Jadi bagaimana Anda melakukan ini? Ingat interpolasi linier dari ketika
Anda bekerja dengan format seperti md2? Konsep yang sama berlaku di sini.
Yang Anda butuhkan adalah perubahan waktu antara dua frame (At). menggunakan
nilai ini, Anda dapat menghitung nilai interpolasi untuk digunakan ketika Anda
melakukan interpolasi linier. Hal ini dilakukan dengan menggunakan rumus berikut:
(CurrentTime – TimeOfPrevFrame)/ Δt
Anda mengurangi waktu dari frame sebelumnya dari arus
waktu dan kemudian membaginya dengan perubahan waktu antara dua frame.
Menggunakan nilai yang dihasilkan, Anda dapat dengan mudah Anda interpolasi antara X, Y,
dan Z penerjemahan nilai-nilai.
Rotasi
Sekarang untuk rotasi. Rotasi hampir proses yang sama. arus
dan frame sebelumnya dihitung dengan cara yang sama, dan Anda ikuti
hampir proses yang sama jika model pada ekstrem. Jika model ini
di salah satu ekstrem, yang berarti keyframe saat ini adalah sama dengan
pertama atau yang terakhir keyframe model, rotasi nilai-nilai
keyframe terakhir ditempatkan dalam matriks sementara menggunakan SetRotation yang
fungsi dari kelas CMatrix4X4.
Perbedaan utama muncul ketika Anda perlu interpolasi antara
rotasi. Meskipun Anda bisa menggunakan metode yang sama seperti yang Anda lakukan untuk
terjemahan, metode yang menyenangkan jauh lebih baik dan lebih grafis
melibatkan menggunakan quaternions. Jika Anda melompati Bab 2, sekarang
menjadi saat yang baik untuk membaca (atau baca ulang) itu.
Karena model toko sudut rotasi sebagai tiga sudut Euler,
Hal pertama yang dilakukan adalah membuat dua quaternions menggunakan fungsi FromEulers.
Anda perlu membuat dua quaternions terpisah dari saat ini dan
sebelumnya rotasi keyframes. Kedua quaternions mewakili
rotasi dari masing-masing frame. Sekarang, Anda harus menghitung angka empat yang
yang mewakili rotasi pada posisi saat ini. setelah
interpolasi nilai dihitung (hal itu dilakukan dengan cara yang sama seperti untuk terjemahan),
yang quaternions dapat dimasukkan ke dalam fungsi SLERP, bersama dengan
nilai interpolasi, untuk membuat angka empat baru yang berisi
benar rotasi untuk waktu sekarang.
Karena OpenGL menggunakan matriks, perlu untuk mengkonversi angka empat tersebut.
Sebuah angka empat kemudian dapat berubah kembali menjadi matriks menggunakan
ToMatrix4 fungsi dari kelas CQuaternion.
Setelah matriks rotasi dibangun, Anda perlu menambahkan terjemahan untuk itu.
Menggunakan matriks suhu yang sama yang digunakan untuk rotasi, Anda dapat menghubungi SetTranslation fungsi dengan penerjemahan nilai-nilai Anda dihitung
sebelumnya untuk menyelesaikannya. Hanya ada satu langkah tersisa sebelum Anda dapat mulai menggambar model lagi. Untuk menemukan matriks akhir sendi, Anda perlu mengalikan sendi lokal matriks (m_matLocal) dengan matriks sementara yang anda buat. Ini Hasilnya dapat disimpan dalam m_matFinal, dan merupakan matriks yang akan digunakan untuk mengubah simpul dan normals kemudian.
Jika Anda ingin, Anda sebenarnya dapat menarik tulang saja sekarang. X, Y, dan Posisi Z tulang disimpan dalam elemen 12, 13, dan 14 joint akhir matriks. Menggambar garis dari titik ini untuk elemen 12, 13, dan 14 dari matriks akhir induk sendi akan memberikan Anda sebuah animasi "Kerangka" untuk model, seperti Gambar 6.3 menunjukkan. Gambar 6.3
Gambar 6.3 Kerangka animasi dari model semua dengan sendirinya.
Sekarang bahwa Anda memiliki informasi patungan baru, Anda harus mengubah semua simpul dan simpul normals. Untuk kejelasan, semua kode untuk mengubah dan menampilkan model dalam Cmas3d:: RenderT () fungsi. Fungsi ini disebut matriks langsung setelah final bagi semua sendi dihitung. Seperti dengan fungsi membuat lama, jerat diambil satu per satu. Untuk mesh setiap bahan yang pertama didirikan. Kemudian, Anda pindah ke mengubah simpul dan normals. Anda dapat memulai render yang tepat
pergi. Seperti wajah-wajah yang dilingkarkan melalui, orang-orang yang tidak memiliki tulang terlampir diambil seperti biasa, tanpa modifikasi apapun.
Sisanya dari mereka, di sisi lain, adalah cerita yang berbeda. Sebuah sementara
vektor dibentuk untuk memegang kedua normal baru dan simpul baru. pertama
normal harus diambil dari perawatan. Karena matriks transformasi
benar-benar memodifikasi vektor yang diberikan kepada mereka, hal pertama yang dilakukan adalah
menempatkan normal saat ini menjadi pemegang sementara. setelah menentukan
yang bersama vertex normal terpasang, Anda dapat menggunakan Transform3 yang
fungsi untuk memodifikasi normal sementara, hanya menggunakan rotasi
bagian dari matriks.
Simpul dapat diubah dalam banyak cara yang sama. Satu-satunya utama
Perbedaannya adalah bahwa fungsi Transform4 harus digunakan, bukan
dari Transform3. Transform4 menambahkan terjemahan dalam juga, memberikan
titik posisi yang tepat.
Semuanya sekarang dapat dikirim ke API render. Jangan lupa untuk mengirimkan
koordinat tekstur serta normal dan titik-pastikan
mereka dikirim dalam urutan yang benar!
Gambar 6.4 menunjukkan model dalam kemuliaan menghidupkan penuh.
Gambar 6.4 Model berubah pada pertengahan animasi. Perhatikan tulang yang telah
telah ditarik dari atas sebagai referensi.
Seperti yang Anda perhatikan pada Gambar 6.4, Anda dapat melihat tulang-tulang di bagian atas model. Dalam demo ini dilakukan dengan menonaktifkan pengujian kedalaman dan menggambar tulang menggunakan teknik yang diajukan sebelumnya. Hanya pastikan anda mengaktifkan kembali pengujian mendalam ketika Anda selesai.
Nah, di sana Anda memilikinya. Anda sekarang dapat memuat format poligon rendah, yang adalah favorit pribadi saya. (Sebuah format poligon rendah berarti bahwa
menghasilkan model dengan jumlah poligon rendah. Karena Anda berurusan
dengan permainan yang harus diberikan secara real-time, model poligon rendah
adalah suatu keharusan. Menggunakan model poligon yang tinggi akan memperlambat permainan Anda ke titik yang tidak dapat dimainkan.) Saya pasti menyarankan memeriksa editor dan format tahu apakah Anda adalah pengembangan independen atau uang-kekurangan tim. Hal ini sangat bagus, dan datang pada harga yang besar.
Semoga Anda menikmati bab ini. Ingat, jika Anda memiliki komentar,
pertanyaan, atau saran, jangan ragu untuk menghubungi saya di evan@codershq.com.
Kesimpulan
Bab ini menandakan selesainya pertama Anda skeletally animasi format. Anda sekarang harus dapat memuat dan menggunakan format yang sangat berguna bahwa hampir tampaknya dibuat hanya untuk permainan. Anda telah belajar bagaimana menghidupkan tulang oleh interpolasi antara rotasi dan translasi. Anda juga telah melihat penggunaan paling umum dari quaternions dalam 3D permainan pemrograman, disisipkan antara rotasi. Akhirnya, bab ini mengikat semua bersama-sama untuk menciptakan MS3D sepenuhnya bekerja loader, lengkap dengan animasi kerangka. Pastikan untuk memeriksa
Milkshape di situs di http://www.milkshape3d.com dan check out demo
Milkshape 3D di CD (dalam direktori Software/MilkShape3d). Bab berikutnya mencakup salah satu format yang paling populer, 3DS. Anda akan belajar bagaimana untuk memuat dan membuat format ini cukup rumit. Anda akan belajar bagaimana menggunakan "potongan-berbasis" format. Anda juga akan menangkap sekilas dari beberapa informasi yang bisa Anda simpan di file.
Bab 7
Milkshape
3d
Saat aku menelusuri berbagai forum internet, baca mailing list, dan bahkan hanya berbicara dengan berbagai orang tentang model 3Ds, format ini tampaknya datang banyak. Pertanyaan "Bagaimana cara memuat model 3ds?" Dan "Bagaimana saya menggunakan model 3ds dalam program saya "muncul di semua tempat? Sayangnya, sampai sekarang, tidak ada referensi yang sangat bagus ini format yang tersedia. Referensi luar sana jatuh ke salah satu dari tiga kategori: perpustakaan yang menawarkan Anda kontrol sedikit lebih dari apa yang Anda bisa lakukan, kode dan program yang tidak mungkin untuk menguraikan, dan membingungkan, dokumen yang sangat teknis. Meskipun tulisan ini tidak berarti meliputi semuanya harus dilakukan dengan format 3ds, tujuan saya di sini adalah untuk Anda mulai dalam arah yang benar. Bab ini memandu Anda melalui loading dan menampilkan model 3d diekspor langsung dari format 3ds Autodesk (http://www.autodesk.com). Ada ton informasi yang terkandung dalam file 3ds. Bab ini
ekstrak data yang akan paling berguna untuk Anda dalam permainan Anda atau lainnya
Program 3D. Data ini mencakup data mesh dan informasi material, mengabaikan bendera editor, lampu, dan potongan lainnya. Namun, jika Anda lakukan merasa membutuhkan tambahan ini, ini masalah sederhana untuk menambahkan mereka setelah Anda mendapatkan melalui bab ini.
Memahami Chunky 3ds File
Format dari file 3ds sudah diatur dalam cara yang sangat menarik. setiap bagian dari file sendiri "sepotong". Potongan masing-masing berisi identifier, yang panjang, dan sekelompok byte yang menyimpan data untuk sepotong itu. Meskipun file lain memiliki potongan, mereka baik termasuk satu header memberitahu Anda di mana Anda akan menemukan potongan masing-masing atau memiliki potongan mengatur
dalam urutan tertentu. Sebuah file 3ds tidak melakukan keduanya ini. File-file 3ds dapat
memiliki potongan dalam urutan apapun dan tidak ada header yang memberitahu Anda di mana mereka berada di file atau bahkan berapa banyak dari setiap potongan yang ada.
Ide ini ditampilkan dalam Gambar 7.1, yang mengilustrasikan sebuah file tunggal, terbuat dari beberapa potongan up.
Gambar 7.1 Sebuah file yang terbuat dari potongan data.
Ini bagus dan keren sampai Anda melihat sedikit lebih mendalam. Setup dari sebuah file 3ds bisa sangat membingungkan pada pandangan pertama. Tidak hanya itu dibagi menjadi potongan kecil banyak, tapi masing-masing potongan yang lebih kecil dapat memiliki sub-potongan, yang mungkin bahkan lebih sub-potongan. Terburuk dari semua, untuk sebagian besar, potongan tidak perlu dalam urutan tertentu dalam file.
Struktur dari file 3ds diatur lebih seperti Gambar 7.2, bukan format yang ditampilkan pada Gambar 7.1. Gambar 7.1 Sebuah file yang terbuat dari potongan data.
Gambar 7.2 Sebuah representasi grafis dari sebuah file dengan menggunakan struktur yang mirip dengan 3ds.
Jadi bagaimana Anda menangani itu? Cara termudah saya telah menemukan adalah untuk pertama baca di header chunk tersebut. Setiap potongan dalam file 3ds memiliki enam byte header yang memegang identifier dan panjang potongan tersebut. Kemudian, berdasarkan jenis potongan dan panjang, bagian tertentu dari program ini dapat
dipanggil untuk memanipulasi dan memproses data. Potongan dapat mewakili
jerat atau segitiga dari sebuah objek, sedangkan sub-potongan potongan ini terus
simpul, koordinat tekstur, dan informasi material untuk mesh itu. Potongan lainnya dapat menahan bahan dengan sub-potongan untuk menyebar, specular, dan bahan ambien, dengan sub-potongan untuk setiap warna. Ada banyak manfaat untuk menangani dengan cara ini. Pertama-tama, hal itu memungkinkan Anda hanya proses potongan tertentu. Jika sepotong berisi data yang tidak dibutuhkan, Anda hanya dapat membaca benar melewatinya. Dalam 3ds file data, untuk misalnya, Anda mungkin akan ingin melewatkan potongan yang berisi data yang relevan dengan editor. Kedua, itu membuat sangat mudah untuk bekerja dengan file seperti model 3ds mana potongan tidak dalam urutan yang ditetapkan. Karena header dibaca pertama, jenis potongan dapat ditentukan,
dan tindakan tepat dapat diambil oleh kode. Terakhir, pendekatan ini mengarah pada program yang sangat modular. Jika Anda perlu mengubah spesifikasi sepotong atau Anda menambahkan kode untuk potongan baru, itu adalah proses yang cukup sederhana. Hal ini sangat mudah untuk menemukan bagian yang membahas dengan bagian-bagian tertentu dari kode. Menambahkan kode untuk menangani jenis baru hanya memerlukan memaku potongan kode tambahan ke akhir. Ini contoh program menggunakan vektor STL cukup sedikit. Jika Anda membutuhkan informasi pada vektor, periksa Lampiran B, "STL Primer Vector," untuk singkat pengenalan atau penyegaran.
Mulai 3ds Chunk Header
Setiap potongan dalam file 3ds dimulai dengan struktur S3dsChunkHeader.
//----------------------------------------------
//- S3dsChunkHeader
//- Header for each chunk
struct S3dsChunkHeader
{
unsigned short m_usID;
unsigned int m_uiLength;
};
Variabel pertama adalah identifier potongan dua-byte. Ini memberitahu Anda apa yang
Jenis data sepotong berisi. Hal ini dapat berupa data titik, data wajah, animasi data, data informasi bahkan tidak berguna. Variabel panjang adalah panjang sepotong keseluruhan, termasuk header. Hal ini dapat sedikit menyesatkan namun; nilai panjang meliputi potongan saat ini ditambah panjang dari semua sub-potongan mengandung, jadi hati-hati. Tabel 7.1 daftar ID potongan yang paling umum bersama dengan menggunakan mereka.
File Data 3ds
File 3ds terdiri dari potongan banyak, beberapa yang penting
untuk permainan, banyak yang tidak. Dalam bagian berikut, Anda akan belajar
tentang potongan yang paling penting dalam sebuah file 3ds. Menggunakan potongan
Anda dapat memuat, membuat, dan tekstur file 3ds. Meskipun pertama
potongan dijamin berada di awal file, sisanya mungkin
muncul di mana saja di file tersebut, sebagian besar dari mereka beberapa kali.
Header 0x4Dx4D
Sekarang Anda siap untuk menggali ke dalam file itu sendiri. Enam byte pertama dari
file harus berisi header sepotong utama. ID dari ini header 0x4d4d dan panjang variabel harus berisi total panjang file. Potongan ini berisi semua sub-potongan yang membuat file. Tidak ada tindakan yang diperlukan pada titik ini, Anda hanya dapat bergerak melewati header dan mulai membaca potongan. Setiap 3ds Model hanya berisi satu potongan tersebut.
Chunck 0x0002
Di suatu tempat dalam file, umumnya potongan sub-pertama (potongan 0x4D4D), adalah
potongan ini. Sebanyak 10 byte panjangnya, data terdiri dari fourbyte tunggal
bilangan bulat yang berisi versi format. Ini harus lebih besar dari 3. Versi 3ds Max berbeda dari versi yang lebih baru dalam cara file diatur, yang berarti bahwa program ini tidak mungkin beban mereka dengan benar.
Objek dalam Chunk 0x4000
Potongan 0x4000 berisi data tentang obyek-obyek. Sebuah objek dapat menjadi
segitiga mesh, cahaya, kamera, atau bahkan untuk pengaturan jendela editor. untuk
pemrograman game, biasanya Anda ingin mengabaikan segalanya.
Tabel 7.1 3ds chunks umum dan Menggunakan Gunakan Nomor ID
0x4Dx4D Digunakan pada awal file untuk menandakan bahwa file tersebut adalah file 3ds.
0x0002 Menyimpan nomor versi dari file.
0x4000 "objek" seperti mesh, kamera, atau cahaya.
Setiap potongan 0x4000 berisi sub-potongan dengan verteks,
tekstur koordinat, dan informasi lainnya
0x4100 Sebuah sub-0x4100 potongan 0x4000. Sebuah potongan 0x4100 berisi semua
dibutuhkan untuk membangun sebuah segitiga mesh.
Berisi 0x4110 simpul untuk objek. Ini adalah serangkaian sub-
0x4100.
0x4120 Juga sepotong sub-0x4100, 0x4120 sepotong berisi
Wajah informasi, termasuk indeks titik yang menceritakan
simpul yang membentuk wajah masing-masing.
0x4130 ain sub-sepotong 0x4100, 0x4130 berisi informasi
bahan yang harus diterapkan untuk yang menghadap.
0x4140 Bahkan potongan lain sub-0x4100, bongkahan ini berisi
koordinat tekstur wajah yang memungkinkan untuk menjadi tekstur yang dipetakan.
0xAFFF Definisi material ditemukan di dalam 0xAFFF, warna untuk
ambien, bahan menyebar, dan specular, serta
shininess dan transparansi dalam sepotong ini.
0xA000 sepotong 0xAFFF yang berisi nama material.
0xA010 Sebuah potongan sub-0xAFFF juga, berisi ambien
warna untuk materi.
0xA020 potongan sub-0xAFF, 0xA020 berisi meredakan
warna bahan.
0xA030 keempat sub-sepotong 0xAFF, ini berisi specular
sorot warna untuk bahan tertentu.
0xA040 Namun lain sub-potongan 0xAFFF, kali ini berisi
shininess material.
0xA050 Sekali lagi, sub-sepotong 0xAFFF yang mengontrol seberapa transparan
atau buram suatu material.
0xA200 juga sepotong sub-potongan 0xAFFF. ini
potongan menyimpan nama file dari peta tekstur atau kulit untuk
bahan saat ini.
Loading dan lampu kamera dapat mengganggu dengan bagian lain dari
Anda permainan dan menyebabkan beberapa artefak visual yang tampak sangat aneh. Namun, Anda mungkin ingin melihat ke dalam loading dan menggunakan lampu jika Anda tertarik menggunakan format 3ds untuk format tingkat atau dunia.
Potongan 0x4000 mengandung sedikit data dengan sendirinya, sisanya adalah dibungkus dalam sub-potongan. Ini berisi string diakhiri dengan null yang menyimpan nama mesh. Karena panjang string yang tidak diatur, maka harus null diakhiri. Menggunakan strcpy pada buffer data dapat mengekstrak nama ini. Namun, jika Anda menggunakan pointer sementara untuk bergerak melalui array, pastikan Anda memindahkannya strlen (m_cName) + 1 byte untuk account untuk terminator null pada akhir string. setelah
string adalah membaca, sisa panjang data penuh sub-potongan. Perlu pikiran mungkin ada banyak jerat dalam file yang sama, jadi pastikan untuk menjaga melacak di mana Anda berada.
//pseudocode to begin reading the 0x4000 chunk
if chunkID is 0x4000
read null terminated string (strcpy)
advance pointer past string and null pointer (strlen+1)
Segitiga Mesh 0x4100
Ini adalah dimana data yang Anda inginkan adalah. Sebuah potongan jala segitiga
memegang hanya itu, segitiga yang berisi mesh. Jala adalah sekelompok
poligon yang membentuk permukaan; mesh segitiga adalah sama kecuali
itu terdiri dari segitiga saja. Dalam potongan 0x4100 adalah simpul, wajah,
dan tekstur koordinat, serta informasi material wajah. Sebelum Anda khawatir tentang membaca dalam data ini, Anda butuh tempat untuk menyimpan itu. Itu adalah di mana struktur S3dsMesh masuk Setiap struktur S3dsMesh memegang satu jala segitiga. Karena file tersebut tidak mengungkapkan bagaimana jerat banyak ada total, Anda dapat memanggil kekuatan STL dan std:: vector untuk membuat sebuah tempat untuk menyimpan sejumlah jerat. Berikut adalah struktur mesh:
//------------------------------------------------
//- S3dsMesh
//- Group Mesh Data
struct S3dsMesh
{
char m_cName[256];
vector<S3dsVertex *> m_vVertices;
vector<S3dsTexCoord *> m_vTexCoords;
vector<S3dsFace *> m_vFaces;
vector<S3dsObjMat *> m_vMaterials;
unsigned short m_usNumFaces;
};
Variabel pertama (m_cName) adalah nama mesh. Ini harus ditetapkan saat anda terlebih dahulu masukkan potongan 0x4000, karena di situlah nama string terletak. Anda perlu berhati-hati di sini. String dalam 3ds model terbatas, bisa panjang apapun. Namun, dalam struktur Anda hanya ada 256 byte penyimpanan. Mudah-mudahan, ini akan cukup untuk
tahan salah satu nama mesh, tetapi pastikan untuk memeriksa terlebih dahulu. Jika panjang string dalam file melebihi 256 karakter, Anda akan perlu untuk memotong
sebelum menyimpan. Sisa data yang terkandung di sub-potongan potongan 0x4100.
(Ya, Anda dapat memiliki sub-sub-potongan potongan!) Karena Anda tidak tahu berapa banyak simpul, wajah, atau bahan akan ada, Anda mungkin ingin untuk menggunakan std:: vector. Ini pada dasarnya adalah sebuah array resizable. Menggunakan std:: vektor, Anda dapat menambahkan objek ke array sebanyak sesuka Anda tanpa mengalokasikan atau mengubah ukuran itu. Jika Anda tidak yakin tentang cara menggunakan std:: vektor, saya sarankan anda melompat ke Lampiran B. Pada bagian berikutnya, Anda belajar tentang sub-potongan dari 0x4100 sepotong. Potongan ini menyimpan informasi tentang simpul, koordinat tekstur, wajah, dan bahan. Sebuah objek tertentu dapat memiliki semua ini sub-potongan, atau hanya beberapa. Sebuah objek tidak harus mengandung tekstur mengkoordinasikan dan informasi material.
Simpul 0x4110
Potongan sub-0x4110 dari bongkahan 0x4100 berisi semua simpul informasi untuk mesh. Ini termasuk X, Y, dan Z koordinat untuk setiap vertex. (Untungnya, tidak mengandung tingkat lain subchunks.) Simpul disimpan dalam struktur mesh dalam
m_vVertices vektor array. Setiap vektor hanya tiga floating-point nilai, dibungkus dalam suatu struktur untuk kejelasan. Berikut adalah struktur, sangat sederhana S3dsVertex cukup membosankan. Anda mungkin memperhatikan bahwa kelas berisi tiga titik di sini mengapung, bukan CVector3 kelas seperti simpul MS3D lakukan. Saya memilih untuk melakukan hal ini karena suatu alasan. Karena simpul tidak perlu diubah seperti yang dari MS3D, tidak ada gunanya membebani program anda dengan file tambahan. Dalam hal ini, array sederhana dari tiga mengapung akan melakukannya dengan baik.
/------------------------------------------------
//- S3dsVertex
//- Vertex structure for 3ds models
struct S3dsVertex
{
float m_fVert[3];
};
Dua byte pertama dalam sepotong 0x4110 menentukan jumlah simpul di mesh. Segera setelah kedua byte banyak set floating-point tiga kali lipat-posisi X, Y, dan Z untuk setiap vertex, yang dibaca dan disimpan sebagai struktur S3dsVertex.
Sekarang, jika Anda ingin untuk memeriksa kemajuan Anda, Anda dapat menggunakan rendering fungsi (C3ds:: Render ()) untuk membuat semua simpul sebagai titik. untuk melakukan ini, Anda harus loop melalui jala-jala satu per satu, dan untuk setiap mesh, menggambar setiap sudut sebagai titik dalam ruang.
Wajah 0x4120
Potongan lain yang sangat penting adalah sepotong 0x4120, atau wajah sepotong. Potongan ini berisi indeks titik untuk semua segitiga dalam jala saat ini; sangat penting jika Anda ingin model yang solid. wajah-wajah disimpan dalam struktur mesh saat ini juga. Struktur S3dsFace merupakan struktur cukup sederhana:
//------------------------------------------------
//- S3dsFace
//- Face of a 3ds model
struct S3dsFace
{
unsigned short m_usIndices[3]; //Vertex indices
CVector3 m_vecNormal; //Face Normal
};
Sekarang, Anda harus mendapatkan komponen-komponen wajah keluar dari file dan ke memori. Seperti simpul, dua byte pertama dari sepotong 0x4120 yang didedikasikan untuk memegang jumlah wajah untuk mesh (dalam hal ini,tiga). Setelah itu, ada indeks titik. Ada tiga indeks untuk setiap segitiga, satu untuk setiap sudut. Itu berarti ada tiga kali jumlah indeks wajah. Bagian m_vecNormal dari struktur wajah tidak disimpan dalam file; itu harus dihitung. Hal ini dilakukan dengan menggunakan CalcFaceNormal fungsi didefinisikan dalam model.h. Perhitungan ini akan memberikan Anda vektor satuan tegak lurus terhadap bidang segitiga terletak masuk Nilai ini diperlukan untuk penerangan dan bahan saat rendering. Fungsi CalcFaceNormal menghasilkan normal wajah menggunakan poin segitiga. Pertama, dua vektor yang dibuat dari titik menggunakan titik awal titik pertama dan titik akhir sebagai titik kedua untuk vektor pertama, dan titik ketiga untuk vektor kedua. Salib produk dari vektor-vektor ini kemudian dihitung. Vektor yang dihasilkan tegak lurus segitiga, persis seperti vektor normal. hanya
hal yang tersisa untuk dilakukan adalah menormalkan vektor yang dihasilkan sehingga dapat dikirim ke render API. Normals ini hanya wajah per. Untuk melakukan shading halus Anda akan perlu per-vertex normals. Sebuah titik yang normal dapat dihitung untuk tertentu simpul dengan rata-rata normals menghadapi semua wajah saham bahwa
simpul tertentu. Anda sekarang dapat membuat model sebagai benda padat hanya menggunakan simpul indeks. Anda bahkan dapat mengirim vektor normal Anda dihitung untuk setiap segitiga dan cahaya model juga.
Info Bahan Wajah 0x4130
Namun tingkat lain sub-potongan. Informasi Bahan wajah adalah suatu subchunk
dari sepotong wajah (0x4120). Satu hal baik tentang jala-jala di file 3ds adalah bahwa Anda tidak hanya dapat menempatkan bahan yang berbeda pada yang berbeda jerat seperti Anda bisa ketika menggunakan format 3D Milkshape, namun anda bahkan dapat menempatkan bahan yang berbeda pada bagian yang berbeda yang sama
mesh. Sebagai contoh, sebuah kapal ruang angkasa terbuat dari mesh tunggal mungkin perlu tekstur yang berbeda di atas kapal daripada di bagian bawah. Itu apa potongan 0x4130 adalah semua tentang. Para S3dsObjMat menyimpan informasi yang mendefinisikan muka yang akan ditutupi dengan yang bahan. Potongan 0x4130 hanya berisi indeks ke dalam array materi, itu tidak menyimpan informasi material.
Sekali lagi, ada vektor dari struktur dalam struktur mesh karena fakta bahwa mungkin ada lebih dari satu dari mereka. para S3dsObjMesh struktur berisi indeks ke dalam array bahan (yang Anda akan mendapatkan dalam menit), serta daftar indeks wajah yang menggunakan bahan ini.
//------------------------------------------------
//- S3ds Objmat
//- Structure that holds which faces go to a material
struct S3dsObjMat
{
unsigned short m_usMatIdx;
vector<unsigned short> m_vFaceIndices;
};
Data dalam potongan 0x4130 adalah dalam urutan sebagai berikut:
1. Pertama, Anda melihat string diakhiri dengan null yang berisi nama bahan untuk digunakan. Anda dapat membandingkan string dengan nama bahan diambil dari potongan bahan (0xAFFF, didefinisikan berikutnya). Ketika pertandingan string, Anda dapat mengatur m_uiMatIdx yang variabel ke indeks dari materi itu. Ini akan menghemat banyak waktu selama rendering karena Anda tidak perlu mencari materi yang tepat.
2. Sekarang-akrab dua-byte integer yang memberitahu Anda jumlah wajah yang menggunakan bahan ini di samping.
3. Jumlah dua-byte bilangan bulat, masing-masing mewakili wajah, yang lalu. Setiap nomor indeks ke dalam array wajah. Sebuah "0" sesuai dengan wajah 0, dengan wajah 1 "1", dan seterusnya. Sekarang, saat rendering model,
Anda harus terlebih dahulu mengatur materi sifat-sifat material pertama,
membuat semua wajah yang menggunakan materi, beralih ke yang berikutnya
materi, dan ulangi rendering proses sampai semua wajah diambil.
1. Pertama, Anda melihat string diakhiri dengan null yang berisi nama bahan untuk digunakan. Anda dapat membandingkan string dengan nama bahan diambil dari potongan bahan (0xAFFF, didefinisikan berikutnya). Ketika pertandingan string, Anda dapat mengatur m_uiMatIdx yang variabel ke indeks dari materi itu. Ini akan menghemat banyak waktu selama rendering karena Anda tidak perlu mencari materi yang tepat.
2. Sekarang-akrab dua-byte integer yang memberitahu Anda jumlah wajah yang menggunakan bahan ini di samping.
3. Jumlah dua-byte bilangan bulat, masing-masing mewakili wajah, yang lalu. Setiap nomor indeks ke dalam array wajah. Sebuah "0" sesuai dengan wajah 0, dengan wajah 1 "1", dan seterusnya. Sekarang, saat rendering model,
Anda harus terlebih dahulu mengatur materi sifat-sifat material pertama,
membuat semua wajah yang menggunakan materi, beralih ke yang berikutnya
materi, dan ulangi rendering proses sampai semua wajah diambil.
Tekstur koordinat Chunck 0x4140
Bagian penting terakhir dari mesh dan potongan 0x4000 adalah 0x4140 sub-sepotong. Ini potongan sub-memegang pemetaan u dan v koordinat tekstur untuk setiap vertex mesh. Posisi ini nilai-nilai tekstur pada segitiga. Mereka umumnya berkisar 0,0-1,0, tetapi dapat pergi lebih tinggi jika tekstur yang akan diulang atau keramik pada segitiga. Dua byte pertama dari data memberikan Anda jumlah simpul. Ada kemudian dua floatingpoint
nilai untuk setiap vertex. Ini disimpan dalam struktur S3dsTexCoord, yang mirip dengan struktur S3dsVertex, tetapi berisi hanya dua mengapung
bukan tiga.
Di sana, semua geometri sekarang dimuat. Sebuah lega bukan? Tunggu, yang
hanya setengah pertempuran. Anda masih perlu untuk memuat semua bahan dan tekstur
informasi sehingga Anda dapat membuat model Anda terlihat jauh lebih menarik
dari gambar, putih datar yang diarsir.
Bahan 0xAFFF
Untuk membuat model Anda lebih menarik dan memberikan warna, highlight, dan tekstur, Anda harus memuat dalam sepotong bahan. Bahan potongan menyimpan informasi tentang ambien,, menyebar dan warna specular, serta tekstur informasi dan shininess. Wajah jala potongan yang berisi indeks ke dalam array dari bahan yang menentukan bahan apa yang digunakan untuk wajah model. Potongan bahan lain konglomerat potongan, seperti potongan jala 0x4000. bahkan meskipun memiliki banyak sub-potongan, aku janji itu tidak seburuk sepotong mesh. Mari kita melihat struktur materi segera:
//------------------------------------------------
//- S3dsMaterial
//- Material structure
struct S3dsMaterial
{
char m_cName[256]; //Name of material
float m_fAmbient[4]; //Ambient color
float m_fDiffuse[4]; //Diffuse color
float m_fSpecular[4]; //Specular color
float m_fShininess; //Matl shininess
CImage m_Texture; //Texturemap
};
Jika Anda melihat di kelas C3ds, ada vektor struktur S3dsMaterial, dengan cara yang sama ada vektor dari jerat. Ini berarti ada bisa lebih dari satu materi dalam file 3ds, sehingga sekali lagi, seperti dengan jerat, akan pastikan untuk melacak material Anda berada. Potongan bahan 0xAFFF tidak mengandung data apapun sendiri, hanya
sub-potongan, yang didefinisikan dalam bagian berikut.
Nama Bahan 0xA000
Potongan 0xA000 berisi nama material. Ini hanya nullterminated string, sama seperti nama mesh. Hal ini dapat disalin ke dalam m_cName variabel dari bahan saat ini. Sekali lagi, berhati-hatilah bahwa anda nama materi tidak melebihi 256 karakter. Jika tidak, anda harus memotong itu dari sebelum Anda menyimpannya atau Anda akan menimpa array dan risiko menabrak program anda.
Warna Ambient 0xA010
Potongan ini berisi warna ambien dari bahan saat ini. Sebuah subchunk 0x0011 mengandung warna dalam bentuk RGB, dengan satu byte untuk setiap nilai warna, untuk total tiga byte. Sebelum disimpan dalam Array m_fAmbient, setiap elemen warna RGB harus dikonversi ke floating-point nilai antara 0,0 dan 1,0 sehingga mereka dapat dikirim
langsung ke API render. Hal ini dapat dengan mudah dilakukan dengan menggunakan rumus (255 - R) / 255, dimana R adalah nilai untuk merah, hijau, atau biru
komponen warna. Nilai keempat adalah selalu diatur untuk 1.0f dalam hal ini
kasus, karena nilai tidak ditentukan sebaliknya. Nilai keempat adalah termasuk dalam array sehingga dapat dikirim langsung ke penyaji, seperti OpenGL. Pseudo kode berikut membaca warna ambien, serta specular dan menyebar warna.
//pseudocode to read colors for materials
read in three bytes of information, one for each red, green, and blue
convert each value to a floating point value between 0 and 1
store the new values in the first three appropriate array (m_fAmbient
for ambient color)
set the fourth value of the array to 1.0 so that the whole array can
be sent to the rendering API
Diffuse 0xA020 dan specular 0xA030 Warna
Warna difus dan specular bekerja dengan cara yang sama seperti ambien warna, kecuali mereka disimpan dalam variabel mereka sendiri. Keduanya memiliki sama jenis potongan warna dan nilai-nilai.
Shininess 0xA040 dan Transparansi 0xA050
Sebuah persentase sub-potongan memberikan kedua nilai. Potongan sub-0x0030 berisi sebuah integer dua-byte tunggal yang berkisar dari 0 sampai 100 (persentase). Membaginya dengan 100 mengembalikan nilai floating-point untuk antara 0 dan 1. Persentase shininess (0 persen menjadi kusam, 100 persen menjadi sebagai mengkilap mungkin) masuk ke dalam variabel m_fShininess; transparansi adalah nilai keempat dalam array m_fDiffuse.
//pseudo code to read shininess and transparency
Read shininess chunk
Convert shininess to a value between 0 and 1 by dividing by 100
Set shininess parameter equal this value
Read the transparency value
Convert shininess into a value from 0 to 1 in the same way as transparency
Set the fourth value in the diffuse color array equal to the resulting value
Peta Tekstur 0xA200 dan 0xA300
Potongan yang terakhir dan mungkin paling penting dari bahan ini adalah tekstur. Tekstur adalah "kulit" yang meliputi model dan berisi terlalu sulit untuk menciptakan hanya dengan segitiga dan warna detail. Hal ini memberikan model tampilan yang pasti, meningkatkan kepercayaan dan realisme. Struktur peta tekstur dimulai dengan sepotong jenis 0xA200. Ini 0xA200 atau tekstur potongan peta mengandung sub-potongan juga, meskipun hanya sub-potongan yang Anda butuhkan adalah 0xA300. Ini berisi null diakhiri string yang menentukan nama file tekstur itu. String ini dapat diberi makan
langsung ke fungsi beban dari kelas CImage terkandung dalam materi struktur. Jika Anda tidak menggunakan kelas CImage termasuk dalam basecode, Anda dapat mengekstrak string ini dengan strcpy dan menyimpannya dalam buffer sementara untuk lulus untuk Anda sendiri gambar-loading fungsi.
//Reading and loading the texture filename
Set a pointer (char *) equal to the start of the data in the 0xA300
chunk.
Pass this pointer to the CImage::Load() function.
Wah, sekarang Anda memiliki semua data yang statis Anda harus setidaknya render
model dengan cahaya dan tekstur.
Rendering File 3ds Anda
Mari kita lihat bagaimana untuk menarik semua data yang Anda telah dimuat bersama-sama dan memasukkannya ke layar. Anda harus berurusan dengan banyak masalah sementara render. Beberapa jerat memiliki informasi material yang disimpan di dalamnya; beberapa jerat tidak memiliki bahan sama sekali. Beberapa jerat tidak mungkin berisi setiap segitiga, karena fakta mereka dibuat untuk kamera dan lampu. Anda harus mempertimbangkan dan menangani semua keadaan ini. Cara terbaik untuk memvisualisasikan bagaimana file-file yang diberikan adalah untuk melihat flow chart. Aliran render ditunjukkan pada Gambar 7.3. Lihatlah itu sebelum Anda membaca.
No
|
|
|
|
|
|
No
Yes
|
Fungsi render anda harus berisi sebuah loop yang loop melalui masing-masing
dari jerat dan menarik mereka jika diperlukan.Hal pertama yang Anda harus menentukan adalah apakah mesh saat ini berisi informasi wajah. Hal ini dapat dilakukan dengan memeriksa ukuran dari array wajah. Jika itu adalah 0, hanya melewatkan mesh sama sekali dan melanjutkan. Jika tidak mengandung segitiga, itu bukan segitiga mesh-yang valid. Anda berikutnya harus menentukan bagaimana bagian-bagian yang berbeda dari model yang diberikan. Jika ada informasi dalam variabel m_vObjMat, mesh
berisi informasi material dan itu harus diatur terlebih dahulu. Jika cabang ini
diambil, loop baru harus dibentuk untuk loop melalui semua ObjMat yang benda sehingga bahan yang tepat diterapkan pada wajah yang tepat. Kemudian, dengan menggunakan indeks materi, Anda harus mengatur sifat material. Pastikan untuk mengaktifkan pencampuran, pencahayaan, dan tekstur mengikat sebagai baik. Jika Anda menggunakan OpenGL, bahan-bahan yang ditentukan dengan menggunakan glMaterialf fungsi, dan pencampuran diaktifkan menggunakan glEnable fungsi dengan parameter GL_BLEND. Pastikan untuk mengatur pencampuran anda fungsi menggunakan kode ini:
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Sedangkan untuk tekstur, Anda dapat menggunakan CImage:: Bind () fungsi jika Anda menggunakan kelas CImage, atau glBindTexture jika Anda menggunakan lain imageloading rutin. Kemudian, anda dapat membuat wajah-wajah yang ditentukan oleh bahan saat ini informasi. Jika tidak ada bahan untuk objek tertentu, Anda sekarang dapat membuat wajah-wajah. Karena meninggalkan diaktifkan pencampuran dapat menyebabkan terdefinisi hasil, yang terbaik adalah untuk menonaktifkan ketika Anda mengambil rute ini. Anda juga harus menonaktifkan texturing juga. Dalam C3ds:: fungsi Render, saya menggunakan modus langsung untuk mengirim verteks, koordinat tekstur, dan informasi normal untuk OpenGL. Ada lebih baik cara untuk melakukan ini, misalnya menggunakan array vertex. Saya memutuskan untuk tinggalkan cara ini untuk membuatnya lebih mudah untuk mengerti, terutama jika anda tidak tahu OpenGL dengan baik.
Kesimpulan
Bab ini harus berisi banyak informasi untuk membuat Anda pergi. Anda sekarang dapat memuat dan membuat model 3ds, lengkap dengan tekstur dan bahan. Anda juga telah diperkenalkan ke format yang menggunakan sistem potongan independen untuk membuat file. Sekarang Anda harus dapat melihat format 3ds penuh, yang dapat diperoleh dari situs seperti http://www.wotsit.org, dan dapat memuat dan memanfaatkan salah satu tambahan potongan yang disimpan dalam file yang tidak dibahas di sini. 3ds Format toko satu ton informasi dan memegang kemungkinan tak terbatas. Berikutnya bab menunjukkan Anda bagaimana untuk membuat loader untuk format MDL Half-Life
menggunakan basecode dirilis oleh Valve Software. Bab yang menggunakan yang sudah ada basecode daripada menciptakan seluruh loader dari awal karena kompleksitas format. Baca terus untuk mengetahui bagaimana beban ini populer, Format kuat.
LAMPIRAN B
STL Vektor
cat dasar
Seperti yang Anda telah membaca melalui buku ini, Anda tidak ragu mendengar banyak tentang membangun matematika yang dikenal sebagai vektor. Nah, di sini Anda akan diperkenalkan dengan jenis lain dari vektor, yang penyimpanan data vektor. Standard Template Library (STL) adalah kumpulan dari wadah kelas, iterator kelas, dan kelas utilitas dan fungsi lainnya. Sebuah kontainer hanya itu, berisi data dari tipe yang ditentukan pengguna. yang bagus hal tentang kontainer STL adalah bahwa mereka membersihkan diri mereka sendiri, yang berarti bahwa mereka mengalokasikan memori apapun, mereka juga bebas. Namun, hal ini tidak termasuk memori yang Anda dialokasikan. Iterator adalah jenis kelas yang menunjuk ke data yang terdapat dalam kelas kontainer. Iterator memiliki kemampuan untuk bergerak melalui elemen-elemen dalam wadah, memberikan Anda akses ke elemen-elemen yang menunjuk ke iterator .
STL Vektor
Vektor STL kelas (mulai sekarang hanya disebut vektor) adalah wadah objek yang menyimpan unsur-unsur dalam susunan linear, memungkinkan untuk cepat sisipan dan penghapusan pada akhir vektor, dan insersi lebih lambat dan penghapusan di tengah atau di awal. Bila menggunakan fungsi vektor <>:: akhir, nilai kembali akan menjadi elemen setelah elemen terakhir dalam vektor. Anda akan menemukan bahwa "masa lalu akhir "tema yang diulang seluruh STL.
Dasar-dasar Menggunakan Vektor
Hal pertama yang perlu Anda lakukan untuk menggunakan vektor adalah untuk menyertakan header file yang terkandung dalam:
# include <vector>
Anda akan melihat bahwa tidak ada perpanjangan jam di atasnya., Yang tidak salah ketik.
Header STL di semua implementasi yang saya digunakan tidak memiliki ekstensi. Hal berikutnya yang perlu Anda lakukan adalah untuk menyatakan suatu vektor, dalam hal ini, Anda menyatakan vektor bilangan bulat: std:: vector <int> vec; / / Mendeklarasikan sebuah vektor yang akan berisi bilangan bulat. Jadi apa dengan std ini:: hal? Nah, std adalah namespace, yang saya tidak akan masuk ke banyak, selain mengatakan bahwa untuk program C + +, semua yang anda perlu dilakukan untuk menghindari menggunakan std ini:: adalah menambahkan baris ini setelah meliputi: menggunakan namespace std; Namun, dalam program C + + di mana Anda memiliki beberapa file, termasuk header dan seperti itu, umumnya merupakan ide yang baik untuk hanya menempel dengan menggunakan std:: ekstensi untuk mencegah termasuk ruang nama tidak sengaja. Baiklah, sejauh ini Anda telah disertakan dan dinyatakan sebagai vektor yang akan mengandung bilangan bulat. Sekarang Anda akan memasukkan beberapa data ke dalamnya. Memasukkan data pada akhir vektor adalah mudah, Anda hanya menggunakan fungsi push_back. Para kode berikut akan menyisipkan angka 0-9 pada akhir vektor:
for (int i = 0; i <10; i + +)
vec.push_back (i); / / Sisipan saya ke ujung vektor
Untuk menghapus data dari ujung vektor, Anda hanya menggunakan pop_back yang
fungsi. Fungsi pop_back tidak membutuhkan argumen, dan mengembalikan void.
Jadi, jika Anda ingin menghapus bahwa sembilan terakhir dari vektor, Anda akan
lakukan hal berikut:
vec.pop_back (); / / Menghapus 9 dari ujung vektor
Sekarang bahwa Anda memiliki beberapa data yang disimpan dalam vektor Anda, Anda perlu belajar bagaimana untuk mengakses data. Untuk melakukannya, Anda menggunakan hal kecil disebut iterator. Sebuah iterator pada bentuk yang paling dasar adalah sebuah kelas yang memberi anda akses ke data yang disimpan dalam sebuah wadah pada elemen-elemen dengan dasar. Anda dapat menggunakan iterator untuk memasukkan, menghapus, mencari, dan menyortir data yang tersimpan dalam kata kontainer. Jadi mari kita buat sebuah iterator dalam rangka untuk berjalan melalui data Anda dan mencetak:
std::vector<int>::iterator l, endi;
endi = vec.end(); // Returns the element "Past the end" of the
// last element
for(l = vec.begin(); // Returns the first element in the vector
l != endi; // Checks to make sure that we are not at the end
++l) // Moves l to the next element, using ++l instead of l++ is
// faster because you don't create a copy
{
cout<<"Vector vec contains [ "
<<*l // the * operator returns a reference to the element l points to
<<endl;
}
Tidak sulit, ya? Untuk menyisipkan data ke dalam vektor pada titik tertentu, anda harus menggunakan vektor <>:: insert () fungsi. Sebuah peringatan, meskipun: Jika anda menemukan bahwa anda melakukan banyak sisipan dan penghapusan mana saja kecuali pada akhir vektor, disarankan bahwa Anda melihat ke salah satu kelas wadah lain sebagai gantinya. Dikirim pada titik-titik lain selain akhir vektor lebih mahal daripada di akhir. Untuk menyisipkan, katakanlah, awal dari sebuah vektor, anda harus terlebih dahulu mendapatkan sebuah iterator yang menunjuk ke awal, dan kemudian anda hanya memanggil fungsi insert dengan iterator, serta data yang ingin anda masukkan.
std::vector<int>::iterator l = vec.end();
while(l != vec.begin())
{
--l;
vec.insert(vec.begin(), *l) // Insert at the beginning
// the element contained in l
}
Ada dua cara untuk menghapus data dari sebuah vektor pada titik-titik lain selain akhir. Salah satu adalah dengan menggunakan vektor <>:: menghapus fungsi dan yang lain adalah untuk menggunakan fungsi menghapus. Perbedaan antara mereka adalah menghapus yang akan menghapus dan mengubah ukuran vektor, sedangkan menghapus akan mempertahankan relatif penempatan elemen, tetapi akan menghapus elemen yang ditentukan.
// Using remove:
std::vector<int>::iterator newend;
// Searches the vector for all 7's and removes them
newend = std::remove ( vec.begin( ), vec.end( ), 7 );
cout << "Vector vec with value 7 removed is [ " ;
for ( l = vec.begin( ) ; l != vec.end( ) ; l++ )
cout << *l << " ";
cout << "]." << endl;
// To change the sequence size, use erase
vec.erase (newend, vec.end( ) );
cout << "Vector vec resized with value 7 removed is [ " ;
for ( l = vec.begin( ) ; Iter1 != l.end( ) ; l++ )
cout << *l << " ";
cout << "]." << endl;
Bagian berikutnya dari kode menghapus semua kejadian dari 7 dan mengubah ukuran
vektor.
// Using just erase:
vec.erase(vec.begin(), vec.end(), 7);
Set lengkap dari fungsi menghapus termasuk remove_if, remove_copy, dan remove_copy_if. Para remove_copy dan remove_copy_if fungsi menciptakan berbagai nilai baru, hanya menyalin nilai-nilai yang bukan bagian dari nilai yang ditentukan. Para remove_copy_if dan remove_if fungsi mengambil argumen fungsi yang disebut predikat biner, yang fungsi benar-salah yang akan mengembalikan true untuk elemen-elemen yang cocok predikat, dan salah untuk semua orang lain. Fungsi-fungsi ini kemudian menghapus semua elemen yang memenuhi predikat biner.
bool gt6( int val )
{
return (val > 6); //returns true if greater than 6
}
.
.
.
std::vector<int> v2;
newend = std::remove_copy_if ( vec.begin( ), vec.end( ),
v2.begin( ), gt6 ); // copies to a new vector containing all values
// less than or equal 6
Penyortiran
Pada titik tertentu, anda mungkin akan ingin menyortir vektor anda sehingga anda dapat
hal-hal yang keren seperti menggunakan pencarian biner untuk menemukan elemen. Penyortiran vektor adalah proses yang cukup sederhana, Anda hanya menggunakan fungsi semacam. Mengurutkan fungsi yang disertakan menggunakan header algoritma.
#include <algorithm>
.
.
.
// The following code will sort your vector of ints in ascending order
std::sort(vec.begin(), vec.end());
Algoritma semacam juga dapat mengambil argumen fungsi yang akan digunakan untuk menentukan apakah item harus dipindahkan. STL memiliki beberapa fungsi-fungsi ini sudah, Anda hanya perlu untuk memasukkan <functional> yang header.
#include <functional>
#include <algorithm>
.
.
.
//sorts using the greater function object ( descending order )
std::sort(vec.begin(), vec.end(), greater<int>());
Para <int> besar () bagian sebenarnya adalah obyek fungsi, yang pada dasarnya
kelebihan beban operator () yang terkandung dalam struktur atau kelas.
Pencarian
Ada beberapa metode untuk mencari vektor, namun, bagian ini hanya mencakup penggunaan menemukan fungsi serta pencarian biner fungsi. Menemukan fungsi bekerja pada kedua disortir dan dipilah vektor. Mereka bekerja dengan membandingkan setiap elemen dengan nilai yang dicari. Fungsi pencarian biner memerlukan vektor diurutkan, namun mereka mengambil waktu kurang signifikan untuk menemukan elemen.
Fungsi menemukan memiliki empat versi. Mereka menemukan, find_end, find_first_of, dan find_if. Fungsi menemukan menemukan kejadian pertama dari suatu elemen. Fungsi find_if menemukan kejadian pertama dari sebuah elemen yang memenuhi kondisi tertentu. Dua lainnya fungsi, find_end dan find_first_of, tidak dibahas di sini.
Untuk menggunakan menemukan, cukup menyebutnya dengan jangkauan yang ingin anda cari dan elemen yang ingin Anda temukan. Jadi, untuk menemukan nomor dalam vektor bilangan bulat, anda akan melakukan hal berikut:
#include <algorithm>
.
.
.
// Starts at the beginning of the vector, and proceeds to the end
// Till it finds a 7, or returns the element past the end if it doesn't
std::vector<int>::iterator i;
i = find(vec.begin(), vec.end(), 7);
if(i != vec.end()) // We found it
.
.
.
To use the find_if function, you simply do the following:
#include <algorithm>
.
.
.
bool IsGreaterThan5( int val )
{
return (val > 5);
}
.
.
.
std::vector<int>::iterator i;
//Will find the first element that is greater than five
i = find_if(vec.begin(), vec.end(), IsGreaterThan5);
if(i != vec.end()) // if we found it
.
. // Do stuff
.
Fungsi pencarian biner memerlukan vektor diurutkan, namun dapat mengambil secara signifikan lebih sedikit waktu untuk menemukan elemen yang anda cari. Para fungsi pencarian biner yang binary_search, lower_bound, upper_bound, dan equal_range. Fungsi binary_search mengembalikan nilai true jika eleme mencari ada. Fungsi lower_bound menemukan kejadian pertama sebuah elemen dalam vektor, atau posisi itu akan berada di jika ada. Fungsi upper_bound menemukan elemen masa lalu kejadian terakhir elemen dalam vektor mencari, atau di mana ia akan jika elemen mencari ada. Fungsi equal_range hanya kombinasi dari fungsi lower_bound dan upper_bound.
Untuk menggunakan fungsi pencarian biner, anda hanya lulus kisaran
diurutkan elemen yang anda ingin mencari dan elemen yang ingin anda temukan.
#include <algorithm>
.
.
.
std::vector<int> vec;
for(int j = 0; j < 10; j++)
{
vec.push_back(j); // Insert 2 copies of the number at the end.
vec.push_back(j);
}
sort(vec.begin(), vec.end()); // Sort the vector
bool found = binary_search(vec.begin(), vec.end(), 7); //Is there a 7?
assert(found == true); //should be
std::vector<int>::iterator k, l;
k = lower_bound(vec.begin(), vec.end(), 5); // Find the first five
l = upper_bound(vec.begin(), vec.end(), 5); // Find the item past the five
assert(*k == 5); // should be the first five
assert(*l == 6); // Should equal the element after the last five
Menggunakan Objek Sendiri
Sejauh ini, anda baru saja menggunakan vektor yang mengandung bilangan bulat. Meskipun integer yang bagus dan keren, anda mungkin akan ingin anda gunakan sendiri
tipe user-defined dengan vektor. Bagian ini mencakup beberapa isu tentang menggunakan benda anda sendiri dengan vektor. Hal pertama yang perlu anda lakukan sebelum anda dapat menyimpan obyek dalam vektor adalah mendefinisikan objek. Sebuah objek yang akan disimpan dalam vektor harus mencakup copy constructor, karena konstruktor salinan digunakan ketika benda bergerak di sekitar. Tugas overload Operator juga dapat membantu.
class MyObject {
int age
int height; //in CM
public:
MyObject() {}
MyObject( int a, int h) : age(a), height(h) {}
// Copy Constructor
MyObject(const MyObject& a) : age(a.age), height(a.height) {}
void SetAge(int a) { age = a; }
void SetHeight(int a) { height = a; }
int GetAge() { return age; }
int GetHeight() { return height; }
//Overloaded assignment operator
MyObject& operator=(const MyObject& r) {
age = r.age; height = r.height; return *this; }
};
Ada objek dasar. Untuk memberitahu vektor untuk menggunakannya, anda cukup mengganti
bagian int vektor dengan MyObject:
std::vector<MyObject> vec; //vector to hold MyObject types
Sekarang sampai pada bagian-menyimpan rumit di dalam MyObject vektor.
Untuk melakukannya, Anda menelepon push_back dengan MyObject suatu, seperti:
.
.
.
// Stick 10 random MyObject's into the vector
for(int j = 0; j < 10; j++)
{
vec.push_back(MyObject(rand()%20+1, rand()%120 + 1));
}
Untuk mengurutkan objek, anda harus baik overload operator <atau pasokan semacam fungsi dengan fungsi lain. anda akan melihat dalam contoh kode bahwa anda melewati semuanya dengan referensi. Hal ini untuk menghindari copy dan dengan demikian
menghemat memori dan waktu. Contoh ini macam vektor berdasarkan umur:
#include <algorithm>
.
.
.
bool LesserAge( MyObject& l, MyObject &r)
{
return (l.GetAge() < r.GetAge());
}
//or:
bool operator<( MyObject& l, MyObject &r)
{
return (l.GetAge() < r.GetAge());
}
.
.
.
sort(vec.begin(), vec.end(), LesserAge); // Sorts by age
sort(vec.begin(), vec.end()); // Sorts by age using <
Karena fungsi menemukan menggunakan operator ==, anda harus overload untuk
bekerja dengan kelas anda. Ini adalah operasi sederhana:
#include <algorithm>
.
.
.
bool operator==(MyObject l, MyObject r)
{
if(l.GetAge() != r.GetAge())
return false;
if(l.GetHeight() != r.GetHeight())
return false;
return true;
}
.
.
.
std::vector<MyObject>::iterator j;
j = std::find(vec.begin(), vec.end(), MyObject(10, 120));
if(j != vec.end()) //We found it
.
.
.
Operator kesetaraan (== Operator) digunakan dalam perbandingan untuk menentukan apakah salah satu elemen sama lain. Namun, itu hanya akan bekerja
pada C + + didefinisikan jenis. Untuk menyiasati keterbatasan ini, anda dapat membebani
untuk menerima jenis lainnya. Dalam hal ini, kelebihan beban untuk menerima MyObject jenis dan membandingkan mereka berdasarkan usia dan tinggi badan. Namun, anda bisa dengan mudah membandingkan berdasarkan usia saja, sehingga memungkinkan untuk
tingkat kesetaraan.
Pointer
Salah satu kelemahan menyimpan suatu objek dalam vektor adalah bahwa kapan itu akan pindah, ia harus menyalin seluruh objek untuk baru lokasi. Untuk vektor kecil, ini mungkin mungkin, tapi ketika anda mulai mendapatkan vektor yang lebih besar, menjadi tidak dapat diterima. Cara untuk menghindari ini adalah dengan menggunakan pointer. Pointer adalah sedikit lebih kecil dari objek yang paling, sehingga memindahkan mereka sekitar membutuhkan waktu jauh lebih sedikit. Namun, karena pointer menggunakan memori anda telah dialokasikan, anda juga harus ingat untuk gratis yang memori ketika anda selesai. Mendeklarasikan sebuah vektor untuk menyimpan pointer ke objek ini cukup sederhana, anda hanya mengganti bagian MyObject dengan konversi yang sesuai:
std:: vector <MyObject*> vec;
Untuk meletakkan sesuatu ke vektor, semua anda benar-benar harus lakukan, dari
kode terakhir, adalah menambahkan operator baru:
.
.
.
// Stick 10 random MyObject's into the vector
for(int j = 0; j < 10; j++)
{
vec.push_back( new MyObject(rand()%20+1, rand()%120 + 1));
}
Sortasi vektor adalah sedikit berbeda, karena operator akan menggunakan semacam biner predikat yang anda tentukan atau operator <secara default. Karena pointer hanya integer, operator <akan mengurutkan oleh memori alamat dan bukan isi dari MyObject.
#include <algorithm>
.
.
.
bool operator==(MyObject *l, MyObject r)
{
if(l->GetAge() != r.GetAge())
return false;
if(l->GetHeight() != r.GetHeight())
return false;
return true;
}
.
.
.
std::vector<MyObject>::iterator j;
j = std::find(vec.begin(), vec.end(), MyObject(10, 120));
if(j != vec.end()) //We found it
.
.
.
Sekali lagi, anda overloading operator kesetaraan (==) seperti yang anda lakukan sebelumnya. Namun, kali ini, dapat membandingkan pointer ke MyObject suatu
objek dan membandingkan objek MyObject untuk dirinya sendiri. Setelah Anda selesai dengan vektor anda dari pointer, anda harus membebaskan memori yang dialokasikan anda. Ini adalah proses yang cukup sederhana:
#include<algorithm>
.
.
.
template<typename T>
class DeletePtr
{
public:
void operator()(T *elem)
{
delete elem;
}
};
.
.
.
std::for_each(vec.begin(), vec.end(), DeletePtr<MyObject>());
DeletePtr kelas dengan operator fungsi anggota () disebut fungsi objek. Semua hal ini adalah pointer menghapus MyObject. Jika anda ingin, anda bisa membuatnya menghapus pointer bulat dengan hanya mengubah baris ini:
for_each(vec.begin(), vec.end(), DeletePtr<MyObject>());
to:
for_each(vec.begin(), vec.end(), DeletePtr<int>());
Sederhana dan mudah (dan berguna juga).
Kesimpulan
Jika anda bertanya-tanya tentang beberapa aplikasi vektor dalam game, aku punya ide. Ide saya adalah untuk adegan-grafik sederhana. Jika anda memperoleh semua benda anda dari beberapa objek dasar, anda bisa menggunakan vektor dari pointer ke objek dasar. Ini akan memungkinkan anda untuk dengan mudah melakukan update, tabrakan, dan rendering. Karena anda akan tahu bahwa semua objek sebelum objek saat ini sudah pindah dan telah tabrakan diuji, anda tidak perlu uji terhadap mereka untuk anda saat ini
objek. Juga, anda bisa menggunakan kembali elemen vektor, seperti ketika makhluk mati, sehingga anda mengatur elemen vektor ke obyek kosong, dan ketika anda membutuhkan objek baru, hanya menggunakan kembali yang kosong. Akhirnya, saya akan merekomendasikan bahwa anda melakukan penelitian lebih lanjut ke Template pemrograman, terutama yang berkaitan dengan Standard Template Perpustakaan. Ini memiliki banyak jenis kontainer, termasuk deques, daftar, set, multisets, dan peta. Setiap kontainer memiliki kelebihan dan kekurangan, sehingga memilih orang yang tepat tidak selalu mudah. Ada biasanya satu wadah yang akan lebih cocok untuk sebuah aplikasi tertentu daripada yang lain.
Tidak ada komentar:
Posting Komentar