2010. március 21., vasárnap

Tao Start 08 - Glut szövegelés

Bármilyen programról is legyen, szó általában ki kell írni bizonyos dolgokat a képernyőre. A játékokban ilyen például, hogy mennyi töltényünk van, mennyi életünk maradt stb. Ezzel kapcsolatban rögtön eszünkbe jut, hogy mi lenne, ha mondjuk, egy 512×512-es textúrába megrajzolnánk az összes karaktert, amiből szöveget alkothatunk, majd kiíráskor ezeket a megfelelő módon bizonyos méretű négyzetekre húznánk. Ez nagyon jó ötlet, ezt használja például a Quake 3 a számok kijelzésére (életerő stb).
Már tudtunk textúrázni, szóval ezt megcsinálhatja mindenki maga. De mi van, ha nekünk nem kellenek textúrázott bitmap karakterek, nem akarunk minden karaktert magunk megrajzolni és nem akarunk memóriát pazarolni egy 512×512-es bitmapnak. Szerencsére a GLUT (OpenGL Utility Toolkit) tartalmaz erre a problémára eljárásokat. A GLUT az OpenGL kibővítése extra funkciókkal, később részletesebben is foglalkozunk vele. Hogyan lehet használni a Tao-ban a GLUT szolgáltatásait? Először is a References-ben fel kell venni a Tao.FreeGLUT.dll fájlt. Ezután a névterekhez adjuk hozzá a következő sort:
using Tao.FreeGlut;
Ezzel meg is lennénk, de még egy fontos dologra szükség van és ez pedig a nativ dll amire az Tao hivatkozik, vagyis a freeglut.dll, ha nem lenne meg akkor innen letölthető. Végül meg kell hívni a Form konstruktorában a Glut.glutInit(); sort amit minden más GLUT eljárás előtt kell meghívni, mert ez inicializálja a GLUT library-t.
Hogy minden világos legyen a továbbiakban kockaforgatónkat fogjuk kibővíteni. Az egyik módszer a bitmap karakterek használata. Ezt a glutBitmapCharacter eljárás segítségével használhatjuk. Ezt úgy kell elképzelni, hogy az így kirakott szöveg mindig megadott méretű lesz függetlenül ablakméretünktől. A szöveg mindig felénk néz, viszont pozíciójára hatnak a transzformációk (akárki akármit mond). Ezért ezt minden 3D-s rajzolgatás után kell kirakni, méghozzá úgy, hogy átváltunk ortogonális vetítésre, hisz ekkor nem lesznek koordináta problémáink, mert pixelben adhatjuk meg a pozíciót. Természetesen az ortogonális vetítés ablakméretét akkorára kell megadnunk, mint az OpenGL ablakunk mérete. Ezt le lehet kérdezni, de fölösleges kérdezgetni, mit bonyolítsuk a programot, ha úgyis mi állítottuk be az ablakunk méretét. Ekkor viszont felmerül a probléma, hogy mi van akkor, ha méretezzük az ablakunkat? Hát semmi, ezt is le tudjuk kezelni a Reshape metódusban. Az eljárás az így néz ki:
   private void Reshape()
   {
       if (glControl.Height == 0)
       {
           ClientSize = new Size(glControl.Width, 1);
       }

       scrw = glControl.Width;
       scrh = glControl.Height;
       aspect = scrw / scrh;

       swPerspective();
   }
Most írnunk kell két eljárást, amik kapcsolnak Ortho, illetve Perspective nézetre, hogy ne csúnyítsuk majd el a kiíró eljárásunkat 5 soros vetítésváltásokkal. A két eljárás:
   private void swOrtho()
   {
       Gl.glPushMatrix();
       {
           Gl.glMatrixMode(Gl.GL_PROJECTION);
           Gl.glLoadIdentity();
           Gl.glOrtho(0, scrw, 0, scrh, -100, 100);
           Gl.glMatrixMode(Gl.GL_MODELVIEW);
       }
       Gl.glPopMatrix();
   }

   private void swPerspective()
   {
       Gl.glPushMatrix();
       {
           Gl.glMatrixMode(Gl.GL_PROJECTION);
           Gl.glLoadIdentity();

           Gl.glViewport(0, 0, (int)scrw, (int)scrh);
           Glu.gluPerspective(45, aspect, 0.1, 1000);

           Gl.glMatrixMode(Gl.GL_MODELVIEW);
           Gl.glLoadIdentity();
       }
       Gl.glPopMatrix();
   }
Az swOrtho()-t használva ortogonális, az swPerspective() pedig perspektív nézetre vált. Erről eszünkbe juthat, hogy a programunk elején lévő perspektív nézet beállítót is egyszerűen átírhatnánk egy swPerspective() hívásra...javaslom tegyük is meg! (Csak úgy megemlíteném, hogy ha ablakméret független kiírást akarunk csinálni, akkor az ortho nézetet ne scrw és scrh-ra állítsátok, hanem 1,1-re és ekkor a raszterpozíciót [0,1] tartományon kell majd megadni.) Na! Most már jöhet a kiírós dolog. Először a kiíró eljárás:
   private void DrawText(float x, float y, string text)
   {
       swOrtho();
       Gl.glPushMatrix();
       {
           Gl.glDisable(Gl.GL_DEPTH_TEST);
      
           Gl.glRasterPos2f(x, y);

           foreach (char c in text)
           {
               Glut.glutBitmapCharacter(Glut.GLUT_BITMAP_HELVETICA_12, c);
           }

           Gl.glEnable(Gl.GL_DEPTH_TEST);
       }
       Gl.glPopMatrix();
       swPerspective();
   }
Az swOrtho() és swPerspective() után kell megadni, hogy lássa ezeket. Először átváltunk ortho nézetbe. Majd kikapcsoljuk a mélység tesztet. Nyilván nem fogja semmi takarni a szövegünket, de azért fő a biztonság. Majd beállítjuk a megadott paraméter alapján a képernyőn a raszterpozíciót a glRasterPos2f segítségével. A raszter pozíció úgy működik, mint a Descartes-féle koordináta rendszer jobb felső negyede. Tehát a 0, 0 pont az ablakunk bal alsó sarka, az x koordináta jobbra növekszik, az y koordináta pedig felfele növekszik. Miután a pozíció be lett lőve karakterenként kiírjuk a szöveget. Ez a már említett glutBitmapCharacter eljárással történik. Első paraméterben meg kell adni a betűtípust másodikban pedig a kiírandó karakter kódját. A raszterpozíciót automatikusan viszi tovább x irányban. Szóval nincs más dolgunk, csak egy for ciklussal végiggyalogolni a kiírandó szövegünkön. A következő bitmap betűtípusok vannak:
  • GLUT_BITMAP_9_BY_15
  • GLUT_BITMAP_8_BY_13
  • GLUT_BITMAP_TIMES_ROMAN_10
  • GLUT_BITMAP_TIMES_ROMAN_24
  • GLUT_BITMAP_HELVETICA_10
  • GLUT_BITMAP_HELVETICA_12
  • GLUT_BITMAP_HELVETICA_18
Próbáljuk ki, hogy melyik milyen. Most már nincs más dolgunk, csak az, hogy meghívjuk a kiírót. Ezt a kirajzoló eljárásunkban a fő glPushMatrix-glPopMatrix páron kívül kell meghívni, hiszen így nem hatnak rá az ott bekövetkező transzformációk. Csak, hogy értsétek:
   private void glControl_Paint(object sender, PaintEventArgs e)
   {
       Gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
       Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

       Gl.glPushMatrix();
       {
           Gl.glTranslatef(0.0f, 0.0f, -2.0f);
           Gl.glRotatef(alfa, 0.5f, 1f, 0f);
           DrawCube();
       }
       Gl.glPopMatrix();

       Gl.glColor3f(0, 1, 0);
       DrawText(50, 50, "Ez egy zöld szöveg, hmmm…");
       Gl.glColor3f(1, 0, 0);
       DrawText(50, 100, "Ez pedig egy piros szöveg, wow");
       Gl.glColor3f(1, 1, 1);

       Gl.glFlush();
   }
Általában amit csak úgy a képernyőre kell rajzolni, pl. szöveg, HUD stb azt a rajzolás legvégén kell megtenni. Amint látszik a glColor3f-el lehet ennek is a színét befolyásolni. Ha megváltoztattátok a színt, akkor a végén írjátok vissza fehérre (1,1,1), mert ez a szín, amit beállítunk ugyanúgy hat a továbbiakban a textúrákra, mint a bitmap fontra.

Nagyjából ennyi az egész, remélem érthető volt minden. A példa teljes forrása itt tölthető le:


A fenti tutorial PowR által írt a http://free-pascal.extra.hu/ oldalon megtalálható OGL tutorial alapján készült el. Köszönet érte PowR-nek, valamint Kuba Attila - OGL Programozása jegyzetének!

4 megjegyzés :

  1. Hali Péter!

    Már régóta olvasom az oldalad. Ehhez az íráshoz csak egy észrevételem lenne: te hogy a csudába érted el az ékezetes betűk kiírását??? Mert nekem ez baromira nem megy.

    VálaszTörlés
  2. na erre a blogger szarságra se akartam volna regelni....
    viszont a válaszod érdekel
    glut-ot használok meg c++-ban próbálkozom

    VálaszTörlés
  3. Szia tobibandi!
    Örülök, hogy érdekesnek találsz pár bejegyzést ezen az oldalon. Én a FreeGlut-t használtam ebben a példában ami nem ugyan az mint a sima Glut. De arra emlékszem, hogy a hosszú ő és ű karakterekkel nálam is volt probléma. Azt javaslom felejtsd el a Glut függvénykönyvtárat, mára már elavult és old meg a szövegek kezelését máshogy pl. textúrák használatával, számos példa van fent a neten.

    VálaszTörlés
  4. :D jó, hogy egy év után visszatalálok ide :) szóval most eddig MINDEN jó volt, most újratettem a win-t és nekem sem ír ő és ű -t. (még mindig a glut-tal szarakodok... mondjuk eddig gond nélkül ment). Eddig úgy oldottam meg a szöveg kiiratását, hogy textúrán volt a karakterkészlet, és ezt daraboltam szét - nem nehéz. Viszont most ugyanaz a progi nem írja ki az ő és ű betűket. Valami ötlet esetleg? Vagy arra ötlet, hogy hogyan fordítsam át az egészet sima winapi-ra?
    thx - tobibandi

    VálaszTörlés