/****************************************************************************
 * file:      v06.c
 * status:    25-apr-2001, pfk.
 * functie:   Multiple windows.
 *            Display lists.
 ****************************************************************************/

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

#define FRAME_TITLE                 "Multiple windows & Display lists"
#define SPHERE_LINE_DISPLAY_LIST    1
#define SPHERE_FILL_DISPLAY_LIST    2
#define CYLINDER_LINE_DISPLAY_LIST  3
#define CYLINDER_FILL_DISPLAY_LIST  4
#define MENU_DISPLAY_LIST           6
#define SPHERE                      1
#define CYLINDER                    2
#define ALL_OBJECTS                 3

#define WINSIZE_X    600
#define WINSIZE_Y    600
#define WINPOS_X     100   /* position of left           */
#define WINPOS_Y     100   /*             upper corner   */

#define XMIN        -0.1   /* boundaries of user coordinates in main window */
#define XMAX         1.1
#define YMIN        -0.1
#define YMAX         1.1

GLfloat       light_diffuse[]  = { 1.0, 0.0, 0.0, 1.0,
                                     0.0, 1.0, 0.0, 1.0,
                                     0.4, 0.4, 1.0, 1.0  };
GLfloat       light_diffuseg[] = { 0.0, 1.0, 0.0, 1.0 };
GLfloat       light_position[] = { 4.0, 4.0, 1.0, 0.0 };
GLUquadricObj *qobjSphereFill,
              *qobjSphereLine,
              *qobjCylinderFill,
              *qobjCylinderLine;

static int winIdWork,         /* identifier of main window */
           winIdMenu;         /* identifier of menu window */
static int colourOfFigure;    /* 1=r, 2=g, 3=b */
static GLenum styleOfFigure;  /* GLU_FILL, GLU_LINE */
static int objectType;


/****************************************************************************
 * Print text in string "s" starting at position (x,y).
 ****************************************************************************/
void printText (float x, float y, char s[])
{
  int i;
  glRasterPos2f (x, y);
  for(i=0; i<strlen(s); i++)
    glutBitmapCharacter (GLUT_BITMAP_8_BY_13, s[i]);
}


/****************************************************************************
 * Display of menu window.
 ****************************************************************************/
void menuDisplay (void)
{
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glCallList (MENU_DISPLAY_LIST);
  glutSwapBuffers ();
}


/****************************************************************************
 * Processing of keyboard interrupt in menu window:
 *   stop program if "e", "q" or ESC has been typed.
 ****************************************************************************/
void menuKeyboard (unsigned char key, int x, int y)
{
  switch (key)
  { case 'e': exit (0);
              break;
    case 'q': exit (0);
              break;
    case  27: exit (0);
              break;
    case 'r': colourOfFigure = 1;
              break;
    case 'g': colourOfFigure = 2;
              break;
    case 'b': colourOfFigure = 3;
              break;
    case 'v': styleOfFigure = GLU_FILL;
              break;
    case 'l': styleOfFigure = GLU_LINE;
              break;
    case 'c': objectType = CYLINDER;
              break;
    case 's': objectType = SPHERE;
              break;
    case 'a': objectType = ALL_OBJECTS;
              break;
  }
}


/****************************************************************************
 * Initialize display in menu window.
 ****************************************************************************/
void menuInit (void) 
{
  /* This list should not contain variables with changing values! */
  glNewList (MENU_DISPLAY_LIST, GL_COMPILE);
    glColor3f (1.0, 1.0, 0.0);
    printText (-0.94, 0.95, "Commando's in");
    printText (-0.94, 0.90, "dit window:");
    glColor3f (1.0, 0.0, 0.0);
    printText (-0.94, 0.80, "r = rood object");
    glColor3f (0.0, 1.0, 0.0);
    printText (-0.94, 0.75, "g = groen object");
    glColor3f (0.4, 0.4, 1.0);
    printText (-0.94, 0.70, "b = blauw object");
    glColor3f (0.0, 1.0, 1.0);
    printText (-0.94, 0.60, "l = lijnenobject");
    printText (-0.94, 0.55, "v = gevuld object");
    glColor3f (1.0, 0.0, 1.0);
    printText (-0.94, 0.45, "c = cylinder");
    printText (-0.94, 0.40, "s = bol (sphere)");
    printText (-0.94, 0.35, "a = allebei");
    glColor3f (1.0, 1.0, 1.0);
    printText (-0.94, -0.30, "Geef 'e', 'q' of");
    printText (-0.94, -0.35, "ESC om te stoppen");
    printText (-0.94, -0.40, "met dit programma.");
  glEndList ();
}


/****************************************************************************
 * Display of main window.
 ****************************************************************************/
void workDisplay (void)
{
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLightfv (GL_LIGHT0, GL_DIFFUSE, &light_diffuse[(colourOfFigure-1)*4]);
  if (styleOfFigure == GLU_FILL)
  {
    if (objectType == CYLINDER)
      glCallList (CYLINDER_FILL_DISPLAY_LIST);
    else if (objectType == SPHERE)
      glCallList (SPHERE_FILL_DISPLAY_LIST);
    else
    {
      glCallList (CYLINDER_FILL_DISPLAY_LIST);
      glCallList (SPHERE_FILL_DISPLAY_LIST);
    }
  }
  else
  {
    if (objectType == CYLINDER)
      glCallList (CYLINDER_LINE_DISPLAY_LIST);
    else if (objectType == SPHERE)
      glCallList (SPHERE_LINE_DISPLAY_LIST);
    else
    {
      glCallList (CYLINDER_LINE_DISPLAY_LIST);
      glCallList (SPHERE_LINE_DISPLAY_LIST);
    }
  }
  glutSwapBuffers ();
  glutPostRedisplay ();
}


/****************************************************************************
 * Initialize: make display lists for various objects.
 ****************************************************************************/
void workInit (void) 
{
  /* Set initial values. */
  colourOfFigure = 1;
  styleOfFigure  = GLU_FILL;
  objectType     = SPHERE;

  /* Create filled sphere. */
  qobjSphereFill = gluNewQuadric ();           /* create a quadrics object */
  gluQuadricDrawStyle (qobjSphereFill, GLU_FILL);
  glNewList (SPHERE_FILL_DISPLAY_LIST, GL_COMPILE);
    gluSphere (qobjSphereFill, /* fill quadrics object with sphere */
               1.0,            /* radius */
               20,             /* slices */
               20);            /* stacks */ 
  glEndList ();

  /* Create mesh sphere. */
  qobjSphereLine = gluNewQuadric ();           /* create a quadrics object */
  gluQuadricDrawStyle (qobjSphereLine, GLU_LINE);
  glNewList (SPHERE_LINE_DISPLAY_LIST, GL_COMPILE);
    gluSphere (qobjSphereLine,
               1.0,            /* radius */
               20,             /* slices */
               20);            /* stacks */ 
  glEndList ();

  /* Create filled cylinder. */
  qobjCylinderFill = gluNewQuadric ();         /* create a quadrics object */
  gluQuadricDrawStyle (qobjCylinderFill, GLU_FILL);
  glNewList (CYLINDER_FILL_DISPLAY_LIST, GL_COMPILE);
    gluCylinder (qobjCylinderFill,
               1.2,            /* radius at bottom */
               1.4,            /* radius at top */
               1.5,            /* height */
               20,             /* slices */
               20);            /* stacks */ 
  glEndList ();

  /* Create mesh cylinder. */
  qobjCylinderLine = gluNewQuadric ();         /* create a quadrics object */
  gluQuadricDrawStyle (qobjCylinderLine, GLU_LINE);
  glNewList (CYLINDER_LINE_DISPLAY_LIST, GL_COMPILE);
    gluCylinder (qobjCylinderLine,
               1.2,            /* radius at bottom */
               1.4,            /* radius at top */
               1.5,            /* height */
               20,             /* slices */
               20);            /* stacks */ 
  glEndList ();

  /* Set global lighting for all objects. */
  /* Colour of diffuse light can be changed from Menu window. */
  glLightfv (GL_LIGHT0, GL_DIFFUSE, &light_diffuse[(colourOfFigure-1)*4]);
  glLightfv (GL_LIGHT0, GL_POSITION, light_position);
  glEnable  (GL_LIGHTING);
  glEnable  (GL_LIGHT0);

  /* Set projection matrix. */
  glMatrixMode (GL_PROJECTION);
  gluPerspective (40.0,        /* field of view in degrees */
                   1.0,        /* aspect ratio */
                   1.0,        /* Z near */
                  10.0);       /* Z far */

  /* Set modeling+viewing matrix. */
  glMatrixMode (GL_MODELVIEW);
  gluLookAt (0.0, 0.0, 5.0,    /* eye position */
             0.0, 0.0, 0.0,    /* center position of viewing window */
             0.0, 1.0, 0.0);   /* up is in +Y direction */
  glTranslatef (0.0, 0.0, -1.0);
  glRotatef (20.0, 0.0, 1.0, 0.0);
}


/****************************************************************************
 * Main program.
 ****************************************************************************/
int main (int argc, char** argv)
{
  glutInit            (&argc, argv);
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

  /* Menu window. */
  glutInitWindowSize (WINSIZE_X/4, WINSIZE_Y); 
  glutInitWindowPosition (WINPOS_X+WINSIZE_X+8, WINPOS_Y);
  if (!(winIdMenu = glutCreateWindow("Menu")))
  { fprintf (stderr, "Error, couldn't open Menu window!\n");
    return -1;
  }
  glutDisplayFunc     (menuDisplay);
  glutKeyboardFunc    (menuKeyboard);
  menuInit ();

  /* Work window. */
  glutInitWindowSize (WINSIZE_X, WINSIZE_Y); 
  glutInitWindowPosition (WINPOS_X, WINPOS_Y);
  if (!(winIdWork = glutCreateWindow(FRAME_TITLE)))
  { fprintf (stderr, "Error, couldn't open Work window!\n");
    return -1;
  }
  glutDisplayFunc     (workDisplay);
  workInit ();

  glutMainLoop ();
  return 0;
}
