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