#include <GL/gl.h>
#include <GL/glut.h>
#include <math.h>

/* TEST PROGRAM */

#define CHECK_ERROR(str)                                           \
{                                                                  \
    GLenum error;                                                  \
    if(error = glGetError())                                       \
       printf("GL Error: %s (%s)\n", gluErrorString(error), str);  \
}

enum {SPHERE = 1, CONE};

/*
** Create a single component texture map
*/
GLfloat *make_texture(int maxs, int maxt)
{
    int s, t;
    GLfloat *texture;

    texture = (GLfloat *)malloc(maxs * maxt * sizeof(GLfloat));
    for(t = 0; t < maxt; t++) {
	for(s = 0; s < maxs; s++) {
	    texture[s + maxs * t] = ((s >> 3) & 0x1) ^ ((t >> 3) & 0x1);
	}
    }
    return texture;
}


/* Called when window needs to be redrawn */
void redraw()
{
    /* material properties for objects in scene */
    static GLfloat wall_mat[] = {1.f, 1.f, 1.f, 1.f};

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    /*
    ** Note: wall verticies are ordered so they are all front facing
    ** this lets me do back face culling to speed things up.
    */
 
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, wall_mat);
    glColor3f(1.f, 1.f, 1.f);


    glEnable(GL_TEXTURE_2D);

    glBegin(GL_QUADS);

    /* floor */

    /* glTexGen creates texture coordinates */
    glNormal3f(0.f, 1.f, 0.f);
    glVertex3f(-100.f, -100.f,  100.f);
    glVertex3f( 100.f, -100.f,  100.f);
    glVertex3f( 100.f, -100.f, -100.f);
    glVertex3f(-100.f, -100.f, -100.f);

    glEnd();

    glDisable(GL_TEXTURE_2D);

    glBegin(GL_QUADS);

    /* left wall */
    glNormal3f(1.f, 0.f, 0.f);
    glVertex3f(-100.f, -100.f,  100.f);
    glVertex3f(-100.f, -100.f, -100.f);
    glVertex3f(-100.f,  100.f, -100.f);
    glVertex3f(-100.f,  100.f,  100.f);


    /* right wall */
    glNormal3f(-1.f, 0.f, 0.f);
    glVertex3f( 100.f, -100.f,  100.f);
    glVertex3f( 100.f,  100.f,  100.f);
    glVertex3f( 100.f,  100.f, -100.f);
    glVertex3f( 100.f, -100.f, -100.f);


    /* ceiling */
    glColor3f(0.f, .5f, 1.f);
    glNormal3f(0.f, -1.f, 0.f);
    glVertex3f(-100.f,  100.f,  100.f);
    glVertex3f(-100.f,  100.f, -100.f);
    glVertex3f( 100.f,  100.f, -100.f);
    glVertex3f( 100.f,  100.f,  100.f);


    /* back wall */
    glColor3f(.2f, .5f, 1.f);
    glNormal3f(0.f, 0.f, 1.f);
    glVertex3f(-100.f, -100.f, -100.f);
    glVertex3f( 100.f, -100.f, -100.f);
    glVertex3f( 100.f,  100.f, -100.f);
    glVertex3f(-100.f,  100.f, -100.f);
    glEnd();


    /* draw the sphere */
    glColor3f(1.f, .5f, 0.f);
    glPushMatrix();
    glTranslatef(0.f, -80.f, -80.f);
    glCallList(SPHERE);
    glPopMatrix();

    /* draw the cone */
    glColor3f(.5f, 1.f, 0.f);
    glPushMatrix();
    glTranslatef(-60.f, -100.f, -5.f);
    glCallList(CONE);
    glPopMatrix();

    CHECK_ERROR("OpenGL Error in redraw()");

    glFlush(); /* some OpenGL implementations need this if not animating */
}


void key(unsigned char key, int x, int y)
{
    if(key == '\033')
	exit(0);
}



main(int argc, char *argv[])
{
    GLfloat *tex;
    static GLfloat lightpos[]   = {50.f, 50.f, 0.f, 1.f};

    static GLfloat sphere_mat[] = {1.f, .5f, 0.f, 1.f};
    static GLfloat cone_mat[]   = {0.f, .5f, 1.f, 1.f};
    static GLfloat tex_plane_s[]  = {1/200.f, 0.f, 0.f, 100.f};
    static GLfloat tex_plane_t[]  = {0.f, 0.f, 1/200.f, 100.f};

    GLUquadricObj *sphere, *cone, *base;
    int maxsize;

    glutInit(&argc, argv);
    glutInitWindowSize(512, 512);
    glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH);
    (void)glutCreateWindow("room using texgen to get texture coordinates");
    glutDisplayFunc(redraw);
    glutKeyboardFunc(key);

    /* draw a perspective scene */
    glMatrixMode(GL_PROJECTION);
    glFrustum(-100., 100., -100., 100., 300., 600.); 
    glMatrixMode(GL_MODELVIEW);
    /* look at scene from (0, 0, 400) */
    gluLookAt(0., 0., 400., 0., 0., 0., 0., 1., 0.);

    /* turn on depth buffering */
    glEnable(GL_DEPTH_TEST);

    /* turn on lighting */
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    /* place light 0 in the right place */
    glLightfv(GL_LIGHT0, GL_POSITION, lightpos);

    /* remove back faces to speed things up */
    glCullFace(GL_BACK);


    /* makes texturing faster, and looks better than GL_LINEAR */
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    /* use object space coordinates */
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

    /* map -100 -> 100 to 0 -> 1 for x to s and z to t */
    glTexGenfv(GL_S, GL_OBJECT_PLANE, tex_plane_s);
    glTexGenfv(GL_T, GL_OBJECT_PLANE, tex_plane_t);

    /* turn on texgen */
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);

    /* make a display list containing a sphere */
    glNewList(SPHERE, GL_COMPILE);
    sphere = gluNewQuadric();
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, sphere_mat);
    gluSphere(sphere, 20.f, 20, 20);
    gluDeleteQuadric(sphere);
    glEndList();

    /* create a display list containing a cone */
    glNewList(CONE, GL_COMPILE);
    cone = gluNewQuadric();
    base = gluNewQuadric();
    glRotatef(-90.f, 1.f, 0.f, 0.f);
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cone_mat);
    gluQuadricOrientation(base, GLU_INSIDE);
    gluDisk(base, 0., 20., 20, 1);
    gluCylinder(cone, 20., 0., 60., 20, 20);
    gluDeleteQuadric(cone);
    gluDeleteQuadric(base);
    glEndList();

    /* load pattern for current 2d texture */
    tex = make_texture(128, 128);
    glTexImage2D(GL_TEXTURE_2D, 0, 1, 128, 128, 0, GL_RED, GL_FLOAT, tex);
    free(tex);
    CHECK_ERROR("end of main");
    glutMainLoop();
}

