OpenGL Game Tutorial
Visual C++ 6.0



Načítání souborů MilkShape (*.ms3d)


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


Load MilkShape souborů (*.ms3d)
Vykreslení modelu MilkShape
Rotace kloubů modelu pomocí Quaternionů
Načítání textur ze souboru (*.tga)
Nová kamera, transformace a rotace pomocí glTranslatef(), glRotatef()
Světlo pohybující se s kamerou

glIsEnabled( GL_TEXTURE_2D )
glMaterialfv()
glEnable( GL_TEXTURE_2D )
glDisable( GL_TEXTURE_2D )
glBegin( GL_TRIANGLES )
glEnd()
glTexCoord2f()
glNormal3fv()
glVertex3fv()
glRotatef()
glTranslatef()
auxDIBImageLoad()
glGenTextures()
glBindTexture()
glTexImage2D()
glTexParameteri()
glLightfv()
glEnable(GL_LIGHTING)
glDisable(GL_LIGHTING)
glEnable(GL_LIGHT0)




Load MilkShape souborů (*.ms3d)

Milk Shape 3D je 3D editor animovaných modelů. Lze v něm načítat již vytvořené postavy z různých her (Quake I, II, III, Half-Life, Unreal Tournament a další), vytvořit si vlastní animace a uložit do MilkShape formátu (*.ms3d). Jak načítat tento formát do OpenGL? O tom bude tento tutorial.

Na specifikaci tohoto souboru se můžete mrknout zde.

Podle této specifikace jsem vytvořil struktury třídy K_CMilkshapeModel.


// Struktura hlavičky souboru
struct TMs3dHeader
{
     char pc_id[10];               // Identifikace souboru, vždy "MS3D000000"
     int i_version;                // Verze souboru
};


// Informace o vrcholech
struct TMs3dVertex
{
     byte by_flags;         // Hodnota, zda je vrchol označen, nebo není (tuto hodnotu nebudem načítat)
     float pf_vertex[3];    // Hodnota vrcholu x, y, z (x = f_vertex[0], y = f_vertex[1], z = f_vertex[2])
     char c_bone_id;        // Identifikační číslo kostry
     byte by_ref_count;     // Hodnota, která určuje, zda kostra existuje, či ne (-1 = neexistuje)(tuto hodnotu nebudem načítat)
};


// Informace o trojúhelnících
struct TMs3dTriangle
{
     word  w_flags;                      // Hodnota, zda je trojúhelník označen, nebo není (tuto hodnotu nebudem načítat)
     word  pw_vertex_indices[3];         // Identifikátor hodnot tří vrcholů trojúhelníka
     float ppf_vertex_normals[3][3];     // Normálové vektory trojúhelníka
     float pf_s[3], pf_t[3];             // Texturové koordinace trojúhelníka
     byte  by_smoothing_group;           // Vyhlazení skupiny trojúhelníků (tuto hodnotu nebudem načítat, ale možná bude potřebná)
     byte  by_group_index;               // Index skupiny (tuto hodnotu nebudem načítat, ale možná bude potřebná)
};

// Informace o materiálu
struct TMs3dMaterial
{
    char  pc_name[32];        // Jméno materiálu (tuto hodnotu nebudem načítat)
    float pf_ambient[4];      // Ambientní hodnoty nasvíceného materiálu, viz. níže
    float pf_diffuse[4];      // Difuzní hodnoty nasvíceného materiálu, viz. níže
    float pf_specular[4];     // Specular hodnoty nasvíceného materiálu, viz. níže
    float pf_emission[4];     // Emmision hodnoty nasvíceného materiálu, viz. níže
    float f_shininess;        // Emmision hodnoty nasvíceného materiálu, viz. níže
    float f_transparency;     // Transparentní hodnota zadává průhlednost (tuto hodnotu nebudem zatím načítat)
    byte  by_mode;            // Mód (tuto hodnotu nebudem zatím načítat)
    char  pc_texture[128];    // Číslo textury
    char  pc_alphamap[128];   // Číslo alfa mapy (tuto hodnotu nebudem zatím načítat)


// Ambientní světlo se šíří všemy směry a svítí i když jsou ostatní světla vypnuta
// Difuzní světlo je světlo, které zdroj vyzařuje do okolí
// Specular světlo světlo, zrcadlově odražené
// Emmision světlo je světlo, které vyzařuje určitý objekt, nepůsobí však jako zdroj světla
// Shininess je, intenzita světelného odlesku, hodnoty jsou od 0.0f do 128.0f

};

// Informace o kloubech kostry
struct TMs3dJoint
{
     byte by_flags;                    // Hodnota, zda je kloub označen, nebo není (tuto hodnotu nebudem načítat)
     char pc_name[32];                 // Jméno kloubu
     char pc_parent_name[32];          // Jméno celé kostry
     float pf_rotation[3];             // Rotace kloubu
     float pf_translation[3];          // Posunutí kloubu
     word w_num_key_frames_rot;        // Počet snímků rotace
     word w_num_key_frames_trans;      // Počet snímků transformace
};


// Hodnoty rotace a translace animace
struct TMs3dKeyframe
{
     float f_time;             // Počet snímků
     float pf_parameter[3];    // Hodnota vektoru rotace a transformace
};

Teď, když máme definované struktury souboru, zkusíme ho načíst.

// Načti data MilkShape modelu
bool K_CMilkshapeModel::LoadModelData( const char *pc_filename )
{
     /////////////////////////
     // Načti soubor *.ms3d //
     /////////////////////////
     ifstream input_file( pc_filename, ios::in | ios::binary | ios::nocreate );     // Zkontroluj soubor, jestli má odpovídající formát a ulož ho do ifstreamu (vstupní proud)
     if ( input_file.fail())                                                        // Pokud je formát špatný
          {
                cerr << "Couldn't open the model file." << endl;                  // Vyhoď chybovou hlášku
          return false;       // A opusť funkci
        }



     //////////////////////////////////////////////////
     // Zjisti informace o umístění souboru na disku //
     //////////////////////////////////////////////////
     char c_drive[_MAX_DRIVE];          // Disk
     char c_dir[_MAX_DIR];              // Cesta k souboru
     char c_fname[_MAX_FNAME];          // Jméno souboru
     char c_ext[_MAX_EXT];              // Přípona souboru

     _splitpath( pc_filename, c_drive, c_dir, c_fname, c_ext );    // Načti informace
     // Zatím potřebuji jen informaci o ceste k souboru,
     // časem se možná hodí i další hodnoty



     /////////////////////////////////
     // Pokračuj v načítání souboru //
     /////////////////////////////////
     input_file.seekg( 0, ios::end );                     // Nastav ukazatel na konec souboru
     long l_file_size = input_file.tellg();               // Zjisti velikost celého souboru
     input_file.seekg( 0, ios::beg );                     // Nastav ukazatel na začátek souboru

     byte *pby_buffer = new byte[l_file_size];            // Alokuj paměť pro celý soubor
     input_file.read( pby_buffer, l_file_size );          // Načti do vytvořené paměti celý soubor
     input_file.close();                                  // Vyčisti ifstream (vstupní proud)

     const byte *pby_load_data = pby_buffer;              // Soubor načtený v paměti přejmenuj na pLoadData

     /////////////////////////////////
     // Načti data hlavičky souboru //
     /////////////////////////////////
     TMs3dHeader *p_header = ( TMs3dHeader* )pby_load_data;          // Načti ze souboru hodnoty typu MS3DHeader
     pby_load_data += sizeof( TMs3dHeader );                         // Posuň se v souboru na další hodnoty
     if ( strncmp( p_header->pc_id, "MS3D000000", 10 ) != 0 )        // Porovnej data, jestli souhlasí
          {
               cerr << "Not an MS3D file." << endl;                  // Pokud nesouhlasí, tak vyhoď hlášku
               return false;                                         // A vrať funkci hodnotu false
        }
     if ( p_header->i_version < 3 )                       // Jestliže je verze souboru menší než 3
          {
               cerr << "I know nothing about MS3D v1.2" << endl;   // Vytoď hlášku, že soubor je starší verze
               return false;                              // A vrať funkci hodnotu false
        }

     ////////////////////////
     // Načti data vrcholů //
     ////////////////////////
     word w_num_vertices = *( word* )pby_load_data;               // Načti ze souboru počet vrcholů
     wNumVertices = w_num_vertices;                               // Předej hodnotu modelu
     pVertices = new TModelVertex[w_num_vertices];                // Alokuj paměť pro počet vrcholů modelu
     pby_load_data += sizeof( word );                             // Posuň se v souboru na další hodnoty
     for (int i = 0; i < w_num_vertices; i++ )       // Cykluj podle počtu vrcholů
     {
          TMs3dVertex *p_vertex = ( TMs3dVertex* )pby_load_data;      // Načti ze souboru hodnoty typu MS3DVertex
          pVertices[i].c_bone_id = p_vertex->c_bone_id;               // Načti hodnotu identifikačního čísla kostry
          memcpy( pVertices[i].pf_vertex, p_vertex->pf_vertex, sizeof( float ) * 3 );     // Načti hodnotu vrcholu 
          pby_load_data += sizeof( TMs3dVertex );                     // Posuň se v souboru na další hodnoty
     }

     /////////////////////////////
     // Načti data trojúhelníků //
     /////////////////////////////
     word w_num_triangles = *( word* )pby_load_data;          // Načti ze souboru počet trojúhelníků 
     wNumTriangles = w_num_triangles;                         // Předej hodnotu modelu
     pTriangles = new TModelTriangle[w_num_triangles];        // Alokuj paměť pro počet trojúhelníků modelu
     pby_load_data += sizeof( word );                         // Posuň se v souboru na další hodnoty 
     for ( i = 0; i < w_num_triangles; i++ )     // Cykluj podle počtu trojúhelníků 
     {
          TMs3dTriangle *p_triangle = ( TMs3dTriangle* )pby_load_data;                                                 // Načti ze souboru hodnoty typu MS3DTriangle
          float f_t[3] = { 1.0f - p_triangle->pf_t[0], 1.0f - p_triangle->pf_t[1], 1.0f - p_triangle->pf_t[2] };       // Otoč vertikální texturovou koordinaci 
          memcpy( pTriangles[i].ppf_vertex_normals, p_triangle->ppf_vertex_normals, sizeof( float ) * 3 * 3 );         // Načti hodnoty normálových vektorů každého vrcholu trojúhelníka 
          memcpy( pTriangles[i].pf_s, p_triangle->pf_s, sizeof( float ) * 3 );                                         // Načti hodnoty horizontální texturové koordinace
          memcpy( pTriangles[i].pf_t, f_t, sizeof( float ) * 3 );                                                      // Načti hodnoty vertikální texturové koordinace
          memcpy( pTriangles[i].pw_vertex_indices, p_triangle->pw_vertex_indices, sizeof( word ) * 3 );                // Načti hodnoty identifikátorů vrcholů
          pby_load_data += sizeof( TMs3dTriangle );                                                                    // Posuň se v souboru na další hodnoty
     }

     ///////////////////////////////////////
     // Načti hodnoty skupin trojúhelníků //
     ///////////////////////////////////////
     word w_num_groups = *( word* )pby_load_data;        // Načti ze souboru počet skupin
     wNumGroups = w_num_groups;                          // Předej hodnotu modelu
     pGroups = new TModelGroup[w_num_groups];            // Alokuj paměť pro počet skupin modelu
     pby_load_data += sizeof( word );                    // Posuň se v souboru na další hodnoty
     for ( i = 0; i < w_num_groups; i++ )   // Cykluj podle počtu skupin
     {
          pby_load_data += sizeof( byte );                                         // Přeskoč flags
          pby_load_data += 32;                                                     // Přeskoč name
          pGroups[i].w_num_triangles = *( word* )pby_load_data;                    // Načti hodnotu počtu trojúhelníků skupiny
          pby_load_data += sizeof( word );                                         // Posuň se v souboru na další hodnoty
          pGroups[i].pw_triangle_index = new word[pGroups[i].w_num_triangles];     // Alokuj paměť pro počet trojúhelníků skupiny modelu
          for ( int j = 0; j < pGroups[i].w_num_triangles; j++ )      // Cykluj podle počtu trojúhelníků skupiny
          {
               pGroups[i].pw_triangle_index[j] = *( word* )pby_load_data;          // Načti skupinu skupiny trojúhelníků
               pby_load_data += sizeof( word );                                    // Posuň se v souboru na další hodnoty
          }
          pGroups[i].i_material_index = *( char* )pby_load_data;                   // Načti ze souboru index materialu skupiny
          pby_load_data += sizeof( char );                                         // Posuň se v souboru na další hodnoty
     }

     /////////////////////////////
     // Načti hodnoty materiálu //
     /////////////////////////////
     word w_num_materials = *( word* )pby_load_data;          // Načti hodnoty počtu materiálu 
     wNumMaterials = w_num_materials;                         // Předej hodnotu modelu
     pMaterials = new TModelMaterial[w_num_materials];        // Alokuj paměť pro počet materiálů modelu
     pby_load_data += sizeof( word );                         // Posuň se v souboru na další hodnoty
     for ( i = 0; i < w_num_materials; i++ )     // Cykluj podle počtu materiálů
     {
          TMs3dMaterial *p_material = ( TMs3dMaterial* )pby_load_data;                           // Načti ze souboru hodnoty typu MS3DMaterial
          memcpy( pMaterials[i].pf_ambient, p_material->pf_ambient, sizeof( float ) * 4 );       // Načti ambientní hodnoty materiálu
          memcpy( pMaterials[i].pf_diffuse, p_material->pf_diffuse, sizeof( float ) * 4 );       // Načti difuzní hodnoty
          memcpy( pMaterials[i].pf_specular, p_material->pf_specular, sizeof( float ) * 4 );     // Načti specular hodnoty
          memcpy( pMaterials[i].pf_emissive, p_material->pf_emission, sizeof( float ) * 4 );     // Načti emission hodnoty
          pMaterials[i].f_shininess = p_material->f_shininess;                                   // Načti shininess hodnoty
          pMaterials[i].pc_texture_filename = new char[strlen( p_material->pc_texture )+1];      // Alokuj paměť pro název textury
          strcpy( pMaterials[i].pc_texture_filename, c_dir );                                    // Nakopíruj cestu k souboru
          pMaterials[i].pc_texture_filename = strcat(pMaterials[i].pc_texture_filename, p_material->pc_texture);   // K cestě k souboru připoj samotné jméno souboru
          pby_load_data += sizeof( TMs3dMaterial );                                              // Posuň se v souboru na další hodnoty
     }
     ReloadTextures();                                                                           // Načti textury

     ///////////////////////////
     // Načti animační kostru //
     ///////////////////////////
     float f_anim_fps = *( float* )pby_load_data;          // Načti rychlost animace kostry
     pby_load_data += sizeof( float );                     // Posuň se v souboru na další hodnoty
     pby_load_data += sizeof( float );                     // Přeskoč hodnotu aktuálního času
     int i_total_frames = *( int* )pby_load_data;          // Načti počet snímků 
     pby_load_data += sizeof( int );                       // Posuň se v souboru na další hodnoty
     dTotalTime = i_total_frames*1000.0 / f_anim_fps;      // Vypočítej délku animace
     wNumJoints = *( word* )pby_load_data;                 // Načti počet kloubů
     pby_load_data += sizeof( word );                      // Posuň se v souboru na další hodnoty
     pJoints = new TModelJoint[wNumJoints];                // Alokuj paměť pro počet kloubů kostry modelu

     struct TJointNameListRec            // Vytvoř pomocnou strukturu pro načtení indexu a jména specifického kloubu
     {
          int i_joint_index;             // Index kloubu
          const char *pc_joint_name;     // Jméno kloubu
     };

     const byte *pby_temp_load_data = pby_load_data;                        // Vytvoř další buffer pro načtení indexu a jména kloubu

     TJointNameListRec *p_name_list = new TJointNameListRec[wNumJoints];    // Alokuj paměť pro počet indexů typu JoinNameRec
     for ( i = 0; i < wNumJoints; i++ )                        // Cykluj podle počtu kloubů
     {
          TMs3dJoint *p_joint = ( TMs3dJoint* )pby_temp_load_data;          // Načti ze souboru hodnoty typu MS3DJoint
          pby_temp_load_data += sizeof( TMs3dJoint );                       // Přeskoč hodnoty času sekundach
          pby_temp_load_data += sizeof( TMs3dKeyframe ) * ( p_joint->w_num_key_frames_rot + p_joint->w_num_key_frames_trans );   // Přeskoč hodnoty rotace a posunutí, které budem načítat později
          p_name_list[i].i_joint_index = i;                                 // Načti index kloubu
          p_name_list[i].pc_joint_name = p_joint->pc_name;                  // Načti jméno kloubu
     }

     for ( i = 0; i < wNumJoints; i++ )                        // Znova cykluj podle počtu kloubů
     {
          TMs3dJoint *p_joint = ( TMs3dJoint* )pby_load_data;               // Znova načti ze souboru hodnoty typu MS3DJoint
          pby_load_data += sizeof( TMs3dJoint );                            // Posuň se v souboru na další hodnoty 

          int i_parent_index = -1;                                          // Rodičovský index nastav na -1
          if ( strlen( p_joint->pc_parent_name ) > 0 )                      // Jestliže jméno rodičovského kloubu existuje
          {
               for (int j = 0; j < wNumJoints; j++ )           // Cykluj opět podle počtu kloubů
               {
                    if ( strcomp( p_name_list[j].pc_joint_name, p_joint->pc_parent_name ) == 0 )    // Pokud je jméno kloubu a jeho rodiče totožné
                    {
                         i_parent_index = p_name_list[j].i_joint_index;     // Tak nastav stejný index rodiče a potomka
                         break;                                             // A vyskoč z cyklu
                    }
               }
               if ( i_parent_index == -1 )                                  // Jestliže je rodičovský index -1, tak
               {
                    MessageBox( NULL, "Unable to find parent bone in MS3D file", "Error", MB_OK | MB_ICONERROR );     // Vyhoď chybovou hlášku
                    return false;                                           // A funkci předej false
               }
          }

          memcpy( pJoints[i].pf_rotation, p_joint->pf_rotation, sizeof( float ) * 3 );          // Načti rotaci kloubu
          memcpy( pJoints[i].pf_translation, p_joint->pf_translation, sizeof( float ) * 3 );    // Načti posun kloubu
          pJoints[i].i_parent = i_parent_index;                                                 // Načti rodičovský index
          pJoints[i].i_num_rotation_key_frames = p_joint->w_num_key_frames_rot;                 // Načti počet snímků rotace
          pJoints[i].p_keyframe_rot = new TModelKeyframe[p_joint->w_num_key_frames_rot];        // Alokuj paměť podle počtu snímků rotace modelu
          pJoints[i].i_num_translation_key_frames = p_joint->w_num_key_frames_trans;            // Načti počet snímků translace
          pJoints[i].p_keyframe_trans = new TModelKeyframe[p_joint->w_num_key_frames_trans];    // Alokuj paměť podle počtu snímků posunutí modelu

          for ( int j = 0; j < p_joint->w_num_key_frames_rot; j++ )                // Cykluj podle počtu snímků rotace
          {
               TMs3dKeyframe *p_key_frame = ( TMs3dKeyframe* )pby_load_data;                              // Načti ze souboru hodnoty typu TMs3dKeyframe
               pby_load_data += sizeof( TMs3dKeyframe );                                                  // Posuň se v souboru na další hodnoty
               SetJointKeyframe( i, j, p_key_frame->f_time*1000.0f, p_key_frame->pf_parameter, true );    // Nastav hodnoty rotace kloubu
          }

          for ( j = 0; j < p_joint->w_num_key_frames_trans; j++ )       // Cykluj podle počtu snímků translace
          {
               TMs3dKeyframe *p_key_frame = ( TMs3dKeyframe* )pby_load_data;         // Načti ze souboru hodnoty typu TMs3dKeyframe
               pby_load_data += sizeof( TMs3dKeyframe );                             // Posuň se v souboru na další hodnoty
               SetJointKeyframe( i, j, p_key_frame->f_time * 1000.0f, p_key_frame->pf_parameter, false );     // Nastav hodnoty posunutí kloubu
          }
     }

     /////////////////////
     // Ukonči načítání //
     /////////////////////
     delete[] p_name_list;          // Vymaž buffer bufferu souboru (počel listů kloubu)

     SetupJoints();                 // Nastav matice rotace a translace kloubů

     delete[] pby_buffer;           // Vymaž buffer souboru

     Restart();                     // Restartuj animaci

     return true;                   // Vrať funkci true
}

To je nářez co? Tuto funkci volám při načítání scény. Hodnoty se převádějí do hodnot modelu. Struktury modelu musí být proto totožné se strukturou načteného souboru.

K_CModel *pmodel = NULL;          // Ukazatel na MilkShape model nastav na NULL

//----------------------------------------------------------------------------------------------------//

void InitScene( void )               // Načti scénu
{
     pmodel = new K_CMilkshapeModel();                                          // Alokuj paměť pro MilkShape model
     if ( pmodel->LoadModelData( "models/bert/model.ms3d" ) == false )          // Zkus načíst MilkShape model, pokud se to nepodaří, tak
     {
          MessageBox( NULL, "Couldn't load the model: models/bert/model.ms3d", "Error", MB_OK | MB_ICONERROR );     // vyhoď hlášku                                                                 // If Model Didn't Load Quit
     }


     ...
     ...
     ...


}

Hodnoty se převádějí do hodnot modelu. Struktury modelu musí být proto totožné se strukturou načteného souboru.

// Struktura informací o vrcholech
struct TModelVertex
{
       char c_bone_id;          // Identifikační číslo kostry
       float pf_vertex[3];      // Hodnota vrcholu x, y, z (x = m_vertex[0], y = m_vertex[1], z = m_vertex[2])
};

// Informace o trojúhelnících
struct TModelTriangle
{
       float ppf_vertex_normals[3][3];     // Normálové vektory trojúhelníka
       float pf_s[3], pf_t[3];             // Texturové koordinace trojúhelníka
       word pw_vertex_indices[3];          // Identifikátor hodnot tří vrcholů trojúhelníka
};

// Informace o skupinách trojúhelníků
struct TModelGroup
{
       int i_material_index;        // Index materiálu skupiny
       word w_num_triangles;        // Počet trojúhelníků ve skupině
       word *pw_triangle_index;     // Index trojúhelníků skupiny
};

// Informace o materiálu
struct TModelMaterial
{
       float pf_ambient[4], pf_diffuse[4], pf_specular[4], pf_emissive[4];   // Hodnoty materiálu
       float f_shininess;                                                    // Další hodnota materiálu
       GLuint ui_texture;                                                    // Číslo textury
       char *pc_texture_filename;                                            // Jméno textury
};

// Hodnoty animace kloubu obsahující čas, rotaci a posunutí
struct TModelKeyframe
{
       int i_joint_index;         // Index kloubu
       float f_time;              // Čas v milisekundách
       float pf_parameter[3];     // Základní hodnota podle kterého se počítá vektor rotace a posunutí
};

// Informace o kloubech kostry
struct TModelJoint
{
       float pf_rotation[3];       // Lokální rotace
       float pf_translation[3];    // Lokální posunutí
       /*
           * Absolutní, či relativní matice
           * Když pohnu absolutní maticí, pohnu i relativní
           * Když pohnu relativní maticí, tak absolutní se nemění
         */
       K_CMatrix mat_absolute, mat_relative;

       int i_num_rotation_key_frames, i_num_translation_key_frames;     // Počet snímků rotace a posunutí
       TModelKeyframe *p_keyframe_trans;                                // Ukazatel na animační hodnoty posunutí kloubu
       TModelKeyframe *p_keyframe_rot;                                  // Ukazatel na animační hodnoty rotace kloubu

       int i_current_keyframe_trans, i_current_keyframe_rot;            // Aktuální posunutí a rotace
       K_CMatrix mat_final;                                             // Konečná matice

       int i_parent;                                                    // Rodičovský kloub
};

Teď si vytvoříme ukazatele na tyto struktury a další globální proměnné třídy K_CModel. Třída K_CModel je veřejná proměnná třídy K_CMilkshapeModel, ve které jsme načítali soubor. Hodnoty jsme ukládali do struktur třídy K_CModel.

// Informace o vrcholech
word wNumVertices;
TModelVertex *pVertices;

// Informace o trojuhelnících
word wNumTriangles;
TModelTriangle *pTriangles;

// Informace o skupinách trojúhelníků
word wNumGroups;
TModelGroup *pGroups;

// Informace o materiálech
word wNumMaterials;
TModelMaterial *pMaterials;

// Informace o kloubech kostry
word wNumJoints;
TModelJoint *pJoints;

// Časovač
K_CTimer *pTimer;

// Celkový čas animace
double dTotalTime;

// Animační cyklus
bool bLooping;


Některé fukce třídy K_CModel, slouží k načítání souboru. Jsou to:

SetJointKeyframe( int i_joint_index, int i_keyframe_index, float f_time, float *pf_parameter, bool b_rotation )
Nastaví hodnoty jednotlivých snímků pro jednotlivé klouby kostry
i_joint_index = Index snímků kloubu nastav
i_keyframe_index = podle maximálního počtu snímků
f_time = Čas je v milisekundách
pf_parameter = Parametr rotace a posunutí pro každý snímek
b_rotation = Identifikátor, zda se bude provádět rotace (posunutí)

SetupJoints()
Nastaví matice kloubů

ReloadTextures()
Načte textury


//----------------------------------------------------------------------------------------------------//

// Nastav hodnoty jednotlivých snímků pro jednotlivé klouby kostry
void K_CModel::SetJointKeyframe( int i_joint_index, int i_keyframe_index, float f_time, float *pf_parameter, bool b_rotation )
{
     assert( wNumJoints > i_joint_index );      // Pokud je počet kloubů větší než index snímku kloubu tak pokračuj dál, jinak vyhoď chybovou hlášku

     TModelKeyframe& keyframe = b_rotation ? pJoints[i_joint_index].p_keyframe_rot[i_keyframe_index] :     // Zjisti, jestli jde o rotaci,
          pJoints[i_joint_index].p_keyframe_trans[i_keyframe_index];            // nebo posunutí a nastav hodnoty příslušného snímku

     keyframe.i_joint_index = i_joint_index;                                    // Nastav index kloubu
     keyframe.f_time = f_time;                                                  // Nastav čas (v milisekundách)
     memcpy( keyframe.pf_parameter, pf_parameter, sizeof( float ) * 3 );        // Nastav základní hodnotu parametru, podle kterého se počítá vektor rotace s posunutí
}

//----------------------------------------------------------------------------------------------------//

// Nastav matice rotace a posunutí kloubů
void K_CModel::SetupJoints()
{
     int i;                                                                 // Proměnná cyklů
     for ( i = 0; i < wNumJoints; i++ )                                     // Cykluj podle počtu kloubů
     {
          TModelJoint& joint = pJoints[i];                                  // Vytvoř proměnnou joint typu ModelJoint

          joint.mat_relative.SetRotationRadians( joint.pf_rotation );       // Nastav relativní rotační matici
          joint.mat_relative.SetTranslation( joint.pf_translation );        // Nastav relativní matici posunutí
          if ( joint.i_parent != -1 )                                       // Jestliže existuje rodičovský kloub, tak 
          {
               joint.mat_absolute.Set( pJoints[joint.i_parent].mat_absolute.GetMatrix());     // Nastav absolutní matici rotace a posunutí
               joint.mat_absolute.PostMultiply( joint.mat_relative );       // Vynásob absolutní matici maticí relativní (pohni relativnim kloubem)
          }
          else                                                              // Jinak
               joint.mat_absolute.Set( joint.mat_relative.GetMatrix());     // Nastav jen absolutní matici
     }

     for ( i = 0; i < wNumVertices; i++ )                                   // Cykluj podle počtu vrcholů 
     {
          TModelVertex& vertex = pVertices[i];                              // Vytvoř proměnnou vertex typu ModelVertex

          if ( vertex.c_bone_id != -1 )                                           // Jestliže existuje identifikátor kloubu kostry
          {
               const K_CMatrix& mat = pJoints[vertex.c_bone_id].mat_absolute;     // Vytvoř konstantu matrix typu Matrix (absolutní matice)

               mat.InverseTranslateVect( vertex.pf_vertex );                      // Otoč vektor posunutí, aby se vrchol neposouval opačným směrem než kloub
               mat.InverseRotateVect( vertex.pf_vertex );                         // Otoč vektor rotace, aby vrchol nerotoval opačným směrem než kloub
          }
     }

     for ( i = 0; i < wNumTriangles; i++ )
     {                                                    // Cykluj podle počtu trojúhelníků
          TModelTriangle& triangle = pTriangles[i];       // Vytvoř proměnnou triangle typu ModelTriangle
          for ( int j = 0; j < 3; j++ )
          {                                                                               // Provejeď 3 cykly
               const TModelVertex& vertex = pVertices[triangle.pw_vertex_indices[j]];     // Vytvoř konstantu vertex typu ModelVertex
               if ( vertex.c_bone_id != -1 )                                              // Jestliže existuje identifikátor kloubu kostry, tak
               {
                    const K_CMatrix& mat = pJoints[vertex.c_bone_id].mat_absolute;        // Vytvoř konstantu matrix typu Matrix (absolutní matice)
                    mat.InverseRotateVect( triangle.ppf_vertex_normals[j] );              // Otoč vektor normálovych vektorů
               }
          }
     }
}

//----------------------------------------------------------------------------------------------------//

// Načti textury modelu
void K_CModel::ReloadTextures()
{
     K_CTexture tex;

     for ( int i = 0; i < wNumMaterials; i++ )                            // Cykluj podle počtu materiálů
          if ( strlen( pMaterials[i].pc_texture_filename ) > 0 )          // Jestliže existuje jméno textury, tak
          {
               pMaterials[i].ui_texture = tex.LoadGLTexture( pMaterials[i].pc_texture_filename );    // Načti texturu
          }
          else                                                            // Jinak
          {
               pMaterials[i].ui_texture = 0;                              // Nastav texturu na 0

          }
}

//----------------------------------------------------------------------------------------------------//





Vykreslení modelu MilkShape

Vykreslování objektu provádí funkce Draw() třídy K_CModel

// Vykresli model
void K_CModel::Draw()
{
     Animation();                                                  // Rozjeď animaci
     GLboolean glb_tex_enabled = glIsEnabled( GL_TEXTURE_2D );     // Otestuj, jestli je zapnuto texturování

     // Vykresluj podle počtu skupin trojúhelníků
     for ( int i = 0; i < wNumGroups; i++ )
     {
          int i_material_index = pGroups[i].i_material_index;          // Vytvoř pomocnou proměnnou indexu materiálu
          if ( i_material_index >= 0 )                                 // Jestliže existuje index tak
          {
               glMaterialfv( GL_FRONT, GL_AMBIENT, pMaterials[i_material_index].pf_ambient );          // Nastav ambient material
               glMaterialfv( GL_FRONT, GL_DIFFUSE, pMaterials[i_material_index].pf_diffuse );          // Nastav diffuse material
               glMaterialfv( GL_FRONT, GL_SPECULAR, pMaterials[i_material_index].pf_specular );        // Nastav specular material
               glMaterialfv( GL_FRONT, GL_EMISSION, pMaterials[i_material_index].pf_emissive );        // Nastav emission material
               glMaterialf( GL_FRONT, GL_SHININESS, pMaterials[i_material_index].f_shininess );        // Nastav shininess material

               if ( pMaterials[i_material_index].ui_texture > 0 )                                      // Jestli materiál obsahuje texturu 
               {
                    glBindTexture( GL_TEXTURE_2D, pMaterials[i_material_index].ui_texture );           // Otexturuj skupinu trojúhelníků
                    glEnable( GL_TEXTURE_2D );                                                         // Zapni texturování
               }
               else                                                                                    // Jinak
                    glDisable( GL_TEXTURE_2D );                                                        // Vypni texturování
          }
          else                                                                                         // Jestlize neexistuje index materialu 
          {
               glDisable( GL_TEXTURE_2D );                                                             // Tak taky vypni texturování
          }

          glBegin( GL_TRIANGLES );                                                                     // Začni vykreslovat trojúhelníky 
          {
               for ( int j = 0; j < pGroups[i].w_num_triangles; j++ )                                  // Cykluj podle počtu trojúhelníku skupiny
               {
                    int i_material_index = pGroups[i].pw_triangle_index[j];                            // Vytvoř pomocnou proměnnou indexu trojúhelníků
                    const TModelTriangle* pModelTriangle = &pTriangles[i_material_index];              // Vytvoř ukazatel na strukturu ModelTriangle 

                    for ( int k = 0; k < 3; k++ )                                                      // Projeď 3 cykly (x, y, z hodnoty)
                    {
                         int i_index = pModelTriangle->pw_vertex_indices[k];                           // Vytvoř pomocnou proměnnou index vrcholu trojuhelnika skupiny

                         if ( pVertices[i_index].c_bone_id == -1 )                                     // Jestliže neexistuje kostra, tak vykresli staticky model
                         {
                              glTexCoord2f( pModelTriangle->pf_s[k], pModelTriangle->pf_t[k] );        // Texturová koordinace 
                              glNormal3fv( pModelTriangle->ppf_vertex_normals[k] );                    // Normálový vektor 
                              glVertex3fv( pVertices[i_index].pf_vertex );                             // Vrchol trojuhelníka 
                         }
                         else                                                                                   // Jestliže kostra existuje, tak pohybuj vrcholy, podle kloubů kostry
                         {
                              const K_CMatrix& mat_final = pJoints[pVertices[i_index].c_bone_id].mat_final;     // Vytvoř konstantu konečné matice rotace, posunutí

                              glTexCoord2f( pModelTriangle->pf_s[k], pModelTriangle->pf_t[k] );                 // Texturová koordinace

                              K_CVector v_new_normal( pModelTriangle->ppf_vertex_normals[k] );                  // Vytvoř nový normálový vektor typu K_CVector
                              v_new_normal.Transform3( mat_final );                                             // Vypočítej normálový vektor podle konečné matice rotace, posunutí
                              v_new_normal.Normalize();                                                         // Normalizuj vektor
                              glNormal3fv( v_new_normal.GetVector());                                           // Nastav normálový vektor

                              K_CVector v_new_vertex( pVertices[i_index].pf_vertex );                           // Vytvoř nový vrchol typu K_CVector
                              v_new_vertex.Transform( mat_final );                                              // Přesuň vertex podle konečné matice rotaca, posunutí
                              glVertex3fv( v_new_vertex.GetVector());                                           // Nastav vrchol
                         }
                    }
               }
          }
          glEnd();     // Konec vykreslování trojúhelníků
     }

     if ( glb_tex_enabled )                   // Jestliže je hodnota texEnabled pravdivá
          glEnable( GL_TEXTURE_2D );          // Zapni texturování
                                              // Jinak
     else
          glDisable( GL_TEXTURE_2D );         // Vypni texturování

}

která volá funkci Animation(). Funkce Animation() animuje všechny vrchloly modelu, podle kloubů kostry modelu.

// Animuj model
void K_CModel::Animation()
{
     double d_time = pTimer->GetTime();     // Zjisti hodnotu času

     if ( d_time > dTotalTime )             // Jestliže je čas větší než délka animace
     {
          if ( bLooping )                   // Jestliže je zapnuto cyklování, tak 
          {
               Restart();                   // Restartuj animaci 
               d_time = 0;                  // Nastav čas na 0
          }
          else                              // Jinak 
               d_time = dTotalTime;         // Nastav čas na délku animace
     }

     for ( int i = 0; i < wNumJoints; i++ )       // Cykluj podle počtu kloubů kostry 
     {
          float f_trans_vect[3];                  // Pomocná proměnná vektoru posunutí
          K_CMatrix mat_transform;                // Proměnná typu Matrix, matice posunutí 
          int i_frame;                            // Proměnná snímku
          TModelJoint *p_joint = &pJoints[i];     // Ukazatel na kloub kostry

          if ( p_joint->i_num_rotation_key_frames == 0 && p_joint->i_num_translation_key_frames == 0 )     // Jestliže se rotace, nebo posunutí snímku rovná 0, tak
          {
               p_joint->mat_final.Set( p_joint->mat_absolute.GetMatrix());                                 // Resetuj konečnou matici
               continue;                                                                                   // A pokračuj dále 
          }

          // Nastavení posunutí
          i_frame = p_joint->i_current_keyframe_trans;                            // Nastav aktuální posunutí snímku
          while ( i_frame < p_joint->i_num_translation_key_frames && p_joint->p_keyframe_trans[i_frame].f_time < d_time )     // Cykluj pokud je současné posunutí snímku menší než počet posunutí snímku
          {                                                                       // a zároveň čas posunutí snímku je menší než aktuální čas
               i_frame++;                                                         // Přičti snímek
          }
          p_joint->i_current_keyframe_trans = i_frame;                            // Nastav aktuální posunutí snímku

          if ( i_frame == 0 )                                                     // Jestliže je snímek roven 0, tak
               memcpy( f_trans_vect, p_joint->p_keyframe_trans[0].pf_parameter, sizeof ( float )*3 );             // Nastav vektor posunutí, podle základní hodnoty 0 parametru posunutí
          else if ( i_frame == p_joint->i_num_translation_key_frames )                                            //, nebo jestiže je snímek roven počtu posunutí snímku, tak
               memcpy( f_trans_vect, p_joint->p_keyframe_trans[i_frame-1].pf_parameter, sizeof ( float )*3 );     // Nastav vektor posunutí, podle základní hodnoty -1 parametru posunutí
          else                                                                                                    // Jinak 
          {
               assert( i_frame > 0 && i_frame < p_joint->i_num_translation_key_frames );                          // Jestliže je snímek větší než 0 a zároveň je snímek menší, než hodnota rotace snímku, tak pokračuj dále,
                                                                                                                  // jinak vyhoď chybovou hlášku 

               const K_CModel::TModelKeyframe& keyframe_curr = p_joint->p_keyframe_trans[i_frame];                // Nastav hodnotu posunutí aktuálního snímku
               const K_CModel::TModelKeyframe& keyframe_prev = p_joint->p_keyframe_trans[i_frame-1];              // Nastav hodnotu posunutí předešlého snímku

               float f_time_delta = keyframe_curr.f_time - keyframe_prev.f_time;                                  // Vypočti čas delta odečtením předešlého snímku od snímku aktuálního 
               float f_interp_value = ( float )(( d_time - keyframe_prev.f_time ) / f_time_delta );               // Vypočti interpolační hodnotu 

               // Vypočti vektor posunutí 
               f_trans_vect[0] = keyframe_prev.pf_parameter[0] + ( keyframe_curr.pf_parameter[0] - keyframe_prev.pf_parameter[0] ) * f_interp_value;
               f_trans_vect[1] = keyframe_prev.pf_parameter[1] + ( keyframe_curr.pf_parameter[1] - keyframe_prev.pf_parameter[1] ) * f_interp_value;
               f_trans_vect[2] = keyframe_prev.pf_parameter[2] + ( keyframe_curr.pf_parameter[2] - keyframe_prev.pf_parameter[2] ) * f_interp_value;
          }

          // Nastavení rotace 
          i_frame = p_joint->i_current_keyframe_rot;                                                                     // Nastav aktuální rotaci snímku
          while ( i_frame < p_joint->i_num_rotation_key_frames && p_joint->p_keyframe_rot[i_frame].f_time < d_time )     // Cykluj pokud je současná rotace snímku menší než počet rotace snímku 
          {                                                                                                              // a zároveň čas rotace snímku je menší než aktuální čas 
               i_frame++;                                                                                                // Přičti snímek  
          }
          p_joint->i_current_keyframe_rot = i_frame;                                                                     // Nastav aktuální rotaci snímku 

          if ( i_frame == 0 )                                                                                // Jestliže je snímek roven 0, tak
               mat_transform.SetRotationRadians( p_joint->p_keyframe_rot[0].pf_parameter );                  // Nastav vektor rotace, podle základní hodnoty 0 parametru posunutí
          else if ( i_frame == p_joint->i_num_rotation_key_frames )                                          //, nebo jestiže je snímek roven počtu rotace snímku, tak
               mat_transform.SetRotationRadians( p_joint->p_keyframe_rot[i_frame-1].pf_parameter );          // Nastav vektor rotace, podle základní hodnoty -1 parametru rotace 
          else                                                                                               // Jinak 
          {
               assert( i_frame > 0 && i_frame < p_joint->i_num_rotation_key_frames );                        // Jestliže je snímek větší než 0 a zároveň je snímek menší, než hodnota rotace snímku, tak pokračuj dále,
                                                                                                                          // jinak vyhoď chybovou hlášku 

               const K_CModel::TModelKeyframe& keyframe_curr = p_joint->p_keyframe_rot[i_frame];             // Nastav hodnotu rotace aktuálního snímku
               const K_CModel::TModelKeyframe& keyframe_prev = p_joint->p_keyframe_rot[i_frame-1];           // Nastav hodnotu rotace předešlého snímku

               float f_time_delta = keyframe_curr.f_time - keyframe_prev.f_time;                             // Vypočti čas delta odečtením předešlého snímku od snímku aktuálního 
               float f_interp_value = ( float )(( d_time - keyframe_prev.f_time ) / f_time_delta );          // Vypočti interpolační hodnotu

               assert( f_interp_value >= 0 && f_interp_value <= 1 );               // Jestliže je interpolační hodnota větší než 0 a zároveň je menší nebo rovno 1, tak pokračuj, jinak vyhoď chybovou hlášku

#if 1                                                                              // Jestliže je 1, funguje Quaternion (rychlejší výpočet)
               K_CQuaternion qua_prev( keyframe_prev.pf_parameter );               // Vypočti předchozí quaternion podle předešlé hodnoty parametru snímku 
               K_CQuaternion qua_curr( keyframe_curr.pf_parameter );               // Vypočti aktuální quaternion podle aktuální hodnoty parametru snímku
               K_CQuaternion qua_final( qua_prev, qua_curr, f_interp_value );      // Vypočti konečný quaternion pomocí interpolace mezi předchozím a aktuálním quaternionem

               mat_transform.SetRotationQuaternion( qua_final );                   // Převeď quaternion na rotační matici (rychlejší výpočet pomocí quaternionu)

#else                                                                              // Jestliže je 0 funguje obyčejný vektor 
               float f_rot_vect[3];                                                // Proměnná rotace vektoru  

               // Vypočti rotační matici 
               f_rot_vect[0] = keyframe_prev.f_parameter[0] + ( keyframe_curr.f_parameter[0] - keyframe_prev.f_parameter[0] ) * f_interp_value;
               f_rot_vect[1] = keyframe_prev.f_parameter[1] + ( keyframe_curr.f_parameter[1] - keyframe_prev.f_parameter[1] ) * f_interp_value;
               f_rot_vect[2] = keyframe_prev.f_parameter[2] + ( keyframe_curr.f_parameter[2] - keyframe_prev.f_parameter[2] ) * f_interp_value;

               m_transform.SetRotationRadians( f_rot_vect );                // Nastav rotační matici (pomalejší výpočet) 
#endif                                                                      // Konec definic výpočtů rotace 
          }

          mat_transform.SetTranslation( f_trans_vect );                     // Mastav matici posunutí  
          K_CMatrix mat_relative_final( p_joint->mat_relative );            // Vytvoř proměnnou relativeFinal typu Matrix. Jde o relativní matici 
          mat_relative_final.PostMultiply( mat_transform );                 // Vynásob konečnou relativní matici maticí posunutí

          if ( p_joint->i_parent == -1 )                                    // Jestliže kloub neni rodičovský (absolutní matice), tak
               p_joint->mat_final.Set( mat_relative_final.GetMatrix());     // Nastav konečnou relativní matici  
          else                                                              // Jinak  
          {
               p_joint->mat_final.Set( pJoints[p_joint->i_parent].mat_final.GetMatrix() );     // Nastav absolutní matici 
               p_joint->mat_final.PostMultiply( mat_relative_final );                          // Vynásob konečnou matici maticí relativní 
          }
     }
}


Funkce Draw() se volá v nekonečné smyčce




Rotace kloubů modelu pomocí Quaternionů

Rotaci kloubů modelu můžeme vytvořit dvěma způsoby. Pomocí rotačních matic X, Y, Z osy, nebo pomocí tzv. Quaternionů. Výpočet rotace pomocí Quaternionů je méně náročné na výpočet (rychlejší překreslování), protože pracuje s tranformační maticí, která je jednodužší, než matice rotační. Princip je takový, že se transformační matice převede na quaternion, pak se provede sférická interpolace quaternionu. Nakonec se výsledný quaternion převede zpět na tranformační matici. Více o quaternionech si můžete přečíst zde

Pro výpočet rotace kloubů modelu pomocí quaternionů jsem napsal tyto následující funkce.


//----------------------------------------------------------------------------------------------------//

/*
* Převeď úhel zadaný ve stupních na quaternion podle tohoto vzorce
*
* sin_a = sin( angle / 2 );
* cos_a = cos( angle / 2 );
*
* X = axis->x * sin_a;
* Y = axis->y * sin_a;
* Z = axis->z * sin_a;
* W = cos_a;
*
*  Tento výpočet proveď pro všechny osy X, Y, Z
*  p_axisAngle[0] se rovná ose X
*  p_axisAngle[1] se rovná ose Y
*  p_axisAngle[2] se rovná ose Z
*/  
void K_CQuaternion::FromAxisAngle( const float *pf_axis_angle )
{
     float f_angle;
     double d_sin_x, d_sin_y, d_sin_z, d_cos_x, d_cos_y, d_cos_z;

     f_angle = pf_axis_angle[0] * 0.5f;
     d_sin_x = sin( f_angle );
     d_cos_x = cos( f_angle );
     f_angle = pf_axis_angle[1] * 0.5f;
     d_sin_y = sin( f_angle );
     d_cos_y = cos( f_angle );
     f_angle = pf_axis_angle[2] * 0.5f;
     d_sin_z = sin( f_angle );
     d_cos_z = cos( f_angle );

     double d_cos_x_cos_y = d_cos_x * d_cos_y;
     double d_sin_x_sin_y = d_sin_x * d_sin_y;

     pfQuat[0] = ( float )( d_sin_x * d_cos_y * d_cos_z - d_cos_x * d_sin_y * d_sin_z );
     pfQuat[1] = ( float )( d_cos_x * d_sin_y * d_cos_z + d_sin_x * d_cos_y * d_sin_z );
     pfQuat[2] = ( float )( d_cos_x_cos_y * d_sin_z - d_sin_x_sin_y * d_cos_z );
     pfQuat[3] = ( float )( d_cos_x_cos_y * d_cos_z + d_sin_x_sin_y * d_sin_z );
}

//----------------------------------------------------------------------------------------------------//

// Proveď lineární interpolaci mezi quaterniony
void K_CQuaternion::Slerp( const K_CQuaternion& qua_1, K_CQuaternion& qua_2, float f_interp )
{
     // Vypočítej, jestli není druhý quaternion opačný
     float f_a = 0, f_b = 0;
     for (int i = 0; i < 4; i++ )                                             // Cykluj podle rozměru quaternionu
     {
          f_a += ( qua_1[i] - qua_2[i] ) * ( qua_1[i] - qua_2[i] );
          f_b += ( qua_1[i] + qua_2[i] ) * ( qua_1[i] + qua_2[i] );
     }
     if ( f_a > f_b )                                                         // Pokud je druhý quaternion opačný
          qua_2.Inverse();                                                    // Tak ho otoč správným směrem 

     // Vypočítej interpolace 
     float f_cosom = qua_1[0] * qua_2[0] + qua_1[1] * qua_2[1] + qua_1[2] * qua_2[2] + qua_1[3] * qua_2[3];     // Sečti quaterniony 
     double d_sclq1, d_sclq2;                                                 // Pomocné interpolační hodnoty 
     if (( 1.0 + f_cosom ) > 0.00000001 )                                     // Pokud je 1 + součet quaternionů větší než 0.00000001, tak 
     {
          if (( 1.0 - f_cosom ) > 0.00000001 )                                // Jestiže je 1 - součet quaternionů větší než 0.00000001, tak
          {
               /*
               * Protože je hodota součtu quaternionů moc velká, tak normalizuj
               * a vytvoř odpovídající rozmezí pro interpolační křivku
               */  
               double d_omega = acos( f_cosom );
               double d_sinom = sin( d_omega );
               d_sclq1 = sin(( 1.0 - f_interp ) * d_omega ) / d_sinom;
               d_sclq2 = sin( f_interp * d_omega ) / d_sinom;
          }
          else                                                                      // Jinak 
          {
               // Vytvoř odpovídající rozmezí pro interpolační křivku
               d_sclq1 = 1.0 - f_interp;
               d_sclq2 = f_interp;
          }
          for ( i = 0; i < 4; i++ )                                                  // Cykluj podle rozměru quaternionu 
               pfQuat[i] = ( float )( d_sclq1 * qua_1[i] + d_sclq2 * qua_2[i] );     // Výsledné quaterniony vynásob původními a ulož jako nový quaternion  
     }
     else                                                                            // Jinak 
     {
          // Proveď konjugaci quaternionu
          pfQuat[0] = -qua_1[1];
          pfQuat[1] = qua_1[0];
          pfQuat[2] = -qua_1[3];
          pfQuat[3] = qua_1[2];

          // Vytvoř odpovídající rozmezí pro interpolační křivku 
          d_sclq1 = sin(( 1.0 - f_interp ) * 0.5 * PI );
          d_sclq2 = sin( f_interp * 0.5 * PI );

          for (int i = 0; i < 3; i++ )                                                // Cykluj do 3, aby se vytvořil 3d quaternion
               pfQuat[i] = ( float )( d_sclq1 * qua_1[i] + d_sclq2 * pfQuat[i] );     // Výsledné quaterniony vynásob původními a ulož jako nový quaternion 
     }
}

//----------------------------------------------------------------------------------------------------// 

// Nastav opačný quaternion 
inline void K_CQuaternion::Inverse()
{
     pfQuat[0] = -pfQuat[0];
     pfQuat[1] = -pfQuat[1];
     pfQuat[2] = -pfQuat[2];
     pfQuat[3] = -pfQuat[3];
}

//----------------------------------------------------------------------------------------------------// 





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

TGA soubory jsou buď nekomprimované, nebo komprimované. Následující dvě funkce třídy K_CTexture tyto dva druhy TGA souborů načítají.

//----------------------------------------------------------------------------------------------------// 

// Načti nekomprimovaný soubor *.tga
bool K_CTexture::LoadUncompressedTGA( TTexture *ptexture, FILE *pfile_tga )
{
     if( fread( tga.puc_header, sizeof( tga.puc_header ), 1, pfile_tga ) == 0)                 // Zkus načíst TGA hlavičku, jinak
     {
          MessageBox( NULL, "Could not read info header", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu

          if( pfile_tga != NULL )       // Jestliže je soubor otevřen
          {
               fclose( pfile_tga );     // Tak ho zavři
          }

          return false;                 // Ukonči funkci
     }

     ptexture->i_width = tga.puc_header[1] * 256 + tga.puc_header[0];     // Vypočti šířku textury
     ptexture->i_height = tga.puc_header[3] * 256 + tga.puc_header[2];    // Vypočti výšku textury
     ptexture->i_bpp = tga.puc_header[4];                                 // Získej bitovou hloubku barev textury
     tga.i_width = ptexture->i_width;                                     // Kopíruj šířku do lokální struktury
     tga.i_height = ptexture->i_height;                                   // Kopíruj výšku do lokální struktury
     tga.i_bpp     = ptexture->i_bpp;                                     // Kopíruj bitovou hloubku barev do lokální struktury

     if(( ptexture->i_width <= 0 ) || ( ptexture->i_height <= 0 ) || (( ptexture->i_bpp != 24 ) && ( ptexture->i_bpp !=32 )))     // Jestliže existuje jedna z techto podmínek, tak
     {
          MessageBox( NULL, "Invalid texture information", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu

          if( pfile_tga != NULL )            // Jestliže je soubor otevřen 
          {
               fclose( pfile_tga );          // Tak ho zavři
          }

          return false;                      // Ukonči funkci
     }

     if( ptexture->i_bpp == 24 )             // Jestliže je 24 bitová hloubka barev 
     {
          ptexture->i_type = GL_RGB;         // Nastav typ textury na GL_RGB
     }
     else                                    // Jinak, pokud je 32 bitová
     {
          ptexture->i_type = GL_RGBA;        // Nastav typ textury na GL_RGBA 
     }

     tga.i_bytes_perpixel = ( tga.i_bpp / 8 );                                     // Vypočítej počet bytu na pixel
     tga.i_image_size = ( tga.i_bytes_perpixel * tga.i_width * tga.i_height );     // Vypočítej velikost souboru 
     ptexture->puc_image_data = ( GLubyte* ) malloc( tga.i_image_size );           // Alokuj pamět pro soubor

     if( ptexture->puc_image_data == NULL )                                        // Jestliže se nealokovala žádná paměť 
     {
          MessageBox( NULL, "Could not allocate memory for image", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu
          fclose( pfile_tga );          // Zavři soubor 
          return false;                 // Ukonči funkci 
     }

     if( fread( ptexture->puc_image_data, 1, tga.i_image_size, pfile_tga) != tga.i_image_size )         // Jestliže nejdou data přečíst 
     {
          MessageBox( NULL, "Could not allocate memory for image", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu 

          if( ptexture->puc_image_data != NULL )          // Jestliže alokovaná paměť pro soubor obsahuje nejaká data 
          {
               free( ptexture->puc_image_data );          // Tak je vymaž
          }

          fclose( pfile_tga );                            // Zavři soubor
          return false;                                   // Ukonči funkci 
     }

     // Prohoď byty barev z BGR to RGB 
     for( GLuint cswap = 0;  cswap < ( int )tga.i_image_size; cswap += tga.i_bytes_perpixel )
     {
          tga.uc_temp = ptexture->puc_image_data[cswap];
          ptexture->puc_image_data[cswap] = ptexture->puc_image_data[cswap + 2];
          ptexture->puc_image_data[cswap + 2] = tga.uc_temp;
     }

     fclose( pfile_tga );          // Zavři soubor 
     return true;                  // Ukonči funkci 
}

//----------------------------------------------------------------------------------------------------//

// Načti komprimovaný soubor *.tga 
bool K_CTexture::LoadCompressedTGA( TTexture *ptexture, FILE *pfile_tga )
{
     if( fread( tga.puc_header, sizeof( tga.puc_header ), 1, pfile_tga ) == 0)                 // Zkus načíst TGA hlavičku, jinak 
     {
          MessageBox( NULL, "Could not read info header", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu 

          if( pfile_tga != NULL )       // Jestliže je soubor otevřen
          {
               fclose( pfile_tga );     // Tak ho zavři
          }

          return false;                 // Ukonči funkci 
     }

     ptexture->i_width = tga.puc_header[1] * 256 + tga.puc_header[0];     // Vypočti šířku textury 
     ptexture->i_height = tga.puc_header[3] * 256 + tga.puc_header[2];    // Vypočti výšku textury 
     ptexture->i_bpp = tga.puc_header[4];                                 // Získej bitovou hloubku barev textury 
     tga.i_width = ptexture->i_width;                                     // Kopíruj šířku do lokální struktury
     tga.i_height = ptexture->i_height;                                   // Kopíruj výšku do lokální struktury 
     tga.i_bpp     = ptexture->i_bpp;                                     // Kopíruj bitovou hloubku barev do lokální struktury

     if(( ptexture->i_width <= 0 ) || ( ptexture->i_height <= 0 ) || (( ptexture->i_bpp != 24 ) && ( ptexture->i_bpp !=32 )))     // Jestliže existuje jedna z techto podmínek, tak 
     {

          MessageBox( NULL, "Invalid texture information", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu

          if( pfile_tga != NULL )       // Jestliže je soubor otevřen 
          {
               fclose( pfile_tga );     // Tak ho zavři 
          }

          return false;                 // Ukonči funkci 
     }

     tga.i_bytes_perpixel = ( tga.i_bpp / 8 );                                     // Vypočítej počet bytu na pixel
     tga.i_image_size = ( tga.i_bytes_perpixel * tga.i_width * tga.i_height );     // Vypočítej velikost souboru 
     ptexture->puc_image_data = ( GLubyte* ) malloc( tga.i_image_size );           // Alokuj pamět pro soubor

     if( ptexture->puc_image_data == NULL )                                        // Jestliže se nealokovala žádná paměť
     {
          MessageBox( NULL, "Could not allocate memory for image", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu
          fclose( pfile_tga );          // Zavři soubor 
          return false;                 // Ukonči funkci
     }

     GLuint i_pixelcount = tga.i_height * tga.i_width;                          // Počet pixelů v obrázku 
     GLuint i_currentpixel     = 0;                                             // Základní pixel pro čtení nastav na 0 
     GLuint i_currentbyte = 0;                                                  // Základní byte nastav na 0 
     GLubyte *puc_colorbuffer = ( GLubyte* )malloc( tga.i_bytes_perpixel );     // Alokuj paměť pro 1 pixel 

     do                                             // Cykluj dokud neprojedeš všechny pixely 
     {
          GLubyte chunkheader = 0;                                              // Hlavičku nastav na 0 

          if( fread( &chunkheader, sizeof( GLubyte ), 1, pfile_tga ) == 0 )     // Zkus načíst 1 bytovou hlavičku, jinak
          {
               MessageBox( NULL, "Could not read RLE header", "Error", MB_OK | MB_ICONERROR );    // Zobraz chybovou zprávu

               if( pfile_tga != NULL )                         // Jestliže je soubor otevřený 
               {
                    fclose( pfile_tga );                       // Tak ho zavři 
               }

               if( ptexture->puc_image_data != NULL )          // Jestliže alokovaná paměť pro soubor obsahuje nejaká data 
               {
                    free( ptexture->puc_image_data );          // Tak je vymaž 
               }
               return false;                                   // Ukonči funkci
          }

          // Jestliže velikost hlavičky je menší než 128, tak to znamená,
          // že jde o RAW kompresi barevné palety 
          // Následující barva je v hodnotě hlavičky o 1 vyšší 
          if( chunkheader < 128 )
          {
               chunkheader++;          // Přidej 1 pro následující hodnoty barvy

               // Čti RAW hodnoty barev 
               for( short counter = 0; counter < chunkheader; counter++ )
               {
                    if( fread( puc_colorbuffer, 1, tga.i_bytes_perpixel, pfile_tga ) != tga.i_bytes_perpixel )     // Zkus číst jeden pixel, jinak 
                    {
                         MessageBox( NULL, "Could not read image data", "Error", MB_OK | MB_ICONERROR );           // Zobraz chybovou zprávu

                         if( pfile_tga != NULL )                    // Jestliže je soubor otevřen 
                         {
                              fclose( pfile_tga );                  // Tak ho zavři
                         }

                         if( puc_colorbuffer != NULL )              // Jestliže alokovaná paměť pro data v paměti barev obsahuje nějaká data
                         {
                              free( puc_colorbuffer );              // Tak je vymaž
                         }

                         if( ptexture->puc_image_data != NULL )     // Jestliže alokovaná paměť pro soubor obsahuje nějaká data
                         {
                              free( ptexture->puc_image_data );     // Tak je vymaž
                         }

                         return false;                              // Ukonči funkci 
                    }

                    // Zapiš data do paměti a prohoď byty barev z BGR to RGB
                    ptexture->puc_image_data[i_currentbyte] = puc_colorbuffer[2];
                    ptexture->puc_image_data[i_currentbyte + 1] = puc_colorbuffer[1];
                    ptexture->puc_image_data[i_currentbyte + 2] = puc_colorbuffer[0];

                    if( tga.i_bytes_perpixel == 4 )                 // Jestliže jsou 4 byty na pixel, tak jde o 32 bitovou hloubku barev 
                    {
                         ptexture->puc_image_data[i_currentbyte + 3] = puc_colorbuffer[3];    // Proto kopíruj čtvrtý byt
                    }

                    i_currentbyte += tga.i_bytes_perpixel;          // Pričti k aktuálnímu bytu počet bytu na pixel 
                    i_currentpixel++;                               // Přičti aktuálnímu pixelu 1 

                    if( i_currentpixel > i_pixelcount )             // Jestliže je počet aktuálních pixelů větší než celkový počet pixelů obrázku, tak
                    {
                         MessageBox( NULL, "Too many pixels read", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu

                         if( pfile_tga != NULL )                    // Jestliže je soubor otevřen
                         {
                              fclose( pfile_tga );                  // Tak ho zavři
                         }

                         if( puc_colorbuffer != NULL )              // Jestliže alokovaná paměť pro data v paměti barev obsahuje nějaká data 
                         {
                              free( puc_colorbuffer );              // Tak je vymaž 
                         }

                         if( ptexture->puc_image_data != NULL )     // Jestliže alokovaná paměť pro soubor obsahuje nějaká data
                         {
                              free( ptexture->puc_image_data );     // Tak je vymaž
                         }

                         return false;                              // Ukonči funkci
                    }
               }
          }
          else
          {
               // Jestliže velikost hlavičky je větší než 128, tak to znamená,
               // že jde o RLE kompresi barevné palety 
               // Následující barva je v hodnotě hlavičky mínus 127
               chunkheader -= 127;               // Odečti od hodnoty hlavičky 127

               if( fread( puc_colorbuffer, 1, tga.i_bytes_perpixel, pfile_tga ) != tga.i_bytes_perpixel )      // Zkus načíst následující hodnoty, jinak
               {
                    MessageBox( NULL, "Could not read from file", "Error", MB_OK | MB_ICONERROR );             // Zobraz chybovou zprávu 

                    if( pfile_tga != NULL )                         // Jestliže je soubor otevřen 
                    {
                         fclose( pfile_tga );                       // Tak ho zavři  
                    }

                    if( puc_colorbuffer != NULL )                   // Jestliže alokovaná paměť pro data v paměti barev obsahuje nějaká data 
                    {
                         free( puc_colorbuffer );                   // Tak je vymaž 
                    }

                    if( ptexture->puc_image_data != NULL )          // Jestliže alokovaná paměť pro soubor obsahuje nějaká data 
                    {
                         free( ptexture->puc_image_data );          // Tak je vymaž 
                    }

                    return false;                                   // Ukonči funkci 
               }

               // Čti RLE hodnoty barev podle hodnoty hlavičky 
               for( short counter = 0; counter < chunkheader; counter++ )
               {
                    // Zapiš data do paměti a prohoď byty barev z BGR to RGB 
                    ptexture->puc_image_data[i_currentbyte] = puc_colorbuffer[2];
                    ptexture->puc_image_data[i_currentbyte + 1] = puc_colorbuffer[1];
                    ptexture->puc_image_data[i_currentbyte + 2] = puc_colorbuffer[0];

                    if( tga.i_bytes_perpixel == 4 )                 // Jestliže jsou 4 byty na pixel, tak jde o 32 bitovou hloubku barev 
                    {
                         ptexture->puc_image_data[i_currentbyte + 3] = puc_colorbuffer[3];    // Proto kopíruj čtvrtý byt 
                    }

                    i_currentbyte += tga.i_bytes_perpixel;          // Přičti k aktuálnímu bytu počet bytu na pixel 
                    i_currentpixel++;                               // Přičti aktuálnímu pixelu 1 

                    if( i_currentpixel > i_pixelcount )             // Jestliže je počet aktuálních pixelů větší než celkový počet pixelů obrázku, tak 
                    {
                         MessageBox( NULL, "Too many pixels read", "Error", MB_OK | MB_ICONERROR );     // Zobraz chybovou zprávu

                         if( pfile_tga != NULL )                    // Jestliže je soubor otevřen
                         {
                              fclose( pfile_tga );                  // Tak ho zavři 
                         }

                         if( puc_colorbuffer != NULL )              // Jestliže alokovaná paměť pro data v paměti barev obsahuje nějaká data 
                         {
                              free( puc_colorbuffer );              // Tak je vymaž 
                         }

                         if( ptexture->puc_image_data != NULL )     // Jestliže alokovaná paměť pro soubor obsahuje nějaká data 
                         {
                              free( ptexture->puc_image_data );     // Tak je vymaž 
                         }

                         return false;                              // Ukonči funkci 
                    }
               }
          }
     }
     while( i_currentpixel < i_pixelcount );               // Cykluj dokud neprojedeš všechny pixely

     fclose( pfile_tga );               // Zavři soubor 
     return true;                       // Vrať funkci, že vše proběhlo OK 
}

//----------------------------------------------------------------------------------------------------//


Funkce LoadGLTexture( char *pc_filename ) vytváří z načtených dat texturu.

// Vytvoř textury načtením z obrazových souborů (*.tga, *.bmp)
GLuint K_CTexture::LoadGLTexture( char *pc_filename )
{
     
     ///////////////////////////////
     // Načítání ze souboru *.tga //
     ///////////////////////////////
     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


     if ( !strcomp( c_ext, ".tga" ))          // Převeď řetězce na malá písmena a porovnej jestli má načítaný soubor příponu *.tga
     {
          if ( !LoadTGA( &texture, const_cast<char*>( pc_filename )))       // Zkus načíst soubor *.tga 
          {
               return 0;                                                     // Jinak vrať funkci 0
          }

          glGenTextures( 1, &texture.i_tex_id );                // Vytvoř texturu 

          // Začátek generování textury 
          glBindTexture( GL_TEXTURE_2D, texture.i_tex_id );     // Nastav texturový List s následujícími parametry

          // Parametry 2D textury 
          glTexImage2D( GL_TEXTURE_2D, 0, 3, texture.i_width, texture.i_height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture.puc_image_data );

          // 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 );
          // Konec generování textury 

          if ( texture.puc_image_data )              // Jestliže existuje obraz textury 
          {
               free( texture.puc_image_data );       // Tak obraz textury smaž 
          }

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





Nová kamera, transformace a rotace pomocí glTranslatef(), glRotatef()

U nové kamery jsem místo funkce gluLookAt() použil pro transformaci a rotaci kamery transformační a rotační matice glTranslatef() a glRotatef(). Názvy funkcí třídy K_CCamera zůstaly totožné, akorát algoritmy jsou jiné a myslím, že jednodužší.

//----------------------------------------------------------------------------------------------------//

#ifndef PI
#     define PI     3.1415926535897932384626433832795     // Definuj hodnotu PI
#endif

#ifndef DEG
#     define DEG     PI/180     // Převeď úhel RAD na DEG
#endif

//----------------------------------------------------------------------------------------------------//

// Konstruktor třídy K_CCamera 
K_CCamera::K_CCamera()
{
     fSpeed = 1.1f;            // Rychlost pohybu kamery

     pfAngle[0] = 0;           // X rotace kamery
     pfAngle[1] = 0;           // Y rotace kamery
     pfAngle[2] = 0;           // z rotace kamery

     pfPosition[0] = 0;        // X pozice kamery
     pfPosition[1] = 0;        // Y pozice kamery
     pfPosition[2] = -200;     // Z pozice kamery

}

//----------------------------------------------------------------------------------------------------//

// Destruktor třídy K_CCamera
K_CCamera::~K_CCamera()
{
}

//----------------------------------------------------------------------------------------------------//

// Pohni kamerou vpřed, nebo vzad podle hodnoty speed (+ nebo -)
void K_CCamera::MoveCamera( float f_speed )
{
     pfPosition[0] -= ( float ) ( f_speed * sin( -pfAngle[1] * DEG ));     // Vypočítej novou pozici X
     pfPosition[2] -=  ( float ) ( f_speed * cos( pfAngle[1] * DEG ));     // Vypočítej novou pozici Z
}

//----------------------------------------------------------------------------------------------------//

// Rotuj kamerou pomocí myši (First person view)
void K_CCamera::MouseRotate()
{
     POINT tagp_mouse_pos;                     // Pozice kurzoru myši
     int i_middle[2];                          // X, Y stred obrazovky
     i_middle[0] = SCREEN_WIDTH  >> 1;         // X střed obrazovky. Bitový posun doprava, způsobí dělení dvěma šířky obrazovky 
     i_middle[1] = SCREEN_HEIGHT >> 1;         // Y střed obrazovky. Bitový posun doprava, způsobí dělení dvěma výšky obrazovky 

     float f_angle[2];                         // Rotace kolem os (x,y) 
     f_angle[0] = 0.0f;                        // Rotace kolem osy X, otočení kamery nahoru a dolu
     f_angle[1] = 0.0f;                        // Rotace kolem osy Y, otočení kamery doprava a doleva 

     GetCursorPos( &tagp_mouse_pos );          // Získej X,Y pozici kurzoru myši 

     // Pokud je kurzor mimo okno, tak ho hoď doprostřed okna a dál nic nepočítej
     if(( tagp_mouse_pos.x > SCREEN_WIDTH ) && ( tagp_mouse_pos.y > SCREEN_HEIGHT ))
     {
          SetCursorPos( i_middle[0], i_middle[1] );
          return;
     }

     // Pokud je kurzor stále uprostřed okna, nic by se nemělo hýbat, proto opusť funkci
     if(( tagp_mouse_pos.x == i_middle[0] ) && ( tagp_mouse_pos.y == i_middle[1] )) return;

     // Jinak vrať kurzor zpět do středu okna
     SetCursorPos( i_middle[0], i_middle[1] );

    // Získej úhel X a Y podle směru pohybu kurzoru
     f_angle[0] = ( float )( ( i_middle[1] - tagp_mouse_pos.y ) ) / 5.0f;
     f_angle[1] = ( float )( ( i_middle[0] - tagp_mouse_pos.x ) ) / 5.0f;


     pfAngle[0] -= f_angle[0];              // Vypočítej úhel rotace X
     pfAngle[1] -= f_angle[1];              // Vypočítej úhel rotace Y

     if ( pfAngle[0] >= 90 )                // Jestliže je úhel X větší, nebo rovno 90 stupňů, tak
     {
          pfAngle[0] = 90;                  // Nastav úhel X na 90 stupnu
     }

     if ( pfAngle[0] <= -90 )               // Jestliže je úhel X menší, nebo rovno -90 stupňů, tak 
     {
          pfAngle[0] = -90;                 // Nastav úhel X na 90 stupňů 
     }

     if ( pfAngle[1] >= 360 )               // Jestliže je úhel Y větší, nebo rovno 360 stupňů, tak 
     {
          pfAngle[1] = pfAngle[1] - 360;    // Odečti 360 stupňů
     }

     if ( pfAngle[1] < 0 )                   // Jestliže je úhel Y menší než 0 stupňů, tak 
     {
          pfAngle[1] = pfAngle[1] + 360;     // Přičti 360 stupňů
     }
}

//----------------------------------------------------------------------------------------------------//

// Pohni kamerou dopředu
void K_CCamera::GoForward()
{
     MoveCamera( -fSpeed );
}

//----------------------------------------------------------------------------------------------------//

// Pohni kamerou dozadu 
void K_CCamera::GoBackward()
{
     MoveCamera( fSpeed );
}

//----------------------------------------------------------------------------------------------------// 

// Pohni kamerou nahoru 
void K_CCamera::GoUp()
{
     pfPosition[1] -= fSpeed;     // Vypočítej novou pozici Y 
}

//----------------------------------------------------------------------------------------------------//

// Pohni kamerou dolu
void K_CCamera::GoDown()
{
     pfPosition[1] += fSpeed;     // Vypočítej novou pozici Z 
}

//----------------------------------------------------------------------------------------------------//

// Pohni kamerou doleva
void K_CCamera::StrafeLeft()
{
     pfPosition[0] -= ( float ) ( fSpeed * sin(( pfAngle[1] - 90 ) * DEG ));     // Vypočítej novou pozici X 
     pfPosition[2] += ( float ) ( fSpeed * cos(( pfAngle[1] - 90 ) * DEG ));     // Vypočítej novou pozici Z 
}

//----------------------------------------------------------------------------------------------------//

// Pohni kamerou doprava  
void K_CCamera::StrafeRight()
{
     pfPosition[0] += ( float ) ( fSpeed * sin(( pfAngle[1] - 90 ) * DEG ));     // Vypočítej novou pozici X  
     pfPosition[2] -= ( float ) ( fSpeed * cos(( pfAngle[1] - 90 ) * DEG ));     // Vypočítej novou pozici Z 
}

//----------------------------------------------------------------------------------------------------// 

// Otoč kamerou doleva 
void K_CCamera::TurnLeft()
{
     pfAngle[1] -= fSpeed;     // Vypočítej nový úhel Y 
}

//----------------------------------------------------------------------------------------------------// 

// Otoč kamerou doprava 
void K_CCamera::TurnRight()
{
     pfAngle[1] += fSpeed;     // Vypočítej nový úhel Y 
}

//----------------------------------------------------------------------------------------------------//

// Otoč kamerou nahoru
void K_CCamera::TurnUp()
{
     pfAngle[0] -= fSpeed;        // Vypočítej nový úhel X

     if ( pfAngle[0] >= 90 )      // Jestliže je úhel X větší, nebo rovno 90 stupňů, tak
     {
          pfAngle[0] = 90;        // Nastav úhel X na 90 stupnu 
     }

     if ( pfAngle[0] <= -90 )     // Jestliže je úhel X menší, nebo rovno -90 stupňů, tak 
     {
          pfAngle[0] = -90;       // Nastav úhel X na 90 stupňů 
     }
}

//----------------------------------------------------------------------------------------------------//

// Otoč kamerou dolu 
void K_CCamera::TurnDown()
{
     pfAngle[0] += fSpeed;        // Vypočítej nový úhel X 

     if ( pfAngle[0] >= 90 )      // Jestliže je úhel X větší, nebo rovno 90 stupňů, tak 
     {
          pfAngle[0] = 90;        // Nastav úhel X na 90 stupnu 
     }

     if ( pfAngle[0] <= -90 )     // Jestliže je úhel X menší, nebo rovno -90 stupňů, tak 
     {
          pfAngle[0] = -90;       // Nastav úhel X na 90 stupňů
     }
}

//----------------------------------------------------------------------------------------------------//

// Pohybuj kamerou pomocí trasformační a rotační matice
void K_CCamera::Look()
{
     MouseRotate();     // Vypočítej rotaci kamery pomocí myši

     glRotatef( pfAngle[0], 1, 0, 0 );     // Rotace kolem osy X 
     glRotatef( pfAngle[1], 0, 1, 0 );     // Rotace kolem osy Y
     glTranslatef( pfPosition[0], pfPosition[1], pfPosition[2] );     // Pozice kamery

}

//----------------------------------------------------------------------------------------------------//

Tyto funkce jsou volány po stlačení daných kláves, které jsou definovány ve vstupní funkci aplikace WinMain(). Volání funkci třídy K_CCamera je stejné jako v předchozích tutorialech, takže se tím dále nebudem zabývat.




Světlo pohybující se s kamerou

Základní nastavení světla se provádí v konstruktoru třídy K_CLight

// Konstruktor třídy K_CCamera
K_CLight::K_CLight()
{
     // Nastav ambientní světlo, světlo které září i když jsou světla vypnuta
     pfAmbient[0] = 0.2f;
     pfAmbient[1] = 0.2f;
     pfAmbient[2] = 0.2f;
     pfAmbient[3] = 1.0f;

     // Nastav difuzní světlo, barvu světla které zdroj vyzařuje do okolí 
     pfDiffuse[0] = 0.8f;
     pfDiffuse[1] = 0.8f;
     pfDiffuse[2] = 0.8f;
     pfDiffuse[3] = 1.0f;

     // Nastav specular světlo, zrcadlový odraz světla
     pfSpecular[0] = 0.0f;
     pfSpecular[1] = 0.0f;
     pfSpecular[2] = 0.0f;
     pfSpecular[3] = 1.0f;

     // Nastav hodnotu intenzity odlesku 
     pfShininess = new float[1];
     pfShininess[0] = 0;

     // Nastav pozici světla 
     pfPosition[0] = 0.0f;
     pfPosition[1] = 0.0f;
     pfPosition[2] = 1.0f;
     pfPosition[3] = 0.0f;

     // Nastav směr světelného kužele
     pfDirection[0] = 0.0f;
     pfDirection[1] = 0.0f;
     pfDirection[2] = -1.0f;


     // Nastav emission světlo. Světlo, které vyzařuje určitý objekt, nepůsobí jako zdroj světla 
     pfEmission[0] = 0.0f;
     pfEmission[1] = 0.0f;
     pfEmission[2] = 0.0f;
     pfEmission[3] = 1.0f;

     // Nastav hodnotu lineárního slábnutí intenzity světla
     pfLinearAttenuation = new float[1];
     pfLinearAttenuation[0] = 0.2f;

     // Nastav charakter způsob rozptylu světla při odrazu 
     pfSpotExponent = new float[1];
     pfSpotExponent[0] = 1;

     // Nastav úhel, nímž se kužel světla rozevírá od své osy 
     pfSpotCutoff = new float[1];
     pfSpotCutoff[0] = 180;

}


Funkce DrawLight(), nám vykreslí světlo, podle zadaných hodnot.

// Vykresli světlo
void K_CLight::DrawLight()
{
     // Zadej hodnoty světla
     glLightfv(GL_LIGHT0, GL_AMBIENT, pfAmbient);
     glLightfv(GL_LIGHT0, GL_DIFFUSE, pfDiffuse);
     glLightfv(GL_LIGHT0, GL_SPECULAR, pfSpecular);
     glLightfv(GL_LIGHT0, GL_SHININESS, pfShininess);
     glLightfv(GL_LIGHT0, GL_POSITION, pfPosition);
     glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, pfDirection);
     glLightfv(GL_LIGHT0, GL_EMISSION, pfEmission);
     glLightfv(GL_LIGHT0, GL_LINEAR_ATTENUATION, pfLinearAttenuation);
     glLightfv(GL_LIGHT0, GL_SPOT_EXPONENT, pfSpotExponent);
     glLightfv(GL_LIGHT0, GL_SPOT_CUTOFF, pfSpotCutoff);



     glEnable(GL_LIGHTING);            // Zapni osvětlování
     glEnable(GL_LIGHT0);              // Zapni první světlo  
}


Funkce DrawLight()se volá při iniciaci scény. Protože vykreslujeme světlo, před výpočtem pohybu kamery, tak světlo chodí s kamerou. Pokud budeme chtít, aby světlo nereagovalo na kameru, jednoduše ho vykreslíme až po výpočtu kamery.

K_CLight *plig;      // Světlo

//----------------------------------------------------------------------------------------------------//

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

     plig = new K_CLight();    // Alokuj paměť pro světlo
     plig->DrawLight();        // Vykresli světlo



}

//----------------------------------------------------------------------------------------------------//


A to je vše. Samozřejmě jsem vše nevymyslel sám. Nejvíce mi pomohl Brett Porter, z jehož tutorialů jsem čerpal nejvíce informací. Model jsem také nevytvářel já. Autorem je Bart Secker. Tímto oběma děkuji.

Příště zkusíme načíst modely 3d Studia Max (*.3ds).

Kwan




Home