/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                                  R A Y . C                                   *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: ray.c,v 1.7 2004/08/29 15:00:00 jrh Exp $
* $Log: ray.c,v $
* Revision 1.7  2004/08/29 15:00:00  jrh
* Release 2.4.1
*
* Revision 1.6  2003/11/07 11:09:36  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:14:33  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:27:03  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:55:11  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:49:07  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:43:24  jrh
* Initial revision
*
*/
#include<math.h>
#include<stdio.h>
#include<unistd.h>
#include<GL/gl.h>
#include<GL/glu.h>
#include "viewmol.h"
#include "dialog.h"

extern void transformCoordinates(int, float input[4], float output[4]);
extern void *getmem(size_t, size_t);
extern void fremem(void **);

extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern struct ELEMENT *elements;
extern double tmat[4][4];
extern GLfloat light0p[], light1p[];
extern float *transObject, *rotObject;
extern int ne, iwavef, lights, projectionMode;

static struct ELEMENT *material;
static double n[3], nSave[3][3], vSave[3][3];
static int printNormal=FALSE, nCorners;
static FILE *f;

FILE *raytraceInit(char *filename, Dimension width, Dimension height)
{
  struct MOLECULE *mol;
  struct ELEMENT *element=(struct ELEMENT *)NULL;
  FILE *file;
  double box, todeg=45.0/atan(1.0);;
  GLfloat zview, light[4];
  GLfloat lookat[4]={0.0, 0.0, 1.0, 1.0};
  GLfloat up[4]={0.0, 1.0, 0.0, 1.0};
  GLfloat right[4]={-1.0, 0.0, 0.0, 1.0};
  int *done, hbond;
  register int i, j;

  if ((file=fopen(filename, "w")) == NULL) return(NULL);

  if (projectionMode == ORTHO)
    fprintf(file, "camera {\n  orthographic\n");
  else
    fprintf(file, "camera {\n  perspective\n");

  /* Position camera */
  if (2.0*windows[VIEWER].far > transObject[3*VIEWPOINT+2])
    zview=2.0*windows[VIEWER].far-transObject[3*VIEWPOINT+2];
  else
    zview=0.1;
  fprintf(file, "  location <%.7f, %.7f, %.7f>\n", (-transObject[3*VIEWPOINT]),
          (-transObject[3*VIEWPOINT+1]), zview+windows[VIEWER].far);

  /* Define up and right directions for camera */
  if (projectionMode == ORTHO)
  {
    fprintf(file, "  up <0.0, %.7f, 0.0>\n", 2.0*windows[VIEWER].top);
    fprintf(file, "  right <%.7f, 0.0, 0.0>\n", -2.0*windows[VIEWER].right);
  }
  else
  {
    transformCoordinates(VIEWPOINT, up, light);
    fprintf(file, "  up <%.7f, %.7f, %.7f>\n", -light[0], light[1], light[2]);
    fprintf(file, "  sky <%.7f, %.7f, %.7f>\n", -light[0], light[1], light[2]);
    right[0]=-(double)width/(double)height;
    transformCoordinates(VIEWPOINT, right, light);
    fprintf(file, "  right <%.7f, %.7f, %.7f>\n", light[0], light[1], light[2]); 
  }

  /* Point camera into right direction and set its viewing angle */
  fprintf(file, "  angle %.7f\n", 2.0*todeg*atan(windows[VIEWER].right/zview));
  lookat[0]=-transObject[3*VIEWPOINT];
  lookat[1]=-transObject[3*VIEWPOINT+1];
  lookat[2]=zview;
  transformCoordinates(VIEWPOINT, lookat, light);
  fprintf(file, "  look_at <%.7f, %.7f, %.7f>}\n", light[0], light[1],  light[2]);

  /* Set background color */
  fprintf(file, "background { color rgb <%.3f, %.3f, %.3f>}\n", windows[VIEWER].background_rgb[0],
          windows[VIEWER].background_rgb[1], windows[VIEWER].background_rgb[2]);

  /* Position lights */
  box=2.0*windows[VIEWER].far;
  if (lights & 0x1)
  {
    transformCoordinates(LIGHTNO0, light0p, light);
    fprintf(file, "light_source { <%.7f, %.7f, %.7f>\n  color <1.0, 1.0, 1.0>\n  shadowless\n}\n",
            box*light[0], box*light[1], box*light[2]);
  }
  if (lights & 0x2)
  {
    transformCoordinates(LIGHTNO1, light1p, light);
    fprintf(file, "light_source { <%.7f, %.7f, %.7f>\n  color <1.0, 1.0, 1.0>\n}\n",
            box*light[0], box*light[1], box*light[2]);
  }

  /* Draw the molecule */
  if (windows[VIEWER].set >= 0)
    mol=&molecules[windows[VIEWER].set];
  else
    mol=&molecules[0];
  hbond=FALSE;
  for (i=0; i<mol->nb; i++)
  {
    if (mol->bonds[i].order == (-1))
    {
      hbond=TRUE;
      break;
    }
  }
  done=(int *)getmem(mol->na, sizeof(int));
  for (i=0; i<mol->na; i++)
  {
    if (!done[i])
    {
      element=mol->atoms[i].element;
      fprintf(file, "#declare %s = texture {\n", element->symbol);
      fprintf(file, "  pigment {color rgbf <%.3f, %.3f, %.3f, %.3f>}\n",
              (element->dark[0]+element->light[0])*0.5,
              (element->dark[1]+element->light[1])*0.5,
              (element->dark[2]+element->light[2])*0.5,
              1.0-element->alpha);
      fprintf(file, "  finish {ambient rgb <%.3f, %.3f, %.3f>\n  phong 0.9\n",
              element->ambient[0], element->ambient[1], element->ambient[2]);
      fprintf(file, "  specular %.3f}}\n", element->shininess/128.0);
      for (j=i; j<mol->na; j++)
      {
        if (mol->atoms[j].element == element) done[j]=TRUE;
      }
    }
  }
  if (hbond)
  {
    element=mol->bondStyle;
    fprintf(file, "#declare %s = texture {\n", element->symbol);
    fprintf(file, "  pigment {color rgbf <%.3f, %.3f, %.3f, %.3f>}\n",
            (element->dark[0]+element->light[0])*0.5,
            (element->dark[1]+element->light[1])*0.5,
            (element->dark[2]+element->light[2])*0.5,
            1.0-element->alpha);
    fprintf(file, "  finish {ambient rgb <%.3f, %.3f, %.3f>\n  phong 0.9\n",
            element->ambient[0], element->ambient[1], element->ambient[2]);
    fprintf(file, "  specular %.3f}}\n", element->shininess/128.0);
  }
  if (mol->unitcell)
  {
    element=mol->unitcell->corners[0].element;
    fprintf(file, "#declare %s = texture {\n", element->symbol);
    fprintf(file, "  pigment {color rgbf <%.3f, %.3f, %.3f, %.3f>}\n",
            (element->dark[0]+element->light[0])*0.5,
            (element->dark[1]+element->light[1])*0.5,
            (element->dark[2]+element->light[2])*0.5,
            1.0-element->alpha);
    fprintf(file, "  finish {ambient rgb <%.3f, %.3f, %.3f>\n  phong 0.9\n",
            element->ambient[0], element->ambient[1], element->ambient[2]);
    fprintf(file, "  specular %.3f}}\n", element->shininess/128.0);
  }
  if (iwavef != ALL_OFF)
  {
    for (i=0; i<ne; i++)
    {
      if (!strcmp(elements[i].symbol, "Ps") || !strcmp(elements[i].symbol, "Ms"))
      {
        fprintf(file, "#declare %s = texture {\n", elements[i].symbol);
        fprintf(file, "  pigment {color rgbf <%.3f, %.3f, %.3f, %.3f>}\n",
                (elements[i].dark[0]+elements[i].light[0])*0.5,
                (elements[i].dark[1]+elements[i].light[1])*0.5,
                (elements[i].dark[2]+elements[i].light[2])*0.5,
                1.0-elements[i].alpha);
        fprintf(file, "  finish {ambient rgb <%.3f, %.3f, %.3f>\n  phong 0.9\n",
                element->ambient[0], element->ambient[1], element->ambient[2]);
        fprintf(file, "  specular %.3f}}\n", element->shininess/128.0);
      }
    }
  }
  fremem((void **)&done);
  if (projectionMode == PERSPECTIVE)
  {
    fprintf(file, "#declare ground = texture {\n");
    fprintf(file, "  pigment {color rgb <%.3f, %.3f, %.3f>}}\n",
            windows[VIEWER].foreground_rgb[0], windows[VIEWER].foreground_rgb[1],
            windows[VIEWER].foreground_rgb[2]);
    fprintf(file, "plane { <0.0, 1.0, 0.0>, %10.5f\n  texture {ground}}\n", windows[VIEWER].bottom);
  }
  f=file;
  return(file);
}

void raytraceClose(FILE *file)
{
  fclose(file);
}

void raytracerBegin(GLenum what)
{
  fprintf(f, "smooth_triangle {\n");
  glGetDoublev(GL_MODELVIEW_MATRIX, &tmat[0][0]);
  nCorners=0;
}

void raytracerEnd()
{
}

void raytracerVertex3d(double vx, double vy, double vz)
{
  double t[3];
  register int i;

  if (nCorners > 2)
  {
    fprintf(f, "smooth_triangle {\n  <%.6f, %.6f, %.6f>, ", vSave[0][1], vSave[1][1],
            vSave[2][1]);
    if (printNormal)
      fprintf(f, "<%.6f, %.6f, %.6f>,\n", nSave[0][1], nSave[1][1], nSave[2][1]);
    fprintf(f, "<%.6f, %.6f, %.6f>, ", vSave[0][2], vSave[1][2], vSave[2][2]);
    if (printNormal)
      fprintf(f, "<%.6f, %.6f, %.6f>,\n", nSave[0][2], nSave[1][2], nSave[2][2]);
  }
  t[0]=vx*tmat[0][0]+vy*tmat[1][0]+vz*tmat[2][0]+tmat[3][0];
  t[1]=vx*tmat[0][1]+vy*tmat[1][1]+vz*tmat[2][1]+tmat[3][1];
  t[2]=vx*tmat[0][2]+vy*tmat[1][2]+vz*tmat[2][2]+tmat[3][2];
  fprintf(f, "<%.6f, %.6f, %.6f>, ", t[0], t[1], t[2]);
  nCorners++;

  for (i=0; i<3; i++)
  {
    vSave[i][0]=vSave[i][1];
    vSave[i][1]=vSave[i][2];
    vSave[i][2]=t[i];
  }
  if (printNormal)
  {
    t[0]=n[0]*tmat[0][0]+n[1]*tmat[1][0]+n[2]*tmat[2][0]+tmat[3][0];
    t[1]=n[0]*tmat[0][1]+n[1]*tmat[1][1]+n[2]*tmat[2][1]+tmat[3][1];
    t[2]=n[0]*tmat[0][2]+n[1]*tmat[1][2]+n[2]*tmat[2][2]+tmat[3][2];
    fprintf(f, "<%.6f, %.6f, %.6f>\n", t[0], t[1], t[2]);
    printNormal=FALSE;
    for (i=0; i<3; i++)
    {
      nSave[i][0]=nSave[i][1];
      nSave[i][1]=nSave[i][2];
      nSave[i][2]=t[i];
    }
    if (nCorners > 2)
      fprintf(f, " texture {%s}}\n", material->symbol);
  }
}

void raytracerNormal3d(double vx, double vy, double vz)
{
  n[0]=vx;
  n[1]=vy;
  n[2]=vz;
  printNormal=TRUE;
}

void raytracerSphere(GLUquadricObj *object, GLdouble radius, GLint dummy1,
                     GLint dummy2)
{
  double matrix[4][4];

  glGetDoublev(GL_MODELVIEW_MATRIX, &matrix[0][0]);
  fprintf(f, "sphere { <%10.6f, %10.6f, %10.6f>, %f\n  texture {%s}}\n",
          matrix[3][0], matrix[3][1], matrix[3][2], radius, material->symbol);
}

void raytracerCylinder(GLUquadricObj *object, GLdouble top, GLdouble bottom,
                       GLdouble height, GLint dummy1, GLint dummy2)
{
  double matrix[4][4];

  glGetDoublev(GL_MODELVIEW_MATRIX, &matrix[0][0]);
  fprintf(f, "cylinder {<%10.6f, %10.6f, %10.6f>,\n  <%10.6f, %10.6f, %10.6f>,\n  %f open\n texture {%s}}\n",
          matrix[3][0], matrix[3][1], matrix[3][2],
          height*matrix[2][0]+matrix[3][0], height*matrix[2][1]+matrix[3][1],
          height*matrix[2][2]+matrix[3][2], bottom, material->symbol);
}

void raytracerCone(GLUquadricObj *object, GLdouble top, GLdouble bottom,
                   GLdouble height, GLint dummy1, GLint dummy2)
{
  double matrix[4][4];

  glGetDoublev(GL_MODELVIEW_MATRIX, &matrix[0][0]);
  fprintf(f, "cone {<%.6f, %.6f, %.6f>, %.6f\n  <%.6f, %.6f, %.6f>, %.6f\n open\n texture {%s}}\n",
          matrix[3][0], matrix[3][1], matrix[3][2], top,
          height*matrix[2][0]+matrix[3][0], height*matrix[2][1]+matrix[3][1],
          height*matrix[2][2]+matrix[3][2], bottom, material->symbol);
}

void raytracerColor4fv(const GLfloat *color)
{
}

void raytracerClearColor(GLclampf red, GLclampf green , GLclampf blue, GLclampf alpha)
{
}

void raytracerMaterial(struct ELEMENT *e)
{
  material=e;
}
