//---------------------------------------------------------------------
// Ukazkovy priklad k serii clanku o graficke knihovne OpenGL a GLU
//
// Autor:          Pavel Tisnovsky
// Cislo clanku:   21
// Cislo prikladu: 1
//
// Program otevre jedno hlavni okno a vykresli do nej slozitejsi
// polygon vytvoreny pomoci teselatoru. Pomoci mezerniku je mozne menit
// pravidla pro zjistovani vnejsi a vnitrni casti polygonu.
// Okno (a tim i celou aplikaci) lze ukoncit stiskem klavesy ESC.
//---------------------------------------------------------------------

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>                                // hlavickovy soubor funkci GLUTu
#include <stdlib.h>
#include <stdio.h>

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

#ifndef CALLBACK
#define CALLBACK
#endif

GLdouble currentWinding = GLU_TESS_WINDING_ODD;
GLUtesselator *gluTessObject;



//---------------------------------------------------------------------
// Nastaveni souradneho systemu v zavislosti na velikosti okna
//---------------------------------------------------------------------
void onResize(int w, int h)                         // w a h reprezentuje 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
    glTranslatef(0.0, h, 0.0);
    glScalef(1.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}



//---------------------------------------------------------------------
// Callback funkce volana pri zahajeni vykreslovani graficke primitivy
//---------------------------------------------------------------------
void CALLBACK callbackBegin(GLenum which)
{
    glBegin(which);
    puts("glBegin");
}



//---------------------------------------------------------------------
// Callback funkce volana pri ukonceni vykreslovani graficke primitivy
//---------------------------------------------------------------------
void CALLBACK callbackEnd(void)
{
   glEnd();
   puts("glEnd");
}



//---------------------------------------------------------------------
// Callback funkce volana pri vyskytu chyby
//---------------------------------------------------------------------
void CALLBACK callbackError(GLenum errorCode)
{
   const GLubyte *s=gluErrorString(errorCode);
   fprintf(stderr, "chyba pri teselaci: %s\n", s);
   exit(0);
}



//---------------------------------------------------------------------
// Tato funkce je volana pri vyskytu pruseciku dvou hran polygonu
//---------------------------------------------------------------------
void CALLBACK callbackCombine(GLdouble coords[3],
                              GLdouble *data[4],
                              GLfloat weight[4],
                              GLdouble **dataOut)
{
   GLdouble *vertex;
   vertex = (GLdouble *) malloc(3 * sizeof(GLdouble));

   vertex[0] = coords[0];
   vertex[1] = coords[1];
   vertex[2] = coords[2];
   *dataOut = vertex;
   puts("combine");
}



//---------------------------------------------------------------------
// Tato funkce je volana pri vytvoreni kazdeho vrcholu teselovaneho
// polygonu
//---------------------------------------------------------------------
void CALLBACK callbackVertex(GLdouble coords[3])
{
    printf("glVertex3d(%5.1f, %5.1f, %5.1f)\n", (float)coords[0], (float)coords[1], (float)coords[2]);
    glVertex3dv(coords);
}



//---------------------------------------------------------------------
// Tato funkce je volana pri kazdem prekresleni okna
//---------------------------------------------------------------------
void onDisplay(void)
{
    glClearColor(0.0, 0.0, 0.0, 0.0);               // nastaveni mazaci barvy na cernou
    glClear(GL_COLOR_BUFFER_BIT);                   // vymazani bitovych rovin barvoveho bufferu
    glShadeModel(GL_FLAT);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);      // rezim vykreslovani polygonu
    glEnable(GL_LINE_SMOOTH);

    glColor3f(1.0f, 1.0f, 1.0f);                    // nastaveni barvy pro kresleni
    glBegin(GL_POLYGON);                            // vykresleni konvexniho polygonu
            {
                double d=0.0;
                int i;
                for (i=0; i<6; i++) {
                    glVertex2f(120.0+100.0*sin(d), 125.0-100.0*cos(d));
                    d+=144.0*3.1415/180.0;
                }
            };
    glEnd();

    glColor3f(1.0f, 1.0f, 0.0f);                    // nastaveni barvy pro kresleni
    gluTessObject=gluNewTess();                     // vytvoreni objektu pro teselaci
    gluTessCallback(gluTessObject, GLU_TESS_VERTEX, callbackVertex);
    gluTessCallback(gluTessObject, GLU_TESS_BEGIN, callbackBegin);
    gluTessCallback(gluTessObject, GLU_TESS_END, callbackEnd);
    gluTessCallback(gluTessObject, GLU_TESS_ERROR, callbackError);
    gluTessCallback(gluTessObject, GLU_TESS_COMBINE, callbackCombine);
    gluTessProperty(gluTessObject, GLU_TESS_WINDING_RULE, currentWinding);
    gluTessBeginPolygon(gluTessObject, NULL);        // zahajeni noveho polygonu
        gluTessBeginContour(gluTessObject);          // zahajeni nove kontury
            // specifikace jednotlivych vrcholu kontury
        {
            static GLdouble vertex[6][3];
            double d=0.0;
            int i;
            for (i=0; i<6; i++) {
                vertex[i][0]=330.0+100.0*sin(d);
                vertex[i][1]=125.0-100.0*cos(d);
                vertex[i][2]=0.0;
                d+=144.0*3.1415/180.0;
                gluTessVertex(gluTessObject, vertex[i], vertex[i]);
            }
        }

        gluTessEndContour(gluTessObject);           // konec specifikace kontury
    gluTessEndPolygon(gluTessObject);               // konec specifikace polygonu
    gluDeleteTess(gluTessObject);                   // zruseni objektu teselatoru

    glFlush();                                      // provedeni a vykresleni zmen
}



//---------------------------------------------------------------------
// Tato funkce je volana pri stlaceni ASCII klavesy
//---------------------------------------------------------------------
void onKeyboard(unsigned char key, int x, int y)
{
    if (key==27) exit(0);                           // pokud byla stlacena klavesa ESC, konec programu
    if (key==' ') {
        if (currentWinding == GLU_TESS_WINDING_ODD)
            currentWinding = GLU_TESS_WINDING_NONZERO;
         else if (currentWinding == GLU_TESS_WINDING_NONZERO)
            currentWinding = GLU_TESS_WINDING_POSITIVE;
         else if (currentWinding == GLU_TESS_WINDING_POSITIVE)
            currentWinding = GLU_TESS_WINDING_NEGATIVE;
         else if (currentWinding == GLU_TESS_WINDING_NEGATIVE)
            currentWinding = GLU_TESS_WINDING_ABS_GEQ_TWO;
         else if (currentWinding == GLU_TESS_WINDING_ABS_GEQ_TWO)
            currentWinding = GLU_TESS_WINDING_ODD;
         glutPostRedisplay();
    }
}



//---------------------------------------------------------------------
// Hlavni funkce konzolove aplikace
//---------------------------------------------------------------------
int main(int argc, char **argv)
{
    glutInit(&argc, argv);                          // inicializace knihovny GLUT
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);    // nastaveni jednoho barvoveho bufferu
    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
    glutMainLoop();                                 // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                                       // navratova hodnota vracena operacnimu systemu
}



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