/*****************************************************************
  crayoland.c
  by Dave Pape
  9 Feb 2003
  
  This program demonstrates creating textures with alpha from
  4-channel (RGBA) image files.
  It reads three image files - tree.sgi, cloud.sgi, and grass.sgi -
  and defines an RGBA texture map for each one.
  It then draws three squares, each one using one of the textures.
  The grass square is drawn without blending being enabled, so
  its alpha is ignored (in addition, as the image grass.sgi has no
  alpha channel, readSGIImage will make its alpha channel all 255
  (opaque), so it wouldn't appear transparent anyways).

*****************************************************************/
#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);
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);

/*************** CODE OF INTEREST ******************************/
/*    Define three textures, using the three image files.      */
/*                                                             */
    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();

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

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

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

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

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


/*************** CODE OF INTEREST ******************************/
/*    Define a new texture, loading the pixel data from the    */
/*    specified SGI format image file.  The texture will be    */
/*    RGBA format, so that it can include transparency.        */
/*                                                             */
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();
    }
