/*****************************************************************
  bergen1.cpp
  by Dave Pape
  25 Feb 2003

 An extension of bergen0.cpp.
 
 This program will dynamically change the amplitude of the sound  
 that we regard as coming from the teapot.  The sound's amplitude
 is inversely proportional to the distance between the view (the
 camera) and the teapot.
 
*****************************************************************/
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <dms/dms.h>
#include <bergenServer.h>
#include <bergenSample.h>

void drawEverything(void);
void updateBall(dms::Object&,void*);
void drawFloorMesh(dms::Object&,void*);
void drawTeapot(dms::Object&,void*);
void updateTeapot(dms::Object&,void*);

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


dms::PerspCamera camera;
dms::Light light;

dms::Vector4 green(0, 0.8, 0.1, 1);
dms::Vector4 red(0.8, 0, 0, 1);
dms::Vector4 white(1, 1, 1, 1);
dms::Vector4 grey(0.6, 0.6, 0.6, 1);
dms::Vector4 cyan(0, 1, 1, 1);
    
dms::Material floorMaterial(green);
dms::Material teapotMaterial(red, white, 90);
dms::Material pedestalMaterial(grey, white, 30);
dms::Material ballMaterial(cyan);

dms::Texture2D texture("texture.tif");

dms::SimpleTransform ballTransform;
dms::SimpleTransform pedestalTransform;
dms::SimpleTransform pedestalCapTransform;
dms::SimpleTransform teapotTransform;

dms::QuadricObject ball;
dms::QuadricObject pedestal;
dms::QuadricObject pedestalCap;
dms::Object teapot;
dms::Object floormesh;

    
bergenServer *soundserver;
bergenSample *bgsound, *bouncesound, *teapotsound;

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, 0, 10);
    light.setInfinitePosition(-5, 4, 1);

    floormesh.setMaterial(floorMaterial);
    floormesh.setDrawCallback(drawFloorMesh, (void*)32);

    pedestal.makeCylinder(4.0, 4.0, 4.0, 16, 1, DMS_Y);
    pedestal.setMaterial(pedestalMaterial);
    pedestalTransform.setTranslation(2.0, -2.0, -1.0);
    pedestal.setTransform(pedestalTransform);
    
    pedestalCap.makeDisk(0.0, 4.0, 16, 1, DMS_Y);
    pedestalCap.setMaterial(pedestalMaterial);
    pedestalCapTransform.setTranslation(4 * dms::Vector3::Y_Axis);
    pedestalCap.setTransform(pedestalCapTransform);
    
    teapot.setMaterial(teapotMaterial);
    teapot.material().diffuse()[3] = 0.75;
    teapot.setTexture(texture);
    teapot.setTransparency(dms::Transparency::StandardBlend);
    teapotTransform.setTranslation(5.5 * dms::Vector3::Y_Axis);
    teapot.setTransform(teapotTransform);
    teapot.setDrawCallback(drawTeapot);
    teapot.setUpdateCallback(updateTeapot);
    
    ball.makeSphere(2.0, 16, 8);
    ball.setMaterial(ballMaterial);
    ballTransform.setTranslation(-10.0, 0.0, -15.0);
    ball.setTransform(ballTransform);
    ball.setUpdateCallback(updateBall);

    floormesh.attach(ball);
    floormesh.attach(pedestal);
    pedestal.attach(pedestalCap);
    pedestal.attach(teapot);

    soundserver = new bergenServer;
    bgsound = new bergenSample("wind.wav", soundserver);
    bgsound->setLoop(1);
    bgsound->setAmplitude(0.15);
    bgsound->play();
    bouncesound = new bergenSample("boing.wav", soundserver);
/*************** CODE OF INTEREST ******************************/
/* Create a new sound object, for the continuous teapot sound. */
/* This plays all the time, but starts with an amplitude of 0, */
/* to make sure that it's not too loud before the update has a */
/* chance to adjust it.                                        */
/*                                                             */
    teapotsound = new bergenSample("squeak.wav", soundserver);
    teapotsound->setLoop(1);
    teapotsound->setAmplitude(0);
    teapotsound->play();
    
    glutMainLoop();
    return 0;
    }

   
void drawFloorMesh(dms::Object&,void* data)
    {
    int resolution = (int)data;
    int i,j;
    GLfloat x,z;
    glNormal3f(0.0, 1.0, 0.0);
    for (j = 0; j < resolution; j++)
        {
        glBegin(GL_TRIANGLE_STRIP);
        for (i = 0; i < resolution; i++)
            {
            x = (((float)i) / resolution) * 40.0 - 20.0;
            z = (((float)j) / resolution) * 40.0 - 20.0;
            glVertex3f(x, -2.0, z);
            z = (((float)j+1) / resolution) * 40.0 - 20.0;
            glVertex3f(x, -2.0, z);
            }
        glEnd();
        }
    }


void drawTeapot(dms::Object&,void *)
    {
    glutSolidTeapot(2.0);
    }


void updateTeapot(dms::Object& object,void *)
    {
    dms::SimpleTransform& xform = (dms::SimpleTransform&)object.transform();
    xform.setRotation(dms::currentTime() * 30.0, 0.0, 1.0, 0.0);
/*************** CODE OF INTEREST ******************************/
/* Compute the distance from the camera to the teapot (using   */
/* the teapot's transformation for its position), and adjust   */
/* the sound amplitude in inverse proportion to this distance  */
/* (making sure that it doesn't exceed 1.0).                   */
/*                                                             */
    float distance = camera.position().distance(xform.translation());
    if (distance <= 1)
        teapotsound->setAmplitude(1.0);
    else
        teapotsound->setAmplitude(1.0 / distance);
    }


void updateBall(dms::Object&,void *)
    {
    static float prevY = 0;
    float y = sin(dms::currentTime()) * 3.0;
    if (((y > 0) && (prevY < 0)) || ((y < 0) && (prevY > 0)))
        bouncesound->play();
    prevY = y;
    ballTransform.setTranslation(-10.0, fabs(y), -15.0);
    }


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();
    glEnable(GL_LIGHTING);
    light.apply();
    floormesh.drawAll();
    glDisable(GL_LIGHTING);

    glutSwapBuffers();

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


void key(unsigned char k, int x, int y)
    {
    if (k == 27)
        {
        delete soundserver;
        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)
    {
    floormesh.updateAll();
    glutPostRedisplay();
    }

