/* This is the template of a CAVE program
 *      using the "CAVE TooLs" calls
 *
 *       written by
 *      Jim Costigan
 *   costigan@evl.uic.edu
 *
 *  Last modified 10/5/98
 *
 * Notations are included within the code
 * Tools calls are in RED
 */
 
 
//stock includes
#include <cave_ogl.h>
#include <malloc.h>
#include <GL/gl.h> S
#include <GL/glu.h>
#include <iostream.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>

//includes necessary for CAVE TooLs

#include "Tools.h"

// Global Variables and #defines

#define SPEED 5.0f  // Max navigation speed in feet per second
#define TRUE 1
#define FALSE 0
 

// These # defines help to make the code more readable
#define red 0
#define green 1
#define blue 2
#define detail 3
#define shading 4
 
 
 

//create an array of pointers to the Tools class
Tools *tool[8];

//you can also create pointers to the individual classes
Slider *Size;

GLuint shiny_material=11;
 

// Fuinction definitions
void navigate(void);
void init_gl(void);
void draw_it (void);
void calculate(void);
void floor (void);
void Sphere (void);
void lighting (void);
void Lables (void);
void Dot (void);
 

 main(int argc, char **argv)
{
// Initialize the CAVE
 CAVEConfigure(&argc,argv,NULL);
 
 // You can create sliders anywhere,
 // Arguments are ( X position, Y position, Z position, Trigger distance)
 // or ( X position, Y position, Z position )
 // Trigger distance is how far away the trigger function will work in feet
 // the default is 15 feet
 
 tool[red] = new Slider (2.0,  2.0,  -2.0);
 tool[green] = new Slider (2.5,  2.0,  -2.0);
 tool[blue] = new Slider (3.0, 2.0,  -2.0);
 Size = new Slider (3.5,  2.0,  -2.0);
 tool[detail]= new Knob (4.0,  3.2,  -5.0,  9.0);
 tool[shading]= new Button (4.0,  3.8,  -5,  9.0);
 
 
 //You can input any scale value greater than 0
 tool[red]->scale(0.5);
 tool[green]->scale(1.0);
 tool[blue]->scale(1.25);

 // You can have objects pasted to a CAVE position
 // instead of being in the world navigation set
 // 1 is true all other arguments are ignored
 tool[shading]->NonNav (1);
 tool[detail]->NonNav(TRUE);
 
 
  //  SliderRotate, ButtonRotate and KnobRotate
 //   are the only rotates that the intersection look up knows about
 //   Rotations done in the draw process
 //   such as glRotate are not recognised

  for (int i = 0; i < 2; i++)
     tool[i]->SliderRotate (45,  0 , 0);
 
 Size->SliderRotate (45,  0 , 0);
 tool[shading]->ButtonRotate(90,0,0);
 tool[detail]->SliderRotate(90,0,0);
 
 //You can change the button that activates the object
 // by calling selection button.  The numbers called correspond
 //  to the CAVEBUTTONS.  2 is the default and 1 - 4 is valid
 
 tool[red]->selection_button (1);
 tool[blue]->selection_button (3);
 
 tool[red]->set_start(75);
 tool[blue]->set_start(.75);
 tool[detail]->set_start(.50);
 
 CAVEInit();
 CAVEInitApplication(lighting, 0);
 CAVEDisplay(draw_it,0);
 while (!CAVEgetbutton(CAVE_ESCKEY))
 {
 navigate();
 calculate();
 sginap(1);
 }
 
 // My own little exit
 cout << " \n Wasn't that fun!!! \n \n";
 CAVEExit();

}
 

 
 //  Standard Navigation
 void navigate(void)
{
 float jx=CAVE_JOYSTICK_X,jy=CAVE_JOYSTICK_Y,dt,t;
 static float prevtime = 0;
 t = CAVEGetTime();
 dt = t - prevtime;
 prevtime = t;
 if (fabs(jy)>0.2)
 {
 float wandFront[3];
 CAVEGetVector(CAVE_WAND_FRONT,wandFront);
 CAVENavTranslate(wandFront[0]*jy*SPEED*dt, wandFront[1]*jy*SPEED*dt,
   wandFront[2]*jy*SPEED*dt);
 }
 if (fabs(jx)>0.2)
 CAVENavRot(-jx*90.0f*dt,'y');
}
 
 
 

void draw_it()
{
 
 
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // All of the Tools classes pay attention to the NonNav() agument
 // If the argument is true then they will not be drawn under the CAVENavTransform
 // All Tools should therefore be drawn outside the CAVENav Transform

 
 
 glPushMatrix();

 glEnable(GL_LIGHT0);
 glEnable(GL_LIGHTING);
 
 glPushMatrix();

 tool[red]->draw();
 tool[green]->draw();
 tool[blue]->draw();
 Size->draw();
 tool[red]->Pointer();
 tool[shading]->draw();
 tool[detail]->face_plate();
 tool[detail]->draw();

 glPopMatrix();
 
 
 glPushMatrix();
 CAVENavTransform();
 glPushMatrix();
 glDisable(GL_LIGHTING);
 glTranslatef (0,  2,  -8 );

 // The default value's of knob's and sliders are from 0 to 1
 // and can be called anywhere
 glColor3f (tool[red]->value(), tool[green]->value(),tool[blue]->value());
 
 if (tool[shading]->value()){
  glEnable(GL_LIGHTING);
  glCallList(shiny_material);
 }
 
 Sphere();
 
 Lables (); //makes the lables for the sliders
 glPopMatrix();
 glPopMatrix();
 
 glPushMatrix();
 glDisable(GL_LIGHTING);
 floor ();
 glEnable(GL_LIGHTING);
 glPopMatrix();
 glPopMatrix();

 glDisable(GL_LIGHTING);
 
}
 
 
 

void calculate ()
 {
 
 // if you don't include a value call somewhere
 // the object will not check for intersection
 // and no bounding sphere will be drawn

 // This little bit of code allows you to output the values
 // of the variables when the "V" button is pressed.
 // It is pretty handy when trying to find the corret color
 
  if (CAVEgetbutton(CAVE_VKEY)) {
  static float time = 0;    //keeps the buffer from repeating a bunch of time
if ((*CAVETime - time) > 0.5){
                         time = *CAVETime;
                         cout << "Red = "   << tool[red]->value() << " Green = " << tool[green]->value() << " Blue = "  << tool[blue]->value() << " LOD = " << tool[detail]->value()*50 + 10 << endl << endl;
                    }
                 }

 
 
    }
 
 
//
//  The rest of this code creates the sphere and dot's that are used in the environment
//  environment.  No slider, knob or button calls happen below.
//

void Sphere (void)
 {
 float surface;
  GLUquadricObj* qobj = gluNewQuadric();
 gluQuadricNormals(qobj,  GLU_SMOOTH);
 surface = tool[detail]->value()*50 + 10;
 glPushMatrix();
  gluSphere(qobj, ( Size->value()* 4 +1),  surface , surface);
  glPopMatrix();
 glPopMatrix();
 }
 
void Lables (void){

 
  glPushMatrix();
   glDisable(GL_LIGHTING);
   glColor3f(0.9,  0.0,  0.0);
   glTranslatef(2,  2.73333,  -2.6);
   Dot();
  glPopMatrix();
  glPushMatrix();
   glColor3f(0.0,  1.0,  0.0);
   glTranslatef(2.5,  2.73333,  -2.6);
   Dot();
  glPopMatrix();
  glPushMatrix();
   glColor3f(0.0,  0.0,  0.9);
   glTranslatef(3.0,  2.73333,  -2.6);
   Dot();
  glPopMatrix();
  glPushMatrix();
   glColor3f(0.25,  0.25,  0.25);
   glTranslatef(3.4,  2.73333,  -2.6);
   glScalef(0.75, 0.75, 0.75);
   Dot();
  glPopMatrix();
  glPushMatrix();
   glColor3f(0.26,  0.26,  0.26);
   glTranslatef(3.55, 2.73333,  -2.6);
   glScalef(1.5, 1.5, 1.5);
   Dot();
  glPopMatrix();
  glEnable(GL_LIGHTING);
}
 
 
void Dot (void)
 {
  GLUquadricObj* qobj = gluNewQuadric();
  gluSphere(qobj, 0.05,  6,  6);
   }
 
void floor (void)
 {
 glPushMatrix();
   glDisable(GL_LIGHTING);
   glColor3f(0.4,  0.4,  0.4);
   glTranslatef (0,  -13,  0 );
   glRotatef(90,  1.0,  0.0,  0.0);
    glRectf(-100,-100,  100, 100);
      glEnable(GL_LIGHTING);
 glPopMatrix();
 }

void lighting (void)
 {
  GLfloat matnomat[]={0.0, 0.0, 0.0, 1.0};
  GLfloat matspec[]={1.0, 1.0, 1.0, 1.};
 
  //shiny lighting
    glNewList(shiny_material,GL_COMPILE);
 
   glMaterialfv(GL_FRONT, GL_AMBIENT, matnomat);
   glMaterialfv(GL_FRONT, GL_SPECULAR, matspec);
   glMaterialf(GL_FRONT, GL_SHININESS,  100.);
   glMaterialfv(GL_FRONT, GL_EMISSION, matnomat);
   glColorMaterial(GL_FRONT, GL_DIFFUSE);
   glEnable(GL_COLOR_MATERIAL);
  glEndList();
 
}