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

  This program is a first step toward using lightmapping.  In
  this case we start using texgen to apply an 'external' texture
  to objects in the scene, with coordinates based on the moving
  light's position.  The texture is an ordinary pattern-texture,
  rather than something that simulates lighting.

*****************************************************************/
#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 texture.  This texture will not be attached to   */
/* any objects directly, but simply applied to the entire      */
/* scene (this requires that none of the objects have textures */
/* themselves).                                                */
/*                                                             */
Texture2D lightTex("zebra.tif", GL_REPEAT);


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);
    
/*************** CODE OF INTEREST ******************************/
/* Set up texgen to work in eye (world) coordinates.           */
/*                                                             */
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
    
/*************** CODE OF INTEREST ******************************/
/* Here we make the light be a purely ambient light source, so */
/* that everything is rendered as if lighting were not in use. */
/* This is done just so that only the effect of the texture on */
/* object colors is visible, without GL lighting effects to    */
/* confuse things.                                             */
/*                                                             */
    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();

/*************** CODE OF INTEREST ******************************/
/* Compute & apply texgen coefficients that will cause the     */
/* texture to be applied in the X/Z plane, with an offset (the */
/* D coefficient) based on the X & Z coordinates of the light  */
/* source, so that the texture moves with the light.           */
/* The factor of .1 controls the size of the texture - a larger*/
/* value will make the texture appear smaller.                 */
/*                                                             */
    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;
    TplaneCoefficients[3] = -z * 0.1;
    glTexGenfv(GL_S, GL_EYE_PLANE, SplaneCoefficients);
    glTexGenfv(GL_T, GL_EYE_PLANE, TplaneCoefficients);
    
/*************** CODE OF INTEREST ******************************/
/* Enable the "light-map" texture, so that it will be applied  */
/* to everything.                                              */
/*                                                             */
    lightTex.apply();
    
    root.drawAll();

/*************** CODE OF INTEREST ******************************/
/* Turn off texgen & the texture before drawing anything else  */
/* that should not be affected by it (just the light source    */
/* marker, in this case).                                      */
/*                                                             */
    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();
    }
