Základy OpenGL

Základy OpenGL

main.cpp
///////////////////////////////////////////////////////////////////////////////
// Zaklady pocitacove grafiky
// Cviceni #7
//
// Autor: Adam Herout, modified by Michal Spanel
//
// Obsah cviceni: "Informativni nejzakladnejsi zaklady OpenGL"
//
// Demonstrovane jevy:
//      - OpenGL + GLUT
//      - vykreslovani
//      - display-listy
//      - barvy
//      - osvetleni
//      - pruhlednost
//      - stencil buffer
//
// *) Ovladani programu:
//      mys + leve tl.  - rotace cele sceny
//      mys + prave tl. - priblizeni sceny
//      1,2,3           - polygony vyplnit, carami, body
//
// Opravy:
//
///////////////////////////////////////////////////////////////////////////////
 
 
///////////////////////////////////////////////////////////////////////////////
// include
 
#ifdef _WIN32
#  include <windows.h>
#endif
 
#include <GL/gl.h>
#include <GL/glut.h>
//#include <GL/glext.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
 
 
///////////////////////////////////////////////////////////////////////////////
// globalni konstanty
 
// titulek hlavniho okna
const char      * PROGRAM_TITLE = "IZG Labs #07, Informativni nejzakladnejsi zaklady OpenGL";
 
const float     fov = 45.0;                 // zorny uhel - field of view
const float     near_plane = 10.0;          // blizsi orezavaci rovina
const float     far_plane = 250.0;          // zdalenejsi orezavaci rovina
 
const int       default_w = 600;            // defaultni velikost okna
const int       default_h = 600;
 
 
///////////////////////////////////////////////////////////////////////////////
// globalni promenne
 
int             scene_rot_x = 0;             // rotace sceny dana mysovanim
int             scene_rot_y = 0;
int             scene_move_z = -50;          // posuv sceny
int             time = 0;                    // virtualni cas [s]
 
 
///////////////////////////////////////////////////////////////////////////////
// zakladni nastaveni OpenGL
// - funkce se vola jednou, pri zacatku programu
//
 
void Init(void)
{
  // nastaveni clear color
  glClearColor(0.0, 0.0, 0.0, 0.0);
 
  // parametry svetla
  GLfloat light_ambient[]  = {0.5, 0.5, 0.5, 1.0};  // ambientni slozka svetla
  GLfloat light_specular[] = {0.8, 0.8, 0.8, 1.0};  // barva odlesku
  GLfloat light_diffuse[]  = {0.9, 0.9, 0.9, 1.0};  // difuzni slozka svetla
  GLfloat light_position[] = {15.0, 0.0, 0.0, 1.0}; // definice pozice svetla
 
  // nastaveni svetla
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
 
  // nastaveni osvetlovaciho modelu
//  glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);
 
  // povoleni z-bufferu 
  glClearDepth(1000.0);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LESS);
 
  // velikost bodu, pokud se nejaky kresli...
  glPointSize(5.0);
}
 
 
///////////////////////////////////////////////////////////////////////////////
// vygenerovani display-listu ozubeneho kolecka
// - funkce se vola pri zacatku programu
// - vraci id vytvoreneho display-listu
 
// zakladni parametry kolecka
#define COG_COUNT       14                      // pocet zubu kola
#define RADIUS          4.0                     // radius kola (vnitrni radius zubu)
 
// dalsi parametry kolecka
#define COG_HEIGHT      1.0                     // vyska zubu
#define RADIUS_AXLE     1.0                     // radius diry pro osu v kole
#define WHEEL_THICKNESS 3.0                     // tloustka kola
#define CONST_PI        3.141592653589793
 
// identifikator display-listu kolecka
int wheel;
int wheel2;
 
int InitWheelDisplayList(double radius, int cog_count)
{
  double phi = 0;
 
  // vygeneruje identifikator display-listu
  int id = glGenLists(1);
 
  // naplneni display-listu s koleckem 
  glNewList(id, GL_COMPILE);
  {
    glBegin(GL_QUADS);                          // vykreslujem same ctyruhelniky
    for (int i = 0; i < cog_count; i++) {       // pro kazdy zub...
      glNormal3f(0, 0, -1);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi), (radius+COG_HEIGHT)*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi + 2/7.0*2*CONST_PI/cog_count),
                 (radius+COG_HEIGHT)*sin(phi + 2/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 2/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 2/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
 
      glNormal3f(cos(phi + 2/7.0*2*CONST_PI/cog_count/2), sin(phi + 2/7.0*2*CONST_PI/cog_count/2), 0.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi), (radius+COG_HEIGHT)*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi + 2/7.0*2*CONST_PI/cog_count),
                 (radius+COG_HEIGHT)*sin(phi + 2/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0
                 );
      glVertex3f((radius+COG_HEIGHT)*cos(phi + 2/7.0*2*CONST_PI/cog_count),
                 (radius+COG_HEIGHT)*sin(phi + 2/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi), (radius+COG_HEIGHT)*sin(phi), WHEEL_THICKNESS/2.0);
 
      glNormal3f(0, 0, 1);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi), (radius+COG_HEIGHT)*sin(phi), WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi + 2/7.0*2*CONST_PI/cog_count),
                 (radius+COG_HEIGHT)*sin(phi + 2/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 2/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 2/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
 
      glNormal3f(-cos(phi + 2/7.0*2*CONST_PI/cog_count/2), -sin(phi + 2/7.0*2*CONST_PI/cog_count/2), 0.0);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 2/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 2/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 2/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 2/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), WHEEL_THICKNESS/2.0);
      phi += 2/7.0*2*CONST_PI/cog_count;
 
 
      glNormal3f(0, 0, -1);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi), (radius+COG_HEIGHT)*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 radius*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
 
      glNormal3f(cos(phi + 1/7.0*2*CONST_PI/cog_count/2), sin(phi + 1/7.0*2*CONST_PI/cog_count/2), 0.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi), (radius+COG_HEIGHT)*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 radius*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 radius*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi), (radius+COG_HEIGHT)*sin(phi), WHEEL_THICKNESS/2.0);
 
      glNormal3f(0, 0, 1);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi), (radius+COG_HEIGHT)*sin(phi), WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 radius*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
 
      glNormal3f(-cos(phi + 1/7.0*2*CONST_PI/cog_count/2), -sin(phi + 1/7.0*2*CONST_PI/cog_count/2), 0.0);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), WHEEL_THICKNESS/2.0);
      phi += 1/7.0*2*CONST_PI/cog_count;
 
 
      glNormal3f(0, 0, -1);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi), radius*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi + 3/7.0*2*CONST_PI/cog_count),
                 radius*sin(phi + 3/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 3/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 3/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
 
      glNormal3f(cos(phi + 3/7.0*2*CONST_PI/cog_count/2), sin(phi + 3/7.0*2*CONST_PI/cog_count/2), 0.0);
      glVertex3f(radius*cos(phi), radius*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi + 3/7.0*2*CONST_PI/cog_count),
                 radius*sin(phi + 3/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi + 3/7.0*2*CONST_PI/cog_count),
                 radius*sin(phi + 3/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi), radius*sin(phi), WHEEL_THICKNESS/2.0);
 
      glNormal3f(0, 0, 1);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi), radius*sin(phi), WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi + 3/7.0*2*CONST_PI/cog_count),
                 radius*sin(phi + 3/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 3/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 3/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
 
      glNormal3f(-cos(phi + 3/7.0*2*CONST_PI/cog_count/2), -sin(phi + 3/7.0*2*CONST_PI/cog_count/2), 0.0);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 3/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 3/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 3/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 3/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), WHEEL_THICKNESS/2.0);
      phi += 3/7.0*2*CONST_PI/cog_count;
 
 
      glNormal3f(0, 0, -1);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi), radius*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 (radius+COG_HEIGHT)*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
 
      glNormal3f(cos(phi + 1/7.0*2*CONST_PI/cog_count/2), sin(phi + 1/7.0*2*CONST_PI/cog_count/2), 0.0);
      glVertex3f(radius*cos(phi), radius*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 (radius+COG_HEIGHT)*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 (radius+COG_HEIGHT)*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi), radius*sin(phi), WHEEL_THICKNESS/2.0);
 
      glNormal3f(0, 0, 1);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), WHEEL_THICKNESS/2.0);
      glVertex3f(radius*cos(phi), radius*sin(phi), WHEEL_THICKNESS/2.0);
      glVertex3f((radius+COG_HEIGHT)*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 (radius+COG_HEIGHT)*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
 
      glNormal3f(-cos(phi + 1/7.0*2*CONST_PI/cog_count/2), -sin(phi + 1/7.0*2*CONST_PI/cog_count/2), 0.0);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 -WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi + 1/7.0*2*CONST_PI/cog_count),
                 RADIUS_AXLE*sin(phi + 1/7.0*2*CONST_PI/cog_count),
                 WHEEL_THICKNESS/2.0);
      glVertex3f(RADIUS_AXLE*cos(phi), RADIUS_AXLE*sin(phi), WHEEL_THICKNESS/2.0);
      phi += 1/7.0*2*CONST_PI/cog_count;
    }                                           // konec cyklu pro kazdy zub
    glEnd();                                    // GL_QUADS
  }
  glEndList();                                  // to je ke kolecku vsechno...
 
  // vratime identifikator display-listu
  return id;
}
 
 
///////////////////////////////////////////////////////////////////////////////
// vygenerovani display-listu pro dva materialy
// - funkce se vola jednou, pri zacatku programu
 
// identifikatory display-listu pro materialy
int red_mat, blue_mat;
 
void InitMaterialDisplayLists()
{
  // vygenerujeme display listy pro dva dulezite materialy
  red_mat = glGenLists(1);                      // cerveny material - display-list
  glNewList(red_mat, GL_COMPILE);               // budeme plnit prislusny display-list
  {
    GLfloat mat_diffuse[] = {0.9, 0.0, 0.0, 1.0};  // difuzni (matna) slozka materialu
    GLfloat mat_ambient[] = {0.2, 0.2, 0.2, 1.0};  // ambient (rozptylena) slozka materialu
    GLfloat mat_specular[] = {0.5, 0.5, 0.5, 1.0}; // spekularni (odleskova) slozka materialu
 
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 80); // lesklost materialu - 1-128
  }
  glEndList();                                  // konec cerveneho materialu
 
  // vygenerujeme display-list pro modry material
  blue_mat = glGenLists(1);
  glNewList(blue_mat, GL_COMPILE);
  {
    GLfloat mat_diffuse[] = {0.0, 0.0, 0.9, 1.0};  // difuzni slozka materialu
    GLfloat mat_ambient[] = {0.2, 0.2, 0.2, 1.0};  // ambient (rozptylena) slozka materialu
    GLfloat mat_specular[] = {0.5, 0.5, 0.5, 1.0}; // spekularni slozka materialu
 
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); 
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 120);
  }
  glEndList();                                  // konec popisu modreho materialu
}
 
 
///////////////////////////////////////////////////////////////////////////////
// vygenerovani textury - bitmapy RGBA o vhodnych rozmerech
// - funkce se vola jednou, pri zacatku programu
 
// parametry textury
#define TEXTURE_WIDTH   256                     // sirka textury - musi byt mocnina 2
#define TEXTURE_HEIGHT  256                     // vyska textury - musi byt mocnina 2
 
// pole kde budou data textury
unsigned char texture[TEXTURE_WIDTH * TEXTURE_HEIGHT * 4];
 
// identifikator ("jmeno") textury
unsigned texid;
 
void InitTexture()
{
  unsigned char *p = texture;                   // ukazatel, ktery "leze" pres texturu
  for (int i = 0; i < TEXTURE_HEIGHT; ++i) {
    for (int j = 0; j < TEXTURE_WIDTH; ++j) {
      *p++ = (((int)(i+10*(sin(j/20.0)+1))&0x10)^(j&0x10)) ? 0xff:160; // red
      *p++ = (((int)(i+10*(sin(j/20.0)+1))&0x10)^(j&0x10)) ? 0xff:180; // green
      *p++ = (((int)(i+10*(sin(j/20.0)+1))&0x10)^(j&0x10)) ? 0xff:180; // blue
      *p++ = (((int)(i+10*(sin(j/20.0)+1))&0x10)^(j&0x10)) ? 0xff:160; // alpha
    }
  }
 
  // vygeneruje (prideli) identifikator textury - ulozi do promenne texid
  glGenTextures(0, &texid);
 
  // texid bude aktivni textura
  glBindTexture(GL_TEXTURE_2D, texid);
 
  // nastaveni parametru textury
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 
  // vytvoreni textury - podle naseho vygenerovaneho pole v promenne texture
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXTURE_WIDTH, TEXTURE_HEIGHT, 
               0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
}
 
 
///////////////////////////////////////////////////////////////////////////////
// nastaveni souradneho systemu podle velikosti okna
// - funkci zavola GLUT vzdy, kdyz se zmeni rozmery okna
 
void onReshape(int w, int h)
{
  glViewport(0, 0, w, h);                       // viditelna oblast
  glMatrixMode(GL_PROJECTION);                  // nastavujeme projekcni matici
  glLoadIdentity();                             // nahrat jednotkovou matici
  gluPerspective(fov, (double)w / (double)h, near_plane, far_plane); // perspektivni projekce
  glMatrixMode(GL_MODELVIEW);                   // modelova matice
  glLoadIdentity();
}
 
 
///////////////////////////////////////////////////////////////////////////////
// tato funkce je volana pro prekresleni okna
// - vola ji GLUT, kdyz okno vystoupi do popredi, nebo kdyz si to
//   objedname funkci glutPostRedisplay()
 
void onDisplay(void)
{
  // vymazani bufferu
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
  glMatrixMode(GL_MODELVIEW);                   // bude se menit modelova matice
  glLoadIdentity();                             // nahrat jednotkovou matici
 
  ////////////////////////////////////////////////////////////////
  // nastaveni kamery
 
  glTranslatef(0.0f, 0.0f, scene_move_z);       // posun sceny podle pohybu mysi
  glRotatef(scene_rot_x, 1.0, 0.0, 0.0);        // rotace sceny podle pohybu kurzoru mysi
  glRotatef(scene_rot_y, 0.0, 1.0, 0.0);
 
  ////////////////////////////////////////////////////////////////
  // vykresleni sceny
 
  // kolecka vpredu
#if 1
  glPushMatrix();                               // zapamatovat souradny system
    glTranslatef(0.0, 0.0, 6.0);
 
    glCallList(red_mat);                        // cerveny material - display-list
    glPushMatrix();                             // zapamatujem souradny system
      glCallList(wheel);                        // vykreslime kolecko
    glPopMatrix();                              // vratime souradny system
 
    glCallList(blue_mat);
    glPushMatrix();
      glRotatef(time, 0, 0, 1);
      glTranslatef(((2 * RADIUS + COG_HEIGHT) / 2) + 2.5, 0, 0);
      glRotatef(2 * time - 14, 0, 0, 1);
      glCallList(wheel2);
    glPopMatrix();
  glPopMatrix();                                // vratit zapamatovany souradny system
#endif
 
  // vykresleni POLOHY zrcadla do stencil-bufferu
  glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // zakaze zapis do color-bufferu
  glDepthMask(GL_FALSE);                        // zakaze zapis do depth-bufferu
  glEnable(GL_CULL_FACE);                       // vykreslovani jen licu polygonu
  glEnable(GL_STENCIL_TEST);                    // zapne praci se stencil-bufferem
  glStencilFunc(GL_ALWAYS, 1, 1);               // zapis VZDY
  glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);       // inkrementujeme hodnotu ve stencil-bufferu
  glBegin(GL_QUADS);                            // obdelnik
    glVertex3f(-16.0, -11.0, 0.0);
    glVertex3f( 16.0, -11.0, 0.0);
    glVertex3f( 16.0,  11.0, 0.0);
    glVertex3f(-16.0,  11.0, 0.0);
  glEnd();
  glDisable(GL_CULL_FACE);                      // vratit nastaveni - licove i rubove strany polygonu
  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // vratit nastaveni - bude se kreslit
  glDepthMask(GL_TRUE);                         // vratit nastaveni - bude se psat do depth-bufferu
 
#if 1
  // kolecka jeste jednou - tentokrate za zrcadlem
  glEnable(GL_STENCIL_TEST);                    // zapne praci se stencil-bufferem
  glStencilFunc(GL_EQUAL, 1, 1);                // podminka stencil bufferu - musi tam byt 1
  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);       // operace stencil bufferu - necha tam, co bylo
  glPushMatrix();
    glScalef(1.0, 1.0, -1.0);
    glTranslatef(0, 0, 6);
 
    glCallList(red_mat);
    glPushMatrix();
      glCallList(wheel);
    glPopMatrix();
 
    glCallList(blue_mat);
    glPushMatrix();
      glRotatef(time, 0, 0, 1);
      glTranslatef(((2 * RADIUS + COG_HEIGHT) / 2) + 2.5, 0, 0);
      glRotatef(2 * time - 14, 0, 0, 1);
      glCallList(wheel2);
    glPopMatrix();
  glPopMatrix();
  glDisable(GL_STENCIL_TEST);                   // vratit nastaveni - vypneme praci se stencil bufferem
#endif
 
  // mrizka z car dole
  glDisable(GL_LIGHTING);                       // nepracujeme se svetlem - porad stejne sede
  glColor3f(0.5, 0.5, 0.5);                     // seda barva
  glLineWidth(1.0);                             // tloustka cary - 1 pixel
  glBegin(GL_LINES);                            // budem kreslit cary
  for (int i = -10; i <= 10; i++) {
    glColor3f(1, 0, 0);  glVertex3f(1.5*i, -11.0, -20.0);
    glColor3f(0, 1, 0);  glVertex3f(1.5*i, -11.0,  20.0);
    glColor3f(1, 1, 1);  glVertex3f(-20.0, -11.0, 1.5*i);
    glColor3f(0, 0, 0);  glVertex3f( 20.0, -11.0, 1.5*i);
  }
  glEnd();
  glEnable(GL_LIGHTING);                        // vratit nastaveni - zapnout osvetleni
 
#if 1
  // material zrcadla
  GLfloat mat_diffuse[] = {0.8, 0.8, 0.0, 1.0}; // difuzni (matna) slozka materialu
  GLfloat mat_ambient[] = {0.4, 0.4, 0.0, 1.0}; // ambient (rozptylena) slozka materialu
  GLfloat mat_specular[]= {0.6, 0.9, 0.7, 1.0}; // specular (odleskova) slozka materialu
 
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125);
 
  // vykresleni zrcadla s texturou, ktera udava barvy a pruhlednost
  glEnable(GL_TEXTURE_2D);                      // zapnout texturovani
  glEnable(GL_BLEND);                           // zapnout pruhlednost
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // pruhlednostni funkce - alpha slozka materialu
  glBegin(GL_QUADS);
    glNormal3f(0.0, 0.0, 1.0);                  // nastaveni normaloveho vektoru
    glTexCoord2f(0.0, 0.0);  glVertex3f(-16.0, -11.0, 0.0);
    glTexCoord2f(1.0, 0.0);  glVertex3f( 16.0, -11.0, 0.0);
    glTexCoord2f(1.0, 1.0);  glVertex3f( 16.0,  11.0, 0.0);
    glTexCoord2f(0.0, 1.0);  glVertex3f(-16.0,  11.0, 0.0);
  glEnd();
  glDisable(GL_BLEND);                          // vratit nastaveni - vypnout michani/pruhlednost
  glDisable(GL_TEXTURE_2D);                     // vratit nastaveni - vypnout texturovani
#endif
 
  // takrka povinne ukonceni
  glFlush();                                    // provedeni vsech prikazu
  glutSwapBuffers();                            // a prohozeni bufferu
}
 
 
///////////////////////////////////////////////////////////////////////////////
// funkce volana pri stisku klavesy
// - vola ji GLUT, kdyz uzivatel stiskne klavesu
 
void onKeyboard(unsigned char key, int x, int y)
{
  // uprava velkych pismen na mala
  if (key >= 'A' && key <= 'Z')
    key += 'a' - 'A'; 
 
  // rozeskok podle stlacene klavesy
  switch (key)
  {
    // prepnuti na zobrazeni pouze vrcholu
    case '1':
      glPolygonMode(GL_FRONT, GL_FILL);
      glPolygonMode(GL_BACK, GL_FILL);
      break;
 
    // prepnuti na zobrazeni dratoveho modelu
    case '2':
      glPolygonMode(GL_FRONT, GL_LINE);
      glPolygonMode(GL_BACK, GL_LINE);
      break;
 
    // prepnuti na zobrazeni vyplnenych sten
    case '3':
      glPolygonMode(GL_FRONT, GL_POINT);
      glPolygonMode(GL_BACK, GL_POINT);
      break;
 
    // ukonceni programu
    case 27:                                    // klavesa Escape
    case 'q':
    case 'x':
      exit(0);
      break;
 
    // prepnuti na celou obrazovku
    case 'f':
      glutFullScreen();
      break;
 
    // prepnuti zpet do okna
    case 'w':
      glutReshapeWindow(default_w, default_h);
      break;
 
    default:
      break;
  }
 
  // vyvolani prekresleni OpenGL okna
  glutPostRedisplay();
}
 
 
///////////////////////////////////////////////////////////////////////////////
// funkce volana pri stisku tlacitek mysi                          
// - vola GLUT, kdyz uzivatel stiskne tlacitko mysi
 
int old_mouse_x = 0, old_mouse_y = 0;           // minula pozice mysi, ze ktere se pocitaji rotace
int stav = 0;                                   // stav tlacitek mysi
 
void onMouseClick(int button, int state, int x, int y)
{
  // zapamatovat pozici kurzoru mysi
  old_mouse_x = x;
  old_mouse_y = y;
 
  if (button == GLUT_LEFT_BUTTON) {             // leve tlacitko aktivuje rotaci
    stav = (state == GLUT_DOWN) ? 1 : 0;        // otestuj stlaceni
  }
  else if (button == GLUT_RIGHT_BUTTON) {       // prave tlacitko aktivuje posun
    stav = (state == GLUT_DOWN) ? 2 : 0;        // otestuj stlaceni
  }
  else {
    stav = 0;
  }
 
  // prekresleni sceny
  glutPostRedisplay();
}
 
 
///////////////////////////////////////////////////////////////////////////////
// funkce volana jestlize se mys pohybuje a je stlaceno nektere tlacitko
// - vola GLUT, kdyz uzivatel pohne mysi
 
void onMouseMotion(int x, int y)
{
  if (stav == 1) {                          // rotace sceny
    scene_rot_y += x - old_mouse_x;         // vypocitat novou pozici
    scene_rot_x += y - old_mouse_y;
    glutPostRedisplay();                    // a prekreslit scenu
  }
  else if (stav == 2) {                     // posuv sceny
    scene_move_z += old_mouse_y - y;
    glutPostRedisplay();
  }
 
  // zapamatovat novou pozici kurzoru
  old_mouse_x = x;
  old_mouse_y = y;
}
 
 
///////////////////////////////////////////////////////////////////////////////
// funkce volana 40x za sekundu                                         
// - vola GLUT                                                       
 
void onTimer(int x)
{
  time++;                               // zaznacime, ze doslo ke zmene stavu
  glutPostRedisplay();                  // a rekneme, ze ma dojit k prekresleni
  glutTimerFunc(25, onTimer, 0);        // naplanujeme, ze za dalsich 25ms se ma funkce zavolat
}
 
 
///////////////////////////////////////////////////////////////////////////////
// hlavni funkce koznolove aplikace                                
// argc - pocet vstupnich parametru
// argv - pole vstupnich parametru
 
int main(int argc, char **argv)
{
  // inicializace knihovny GLUT
  glutInit(&argc, argv);
 
  // nastaveni grafickeho modu okna
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
  // pocatecni velikost okna
  glutInitWindowSize(default_w, default_h);
  // pocatecni pozice okna
//  glutInitWindowPosition(200, 100);
  // vytvoreni okna pro kresleni
  glutCreateWindow(PROGRAM_TITLE);
 
  // registrace funkce volane pri prekreslovani
  glutDisplayFunc(onDisplay);
  // registrace funkce volane pri zmene velikosti
  glutReshapeFunc(onReshape);
  // registrace funkce volane pri stisku klavesy
  glutKeyboardFunc(onKeyboard);
  // registrace funkce volane pri stisku tlacitek mysi
  glutMouseFunc(onMouseClick);
  // registrace funkce volane pri pohybu mysi
  glutMotionFunc(onMouseMotion);
  // registrace funkce volane za 25 milisekund
  glutTimerFunc(25, onTimer, 0);
 
  // provest nasi uzivatelskou inicializaci
  Init();
  InitMaterialDisplayLists();
  wheel = InitWheelDisplayList(RADIUS, COG_COUNT);
  wheel2 = InitWheelDisplayList(RADIUS/2, COG_COUNT/2);
  InitTexture();
 
  // nekonecna smycka volajici registrovane funkce
  glutMainLoop();
 
  // ANSI C potrebuje ukoncit fci main prikazem return
  return 0;
}
 
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////