/*****************************************************************
  transparent.c
  by Dave Pape
  9 Feb 2003
  
  This program is a first demonstration of transparency in OpenGL.
  It draws an opaque sphere, with a 50% opaque red cube and a
  25% opaque green cone in front of it.  By enabling alpha
  blending, the cube and cone's colors will be blended with the
  sphere's, and they will appear transparent.
  
  New functions shown here:
        - glEnable(GL_BLEND)
        - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

*****************************************************************/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <GL/glut.h>
#include <GL/gl.h>

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

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)
    {
/*************** CODE OF INTEREST ******************************/
/*      Here we assign the red & green colors alphas to        */
/*        make them transparent.                               */
    GLfloat red[4] = { 1, 0, 0, 0.5 };                       /**/
    GLfloat green[4] = { 0, 1, 0, 0.25 };                    /**/
    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();

/*************** CODE OF INTEREST ******************************/
/*      Here we enable blending, and define the "standard"     */
/*        blending function for transparency.                  */
    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 ******************************/
/*      Here we apply the transparent red material.            */
    glMaterialfv(GL_FRONT, GL_DIFFUSE, red);                 /**/
    glPushMatrix();
     glTranslatef(-1.0, -1.0, -2.0);
     glutSolidCube(2.0);
    glPopMatrix();    
    
/*************** CODE OF INTEREST ******************************/
/*      Here we apply the transparent green material.          */
    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();    

    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;
    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();
    }
