OBJ Model Kütüklerini Kullanmak

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




Bu haberin geldigi yer: oyunyapimi.org
http://www.oyunyapimi.org

Bu haber icin adres:
http://www.oyunyapimi.org/modules.php?name=Sections&op=viewarticle&artid=32