/*****************************************************************
  textureAlpha.c
  by Dave Pape
  9 Feb 2003
  
  This program demonstrates using alpha with textures.
  It draws a solid sphere, with two different textured squares in
  front of it.  One square is drawn with a simple RGB texture,
  with a 50% transparent alpha defined in the square's material.
  The other square is drawn with an RGBA texture, where the alpha
  in the texture map varies from texel to texel; this gives the
  square varying transparency over its surface.

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

GLfloat viewRotY = 0, viewRotX = 0, cameraDistance = 5;

GLuint textureID1, textureID2;

void drawEverything(void);
void defineTextures(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);
    defineTextures();
    glutMainLoop();
    return 0;
    }


void drawEverything(void)
    {
    GLfloat opaqueWhite[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat transparentWhite[] = { 1.0, 1.0, 1.0, 0.5 };
    
    glClearColor(0.5, 0.7, 1.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(50.0, 1.0, 0.1, 100.0);
    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

    initLight();

    glTranslatef(0.0, 0.0, -cameraDistance);
    glRotatef(viewRotX, 1.0, 0.0, 0.0);
    glRotatef(viewRotY, 0.0, 1.0, 0.0);
    
    glPushMatrix();
     glTranslatef(0.0, 0.0, -5.0);
     glutSolidSphere(3.0, 16, 8);
    glPopMatrix();    

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glEnable(GL_TEXTURE_2D);

/*************** CODE OF INTEREST ******************************/
/*    Bind the RGBA texture to the first square.  The square's */
/*    material has a constant alpha of 1.                      */
/*                                                             */
    glMaterialfv(GL_FRONT, GL_DIFFUSE, opaqueWhite);         /**/
    glBindTexture(GL_TEXTURE_2D, textureID1);                /**/
    glNormal3f(0.0, 0.0, 1.0);
    glBegin(GL_QUADS);
     glTexCoord2i(0,0);
     glVertex3f(-2.0, -1.0, 0.0);
     glTexCoord2i(1,0);
     glVertex3f(-0.1, -1.0, 0.0);
     glTexCoord2i(1,1);
     glVertex3f(-0.1, 1.0, 0.0);
     glTexCoord2i(0,1);
     glVertex3f(-2.0, 1.0, 0.0);
    glEnd();

/*************** CODE OF INTEREST ******************************/
/*    Bind the RGB texture to the second square.  The square's */
/*    material has a constant alpha of 0.5.                    */
/*                                                             */
    glMaterialfv(GL_FRONT, GL_DIFFUSE, transparentWhite);    /**/
    glBindTexture(GL_TEXTURE_2D, textureID2);                /**/
    glNormal3f(0.0, 0.0, 1.0);
    glBegin(GL_QUADS);
     glTexCoord2i(0,0);
     glVertex3f(0.1, -1.0, 0.0);
     glTexCoord2i(1,0);
     glVertex3f(2.0, -1.0, 0.0);
     glTexCoord2i(1,1);
     glVertex3f(2.0, 1.0, 0.0);
     glTexCoord2i(0,1);
     glVertex3f(0.1, 1.0, 0.0);
    glEnd();

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);
    
    glutSwapBuffers();

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


#define IMAGE_HEIGHT 128
#define IMAGE_WIDTH 128
GLubyte textureImage[IMAGE_HEIGHT][IMAGE_WIDTH][4];

/*************** CODE OF INTEREST ******************************/
/*    Define the two textures.  One will be RGBA, the other    */
/*    just RGB.  Note that both textures use the same image    */
/*    data array, so both set their format to GL_RGBA.         */
/*    However, the second texture sets its internal format to  */
/*    RGB, so OpenGL will ignore the alpha data.               */
/*                                                             */
void defineTextures(void)
    {
    int i,j;
    for (i=0; i < IMAGE_HEIGHT; i++)
        for (j=0; j < IMAGE_WIDTH; j++)
            {
            textureImage[i][j][0] = (i>j) * 255;
            textureImage[i][j][1] = ((IMAGE_HEIGHT-i) > j) * 255;
            textureImage[i][j][2] = (j >= (IMAGE_WIDTH/2)) * 255;
            textureImage[i][j][3] = (int)((sin(i*0.2) * sin(j*0.2) + 1.0) * 127.5);
            }
    glGenTextures(1, &textureID1);
    glBindTexture(GL_TEXTURE_2D, textureID1);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_WIDTH, IMAGE_HEIGHT,
                0, GL_RGBA, GL_UNSIGNED_BYTE, textureImage);
    glGenTextures(1, &textureID2);
    glBindTexture(GL_TEXTURE_2D, textureID2);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_WIDTH, IMAGE_HEIGHT,
                0, GL_RGBA, GL_UNSIGNED_BYTE, textureImage);
    glBindTexture(GL_TEXTURE_2D, 0);
    }
/***************************************************************/


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

