
/* Parts Copyright (c) Mark J. Kilgard, 1997. */

/* tween.c
   Hack job to ilustrate 3D linear tweening between a cube and a tetrahedron 
*/

#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <GL/glut.h>

GLfloat light_diffuse[] = {1.0, 0.0, 0.0, 1.0};  /* Red diffuse light. */
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};  /* Infinite light location. */

GLfloat XAngle = 0;
GLfloat YAngle = 0;
GLfloat ZAngle = 0;

GLfloat t = 0.0;
GLfloat Deltat = 0.1;

/* points to display */
GLfloat V[8][3];

/* vertices at t = 0 - currently a cube */
GLfloat V1[8][3] = 
    {  { 1.0,  1.0,  1.0}, 
	   { 1.0, -1.0,  1.0}, 
       { -1.0, -1.0,  1.0},
       { -1.0,  1.0,  1.0},  
       { -1.0, 1.0, -1.0}, 
       { -1.0, -1.0, -1.0}, 
       {  1.0,-1.0, -1.0},   
       {  1.0, 1.0, -1.0} };

/* vertices at t = 1 - currently a tetrahedron (skew) */      
GLfloat V2[8][3] = 
      {  { 0.0,  0.0,  1.0}, 
	   { 0.0, 0.0,  1.0}, 
       { 0.0, 0.0,  1.0},
       { 0.0,  0.0,  1.0}, 
       { -1.0, 1.0, -1.0}, 
       { -1.0, -1.0, -1.0}, 
       {  0.0, 0.0, -1.0},   
       {  1.0, 1.0, -1.0} };
      
      
    
void
stroke_output(GLfloat Scale, GLfloat x, GLfloat y, GLfloat z, char *format,...)
{
  va_list args;
  char buffer[200], *p;

  va_start(args, format);
  vsprintf(buffer, format, args);
  va_end(args);
  glPushMatrix();
  glTranslatef(x, y, z);
  glScalef(0.005*Scale, 0.005*Scale, 0.005*Scale);
  for (p = buffer; *p; p++)
    glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
  glPopMatrix();
}


void
drawAxes(void) 
{

  glDisable(GL_LIGHTING);
   glBegin(GL_LINES);
   glColor3f(1.0, 1.0, 1.0);
   glVertex3f(-2.0, 0.0, 0.0);
   glVertex3f( 2.0, 0.0, 0.0);
   glVertex3f( 0.0, -2.0, 0.0);
   glVertex3f( 0.0,  2.0, 0.0);
   glVertex3f( 0.0, 0.0, -2.0);
   glVertex3f( 0.0, 0.0, 2.0);
   glEnd();
   
   stroke_output(0.5, 2.0, 0.0, 0.0, "X");
   stroke_output(0.5, 0.0, 2.0, 0.0, "Y");
   stroke_output(0.5, 0.0, 0.0, 2.0, "Z");
}

GLfloat * MakeNormal( GLfloat V1[3], GLfloat V2[3], GLfloat V3[3]) {

/* Purpose: given vertices of a triangle (or 3 vertices of a larger polygon),
		computes the normal of that triangle */

GLfloat N[3], A[3], B[3];

A[0] = V2[0] - V1[0];
A[1] = V2[1] - V1[1];
A[2] = V2[2] - V1[2];

B[0] = V3[0] - V1[0];
B[1] = V3[1] - V1[1];
B[2] = V3[2] - V1[2];

N[0] = A[1] * B[2] - A[2] * B[1];
N[1] = A[2] * B[0] - A[0] * B[2];
N[2] = A[0] * B[1] - A[1] * B[0];

return N;

}

void
drawBox(void)
{
  

  glEnable(GL_LIGHTING);
    glBegin(GL_QUADS);
    /*glNormal3f(0.0,0.0,-1.0); Front side 1 2 3 4*/
    glNormal3fv(MakeNormal(V[0],V[1],V[2]));
    glVertex3fv(V[0]); glVertex3fv(V[1]);
    glVertex3fv(V[2]); glVertex3fv(V[3]);
	/* glNormal3f(-1.0, 0.0, 0.0); /*Left side 4 5 6 3*/
    glNormal3fv(MakeNormal(V[3],V[4],V[5]));
    glVertex3fv(V[3]); glVertex3fv(V[4]);
    glVertex3fv(V[5]); glVertex3fv(V[2]);  
	/* glNormal3f(1.0, 0.0, 0.0); /*Right side 8 1 2 7*/
    glNormal3fv(MakeNormal(V[7],V[0],V[1]));
    glVertex3fv(V[7]); glVertex3fv(V[0]);
    glVertex3fv(V[1]); glVertex3fv(V[6]);
 	/* glNormal3f(0.0, 0.0, -1.0); /*Back side 5 6 7 8*/
    glNormal3fv(MakeNormal(V[4],V[5],V[6]));
    glVertex3fv(V[4]); glVertex3fv(V[5]);
    glVertex3fv(V[6]); glVertex3fv(V[7]);
  	/* glNormal3f(0.0, 1.0, 0.0); /*Top side 8 5 4 1*/
    glNormal3fv(MakeNormal(V[7],V[4],V[3]));
    glVertex3fv(V[7]); glVertex3fv(V[4]);
    glVertex3fv(V[3]); glVertex3fv(V[0]);
   	/* glNormal3f(0.0, -1.0, 0.0); /*Bottom side 2 3 6 7*/
    glNormal3fv(MakeNormal(V[1],V[2],V[5]));
    glVertex3fv(V[1]); glVertex3fv(V[2]);
    glVertex3fv(V[5]); glVertex3fv(V[6]);;
    glEnd();

}

void
display(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    /* Adjust cube position to be asthetic angle. */
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(0, 0, 5.0,  /* eye is at (0,0,5) */
    0.0, 0.0, 0.0,      /* center is at (0,0,0) */
    0.0, 1.0, 0.);      /* up is in positi_ve Y direction */
  glTranslatef(0.0, 0.0, -1.0);
  glRotatef(XAngle, 1.0, 0.0, 0.0);
  glRotatef(YAngle, 0.0, 1.0, 0.0);
  drawAxes();
  drawBox();
  glutSwapBuffers();
}

void
init(void)
{

  /* Enable a single OpenGL light. */
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHTING);

  /* Use depth buffering for hidden surface elimination. */
  glEnable(GL_DEPTH_TEST);

  /* Setup the _view of the cube. */
  glMatrixMode(GL_PROJECTION);
  gluPerspective( /* field of view in degree */ 40.0,
    /* aspect ratio */ 1.0,
    /* Z near */ 1.0, /* Z far */ 10.0);
  glMatrixMode(GL_MODELVIEW);
  gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
    0.0, 0.0, 0.0,      /* center is at (0,0,0) */
    0.0, 1.0, 0.);      /* up is in positi_ve Y direction */

  /* Adjust cube position to be asthetic angle. */
  glTranslatef(0.0, 0.0, -1.0);
  glRotatef(XAngle, 1.0, 0.0, 0.0);
  
  glEnable(GL_NORMALIZE);

}

void moveit(int x, int y) {

/* Set X and Y rotation angle to mouse position */
/* Not elegant, but it works */
  XAngle = x;
  YAngle = y;
  glutPostRedisplay();

}

void DoTween(void) {

int I;

  
if ((t >= 1.0) || (t < 0.0))
    Deltat *= -1;
    
t += Deltat;
    
    
 /* Tween the vertices */
    
 for (I = 0; I < 8; I++) {
  
      V[I][0] = (1-t) * V1[I][0] + t * V2[I][0];
      V[I][1] = (1-t) * V1[I][1] + t * V2[I][1];
      V[I][2] = (1-t) * V1[I][2] + t * V2[I][2];
      
      }
    
 glutPostRedisplay();



}

int
main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutCreateWindow("red 3D lighted cube");
  glutDisplayFunc(display);
  glutMotionFunc(moveit);
  glutIdleFunc(DoTween);
  init();
  glutMainLoop();
  return 0;             /* ANSI C requires main to return int. */
}
