//---------------------------------------------------------------------
// Ukazkovy priklad cislo 59
// Autor: Pavel Tisnovsky
//
// Program, ktery zobrazi ctverec s nanesenou texturou. Pomoci leveho
// tlacitka mysi lze menit souradnice s,t v jednotlivych vrcholech
// ctverce v rozmezi 0.0-2.0. Je nastaveno opakovani textury.
//---------------------------------------------------------------------

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

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

#define CTRL_MINX       300                     // mezni hodnoty pro ridici body na obrazovce
#define CTRL_MAXX       500
#define CTRL_MINY       100
#define CTRL_MAXY       300
#define DELTA_X         (CTRL_MAXX-CTRL_MINX)   // sirka ctverce s ridicimi body
#define DELTA_Y         (CTRL_MAXY-CTRL_MINY)   // vyska ctverce s ridicimi body



unsigned int  points[4][2]={                    // souradnice s,t v texture
    {CTRL_MINX, CTRL_MINY},                     // prevedene na souradnice v okne
    {CTRL_MINX+DELTA_X/2, CTRL_MINY},
    {CTRL_MINX+DELTA_X/2, CTRL_MINY+DELTA_Y/2},
    {CTRL_MINX, CTRL_MINY+DELTA_Y/2},
};

unsigned char texture[TEXTURE_HEIGHT][TEXTURE_WIDTH][4];// pole pro ulozeni pixmapy textury
int           activePoint=-1;                   // vybrany ridici bod
int           height;                           // vyska okna



//---------------------------------------------------------------------
// 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
        }
    }
}



//---------------------------------------------------------------------
// Vycisleni souradnice 's' nebo 't' zadaneho vrcholu
//---------------------------------------------------------------------
float getTexCoord(int vertex, int st)
{
    if (!st) return (float)(points[vertex][0]-CTRL_MINX)/DELTA_X*2.0;
    else     return (float)(points[vertex][1]-CTRL_MINY)/DELTA_Y*2.0;
}



//---------------------------------------------------------------------
// 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 souradnic v texture
//---------------------------------------------------------------------
void drawInformations(void)
{
    char str[100];
    int  i;
    glDisable(GL_TEXTURE_2D);
    glColor3f(1.0f, 1.0f, 1.0f);
    for (i=0; i<4; i++) {                       // vsechny ctyri vrcholy
        sprintf(str, "tex.coord %d: [%4.2f, %4.2f]", i, getTexCoord(i, 0), getTexCoord(i, 1));
        printString(10, 75-i*18, str);
    }
}



//---------------------------------------------------------------------
// Funkce pro vykresleni otexturovaneho ctverce
//---------------------------------------------------------------------
void drawSquare(void)
{
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
        glTexCoord2f(getTexCoord(0, 0), getTexCoord(0, 1));
        glVertex2i( 50, 100);
        glTexCoord2f(getTexCoord(1, 0), getTexCoord(1, 1));
        glVertex2i(250, 100);
        glTexCoord2f(getTexCoord(2, 0), getTexCoord(2, 1));
        glVertex2i(250, 300);
        glTexCoord2f(getTexCoord(3, 0), getTexCoord(3, 1));
        glVertex2i( 50, 300);
    glEnd();
}



//---------------------------------------------------------------------
// Funkce pro vykresleni mezi ridicich bodu
//---------------------------------------------------------------------
void drawControlField(void)
{
    glDisable(GL_TEXTURE_2D);
    glColor3f(0.8f, 0.8f, 0.8f);
    glBegin(GL_LINE_LOOP);
        glVertex2i(CTRL_MINX-5, CTRL_MINY-5);
        glVertex2i(CTRL_MAXX+5, CTRL_MINY-5);
        glVertex2i(CTRL_MAXX+5, CTRL_MAXY+5);
        glVertex2i(CTRL_MINX-5, CTRL_MAXY+5);
    glEnd();
}



//---------------------------------------------------------------------
// Funkce pro vykresleni ridicich bodu
//---------------------------------------------------------------------
void drawControlPoints(void)
{
    int i;
    glDisable(GL_TEXTURE_2D);
    glColor3f(0.7f, 0.7f, 1.0f);
    glBegin(GL_LINE_LOOP);
        for (i=0; i<=3; i++)
            glVertex2i(points[i][0], points[i][1]);
    glEnd();
    glPointSize(5.0f);
    glBegin(GL_POINTS);
        for (i=0; i<=3; i++)
            glVertex2i(points[i][0], points[i][1]);
    glEnd();
}



//---------------------------------------------------------------------
// Funkce pro vykresleni aktivniho ridiciho bodu
//---------------------------------------------------------------------
void drawActivePoint(void)
{
    if (activePoint==-1) return;
    glDisable(GL_TEXTURE_2D);
    glColor3f(1.0f, 0.7f, 0.7f);
    glPointSize(8.0f);
    glBegin(GL_POINTS);
        glVertex2i(points[activePoint][0], points[activePoint][1]);
    glEnd();
}



//---------------------------------------------------------------------
// Funkce pro inicializaci vykreslovani
//---------------------------------------------------------------------
void onInit(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       // barva pozadi obrazku
    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
    glDisable(GL_CULL_FACE);                    // zadne hrany ani steny se nebudou odstranovat

    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);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    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_POINT_SMOOTH);                  // povoleni antialiasingu bodu
    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, w, h);                     // viditelna oblast pres cele okno
    glMatrixMode(GL_PROJECTION);                // zacatek modifikace projekcni matice
    glLoadIdentity();                           // vymazani projekcni matice (=identita)
    glOrtho(0, w, 0, h, -1, 1);                 // mapovani abstraktnich souradnic do souradnic okna
    height=h;
}



//--------------------------------------------------------------------
// Tato funkce je volana pri kazdem prekresleni okna
//--------------------------------------------------------------------
void onDisplay(void)
{
    glClear(GL_COLOR_BUFFER_BIT);               // vymazani barvoveho bufferu i pameti hloubky
    drawSquare();                               // vykresleni otexturovaneho ctverce
    drawControlField();                         // vykresleni mezi ridicich bodu
    drawControlPoints();                        // vykresleni ridicich bodu
    drawActivePoint();                          // vykresleni aktivniho ridiciho bodu
    drawInformations();                         // vykresleni souradnic v texture
    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
        default:                        break;
    }
}



//---------------------------------------------------------------------
// Tato funkce je volana pri stisku ci pusteni tlacitka mysi
//---------------------------------------------------------------------
void onMouseButton(int button, int state, int x, int y)
{
    int i;
    if (button==GLUT_LEFT_BUTTON) {             // pri zmene stavu leveho tlacitka
        if (state==GLUT_DOWN) {                 // pri stlaceni tlacitka
            activePoint=-1;
            for (i=0; i<=3; i++) {              // najit ridici bod pod kurzorem
                if ((abs(x-points[i][0])<11)
                 && (abs(height-y-points[i][1]<9))) {
                    activePoint=i;              // bod jsme nasli
                    glutPostRedisplay();
                    return;
                }
            }
        }
        else {                                  // pri pusteni tlacitka mysi
            activePoint=-1;                     // zrusit aktivni bod
            glutPostRedisplay();
        }
    }
}



//---------------------------------------------------------------------
// Tato funkce je volana pri pohybu mysi se stlacenym tlacitkem.
//---------------------------------------------------------------------
void onMouseMotion(int x, int y)
{
    if (activePoint==-1) return;                // neni vybran zadny ridici bod
    y=height-y;
    if (x>=CTRL_MINX && x<=CTRL_MAXX)           // kurzor mysi je v zadanem ctverci
        points[activePoint][0]=x;               // posunout ridici bod
    if (y>=CTRL_MINY && y<=CTRL_MAXY)
        points[activePoint][1]=y;
    glutPostRedisplay();
}



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