/*****************************************************************
  crayoland2.c
  by Dave Pape
  9 Feb 2003
  
  This program demonstrates the use of alpha testing for
  transparency, in place of blending.
  The objects rendered are exactly the same as in crayoland.c,
  the only change is that GL_ALPHA_TEST & glAlphaFunc are used
  instead of GL_BLEND and glBlendFunc.
  
  New functions shown here:
    - glEnable(GL_ALPHA_TEST)
    - glAlphaFunc()

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

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

GLuint treeTextureID, cloudTextureID, grassTextureID;

void drawEverything(void);
void drawTree(void);
GLuint defineTexture(char *filename);
void readSGIImage(char *filename,int *xdim,int *ydim,unsigned long **image);
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);
    treeTextureID = defineTexture("tree.sgi");
    cloudTextureID = defineTexture("cloud.sgi");
    grassTextureID = defineTexture("grass.sgi");
    glutMainLoop();
    return 0;
    }


void drawEverything(void)
    {
    GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };

    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, -1.0, -cameraDistance);
    glRotatef(viewRotX, 1.0, 0.0, 0.0);
    glRotatef(viewRotY, 0.0, 1.0, 0.0);
    
    glEnable(GL_TEXTURE_2D);

    glMaterialfv(GL_FRONT, GL_DIFFUSE, white);

    glBindTexture(GL_TEXTURE_2D, grassTextureID);
    glNormal3f(0.0, 1.0, 0.0);
    glBegin(GL_QUADS);
     glTexCoord2i(0,0);
     glVertex3f(-10.0, 0.0, -10.0);
     glTexCoord2i(1,0);
     glVertex3f(10.0, 0.0, -10.0);
     glTexCoord2i(1,1);
     glVertex3f(10.0, 0.0, 10.0);
     glTexCoord2i(0,1);
     glVertex3f(-10.0, 0.0, 10.0);
    glEnd();


/*************** CODE OF INTEREST ******************************/
/*    Enable the alpha test, and set it to accept any pixels   */
/*    that have an alpha > 0.25                                */
/*                                                             */
    glEnable(GL_ALPHA_TEST);                                 /**/
    glAlphaFunc(GL_GREATER, 0.25);                           /**/

    glBindTexture(GL_TEXTURE_2D, cloudTextureID);
    glNormal3f(0.0, 0.0, 1.0);
    glBegin(GL_QUADS);
     glTexCoord2i(0,0);
     glVertex3f(1.0, 3.0, -5.0);
     glTexCoord2i(1,0);
     glVertex3f(5.0, 3.0, -5.0);
     glTexCoord2i(1,1);
     glVertex3f(5.0, 5.0, -5.0);
     glTexCoord2i(0,1);
     glVertex3f(1.0, 5.0, -5.0);
    glEnd();

    drawTree();
    
    glPushMatrix();
     glTranslatef(-1.0, 0.0, -2.0);
     drawTree();
    glPopMatrix();
    
    glPushMatrix();
     glTranslatef(1.0, 0.0, -6.0);
     drawTree();
    glPopMatrix();
    
    glPushMatrix();
     glTranslatef(2.0, 0.0, -3.0);
     drawTree();
    glPopMatrix();
    
    glDisable(GL_TEXTURE_2D);


/*************** CODE OF INTEREST ******************************/
/*    Disable the alpha test when we're done with it           */
/*                                                             */
    glDisable(GL_ALPHA_TEST);                                /**/
    
    glutSwapBuffers();

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


void drawTree(void)
    {
    glBindTexture(GL_TEXTURE_2D, treeTextureID);
    glNormal3f(0.0, 0.0, 1.0);
    glBegin(GL_QUADS);
     glTexCoord2i(0,0);
     glVertex3f(-1.0, 0.0, 0.0);
     glTexCoord2i(1,0);
     glVertex3f(1.0, 0.0, 0.0);
     glTexCoord2i(1,1);
     glVertex3f(1.0, 3.0, 0.0);
     glTexCoord2i(0,1);
     glVertex3f(-1.0, 3.0, 0.0);
    glEnd();
    }


GLuint defineTexture(char *filename)
    {
    GLuint textureID;
    int xdim, ydim;
    unsigned long *image;
    readSGIImage(filename, &xdim, &ydim, &image);
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, xdim, ydim,
                      GL_RGBA, GL_UNSIGNED_BYTE, image);
    glBindTexture(GL_TEXTURE_2D, 0);
    return textureID;
    }


void readSGIImage(char *filename,int *xdim,int *ydim,unsigned long **image)
    {
    unsigned short *img_rbuf, *img_gbuf, *img_bbuf, *img_abuf;
    IMAGE *imagefp;
    int x,y;
    unsigned long *p;
    if ((imagefp=iopen(filename,"r")) == NULL)
        {
        fprintf(stderr,"createTexture: can't open input file %s\n",
            filename);
        exit(1);
        }
    *xdim = imagefp->xsize;
    *ydim = imagefp->ysize;
    img_rbuf = (unsigned short *) malloc(imagefp->xsize*sizeof(unsigned short));
    img_gbuf = (unsigned short *) malloc(imagefp->xsize*sizeof(unsigned short));
    img_bbuf = (unsigned short *) malloc(imagefp->xsize*sizeof(unsigned short));
    img_abuf = (unsigned short *) malloc(imagefp->xsize*sizeof(unsigned short));
    p = *image = (unsigned long *) malloc((*xdim)*(*ydim)*sizeof(unsigned long));
    for (y=0; y < imagefp->ysize; y++)
        {
        getrow(imagefp,img_rbuf,y,0);
        getrow(imagefp,img_gbuf,y,1);
        getrow(imagefp,img_bbuf,y,2);
        if (imagefp->zsize > 3)
            {
            getrow(imagefp,img_abuf,y,3);
            for (x=0; x < imagefp->xsize; x++)
                *p++ = img_rbuf[x] | (img_gbuf[x]<<8) |
                    (img_bbuf[x]<<16) | (img_abuf[x]<<24);
            }
        else
            {
            for (x=0; x < imagefp->xsize; x++)
                *p++ = img_rbuf[x] | (img_gbuf[x]<<8) |
                    (img_bbuf[x]<<16) | (0xff000000);
            }
        }
    iclose(imagefp);
    free(img_abuf);
    free(img_bbuf);
    free(img_gbuf);
    free(img_rbuf);
    }


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


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