Üye Girişi
x

Giriş Başarılı.

Yanlış Bilgiler.

E-mail adresinizi doğrulamalısınız.

Facebook'la giriş | Kayıt ol | Şifremi unuttum
İletişim
x

Mesajınız gönderildi.

Mesajınız gönderilemedi.

Güvenlik sorusu yanlış.

Kullandığınız Sosyal Medyayı Seçin
Yeni Klasör 8 yıldır sizin için en güvenli hizmeti veriyor...

Teknoloji dünyasındaki son gelişmeler ve sürpriz hediyelerimiz için bizi takip edin.

C++ OpenGL Kullanımı Örneği

opengl örn

Forumlar / Teknoloji / Anlatımlar

 
> 1 <

Quick_man34
-- AdministratoR --

grup tuttuğum takım
Yüzbaşı Grup
Hat durumu Cinsiyet Özel mesaj 1028 ileti
Yer: Bangalore
İş: Although I would say
Kayıt: 25-08-2009 17:13

işletim sistemim [+][+3][+5] [-]
kırık link bildirimi Kırık Link Bildir! #343604 07-09-2009 23:21 GMT-1 saat    
ÖNEMLİ NOT
Tüm bu seri boyunca okuyucunun C/C++ bildiği varsayılacak ve teknik terimlerin açıklamaları düzenli bir şekilde verilecektir. Tüm kod örnekleri Courier fontu ile yazılacak ve aksi belirtilmedikçe C++ olacaktır. Okuyucunun Visual C++ 6 (ya da üzeri / uyumlu bir C++ derleyicisi) kullandığı varsayılacaktır.
Bölüm 1: Sistem Altyapısı
A- SIMD mimarisi hakkında ön bilgi
Bu yazı grafik programlama ile ilgili olmasına rağmen, ilk önce elimizdeki sistemi tanıyarak ve bazı kurallar koyarak başlayacağız.
Bilgisayarlar 1983'den bu yana oldukça fazla gelişmelerine rağmen aynı taban üzerinde ayakta kalmaktalar. Süreklilik gerektiren işlerde oldukça yetenekliler, fakat düzensiz işlemlerde, örneğin değişken bir üç boyutlu sahneyi işlemekte, oldukça yavaşlar. Oyunlarda sıkça yapılan şeylerden birisi bu şekildeki verinin işlenmesi oldugu için, bu seride ilk yapacağımız şey, bilgisayarların ne olduğunu (ve olmadığını) öğrenmek ve bu kısıtlamanın üstesinden nasıl gelineceğini öğrenmek olacak.
Bir günümüz bilgisayarı:
1- Doğrusal ve uzun bloklar halindeki işlemleri daha hızlı yapabilir.
2- Ne kadar az hafıza erişimi yaparsa o kadar hızlı çalışır.
3- Elimizdeki "düzensiz" veri miktarı arttıkça adresleme hataları da o denli artar.
Tüm bunlar, Intel'in ve diğer firmaların şu anda kullandığı SIMD (Single Instruction Multiple Data = Tek komutta birden çok veri) tarzı işletim ile neredeyse tamamen örtüşen bir durum oluşturuyor. İlk kısıtlamamiz, bize uzun blokların tek seferde daha hızlı işlendiğini söylüyor, bu da tam olarak SIMD'nin yaptığı şey, uzun blokları tek seferde işlemek. Diğer iki kısıtlama bize adres çözme işleminin problemlerini, yani "cache miss" ve "misalign" sorunlarını anlatıyor. "Cache miss" ile kastedilen, bir adres çözme işlemi sonucu erişilecek verinin kaşe bellekte bulunmadığının anlaşılıp yeniden bir adres çözme işlemine yol açması. "Misalign" ise çözülen adresin 8 (ya da şimdilerde 16 byte)'ın katlarına denk gelmemesi, örneğin hafızanın 3. byte'ında bulunması sonucu gereksiz miktarda verinin birden çok kez okunmasına yol açmasına verilen isim.
Şimdi bu ufak tablodaki kısıtlamalardan kurtulmak için bu seri boyunca uyacağımız kurallara bir göz atalım:
1- SIMD ve/veya SIMD2 işlemlerine ağırlık vermek ya da bunlara dönüşebilir şekilde kod yazmak.
2- Veriyi daima "hizalanmış" hafıza adreslerinden başlatmak.
3- Veriyi daima 16 byte'ın katları olarak tutmaya çalışmak, mümkünse {X,Y,Z} gibi sırasız veri akışını {X[],Y[],Z[]} haline getirmek.
Üçüncü madde biraz karışık gelebilir. Tipik bir programda sakladığımız veri eğer
class nokta
{
float x, y, z;
};
nokta benimNoktalarım[1024];
ise, bu SIMD için daha uygun olan:
class yogunNokta
{
float x[1024], y[1024], z[1024];
};
yogunNokta benimNoktalarim;
şeklinde yeniden düzenlenmelidir. Tabii bu şekilde statik veri ayrılması pek önerilen birşey olmadığı için bunu sadece konuyu açıklamak için verdiğimiz bir örnek olarak yorumlamanız gerekiyor, pratikte:
class yogunNokta
{
float *x,* y, *z;
};
yogunNokta benimNoktalarim;
x=(float*) aligned_malloc( 1024 * sizeof(float), 16);
y=(float*) aligned_malloc( 1024 * sizeof(float), 16);
z=(float*) aligned_malloc( 1024 * sizeof(float), 16);
daha doğru bir yaklaşım. Buradaki aligned_malloc komutu 16 byte'ın katı olan bir adresten başlayacak şekilde bellek ayırmamızı sağlıyor.
B- Optimizasyon
Kullandığımız sistem, herşeyden önce, saf C/C++ kodunun daima en basit hedef işlemciye göre (zorlanmadıkça) derlendiği bir sistemdir. Yani yazdığımız aşağıdakine benzer bir satır, eğer biz derleyiciyi buna zorlamazsak, 80386'da hızlı çalışacak şekilde derlenir:
float fArray[16] = {1.0f, 4.6f, 12.0f, 43.0f, 61.0f, 52.0f, 2.0f, 1.0f, 3.0f, 4.0f, 6.0f, 13.0f, 421.0f, 54.0f, 654.0f, 1.0f};
for(unsigned long i=0; i<16; i++)
{
fArray *= 2.49f;
}
Bunun assembly karşılığı, standart derleyici çıktısı olarak:
xor eax, eax
$loop1:
fld DWORD PTR _fArray$[esp+eax*4+64]
fmul DWORD PTR __real@401f5c29
inc eax
cmp eax, 16
fstp DWORD PTR _fArray$[esp+eax*4+60]
jb SHORT $loop1
olacaktır. Birinci kuralı ihlal ettik. Burada işlemcinin FPU kısmı kullanılarak aynı anda sadece bir sayı ile çarpma yapılmaktadır. Bunun yerine eğer bir P3/P4 derleyicisi ve el ile optimizasyon yöntemleri kullanırsak yukarıdaki kod şu şekli alır:
float fArray[16] = {1.0f, 4.6f, 12.0f, 43.0f, 61.0f, 52.0f, 2.0f, 1.0f, 3.0f, 4.0f, 6.0f, 13.0f, 421.0f, 54.0f, 654.0f, 1.0f};
float fPack[4] = {2.49f, 2.49f, 2.49f, 2.49f}; // 4 adet çarpan
float *p1,*p2;
p1 = &(fArray[0]);
p2 = &(fPack[0]);
_asm{
mov ecx,4
mov eax, p2
movups xmm1, [eax]
mov eax, p1
mLoop1:
movups xmm0, [eax]
mulps xmm0, xmm1
movups [eax], xmm0
add eax, 0x10 // 16, 4 tane 4 byte(float)
loop mLoop1
};
Görüldüğü gibi artık tek komutta aynı anda dört çarpma yaparak daha uzun fakat daha hızlı bir kod elde ettik. Buradan da şu yeni kural çıkıyor:
Uzun kod daha hızlı çalışabilir.
Tabii bu kullanılan işlemciye göre değişeceği için OpenGL ya da DirectX gibi kütüphanelerde aynı rutin birden fazla işlemciye göre yeniden yazılmış şekillerde bulunurlar.
Bölüm 2: OpenGL
A- OpenGL mi DirectX mi?
Yeni programlamaya başlayanlar için çok daha az zahmetli olacağına inandığım OpenGL ile seriye başlamak istiyorum. DirectX her ne kadar daha "****le yakın" bir tarza sahip olsa da, burada okuyucunun derleyici ile yüksek bir ihtimalle beraber kurmuş olacağı OpenGL kütüphanelerini kullanacağız. Bu arada çoğu kullanıcının bir çeşit nVidia ya da ATI görüntü kartına sahip olduğunu ve en son OpenGL destekli (beta olmayan) sürücüleri yüklemiş olduğunu da varsayıyoruz.
OpenGL, DirectX'in aksine sürekli genişletilen (ve genişletilebilen) bir altyapıya sahip. Örneğin eğer bir firma bir kartı bu gün piyasaya çıkartır ve "raytrace destekliyor" olduğunu söylerse, bunun için (SGI ile konuşup eklentiyi kayıt ettirmenin yanında) tek yapmaları gereken bir adet "eklenti" yazmak ve bunu sürücülerine dahil etmek olacaktır. DirectX bu şekilde genişletilemez, sadece "yazılım ile render" eklentilerini kabul eder ve bunlar da donanınm ile çalışmazlar, sadece yazılımsal eklerdir ve "yeterince hızlı" değillerdir.
OpenGL desteğimiz tam olduğuna göre, ilk OpenGL testimiz ile işe başlayabiliriz...
B- Önce biraz ön bilgi
OpenGL, bir "state machine" dir, yani her an verilen bir konumdadır ve sonraki komutlar bu konumda olunduğu göz önüne alınarak çalışır. Örneğin "ışıklandırma açık" ya da "kapalı" gibi. OpenGL aynı zamanda "batch" komut işletebilir, yani ardışık komutları saklayıp "bitir" sözcüğü ile birtilkte işletmeye alabilir.
Bu yapısı yüzünden OpenGL ile çalışırken her an hangi durumda olduğumuzu kesinleştirmek için uzun işlemler öncesi yeniden durumlarımızı ayarlarız. Örneğin ekrana yazı yazarken ışıklandırma gereksiz olduğu için kapatırken, sahne çizimini yapan kod parçası ışıkları tekrar açmalıdır. Her durum değişikliği bir çok hesaplama ve/ya da donanıma erişim gerektirdiği için çok iyi bir dizayn yaparak bunları minimum seviyede tutmakta yarar vardır.
Örneğin, her üçgeni çizmeden önce bir doku seçmek son derece yavaş olacaktır, bunun yerine aynı dokuyu paylaşan üçgenleri bir araya toplayıp tek seferde çizmek en iyi metodlardan biridir. Bu yüzdendir ki bazı basit BSP uygulamaları modern kartlarda yavaştır, çünkü sırasız ve düzensiz durum değişikliklerine yol açarlar.
Sıkça kullanılan bir yöntem de bir çok dokuyu tek bir büyük dokunun içerisine toplamaktır, böylece sadece bir kez doku seçilir.
Fakat bu iki probleme yol açar:
1- Doku gereğinden fazla büyük olur ve ya video belleğe sığmaz ya da yavaş erişime yol açar.
2- Doku ufak tutulur, fakat bu sefer ortalama kalite yarıya ya da daha kötü bir orantıya iner.
Bir başka çare de büyük ve kalitesiz bir dokunun üzerine birden fazla ufak ve detaylı doku konumlandırmaktır. Bu tip dokulara "detay dokusu" adını verebiliriz. En hızlı yöntemlerden biri olmakla birlikte kendine has problemleri (detay dokusunun tekrarladığının belli olması gibi) vardır.
İşte bu tip problemler varken, dokuları ufak tutup bunları bir "lightmap" (ışık dokusu) arkasına saklamak en basit ve hızlı çaredir. Böylece normal doku tekrarlasa bile, yüzeyin her noktasında farkı bir ışık değeri alacağı için tekrarsızmış gibi görünecektir.
C- Henüz FPS söz konusu değilken...
İlk yapacağımız şey, Windows alt sisteminden arınmış bir şekilde çalışmak olacak. GLUT kullanarak (openGL Utility Library = OpenGL yardımcı fonksiyon kütphanesi) oluşturduğumuz ilk örnek programımız iki adet üçgen ile bir kare çiziyor ve bunu ışıklandırıp renklendiriyor, fakat henüz doku içermiyor. Programımızın kodunu burada verip sizi yazma zahmetinden kurtarıyoruz:
Örnek1.zip
Bu kodu Visual C++ 6 ile derleyebilir ve ilk iki üçgenimize bakabilirsiniz. Fakat bundan fazlası için okumaya devam etmeniz gerekiyor...
D- Adım adım inceleme
Kodun bir kısmı (GLUT fonksiyonları) gözlerimizden uzakta OpenGL'i bir pencerede başlatıyor ve işin basit kısmı ile bizi başbaşa bırakıyor. Aşağıdaki satırlara bir göz atalım:
**** display(****)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor4f(.2,.4,.2,1.0);
glNormal3f(0.0, 1.0, 0.0);
glBegin(GL_TRIANGLES);
glVertex3f(-20.0,-15,20.0);
glVertex3f(20.0,-15,20.0);
glVertex3f(-20.0,-15,-20.0);
glVertex3f(20.0,-15,-20.0);
glVertex3f(-20.0,-15,-20.0);
glVertex3f(20.0,-15,20.0);
glEnd();
glutSwapBuffers();
}
Burada sırayla şu işlemleri uyguluyoruz:
1- glClear: ekran dışı belleği sil (renk ve derinlik kanallarının ikisini de)
2- glColor4f: bundan sonraki noktalar için renk seç (yaklaşık yeşil, %100 opak)
3- glNormal3f: bundan sonraki noktaların işaret ettiği yönü belirt (tam yukarıya)
4- glBegin: buradan sonra verilen her üç nokta bir üçgene denk gelecektir (GL_TRIANGLES)
5- her 6 nokta için: glVertex3f: noktanın uzaydaki konumu
6- glEnd: bitir (burada OpenGL iki adet üçgeni "gerçekten" çizer)
7- glutSwapBuffers: GLUT ekran dışı belleği ekrana taşır (böylece çizdiğimizi görebiliriz)
Buradaki kod, tamamen "inline" (kod içerisinde) bulunan veri ile iki adet yeşil üçgen çiziyor. Dışarıdan dosya okuma gibi işleri sonraki yazılara bırakacağız o yüzden şimdilik böyle olması gerekiyor.
E- Her noktaya farklı renk
Yukarıdaki örnekte sadece bir adet glColor komutu yer alıyor. OpenGL bir "durum saklayan makina" (yukarıyı okuyun) olduğu için bu "durum" tüm çizim işlemleri boyunca geçerliliğini koruyor. Eğer iki adet farklı renkte üçgen çizmek istersek kodu şu şekilde değiştirmemiz gerekiyor:
glBegin(GL_TRIANGLES);
glColor4f(.2,.4,.2,1.0);
glVertex3f(-20.0,-15,20.0);
glVertex3f(20.0,-15,20.0);
glVertex3f(-20.0,-15,-20.0);
glColor4f(.2,.2,.4,1.0);
glVertex3f(20.0,-15,-20.0);
glVertex3f(-20.0,-15,-20.0);
glVertex3f(20.0,-15,20.0);
glEnd();
Böylece bir yeşil bir mavi üçgenimiz oldu. Aslında bir adım daha ileriye giderek her noktaya bir renk verebiliriz:
glBegin(GL_TRIANGLES);
glColor4f(.2,.4,.2,1.0);
glVertex3f(-20.0,-15,20.0);
glColor4f(.4,.4,.2,1.0);
glVertex3f(20.0,-15,20.0);
glColor4f(.4,.4,.4,1.0);
glVertex3f(-20.0,-15,-20.0);
glColor4f(.2,.2,.4,1.0);
glVertex3f(20.0,-15,-20.0);
glColor4f(.2,.4,.4,1.0);
glVertex3f(-20.0,-15,-20.0);
glColor4f(.4,.2,.4,1.0);
glVertex3f(20.0,-15,20.0);
glEnd();
Değişikliğe dikkat edin, üçgenlerimiz yine bir durum değişkenine uyarak "Gouraud" ışıklandırıldılar, yani kenar noktaları arasında renk değerleri doğrusal olarak dolduruldu.
Bu durumu kontrol eden komut:
glShadeModel(GL_SMOOTH)
satırı. İsterseniz şimdi diğer bölüme geçip, daha önceden ihmal ettiğimiz "durum" ayarlarına bir göz atalım.
F- Sahnenin çizim için ayarlanması
Programımızın yukarıdaki kısmına gelmeden önce kısa bir rutin ile bir takım ayarlar yapmıştık. Bu ayarlar, OpenGL'in aktardığımız veriyi nasıl yorumlayacağını düzenlemek üzere bir ihtiyaç doğdukça yapılan türden, örneğin kameranın konumu değiştiği zaman tekrar bir kamera transformasyonu ayarlamamız gerekiyor. Aşağıdaki kod sırası ile anlatılan işlemleri yapmakta:
// derinlik testi açık
glEnable(GL_DEPTH_TEST);
// ışığı renklendir
glLightfv(GL_LIGHT0, GL_SPECULAR, isik_renk);
// ışığı konumlandır
glLightfv(GL_LIGHT0, GL_POSITION, isik_konum);
// ışığı aç
glEnable(GL_LIGHT0);
// OpenGL ışıklandırma açık
glEnable(GL_LIGHTING);
// yüzey saklama açık
glEnable(GL_CULL_FACE);
// arka yüzeyleri sakla
glCullFace(GL_BACK);
// ışık ve rengin çalışması için gerekli
glEnable(GL_COLOR_MATERIAL);
// gouraud tipi ışıklandırma
glShadeModel(GL_SMOOTH);
// projeksiyon
glMatrixMode(GL_PROJECTION);
gluPerspective(
60.0, // 60 derece bakış açısı
1.0, // pixel deformasyon orantısı (y/x)
1.0, // yakın düzlem
10000.0); // uzak düzlem
// kamera transformasyonu
glMatrixMode(GL_MODELVIEW);
gluLookAt(
10.0, 20.0, 30.0, // göz noktası
0.0, -20.0, 0.0, // hedef
0.0, 1.0, 0.); // yukarı
Burada en önemli durum değişkenimiz GL_LIGHTING. Eğer bu durumu "enable" ile açık duruma getirmezsek pek birşey görmemiz mümkün değil. Sahnede en az bir adet ışık kaynağı bulunması gerekiyor ki cisimlerin derinlik ve konumlarını algılamamız kolay olsun. Tabii ışıkları aktif hale getirmeden önce glLight() ile konum ve renk bilgisini vermemiz gerekiyor. Göreceğimiz üzere glLight'ın ikinci parametresi pozisyon ya da renk bilgisini vermekte olduğumuzu belirtiyor.
gluLookAt, kamerayı konumlandırmak için kullanabileceğimiz en kolay rutin ve çok basit 3 parametre alıyor:
gluLookAt( kamera_pozisyonu, hedef_pozisyonu, yukarı_vektörü);
Burada "yukarı_vektörü", o anki projeksiyon düzleminin "yukarısı" için kabul ettiği yönü tarif etmek için kullanılıyor. Bunun ile oynayarak, örneğin bir oyunda karakterin adımları ile oluşan sağa-sola yaylanma hareketini (ve bu nedenle oluşan kamera sarsıntısını) vermek mümkün.
Bütün bunları bir araya koyarsak, temel bir OpenGL programının yapısı şu şekilde olmakta:
1- Sahneyi (ışık ve kamera) hazırla
2- Her ekran tazeleme işlemi için, sahneyi sil ve baştan çiz
Bu şekilde özetlediğimiz kadarıyla, OpenGL dünyanın en kolay grafik arabirimlerinden biri gibi görünmekte. Aslında gücünü de bu basit yapısından alıyor, söz ile anlatabileceğimiz her grafik problemini OpenGL kullanarak ekrana dökmek mümkün.
G- OpenGL komutlarının sözdizimi
Şu ana kadarki komutların bazıları, eğer fark ettiyseniz, sonlarında 2f, 3f gibi ekler içermekte. Bunun amacı, o komuta verilecek olan parametrelerin türünün kullanıcı tarafindan belirtilebilmesini sağlamak. Örneğin bir glColor4f komutu, aşağıdaki şekilleri alabilir:
**** glColor3b(GLbyte red, GLbyte green, GLbyte blue);
**** glColor3d(GLdouble red, GLdouble green, GLdouble blue);
**** glColor3f(GLfloat red, GLfloat green, GLfloat blue);
**** glColor3i(GLint red, GLint green, GLint blue);
**** glColor3s(GLshort red, GLshort green, GLshort blue);
**** glColor3ub(GLubyte red, GLubyte green, GLubyte blue);
**** glColor3ui(GLuint red, GLuint green, GLuint blue);
**** glColor3us(GLushort red, GLushort green, GLushort blue);
**** glColor4b(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha);
**** glColor4d(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha);
**** glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
**** glColor4i(GLint red, GLint green, GLint blue, GLint alpha);
**** glColor4s(GLshort red, GLshort green, GLshort blue, GLshort alpha);
**** glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);
**** glColor4ui(GLuint red, GLuint green, GLuint blue, GLuint alpha);
**** glColor4us(GLushort red, GLushort green, GLushort blue, GLushort alpha);
Kural gereği kullanılan ekleri atarak komutun adını glColor() olarak telaffuz ederiz. Anlaşılacağı üzere eklenen kısaltmalar bize parametrelerin 8, 16, 32 ya da 80(ya da 96) byte uzunluğunda olduğunu belirtiyor. Bu kullanılan renk aralığının hassasiyetini artırmaz, sadece kullanıcıya bir kolaylık sağlar. Dahili olarak tüm renkler ekran kartlarında tamsayı ya da "32 bit float" olarak işlenir.
Bütün parametre alan komutlar bu şekilde isimlendirilirer ve benzer kurallara uyarlar. Diğer komutların yazılışı için Visual C++ içerisinde yer alan yardım dokümanına ve gl.h dosyasına göz atmanızı tavsiye ederiz.
OpenGL, eklerle sürekli genişlemektedir. Bu yüzden yeni komutların yazılışı, parametreleri vb vb için <a href="http://www.opengl.org/">www.opengl.org</a> ya da SGI'daki dokümanlardan faydalanmamız gerekiyor.
H- Sonuç
Buraya kadar temel bilgileri edindik. Elimizdeki örnek kodun çizim kısmını değiştirerek, örneğin diskten veri okuyarak bir çok cismi çizmek mümkün. Fakat bu şekilde çok hızlı görüntü elde etmek olanaksız olduğu için, bir sonraki yazımızda öncelikle sistem belleğinde bulunan üçgen listelerinin çizimine başlayacağız. Ardından, "strip" yapısını anlatıp bunun avantaj / dezavantajlarına değinecek ve ilk doku kaplı yüzeyimizi çizeceğiz.
arago_W&B Çevrimdışı Mesajı Moderatöre bildir Alıntı ile Cevapla

Bunu ilk beğenen siz olun

Hata Oluştu



StevenT26
]->Eskişehirli<-[

grup tuttuğum takım
Teğmen Grup
Hat durumu Cinsiyet Özel mesaj 823 ileti
Yer: Mavi Geceler.!
İş: Fotography
Kayıt: 16-01-2009 07:19

işletim sistemim [+][+3][+5] [-]
#343632 08-09-2009 00:34 GMT-1 saat    
Konu taşındı..

Bunu ilk beğenen siz olun

Hata Oluştu




bağlantıyı göster (facebook ile) bağlantıyı göster (klasik üye girişi ile)
Sadece Şahlar Hamleleri Önceden Sezer!!!

Quick_man34
-- AdministratoR --

grup tuttuğum takım
Yüzbaşı Grup
Hat durumu Cinsiyet Özel mesaj 1028 ileti
Yer: Bangalore
İş: Although I would say
Kayıt: 25-08-2009 17:13

işletim sistemim [+][+3][+5] [-]
#344155 11-09-2009 18:00 GMT-1 saat    
Teşekkürler Taşıdını için.

Bunu ilk beğenen siz olun

Hata Oluştu



bekonic

grup tuttuğum takım
Yüzbaşı Grup
Hat durumu Cinsiyet Özel mesaj 1539 ileti
Yer: Antalya
İş:
Kayıt: 03-04-2006 15:23

işletim sistemim [+][+3][+5] [-]
#350714 21-12-2012 08:07 GMT-1 saat    
Teşekkürler paylaşım için.

Bunu ilk beğenen siz olun

Hata Oluştu


> 1 <