//---------------------------------------------------------------------
// Ukazkovy priklad k serii clanku OpenGL a GLU
//
// Autor:          Pavel Tisnovsky
// Cislo clanku:   12
// Cislo prikladu: 1
//
// Vykresleni NURB krivky stupne 1. Po vykresleni krivky je mozne menit
// pomoci mysi polohu jednotlivych ridicich bodu a tak menit tvar krivky.
// Pri vyskytu chyby se zavola registrovana callback funkce.
// Pomoci klavesy 'f' lze provest prepnuti do celeho okna, klavesou 'w'
// se provede nastaveni puvodni velikosti okna, tj. 450x450 pixelu.
// Klavesou ESC je mozne program ukoncit.
//---------------------------------------------------------------------

#include <windows.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

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

#define POINTS      9
#define KNOT_COUNT  11
#define ORDER       2

// ridici body
GLfloat ctlpoints[][3]={
    { 25,  50, 0},
    { 75, 350, 0},
    {125,  50, 0},
    {175, 350, 0},
    {225,  50, 0},
    {275, 350, 0},
    {325,  50, 0},
    {375, 350, 0},
    {415,  50, 0}
};

// uzlovy vektor
GLfloat knots[] =  {0.0, 0.0,
                    0.2, 0.3, 0.4, 0.5, 0.7, 0.8, 0.9,
                    1.0, 1.0,
};

GLUnurbs *nurbs;                                    // objekt NURB krivky

int mouseState=0;                                   // stav tlacitek mysi
int selected=0;                                     // vybrany ridici bod



//---------------------------------------------------------------------
// Tato funkce vykresli retezec zadanym bitmapovym fontem
//---------------------------------------------------------------------
void printGlutBitmapFont(char *string, void *font, int x, int y, float r, float g, float b)
{
    glColor3f(r, g, b);                             // nastaveni barvy vykreslovanych bitmap
    glRasterPos2i(x, y);                            // nastaveni pozice pocatku bitmapy
    while (*string)                                 // projit celym retezcem
        glutBitmapCharacter(font, *string++);       // vykresleni jednoho znaku
}



//---------------------------------------------------------------------
// Callback funkce volana pri vyskytu chyby pri renderovani NURB krivek
//---------------------------------------------------------------------
void __stdcall onError(int errorCode)
{
    printf("Error: %d\nError string: %s\n\n", errorCode, gluErrorString(errorCode));
}



//---------------------------------------------------------------------
// Funkce pro inicializaci vykreslovani
//---------------------------------------------------------------------
void onInit(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_POINT_SMOOTH);
    glPointSize(5.0f);
    nurbs=gluNewNurbsRenderer();                    // vytvoreni NURBS
    gluNurbsCallback(nurbs, GLU_ERROR, onError);    // registrace callback funkce
}



//---------------------------------------------------------------------
// Tato funkce je volana pri kazdem prekresleni okna
//---------------------------------------------------------------------
void onDisplay(void)
{
    int d;

    glClear(GL_COLOR_BUFFER_BIT);                   // smazani barvoveho bufferu

    // propojit ridici body polycarou
    glColor3f(0.6f, 0.6f, 0.6f);                    // barva polycary
    glBegin(GL_LINE_STRIP);                         // projit vsemi ridicimi body
    for (d=0; d<POINTS; d++)
        glVertex2f(ctlpoints[d][0], ctlpoints[d][1]);
    glEnd();

    // vykreslit ridici body
    glColor3f(0.5f, 0.5f, 1.0f);                    // barva ridicich bodu
    glBegin(GL_POINTS);                             // projit vsemi ridicimi body
    for (d=0; d<POINTS; d++)
        glVertex2f(ctlpoints[d][0], ctlpoints[d][1]);
    glEnd();

    // vykreslit krivku
    glColor3f(1.0f, 0.0f, 0.0f);
    gluBeginCurve(nurbs);                           // zacatek specifikace NURBS
    gluNurbsCurve(nurbs,
        KNOT_COUNT, knots,                          // pocet slozek v uzlovem vektoru
        3,                                          // pocet floatu pro jeden ridici bod
        &ctlpoints[0][0],                           // ridici body
        ORDER,                                      // stupen krivky
        GL_MAP1_VERTEX_3                            // funkce ridicich bodu
    );
    gluEndCurve(nurbs);                             // konec specifikace NURBS

    // vypsat cisla jednotlivych ridicich bodu
    for (d=0; d<POINTS; d++) {                      // projit vsemi ridicimi body
        char str[10];
        sprintf(str, "%d", d);
        // stin znaku
        printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, ctlpoints[d][0]+1, ctlpoints[d][1]+16, 0.0f, 0.0f, 0.0f);
        // svetly znak
        printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, ctlpoints[d][0], ctlpoints[d][1]+15, 1.0f, 1.0f, 1.0f);
    }

    glFlush();
    glutSwapBuffers();
}



//---------------------------------------------------------------------
// Nastaveni souradneho systemu v zavislosti na velikosti okna
//---------------------------------------------------------------------
void onResize(GLsizei w, GLsizei h)
{
    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
    glScalef(1, -1, 1);                             // inverze y-ove osy, aby se y zvetsovalo smerem dolu
    glTranslatef(0, -h, 0);                         // posun pocatku do leveho horniho rohu
}



//---------------------------------------------------------------------
// 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
        case 'f':   glutFullScreen();   break;      // prepnuti na celou obrazovku
        case 'w':   glutReshapeWindow(WINDOW_WIDTH, WINDOW_HEIGHT); break;
        default:                        break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



//---------------------------------------------------------------------
// Callback funkce volana pri stisku ci pusteni tlacitka mysi
//---------------------------------------------------------------------
void onMouse(int button, int state, int x, int y)
{
    mouseState=0;                                   // konec presunu ridicich bodu
    if (state==GLUT_DOWN) {                         // pokud je tlacitko stlaceno
        int i;
        for (i=0; i<POINTS; i++) {                  // najit ridici bod v miste kurzoru mysi
            if (abs(x-ctlpoints[i][0])<10 &&
                abs(y-ctlpoints[i][1])<10) {
                    mouseState=1;                   // bod byl nalezen
                    selected=i;                     // zpamatovat si jeho cislo
                    glutPostRedisplay();
                    return;
                }
        }
    }
    glutPostRedisplay();
}



//---------------------------------------------------------------------
// Callback funkce volana pri pohybu mysi
//---------------------------------------------------------------------
void onMouseMotion(int x, int y)
{
    if (mouseState) {                               // pokud je nejake tlacitko stlaceno
        ctlpoints[selected][0]=x;                   // posun vybraneho ridiciho bodu
        ctlpoints[selected][1]=y;
        glutPostRedisplay();                        // a prekresleni obrazovky
    }
}



//---------------------------------------------------------------------
// Callback funkce volana pri pasivnim pohybu mysi
//---------------------------------------------------------------------
void onMousePassiveMotion(int x, int y)
{
    int i;
    for (i=0; i<POINTS; i++) {                      // najit ridici bod v miste kurzoru mysi
        if (abs(x-ctlpoints[i][0])<5 &&             // pokud byl bod nalezen, zmenit tvar
            abs(y-ctlpoints[i][1])<5) {             // kurzoru mysi
                glutSetCursor(GLUT_CURSOR_CROSSHAIR);
                return;
            }
    }
    glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
}



//---------------------------------------------------------------------
// 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(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(onMouse);                         // registrace funkce volane pri stisku tlacitka mysi
    glutMotionFunc(onMouseMotion);                  // registrace funkce volane pri pohybu mysi
    glutPassiveMotionFunc(onMousePassiveMotion);    // registrace funkce volane pri pasivnim pohybu mysi
    onInit();                                       // inicializace vykreslovani
    glutMainLoop();                                 // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                                       // navratova hodnota vracena operacnimu systemu
}



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