//---------------------------------------------------------------------
// Ukazkovy priklad cislo 33
// Autor: Pavel Tisnovsky
//
// Tento priklad svym ovladanim navazuje na priklad cislo 28 a jeho
// pokracovani.
// Nove je pridana funkce pro zmenu prikazu vykreslovani:
// krome klasickeho vykreslovani (glBegin, glEnd) lze pouzit i funkce
// glArrayElement() a glDrawArrays().
// Jsou pridany take tri klavesove zkratky - viz nize.
//
// Program lze ovladat kombinaci mysi a klavesnice. Pomoci leveho
// tlacitka mysi se aplikuje nektera operace, prave tlacitko mysi slouzi
// k zobrazeni kontextoveho menu. Zakladni udaje o scene se vypisuji
// primo do okna aplikace.
//
// Funkce klaves v tomto programu:
// ESC - ukonceni aplikace
// 'q' - ukonceni aplikace
// 'r' - rezim rotace objektu
// 't' - rezim posunu objektu dale/blize ke kamere
// 'z' - rezim posunu blizke orezavaci roviny
// 'x' - rezim posunu vzdalene orezavaci roviny
// 'f' - prepnuti do rezimu full-screen
// 'w' - prepnuti zpet do okna
// 'e' - povoleni testu pameti hloubky
// 'd' - zakazani testu pameti hloubky
// '[' - povoleni orezavani
// ']' - zakaz orezavani
// '.' - zmenseni zorneho uhlu
// ',' - zvetseni zorneho uhlu
// '>' - rychlejsi zmenseni zorneho uhlu
// '<' - rychlejsi zvetseni zorneho uhlu
// '0'-'9' - zmena rezimu vykreslovani prednich a zadnich stran polygonu
// F1  - normalni vykreslovani (glBegin() a glEnd())
// F2  - vykreslovani pomoci funkce glArrayElement()
// F3  - vykreslovani pomoci funkce glDrawArrays()
//---------------------------------------------------------------------

#include <GL/glut.h>                            // hlavickovy soubor funkci GLUTu a OpenGL
#include <stdio.h>
#include <stdlib.h>



enum {                                          // operace, ktere se mohou provadet s mysi:
    ROTATE,                                     // rotace objektu
    TRANSLATE,                                  // posun objektu
    NEAR_PLANE,                                 // zmena blizsi orezavaci roviny
    FAR_PLANE                                   // zmena vzdalenejsi orezavaci roviny
} operation=ROTATE;

enum {                                          // prikazy z kontextoveho menu:
    COMMAND_ROTATE,                             // rotace objektu
    COMMAND_TRANSLATE,                          // posun objektu
    COMMAND_NEAR_PLANE,                         // zmena blizsi orezavaci roviny
    COMMAND_FAR_PLANE,                          // zmena vzdalenejsi orezavaci roviny
    COMMAND_FRONT_FACE_DISABLE,                 // zakazani zobrazovani prednich sten polygonu
    COMMAND_FRONT_FACE_ENABLE,                  // povoleni zobrazovani prednich sten polygonu
    COMMAND_FRONT_FACE_POINTS,                  // predni steny polygonu vykreslovat jako body ve vrcholech
    COMMAND_FRONT_FACE_LINES,                   // predni steny polygonu vykreslovat jako usecky na hranach
    COMMAND_FRONT_FACE_POLYGONS,                // predni steny polygonu vykreslovat vyplnene
    COMMAND_BACK_FACE_DISABLE,                  // zakazani zobrazovani zadnich sten polygonu
    COMMAND_BACK_FACE_ENABLE,                   // povoleni zobrazovani zadnich sten polygonu
    COMMAND_BACK_FACE_POINTS,                   // zadni steny polygonu vykreslovat jako body ve vrcholech
    COMMAND_BACK_FACE_LINES,                    // zadni steny polygonu vykreslovat jako usecky na hranach
    COMMAND_BACK_FACE_POLYGONS,                 // zadni steny polygonu vykreslovat vyplnene
    COMMAND_ENABLE_DEPTH_TEST,                  // povoleni Z-bufferu
    COMMAND_DISABLE_DEPTH_TEST,                 // zakazani Z-bufferu
    COMMAND_DEPTH_FUNC_NEVER,                   // |
    COMMAND_DEPTH_FUNC_LESS,                    // |
    COMMAND_DEPTH_FUNC_LEQUAL,                  // | nastaveni funkci, ktere se budou provadet mezi hodnotou
    COMMAND_DEPTH_FUNC_EQUAL,                   // | zapsanou v Z-bufferu a hodnotou prave vykreslovaneho
    COMMAND_DEPTH_FUNC_GEQUAL,                  // | fragmentu
    COMMAND_DEPTH_FUNC_GREATER,                 // |
    COMMAND_DEPTH_FUNC_NOTEQUAL,                // |
    COMMAND_DEPTH_FUNC_ALWAYS,                  // |
    COMMAND_FULL_SCREEN,                        // prepnuti do rezimu okna pres celou obrazovku
    COMMAND_WINDOW,                             // prepnuti do rezimu normalniho okna
    COMMAND_CLIP_PLANE_ENABLE,                  // povoleni pridavne orezavaci roviny
    COMMAND_CLIP_PLANE_DISABLE,                 // zakazani pridavne orezavaci roviny
    COMMAND_CLIP_PLANE_XY,                      // |
    COMMAND_CLIP_PLANE_XZ,                      // |
    COMMAND_CLIP_PLANE_YZ,                      // | nastaveni orientace pridavne orezavaci roviny
    COMMAND_CLIP_PLANE_POSITIVE_DIRECTION,      // |
    COMMAND_CLIP_PLANE_NEGATIVE_DIRECTION,      // |
    COMMAND_CLIP_PLANE_DIST_0,                  // --
    COMMAND_CLIP_PLANE_DIST_1,                  // |
    COMMAND_CLIP_PLANE_DIST_2,                  // |
    COMMAND_CLIP_PLANE_DIST_3,                  // | nastaveni vzdalenosti pridavne orezavaci roviny
    COMMAND_CLIP_PLANE_DIST_4,                  // |
    COMMAND_CLIP_PLANE_DIST_5,                  // |
    COMMAND_DRAW_MODE_NORMAL,                   // vykreslovani pres funkce glBegin() a glEnd()
    COMMAND_DRAW_MODE_ARRAY_ELEMENT,            // vykreslovani pres funkci glArrayElement()
    COMMAND_DRAW_MODE_DRAW_ARRAYS,              // vykreslovani pres funkci glDrawArrays()
    COMMAND_QUIT,                               // ukonceni aplikace
    COMMAND_NONE
} command;

int   xnew=0, ynew=0, znew=0;                   // soucasna pozice mysi, ze ktere se pocitaji rotace a posuvy
int   xold=0, yold=0, zold=0;                   // minula pozice mysi, ze ktere se pocitaji rotace a posuvy
int   xx, yy, zz;                               // bod, ve kterem se nachazi kurzor mysi
int   np_old=1, np_new=1, npp=1;                // priznaky blizke orezavaci roviny
int   fp_old=900, fp_new=900, fpp=900;          // priznaky vzdalene orezavaci roviny

float fov=70.0;                                 // hodnota zorneho uhlu - field of view
float nearPlane=0.1;                            // blizsi orezavaci rovina
float farPlane=90.0;                            // vzdalenejsi orezavaci rovina
int   windowWidth;                              // sirka okna
int   windowHeight;                             // vyska okna
int   depthBufferEnabled=1;                     // priznak, zda je povolena pamet hloubky
int   frontFaceEnabled=1;                       // priznak viditelnosti prednich sten
int   backFaceEnabled=1;                        // priznak viditelnosti zadnich sten
int   clipPlaneNormal=COMMAND_CLIP_PLANE_XY;    // normala orezavaci roviny
int   clipPlaneDirection=COMMAND_CLIP_PLANE_POSITIVE_DIRECTION;// orientace normaly
int   clipPlaneDistance=COMMAND_CLIP_PLANE_DIST_0;// vzdalenost orezavaci roviny od pocatku
int   drawMode=COMMAND_DRAW_MODE_NORMAL;        // zpusob vykreslovani



//---------------------------------------------------------------------
// Funkce, ktera je zavolana pri vyberu prikazu z kontextoveho menu
//---------------------------------------------------------------------
void onMenu(int command)
{
    switch (command) {                          // rozeskok podle vybraneho prikazu
        case COMMAND_ROTATE:              operation=ROTATE;     break;
        case COMMAND_TRANSLATE:           operation=TRANSLATE;  break;
        case COMMAND_NEAR_PLANE:          operation=NEAR_PLANE; break;
        case COMMAND_FAR_PLANE:           operation=FAR_PLANE;  break;
        case COMMAND_FRONT_FACE_DISABLE:  glCullFace(GL_FRONT); glEnable(GL_CULL_FACE); glutPostRedisplay(); break;
        case COMMAND_FRONT_FACE_ENABLE:   glDisable(GL_CULL_FACE); glutPostRedisplay(); break;
        case COMMAND_FRONT_FACE_POINTS:   glPolygonMode(GL_FRONT, GL_POINT); glutPostRedisplay(); break;
        case COMMAND_FRONT_FACE_LINES:    glPolygonMode(GL_FRONT, GL_LINE); glutPostRedisplay(); break;
        case COMMAND_FRONT_FACE_POLYGONS: glPolygonMode(GL_FRONT, GL_FILL); glutPostRedisplay(); break;
        case COMMAND_BACK_FACE_DISABLE:   glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glutPostRedisplay(); break;
        case COMMAND_BACK_FACE_ENABLE:    glDisable(GL_CULL_FACE); glutPostRedisplay(); break;
        case COMMAND_BACK_FACE_POINTS:    glPolygonMode(GL_BACK, GL_POINT); glutPostRedisplay(); break;
        case COMMAND_BACK_FACE_LINES:     glPolygonMode(GL_BACK, GL_LINE); glutPostRedisplay(); break;
        case COMMAND_BACK_FACE_POLYGONS:  glutPostRedisplay(); glPolygonMode(GL_BACK, GL_FILL); break;
        case COMMAND_ENABLE_DEPTH_TEST:   depthBufferEnabled=1; glEnable(GL_DEPTH_TEST); glutPostRedisplay(); break;
        case COMMAND_DISABLE_DEPTH_TEST:  depthBufferEnabled=0; glDisable(GL_DEPTH_TEST); glutPostRedisplay(); break;
        case COMMAND_DEPTH_FUNC_NEVER:    glDepthFunc(GL_NEVER); glutPostRedisplay(); break;
        case COMMAND_DEPTH_FUNC_LESS:     glDepthFunc(GL_LESS); glutPostRedisplay(); break;
        case COMMAND_DEPTH_FUNC_LEQUAL:   glDepthFunc(GL_LEQUAL); glutPostRedisplay(); break;
        case COMMAND_DEPTH_FUNC_EQUAL:    glDepthFunc(GL_EQUAL); glutPostRedisplay(); break;
        case COMMAND_DEPTH_FUNC_GEQUAL:   glDepthFunc(GL_GEQUAL); glutPostRedisplay(); break;
        case COMMAND_DEPTH_FUNC_GREATER:  glDepthFunc(GL_GREATER); glutPostRedisplay(); break;
        case COMMAND_DEPTH_FUNC_NOTEQUAL: glDepthFunc(GL_NOTEQUAL); glutPostRedisplay(); break;
        case COMMAND_DEPTH_FUNC_ALWAYS:   glDepthFunc(GL_ALWAYS); glutPostRedisplay(); break;
        case COMMAND_FULL_SCREEN:         glutFullScreen(); break;
        case COMMAND_WINDOW:              glutReshapeWindow(500, 500); break;
        case COMMAND_CLIP_PLANE_ENABLE:   glEnable(GL_CLIP_PLANE0); glutPostRedisplay(); break;
        case COMMAND_CLIP_PLANE_DISABLE:  glDisable(GL_CLIP_PLANE0); glutPostRedisplay(); break;
        case COMMAND_CLIP_PLANE_XY:
        case COMMAND_CLIP_PLANE_XZ:
        case COMMAND_CLIP_PLANE_YZ:       clipPlaneNormal=command; glutPostRedisplay(); break;
        case COMMAND_CLIP_PLANE_POSITIVE_DIRECTION:
        case COMMAND_CLIP_PLANE_NEGATIVE_DIRECTION: clipPlaneDirection=command; glutPostRedisplay(); break;
        case COMMAND_CLIP_PLANE_DIST_0:
        case COMMAND_CLIP_PLANE_DIST_1:
        case COMMAND_CLIP_PLANE_DIST_2:
        case COMMAND_CLIP_PLANE_DIST_3:
        case COMMAND_CLIP_PLANE_DIST_4:
        case COMMAND_CLIP_PLANE_DIST_5:
                                          clipPlaneDistance=command; glutPostRedisplay(); break;
        case COMMAND_DRAW_MODE_NORMAL:
        case COMMAND_DRAW_MODE_ARRAY_ELEMENT:
        case COMMAND_DRAW_MODE_DRAW_ARRAYS:
                                          drawMode=command; glutPostRedisplay(); break;
        case COMMAND_QUIT:                exit(0); break;
        case COMMAND_NONE: break;
    }
}



//---------------------------------------------------------------------
// Funkce pro inicializaci vykreslovani
//---------------------------------------------------------------------
void onInit(void)
{
    GLdouble equation[]={0.0, 1.0, 0.0, 0.0};
    int menu;                                   // identifikatory jednotlivych menu a submenu
    int menuMode;
    int menuDrawMode;
    int menuDepth;
    int menuDFunc;
    int menuFrontFace;
    int menuBackFace;
    int menuClipping, menuClipNormal, menuClipDirection, menuClipDistance;
    int menuQuit;

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       // barva pozadi obrazku
    glClearDepth(1.0f);                         // implicitni hloubka ulozena v pameti hloubky
    glEnable(GL_DEPTH_TEST);                    // povoleni funkce pro testovani hodnot v pameti hloubky
    glDepthFunc(GL_LESS);                       // funkce pro testovani fragmentu
    glShadeModel(GL_SMOOTH);                    // nastaveni stinovaciho rezimu
    glPolygonMode(GL_FRONT, GL_FILL);           // nastaveni rezimu vykresleni modelu
    glPolygonMode(GL_BACK, GL_FILL);            // jak pro predni tak pro zadni steny
    glPointSize(3.0f);                          // body zobrazovat s prumerem 3 pixely
    glEnable(GL_POINT_SMOOTH);                  // povoleni antialiasingu bodu
    glLineWidth(1.0f);                          // usecky zobrazovat siroke 1 pixel
    glEnable(GL_LINE_SMOOTH);                   // povoleni antialiasingu usecek
    glDisable(GL_CULL_FACE);                    // zadne hrany ani steny se nebudou odstranovat
    glClipPlane(GL_CLIP_PLANE0, equation);

    menuMode=glutCreateMenu(onMenu);            // vytvoreni kontextoveho menu a jeho submenu
    glutSetMenu(menuMode);
    glutAddMenuEntry("Rotate object\tR", COMMAND_ROTATE);
    glutAddMenuEntry("Translate object\tT", COMMAND_TRANSLATE);
    glutAddMenuEntry("Translate near clipping plane\tZ", COMMAND_NEAR_PLANE);
    glutAddMenuEntry("Translate far clipping plane\tX", COMMAND_FAR_PLANE);

    menuDrawMode=glutCreateMenu(onMenu);
    glutSetMenu(menuDrawMode);
    glutAddMenuEntry("Normal\tF1", COMMAND_DRAW_MODE_NORMAL);
    glutAddMenuEntry("ArrayElement\tF2", COMMAND_DRAW_MODE_ARRAY_ELEMENT);
    glutAddMenuEntry("DrawArrays\tF3", COMMAND_DRAW_MODE_DRAW_ARRAYS);

    menuDepth=glutCreateMenu(onMenu);
    glutSetMenu(menuDepth);
    glutAddMenuEntry("Enable depth test\tE", COMMAND_ENABLE_DEPTH_TEST);
    glutAddMenuEntry("Disable depth test\tD", COMMAND_DISABLE_DEPTH_TEST);

    menuDFunc=glutCreateMenu(onMenu);
    glutSetMenu(menuDFunc);
    glutAddMenuEntry("Never", COMMAND_DEPTH_FUNC_NEVER);
    glutAddMenuEntry("Less", COMMAND_DEPTH_FUNC_LESS);
    glutAddMenuEntry("Less or Equal", COMMAND_DEPTH_FUNC_LEQUAL);
    glutAddMenuEntry("Equal", COMMAND_DEPTH_FUNC_EQUAL);
    glutAddMenuEntry("Greater or Equal", COMMAND_DEPTH_FUNC_GEQUAL);
    glutAddMenuEntry("Greater", COMMAND_DEPTH_FUNC_GREATER);
    glutAddMenuEntry("Not Equal", COMMAND_DEPTH_FUNC_NOTEQUAL);
    glutAddMenuEntry("Always", COMMAND_DEPTH_FUNC_ALWAYS);

    menuFrontFace=glutCreateMenu(onMenu);
    glutSetMenu(menuFrontFace);
    glutAddMenuEntry("Disable\t1", COMMAND_FRONT_FACE_DISABLE);
    glutAddMenuEntry("Enable\t2", COMMAND_FRONT_FACE_ENABLE);
    glutAddMenuEntry("Points\t3", COMMAND_FRONT_FACE_POINTS);
    glutAddMenuEntry("Lines\t4", COMMAND_FRONT_FACE_LINES);
    glutAddMenuEntry("Polygons\t5", COMMAND_FRONT_FACE_POLYGONS);

    menuBackFace=glutCreateMenu(onMenu);
    glutSetMenu(menuBackFace);
    glutAddMenuEntry("Disable\t6", COMMAND_BACK_FACE_DISABLE);
    glutAddMenuEntry("Enable\t7", COMMAND_BACK_FACE_ENABLE);
    glutAddMenuEntry("Points\t8", COMMAND_BACK_FACE_POINTS);
    glutAddMenuEntry("Lines\t9", COMMAND_BACK_FACE_LINES);
    glutAddMenuEntry("Polygons\t0", COMMAND_BACK_FACE_POLYGONS);

    menuClipNormal=glutCreateMenu(onMenu);
    glutSetMenu(menuClipNormal);
    glutAddMenuEntry("x-axis", COMMAND_CLIP_PLANE_YZ);
    glutAddMenuEntry("y-axis", COMMAND_CLIP_PLANE_XZ);
    glutAddMenuEntry("z-axis", COMMAND_CLIP_PLANE_XY);

    menuClipDirection=glutCreateMenu(onMenu);
    glutSetMenu(menuClipDirection);
    glutAddMenuEntry("Positive", COMMAND_CLIP_PLANE_POSITIVE_DIRECTION);
    glutAddMenuEntry("Negative", COMMAND_CLIP_PLANE_NEGATIVE_DIRECTION);

    menuClipDistance=glutCreateMenu(onMenu);
    glutSetMenu(menuClipDistance);
    glutAddMenuEntry("0", COMMAND_CLIP_PLANE_DIST_0);
    glutAddMenuEntry("1", COMMAND_CLIP_PLANE_DIST_1);
    glutAddMenuEntry("2", COMMAND_CLIP_PLANE_DIST_2);
    glutAddMenuEntry("3", COMMAND_CLIP_PLANE_DIST_3);
    glutAddMenuEntry("4", COMMAND_CLIP_PLANE_DIST_4);

    menuClipping=glutCreateMenu(onMenu);
    glutSetMenu(menuClipping);
    glutAddMenuEntry("Enable clipping", COMMAND_CLIP_PLANE_ENABLE);
    glutAddMenuEntry("Disable clipping", COMMAND_CLIP_PLANE_DISABLE);
    glutAddSubMenu("Normal", menuClipNormal);
    glutAddSubMenu("Normal' direction", menuClipDirection);
    glutAddSubMenu("Distance", menuClipDistance);

    menuQuit=glutCreateMenu(onMenu);
    glutSetMenu(menuQuit);
    glutAddMenuEntry("Yes\tQ", COMMAND_QUIT);
    glutAddMenuEntry("No", COMMAND_NONE);

    menu=glutCreateMenu(onMenu);
    glutAddSubMenu("Mode", menuMode);
    glutAddSubMenu("Draw mode", menuDrawMode);
    glutAddSubMenu("Front faces", menuFrontFace);
    glutAddSubMenu("Back faces", menuBackFace);
    glutAddSubMenu("Depth test", menuDepth);
    glutAddSubMenu("Depth function", menuDFunc);
    glutAddSubMenu("Clipping", menuClipping);
    glutAddMenuEntry("Full screen\tF", COMMAND_FULL_SCREEN);
    glutAddMenuEntry("Window\tW", COMMAND_WINDOW);
    glutAddSubMenu("Quit", menuQuit);
    glutAttachMenu(GLUT_RIGHT_BUTTON);          // menu je navazano na prave tlacitko mysi
}



//---------------------------------------------------------------------
// Nastaveni souradneho systemu v zavislosti na velikosti okna
//---------------------------------------------------------------------
void onResize(int w, int h)                     // argumenty w a h reprezentuji novou velikost okna
{
    glViewport(0, 0, w, h);                     // viditelna oblast pres cele okno
    windowWidth=w;                              // zapamatovat si velikost okna
    windowHeight=h;
}



//---------------------------------------------------------------------
// Nastaveni perspektivni projekce
//---------------------------------------------------------------------
void setPerspectiveProjection(void)
{
    glMatrixMode(GL_PROJECTION);                // zacatek modifikace projekcni matice
    glLoadIdentity();                           // vymazani projekcni matice (=identita)
    gluPerspective(fov, (double)windowWidth/(double)windowHeight, nearPlane, farPlane);// nastaveni perspektivni kamery
    glMatrixMode(GL_MODELVIEW);                 // bude se menit modelova matice
    glLoadIdentity();                           // nahrat jednotkovou matici
}



//---------------------------------------------------------------------
// Nastaveni ortogonalni projekce
//---------------------------------------------------------------------
void setOrthogonalProjection(void)
{
    glMatrixMode(GL_PROJECTION);                // zacatek modifikace projekcni matice
    glLoadIdentity();                           // vymazani projekcni matice (=identita)
    glOrtho(0, windowWidth, 0, windowHeight, -1, 1);
    glMatrixMode(GL_MODELVIEW);                 // bude se menit modelova matice
    glLoadIdentity();                           // nahrat jednotkovou matici
}



//---------------------------------------------------------------------
// Funkce pro nastaveni orezavaci roviny
//---------------------------------------------------------------------
void setClipPlane(void)
{
    GLdouble dir=1.0;                           // je bud -1 nebo 1 podle orientace normaly
    GLdouble equation[]={0.0f, 0.0f, 0.0f, 0.0f};// parametry A, B, C, D ze vztahu Ax+By+Cz+D=0

    if (clipPlaneDirection==COMMAND_CLIP_PLANE_NEGATIVE_DIRECTION)
        dir=-1.0;                               // podle vyberu z menu se muze zmenit orientace normaly

    switch (clipPlaneNormal) {                  // zmena smeru normaly
        case COMMAND_CLIP_PLANE_XY: equation[2]=dir; break;
        case COMMAND_CLIP_PLANE_XZ: equation[1]=dir; break;
        case COMMAND_CLIP_PLANE_YZ: equation[0]=dir; break;
    }
    equation[3]=clipPlaneDistance-COMMAND_CLIP_PLANE_DIST_0;// vzdalenost orezavaci roviny
    glClipPlane(GL_CLIP_PLANE0, equation);      // nastaveni orezavaci roviny
}



//---------------------------------------------------------------------
// Vypsani retezce do okna na pozici [x,y]
//---------------------------------------------------------------------
void printString(int x, int y, char *text)
{
    glRasterPos2i(x, y);                        // pozice prvniho znaku retezce
    for (; *text; text++)                       // pruchod retezcem
        glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *text);// vykresleni jednoho znaku
}



//--------------------------------------------------------------------
// Vypsani informaci o nastavenych parametrech
//--------------------------------------------------------------------
void drawInfoText(void)
{
    char str[100];
    char *dm[]={"normal", "glArrayElement", "glDrawArrays"};
    int  ce=glIsEnabled(GL_CLIP_PLANE0);
    glDisable(GL_CLIP_PLANE0);                  // text by se mel vypsat vzdy, proto vypneme orezavani
    glColor3f(1.0,1.0,1.0);
    sprintf(str, "Rotation x:    %d", xnew);
    printString(10, 100, str);                  // vypsat rotaci ve smeru osy x
    sprintf(str, "Rotation y:    %d", ynew);
    printString(10, 85, str);                   // vypsat rotaci ve smeru osy y
    sprintf(str, "Translation:   %d", znew);
    printString(10, 70, str);                   // vypsat posun objektu od kamery
    sprintf(str, "Near plane:    %5.2f", nearPlane);
    printString(10, 55, str);                   // vzdalenost blizke orezavaci roviny
    sprintf(str, "Far plane:     %5.2f", farPlane);
    printString(10, 40, str);                   // vzdalenost vzdalene orezavaci roviny
    sprintf(str, "Field of view: %5.2f", fov);
    printString(10, 25, str);                   // zorny uhel kamery
    sprintf(str, "Clip plane:    %s", ce ? "enabled":"disabled");
    printString(10, 10, str);                   // povoleni ci zakazani orezavaci roviny
    sprintf(str, "Draw mode: %s", dm[drawMode-COMMAND_DRAW_MODE_NORMAL]);
    printString(250, 100, str);
    if (ce) glEnable(GL_CLIP_PLANE0);           // opetovne zapnuti orezavani
}



//--------------------------------------------------------------------
// Vykresleni objektu
//--------------------------------------------------------------------
void drawObjectNormal(void)
{
    glBegin(GL_QUADS);                          // vykresleni otevrene krychle - sten domecku
        glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(-5.0f, -5.0f, -5.0f);
        glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-5.0f, -5.0f,  5.0f);
        glColor3f(0.0f, 1.0f, 1.0f); glVertex3f( 5.0f, -5.0f,  5.0f);
        glColor3f(1.0f, 0.0f, 0.0f); glVertex3f( 5.0f, -5.0f, -5.0f);

        glColor3f(1.0f, 0.0f, 1.0f); glVertex3f(-5.0f,  5.0f, -5.0f);
        glColor3f(1.0f, 1.0f, 0.0f); glVertex3f(-5.0f,  5.0f,  5.0f);
        glColor3f(1.0f, 1.0f, 1.0f); glVertex3f( 5.0f,  5.0f,  5.0f);
        glColor3f(0.0f, 0.0f, 1.0f); glVertex3f( 5.0f,  5.0f, -5.0f);

        glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-5.0f, -5.0f, -5.0f);
        glColor3f(0.0f, 1.0f, 1.0f); glVertex3f(-5.0f, -5.0f,  5.0f);
        glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(-5.0f,  5.0f,  5.0f);
        glColor3f(1.0f, 0.0f, 1.0f); glVertex3f(-5.0f,  5.0f, -5.0f);

        glColor3f(0.0f, 1.0f, 0.0f); glVertex3f( 5.0f, -5.0f, -5.0f);
        glColor3f(0.0f, 1.0f, 1.0f); glVertex3f( 5.0f, -5.0f,  5.0f);
        glColor3f(1.0f, 0.0f, 0.0f); glVertex3f( 5.0f,  5.0f,  5.0f);
        glColor3f(1.0f, 0.0f, 1.0f); glVertex3f( 5.0f,  5.0f, -5.0f);
    glEnd();

    glBegin(GL_TRIANGLES);                      // vykresleni strechy domecku z trojuhelniku
        glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(-5.0f,  5.0f, -5.0f);
        glColor3f(0.0f, 1.0f, 1.0f); glVertex3f( 5.0f,  5.0f, -5.0f);
        glColor3f(1.0f, 1.0f, 1.0f); glVertex3f( 0.0f, 11.0f,  0.0f);

        glColor3f(1.0f, 0.0f, 0.0f); glVertex3f( 5.0f,  5.0f, -5.0f);
        glColor3f(1.0f, 1.0f, 0.0f); glVertex3f( 5.0f,  5.0f,  5.0f);
        glColor3f(1.0f, 1.0f, 1.0f); glVertex3f( 0.0f, 11.0f,  0.0f);

        glColor3f(0.0f, 1.0f, 0.0f); glVertex3f( 5.0f,  5.0f,  5.0f);
        glColor3f(0.0f, 1.0f, 1.0f); glVertex3f(-5.0f,  5.0f,  5.0f);
        glColor3f(1.0f, 1.0f, 1.0f); glVertex3f( 0.0f, 11.0f,  0.0f);

        glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-5.0f,  5.0f,  5.0f);
        glColor3f(1.0f, 1.0f, 0.0f); glVertex3f(-5.0f,  5.0f, -5.0f);
        glColor3f(1.0f, 1.0f, 1.0f); glVertex3f( 0.0f, 11.0f,  0.0f);
    glEnd();
}



static GLint vertices[]={-5,-5,-5,      // pole pozic vrcholu
                         -5,-5, 5,
                          5,-5, 5,
                          5,-5,-5,
                         -5, 5,-5,
                         -5, 5, 5,
                          5, 5, 5,
                          5, 5,-5,
                         -5,-5,-5,
                         -5,-5, 5,
                         -5, 5, 5,
                         -5, 5,-5,
                          5,-5,-5,
                          5,-5, 5,
                          5, 5, 5,
                          5, 5,-5};
static GLfloat colors[]={ 0.0f, 0.0f, 1.0f,// pole barev vrcholu
                          0.0f, 1.0f, 0.0f,
                          0.0f, 1.0f, 1.0f,
                          1.0f, 0.0f, 0.0f,
                          1.0f, 0.0f, 1.0f,
                          1.0f, 1.0f, 0.0f,
                          1.0f, 1.0f, 1.0f,
                          0.0f, 0.0f, 1.0f,
                          0.0f, 1.0f, 0.0f,
                          0.0f, 1.0f, 1.0f,
                          1.0f, 0.0f, 0.0f,
                          1.0f, 0.0f, 1.0f,
                          0.0f, 1.0f, 0.0f,
                          0.0f, 1.0f, 1.0f,
                          1.0f, 0.0f, 0.0f,
                          1.0f, 0.0f, 1.0f};



//--------------------------------------------------------------------
// vykresleni sceny pomoci funkce glArrayElement()
// pro jednoduchost se vykresluje pouze otevrena krychle
//--------------------------------------------------------------------
void drawArrayElement(void)
{
    int i;
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glColorPointer(3, GL_FLOAT, 0, colors);
    glVertexPointer(3, GL_INT, 0, vertices);
    glBegin(GL_QUADS);
        for (i=0; i<16; i++)                // vykreslit vsechny vrcholy
            glArrayElement(i);
    glEnd();
}



//--------------------------------------------------------------------
// vykresleni sceny pomoci funkce glDrawArrays()
// pro jednoduchost se vykresluje pouze otevrena krychle
//--------------------------------------------------------------------
void drawArrays(void)
{
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glColorPointer(3, GL_FLOAT, 0, colors);
    glVertexPointer(3, GL_INT, 0, vertices);
    glDrawArrays(GL_QUADS, 0, 16);
}



//--------------------------------------------------------------------
// Tato funkce je volana pri kazdem prekresleni okna
//--------------------------------------------------------------------
void onDisplay(void)
{
    if (depthBufferEnabled)                     // podle povoleni pameti hloubky
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// vymazani barvoveho bufferu i pameti hloubky
    else
        glClear(GL_COLOR_BUFFER_BIT);           // vymazani vsech bitovych rovin barvoveho bufferu

    setOrthogonalProjection();                  // ortogonalni rezim pro kresleni 2D obrazku
    drawInfoText();                             // vypsat informace o nastaveni

    setPerspectiveProjection();                 // nastaveni perspektivni kamery
    glTranslatef(0.0f, 0.0f, -50.0f);           // posun objektu dale od kamery
    glTranslatef(0.0f, 0.0f, znew);             // priblizeni ci vzdaleni objektu podle pohybu kurzoru mysi
    glRotatef(ynew, 1.0f, 0.0f, 0.0f);          // rotace objektu podle pohybu kurzoru mysi
    glRotatef(xnew, 0.0f, 1.0f, 0.0f);
    setClipPlane();                             // nastaveni orezavaci roviny

    switch (drawMode) {                         // vykresleni objektu ve scene
        case COMMAND_DRAW_MODE_NORMAL:
            drawObjectNormal();
            break;
        case COMMAND_DRAW_MODE_ARRAY_ELEMENT:
            drawArrayElement();
            break;
        case COMMAND_DRAW_MODE_DRAW_ARRAYS:
            drawArrays();
            break;
        default:
            break;
    }

    glFlush();                                  // provedeni a vykresleni vsech zmen
    glutSwapBuffers();                          // a prohozeni predniho a zadniho bufferu
}



//---------------------------------------------------------------------
// Tato funkce je volana pri stlaceni ASCII klavesy
//---------------------------------------------------------------------
void onKeyboard(unsigned char key, int x, int y)
{
    if (key>='A' && key<='Z')                   // uprava velkych pismen na mala
        key+='a'-'A';                           // pro zjednoduseni prikazu switch

    switch (key) {
        case 27:    exit(0);                            break;  // ukonceni aplikace
        case 'q':   exit(0);                            break;  // ukonceni aplikace
        case 'f':   onMenu(COMMAND_FULL_SCREEN);        break;  // prepnuti do full-screen rezimu
        case 'w':   onMenu(COMMAND_WINDOW);             break;  // prepnuti zpet do okna
        case 'e':   onMenu(COMMAND_ENABLE_DEPTH_TEST);  break;  // povoleni funkce pameti hloubky
        case 'd':   onMenu(COMMAND_DISABLE_DEPTH_TEST); break;  // zakazani funkce pameti hloubky
        case 'r':   onMenu(COMMAND_ROTATE);             break;  // rezim rotace objektu
        case 't':   onMenu(COMMAND_TRANSLATE);          break;  // rezim posunu objektu
        case 'z':   onMenu(COMMAND_NEAR_PLANE);         break;  // rezim posunu blizke orezavaci roviny
        case 'x':   onMenu(COMMAND_FAR_PLANE);          break;  // rezim posunu vzdalene orezavaci roviny
        case '[':   onMenu(COMMAND_CLIP_PLANE_ENABLE);  break;  // povoleni orezavani
        case ']':   onMenu(COMMAND_CLIP_PLANE_DISABLE); break;  // zakaz orezavani
        case '.':   fov--;  glutPostRedisplay();        break;  // zmenseni zorneho uhlu
        case ',':   fov++;  glutPostRedisplay();        break;  // zvetseni zorneho uhlu
        case '>':   fov-=5; glutPostRedisplay();        break;  // rychlejsi zmenseni zorneho uhlu
        case '<':   fov+=5; glutPostRedisplay();        break;  // rychlejsi zvetseni zorneho uhlu
        case '1':   onMenu(COMMAND_FRONT_FACE_DISABLE); break;  // zakaz vykresleni prednich sten
        case '2':   onMenu(COMMAND_FRONT_FACE_ENABLE);  break;  // povoleni vykresleni prednich sten
        case '3':   onMenu(COMMAND_FRONT_FACE_POINTS);  break;  // predni steny se vykresli jako body
        case '4':   onMenu(COMMAND_FRONT_FACE_LINES);   break;  // predni steny se vykresli jako usecky
        case '5':   onMenu(COMMAND_FRONT_FACE_POLYGONS);break;  // predni steny se vykresli jako polygony
        case '6':   onMenu(COMMAND_BACK_FACE_DISABLE);  break;  // zakaz vykresleni zadnich  sten
        case '7':   onMenu(COMMAND_BACK_FACE_ENABLE);   break;  // povoleni vykresleni zadnich sten
        case '8':   onMenu(COMMAND_BACK_FACE_POINTS);   break;  // zadni steny se vykresli jako body
        case '9':   onMenu(COMMAND_BACK_FACE_LINES);    break;  // zadni steny se vykresli jako usecky
        case '0':   onMenu(COMMAND_BACK_FACE_POLYGONS); break;  // zadni steny se vykresli jako polygony
        default:                                        break;
    }
}



//---------------------------------------------------------------------
// Tato funkce je zavolana pri stlaceni klavesy, ktera nema ASCII kod
//---------------------------------------------------------------------
void onSpecialKey(int key, int x, int y)
{
    switch (key) {
        case GLUT_KEY_F1:   onMenu(COMMAND_DRAW_MODE_NORMAL);           break;
        case GLUT_KEY_F2:   onMenu(COMMAND_DRAW_MODE_ARRAY_ELEMENT);    break;
        case GLUT_KEY_F3:   onMenu(COMMAND_DRAW_MODE_DRAW_ARRAYS);      break;
        default:                                                        break;
    }
}



//---------------------------------------------------------------------
// Tato funkce je volana pri stisku ci pusteni tlacitka mysi
//---------------------------------------------------------------------
void onMouseButton(int button, int state, int x, int y)
{
    if (button==GLUT_LEFT_BUTTON) {             // pri zmene stavu leveho tlacitka
        switch (operation) {
            case ROTATE:                        // rotace objektu
                if (state==GLUT_DOWN) {         // pri stlaceni tlacitka
                    xx=x;                       // zapamatovat pozici kurzoru mysi
                    yy=y;
                }
                else {                          // pri pusteni tlacitka
                    xold=xnew;                  // zapamatovat novy pocatek
                    yold=ynew;
                }
                glutPostRedisplay();            // prekresleni sceny
                break;
            case TRANSLATE:                     // posun objektu
                if (state==GLUT_DOWN) zz=y;     // pri stlaceni tlacitka zapamatovat polohu kurzoru mysi
                else zold=znew;                 // pri pusteni tlacitka zapamatovat novy pocatek
                glutPostRedisplay();            // prekresleni sceny
                break;
            case NEAR_PLANE:                    // zmena polohy blizke orezavaci roviny
                if (state==GLUT_DOWN) npp=y;    // pri stlaceni tlacitka zapamatovat polohu kurzoru mysi
                else np_old=np_new;
                break;
            case FAR_PLANE:                     // zmena polohy vzdalene orezavaci roviny
                if (state==GLUT_DOWN) fpp=y;    // pri stlaceni tlacitka zapamatovat polohu kurzoru mysi
                else fp_old=fp_new;
                break;
            default:
                break;
        }
    }
}



//---------------------------------------------------------------------
// Tato funkce je volana pri pohybu mysi se stlacenym tlacitkem.
// To, ktere tlacitko je stlaceno si musime predem zaznamenat do
// globalni promenne stav ve funkci onMouseButton()
//---------------------------------------------------------------------
void onMouseMotion(int x, int y)
{
    switch (operation) {
        case ROTATE:                            // stav rotace objektu
            xnew=xold+x-xx;                     // vypocitat novou pozici
            ynew=yold+y-yy;
            glutPostRedisplay();                // a prekreslit scenu
            break;
        case TRANSLATE:                         // stav priblizeni/oddaleni objektu
            znew=zold+y-zz;                     // vypocitat novou pozici
            glutPostRedisplay();                // a prekreslit scenu
            break;
        case NEAR_PLANE:                        // priblizeni/vzdaleni blizke orezavaci roviny
            np_new=np_old+y-npp;
            nearPlane=np_new/100.0;
            onResize(windowWidth, windowHeight);// zmena projekcni matice
            glutPostRedisplay();                // a prekreslit scenu
            break;
        case FAR_PLANE:                         // priblizeni/vzdaleni vzdalene orezavaci roviny
            fp_new=fp_old+y-fpp;
            farPlane=fp_new/10.0;
            onResize(windowWidth, windowHeight);// zmena projekcni matice
            glutPostRedisplay();                // a prekreslit scenu
            break;
    }
}



//---------------------------------------------------------------------
// Hlavni funkce konzolove aplikace
//---------------------------------------------------------------------
int main(int argc, char **argv)
{
    glutInit(&argc, argv);                      // inicializace knihovny GLUT
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);// nastaveni dvou barvovych bufferu a pameti hloubky
    glutInitWindowPosition(30, 30);             // pocatecni pozice leveho horniho rohu okna
    glutInitWindowSize(500, 500);               // pocatecni velikost okna
    glutCreateWindow("Priklad na OpenGL cislo 33");// vytvoreni okna pro kresleni
    glutDisplayFunc(onDisplay);                 // registrace funkce volane pri prekreslovani okna
    glutReshapeFunc(onResize);                  // registrace funkce volane pri zmene velikosti okna
    glutKeyboardFunc(onKeyboard);               // registrace funkce volane pri stlaceni klavesy
    glutSpecialFunc(onSpecialKey);              // registrace funkce volane pri stlaceni non-ASCII klavesy
    glutMouseFunc(onMouseButton);               // registrace funkce volane pri stlaceni ci pusteni tlacitka
    glutMotionFunc(onMouseMotion);              // registrace funkce volane pri pohybu mysi se stlacenym tlacitkem
    onInit();                                   // inicializace vykreslovani
    glutMainLoop();                             // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                                   // navratova hodnota vracena operacnimu systemu
}



//---------------------------------------------------------------------
// Konec zdrojoveho souboru
//---------------------------------------------------------------------