/* plane2gl.c  Rotating *.dat with lighting */
/*             Utah Graphics ASCII data *.dat of *.det */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GL/glut.h>
#include "plane_fuse.h" /* compute some day */

static GLfloat size = 8.5;     /* auto scale */
static GLfloat roll = 0.0;     /* rotate about Y axis 'r' */
static GLfloat pitch = 0.0;    /* rotate about Z axis 'p' */
static GLfloat heading = 0.0;  /* rotate about Y axis 'h' */
static GLfloat pos[3] = {0.0, 0.0, 0.0};
static int background = 1;     /* background color 'b' */
static int wire = 0;           /* draw wireframe 'w' */
static int vert = 0;           /* draw vertices 'v' */
static int colorz = 0;         /* color based on z 'c' */
static GLfloat zavg = 0.0;
static GLfloat zmin = 0.0;
static GLfloat zmax = 0.0;
static double xw[30][17]; /* wing raw mesh */
static double yw[30][17];
static double zw[30][17];
static double xws[30][17]; /* wing scaled and offset mesh */
static double yws[30][17];
static double zws[30][17];

static GLfloat amb[4] = { 0.329412, 0.223529, 0.027451, 1.0}; /* brass */
static GLfloat dif[4] = { 0.780392, 0.568627, 0.113725, 1.0}; /* material */
static GLfloat spe[4] = { 0.992157, 0.941176, 0.807843, 1.0};
static GLfloat shiney = 0.21794872;

                                                  /* light source    */
static GLfloat ambient[] = {0.0, 0.0, 0.0, 1.0};  /* no   ambient    */
static GLfloat diffuse[] = {0.4, 0.4, 0.4, 1.0};  /* mid    diffuse  */
static GLfloat specular[] = {0.4, 0.4, 0.4, 1.0}; /* mid    specular */
static GLfloat position[] = {0.0, 0.0, 3.0, 0.0};

static GLdouble m[16];   /* 4 by 4 transformation matrix */
static int win_w, win_h;


static double f(double x) /* 0015 wing */
{
  return (15./20.)*(0.29690*sqrt(x)-0.12600*x-0.35160*x*x+
		    0.28430*x*x*x-0.10150*x*x*x*x);
}

void wing_2d(double xw[30][17], double yw[30][17], double zw[30][17])
{
  /* function [zw, xw, yw] = naca0015 */
  int ni = 30;
  int nj = 17;
  double *x;
  double *y;
  double xmin=0.0;
  double xmax=1.0;
  double dx, dz, x1, y1;
  int i, j;

  x=(double *)malloc(ni*sizeof(double));
  y=(double *)malloc(ni*sizeof(double));
  dx = (xmax-xmin)/(double)((ni/2)-1);
  for(i=0; i<ni/2; i++)
  {
    x1 = xmin+dx*i;
    x[i]=x1;
    y1 = f(x1);
    y[i]=y1;
    x[ni-1-i]=x1;
    y[ni-1-i]=-y1;
  }
  dz=8.0/(double)(nj-1);
  for(j=0; j<nj; j++)
  {
    for(i=0; i<ni; i++)
    {
      xw[i][j] = x[i];
      yw[i][j] = dz*(double)j; 
      zw[i][j] = y[i]; 
    }
  }
  for(i=0; i<ni; i++)
  {
    zw[i][0] = 0.0; /* close end of wing */
    zw[i][16] = 0.0; /* don't know which end */
  }
} /* end wing_2d */

static void scale(double fctr, double offs, double meshin[30][17], double mesh[30][17])
{                                          /* can swap x,y etc also */
  int i, j;
  for(i=0; i<30; i++)
  {
    for(j=0; j<17; j++)
    {
      mesh[i][j] = fctr*meshin[i][j]+offs;
    }
  }
} /* end scale */
 
static void norml(double x1, double y1, double z1,
                  double x2, double y2, double z2,
                  double x3, double y3, double z3,
                  double *nx, double *ny, double *nz)
{        /* compute normal at second point */
  GLfloat ax, ay, az, bx, by, bz, tnx, tny, tnz, s;
  ax = x3-x2;
  ay = y3-y2;
  az = z3-z2;
  bx = x2-x1;
  by = y2-y1;
  bz = z2-z1;
  tnx = ay*bz-az*by;
  tny = az*bx-ax*bz;
  tnz = ax*by-ay*bx;
  s = sqrt(tnx*tnx+tny*tny+tnz*tnz);
  *nx = tnx / s;
  *ny = tny / s;
  *nz = tnz / s;
} /* end nornl */

static void draw_solid(double meshx[30][17], double meshy[30][17], double meshz[30][17])
{
  int i, j;
  double x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4;
  double nx, ny, nz;


  glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
  glLightfv(GL_LIGHT0, GL_POSITION, position);

  glFrontFace(GL_CCW);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_NORMALIZE);
  glEnable(GL_AUTO_NORMAL);
  glEnable(GL_DEPTH_TEST); 
  glLightfv(GL_LIGHT0, GL_POSITION, position);

  glPushMatrix();
  glLoadIdentity();
  glRotatef(pitch, 0.0, 0.0, 1.0);
  glRotatef(heading, 0.0, 1.0, 0.0);
  glRotatef(roll, 1.0, 0.0, 0.0);
  glTranslatef(pos[0], pos[1], pos[2]);
  glGetDoublev(GL_MODELVIEW_MATRIX, m); /* save transformation */

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, amb);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, dif);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spe);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shiney * 64.0);

  /* draw surfaces */
  for(i=0; i<30-1; i++)
  {
    for(j=0; j<17-1; j++)
    {
      x1 = meshx[i][j];
      x2 = meshx[i][j+1];
      x3 = meshx[i+1][j+1];
      x4 = meshx[i+1][j];
      y1 = meshy[i][j];
      y2 = meshy[i][j+1];
      y3 = meshy[i+1][j+1];
      y4 = meshy[i+1][j];
      z1 = meshz[i][j];
      z2 = meshz[i][j+1];
      z3 = meshz[i+1][j+1];
      z4 = meshz[i+1][j];
      glBegin(GL_POLYGON);
        norml(x4, y4, z4, x1, y1, z1, x2, y2, z2, &nx, &ny, &nz);
        glNormal3f(nx, ny, nz);
        glVertex3f(x1, y1, z1);
        norml(x1, y1, z1, x2, y2, z2, x3, y3, z2, &nx, &ny, &nz);
        glNormal3f(nx, ny, nz);
        glVertex3f(x2, y2, z2);
        norml(x2, y2, z2, x3, y3, z3, x4, y4, z4, &nx, &ny, &nz);
        glNormal3f(nx, ny, nz);
        glVertex3f(x3, y3, z3);
        norml(x3, y3, z3, x4, y4, z4, x1, y1, z1, &nx, &ny, &nz);
        glNormal3f(nx, ny, nz);
        glVertex3f(x4, y4, z4);
      glEnd();
    }
  }
  glPopMatrix();
} /* end draw_solid */

static void draw_wire(double meshx[30][17], double meshy[30][17], double meshz[30][17])
{
  int i, j;
  double x1, y1, z1, x2, y2, z2, x3, y3, z3;

  glDisable(GL_LIGHTING);
  glDisable(GL_LIGHT0);
  glDisable(GL_NORMALIZE);
  glDisable(GL_AUTO_NORMAL);
  if(background)  glColor3f(0.0, 0.0, 0.0);
  if(!background) glColor3f(1.0, 1.0, 1.0);
  glPointSize(3.0);
  glPushMatrix();
  glLoadIdentity();
  glRotatef(pitch, 0.0, 0.0, 1.0);
  glRotatef(heading, 0.0, 1.0, 0.0);
  glRotatef(roll, 1.0, 0.0, 0.0);
  glTranslatef(pos[0], pos[1], pos[2]);
  glGetDoublev(GL_MODELVIEW_MATRIX, m); /* save transformation */

  /* draw wire frame surfaces */
  glBegin(GL_LINES); /* fuse 2D surface in 3D*/
    for(i=0; i<30-1; i++)
    {
      for(j=0; j<17-1; j++)
      {
        x1 = meshx[i][j];
        x2 = meshx[i][j+1];
        x3 = meshx[i+1][j];
        y1 = meshy[i][j];
        y2 = meshy[i][j+1];
        y3 = meshy[i+1][j];
        z1 = meshz[i][j];
        z2 = meshz[i][j+1];
        z3 = meshz[i+1][j];
        glVertex3f(x1, y1, z1);
        glVertex3f(x2, y2, z2);

        glVertex3f(x1, y1, z1);
        glVertex3f(x3, y3, z3);
      }
    }
    for(i=1; i<30; i++)
    {
      j=17-1;
      x1 = meshx[i][j];
      x2 = meshx[i][j-1];
      x3 = meshx[i-1][j];
      y1 = meshy[i][j];
      y2 = meshy[i][j-1];
      y3 = meshy[i-1][j];
      z1 = meshz[i][j];
      z2 = meshz[i][j-1];
      z3 = meshz[i-1][j];
      glVertex3f(x1, y1, z1);
      glVertex3f(x2, y2, z2);

      glVertex3f(x1, y1, z1);
      glVertex3f(x3, y3, z3);
    }
    for(j=1; i<17; i++)
    {
      i=30-1;
      x1 = meshx[i][j];
      x2 = meshx[i][j-1];
      x3 = meshx[i-1][j];
      y1 = meshy[i][j];
      y2 = meshy[i][j-1];
      y3 = meshy[i-1][j];
      z1 = meshz[i][j];
      z2 = meshz[i][j-1];
      z3 = meshz[i-1][j];
      glVertex3f(x1, y1, z1);
      glVertex3f(x2, y2, z2);

      glVertex3f(x1, y1, z1);
      glVertex3f(x3, y3, z3);
    }
  glEnd();
  glPopMatrix();
} /* end draw_wire */

static void draw_vert(double meshx[30][17], double meshy[30][17], double meshz[30][17])
{
  int i, j;
  double x1, y1, z1, x2, y2, z2, x3, y3, z3;

  glDisable(GL_LIGHTING);
  glDisable(GL_LIGHT0);
  glDisable(GL_NORMALIZE);
  glDisable(GL_AUTO_NORMAL);
  if(background)  glColor3f(0.0, 0.0, 0.0);
  if(!background) glColor3f(1.0, 1.0, 1.0);
  glPointSize(3.0);
  glPushMatrix();
  glLoadIdentity();
  glRotatef(pitch, 0.0, 0.0, 1.0);
  glRotatef(heading, 0.0, 1.0, 0.0);
  glRotatef(roll, 1.0, 0.0, 0.0);
  glTranslatef(pos[0], pos[1], pos[2]);
  glGetDoublev(GL_MODELVIEW_MATRIX, m); /* save transformation */

  /* draw vertices only of surfaces */
  glBegin(GL_POINTS);  /* fuse 2D surface in 3D*/
    for(i=0; i<30; i++)
    {
      for(j=0; j<17; j++)
      {
        x1 = meshx[i][j];
        y1 = meshy[i][j];
        z1 = meshz[i][j];
        glVertex3f(x1, y1, z1);
      }
    }
  glEnd();
  glPopMatrix();
} /* end draw_vert */

static writeText(GLfloat x, GLfloat y, GLfloat z, GLfloat csize, char * msg)
{            /* uses background and wire frame */
  char * p;
  GLfloat wht[] = {1.0, 1.0, 1.0, 1.0};
  GLfloat blk[] = {0.0, 0.0, 0.0, 0.0};

  glPushMatrix();
    glLoadIdentity ();
    if(background)
    {
      if(!wire)
      { 
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, blk); 
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, blk);
      }
      else
      {
        glColor3f(0.0, 0.0, 0.0);
      }
    }
    else
    {
      if(!wire)
      { 
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, wht); 
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, wht);
      }
      else
      {
        glColor3f(1.0, 1.0, 1.0);
      }
    } 
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glTranslatef(x, y, z);
    glScalef(csize/1000.0, csize/1000.0, 0.0);
    for(p=msg; *p; p++)
      glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
  glPopMatrix();
}

void display(void)
{
  int i, j;
  char xval[30], yval[30], zval[30];
  char rval[30], pval[30], hval[30];

  sprintf(xval, "x=%f\n", pos[0]);
  sprintf(yval, "y=%f\n", pos[1]);
  sprintf(zval, "z=%f\n", pos[2]);
  sprintf(rval, "roll=%f\n", roll);
  sprintf(pval, "pitch=%f\n", -pitch);
  sprintf(hval, "heading=%f\n", heading);

  if(background)  glClearColor(1.0, 1.0, 1.0, 1.0); 
  if(!background) glClearColor(0.0, 0.0, 0.0, 1.0); 
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  if(vert)      draw_vert(fuse_x, fuse_y, fuse_z);
  else if(wire) draw_wire(fuse_x, fuse_y, fuse_z);
  else          draw_solid(fuse_x, fuse_y, fuse_z);

  scale(2.0, 2.0, xw, xws); /* left wing */
  scale(2.0, 0.5, zw, yws);
  scale(0.7, 0.7, yw, zws);
  if(vert)      draw_vert(xws, yws, zws);
  else if(wire) draw_wire(xws, yws, zws);
  else          draw_solid(xws, yws, zws);

  scale(2.0, 2.0, xw, xws); /* right wing */
  scale(2.0, 0.5, zw, yws);
  scale(-0.7, -0.7, yw, zws);
  if(vert)      draw_vert(xws, yws, zws);
  else if(wire) draw_wire(xws, yws, zws);
  else          draw_solid(xws, yws, zws);

  scale(1.3, 6.5, xw, xws); /* left horizontal stabalizer */
  scale(1.3, 0.4, zw, yws);
  scale(0.3, 0.0, yw, zws);
  if(vert)      draw_vert(xws, yws, zws);
  else if(wire) draw_wire(xws, yws, zws);
  else          draw_solid(xws, yws, zws);

  scale(1.3, 6.5, xw, xws); /* right horizontal stabalizer */
  scale(1.3, 0.4, zw, yws);
  scale(-0.3, 0.0, yw, zws);
  if(vert)      draw_vert(xws, yws, zws);
  else if(wire) draw_wire(xws, yws, zws);
  else          draw_solid(xws, yws, zws);

  scale(1.3, 6.5, xw, xws); /* verticle stabalizer */
  scale(0.3, 0.0, zw, zws);
  scale(0.22, 0.1, yw, yws);
  if(vert)      draw_vert(xws, yws, zws);
  else if(wire) draw_wire(xws, yws, zws);
  else          draw_solid(xws, yws, zws);

  writeText(-size*0.95, -size*0.95, 0.0, size/2.5,
            "Key x,X y,Y z,Z to position, w for wireframe, v for vertices");  
  writeText(-size*0.95, -size*0.95+size/16.0, 0.0, size/2.5,
            "Key r,R roll, p,P pitch, h,H heading,  b toggles background"); 
  writeText(-size*0.95, -size*0.95+2*size/16.0, 0.0, size/2.5, xval); 
  writeText(-size*0.4, -size*0.95+2*size/16.0, 0.0, size/2.5, yval); 
  writeText( size*0.2, -size*0.95+2*size/16.0, 0.0, size/2.5, zval); 
  writeText(-size*0.95, -size*0.95+3*size/16.0, 0.0, size/2.5, rval); 
  writeText(-size*0.4, -size*0.95+3*size/16.0, 0.0, size/2.5, pval); 
  writeText( size*0.2, -size*0.95+3*size/16.0, 0.0, size/2.5, hval); 
  glFlush();
  glutSwapBuffers();
} /* end display */

void mouse(int button, int state, int x, int y) 
{
  if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
  {
  }
  if(button==GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) ; /* commit */
  if(button==GLUT_RIGHT_BUTTON && state == GLUT_DOWN) ;  /* drag */
}

void myReshape(int w, int h)
{
  win_w = w;
  win_h = h;
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if(w <= h)
      glOrtho(-size, size, -size * (GLfloat)h / (GLfloat)w,
              size * (GLfloat)h / (GLfloat)w, -size, size);
  else
      glOrtho(-size * (GLfloat)w / (GLfloat)h,
              size * (GLfloat)w / (GLfloat)h, -size, size, -size, size);
  glMatrixMode(GL_MODELVIEW);
}

/* x,X,y,Y,z and Z keys for moving position
   r,R,p,P,h and H keys for rool, pitch and heading
   w toggle wire frame, v toggle vertices
   b toggle background, c toggle color in z */
void keyboard (unsigned char key, int x, int y)
{
  if(key == 'x') pos[0] -= 0.5;
  if(key == 'X') pos[0] += 0.5;
  if(key == 'y') pos[1] -= 0.5;
  if(key == 'Y') pos[1] += 0.5;
  if(key == 'z') pos[2] -= 0.5;
  if(key == 'Z') pos[2] += 0.5;
  if(key == 'r') roll    -= 5.0;
  if(key == 'R') roll    += 5.0;
  if(key == 'p') pitch   -= 2.0;
  if(key == 'P') pitch   += 2.0;
  if(key == 'h') heading -= 5.0;
  if(key == 'H') heading += 5.0;
  if(key == 'b' || key == 'B') background = 1-background;
  if(key == 'w' || key == 'W') wire = 1-wire;
  if(key == 'v' || key == 'V') vert = 1-vert;
  if(key == 'c' || key == 'C') colorz = 1-colorz;
  if(key == 's' || key == 'S')
  {
    roll = 0.0;
    pitch = 0.0;
    heading = 0.0;
    pos[0] = 0.0;
    pos[1] = 0.0;
    pos[2] = 0.0;
  }
  if(key == 't' || key == 'T')
  {
    roll = 20.0;
    pitch = -5.0;
    heading = 15.0;
    pos[0] = 0.0;
    pos[1] = 0.0;
    pos[2] = 0.0;
  }
  glutPostRedisplay();
} /* end keyboard */

int main(int argc, char *argv[])
{
  glutInit(&argc, argv);
  /* need both double buffering and z buffer */
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(800, 800);
  glutCreateWindow(argv[0]);
  glutReshapeFunc(myReshape);
  glutDisplayFunc(display);
  glutMouseFunc(mouse);       /* "mouse" setup    */
  glutKeyboardFunc(keyboard); /* "keyboard" setup */
  glEnable(GL_DEPTH_TEST);    /* Enable hidden--surface--removal */
  wing_2d(xw, yw, zw);
  glutMainLoop();
  return 0;
} /* end plane2gl.c */



