//---------------------------------------------------------------------
// Ukazkovy priklad k serii clanku OpenGL a GLU
//
// Autor:          Pavel Tisnovsky
// Cislo clanku:   3
// Cislo prikladu: 3
//
// Tento program po svem spusteni zobrazi 3D teleso nastavenou kamerou.
// Pomoci mysi a klavesnice je mozne ovlivnit vsechny parametry kamery,
// tj. parametry funkce gluLookAt().
// Se stlacenou klavesou SHIFT se modifikuje vektor "center", bez teto
// klavesy se modifikuje vektor "eye".
// Klavesou ESC je mozne program ukoncit.
//---------------------------------------------------------------------



#ifdef __BORLANDC__
#include <windows.h>                                // oprava chyby v nekterych Borlandskych prekladacich
#endif

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

#ifdef __BORLANDC__
#pragma hdrstop                                     // konec predkompilovanych hlavicek pro Borlandske prekladace
#endif

#define WINDOW_WIDTH    450                         // velikost okna
#define WINDOW_HEIGHT   450
#define WINDOW_TITLE    "OpenGL a GLU, priklad 3.3" // titulek okna



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   eyeX=10, eyeY=10, eyeZ=10;                    // pozice kamery
int   centerX=0, centerY=0, centerZ=0;              // bod, na ktery se kamera diva
int   upX=0, upY=0, upZ=1;                          // vektor smerujici nahoru ke "stropu"

int   newX=0, newY=0, newZ=0;                       // soucasna pozice, ze ktere se pocitaji dalsi parametry
int   oldX=0, oldY=0, oldZ=0;                       // body, ve kterych se nachazi kurzor mysi
int   mouseStatus=0;                                // stav tlacitek mysi
int   modify=0;                                     // ktery vektor se ma modifikovat

float fieldOfView=45.0;                             // zorny uhel - field of view
float nearClippingPlane=0.1;                        // blizsi orezavaci rovina
float farClippingPlane=200.0;                       // vzdalenejsi orezavaci rovina



//---------------------------------------------------------------------
// Funkce pro inicializaci vykreslovani
//---------------------------------------------------------------------
void onInit(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);           // barva pozadi obrazku
    glClearDepth(1.0f);                             // implicitni hloubka ulozena v pameti hloubky
    glShadeModel(GL_SMOOTH);                        // nastaveni stinovaciho rezimu
    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
    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)                         // 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)
    gluPerspective(fieldOfView, (double)w/(double)h, nearClippingPlane, farClippingPlane);
}



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

    // nastaveni pozice a orientace kamery
    if (modify) {
        gluLookAt(eyeX, eyeY, eyeZ, centerX+newX, centerY+newY, centerZ+newZ, upX, upY, upZ);
        // lze menit take vektor "up"
        //gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX+newX, upY+newY, upZ+newZ);
    }
    else {
        gluLookAt(eyeX+newX, eyeY+newY, eyeZ+newZ, centerX, centerY, centerZ, upX, upY, upZ);
    }
    glutSolidTeapot(5);                             // vykresleni telesa
    glFlush();                                      // provedeni a vykresleni vsech zmen
    glutSwapBuffers();
}



//---------------------------------------------------------------------
// Tato funkce je volana pri stlaceni ASCII klavesy
//---------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par                               // zabranit warningum pri prekladu
#endif                                              // u borlandskych prekladacu
void onKeyboard(unsigned char key, int x, int y)
{
    if (key>='A' && key<='Z')                       // uprava velkych pismen na mala
        key+=(unsigned char)('a'-'A');              // pro zjednoduseni prikazu switch

    switch (key) {
        case 27:    exit(0);            break;      // ukonceni aplikace
        case 'q':   exit(0);            break;      // ukonceni aplikace
        default:                        break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



//---------------------------------------------------------------------
// Tato funkce je volana pri zmene stavu tlacitek mysi
//---------------------------------------------------------------------
void onMouseClick(int button, int state, int x, int y)
{
    if (glutGetModifiers()==GLUT_ACTIVE_SHIFT) {    // zjistit stav klavesy SHIFT
        modify=1;
    }
    else {
        modify=0;
    }
    if (button==GLUT_LEFT_BUTTON) {                 // leve tlacitko mysi
        if (state==GLUT_DOWN) {                     // pri stlaceni
            mouseStatus=1;
            oldX=x;
            oldY=y;
        }
        else {                                      // pri pusteni tlacitka
            mouseStatus=0;
            oldX=x;
            oldY=y;
        }
    }
    if (button==GLUT_RIGHT_BUTTON) {                // prave tlacitko mysi
        if (state==GLUT_DOWN) {                     // pri stlaceni
            mouseStatus=2;
            oldZ=y;
        }
        else {                                      // pri pusteni tlacitka
            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) {                   // 
        newX=x-oldX;
        newY=y-oldY;
        glutPostRedisplay();
    }
    if (mouseStatus==2) {
        newZ=y-oldZ;
        glutPostRedisplay();
    }
}



//---------------------------------------------------------------------
// 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(WINDOW_WIDTH, WINDOW_HEIGHT);// pocatecni velikost okna
    glutCreateWindow(WINDOW_TITLE);                 // 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(onMouseClick);                    // registrace funkce volane pri stlaceni nebo pusteni tlacitka mysi
    glutMotionFunc(onMouseDrag);                    // registrace funkce volane pri posunu 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
//---------------------------------------------------------------------