//---------------------------------------------------------------------
// Ukazkovy priklad k serii clanku OpenGL evaluatorech
//
// Autor:          Pavel Tisnovsky
// Cislo clanku:   8
// Cislo prikladu: 3
//
// Po spusteni tohoto prikladu se vykresli Bezierova bilinearni plocha,
// ktera je vypoctena s pouzitim evaluatoru. Vypocet probiha ve funkci
// drawBezierSurfaceUsingEvaluators(). Bezierova plocha se zobrazi
// pomoci plosek vykreslovanych primitivou GL_QUADS.
// Kazda ploska je otexturovana, pricemz textura je pocitana pomoci
// evaluatoru. Zmena souradnic do textury je prubezne menena, cimz se
// dosahuje zajimavych efektu.
// Klavesou ESC je mozne program ukoncit, klavesa F prepina zobrazeni
// na celou obrazovku, klavesou W se zobrazeni prepne zpet do okna.
//---------------------------------------------------------------------



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

#include <stdio.h>
#include <stdlib.h>
#include <math.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_LEFT      30                         // pozice leveho horniho rohu okna na desktopu
#define WINDOW_TOP       30
#define WINDOW_TITLE    "OpenGL evaluatory, priklad cislo 8.3" // titulek okna

#define TEXTURE_WIDTH  64                           // sirka textury zadana v texelech
#define TEXTURE_HEIGHT 64                           // vyska textury zadana v texelech

unsigned char texture[TEXTURE_HEIGHT][TEXTURE_WIDTH][4];// pole pro ulozeni pixmapy textury

typedef struct {                                    // novy datovy typ zapouzdrujici velikost okna
    unsigned int    width;
    unsigned int    height;
} Window;

Window window;

GLfloat ctrlPoints[2][2][3] = {                     // ridici body Bezierovy plochy v 3D prostoru
    { {-3.5,-3.5, 2.0}, { 3.5,-3.5, 2.0} },
    { {-3.5, 3.5, 2.0}, { 3.5, 3.5, 2.0} }
};

GLfloat texturePoints[4][4][2] = {                  // ridici body Bezierovy plochy v prostoru u-v
    { { 0.0, 0.0}, { 0.3, 0.0}, { 0.6, 0.0}, { 1.0, 0.0}},
    { { 0.0, 0.3}, { 0.3, 0.3}, { 0.6, 0.3}, { 1.0, 0.3}},
    { { 0.0, 0.6}, { 0.3, 0.6}, { 0.6, 0.6}, { 1.0, 0.6}},
    { { 0.0, 1.0}, { 0.3, 1.0}, { 0.6, 1.0}, { 1.0, 1.0}}
};

static float angle1=0;                              // uhel zmeny nekterych ridicich bodu
static float angle2=0;                              // Bezierovy plochy
static float angle3=0;
static int   animation=1;                           // priznak ridici animaci



//---------------------------------------------------------------------
// Vytvoreni rastroveho vzorku pro texturu
//---------------------------------------------------------------------
void makeRasterTexture(void)
{
    int i,j,c;                                      // pocitadla smycek
    unsigned char * P;                              // ukazatel na zapisovany subtexel

    for (j=0; j<TEXTURE_HEIGHT; j++) {              // pro vsechny radky pixmapy
        P=texture[j][0];
        for (i=0; i<TEXTURE_WIDTH; i++) {           // pro vsechny texely na radku
            c=((((i&0x08)==0)^((j&0x08))==0)) * 255;
            *P++=(unsigned char)c;                  // zapsat barevne slozky RGB
            *P++=(unsigned char)(i<<2);
            *P++=(unsigned char)(j<<2);
            *P++=(unsigned char)255;                // alfa slozka
        }
    }
}



//---------------------------------------------------------------------
// Tato funkce vykresli Bezierovu plochu pomoci evaluatoru
//---------------------------------------------------------------------
void drawBezierSurfaceUsingEvaluators(GLfloat points[][2][3])
{
#define DELENI 20
    int i,j;

    glEnable(GL_MAP2_VERTEX_3);                     // povoleni 2D evaluatoru
    glEnable(GL_MAP2_TEXTURE_COORD_2);              // mapovani barev pomoci evaluatoru

    // predani ridicich bodu Bezierovy plochy
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 2,
                              0, 1, 6, 2, &points[0][0][0]);

    // predani ridicich bodu pro vypocet textur pomoci evaluatoru
    glMap2f(GL_MAP2_TEXTURE_COORD_2,
                              0, 1, 2, 4,
                              0, 1, 8, 4, &texturePoints[0][0][0]);

    // vykresleni Bezierovy plochy
    glMapGrid2f(DELENI, 0.0f, 1.0f, DELENI, 0.0f, 1.0f);
    glEvalMesh2(GL_FILL, 0, DELENI, 0, DELENI);

    // zakaz aplikace obou typu evaluatoru
    glDisable(GL_MAP2_VERTEX_3);
    glDisable(GL_MAP2_TEXTURE_COORD_2);
}



//---------------------------------------------------------------------
// Funkce pro inicializaci vykreslovani
//---------------------------------------------------------------------
void onInit(void)
{
    // nastaveni vlastnosti framebufferu
    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);
    glDepthFunc(GL_LESS);                           // povoleni a nastaveni testu hloubky fragmentu

    // styl vykreslovani
    glPointSize(2.0f);                              // velikost vykreslovanych ridicich bodu
    glLineWidth(2.0f);                              // sirka vykreslovanych car
    glEnable(GL_POINT_SMOOTH);                      // povoleni antialiasingu bodu
    glEnable(GL_LINE_SMOOTH);                       // povoleni antialiasingu car
    glShadeModel(GL_SMOOTH);                        // nastaveni Gouraudova stinovani

    // prace s texturou
    makeRasterTexture();                            // vytvoreni vzorku v texure
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);          // zpusob ulozeni bytu v texture
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // opakovani textury
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // volba filtru
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,         // nahrani rastrovych dat do textury
                 TEXTURE_WIDTH, TEXTURE_HEIGHT,
                 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);// vylepseni zobrazovani
    glEnable(GL_TEXTURE_2D);                        // povoleni texturovani
}



//---------------------------------------------------------------------
// 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, (GLsizei)w, (GLsizei)h);       // viditelna oblast pres cele okno
    glMatrixMode(GL_PROJECTION);                    // zacatek modifikace projekcni matice
    glLoadIdentity();                               // vymazani projekcni matice (=identita)

    if (w<=h) {                                     // jestlize je okno nastaveno na vysku
        glOrtho(-5.0, 5.0, -5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w, -5.0, 5.0);
    }
    else {                                          // jestlize je okno nastaveno na sirku
        glOrtho(-5.0*(GLfloat)w/(GLfloat)h,5.0*(GLfloat)w/(GLfloat)h, -5.0, 5.0, -5.0, 5.0);
    }
    window.width=w;                                 // zapamatovat si velikost okna
    window.height=h;
    glMatrixMode(GL_MODELVIEW);                     // zacatek modifikace modelove matice
    glLoadIdentity();                               // vymazani modelove matice (=identita)
}



//--------------------------------------------------------------------
// Tato funkce je volana pri kazdem prekresleni okna
//--------------------------------------------------------------------
void onDisplay(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// smazani barvoveho a Z-bufferu

    glMatrixMode(GL_MODELVIEW);                     // bude se menit modelova matice
    glLoadIdentity();                               // vymazat predchozi transformace

    drawBezierSurfaceUsingEvaluators(ctrlPoints);   // vykresleni Bezierovy plochy pomoci evaluatoru

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



//---------------------------------------------------------------------
// Tato funkce je volana pri volnem casovem slotu
//---------------------------------------------------------------------
void onIdle(void)
{
    // amplituda zmen
#define A 0.5
    if (animation) {
        angle1+=0.07f;
        angle2+=0.31f;
        angle3+=0.23f;
        texturePoints[1][1][0]=0.3+A*cos(angle1);   // zmena nekterych ridicich bodu
        texturePoints[1][1][1]=0.3+A*sin(angle1);   // plochy v prostoru uv
        texturePoints[1][2][0]=0.3+A*cos(angle2);
        texturePoints[1][2][1]=0.3+A*sin(angle2);
        texturePoints[2][1][0]=0.3+A*cos(angle3);
        texturePoints[2][1][1]=0.3+A*sin(angle3);
        texturePoints[2][2][0]=0.3+A*cos(angle1);
        texturePoints[2][2][1]=0.3+A*sin(angle3);
        glutPostRedisplay();                        // prekresleni sceny
    }
}



//---------------------------------------------------------------------
// Tato funkce je volana pri stlaceni ASCII klavesy
//---------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par                               // aby Borlandi prekladace nehlasily
#endif                                              // warningy ze argumenty nejsou pouzity
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':   glutFullScreen();   break;      // full screen
        case 'w':   glutReshapeWindow(WINDOW_WIDTH, WINDOW_HEIGHT);// prepnuti zpet do okna
                    glutPositionWindow(WINDOW_LEFT, WINDOW_TOP);
                                        break;
        case 's':   animation=!animation;break;     // zapnuti/vypnuti animace
        default:                        break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



//---------------------------------------------------------------------
// 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(WINDOW_LEFT, WINDOW_TOP);// 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
    glutIdleFunc(onIdle);                           // registrace funkce volane pri volnem casovem slotu
    onInit();                                       // inicializace vykreslovani
    glutMainLoop();                                 // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                                       // navratova hodnota vracena operacnimu systemu
}



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