/*****************************************************************
  drawOrder1.c
  by Dave Pape
  9 Feb 2003
  
  This program further demonstrates the effect of drawing order on
  blended transparency.  It draws an opaque sphere, with a transparent
  cube, cone, and teapot in front of it.
  Hitting the spacebar toggles between three different orders of
  drawing the transparent objects.  (There are three other possible
  orders that aren't shown.)
  No matter which order is used, it is possible to rotate the camera
  to a view where the transparency comes out wrong for that order.
  For the scene to always appear correct, we would have to compute
  the distance of each of the transparent objects from the camera
  at each frame, and then draw them in order from farthest to
  nearest.
  
*****************************************************************/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <GL/glut.h>
#include <GL/gl.h>

float viewRotY = 0, viewRotX = 0, cameraDistance = 10;
int drawOrder = 0;

void drawEverything(void);
void initLight(void);
void checkGLError(char *);
void key(unsigned char k, int x, int y);
void specialkey(int k, int x, int y);


int main(int argc, char *argv[])
    {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(512, 512);
    glutCreateWindow(argv[0]);
    glutDisplayFunc(drawEverything);
    glutKeyboardFunc(key);
    glutSpecialFunc(specialkey);
    glutMainLoop();
    return 0;
    }


void drawEverything(void)
    {
    GLfloat red[4] = { 1, 0, 0, 0.5 };
    GLfloat green[4] = { 0, 1, 0, 0.25 };
    GLfloat blue[4] = { 0, 0, 0.8, 0.33 };
    GLfloat white[4] = { 1, 1, 1, 1 };
    glClearColor(0.5, 0.7, 1.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30.0, 1.0, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

    initLight();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    glTranslatef(0.0, 0.0, -cameraDistance);
    glRotatef(viewRotX, 1.0, 0.0, 0.0);
    glRotatef(viewRotY, 0.0, 1.0, 0.0);

    glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
    glPushMatrix();
     glTranslatef(0.0, 0.0, -5.0);
     glutSolidSphere(2.5, 16, 8);
    glPopMatrix();    

/*************** CODE OF INTEREST ******************************/
/*    Depending on the variable 'drawOrder', we draw the       */
/*    objects in one of three different orders.                */
/*    The first order is: cube, cone, teapot                   */
/*                                                             */
    if (drawOrder == 0)                                      /**/
        {                                                    /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, red);             /**/
        glutSolidCube(2.0);                                  /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, green);           /**/
        glPushMatrix();                                      /**/
         glTranslatef(1.0, -1.0, 0.0);                       /**/
         glRotatef(-90.0, 1.0, 0.0, 0.0);                    /**/
         glutSolidCone(1.5, 3.5, 16, 1);                     /**/
        glPopMatrix();                                       /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, blue);            /**/
        glPushMatrix();                                      /**/
         glTranslatef(0.0, 0.0, 3.0);                        /**/
         glutSolidTeapot(0.5);                               /**/
        glPopMatrix();                                       /**/
        }                                                    /**/
/*                                                             */
/*    The second order is: cone, cube, teapot                  */
/*                                                             */
    else if (drawOrder == 1)                                 /**/
        {                                                    /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, green);           /**/
        glPushMatrix();                                      /**/
         glTranslatef(1.0, -1.0, 0.0);                       /**/
         glRotatef(-90.0, 1.0, 0.0, 0.0);                    /**/
         glutSolidCone(1.5, 3.5, 16, 1);                     /**/
        glPopMatrix();                                       /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, red);             /**/
        glutSolidCube(2.0);                                  /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, blue);            /**/
        glPushMatrix();                                      /**/
         glTranslatef(0.0, 0.0, 3.0);                        /**/
         glutSolidTeapot(0.5);                               /**/
        glPopMatrix();                                       /**/
        }                                                    /**/
/*                                                             */
/*    The third order is: cone, teapot, cube                   */
/*                                                             */
    else if (drawOrder == 2)                                 /**/
        {                                                    /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, green);           /**/
        glPushMatrix();                                      /**/
         glTranslatef(1.0, -1.0, 0.0);                       /**/
         glRotatef(-90.0, 1.0, 0.0, 0.0);                    /**/
         glutSolidCone(1.5, 3.5, 16, 1);                     /**/
        glPopMatrix();                                       /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, blue);            /**/
        glPushMatrix();                                      /**/
         glTranslatef(0.0, 0.0, 3.0);                        /**/
         glutSolidTeapot(0.5);                               /**/
        glPopMatrix();                                       /**/
        glMaterialfv(GL_FRONT, GL_DIFFUSE, red);             /**/
        glutSolidCube(2.0);                                  /**/
        }                                                    /**/

    glutSwapBuffers();
    
    checkGLError("end-of-frame");
    }


void initLight(void)
    {
    GLfloat white[4] = { 1, 1, 1, 1 };
    GLfloat lightPos[4] = {-1, 1, 1, 0};
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, white);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
    }


void checkGLError(char *prefix)
    {
    GLenum err = glGetError();
    if (err != GL_NO_ERROR)
        printf("%s GL error '%s'\n",prefix,gluErrorString(err));
    }


void key(unsigned char k, int x, int y)
    {
    if (k == 27)
        exit(0);
    else if (k == '-')
        cameraDistance += 1;
    else if (k == '=')
        cameraDistance -= 1;
    else if (k == ' ')
        drawOrder = (drawOrder+1) % 3;
    glutPostRedisplay();
    }


void specialkey(int k, int x, int y)
    {
    if (k == GLUT_KEY_LEFT)
        viewRotY += 3;
    else if (k == GLUT_KEY_RIGHT)
        viewRotY -= 3;
    else if (k == GLUT_KEY_UP)
        viewRotX += 3;
    else if (k == GLUT_KEY_DOWN)
        viewRotX -= 3;
    glutPostRedisplay();
    }
