/*****************************************************************
  drawbacks.c
  by Dave Pape
  9 Feb 2003
  
  This program demonstrates using different modes of backface
  culling, along with drawing the scene multiple times ("multi-pass
  rendering").
  It draws an a transparent cube and cone in front of an opaque
  sphere.
  Hitting the spacebar toggles between two modes:
   In the default mode, the objects are drawn once, with normal
   backface culling.
   In the alternate mode, the objects' back faces are rendered
   first, by culling the front faces.  Then the front faces are
   rendered normally.
  
  New functions shown here:
        - glCullFace()

*****************************************************************/
#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 drawbacks = 0;

void drawEverything(void);
void drawTransparentObjects(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 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, -2.0, -5.0);
     glutSolidSphere(2.5, 16, 8);
    glPopMatrix();    

/*************** CODE OF INTEREST ******************************/
/*    If the flag 'drawbacks' is true, draw the backs of the   */
/*    transparent objects by changing the glCullFace mode      */
/*    rendering.                                               */
/*                                                             */
    if (drawbacks)                                           /**/
        {                                                    /**/
        glCullFace(GL_FRONT);                                /**/
        drawTransparentObjects();                            /**/
        }                                                    /**/
    
/*    Then render the fronts of the objects, with normal       */
/*    backface culling.                                        */
/*                                                             */
    glCullFace(GL_BACK);                                     /**/
    drawTransparentObjects();                                /**/

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


void drawTransparentObjects(void)
    {    
    GLfloat red[4] = { 1, 0, 0, 0.5 };
    GLfloat green[4] = { 0, 1, 0, 0.25 };

    glMaterialfv(GL_FRONT, GL_DIFFUSE, red);
    glPushMatrix();
     glTranslatef(-1.0, -1.0, -2.0);
     glutSolidCube(2.0);
    glPopMatrix();    
    
    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();    
    }



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 == ' ')
        drawbacks = !drawbacks;
    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();
    }
