//---------------------------------------------------------------------
// Priklad cislo 18
// Autor: Pavel Tisnovsky
//
// Vykresleni trojrozmernych objektu vytvorenych v knihovne GLUT.
//---------------------------------------------------------------------

#include <math.h>
#include <GL/glut.h>

float materialAmbient[]={0.3, 0.2, 0.2, 1.0}; // ambientni slozka materialu
float materialDiffuse[]={0.9, 0.1, 0.1, 1.0}; // difuzni slozka materialu
float materialSpecular[]={0.0, 1.0, 1.0, 0.5}; // barva odlesku
float lightAmbient[]= {0.5, 0.5, 0.5, 1.0}; // ambientni slozka svetla
float lightDiffuse[]= {0.9, 0.9, 0.9, 1.0}; // difuzni slozka svetla
float lightSpecular[]={0.0, 0.0, 0.0, 1.0}; // barva odlesku
float lightPosition[]={1.0, 1.0, 1.0, 0.0}; // definice pozice svetla

int   newX=0, newY=0, newZ=0;               // soucasna pozice, ze ktere se pocitaji rotace a posuny
int   oldZ=0;                               // minula pozice, ze ktere se pocitaji posuny
int   mouseX=0, mouseY=0, mouseZ=0;         // body, ve kterych se nachazi kurzor mysi
int   mouseStatus=0;                        // stav tlacitek mysi

float fieldOfView=45.0;                     // zorny uhel - field of view
float nearClippingPlane=0.1;                // blizsi orezavaci rovina
float farClippingPlane=200.0;               // vzdalenejsi orezavaci rovina
int   objectType=9;                         // typ vykreslovaneho objektu



//---------------------------------------------------------------------
// Tato funkce provede zakladni nastaveni vykreslovani teles
//---------------------------------------------------------------------
void init(void)
{
    glClearColor(0.0, 0.0, 0.0, 0.0);       // barva pro mazani color-bufferu
    glShadeModel(GL_SMOOTH);                // nastaveni Gouraudova stinovani
    glClearDepth(1.0f);                     // barva pro mazani Z-bufferu
    glEnable(GL_DEPTH_TEST);                // nastaveni funkce pro testovani hodnot v Z-bufferu
    glDepthFunc(GL_LESS);                   // nastaveni porovnavaci funkce pro Z-buffer
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// vylepseni zobrazovani pri vypoctu perspektivy
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // stav vykresleni vyplnenych polygonu
    glPointSize(5.0);                       // velikost vykreslovanych bodu
    glLineWidth(3.0);                       // sirka vykreslovanych car
    glEnable(GL_POINT_SMOOTH);              // povoleni antialiasingu bodu
    glEnable(GL_LINE_SMOOTH);               // povoleni antialiasingu car
    glLightfv(GL_LIGHT0,GL_AMBIENT,lightAmbient); // nastaveni parametru svetla
    glLightfv(GL_LIGHT0,GL_DIFFUSE,lightDiffuse);
    glLightfv(GL_LIGHT0,GL_POSITION,lightPosition);
    glEnable(GL_LIGHTING);                  // povoleni osvetleni
    glEnable(GL_LIGHT0);                    // zapnuti prvniho svetla
    glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,materialAmbient);// nastaveni vlastnosti materialu
    glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,materialDiffuse);
    glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,materialSpecular);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0);
}



//---------------------------------------------------------------------
// Nastaveni souradneho systemu v zavislosti na velikosti okna
//---------------------------------------------------------------------
void onResize(int w, int h)
{
    glViewport(0, 0, w, h);                 // viditelna oblast
    glMatrixMode(GL_PROJECTION);            // zmena projekcni matice
    glLoadIdentity();                       // nastaveni perspektivni projekce
    gluPerspective(fieldOfView, (double)w/(double)h, nearClippingPlane, farClippingPlane);
    glMatrixMode(GL_MODELVIEW);             // zmena modelove matice
}



//---------------------------------------------------------------------
// Tato funkce je volana pri kazdem prekresleni okna
//---------------------------------------------------------------------
void onDisplay(void)
{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); // vymazani barvoveho bufferu
    glMatrixMode(GL_MODELVIEW);             // bude se menit modelova matice
    glLoadIdentity();                       // nahrat jednotkovou matici

    glTranslatef(0.0f, 0.0f, -50.0f);       // posun modelu dale od kamery
    glTranslatef(0.0f, 0.0f, newZ);
    glRotatef(newY, 1.0, 0.0, 0.0);         // rotace objektu podle pohybu kurzoru mysi
    glRotatef(newX, 0.0, 1.0, 0.0);

    switch (objectType) {                   // vykresleni vybraneho objektu
        case  0: glutWireCube(5);                          break;
        case  1: glutWireSphere(5, 16, 16);                break;
        case  2: glutWireCone(5, 5, 16, 16);               break;
        case  3: glutWireTorus(2, 5, 16, 16);              break;
        case  4: glScalef(3,3,3); glutWireDodecahedron();  break;
        case  5: glScalef(3,3,3); glutWireOctahedron();    break;
        case  6: glScalef(3,3,3); glutWireTetrahedron();   break;
        case  7: glScalef(3,3,3); glutWireIcosahedron();   break;
        case  8: glutWireTeapot(5);                        break;
        case  9: glutSolidCube(5);                         break;
        case 10: glutSolidSphere(5,16,16);                 break;
        case 11: glutSolidCone(5,5,16,16);                 break;
        case 12: glutSolidTorus(2,5,16,16);                break;
        case 13: glScalef(3,3,3); glutSolidDodecahedron(); break;
        case 14: glScalef(3,3,3); glutSolidOctahedron();   break;
        case 15: glScalef(3,3,3); glutSolidTetrahedron();  break;
        case 16: glScalef(3,3,3); glutSolidIcosahedron();  break;
        case 17: glutSolidTeapot(5);                       break;
    }

    glFlush();                              // provedeni vsech prikazu
    glutSwapBuffers();                      // a prohozeni bufferu
}



//---------------------------------------------------------------------
// Tato funkce je volana pri stlaceni ASCII klavesy
//---------------------------------------------------------------------
void onKeyboard(unsigned char key, int x, int y)
{
    key=(key>'A' && key<='Z') ? key+'a'-'A':key; // prevod na mala pismena

    switch (key) {                          // rozeskok podle stlacene klavesy
        case 27:                            // klavesa Escape
            exit(0);                        // ukonceni programu
            break;
        case 'f':
            glutFullScreen();               // prepnuti na celou obrazovku
            break;
        case 'w':
            glutReshapeWindow(500, 500);    // prepnuti zpet do okna
            glutPositionWindow(50, 50);
            break;
        default:
            break;
    }
}



//---------------------------------------------------------------------
// Tato funkce je volana pri zmene stavu tlacitek mysi
//---------------------------------------------------------------------
void onMouseClick(int button, int state, int x, int y)
{
    if (button==GLUT_RIGHT_BUTTON) {        // prave tlacitko aktivuje posun od pozorovatele
        if (state==GLUT_DOWN) {             // pri stlaceni
            mouseStatus=1;                  // nastaveni pro funkci motion
            mouseZ=y;                       // zapamatovat pozici kurzoru mysi
        }
        else {
            mouseStatus=0;
            oldZ=newZ;                      // zapamatovat novy pocatek
        }
    }
    glutPostRedisplay();                    // prekresleni sceny
}



//---------------------------------------------------------------------
// Tato funkce je volana pri posunu mysi se stlacenym tlacitkem
//---------------------------------------------------------------------
void onMouseDrag(int x, int y)
{
    if (mouseStatus==1) {                   // provadi se presun
        newZ=oldZ+y-mouseZ;                 // vypocitat novou pozici
        glutPostRedisplay();                // a prekreslit scenu
    }
}



//---------------------------------------------------------------------
// Tato funkce je volana pri posunu mysi bez stlaceneho tlacitka
//---------------------------------------------------------------------
void onMouseMove(int x, int y)
{
    newX=x;                                 // zapamatovat si pozici kurzoru
    newY=y;                                 // pro otaceni
    glutPostRedisplay();                    // prekreslit scenu
}



//---------------------------------------------------------------------
// Tato funkce je zavolana pri vyberu polozky z menu
//---------------------------------------------------------------------
void onCommand(int command)
{
    if (command<18)                         // byla vybrana polozka s nazvem objektu
        objectType=command;

    switch (command) {
        case 18:                            // zobrazovat se budou pouze vrcholy polygonu
            glPolygonMode(GL_FRONT,GL_POINT);
            glPolygonMode(GL_BACK,GL_POINT);
            break;
        case 19:                            // zobrazovat se budou pouze hrany polygonu
            glPolygonMode(GL_FRONT,GL_LINE);
            glPolygonMode(GL_BACK,GL_LINE);
            break;
        case 20:                            // zobrazovat se budou vyplnene polygony
            glPolygonMode(GL_FRONT,GL_FILL);
            glPolygonMode(GL_BACK,GL_FILL);
            break;
        case 21:                            // ukonceni aplikace
            exit(0);
        default:
            break;
    }

    glutPostRedisplay();                    // prekreslit scenu
}



//---------------------------------------------------------------------
// Tato funkce vytvori menu a navaze ho na leve tlacitko mysi
//---------------------------------------------------------------------
void createMenu(void)
{
    int i, j, k, l;
    i=glutCreateMenu(onCommand);            // vytvoreni submenu a registrace callback funkce
    glutAddMenuEntry("Wireframe cube",         0);
    glutAddMenuEntry("Wireframe sphere",       1);
    glutAddMenuEntry("Wireframe cone",         2);
    glutAddMenuEntry("Wireframe torus",        3);
    glutAddMenuEntry("Wireframe dodecahedron", 4);
    glutAddMenuEntry("Wireframe octahedron",   5);
    glutAddMenuEntry("Wireframe tetrahedron",  6);
    glutAddMenuEntry("Wireframe icosahedron",  7);
    glutAddMenuEntry("Wireframe teapot",       8);
    j=glutCreateMenu(onCommand);            // vytvoreni submenu a registrace callback funkce
    glutAddMenuEntry("Solid cube",             9);
    glutAddMenuEntry("Solid sphere",          10);
    glutAddMenuEntry("Solid cone",            11);
    glutAddMenuEntry("Solid torus",           12);
    glutAddMenuEntry("Solid dodecahedron",    13);
    glutAddMenuEntry("Solid octahedron",      14);
    glutAddMenuEntry("Solid tetrahedron",     15);
    glutAddMenuEntry("Solid icosahedron",     16);
    glutAddMenuEntry("Solid teapot",          17);
    k=glutCreateMenu(onCommand);            // vytvoreni submenu a registrace callback funkce
    glutAddMenuEntry("Points",                18);
    glutAddMenuEntry("Lines",                 19);
    glutAddMenuEntry("Fill",                  20);
    l=glutCreateMenu(onCommand);            // vytvoreni submenu a registrace callback funkce
    glutAddMenuEntry("Yes",                   21);
    glutAddMenuEntry("No",                    22);
    glutCreateMenu(onCommand);              // vytvoreni menu
    glutAddSubMenu("Wireframe objects", i);
    glutAddSubMenu("Solid objects", j);
    glutAddSubMenu("Polygon mode", k);
    glutAttachMenu(GLUT_LEFT_BUTTON);
}



//---------------------------------------------------------------------
// Hlavni funkce konzolove aplikace
//---------------------------------------------------------------------
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);                  // inicializace knihovny GLUT
    glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH); // graficky mod okna
    glutInitWindowSize(500, 500);           // pocatecni velikost okna
    glutInitWindowPosition(10, 10);         // pocatecni pozice okna
    glutCreateWindow("Priklad cislo 18");   // vytvoreni okna pro kresleni
    glutReshapeWindow(500, 500);            // nastaveni velikosti okna
    glutDisplayFunc(onDisplay);             // registrace funkce volane pri prekreslovani okna
    glutReshapeFunc(onResize);              // registrace funkce volane pri zmene velikosti okna
    glutKeyboardFunc(onKeyboard);           // registrace funkce volane pri stlaceni ASCII klavesy
    glutMouseFunc(onMouseClick);            // registrace funkce volane pri stlaceni nebo pusteni tlacitka mysi
    glutMotionFunc(onMouseDrag);            // registrace funkce volane pri posunu mysi se stlacenym tlacitkem
    glutPassiveMotionFunc(onMouseMove);     // registrace funkce volane pri posunu mysi bez stlaceneho tlacitka
    init();                                 // inicializace vykreslovani
    createMenu();                           // vytvoreni menu s vyberem telesa
    glutMainLoop();                         // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                               // navratova hodnota vracena operacnimu systemu
}



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