/****************************************************************************
 * file:      vb-01-opengl.c
 * status:    17-apr-2001, pfk.
 *            01-okt-2009, pfk: voorbeeld revisited.
 *            01-Aug-2010, pfk: English version.
 * functie:   Sample program 1: OpenGL survival kit.
 *            Draw lines with different attributes, draw teapot, draw text.
 ****************************************************************************
Description:
In function "main" the graphics package OpenGL is initialised and started.
Declaration of variables, input of data and processing of data to be 
displayed can be done prior to the initialisation of OpenGL, e.g. in 
function "progInit" or in a separate function called from "main".
Display of graphics data with OpenGL is performed in function "progDisplay", 
so you should make the data (e.g. collected or created in "progInit", 
in another function or in "main") available in "progDisplay".
Yoy may use global variables to make data available to the various functions 
by declaring the variables at the beginning of the program code outside any 
function.  See below at "Global variables".
Global variables can be used within all functions, they will keep values 
assigned in one function for use in another function. 
NB: If you declare variables in a function with the same name as globally
    declared variables, the global variables are not available in 
    this function!
NB: Do not use global variables in function argument lists, this will give
    incorrect return values!
 ****************************************************************************/

// Declaration of external library functions.
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// Constants used in the program.

// Size and position of the OpenGL output window.
#define WINSIZE_X    600   // size in X in pixels
#define WINSIZE_Y    400   // size in Y in pixels
#define WINPOS_X     100   // position top
#define WINPOS_Y     100   //              left corner

// Boundaries of user coordinates in OpenGL output window.
// The domains have the same X/Y ratio as the window pixel sizes.
#define XMIN        -3.0   // X of bottom left
#define XMAX         3.0   // X of top right
#define YMIN        -2.0   // Y of bottom left
#define YMAX         2.0   // Y of top right

// Window title.
#define FRAME_TITLE  "Sample program: OpenGL survival kit"

// Global variables.  These variables are not used in this program!
#define GLOBAL_LENGTH 100  // length of array

// Examples of declarations of global variables.
// NB: The word "static" is not always necessary.
static int   IAmAGlobalInt;
static float IAmAGlobalFloat;
static float IAmAGlobalFloatArray[GLOBAL_LENGTH];

/****************************************************************************
 * Initialize. 
 * This is the place to do all the things before starting the display of
 * objects, e.g. computations. 
 ****************************************************************************/
void progInit (void) 
{
  // Set background color of OpenGL window.
  glClearColor (0.0, 0.0, 0.0, 0.0);  // black (R, G, B, opacity)

  // Display instructions for user in command window.
  printf
    ("\n  %s\n  Type 'e', 'q', 's', or ESC in display window to terminate.\n",
     FRAME_TITLE);
}


/****************************************************************************
 * Print text variable "s", starting at position (x,y).
 ****************************************************************************/
void printText (float x, float y, char s[])
{
  // Local declarations.
  int  i,       // loop variabele
       length;  // number of characters, length of "s"
  char c;       // single character

  // Go to begin position in OpenGL window.
  glRasterPos2f (x, y);

  length = strlen (s);      // number of characters in "s"
  for (i=0; i<length; i++)  // loop to print all characters of "s"
  { c = s[i];                                       // fetch character
    glutBitmapCharacter (GLUT_BITMAP_8_BY_13, c);   // print character
  }
}


/****************************************************************************
 * Draw line from (x1,y1) to (x2,y2).
 * Coordinate values are in user coordinates.
 ****************************************************************************/
void drawLine (float x1, float y1, float x2, float y2)
{
  glBegin (GL_LINES);
    glVertex2f (x1, y1);
    glVertex2f (x2, y2);
  glEnd ();
}


/****************************************************************************
 * Create display by calling OpenGL functions.
 * Arguments of drawing functions are in user coordinates.
 * User coordinates are set in function "progReshape".
 ****************************************************************************/
void progDisplay (void)
{
  // Clear OpenGL window, i.e. set background color defined by "glClearColor".
  glClear (GL_COLOR_BUFFER_BIT);

  // Draw purple rectangle just within the OpenGL window.
  glColor3f (1.0, 0.0, 1.0);                         // set color (R, G, B)
  drawLine (XMIN+0.1, YMIN+0.1, XMAX-0.1, YMIN+0.1); // bottom left to right
  drawLine (XMAX-0.1, YMIN+0.1, XMAX-0.1, YMAX-0.1); // right bottom to top
  drawLine (XMAX-0.1, YMAX-0.1, XMIN+0.1, YMAX-0.1); // top right to left
  drawLine (XMIN+0.1, YMAX-0.1, XMIN+0.1, YMIN+0.1); // left top to bottom

  // Draw yellow text.
  glColor3f (1.0, 1.0, 0.0);                         // set color
  printText (XMIN+0.2, YMAX-0.3,
          "This OpenGL window displays various graphical objects.");

  // Draw lines with various attributes.

  // Draw line with double width, default line width = 1.
  glColor3f (0.5, 0.5, 1.0);                         // set color
  glLineWidth (2.0);                                 // set line width
  drawLine (XMIN+0.2, YMAX-0.4, XMAX-0.2, YMAX-0.4); // draw line

  // Draw line with width = 4.
  glColor3f (0.4, 0.4, 1.0);                         // set color
  glLineWidth (4.0);                                 // set line width
  drawLine (XMIN+0.2, YMAX-0.5, XMAX-0.2, YMAX-0.5); // draw line

  /* Line stippling must be enabled by "glEnable", using argument
   * "GL_LINE_STIPPLE" (predefined by OpenGL initialisation). */
  glEnable (GL_LINE_STIPPLE);

  /* Stippling is set using a 16-bit pattern: draw if bit=1, skip if bit=0.
   * Bits are interpreted in a reverse way!
   * A repeat factor (first argument) repeats the bits of the pattern,
   * so repeat factor = 1 for 1010 gives 11001100 and not 10101010. */

  /* Dotted pattern: 1111 0000 1111 0000 -> 0000 1111 0000 1111 */
  glColor3f (0.3, 0.3, 1.0);
  glLineStipple (1, (GLushort) 0x0F0F);
  drawLine (XMIN+0.2, YMAX-0.6, XMAX-0.2, YMAX-0.6);

  /* Dashed pattern: 1111 1111 1111 0000 -> 0000 1111 1111 1111 */
  glColor3f (0.2, 0.2, 1.0);
  glLineStipple (1, (GLushort) 0x0FFF);
  drawLine (XMIN+0.2, YMAX-0.7, XMAX-0.2, YMAX-0.7);

  /* Long dashed pattern, repeat factor 2: 
     1111 1111 1111 1000 -> 00000011 11111111 11111111 11111111 */
  glColor3f (0.3, 0.3, 1.0);
  glLineStipple (2, (GLushort) 0x1FFF);
  drawLine (XMIN+0.2, YMAX-0.8, XMAX-0.2, YMAX-0.8);

  /* Dot-dashed pattern: 1110 0011 1111 1000 -> 0001 1111 1100 0111 */
  glColor3f (0.4, 0.4, 1.0);
  glLineStipple (1, (GLushort) 0x1FC7);
  drawLine (XMIN+0.2, YMAX-0.9, XMAX-0.2, YMAX-0.9);

  /* Fully drawn line, all bits set: 1111 1111 1111 1111 */
  glColor3f (0.5, 0.5, 1.0);
  glLineStipple (1, (GLushort) 0xFFFF);
  drawLine (XMIN+0.2, YMAX-1.0, XMAX-0.2, YMAX-1.0);

  // Disable line stippling.
  glDisable (GL_LINE_STIPPLE);

  // Draw light blue hour-glass with line width 5.
  glColor3f (0.5, 1.0, 1.0);    // set color
  glLineWidth (5.0);            // set line width
  glBegin (GL_LINE_STRIP);            // draw line strip
    glVertex2f (XMIN+0.5, YMIN+1.3);  //   series of points with
    glVertex2f (XMIN+1.2, YMIN+1.3);  //   interconnecting lines
    glVertex2f (XMIN+0.5, YMAX-1.2);
    glVertex2f (XMIN+1.2, YMAX-1.2);
    glVertex2f (XMIN+0.5, YMIN+1.3);
  glEnd ();

  // Draw light green teapot (from glut library) with default line width 1. 
  // The teapot is drawn around the origin of the user coordinate system.
  glColor3f (0.4, 1.0, 0.4);
  glLineWidth (1.0);
  glutWireTeapot (1.0);

  // Draw purple triangle with dotted lines of width 5.
  glColor3f (1.0, 0.5, 1.0);
  glLineWidth (5.0);
  glEnable (GL_LINE_STIPPLE);
  glLineStipple (1, (GLushort) 0x0F0F);
  glBegin (GL_LINE_LOOP);
    glVertex2f (XMAX-0.5, YMIN+1.3);
    glVertex2f (XMAX-1.2, YMIN+1.3);
    glVertex2f (XMAX-0.85, YMAX-1.2);
  glEnd ();
  glDisable (GL_LINE_STIPPLE);
  glLineWidth (1.0);

  // Draw filled orange rectangle with filled red rectangle inside.
  glColor3f (1.0, 0.5, 0.0);
  glRectf (XMIN+0.2, YMIN+0.4, XMAX-0.2, YMIN+1.1);
  glColor3f (1.0, 0.0, 0.0);
  glRectf (XMIN+0.6, YMIN+0.6, XMAX-0.6, YMIN+0.9);

  // Draw turquoise text below in OpenGL window.
  glColor3f (0.0, 0.9, 0.9);
  printText (XMIN+0.2, YMIN+0.2,
             "Type 'e', 'q', 's', or ESC in OpenGL window to terminate.");

  // Proces the above function calls, or, go and draw in OpenGL window!
  glFlush ();
}


/****************************************************************************
 * Handle change in size of output window:
 *   "w" and "h" are the new width and height of the output window in window 
 *   coordinates.
 * The old window is displayed completely in the new window, which may result
 * in distortion of the X/Y ratio.
 * NB: This function is also called after the initialization of OpenGL, to
 *     to set the boundaries for the user coordinates.
 ****************************************************************************/
void progReshape (GLsizei w, GLsizei h)
{
  glViewport (0, 0, w, h);             // Use full area of OpenGL window.
  glMatrixMode (GL_PROJECTION);        // Select projection matrix.
  glLoadIdentity ();                   // Set to identity matrix.
  gluOrtho2D (XMIN, XMAX, YMIN, YMAX); // Multiply projection matrix with the
                                       // matrix that maps the user coordinates
                                       // to the OpenGL window.
  glFlush ();                          // Empty frame buffer and display.
}


/****************************************************************************
 * Handle keyboard interrupts:
 * terminate program if "e", "q", "s" or ESC is entered in the OpenGL window.
 ****************************************************************************/
void progKeyboard (unsigned char key, int x, int y)
{
  switch (key)   // only the following keys are allowed
  { case 'e': 
    case 'q': 
    case 's': 
    case  27: exit (0);  // escape key ESC
              break;
  }
}


/****************************************************************************
 * Main program function.
 ****************************************************************************/
int main (int argc, char** argv)
{
  /* This is the place for declarations, input of data and processing data. 
     The building of graphical objects and the display must be done in
     function "progDisplay".
     See the description at the beginning of this program for a description
     how to move data from input and processing functions to the graphics
     building and display function. */

  // Initialise OpenGL Utility Toolkit (glut).
  // NB: This function must be called prior to calling OpenGL or glut
  //     functions.
  glutInit (&argc, argv);

  // Initialisatie van parameters van het grafische venster.
  glutInitDisplayMode    (GLUT_SINGLE | GLUT_RGB);  // simple buffer,
                                                    // RGB color model
  glutInitWindowSize     (WINSIZE_X, WINSIZE_Y);    // size in pixels
  glutInitWindowPosition (WINPOS_X, WINPOS_Y);      // position top left corner

  // Create OpenGL output window.
  glutCreateWindow (FRAME_TITLE);

  // General initialisation function.
  progInit ();

  // Declaration of event handling functions.
  glutDisplayFunc  (progDisplay);   // display OpenGL objects in output window
  glutReshapeFunc  (progReshape);   // redisplay after change in size of
                                    // output window
  glutKeyboardFunc (progKeyboard);  // handle keyboard interrupts

  // Event handling loop.
  glutMainLoop ();

  return 0;
}
