OpenGL Game Tutorial
Visual C++ 6.0



Načítání souborů 3DStudioMax (*.3ds)


Příklad a zdroják ke stažení (2 392 k)


Load 3DStudioMax souborů (*.3ds)
Vykreslení 3DS modelu
Načítání textur ze souboru (*.jpg)

glEnable( GL_TEXTURE_2D );
glDisable( GL_TEXTURE_2D );
glColor3ub();
glBindTexture();
glBegin( GL_TRIANGLES );
glBegin( GL_LINE_LOOP );
glEnd();
glNormal3f();
glTexCoord2f();
glVertex3f();
glGenTextures();
glTexParameteri();
glTexImage2D();
glPushMatrix();
glPopMatrix();
glTranslatef();
glEnable( GL_COLOR_MATERIAL );




Load 3DStudioMax souborů (*.3ds)

Zdrojový kód načítání a vykreslování modelů 3DS vytvořil kolega Jiří Studnička (Well), inpirovaný tutorialem od DigiBena. Já jsem jen popsal každý řádek kódu, trochu kód upravil a teď budu dělat chytrýho v tomto tutorialu.

Takže, na podrobný anglický popis fomátu (*.3ds) můžete mrknou zde. Všechny hodnoty tohoto formátu jsou uspořádány do bytového stromu. V každé větvi stromu jsou uloženy specifické hodnoty. Tento strom vypadá takto:


///////////////////////////////////////////
//                                                  ///
// * STROM BYTOVYCH DAT SOUBORU 3DS *     ///
//                                                  ///
///////////////////////////////////////////
//
//
//      MAIN3DS  (0x4D4D)
//     |
//     +--EDIT3DS  (0x3D3D)
//     |  |
//     |  +--EDIT_MATERIAL (0xAFFF)
//     |  |  |
//     |  |  +--MAT_NAME01 (0xA000) (See mli Doc)
//     |  |
//     |  +--EDIT_CONFIG1  (0x0100)
//     |  +--EDIT_CONFIG2  (0x3E3D)
//     |  +--EDIT_VIEW_P1  (0x7012)
//     |  |  |
//     |  |  +--TOP            (0x0001)
//     |  |  +--BOTTOM         (0x0002)
//     |  |  +--LEFT           (0x0003)
//     |  |  +--RIGHT          (0x0004)
//     |  |  +--FRONT          (0x0005)
//     |  |  +--BACK           (0x0006)
//     |  |  +--USER           (0x0007)
//     |  |  +--CAMERA         (0xFFFF)
//     |  |  +--LIGHT          (0x0009)
//     |  |  +--DISABLED       (0x0010)
//     |  |  +--BOGUS          (0x0011)
//     |  |
//     |  +--EDIT_VIEW_P2  (0x7011)
//     |  |  |
//     |  |  +--TOP            (0x0001)
//     |  |  +--BOTTOM         (0x0002)
//     |  |  +--LEFT           (0x0003)
//     |  |  +--RIGHT          (0x0004)
//     |  |  +--FRONT          (0x0005)
//     |  |  +--BACK           (0x0006)
//     |  |  +--USER           (0x0007)
//     |  |  +--CAMERA         (0xFFFF)
//     |  |  +--LIGHT          (0x0009)
//     |  |  +--DISABLED       (0x0010)
//     |  |  +--BOGUS          (0x0011)
//     |  |
//     |  +--EDIT_VIEW_P3  (0x7020)
//     |  +--EDIT_VIEW1    (0x7001)
//     |  +--EDIT_BACKGR   (0x1200)
//     |  +--EDIT_AMBIENT  (0x2100)
//     |  +--EDIT_OBJECT   (0x4000)
//     |  |  |
//     |  |  +--OBJ_TRIMESH   (0x4100)
//     |  |  |  |
//     |  |  |  +--TRI_VERTEXL          (0x4110)
//     |  |  |  +--TRI_VERTEXOPTIONS    (0x4111)
//     |  |  |  +--TRI_MAPPINGCOORS     (0x4140)
//     |  |  |  +--TRI_MAPPINGSTANDARD  (0x4170)
//     |  |  |  +--TRI_FACEL1           (0x4120)
//     |  |  |  |  |
//     |  |  |  |  +--TRI_SMOOTH            (0x4150)
//     |  |  |  |  +--TRI_MATERIAL          (0x4130)
//     |  |  |  |
//     |  |  |  +--TRI_LOCAL            (0x4160)
//     |  |  |  +--TRI_VISIBLE          (0x4165)
//     |  |  |
//     |  |  +--OBJ_LIGHT    (0x4600)
//     |  |  |  |
//     |  |  |  +--LIT_OFF              (0x4620)
//     |  |  |  +--LIT_SPOT             (0x4610)
//     |  |  |  +--LIT_UNKNWN01         (0x465A)
//     |  |  |
//     |  |  +--OBJ_CAMERA   (0x4700)
//     |  |  |  |
//     |  |  |  +--CAM_UNKNWN01         (0x4710)
//     |  |  |  +--CAM_UNKNWN02         (0x4720)
//     |  |  |
//     |  |  +--OBJ_UNKNWN01 (0x4710)
//     |  |  +--OBJ_UNKNWN02 (0x4720)
//     |  |
//     |  +--EDIT_UNKNW01  (0x1100)
//     |  +--EDIT_UNKNW02  (0x1201)
//     |  +--EDIT_UNKNW03  (0x1300)
//     |  +--EDIT_UNKNW04  (0x1400)
//     |  +--EDIT_UNKNW05  (0x1420)
//     |  +--EDIT_UNKNW06  (0x1450)
//     |  +--EDIT_UNKNW07  (0x1500)
//     |  +--EDIT_UNKNW08  (0x2200)
//     |  +--EDIT_UNKNW09  (0x2201)
//     |  +--EDIT_UNKNW10  (0x2210)
//     |  +--EDIT_UNKNW11  (0x2300)
//     |  +--EDIT_UNKNW12  (0x2302)
//     |  +--EDIT_UNKNW13  (0x2000)
//     |  +--EDIT_UNKNW14  (0xAFFF)
//     |
//     +--KEYF3DS (0xB000)
//        |
//        +--KEYF_UNKNWN01 (0xB00A)
//        +--............. (0x7001) ( viewport, same as editor )
//        +--KEYF_FRAMES   (0xB008)
//        +--KEYF_UNKNWN02 (0xB009)
//        +--KEYF_OBJDES   (0xB002)
//           |
//           +--KEYF_OBJHIERARCH  (0xB010)
//           +--KEYF_OBJDUMMYNAME (0xB011)
//           +--KEYF_OBJUNKNWN01  (0xB013)
//           +--KEYF_OBJUNKNWN02  (0xB014)
//           +--KEYF_OBJUNKNWN03  (0xB015)
//           +--KEYF_OBJPIVOT     (0xB020)
//           +--KEYF_OBJUNKNWN04  (0xB021)
//           +--KEYF_OBJUNKNWN05  (0xB022)
//
//
//
//


Podle tohoto bytového stromu si vytvoříme definice.

     // Struktura souboru je tvořena z bytového stromu. Každá větev tohoto stromu obsahuje specifickou
         // informaci o své délce v bytech.

         // Definuj základní kmen ( Chunk ), bytového stromu souboru
     #define PRIMARY       0x4D4D

     // Definuj hlavní větev bytového stromu
     #define OBJECTINFO    0x3D3D                     // Obsahuje informace o skupině objektů. Objekt se nazývá též "Mesh" 
     #define VERSION       0x0002                     // Obsahuje informace o verzi *.3ds souboru
     #define EDITKEYFRAME  0xB000                     // Obsahuje infomace o animaci modelu

     // Definuj větev informací o objektu
     #define MATERIAL       0xAFFF                    // Obsahuje informace o texturách
     #define OBJECT         0x4000                    // Obsahuje informace o trojúhelnících, vrcholech, atd.

     // Definuj větev informací o materiálu
     #define MATNAME       0xA000                     // Obsahuje informace o jménu materiálu
     #define MATDIFFUSE    0xA020                     // Obsahuje info o barvě RGB materiálu
     #define MATMAP        0xA200                     // Hlavička pro nový materiál
     #define MATMAPFILE    0xA300                     // Info o jménu textury

     #define OBJECT_MESH   0x4100                     // Info, zda se má vytvořit nová "Mesh", nový objekt

     // Definuj novou větev "Mesh". nový objekt
     #define OBJECT_VERTICES     0x4110               // Vrcholy objektu
     #define OBJECT_TRIANGLES    0x4120               // Trojúhelníky objektu
     #define OBJECT_MATERIAL     0x4130               // Info o materiálu, buď je tvořen z textury, nebo barvy
     #define OBJECT_UV           0x4140               // UV texturová koordinace

A struktury

     // Pomocná struktura pro definování 2D vektoru. Hodnota x = pf[0], y = pf[1]
     struct TVector2d
     {
          float pf[2];
     };

     // Struktura informací o větvích ( chunk ) 
     struct TChunk
     {
          unsigned short int ui_id;                    // Identifikace větví  
          unsigned int ui_length;                      // Délka větve
          unsigned int ui_bytes_read;                  // Množství bytů potřebných pro čtení větve
     };

     // Struktura informací o trojúhelnících, ze kterých je každý objekt tvořen.
          // Struktura uchovává informace o souřadnicích
          // a texturových koordinacích vrcholů trojúhelníka.
     struct TTriangle
     {
          int i_vert_index[3];               // Informace o souřadnicích vrcholů trojúhelníka
          int i_coord_index[3];              // Informace o texturové koordinaci trojúhelníka
     };

     // Struktura informací o materialu. Obsahuje specifické jméno textury a pozici
          // obrazového souboru, ze kterého se textura tvoří. Struktura dále obsahuje
          // barvu objektu zadanou v R = uc_color[0], G = uc_color[1], B = uc_color[2].
          // Další hodnoty se nepoužívaj, ale eventuelně se použít mohou. Jde o informaci
          // o dlaždicovitém UV rozložení textury. 
     struct TMaterial
     {
          char  c_tex_name[255];              // Jméno textury
          char  c_tex_file_name[255];         // Cesta k obrázku, ze kterého se vytváří textura
          unsigned char  uc_color[3];         // Barva objektu
          int   i_tex_id;                     // the texture ID
          float f_u_tile_tex;                 // "u" poměr dlaždicovitého rozložení textury (nepoužívá se)
          float f_v_tile_tex;                 // "v" poměr dlaždicovitého rozložení textury (nepoužívá se)
          float f_u_offset_tex;               // "u" odchylky dlaždicovitého rozložení textury (nepoužívá se)
          float f_v_offset_tex;               // "v" odchylky dlaždicovitého rozložení textury (nepoužívá se)
     };

     // Struktura informací o objektu.
     struct TObject
     {
          int  i_num_vert;              // Počet vrcholů objektu
          int  i_num_triangle;          // Počet trojúhelníků objektu
          int  i_num_tex_coord;         // Počet texturových koordinací objektu
          unsigned  ui_material_id;     // Identifikátor materiálu (textury) objektu
          bool b_texture;               // Určuje zda je zapnuto, nebo vypnuto texturování objektu
          char c_name[255];             // Jméno objektu
          K_CVector  *pvec_vert;        // Vrcholy objektů
          K_CVector  *pvec_normal;      // Normálové vektory vrcholů objektů
          TVector2d *ptvec2d_coord;     // Texturová koordinace objektů
          TTriangle *pttriangle;        // Informace o trojúhelnících
     };

     // Struktura informací o modelu. Model je tvořen z jednoho, nebo několika objektů. 
     struct TModel
     {
          int i_num_objects;                 // Počet objektů modelu
          int i_num_materials;               // Počet materiálů modelu
          vector<TMaterial> vtmaterial;      // Seznam informací o materiálech (textury a barvy)
          vector<TObject> vtobject;          // Seznam informací o objektech modelu
     };

Načítání souboru 3DS provádí funkce Load3ds(), která volá další pomocné funkce.

// Načti soubor *.3ds
bool K_CLoad3ds::Load3ds( TModel *ptmodel, char *pc_filename )
{
     char pc_msg[255] = {0};          // Pomocná proměnná která bude obsahovat text případné chybové hlášky

     // Otevři soubor 3DS
     pfileStream = fopen( pc_filename, "rb" );

     // Jestliže se soubor nepodařilo otevřít, tak vyhoď chybovou hlášku
     if( !pfileStream )
     {
          sprintf( pc_msg, "Unable to find the file: %s!", pc_filename );
          MessageBox(NULL, pc_msg, "Error", MB_OK);
          return false;
     }

     // Zkus číst první větev souboru, která obsahuje primární informace o identifikátoru souboru a jeho délce  v bytech, jinak
     if( !ReadChunk(ptchunkCurrent ))
     {
          return false;     // Vyskoč z této funkce
     }

     // Jestliže id aktuální větve neodpovídá definované struktuře PRIMARY, tak vyhoď chybovou hlášku
     if ( ptchunkCurrent->ui_id != PRIMARY )
     {
          sprintf( pc_msg, "Unable to load PRIMARY chuck from file: %s!", pc_filename );
          MessageBox( NULL, pc_msg, "Error", MB_OK );
          return false;
     }

     // Začni načítat všechny hodnoty objektů
     ProcessNextChunk( ptmodel, ptchunkCurrent );

     // Vypočítej normálové vektory trojúhelníků, pro správné stínování
     ComputeNormals( ptmodel );

     // Vymaž všechny pomocné hodnoty z paměti a zavři obraz načítaného souboru
     CleanUp();

     return 0;
}

Funkce Load3ds() volá pomocnou funkci ProcessNextChunk(), která načte všechny hodnoty.

// Začni načítat všechny hodnoty objektů. Tato funkce je volána rekurzivně, to znamená z vnějšku vlastní funkce
bool K_CLoad3ds::ProcessNextChunk( TModel *ptmodel, TChunk *ptchunk_prev )
{
     TObject tobject_new = {0};           // Nová struktura objektu, která se používá se pro přídání seznamu objektů
     TMaterial tmaterial_new = {0};       // Nová struktura materiálu, která se používá se pro přídání seznamu materiálů
     unsigned short ui_version = 0;       // Proměnná uchovává verzi souboru
     int pi_buffer[50000] = {0};          // Proměnná uchovává načtená data

     ptchunkCurrent = new TChunk;         // Alokuj novou větev

     // Cykluj pokud množství bytů potřebných pro čtení větve je menší než délka větve
     while ( ptchunk_prev->ui_bytes_read < ptchunk_prev->ui_length )
     {
          // Zkus číst další větev, jinak
          if( !ReadChunk( ptchunkCurrent ))
          {
               return false;     // Vyskoč z funkce
          }

          // Zkontroluj ID větve
          switch ( ptchunkCurrent->ui_id )
          {
          case VERSION:     // Informace o verzi souboru

               // Čti verzi souboru
               ptchunkCurrent->ui_bytes_read += fread( &ui_version, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream);

               // Pokud je verze vyšší než 3, tak vyhoď chybovou hlášku
               if ( ui_version > 0x03 )
                    MessageBox( NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK );
               break;     // Vyskoč z větve 

          case OBJECTINFO:     // Obsahuje informace o skupině objektů. Objekt se nazývá též "Mesh" 

               // Zkus číst další větev, jinak
               if( !ReadChunk( ptchunkTemp ))
               {
                    return false;     // Vyskoč z funkce
               }

               // Získej verzi objektu
               ptchunkTemp->ui_bytes_read += fread( &ui_version, 1, ptchunkTemp->ui_length - ptchunkTemp->ui_bytes_read, pfileStream );

               // Přidej k velikosti načítaných dat, velikost dat informace o skupině objektů   
               ptchunkCurrent->ui_bytes_read += ptchunkTemp->ui_bytes_read;

               // Běž na další větev. Volej (rekurzivně) funkci, ve které se nacházíš
               ProcessNextChunk( ptmodel, ptchunkCurrent );
               break;     // Vyskoč z větve

          case MATERIAL:     // Obsahuje informace o materiálu

               // Přičti počet materiálů
               ptmodel->i_num_materials++;

               // Použij funkci STL "vector" třídy, která přidá novou strukturu materiálu do seznamu 
               ptmodel->vtmaterial.push_back( tmaterial_new );

               // Načti hodnoty materiálu
               ProcessNextMaterialChunk( ptmodel, ptchunkCurrent );
               break;     // Vyskoč z větve

          case OBJECT:     // Obsahuje informace o trojúhelnících, vrcholech, atd 

               // Přičti počet objektů
               ptmodel->i_num_objects++;

               // Použij funkci STL "vector" třídy, která přidá novou strukturu objektu do seznamu
               ptmodel->vtobject.push_back( tobject_new );

               // Vyplň druhým parametrem "0" obsah paměti, který je daný sizeof()
               memset( &( ptmodel->vtobject[ptmodel->i_num_objects - 1] ), 0, sizeof( TObject ));

               // Získej jméno objektu. Množství bytů jména objektu přidej k bytům aktuální větve.
               // GetString() čte data (jméno objektu) a vráťí délku celého řetězce
               ptchunkCurrent->ui_bytes_read += GetString( ptmodel->vtobject[ptmodel->i_num_objects - 1].c_name );

               // Načti všechny hodnoty objektu
               ProcessNextObjectChunk( ptmodel, &( ptmodel->vtobject[ptmodel->i_num_objects - 1] ), ptchunkCurrent );
               break;     // Vyskoč z větve

          case EDITKEYFRAME:     // Obsahuje infomace o animaci modelu

               // Tyto data nebudem načítát. Jde o infomace o animaci objektu.
                              // Umíme již načítat animované souboru *.ms3d, takže to
                              // ani potřebovat nebudeme

               // Přidej k množství bytů původní větve, množství bytů této větve
               ptchunkCurrent->ui_bytes_read += fread( pi_buffer, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
               break;     // Vyskoč z větve

          default:     // Pokud žádná větev neodpovídá, provádí se tato základní 

               // Přidej k množství bytů původní větve, množství bytů ignorované, nebo neznámé větve
               ptchunkCurrent->ui_bytes_read += fread( pi_buffer, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
               break;     // Vyskoč z větve
          }

          // Přidej k množství bytů původní větve, množství bytů větve aktuální 
          ptchunk_prev->ui_bytes_read += ptchunkCurrent->ui_bytes_read;
     }

     delete ptchunkCurrent;                  // Uvolni paměť aktuální větve
     ptchunkCurrent = ptchunk_prev;          // Nastav původní větev jako aktuální

     return true;     // Pokud jsi došel až sem, tak vrať funkci true
}


Funkce ProcessNextChunk() opět volá další pomocné funkce, které finálně načítají hodnoty. Pomocná funkce ReadChunk() čte hodnoty identifikátorů (ID) aktuální větve a její délku.

// Čti větev souboru 
bool K_CLoad3ds::ReadChunk( TChunk *ptchunk )
{
     // Čti 2 bytovou informaci o ID souboru 
     ptchunk->ui_bytes_read = fread( &ptchunk->ui_id, 1, 2, pfileStream );

     // Čti 4 bytovou informaci o délce souboru
     ptchunk->ui_bytes_read += fread( &ptchunk->ui_length, 1, 4, pfileStream );

     if( !ptchunk->ui_bytes_read )     // Jestliže je špatný formát *.3ds, tak
     {
          MessageBox( NULL, "Invalid file format *.3ds", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu a
          return false;          // Ukonči funkci
     }

     return true;     // Pokud vše proběhne OK, tak vrať funkci true
}

Pomocná funkce ProcessNextMaterialChunk() čte hodnoty materiálu.

// Načti hodnoty materiálu. Tato funkce je volána rekurzivně, to znamená z vnějšku vlastní funkce
bool K_CLoad3ds::ProcessNextMaterialChunk( TModel *ptmodel, TChunk *ptchunk_prev )
{
     int pi_buffer[50000] = {0};      // Proměnná uchovává načtená data

     ptchunkCurrent = new TChunk;     // Alokuj novou větev

     // Cykluj pokud množství bytů potřebných pro čtení větve je menší než délka větve
     while ( ptchunk_prev->ui_bytes_read < ptchunk_prev->ui_length )
     {
          // Zkus číst další větev, jinak
          if( !ReadChunk( ptchunkCurrent ))
          {
               return false;     // Vyskoč z funkce
          }

          // Zkontroluj ID větve
          switch ( ptchunkCurrent->ui_id )
          {
          case MATNAME:                  // Obsahuje informace o jménu materiálu

               // Čti jméno textury
               ptchunkCurrent->ui_bytes_read += fread( ptmodel->vtmaterial[ptmodel->i_num_materials - 1].c_tex_name, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
               break;     // Vyskoč z větve

          case MATDIFFUSE:               // Obsahuje info o barvě RGB materiálu

               // Čti hodnoty barvy RGB materiálu
               ReadColorChunk( &( ptmodel->vtmaterial[ptmodel->i_num_materials - 1] ), ptchunkCurrent );
               break;     // Vyskoč z větve

          case MATMAP:                   // Hlavička pro nový materiál

               // Načti další informace o novém materiálu. Volej funkci, ve které se nacházíš (rekurzivní volání)
               ProcessNextMaterialChunk( ptmodel, ptchunkCurrent );
               break;     // Vyskoč z větve

          case MATMAPFILE:               // Info o jménu textury

               // Načti jméno textury
               ptchunkCurrent->ui_bytes_read += fread( ptmodel->vtmaterial[ptmodel->i_num_materials - 1].c_tex_file_name, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
               break;     // Vyskoč z větve

          default:               // Pokud žádná větev neodpovídá, provádí se tato základní

               // Přidej k množství bytů původní větve, množství bytů ignorované, nebo neznámé větve
               ptchunkCurrent->ui_bytes_read += fread( pi_buffer, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
               break;     // Vyskoč z větve
          }

          // Přidej k množství bytů původní větve, množství bytů větve aktuální 
          ptchunk_prev->ui_bytes_read += ptchunkCurrent->ui_bytes_read;
     }

     delete ptchunkCurrent;             // Uvolni paměť aktuální větve
     ptchunkCurrent = ptchunk_prev;     // Nastav původní větev jako aktuální

     return true;          // Pokud jsi došel až sem, tak vrať funkci true
}

Pomocná funkce ProcessNextObjectChunk() čte hodnoty objektu.

// Načti hodnoty objektu. Tato funkce je volána rekurzivně, to znamená z vnějšku vlastní funkce 
bool K_CLoad3ds::ProcessNextObjectChunk( TModel *ptmodel, TObject *ptobject, TChunk *ptchunk_prev )
{
     int pi_buffer[50000] = {0};          // Proměnná uchovává načtená data

     // Alokuj novou větev
     ptchunkCurrent = new TChunk;

     // Cykluj pokud množství bytů potřebných pro čtení větve je menší než délka větve
     while ( ptchunk_prev->ui_bytes_read < ptchunk_prev->ui_length )
     {
          // Zkus číst další větev, jinak
          if( !ReadChunk( ptchunkCurrent ))
          {
               return false;     // Vyskoč z funkce
          }

          // Zkontroluj ID větve
          switch ( ptchunkCurrent->ui_id )
          {
          case OBJECT_MESH:              // Info, zda se má vytvořit nová "Mesh", nový objekt

               // Jestliže jsi našel nový objekt tak volej znovu vlastní funkci. Rekurzivní volání.
               ProcessNextObjectChunk( ptmodel, ptobject, ptchunkCurrent );
               break;     // Vyskoč z větve

          case OBJECT_VERTICES:          // Vrcholy objektu

               // Čti hodnoty vrcholů objektu
               ReadVertices( ptobject, ptchunkCurrent );
               break;     // Vyskoč z větve

          case OBJECT_TRIANGLES:         // Trojúhelníky objektu

               // Čti hodnoty trojúhelníků objektu
               ReadTriangles( ptobject, ptchunkCurrent );
               break;     // Vyskoč z větve

          case OBJECT_MATERIAL:          // Info o materiálu, buď je tvořen z textury, nebo barvy

               // Čti hodnoty materiálu
               ReadObjectMaterial( ptmodel, ptobject, ptchunkCurrent );
               break;     // Vyskoč z větve

          case OBJECT_UV:               // UV texturová koordinace

               // Čti UV texturové koordinace
               ReadUVCoordinates( ptobject, ptchunkCurrent );
               break;     // Vyskoč z větve

          default:                      // Pokud žádná větev neodpovídá, provádí se tato základní

               // Přidej k množství bytů původní větve, množství bytů ignorované, nebo neznámé větve
               ptchunkCurrent->ui_bytes_read += fread( pi_buffer, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
               break;     // Vyskoč z větve
          }

          // Přidej k množství bytů původní větve, množství bytů větve aktuální 
          ptchunk_prev->ui_bytes_read += ptchunkCurrent->ui_bytes_read;
     }

     delete ptchunkCurrent;                  // Uvolni paměť aktuální větve
     ptchunkCurrent = ptchunk_prev;          // Nastav původní větev jako aktuální

     return true;     // Pokud jsi došel až sem, tak vrať funkci true
}

Tato funkce opět volá další pomocné funkce. Pomocná funkce ReadVertices() načítá hodnoty vrcholů objektu.

// Čti hodnoty vrcholů objektu
void K_CLoad3ds::ReadVertices( TObject *ptobject, TChunk *ptchunk_prev )
{
     // Načti počet vrcholů
     ptchunk_prev->ui_bytes_read += fread( &( ptobject->i_num_vert), 1, 2, pfileStream );

     // Alokuj paměť pro všechny vrcholy třídy K_CVector
     ptobject->pvec_vert = new K_CVector [ptobject->i_num_vert];

     // Vyplň druhým parametrem "0" obsah paměti, který je daný sizeof() 
     memset( ptobject->pvec_vert, 0, sizeof( K_CVector ) * ptobject->i_num_vert );

     // Čti hodnoty pole vrcholů
     ptchunk_prev->ui_bytes_read += fread( ptobject->pvec_vert, 1, ptchunk_prev->ui_length - ptchunk_prev->ui_bytes_read, pfileStream );
}

Pomocná funkce ReadTriangles() načítá hodnoty trojúhelníků objektu.

// Čti hodnoty trojúhelníků objektu
void K_CLoad3ds::ReadTriangles( TObject *ptobject, TChunk *pchunk_prev )
{
     unsigned short ui_index = 0;     // Index trojúhelníka

     // Čti počet trojúhelníků, které tvoří objekt
     pchunk_prev->ui_bytes_read += fread( &ptobject->i_num_triangle, 1, 2, pfileStream );

     // Alokuj paměť pro trojúhelníky struktury TTriangle
     ptobject->pttriangle = new TTriangle[ptobject->i_num_triangle];

     // Vyplň druhým parametrem "0" obsah paměti, který je daný sizeof()
     memset( ptobject->pttriangle, 0, sizeof( TTriangle ) * ptobject->i_num_triangle );

     // Cykluj dokud nenačteš všechny trojúhelníky
     for( int i = 0; i < ptobject->i_num_triangle; i++ )
     {
          // Cykluj a načítej 4 hodnoty. 
          // Potřebujeme však jen 3 hodnoty (trojúhelník ma 3 vrcholy, jak jinak), proto 4 hodnotu načítat nebudeme 
          for( int j = 0; j < 4; j++ )
          {
               // Čti index vrcholu trojúhelníka
               pchunk_prev->ui_bytes_read += fread(&ui_index, 1, sizeof(ui_index), pfileStream);

               // Čti jenom první 3 hodnoty vrcholů, 4 vynech 
               if(j < 3)
               {
                    // Načti hodnoty vrcholů trojúhelníka
                    ptobject->pttriangle[i].i_vert_index[j] = ui_index;
               }
          }
     }
}

Pomocná funkce ReadObjectMaterial() načítá hodnoty materiálů (textur) objektu.

// Čti hodnoty materiálu
void K_CLoad3ds::ReadObjectMaterial( TModel *ptmodel, TObject *ptobject, TChunk *ptchunk_prev )
{
     char pc_tex_name[255] = {0};     // Proměnná uchovává jméno textury
     int pi_buffer[50000] = {0};      // Proměnná uchovává načtená data

     // Získej jméno textury.
     // Množství bytů jména textury přidej k bytům původní větve.
     // GetString() čte data (jméno textury) a vráťí délku celého řetězce
     ptchunk_prev->ui_bytes_read += GetString( pc_tex_name );

     // Cykluj dokud nenačteš všechny materiály (textury)
     for( int i = 0; i < ptmodel->i_num_materials; i++ )
     {
          // Jestliže půjde načíst jméno textury, tak
          if( strcmp( pc_tex_name, ptmodel->vtmaterial[i].c_tex_name ) == 0 )
          {
               // Nastav ID materiálu (textury)
               ptobject->ui_material_id = i;

               // Jestliže se podařilo načíst jméno textury
               if( strlen( ptmodel->vtmaterial[i].c_tex_file_name ) > 0 )
               {
                    // Tak zapni texturování
                    ptobject->b_texture = true;
               }
               break;     // Vyskoč z podmínky if
          }
          else            // Jinak
          {
               // Nastav ID materiálu na -1
               ptobject->ui_material_id = -1;
          }
     }
     // Přidej k množství bytů původní větve, množství bytů větve aktuální
     ptchunk_prev->ui_bytes_read += fread( pi_buffer, 1, ptchunk_prev->ui_length - ptchunk_prev->ui_bytes_read, pfileStream );
}

A máme tu další pomocnou funkci funkce ReadObjectMaterial() a také funkce ProcessNextChunk(). Jde o funkci GetString(), která čte data (jméno textury, nebo objektu) a vrací délku celého řetězce

// Čti data (jméno objektu, nebo textury) a vrať délku celého řetězce 
int K_CLoad3ds::GetString( char *pc_buffer )
{
     int i = 0;     // Pomocná proměnná pro cyklování

     // Čti první byt, první písmeno jména objektu, nebo textury
     fread( pc_buffer, 1, 1, pfileStream );

     // Cykluj dokud neprojedeš celý řetězec znaků jména objektu, nebo textury
     while ( *( pc_buffer + i++ ) != 0)
     {
          // Ulož znak jména objektu, nebo textury do bufferu
          fread( pc_buffer + i, 1, 1, pfileStream );
     }

     // Vrať funkci délku řetězce jména objektu, nebo textury
     return strlen( pc_buffer ) + 1;
}

Vrátíme se zpět k funkci ProcessNextObjectChunk(), která má ještě jednu pomocnou funkci ReadUVCoordinates(). Čte hodnoty texturové koordinace.

// Čti UV texturové koordinace
void K_CLoad3ds::ReadUVCoordinates( TObject *ptobject, TChunk *pchunk_prev )
{
     // Načti počet texturových koordinací
     pchunk_prev->ui_bytes_read += fread( &ptobject->i_num_tex_coord, 1, 2, pfileStream );

     // Alokuj paměť pro všechny texturové koordinace struktury TVector2d
     ptobject->ptvec2d_coord = new TVector2d [ptobject->i_num_tex_coord];

     // Čti texturové koordinace (pole s hodnotami x, y)
     pchunk_prev->ui_bytes_read += fread( ptobject->ptvec2d_coord, 1, pchunk_prev->ui_length - pchunk_prev->ui_bytes_read, pfileStream );
}

Teď se vrátíme k počáteční funkci Load3ds(), která volá pomocnou funkci ComputeNormals(). Funkce vypočítá normálové vektory trojúhelníků, pro správné stínování objektu.

// Vypočítej normálové vektory trojúhelníků, pro správné stínování
void K_CLoad3ds::ComputeNormals( TModel *ptmodel )
{
     K_CVector vec_1, vec_2, vec_normal, vec_vert[3], vec_sum;     // Pomocné vektory
     int j, k;                                                     // Pomocné proměnné cyklů

     // Pokud neexistuje žádný objet tak opusť funkci
     if( ptmodel->i_num_objects <= 0 )
          return;

     // Cykluj podle počtu objektů
     for( int i = 0; i < ptmodel->i_num_objects; i++ )
     {
          // Získej hodnoty aktuálního objektu
          TObject *ptobject = &( ptmodel->vtobject[i] );

          // Alokuj paměť pro nenormalizovaný normálový vektor všech trojúhelníků třídy K_CVector
          K_CVector *pvec_unnormalize = new K_CVector [ptobject->i_num_triangle];

          // Alokuj paměť normálový vektor všech vrcholů trojúhelníků třídy K_CVector
          ptobject->pvec_normal = new K_CVector [ptobject->i_num_vert];

          // Cykluj podle počtu trojúhelníků
          for( j = 0; j < ptobject->i_num_triangle; j++ )
          {
               // Získej pozici x, y, z každého vrcholu trojúhelníka
               vec_vert[0] = ptobject->pvec_vert[ptobject->pttriangle[j].i_vert_index[0]];          // První vrchol
               vec_vert[1] = ptobject->pvec_vert[ptobject->pttriangle[j].i_vert_index[1]];          // Druhý vrchol
               vec_vert[2] = ptobject->pvec_vert[ptobject->pttriangle[j].i_vert_index[2]];          // Třetí vrchol

               // Vypočítej vektor mezi prvním a třetím vrcholem
               vec_1.VectorBetween( vec_vert[0], vec_vert[2] );

               // Vypočítej vektor mezi třetí a druhým vrcholem
               vec_2.VectorBetween( vec_vert[2], vec_vert[1] );

               // Vypočítej kolmý vektor na vektory vec_1 a vec_2
               vec_normal.Cross( vec_1, vec_2 );

               // Výsledek ulož do nenormalizovaného vektoru
               pvec_unnormalize[j] = vec_normal;
          }

          vec_sum.SetXYZ(0.0, 0.0, 0.0);          // Resetuj vektor součtu všech vektorů
          int i_divider = 0;                      // Dělitel nastav na nulu

          // Cykluj podle počtu vrcholů
          for ( j = 0; j < ptobject->i_num_vert; j++ )
          {
               // Cykluj podle počtu trojúhelníků
               for ( k = 0; k < ptobject->i_num_triangle; k++ )
               {
                    // Jestliže index vrcholu souhlasí s aktuální hodnotou počtu vrcholů, tak
                    if ( ptobject->pttriangle[k].i_vert_index[0] == j ||
                          ptobject->pttriangle[k].i_vert_index[1] == j ||
                          ptobject->pttriangle[k].i_vert_index[2] == j )
                    {
                         // Přidej nenormalizovaný normálový vektor k hodnotě vektoru součtu všech vektorů
                         vec_sum.AddVector(vec_sum, pvec_unnormalize[k]);

                         // Přidej hodnotu dělitele. Dělitel bude mít nakonec hodnotu počtu trojúhelníků
                         i_divider++;
                    }
               }

               // Získej normálový vektor dělením vektoru součtu všech vektorů dělitelem
               ptobject->pvec_normal[j].DivideVector( vec_sum,  -i_divider );

               // Normalizuj výsledný vektor
               ptobject->pvec_normal[j].Normalize();

               vec_sum.SetXYZ(0.0, 0.0, 0.0);          // Resetuj vektor součtu všech vektorů
               i_divider = 0;                          // Dělitel nastav na nulu
          }

          // Uvolni paměť obsahující nenormalizovaný vektor
          delete [] pvec_unnormalize;
     }
}

Poslední pomocná funkce je CleanUp() která vymaže všechny pomocné hodnoty z paměti a zavře obraz načítaného souboru.

// Vymaž všechny pomocné hodnoty z paměti a zavři obraz načítaného souboru
void K_CLoad3ds::CleanUp()
{
     fclose( pfileStream );          // Zavři obraz načítaného soubor
     delete ptchunkCurrent;          // Uvolni data aktuální větve
     delete ptchunkTemp;             // Uvolni data dočasného skladu
}





Vykreslení 3DS modelu

Nejdříve konstruktor třídy K_CModel3ds načte hodnoty z 3DS souboru Load3ds() a pak v cyklu generuje texturu pro všechny objekty modelu pomocí funkce LoadGLTexture(). Na tuto funkci mrkneme později.

// Konstruktor
K_CModel3ds::K_CModel3ds( char *pc_filename )
{
     int i;                 // Pomocná proměnná cyklů
     char pc_path[270];     // Pomocný řetězec znaků cesty k souboru

     char c_drive[_MAX_DRIVE];               // Informace na kterém disku je uložen načítaný soubor
     char c_dir[_MAX_DIR];                   // Informace ve kterém adresáři je uložen načítaný soubor
     char c_fname[_MAX_FNAME];               // Informace o jménu načítaného souboru
     char c_ext[_MAX_EXT];                   // Informace o příponě načítaného souboru
     _splitpath( pc_filename, c_drive, c_dir, c_fname, c_ext );     // Získej všechny tyto informace

     // Cykluj, podle počtu textur, nastav všechny textury na 0
     for( i = 0; i < MAX_TEXTURES; i++)
     {
          puiTexture[i] = 0;          // Nastav texturu na 0
     }

     tmodelInfo.i_num_materials = 0;        // Nastav počet materiálů na 0
     tmodelInfo.i_num_objects = 0;          // Nastav počet objektů na 0

     // Načti všechny hodnoty modelu ze souboru *.3ds
     Load3ds( &tmodelInfo, pc_filename );

     // Cykluj podle počtu materiálů a načítej textury
     for( i = 0; i < tmodelInfo.i_num_materials; i++ )
     {
          // Jestliže existuje jméno textury z načteného souboru, tak textury načti
          if( strlen( tmodelInfo.vtmaterial[i].c_tex_file_name ) > 0 )
          {
               strcpy( pc_path, c_dir );                                        // Ulož do cesty jméno adresáře
               strcat( pc_path, tmodelInfo.vtmaterial[i].c_tex_file_name );     // Přidej k adresáři jméno souboru
               puiTexture[i] = texJpg.LoadGLTexture( pc_path );                 // Načti texturu
          }

          // Nastav ID textury
          tmodelInfo.vtmaterial[i].i_tex_id = i;
     }
}

Nyní, když známe všechny potřebné hodnoty, tak je zobrazíme. Funkce Render() zobrazí model pomocí trojúhelníků

// Vykresli objekt pomocí trojúhelníků
void K_CModel3ds::Render()
{
     TObject *ptobject;   // Struktura objektu
     BYTE *pby_color;     // Barva materiálu

     // Cykluj podle počtu objektů
     for( int i = 0; i < tmodelInfo.i_num_objects; i++ )
     {
          // Jestliže objekt neexistuje, tak vyskoč z cyklu
          if( tmodelInfo.vtobject.size() <= 0 ) break;

          // Získej aktuální strukturu objektu
          ptobject = &tmodelInfo.vtobject[i];

          // Jestliže je zapnuto texturování objektu
          if( ptobject->b_texture )
          {
               // Zapni texturování
               glEnable( GL_TEXTURE_2D );

               // Nastav bílou barvu
               glColor3ub( 255, 255, 255 );

               // Aktivuj texturu
               glBindTexture( GL_TEXTURE_2D, puiTexture[ptobject->ui_material_id] );
          }
          else     // Jinak
          {
               // Vypni texturováni
               glDisable( GL_TEXTURE_2D );

               // Nastav bílou barvu
               glColor3ub( 255, 255, 255 );
          }

          glBegin( GL_TRIANGLES );     // Začni vykreslovat trojúhelníky

          // Projdi všechny trojúhelníky objektu
          for( int j = 0; j < ptobject->i_num_triangle; j++ )
          {
               // Projdi všechny tři vrcholy trojúhelníka
               for( int k = 0; k < 3; k++ )
               {
                    // Získej indexy všech vrcholů všech trojúhelníků
                    int index = ptobject->pttriangle[j].i_vert_index[k];

                    // Nastav normálový vektor pro každý vrchol. Prohoď Y a Z souřadnici.
                    glNormal3f( ptobject->pvec_normal[ index ][0], ptobject->pvec_normal[ index ][2], ptobject->pvec_normal[ index ][1]);

                    // Jestliže je zapnuto texturování objektu
                    if( ptobject->b_texture )
                    {
                         // Jestliže je existuje hodnota texturové koordinace, tak
                         if( ptobject->ptvec2d_coord )
                         {
                              // Nastav texturovou koordinaci
                              glTexCoord2f( ptobject->ptvec2d_coord[ index ].pf[0], ptobject->ptvec2d_coord[ index ].pf[1] );
                         }
                    }
                    else     // Jinak nastav barvu objektu
                    {
                         // Jestliže existují hodnoty materiálu a ID materiálu, tak
                         if( tmodelInfo.vtmaterial.size() && ptobject->ui_material_id >= 0 )
                         {
                              // Získej barvu materiálu
                              pby_color = tmodelInfo.vtmaterial[ptobject->ui_material_id].uc_color;

                              // Nastav aktuální barvu objektu
                              glColor3ub(pby_color[0], pby_color[1], pby_color[2]);
                         }
                    }
                    // Vykresli vrcholy trojúhelníků. Prohoď Y a Z souřadnici
                    glVertex3f( ptobject->pvec_vert[ index ][0], ptobject->pvec_vert[ index ][2], ptobject->pvec_vert[ index ][1]);
               }
          }

          glEnd();     // Ukonči vykreslování trojúhelníků
     }
}

Funkce RenderLine() vykresluje model pomocí čar

// Vykresli objekt pomocí čar
void K_CModel3ds::RenderLine()
{
     TObject *ptobject;     // Struktura objektu
     BYTE *pby_color;       // Barva materiálu

     // Cykluj podle počtu objektů
     for( int i = 0; i < tmodelInfo.i_num_objects; i++ )
     {
          // Jestliže objekt neexistuje, tak vyskoč z cyklu
          if( tmodelInfo.vtobject.size() <= 0 ) break;

          // Získej aktuální strukturu objektu
          ptobject = &tmodelInfo.vtobject[i];

          // Jestliže je zapnuto texturování objektu
          if( ptobject->b_texture )
          {
               // Zapni texturování
               glEnable( GL_TEXTURE_2D );

               // Nastav bílou barvu
               glColor3ub( 255, 255, 255 );

               // Aktivuj texturu
               glBindTexture( GL_TEXTURE_2D, puiTexture[ptobject->ui_material_id] );
          }
          else     // Jinak
          {
               // Vypni texturování
               glDisable( GL_TEXTURE_2D );

               // Nastav bílou barvu
               glColor3ub( 255, 255, 255 );
          }

          // Projdi všechny trojúhelníky objektu
          for( int j = 0; j < ptobject->i_num_triangle; j++ )
          {
               glBegin( GL_LINE_LOOP );     // Začni vykreslovat čáry

               // Projdi všechny tři vrcholy trojúhelníka
               for( int k = 0; k < 3; k++ )
               {
                    // Získej indexy všech vrcholů všech trojúhelníků
                    int index = ptobject->pttriangle[j].i_vert_index[k];

                    // Nastav normálový vektor pro každý vrchol. Prohoď Y a Z souřadnici
                    glNormal3f( ptobject->pvec_normal[ index ][0], ptobject->pvec_normal[ index ][2], ptobject->pvec_normal[ index ][1]);

                    // Jestliže je zapnuto texturování objektu
                    if( ptobject->b_texture )
                    {
                         // Jestliže je existuje hodnota texturové koordinace, tak
                         if( ptobject->ptvec2d_coord )
                         {
                              // Nastav texturovou koordinaci
                              glTexCoord2f( ptobject->ptvec2d_coord[ index ].pf[0], ptobject->ptvec2d_coord[ index ].pf[1] );
                         }
                    }
                    else
                    {
                         // Jestliže existují hodnoty materiálu a ID materiálu, tak
                         if( tmodelInfo.vtmaterial.size() && ptobject->ui_material_id >= 0 )
                         {
                              // Získej barvu materiálu
                              pby_color = tmodelInfo.vtmaterial[ptobject->ui_material_id].uc_color;

                              // Nastav aktuální barvu objektu
                              glColor3ub(pby_color[0], pby_color[1], pby_color[2]);
                         }
                    }
                    // Vykresli vrcholy trojúhelníků. Prohoď Y a Z souřadnici
                    glVertex3f( ptobject->pvec_vert[ index ][0], ptobject->pvec_vert[ index ][2], ptobject->pvec_vert[ index ][1] );
               }
               glEnd();     // Ukonči vykreslování čar
          }
     }
}

Všimněte se, že při vykreslování normálových vektorů a vrcholů jsou prohozeny souřadnice Y a Z. Proč? Protože OpenGL a 3DStudioMax mají rozdílný souřadnicový systém. Osa Y v OpenGL, je v 3DStudiuMax osa Z.


// Nastav normálový vektor pro každý vrchol. Prohoď Y a Z souřadnici
glNormal3f( ptobject->pvec_normal[ index ][0], ptobject->pvec_normal[ index ][2], ptobject->pvec_normal[ index ][1]);

// Vykresli vrcholy trojúhelníků. Prohoď Y a Z souřadnici
glVertex3f( ptobject->pvec_vert[ index ][0], ptobject->pvec_vert[ index ][2], ptobject->pvec_vert[ index ][1] );


Funkce pro vykreslování 3DS modelů se volají v nekonečné smyčce funkcí DrawScene() třídy K_CScene

// Vykresli scénu
void K_CScene::DrawScene( void )
{
       ...
       ...
       ...

       glPushMatrix();                          // Začátek oddělené matice
          glTranslatef(0.0f, -50.0f, 0.0f);     // Posuň model o 50 dolu
          pmodel3ds->Render();                  // Vykresli 3DS model pomocí trojúhelníků
          glTranslatef(50.0f, 0, 0);            // Posuň model o 50 doprava
          pmodel3ds->RenderLine();              // Vykresli 3DS model pomocí čar
       glPopMatrix();                           // Konec oddělené matice

       ...
       ...
       ...
}

Samozřejmě musíme 3DS model před vykreslováním vytvořit. To provádí funkce InitScene() třídy K_CScene


K_CModel3ds *pmodel3ds;          // 3D Studio Max model (*.3ds)

// Načti scénu
void K_CScene::InitScene( void )
{
     ...
     ...
     ...

     pmodel3ds = new K_CModel3ds("models/teapot/teapot.3ds");     // Alokuj paměť pro 3DS model a zároveň ho načti

     ...
     ...
     ...
}





Načítání textur ze souboru (*.jpg)

Načítání textur provádí funkce LoadGLTexture() třídy K_CTexture. Tato funkce zjistí, jaký má načítaný soubor příponu a podle přípony pak volá potřebné funkce.

// Vytvoř textury načtením z obrazových souborů (*.tga, *.bmp, *.jpg) 
GLuint K_CTexture::LoadGLTexture( char *pc_filename )
{

     TTexture texture;              // Lokální proměnná struktury TTexture

     char c_drive[_MAX_DRIVE];      // Informace na kterém disku je uložen načítaný soubor
     char c_dir[_MAX_DIR];          // Informace ve kterém adresáři je uložen načítaný soubor
     char c_fname[_MAX_FNAME];      // Informace o jménu načítaného souboru
     char c_ext[_MAX_EXT];          // Informace o příponě načítaného souboru

     _splitpath( pc_filename, c_drive, c_dir, c_fname, c_ext );     // Získej všechny tyto informace


     ///////////////////////////////
     // Načítání ze souboru *.tga //
     ///////////////////////////////

     ...
     ...
     ...



     ///////////////////////////////
     // Načítání ze souboru *.bmp //
     ///////////////////////////////

     ...
     ...
     ...


     ///////////////////////////////
     // Načítání ze souboru *.jpg //
     ///////////////////////////////

     // Převeď řetězce na malá písmena a porovnej jestli má načítaný soubor příponu *.jpg
     if ( !strcomp( c_ext, ".jpg" ))
     {
          tImageJPG *pimage;     // Vytvoř obraz textury

          pimage = NULL;                       // Obraz textury nastav na NULL
          pimage = LoadJPG( pc_filename );     // Načti obraz textury ze souboru *.jpg

          // Jestliže existuje obraz textury
          if ( pimage != NULL && pimage->data != NULL )
          {
               // Vytvoř texturu
               glGenTextures(1, &texture.i_tex_id );

               // Aktivuj texturu
               glBindTexture( GL_TEXTURE_2D, texture.i_tex_id );

               // Lineárně rozlož (roztáhni) texturu do celého objektu, pokud je textura menší nez plocha objektu
               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

               // Lineárně rozlož (zmenši) texturu, pokud je textura větší než plocha objektu
               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

               // Nastav parametry textury
               glTexImage2D(GL_TEXTURE_2D, 0, 3, pimage->sizeX, pimage->sizeY,     0, GL_RGB, GL_UNSIGNED_BYTE, pimage->data);
          }
          else               // Jinak
          {
               return 0;     // Vyskoč z funkce
          }

          if ( pimage->data )            // Jestliže existuje obraz textury
          {
               // Vymaž obraz textury z paměti
               free( pimage->data );
               free( pimage );
          }

          return texture.i_tex_id;       // Vrať funkci hodnotu identifikátoru texturu

     }

     return 0;         // Pokud se dostaneš az sem, tak vyskoč z funkce
}

Načítání textur ze souboru (*.jpg) provádí pomocná funkce LoadJPG()

// Načti JPG formát
tImageJPG *K_CTexture::LoadJPG( char *pc_filename )
{
     struct jpeg_decompress_struct tjpg_info;     // Struktura hodnot pro dekompresi jpg
     tImageJPG *ptimage_data = NULL;              // Ukazatel na strukturu JPG hodnot nastav na NULL
     FILE *pfile_jpg;                             // Obraz souboru *.jpg

     // Otevři soubor *.jpg
     pfile_jpg = fopen( pc_filename, "rb" );

     // Pokud se obraz souboru nepodařilo otevřít, tak
     if( pfile_jpg == NULL )
     {
          K_CViewValue vie;                                                  // Aktivuj třídu zobrazení kontrolních hodnot
          vie.ErrorMsgChar( "Could not open texture file", pc_filename );    // Zobraz chybovou hlášku
          return NULL;                                                       // Vyskoč z funkce
     }

     // Vytvoř ovladač hodnot pro JPG chybové zprávy
     jpeg_error_mgr jpg_error;

     // Získej adresu ovladače JPG chybových zpráv
     tjpg_info.err = jpeg_std_error( &jpg_error );

     // Vytvoř dekomprimační objekt
     jpeg_create_decompress( &tjpg_info );

     // Načti hodnoty z obrazu souboru
     jpeg_stdio_src( &tjpg_info, pfile_jpg );

     // Alokuj paměť pro strukturu JPG formátu
     ptimage_data = ( tImageJPG* )malloc( sizeof( tImageJPG ));

     // Dekóduj JPG formát
     DecodeJPG( &tjpg_info, ptimage_data );

     // Uvolni pamět obsahující strukturu hodnot JPG formátu
     jpeg_destroy_decompress( &tjpg_info );

     // Zavři obraz souboru *.jpg
     fclose( pfile_jpg );

     // Otoč texturu aby nebyla vzhůru nohama
     if( ptimage_data->data )
     {
          unsigned char*     puc_turn;     // Pomocná proměnná pro otočení
          int i_x, i_y;                    // Pomocné proměnné pro x, y hodnoty pixelů textury

          // Alokuj paměť pro hodnoty otočení
          puc_turn = ( unsigned char* ) malloc( ptimage_data->rowSpan * ptimage_data->sizeY );

          // Cykluj podle velikosti počtu řádků na Y souřadnici
          for( i_y = 0; i_y < ptimage_data->sizeY; i_y++ )
          {
               // Cykluj podle velikosti počtu na X souřadnici
               for( i_x = 0; i_x < ptimage_data->rowSpan; i_x++)
               {
                    // Prohoď hodnoty, aby došlo k otočení textury
                    puc_turn[i_x + i_y  * ptimage_data->rowSpan] = ptimage_data->data[i_x + ( ptimage_data->sizeY - 1 - i_y ) * ptimage_data->rowSpan];
               }
          }
          free( ptimage_data->data );         // Uvolni paměť obsahující strukturu JPG hodnot
          ptimage_data->data = puc_turn;      // A do této paměti ulož nová data otočené textury
     }

     // Vrať funkci výsledná data vytvořené textury
     return ptimage_data;
}


Protože je JPG formát komprimovaný, musíme formát dekomprimovat. To provádí pomocná funkce DecodeJPG()

// Dekóduj JPG formát
void K_CTexture::DecodeJPG( jpeg_decompress_struct* ptjpg_info, tImageJPG *ptimage_data )
{
     // Čti hlavičku souboru
     jpeg_read_header( ptjpg_info, TRUE );

     // Rozjeď dekomprimaci jpeg formátu
     jpeg_start_decompress( ptjpg_info );

     // Získej informace o souboru
     ptimage_data->rowSpan = ptjpg_info->image_width * ptjpg_info->num_components;     // Délka řádku obrázku
     ptimage_data->sizeX   = ptjpg_info->image_width;                                  // Šířka obrázku
     ptimage_data->sizeY   = ptjpg_info->image_height;                                 // Výška obrázku

     // Alokuj paměť pro pixelový zásobník
     ptimage_data->data = new unsigned char[ptimage_data->rowSpan * ptimage_data->sizeY];

     // Vytvoř pole ukazatelů na řádek obrázku
     unsigned char** ppuc_row = new unsigned char*[ptimage_data->sizeY];

     // Cykluj podle velikosti řádku
     for ( int i = 0; i < ptimage_data->sizeY; i++ )
          ppuc_row[i] = &( ptimage_data->data[i * ptimage_data->rowSpan] );     // Získej pixelová data řádku

     int rows_read = 0;          // Nastav počet načítaných řádků na 0

     // Cykluj dokud neprojedeš všechny řádky
     while ( ptjpg_info->output_scanline < ptjpg_info->output_height )
     {
          // Čti hodnoty pixelů v aktuální řadce a nakonec přičti další řadek
          rows_read += jpeg_read_scanlines( ptjpg_info, &ppuc_row[rows_read], ptjpg_info->output_height - rows_read );
     }

     // Vymaž pole ukazatelů na řádek obrázku
     delete [] ppuc_row;

     // Dokonči dekompresi jpeg fomátu
     jpeg_finish_decompress( ptjpg_info );
}

A je hotovo. Příště si něco řekneme o přehrávání muziky pomocí knihovny FMOD.

Kwan




Home