/*****************************************************************
  lightmap2.cpp
  by Dave Pape
  2 April 2003

  This program is the completed example of lightmapping.  It
  takes the texgen method of lightmap1.cpp, and uses a texture
  specifically created for a lightmap.  The texture is a simple
  greyscale circular image, which is full white at the center
  and fades to black near the edges.  By applying this to the
  scene, with texture coordinates generated to place it directly
  under the moving light source, we get a good lighting effect
  without having to use lots of small polygons.

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

using namespace dms;

void createScene(Object& root);
void drawEverything(void);

void key(unsigned char k, int x, int y);
void specialkey(int k, int x, int y);
void idle(void);


PerspCamera camera;
Light light;
Object root;
QuadricObject *lightSource;
SimpleTransform lightPos;

/*************** CODE OF INTEREST ******************************/
/* Create the lightmap texture.                                */
/*                                                             */
Texture2D lightTex("lightcircle.tif", GL_CLAMP);


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);
    glutIdleFunc(idle);
    
    camera.setPosition(0, 3, 15);
    light.setInfinitePosition(-1, 1, 2);
    createScene(root);
    
    glutMainLoop();
    return 0;
    }


void createScene(Object& root)
    {
    QuadricObject *ball = new QuadricObject;
    ball->makeSphere(1.0, 32, 16);
    ball->setMaterial(*(new Material(Color::Red)));
    SimpleTransform *xform = new SimpleTransform;
    xform->setTranslation(-4, 0, 0);
    ball->setTransform(*xform);
    root.attach(*ball);
    
    Square *square = new Square(-10, -10, 10, 10, DMS_Y);
    square->setMaterial(*(new Material(Color::Green)));
    root.attach(*square);
        
    lightSource = new QuadricObject;
    lightSource->makeSphere(0.1, 8, 6);
    lightSource->setMaterial(*(new Material(Color::White)));
    lightSource->material().setEmission(Color::White);
    lightSource->setTransform(lightPos);
    
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
    
    light.setDiffuse(Color::Black);
    light.setAmbient(Color::White);
    }


void drawEverything(void)
    {
    glClearColor(0.5, 0.7, 1.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    
    camera.apply();

    float x = sin(currentTime())*5;
    float z = cos(currentTime())*2.5;
    lightPos.setTranslation(x, 2, z);
    light.setLocalPosition(x, 2, z);
    light.apply();

    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    GLfloat SplaneCoefficients[4] = { .1, 0, 0, 0 };
    GLfloat TplaneCoefficients[4] = { 0, 0, .1, 0 };
    SplaneCoefficients[3] = -x * 0.1 + 0.5;
    TplaneCoefficients[3] = -z * 0.1 + 0.5;
    glTexGenfv(GL_S, GL_EYE_PLANE, SplaneCoefficients);
    glTexGenfv(GL_T, GL_EYE_PLANE, TplaneCoefficients);
    
    lightTex.apply();
    
    root.drawAll();

    lightTex.disable();
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);

    lightSource->drawAll();

    glutSwapBuffers();

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


void key(unsigned char k, int x, int y)
    {
    if (k == 27)
        exit(0);
    }


void specialkey(int k, int x, int y)
    {
    if (k == GLUT_KEY_LEFT)
        camera.turn(3);
    else if (k == GLUT_KEY_RIGHT)
        camera.turn(-3);
    else if (k == GLUT_KEY_UP)
        camera.pitch(2);
    else if (k == GLUT_KEY_DOWN)
        camera.pitch(-2);
    else if (k == GLUT_KEY_HOME)
        camera.moveForward(0.25);
    else if (k == GLUT_KEY_END)
        camera.moveForward(-0.25);
    else if (k == GLUT_KEY_PAGE_UP)
        camera.zoom(-1);
    else if (k == GLUT_KEY_PAGE_DOWN)
        camera.zoom(1);
    }


void idle(void)
    {
    root.updateAll();
    glutPostRedisplay();
    }
