/* sample.c
/*    An example CAVE program, featuring navigation and interaction.
*/

#include <cave_ogl.h>
#include <GL/glu.h>

/* The data that will be shared between processes */
struct _balldata
	{
	float x,y,z;
	float radius;
	int grabbed;
	struct _balldata *next;
	};

void init_gl(void),draw_balls(struct _balldata **);
void check_add(struct _balldata **list);
struct _balldata * update_held(struct _balldata *ball);
struct _balldata * check_grab(struct _balldata **list);
void navigate(void);

main(int argc,char **argv)
{
 struct _balldata **ballList,*heldBall=NULL;
 CAVEConfigure(&argc,argv,NULL);
 ballList = (struct _balldata **) CAVEMalloc(sizeof(struct _balldata *));
 *ballList = NULL;
 CAVEInit();
 CAVEInitApplication(init_gl,0);
 CAVEDisplay(draw_balls,1,ballList);
 while (!CAVEgetbutton(CAVE_ESCKEY))
	{
	navigate();
	check_add(ballList);
	if (heldBall)
		heldBall = update_held(heldBall);
	else
		heldBall = check_grab(ballList);
	sginap(1);
	}
 CAVEExit();
}


#define SPEED 5.0f  /* Max navigation speed in feet per second */

/* navigate - perform the navigation calculations. */
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.125)
	{
	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.125)
	CAVENavRot(-jx * 90.0f * dt,'y');
}


/* check_add - if button 1 is released, create a new ball and add it to the list */
void check_add(struct _balldata **list)
{
 if (CAVEButtonChange(1) == -1)
	{
	struct _balldata *ball;
	float wandPos[3],wandFront[3];
	ball = (struct _balldata *) CAVEMalloc(sizeof(struct _balldata));
	if (!ball)
		{
		fprintf(stderr,"OUT OF MEMORY!\n");
		return;
		}
	CAVEGetPosition(CAVE_WAND_NAV,wandPos);
	CAVEGetVector(CAVE_WAND_FRONT_NAV,wandFront);
	ball->x = wandPos[0] + wandFront[0]*2;
	ball->y = wandPos[1] + wandFront[1]*2;
	ball->z = wandPos[2] + wandFront[2]*2;
	ball->radius = 0.5 + drand48();
	ball->grabbed = 0;
	ball->next = *list;
	*list = ball;
	}
}


/* update_help - when button 2 is released, move the held ball to the wand's position.
	Returns NULL if the ball is released, or the ball pointer if it is still held. */
struct _balldata * update_held(struct _balldata *ball)
{
 if (!CAVEBUTTON2)
	{
	float wandPos[3];
	CAVEGetPosition(CAVE_WAND_NAV,wandPos);
	ball->x = wandPos[0];
	ball->y = wandPos[1];
	ball->z = wandPos[2];
	ball->grabbed = 0;
	return NULL;
	}
 return ball;
}


/* check_grab - if button 2 is pressed while the wand is inside a ball, grab that ball.
	The pointer to the grabbed ball is returned. */
struct _balldata * check_grab(struct _balldata **list)
{
#define SQR(x) ((x)*(x))
 if (CAVEBUTTON2)
	{
	struct _balldata *ball;
	float wandPos[3];
	CAVEGetPosition(CAVE_WAND_NAV,wandPos);
	for (ball = *list; ball; ball = ball->next)
		{
		float distSquared;
		distSquared = SQR(ball->x - wandPos[0]) + SQR(ball->y - wandPos[1]) + 
			SQR(ball->z - wandPos[2]);
		if (distSquared < SQR(ball->radius))
			{
			ball->grabbed = 1;
			return ball;
			}
		}
	}
 return NULL;
}




static GLUquadricObj *sphereObj;

/* init_gl - initialize GL lighting & materials, and create GLU object for the balls */
void init_gl(void)
{
 float greenMaterial[] = { 0, 1, 0, 1 };
 glEnable(GL_LIGHT0);
 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, greenMaterial);
 sphereObj = gluNewQuadric();
}


/* draw_balls - draw the balls, using the shared data for their positions & sizes */
void draw_balls(struct _balldata **ballList)
{
 struct _balldata *ball;

 glClearColor(0., 0., 0., 0.);
 glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
 glEnable(GL_LIGHTING);

 CAVENavTransform();

 for (ball = *ballList; ball; ball = ball->next)
	if (ball->grabbed)
		{
		float wandPos[3];
		glPushMatrix();
		 CAVEGetPosition(CAVE_WAND_NAV, wandPos);
		 glTranslatef(wandPos[0], wandPos[1], wandPos[2]);
		 gluSphere(sphereObj, ball->radius, 8, 8);
		glPopMatrix();
		}
	else
		{
		glPushMatrix();
		 glTranslatef(ball->x, ball->y, ball->z);
		 gluSphere(sphereObj, ball->radius, 8, 8);
		glPopMatrix();
		}

 glDisable(GL_LIGHTING);
}
