OBJ Model Kütüklerini Kullanmak
// Yazdığımız programlarda ve demolarda her zaman
kutular,dikdörtgenler kullanamayız.
// Artık ekrana model çıkarma zamanı geldi diyorsanız bu yazı tam sizler için.
// Bu kodu anladıktan sonra artık diğer model formatlarını kullanmak çok kolay!
// Bir önceki "C/C++ İle Kütük İşlemlerine Giriş" adlı yazıda metin
dosyalarından bahsetmiştik
// Şimdi o bilgileri kullanarak bir Obj dosyayı yüklemeyi inceliyeceğiz
// Basit bir model dosyası (sadece vertex koordinatları ve üçgen(face,triangle)lerin
vertex
// indeksleri olsun) şu şekilde olabilir:
// Dosyada önce vertexler üç bileşeniyle(x,y,z) verilebilir.Örnek olarak;
// numVertices : 6
// 1.0 3.0 4.0
// 2.0 1.0 4.0
// 5.0 6.0 4.5
// 0.4 2.0 2.0
// 3.2 0.4 1.0
// 3.4 2.5 8.5
// 6 adet vertex'imiz var. Peki ama bunlardan nasıl,hangi üçgenler oluşacak.
// Bunu çözmek için vertex indeksleri kullanılır.Dosyada indekslerimiz şu şekilde
olsun;
// numFaces 4
// 0 2 3
// 4 5 1
// 5 3 0
// 1 4 2
// Hepsinden once dosyada kaç vertex,üçgen vs varsa bunlar için önce bellekte dinamik
olarak
// yer ayırmalıyız.(new,malloc).İsteyenler STL(standart template library) den
<vector> kullanabilir.
// Burada 4 üçgen var. Peki bu rakamlar neyi ifade ediyor.
// 0 2 3 -> bu rakamlar, 1.üçgenin yukarıda belirttiğimiz vertexlerden 0. 2. ve 3.
vertexi
// kullanacağını belirtiyor.
// 2.üçgen 4. 5. ve 1. vertexleri kullanacak vs..
// Üçgen dizimiz face[üçgensayısı][3] 3 sayısı üçgenin üç köşesi olduğu için
// vertex dizimiz vert[vertexsayisi] olsun.Birinci(face[0]) üçgeni çizmek için;
// index1=face[0][0]; index2=face[0][1]; index3=face[0][2];
// index değişkenleri sırasıyla yukarıda bahsettiğimiz 0 2 3 değerlerini aldı.
// glVertex3f(vert[index1].x,vert[index1].y,vert[index1].z);
// glVertex3f(vert[index2].x,vert[index2].y,vert[index2].z);
// glVertex3f(vert[index3].x,vert[index3].y,vert[index3].z);
// Tüm model dosyaları bu ve buna benzer yöntemlerle çalışır.
// Obj dosyalarına gelirsek.Obj dosyalari çok basit text dosyalardır.
// Çoğu model programları obj dosyasını export/import edebilir.Ama çoğu kendine göre
// dosyaya eklemeler,çıkarmalar yapıyor.Bazısı normal vektörlerini yazar,
// grup isimlerini ekler,bazısında bu opsiyoneldir vs.İndireceğiniz
*.obj(MilkShape3D'de
// kaydedilmişdir www.milkshape3d.com) dosyasını açıp incelerseniz
// v,vt,vn,f ile başlayan satırlar göreceksiniz.
// Diğer satırları gözardı edebiliriz.
// Dosyada:
// 'v' ile başlayan satırlar vertexi belirtir ve sonraki 3 sayi vertexin x,y,z bileşenleridir
// 'vt' doku kordinatlarını(Texture Coordinates) sonraki 2 sayi u,v bileşenleridir.
// 'vn' vertex normallerini belirtir.3 sayi x,y,z bileşenleridir.
// 'f' ise yukarıda anlattığım üçgenlerin vertex,normal,doku kordinatları
indeksleri içindir.
// Dikkat ettiyseniz 9 değer var
// f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3
// v1/vt1/vn1 üçgenin 1. köşesinin vertex,normal,doku kordinati indeksi diğerleri
2.3.köşeler
// Bu üçgenin sadece hangi vertexlerden oluştuğu değil buna ek olarak hangi doku
// koordinatları(vt),ve hangi normalleri(vn) kullandığını da anlatıyor bu satır.
// Kullanacağımız tipler ve basit obj sınıfı şu şekilde
// Doku kordinatlarını saklamak için kullanacağız
struct Vektor2{
float u;
float v;
};
// Vertex ve normal değerlerini saklamak için
struct Vektor3{
float x;
float y;
float z;
};
// Dikkat ettiyseniz üçgenden bahsederken hep hangi vertex,hangi normal vs diyorum
// Çünkü bu yapıda vertexlerin,normallerin,doku kordinatlarının kendileri değil
indeksleri
// saklanıyor.
// Bir üçgenin hangi vertex,normal,doku koordinatlarını kullandığını saklamak için.
// Bu bilgi ile o üçgenin vertexini,normalini bulup çizeceğiz.
struct OBJ_Face
{
int vertexIndex[3]; // Bu üçgenin hangi vertexlerden oluştuğunu
belirten indeks
int textureIndex[3];// Hangi doku kordinatlarını kullandığını gösteren
indeks
int normalIndex[3]; // Hangi normali kullandığını gösteren indeks
};
class CObj
{
public:
//Yapıcı ve yıkıcı metodlar
CObj();
~CObj();
// LoadObj üye fonksiyonu parametrede
belirten dosyadan verileri okuyup
// üye değişkenlerimize atıyacak.
void LoadObj(const char *dosya);
// Modelimizi çizmek için
void Render();
// Ayrılan belleğin program sonunda geri iadesi için
void Destroy();
private:
// Bu metod sayesinde modelimizin kaç
adet vertex,normal,doku koordinati ve üçgene
// sahip olduğunu öğrenip aşağıda m_num ile başlayan değişkenlere
atıyoruz.
void GetNums(FILE *fp);
int m_numVerts; // Vertex sayısı
int m_numNormals; // Normal sayısı
int m_numTexCoords; // Doku koordinatları sayısı
int m_numFaces; // Üçgen sayısı
// Bu işaretçilere, yukardaki sayılar kadar yer ayrılacak.
Vektor3
*m_pVerts; //
vertex dizisi
Vektor3
*m_pNormals; //
normal dizisi
Vektor2
*m_pTexCoords;//
doku koordinatlari dizisi
OBJ_Face *m_pFaces; // üçgen indeksleri dizisi
};
// Metodlarımızıda inceleyelim.
void CObj::LoadObj(const char *dosya)
{
FILE *fp;
// Dosyadan okunacak her satır bu değişkende
char satir[255];
fp = fopen(dosya,"rt");
// Dosya açılamadıysa ekrana uyarı
mesajı çıkarıp programdan çıkalım.
if(fp==NULL){
printf("\n%s acilamadi!\n",dosya);
exit(0);
}
// vertex,normal vs.. sayısını öğrenip
bellekte yer ayırıyoruz GetNums() ile.
GetNums(fp);
// getStr() fonksiyonu 3.parametresinde verilen karakterle başlıyan satırı okumak için
// Bunun amacı:örneğin v satırları arasında boşluk, yorum satırı
veya ilgilenmediğimiz bir
// satır varsa onu atlamak.
// v 1.0 2.0 2.0
// # exported by...
// v 3.0 2.0 4.0
// gibi..burada # ile başlıyan satırla ilgilenmiyoruz.
// Dosyanın başından itibaren sayısı kadar vertexleri okuyoruz
for(int i=0;i<m_numVerts;i++)
{
getStr(fp,satir,'v');
sscanf(satir,"v %f %f
%f",&m_pVerts[i].x,&m_pVerts[i].y,&m_pVerts[i].z);
}
// Doku koordinatları
for(i=0;i<m_numTexCoords;i++)
{
getStr(fp,satir,'v');
sscanf(satir,"vt %f
%f",&m_pTexCoords[i].u,&m_pTexCoords[i].v);
}
// Normaller
for(i=0;i<m_numNormals;i++)
{
getStr(fp,satir,'v');
sscanf(satir,"vn %f %f
%f",&m_pNormals[i].x,&m_pNormals[i].y,&m_pNormals[i].z);
}
// Kodu kısaltmak için geçici fc değişkenini
tanımladık ve sıfırladık
OBJ_Face fc={0};
// üçgen bilgisi okunuyor.
for(i=0;i<m_numFaces;i++)
{
getStr(fp,satir,'f');
sscanf(satir,"f %d/%d/%d %d/%d/%d
%d/%d/%d",&fc.vertexIndex[0],&fc.textureIndex[0],&fc.normalIndex[0],
&fc.vertexIndex[1],&fc.textureIndex[1],&fc.normalIndex[1],
&fc.vertexIndex[2],&fc.textureIndex[2],&fc.normalIndex[2]
);
m_pFaces[i]=fc;
}
// Dokumuzu yüklüyoruz.
LoadTexture(texture,"claymore.bmp");
// Dosyayı da kapattık.
fclose(fp);
}
void CObj::Render()
{
// modelimizi çizmek için yapmamız
gereken artık çok basit.2 döngü işimizi görüyor.
// 1.si 0 dan m_numFaces'e kadar bir döngü yani tüm üçgenleri çağıracagiz
// 2.si ise her üçgende 3 tane v,vt,vn olacagından 0 dan 2 ye kadar
bir döngü
// en içteki for da o anki üçgenin v,vn,vt indeksleri için bu değişkenler
// Yine kodu kısaltmak için tanımlıyoruz.
int i_v,i_vn,i_vt;
// indeksimizi aldıktan sonra vertex,normal,vs.. dizimizden ilgili
vertex,normal,doku kor
// dinatinin baslangic adresini bu işaretçilerde saklıyoruz
ki.Bunları glVertex,glNormal
// glTexCoord'a parametre olarak geçirelim.
Vektor3 *vV,*vN;
Vektor2 *vT;
// Dokumuzu seçiyoruz.
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_TRIANGLES);
for(int i=0;i <m_numFaces;i++)
{
for(int j=0;j<3;j++)
{
//
Şu anki üçgenin ilgili köşesinin ilgili bileşeninin indeksini alıyoruz.
i_v
= m_pFaces[i].vertexIndex[j];
i_vn
= m_pFaces[i].normalIndex[j];
i_vt
= m_pFaces[i].textureIndex[j];
//
bu indeksler vasıtasıyla ilgili vertexi,normali.vs buluyoruz.
//
Burada indekslerden 1 çıkarmamızın nedeni Obj dosyasında
//
örneğin f 1/1/2 2/3/1 3/1/2 // Hiç sıfır yok
//
burada üçgenin ilk köşesinin vertex indeksi 1 ama biz biliyoruz ki
//
kastetdiği vertex m_pVerts[1] değil m_pVerts[0]
//
Dolayısıyla 1 eksiltiyoruz.
vV
= &m_pVerts[i_v-1];
vN
= &m_pNormals[i_vn-1];
vT
= &m_pTexCoords[i_vt-1];
//
Son olarak da bunları opengl'ye gonderiyoruz.
glNormal3f(vN->x,vN->y,vN->z);
glTexCoord2f(vT->u,vT->v);
glVertex3f(vV->x,vV->y,vV->z);
}
}
glEnd();
}
// Obj dosyaları pek kullanışlı değil. Ama bu işi öğrenmek için çok iyi bir başlangıç.
// Bu dosyayı Milkshape'de kaydettim. Aslında dosyanın kullandığı dokuların dosya
isimleri ayrı
// bir (*.mtl) dosyada saklanıyor. Ama tek dokumuz olduğu için onu ihmal ettim. Eger
modelde
// birden fazla grup(modellerle uğraşanlar bilirler) varsa bunlar için ayrı doku falan
// varsa bu kodlarda biraz oynama yapmak gerekecekti.Zaten bu obj dosyasını fazla
kullanmayacağımız
// için fazla da karıştırmak istemedim kodu.
// Geriye kalan açıklamaları indireceğiniz dosyalarda bulabilirsiniz.Bu döküman ile
ilgili görüşlerinizi belirtmek yada
// aklınıza takılan konularda sorular sorabilmek için www.oyunyapimi.org sitesinin
forumlarını kullanabilirsiniz. İyi çalışmalar.
UzMaN :: 2003 :: www.oyunyapimi.org