// boca - Hip - 2009-12-14
// iscrtavanje plinske boce uz pomoc B-splajnova

#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#ifndef CALLBACK
#define CALLBACK
#endif

// "degree parameter" za B-splajnove
#define D 4

// broj kontrolnih toccaka u horizontali
#define NKTX 7

// broj kontrolnih toccaka duzh vertikale
#define NKTY 9

double kutX = 0.0, kutY = 0.0;
float orange[] = {1.0, 0.65, 0.0, 1.0};
float bijelo[] = {1.0, 1.0, 1.0, 1.0};
float tamnoSivo[] = {0.2, 0.2, 0.2, 0.2};

GLUnurbsObj *ploha1;
float ntol = 25.0; // GLU sampling tolerance

float oblik[NKTY][3] = {
  {0.5, 3.0, 0.0},
  {0.5, 2.0, 0.0},
  {0.5, 1.75, 0.0},
  {2.5, 1.5, 0.0},
  {2.5, 0.0, 0.0},
  {2.5, -1.0, 0.0},
  {2.5, -3.0, 0.0},
  {0.1, -3.0, 0.0},
  {0.0, -3.0, 0.0}
};

float ktocka[NKTX][NKTY][3];

void izracunajKtocke() {
  int i, j, k;
  double krad;

  // - kontrolne toccke periodiccnog B-splajna postavlja u kvadrat
  // - zbog D = 4 zadnje 3 toccke se preklapaju s prve 3 kako bi
  //   dobili zatvorenu krivulju
  for(i = 0; i <= NKTX; i++) {
    krad = i * M_PI / 2.0;
    for(j = 0; j < NKTY; j++) {
      ktocka[i][j][0] = oblik[j][0] * cos(krad) - oblik[j][2] * sin(krad);
      ktocka[i][j][1] = oblik[j][1];
      ktocka[i][j][2] = oblik[j][0] * sin(krad) + oblik[j][2] * cos(krad);
    }
  }
} // izracunajKtocke

void CALLBACK nurbsError(GLenum errorCode) {
  const GLubyte *estring;

  estring = gluErrorString(errorCode);
  fprintf (stderr, "Nurbs Error: %s\n", estring);
  exit (0);
} // nurbsError
		
float cvoroviP[NKTX + D] = // ccvorovi periodiccnog B-splajna
  {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
float cvoroviO[NKTY + D] = // ccvorovi otvorenog B-splajna
  {0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 6.0, 6.0, 6.0};

void pripremiPlohu() {
   izracunajKtocke();

   ploha1 = gluNewNurbsRenderer();
   gluNurbsProperty(ploha1, GLU_DISPLAY_MODE, GLU_FILL);
   gluNurbsCallback(ploha1, GLU_ERROR, nurbsError);
} // pripremi plohu

void svjetlo0() {
  float pozicija[] = {0.0, 0.0, 0.0, 1.0};

  glLightfv(GL_LIGHT0, GL_DIFFUSE, bijelo);
  glLightfv(GL_LIGHT0, GL_SPECULAR, bijelo);
  glLightfv(GL_LIGHT0, GL_AMBIENT, tamnoSivo);
  glLightfv(GL_LIGHT0, GL_POSITION, pozicija);
} // svjetlo0

void iscrtaj(void) {
  int i, j;

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glPushMatrix();
    glTranslated(-10.0, 0.0, 20.0);
    svjetlo0(); // postavi svjetlo
  glPopMatrix();

  glPushMatrix();
    glRotated(kutY, 0.0, 1.0, 0.0);
    glRotated(kutX, 1.0, 0.0, 0.0);

    glMaterialfv(GL_FRONT, GL_DIFFUSE, orange);
    glMaterialfv(GL_FRONT, GL_SPECULAR, bijelo);
    glMaterialf(GL_FRONT, GL_SHININESS, 100.0);

    gluNurbsProperty(ploha1, GLU_SAMPLING_TOLERANCE, ntol);
    gluBeginSurface(ploha1);
    gluNurbsSurface(ploha1, 
      NKTX + D, cvoroviP, NKTY + D, cvoroviO, 
      NKTY * 3, 3, &ktocka[0][0][0], 
      D, D, GL_MAP2_VERTEX_3);
    gluEndSurface(ploha1);
  glPopMatrix();
  glutSwapBuffers();
} // iscrtaj

void skaliraj(int w, int h) {
  float a = 4.0, b;

  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if(w > h) {
    b = a * (float)w / (float)h;
    glOrtho(-b, b, -a, a, -a, a);
  } else {
    b = a * (float)h / (float)w;
    glOrtho(-a, a, -b, b, -a, a);
  }

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
} // skaliraj

void tipka(unsigned char c, int x, int y) {
  if(c == 'q') exit(0);
  if(c == 's') glShadeModel(GL_SMOOTH);
  if(c == 'f') glShadeModel(GL_FLAT);
  if(c == 'A') glEnable(GL_AUTO_NORMAL);
  if(c == 'a') glDisable(GL_AUTO_NORMAL);
  if(c == 'D') glEnable(GL_DEPTH_TEST);
  if(c == 'd') glDisable(GL_DEPTH_TEST);
  if(c == 'C') glEnable(GL_CULL_FACE);
  if(c == 'c') glDisable(GL_CULL_FACE);
  if(c == 'B') glCullFace(GL_BACK);
  if(c == 'b') glCullFace(GL_FRONT);
  if(c == '0') glEnable(GL_LIGHT0);
  if(c == '=') glDisable(GL_LIGHT0);
  if(c == 'm') gluNurbsProperty(ploha1,
    GLU_DISPLAY_MODE, GLU_OUTLINE_POLYGON);
  if(c == 'M') gluNurbsProperty(ploha1,
    GLU_DISPLAY_MODE, GLU_FILL);
  if(c == 'N') { if(++ntol > 50) ntol = 50; }
  if(c == 'n') { if(--ntol < 10) ntol = 10; }
  glutPostRedisplay();
} // tipka

int mtipka, mstanje;

void mish(int tipka, int stanje, int x, int y)
{
  if(tipka == GLUT_MIDDLE_BUTTON) exit(0);

  mtipka = tipka;
  mstanje = stanje;
} // mish

void pomak(int x, int y) {
  int vp[4], vx, vy;

  if (mstanje == GLUT_UP) return;

  if(mtipka == GLUT_LEFT_BUTTON) {
    glGetIntegerv(GL_VIEWPORT, vp);
    vx = vp[2] * 0.5; vy = vp[3] * 0.5;
    kutY = 180.0 * (x - vx) / (float)vx;
    kutX = 180.0 * (y - vy) / (float)vy;
    glutPostRedisplay();
  }
} // pomak

int main(int argc, char** argv) {
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(500, 500);
  glutInitWindowPosition(0, 0);
  glutCreateWindow(argv[0]);

  glClearColor(0.0, 0.0, 0.0, 0.0);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_AUTO_NORMAL); // automatski raccuna normale
  pripremiPlohu();

  glutReshapeFunc(skaliraj);
  glutDisplayFunc(iscrtaj);
  glutMotionFunc(pomak);
  glutMouseFunc(mish);
  glutKeyboardFunc(tipka);
  glutMainLoop();
  return 0; 
} // main

