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

  This program is a preliminary example for lightmapping.  The
  program does *not* use lightmaps; it simply uses an ordinary,
  local GL light source.
  The scene consists of a sphere on a large plane (a single quad),
  with the local light moving in a circle over the plane.  The
  sphere is lit properly, because its polygons a relatively small.
  The lighting of the plane does not look as good, because the
  lighting calculations are only done at the vertices, which are
  far from the light, and so do not produce a good result for the
  region of the plane close to the light source.
  For the ground plane to be lit better in this example, we would
  have to render it as a mesh of smaller triangles, as in the
  'attenuate.c' example from Class 6.

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


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);
    
    lightSource = new QuadricObject;
    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->makeSphere(0.1, 8, 6);
    lightSource->setMaterial(*(new Material(Color::White)));
    lightSource->material().setEmission(Color::White);
    lightSource->setTransform(lightPos);
    }


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

/*************** CODE OF INTEREST *********************************/
/* Move the light in a circle, just above the ground plane.       */
/* This part will continue to be used in the subsequent examples. */
/*                                                                */
    float x = sin(currentTime())*5;
    float z = cos(currentTime())*2.5;
    lightPos.setTranslation(x, 2, z);
    light.setLocalPosition(x, 2, z);
    light.apply();
    
    root.drawAll();
    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();
    }
