//---------------------------------------------------------------------
// Ukazkovy priklad cislo 24
// Autor: Pavel Tisnovsky
//
// V tomto programu se provede nastaveni kamery v ortogonalnim rezimu.
// Pote se vykresli jednoduche teleso ve tvaru domecku, se kterym lze
// rotovat a posouvat pomoci mysi (podle stlaceneho tlacitka mysi). 
// Domecek se poprve vykresli jako vyplneny model, ale pomoci klaves
// '0' az '9' lze provest prepnuti na jiny zpusob vykreslovani.
// Pod domeckem je vykreslena mrizka, aby se vykreslovani trosku
// zpomalilo a byl videt vyznam double-bufferingu.
// V tomto priklade neni double-buffering pouzity.
//---------------------------------------------------------------------

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



int xnew=0, ynew=0, xnew2=0, ynew2=0;           // soucasna pozice mysi, ze ktere se pocitaji rotace a posuny
int xold=0, yold=0, xold2=0, yold2=0;           // minula pozice mysi, ze ktere se pocitaji rotace a posuny
int xx, yy, xx2, yy2;                           // bod, ve kterem se nachazi kurzor mysi
int stav;                                       // stav tlacitek mysi



//---------------------------------------------------------------------
// Funkce pro inicializaci vykreslovani
//---------------------------------------------------------------------
void onInit(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       // barva pozadi
    glPolygonMode(GL_FRONT, GL_FILL);           // nastaveni rezimu vykresleni polygonalniho modelu
    glPolygonMode(GL_BACK, GL_FILL);            // jak pro zadni tak pro predni steny
    glDisable(GL_CULL_FACE);                    // zadne hrany ani steny se nebudou odstranovat
    glPointSize(5.0);                           // velikost vykreslovanych bodu
    glEnable(GL_POINT_SMOOTH);                  // povoleni antialiasingu bodu
}



//---------------------------------------------------------------------
// 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
    glMatrixMode(GL_PROJECTION);                // zacatek modifikace projekcni matice
    glLoadIdentity();                           // vymazani projekcni matice (=identita)
    glOrtho(-20, 20, -20, 20, -30, 30);         // nastaveni ortogonalni projekce
                                                // zkuste zde zmenit polohu orezavacich ploch
}



//---------------------------------------------------------------------
// Tato funkce je volana pri kazdem prekresleni okna
//---------------------------------------------------------------------
void onDisplay(void)
{
    float i;
    glClear(GL_COLOR_BUFFER_BIT);               // vymazani vsech bitovych rovin barvoveho bufferu
    glMatrixMode(GL_MODELVIEW);                 // bude se menit modelova matice
    glLoadIdentity();                           // nahrat jednotkovou matici
                                                // zde se bude provadet nastaveni kamery
    glTranslatef(xnew2/10.0f, -ynew2/10.0f, 0.0f); // posun objektu podle pohybu kurzoru mysi
    gluLookAt(4.0f, 4.0f, 18.0f,                // bod, odkud se kamera diva
              0.0f, 0.0f,  0.0f,                // bod, kam se kamera diva
              0.0f, 1.0f,  0.0f);               // poloha "stropu" ve scene
    glRotatef(ynew, 1.0f, 0.0f, 0.0f);          // rotace objektu podle pohybu kurzoru mysi
    glRotatef(xnew, 0.0f, 1.0f, 0.0f);

    glColor3f(1.0f, 1.0f, 1.0f);
    glBegin(GL_LINES);
    for (i=-10.0f; i<=10.0f; i+=0.5f) {         // vykresleni mrizky pod domeckem pro zdrzeni
        glVertex3f(-10.0f, -5.0f, i);
        glVertex3f( 10.0f, -5.0f, i);
        glVertex3f(     i, -5.0f, -10.0f);
        glVertex3f(     i, -5.0f,  10.0f);
    }
    glEnd();

    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();
    glFlush();                                  // provedeni a vykresleni vsech zmen
}



//---------------------------------------------------------------------
// 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:
        case 'q':
            exit(0);
            break;                              // pokud byla stlacena klavesa ESC, konec programu
        case 'f':
            glutFullScreen();                   // prepnuti na celou obrazovku
            break;
        case 'w':
            glutReshapeWindow(500, 500);        // prepnuti zpet do okna
            break;
        case '1':                               // prepnuti na zobrazeni pouze vrcholu
            glPolygonMode(GL_FRONT,GL_POINT);
            glPolygonMode(GL_BACK,GL_POINT);
            glutPostRedisplay();
            break;
        case '2':                               // prepnuti na zobrazeni dratoveho modelu
            glPolygonMode(GL_FRONT,GL_LINE);
            glPolygonMode(GL_BACK,GL_LINE);
            glutPostRedisplay();
            break;
        case '3':                               // prepnuti na zobrazeni vyplnenych sten
            glPolygonMode(GL_FRONT,GL_FILL);
            glPolygonMode(GL_BACK,GL_FILL);
            glutPostRedisplay();
            break;
        case '4':                               // dalsi mozny zpusob vykreslovani polygonu
            glPolygonMode(GL_FRONT,GL_FILL);
            glPolygonMode(GL_BACK,GL_LINE);
            glutPostRedisplay();
            break;
        case '5':                               // dalsi mozny zpusob vykreslovani polygonu
            glPolygonMode(GL_FRONT,GL_LINE);
            glPolygonMode(GL_BACK,GL_FILL);
            glutPostRedisplay();
            break;
        case '6':                               // dalsi mozny zpusob vykreslovani polygonu
            glPolygonMode(GL_FRONT,GL_POINT);
            glPolygonMode(GL_BACK,GL_FILL);
            glutPostRedisplay();
            break;
        case '7':                               // dalsi mozny zpusob vykreslovani polygonu
            glPolygonMode(GL_FRONT,GL_FILL);
            glPolygonMode(GL_BACK,GL_POINT);
            glutPostRedisplay();
            break;
        case '8':                               // dalsi mozny zpusob vykreslovani polygonu
            glPolygonMode(GL_FRONT,GL_POINT);
            glPolygonMode(GL_BACK,GL_LINE);
            glutPostRedisplay();
            break;
        case '9':                               // dalsi mozny zpusob vykreslovani polygonu
            glPolygonMode(GL_FRONT,GL_LINE);
            glPolygonMode(GL_BACK,GL_POINT);
            glutPostRedisplay();
            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) {             // reakce na zmenu stavu leveho tlacitka mysi
        if (state==GLUT_DOWN) {                 // pri stlaceni
            stav=1;                             // nastaveni pro funkci motion
            xx=x;                               // zapamatovat pozici kurzoru mysi
            yy=y;
        }
        else {                                  // GLUT_UP
            stav=0;                             // normalni stav
            xold=xnew;                          // zapamatovat novy pocatek rotace
            yold=ynew;
        }
    }
    if (button==GLUT_RIGHT_BUTTON) {            // reakce na zmenu stavu praveho tlacitka mysi
        if (state==GLUT_DOWN) {                 // pri stlaceni
            stav=2;                             // nastaveni pro funkci motion
            xx2=x;                              // zapamatovat pozici kurzoru mysi
            yy2=y;
        }
        else {                                  // GLUT_UP
            stav=0;                             // normalni stav
            xold2=xnew2;                        // zapamatovat novy pocatek posunu
            yold2=ynew2;
        }
    }
    glutPostRedisplay();                        // prekresleni sceny
}



//---------------------------------------------------------------------
// Tato funkce je volana pri pohybu mysi se stlacenym tlacitkem
// To, ktere tlacitko je stlaceno si musime zaznamenat do glob. promenne
//---------------------------------------------------------------------
void onMouseMotion(int x, int y)
{
    if (stav==1) {                              // stav rotace objektu
        xnew=xold+x-xx;                         // vypocitat novou pozici
        ynew=yold+y-yy;
        glutPostRedisplay();                    // a prekreslit scenu
    }
    if (stav==2) {                              // stav presunu objektu
        xnew2=xold2+x-xx2;                      // vypocitat novou pozici
        ynew2=yold2+y-yy2;
        glutPostRedisplay();                    // a prekreslit scenu
    }
}



//---------------------------------------------------------------------
// Hlavni funkce konzolove aplikace
//---------------------------------------------------------------------
int main(int argc, char **argv)
{
    glutInit(&argc, argv);                      // inicializace knihovny GLUT
    glutInitDisplayMode(GLUT_RGB);              // nastaveni bufferu
    glutInitWindowPosition(30, 30);             // pocatecni pozice leveho horniho rohu okna
    glutInitWindowSize(500, 500);               // pocatecni velikost okna
    glutCreateWindow("Priklad na OpenGL cislo 24");// 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
    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
//---------------------------------------------------------------------