/************************************************************************/
/* This file contains a 3D replacement for the out_line function called */
/* by the decoder. The purpose is to apply various 3D transformations   */
/* before displaying points. Called once per line of the input file.    */
/*                                                                      */
/* Original Author Tim Wegner, with extensive help from Marc Reinig.    */
/*    July 1994 - TW broke out several pieces of code and added pragma  */
/*                to eliminate compiler warnings. Did a long-overdue    */
/*                formatting cleanup.                                   */
/************************************************************************/

#include <limits.h>

  /* see Fractint.c for a description of the "include"  hierarchy */
#include "port.h"
#include "prototyp.h"

struct point
{
   int x;
   int y;
   int color;
};

struct f_point
{
   float x;
   float y;
   float color;
};

struct minmax
{
   int minx;
   int maxx;
};

/* routines in this module */
int line3d(BYTE *, unsigned);
int _fastcall targa_color(int, int, int);
int targa_validate(char *);
static int first_time(int, VECTOR);
static int H_R(BYTE *, BYTE *, BYTE *, unsigned long, unsigned long, unsigned long);
static int line3dmem(void);
static int R_H(BYTE, BYTE, BYTE, unsigned long *, unsigned long *, unsigned long *);
static int set_pixel_buff(BYTE *, BYTE far *, unsigned);
int startdisk1(char *, FILE *, int);
static void set_upr_lwr(void);
static int _fastcall end_object(int);
static int _fastcall offscreen(struct point);
static int _fastcall out_triangle(struct f_point, struct f_point, struct f_point, int, int, int);
static int _fastcall RAY_Header(void);
static int _fastcall start_object(void);
static void corners(MATRIX, int, double *, double *, double *, double *, double *, double *);
static void draw_light_box(double *, double *, MATRIX);
static void draw_rect(VECTOR, VECTOR, VECTOR, VECTOR, int, int);
static void File_Error(char *, int);
static void line3d_cleanup(void);
static void _fastcall clipcolor(int, int, int);
static void _fastcall interpcolor(int, int, int);
static void _fastcall putatriangle(struct point, struct point, struct point, int);
static void _fastcall putminmax(int, int, int);
static void _fastcall triangle_bounds(float pt_t[3][3]);
static void _fastcall T_clipcolor(int, int, int);
static void _fastcall vdraw_line(double *, double *, int color);
static void (_fastcall * fillplot) (int, int, int);
static void (_fastcall * normalplot) (int, int, int);

/* static variables */
static float deltaphi;          /* increment of latitude, longitude */
static double rscale;           /* surface roughness factor */
static long xcenter, ycenter;   /* circle center */
static double sclx, scly, sclz; /* scale factors */
static double R;                /* radius values */
static double Rfactor;          /* for intermediate calculation */
static LMATRIX llm;             /* "" */
static LVECTOR lview;           /* for perspective views */
static double zcutoff;          /* perspective backside cutoff value */
static float twocosdeltaphi;
static float cosphi, sinphi;    /* precalculated sin/cos of longitude */
static float oldcosphi1, oldsinphi1;
static float oldcosphi2, oldsinphi2;
static BYTE far *fraction;      /* float version of pixels array */
static float min_xyz[3], max_xyz[3];        /* For Raytrace output */
static int line_length1;
static int T_header_24 = 18;/* Size of current Targa-24 header */
static FILE *File_Ptr1 = NULL;
static unsigned int IAmbient;
static int rand_factor;
static int HAZE_MULT;
static void File_Error(char *File_Name1, int ERROR);
static BYTE T24 = 24;
static BYTE T32 = 32;
static BYTE upr_lwr[4];
static int T_Safe; /* Original Targa Image successfully copied to targa_temp */
static VECTOR light_direction;
static BYTE Real_Color;  /* Actual color of cur pixel */
static int RO, CO, CO_MAX;  /* For use in Acrospin support */
static FCODE acro_s1[] =
   {"Set Layer 1\nSet Color 2\nEndpointList X Y Z Name\n"};
static FCODE acro_s2[] = {"LineList From To\n"};
static FCODE s3[] = {"{ Created by FRACTINT Ver. "};
static FCODE s3a[] = {" }\n\n"};
#ifndef XFRACT
static char banner[] = "%Fs%#4.2f%Fs";
#else
static char banner[] = "%s%#4.2f%s";
#endif
static int localpreviewfactor;
static int zcoord = 256;
static double aspect;       /* aspect ratio */
static int evenoddrow;
static float far *sinthetaarray;    /* all sine thetas go here  */
static float far *costhetaarray;    /* all cosine thetas go here */
static double rXrscale;     /* precalculation factor */
static int persp;  /* flag for indicating perspective transformations */
static struct point p1, p2, p3;
static struct f_point f_bad;/* out of range value */
static struct point bad;    /* out of range value */
static long num_tris; /* number of triangles output to ray trace file */

/* global variables defined here */
struct f_point far *f_lastrow;
void (_fastcall * standardplot) (int, int, int);
MATRIX m; /* transformation matrix */
int Ambient;
int RANDOMIZE;
int haze;
int Real_V = 0; /* mrr Actual value of V for fillytpe>4 monochrome images */
char light_name[FILE_MAX_PATH] = "fract001";
int Targa_Overlay, error;
char targa_temp[14] = "fractemp.tga";
int P = 250; /* Perspective dist used when viewing light vector */
BYTE back_color[3];
char ray_name[FILE_MAX_PATH] = "fract001";
char preview = 0;
char showbox = 0;
int previewfactor = 20;
int xadjust = 0;
int yadjust = 0;
int xxadjust;
int yyadjust;
int xshift;
int yshift;
int bad_value = -10000; /* set bad values to this */
int bad_check = -3000;  /* check values against this to determine if good */
struct point far *lastrow; /* this array remembers the previous line */
int RAY = 0;        /* Flag to generate Ray trace compatible files in 3d */
int BRIEF = 0;      /* 1 = short ray trace files */

/* array of min and max x values used in triangle fill */
struct minmax far *minmax_x;
VECTOR view;                /* position of observer for perspective */
VECTOR cross;
VECTOR tmpcross;

struct point oldlast = { 0, 0, 0 }; /* old pixels */


int line3d(BYTE * pixels, unsigned linelen)
{
   int tout;                    /* triangle has been sent to ray trace file */
   int RND;
   float f_water = (float)0.0;        /* transformed WATERLINE for ray trace files */
   double r0;
   int xcenter0 = 0;
   int ycenter0 = 0;      /* Unfudged versions */
   double r;                    /* sphere radius */
   float costheta, sintheta;    /* precalculated sin/cos of latitude */
   int next;                    /* used by preview and grid */
   int col;                     /* current column (original GIF) */
   struct point cur;            /* current pixels */
   struct point old;            /* old pixels */
   struct f_point f_cur;
   struct f_point f_old;
   VECTOR v;                    /* double vector */
   VECTOR v1, v2;
   VECTOR crossavg;
   char crossnotinit;           /* flag for crossavg init indication */
   LVECTOR lv;                  /* long equivalent of v */
   LVECTOR lv0;                 /* long equivalent of v */
   int lastdot;
   long fudge;

   fudge = 1L << 16;


   if (transparent[0] || transparent[1])
      plot = normalplot = T_clipcolor;  /* Use transparent plot function */
   else                         /* Use the usual FRACTINT plot function with
                                 * clipping */
      plot = normalplot = clipcolor;

   currow = rowcount;           /* use separate variable to allow for
                                 * pot16bit files */
   if (pot16bit)
      currow >>= 1;

   /************************************************************************/
   /* This IF clause is executed ONCE per image. All precalculations are   */
   /* done here, with out any special concern about speed. DANGER -        */
   /* communication with the rest of the program is generally via static   */
   /* or global variables.                                                 */
   /************************************************************************/
   if (rowcount++ == 0)
   {
      int err;
      if ((err = first_time(linelen, v)) != 0)
         return (err);
      if(xdots > OLDMAXPIXELS)
         return(-1);
      tout = 0;
      crossavg[0] = 0;
      crossavg[1] = 0;
      crossavg[2] = 0;
      xcenter0 = (int) (xcenter = xdots / 2 + xshift);
      ycenter0 = (int) (ycenter = ydots / 2 - yshift);
   }
   /* make sure these pixel coordinates are out of range */
   old = bad;
   f_old = f_bad;

   /* copies pixels buffer to float type fraction buffer for fill purposes */
   if (pot16bit)
   {
      if (set_pixel_buff(pixels, fraction, linelen))
         return (0);
   }
   else if (grayflag)           /* convert color numbers to grayscale values */
      for (col = 0; col < (int) linelen; col++)
      {
         int pal, colornum;
         colornum = pixels[col];
         /* effectively (30*R + 59*G + 11*B)/100 scaled 0 to 255 */
         pal = ((int) dacbox[colornum][0] * 77 +
                (int) dacbox[colornum][1] * 151 +
                (int) dacbox[colornum][2] * 28);
         pal >>= 6;
         pixels[col] = (BYTE) pal;
      }
   crossnotinit = 1;
   col = 0;

   CO = 0;

   /*************************************************************************/
   /* This section of code allows the operation of a preview mode when the  */
   /* preview flag is set. Enabled, it allows the drawing of only the first */
   /* line of the source image, then every 10th line, until and including   */
   /* the last line. For the undrawn lines, only necessary calculations are */
   /* made. As a bonus, in non-sphere mode a box is drawn to help visualize */
   /* the effects of 3D transformations. Thanks to Marc Reinig for this idea*/
   /* and code -- BTW, Marc did NOT put the goto in, but WE did, to avoid   */
   /* copying code here, and to avoid a HUGE "if-then" construct. Besides,  */
   /* we have ALREADY sinned, so why not sin some more?                     */
   /*************************************************************************/
   lastdot = min(xdots - 1, (int) linelen - 1);
   if (FILLTYPE >= 5)
      if (haze && Targa_Out)
      {
         HAZE_MULT = (int) (haze * (
                                      (float) ((long) (ydots - 1 - currow) *
                                               (long) (ydots - 1 - currow)) /
                        (float) ((long) (ydots - 1) * (long) (ydots - 1))));
         HAZE_MULT = 100 - HAZE_MULT;
      }

   if (previewfactor >= ydots || previewfactor > lastdot)
      previewfactor = min(ydots - 1, lastdot);

   localpreviewfactor = ydots / previewfactor;

   tout = 0;
   /* Insure last line is drawn in preview and filltypes <0  */
   if ((RAY || preview || FILLTYPE < 0) && (currow != ydots - 1) &&
       (currow % localpreviewfactor) && /* Draw mod preview lines */
       !(!RAY && (FILLTYPE > 4) && (currow == 1)))
      /* Get init geometry in lightsource modes */
      goto reallythebottom;     /* skip over most of the line3d calcs */
   if (dotmode == 11)
   {
      static FCODE mapping[] = {"mapping to 3d, reading line "};
      char s[40];
#ifndef XFRACT
      sprintf(s, "%Fs%d", (char far *)mapping, currow);
#else
      sprintf(s, "%s%d", mapping, currow);
#endif
      dvid_status(1, s);
   }

   if (!col && RAY && currow != 0)
      start_object();
      /* PROCESS ROW LOOP BEGINS HERE */
      while (col < (int) linelen)
      {
         if ((RAY || preview || FILLTYPE < 0) &&
             (col != lastdot) &&/* if this is not the last col */
         /* if not the 1st or mod factor col */
             (col % (int) (aspect * localpreviewfactor)) &&
             (!(!RAY && FILLTYPE > 4 && col == 1)))
            goto loopbottom;

         f_cur.color = cur.color = Real_Color = pixels[col];

         if (RAY || preview || FILLTYPE < 0)
         {
            next = (int) (col + aspect * localpreviewfactor);
            if (next == col)
               next = col + 1;
         }
         else
            next = col + 1;
         if (next >= lastdot)
            next = lastdot;

         if (cur.color > 0 && cur.color < WATERLINE)
            f_cur.color = cur.color = Real_Color = (BYTE)WATERLINE; /* "lake" */
         else if (pot16bit)
            f_cur.color += ((float) fraction[col]) / (float) (1 << 8);

         if (SPHERE)            /* sphere case */
         {
            sintheta = sinthetaarray[col];
            costheta = costhetaarray[col];

            if (sinphi < 0 && !(RAY || FILLTYPE < 0))
            {
               cur = bad;
               f_cur = f_bad;
               goto loopbottom; /* another goto ! */
            }
            /************************************************************/
            /* KEEP THIS FOR DOCS - original formula --                 */
            /* if(rscale < 0.0)                                         */
            /* r = 1.0+((double)cur.color/(double)zcoord)*rscale;       */
            /* else                                                     */
            /* r = 1.0-rscale+((double)cur.color/(double)zcoord)*rscale;*/
            /* R = (double)ydots/2;                                     */
            /* r = r*R;                                                 */
            /* cur.x = xdots/2 + sclx*r*sintheta*aspect + xup ;         */
            /* cur.y = ydots/2 + scly*r*costheta*cosphi - yup ;         */
            /************************************************************/

            if (rscale < 0.0)
               r = R + Rfactor * (double) f_cur.color * costheta;
            else if (rscale > 0.0)
               r = R - rXrscale + Rfactor * (double) f_cur.color * costheta;
            else
               r = R;
            /* Allow Ray trace to go through so display ok */
            if (persp || RAY)
            {  /* mrr how do lv[] and cur and f_cur all relate */
               /* NOTE: fudge was pre-calculated above in r and R */
               /* (almost) guarantee negative */
               lv[2] = (long) (-R - r * costheta * sinphi);     /* z */
               if ((lv[2] > zcutoff) && !FILLTYPE < 0)
               {
                  cur = bad;
                  f_cur = f_bad;
                  goto loopbottom;      /* another goto ! */
               }
               lv[0] = (long) (xcenter + sintheta * sclx * r);  /* x */
               lv[1] = (long) (ycenter + costheta * cosphi * scly * r); /* y */

               if ((FILLTYPE >= 5) || RAY)
               {     /* calculate illumination normal before persp */

                  r0 = r / 65536L;
                  f_cur.x = (float) (xcenter0 + sintheta * sclx * r0);
                  f_cur.y = (float) (ycenter0 + costheta * cosphi * scly * r0);
                  f_cur.color = (float) (-r0 * costheta * sinphi);
               }
               v[0] = lv[0];
               v[1] = lv[1];
               v[2] = lv[2];
               v[0] /= fudge;
               v[1] /= fudge;
               v[2] /= fudge;
               perspective(v);
               cur.x = (int) (v[0] + .5 + xxadjust);
               cur.y = (int) (v[1] + .5 + yyadjust);
            }
            /* mrr Not sure how this an 3rd if above relate */
            else if (!(persp && RAY))
            {
               /* mrr Why the xx- and yyadjust here and not above? */
               cur.x = (int) (f_cur.x = (float) (xcenter
                                 + sintheta * sclx * r + xxadjust));
               cur.y = (int) (f_cur.y = (float) (ycenter
                                 + costheta * cosphi * scly * r + yyadjust));
               if (FILLTYPE >= 5 || RAY)        /* mrr why do we do this for
                                                 * filltype>5? */
                  f_cur.color = (float) (-r * costheta * sinphi * sclz);
               v[0] = v[1] = v[2] = 0;  /* MRR Why do we do this? */
            }
         }
         else
            /* non-sphere 3D */
         {
            /* slow float version for comparison */
            v[0] = col;
            v[1] = currow;
            v[2] = f_cur.color;      /* Actually the z value */

            mult_vec(v);     /* matrix*vector routine */

            if (FILLTYPE > 4 || RAY)
            {
               f_cur.x = (float) v[0];
               f_cur.y = (float) v[1];
               f_cur.color = (float) v[2];

               if (RAY == 6)
               {
                  f_cur.x = f_cur.x * ((float)2.0 / xdots) - (float)1.0;
                  f_cur.y = f_cur.y * ((float)2.0 / ydots) - (float)1.0;
                  f_cur.color = -f_cur.color * ((float)2.0 / numcolors) - (float)1.0;
               }

               if (persp && !RAY)
                  perspective(v);
               cur.x = (int) (v[0] + xxadjust + .5);
               cur.y = (int) (v[1] + yyadjust + .5);

               v[0] = 0;
               v[1] = 0;
               v[2] = WATERLINE;
               mult_vec(v);
               f_water = (float) v[2];
            }
         }

         if (RANDOMIZE)
            if (cur.color > WATERLINE)
            {
               RND = rand15() >> 8;     /* 7-bit number */
               RND = RND * RND >> rand_factor;  /* n-bit number */

               if (rand() & 1)
                  RND = -RND;   /* Make +/- n-bit number */

               if ((int) (cur.color) + RND >= colors)
                  cur.color = colors - 2;
               else if ((int) (cur.color) + RND <= WATERLINE)
                  cur.color = WATERLINE + 1;
               else
                  cur.color = cur.color + RND;
               Real_Color = (BYTE)cur.color;
            }

         if (RAY)
         {
            if (col && currow &&
                old.x > bad_check &&
                old.x < (xdots - bad_check) &&
                lastrow[col].x > bad_check &&
                lastrow[col].y > bad_check &&
                lastrow[col].x < (xdots - bad_check) &&
                lastrow[col].y < (ydots - bad_check))
            {
               /* Get rid of all the triangles in the plane at the base of
                * the object */

               if (f_cur.color == f_water &&
                   f_lastrow[col].color == f_water &&
                   f_lastrow[next].color == f_water)
                  goto loopbottom;

               if (RAY != 6)    /* Output the vertex info */
                  out_triangle(f_cur, f_old, f_lastrow[col],
                               cur.color, old.color, lastrow[col].color);

               tout = 1;

               draw_line(old.x, old.y, cur.x, cur.y, old.color);
               draw_line(old.x, old.y, lastrow[col].x,
                         lastrow[col].y, old.color);
               draw_line(lastrow[col].x, lastrow[col].y,
                         cur.x, cur.y, cur.color);
               num_tris++;
            }

            if (col < lastdot && currow &&
                lastrow[col].x > bad_check &&
                lastrow[col].y > bad_check &&
                lastrow[col].x < (xdots - bad_check) &&
                lastrow[col].y < (ydots - bad_check) &&
                lastrow[next].x > bad_check &&
                lastrow[next].y > bad_check &&
                lastrow[next].x < (xdots - bad_check) &&
                lastrow[next].y < (ydots - bad_check))
            {
               /* Get rid of all the triangles in the plane at the base of
                * the object */

               if (f_cur.color == f_water &&
                   f_lastrow[col].color == f_water &&
                   f_lastrow[next].color == f_water)
                  goto loopbottom;

               if (RAY != 6)    /* Output the vertex info */
                  out_triangle(f_cur, f_lastrow[col], f_lastrow[next],
                        cur.color, lastrow[col].color, lastrow[next].color);

               tout = 1;

               draw_line(lastrow[col].x, lastrow[col].y, cur.x, cur.y,
                         cur.color);
               draw_line(lastrow[next].x, lastrow[next].y, cur.x, cur.y,
                         cur.color);
               draw_line(lastrow[next].x, lastrow[next].y, lastrow[col].x,
                         lastrow[col].y, lastrow[col].color);
               num_tris++;
            }

            if (RAY == 6)       /* Output vertex info for Acrospin */
            {
               fprintf(File_Ptr1, "% #4.4f % #4.4f % #4.4f R%dC%d\n",
                       f_cur.x, f_cur.y, f_cur.color, RO, CO);
               if (CO > CO_MAX)
                  CO_MAX = CO;
               CO++;
            }
            goto loopbottom;
         }

         switch (FILLTYPE)
         {
         case -1:
            if (col &&
                old.x > bad_check &&
                old.x < (xdots - bad_check))
               draw_line(old.x, old.y, cur.x, cur.y, cur.color);
            if (currow &&
                lastrow[col].x > bad_check &&
                lastrow[col].y > bad_check &&
                lastrow[col].x < (xdots - bad_check) &&
                lastrow[col].y < (ydots - bad_check))
               draw_line(lastrow[col].x, lastrow[col].y, cur.x,
                         cur.y, cur.color);
            break;

         case 0:
            (*plot) (cur.x, cur.y, cur.color);
            break;

         case 1:                /* connect-a-dot */
            if ((old.x < xdots) && (col) &&
                old.x > bad_check &&
                old.y > bad_check)      /* Don't draw from old to cur on col
                                         * 0 */
               draw_line(old.x, old.y, cur.x, cur.y, cur.color);
            break;

         case 2:                /* with interpolation */
         case 3:                /* no interpolation */
            /*************************************************************/
            /* "triangle fill" - consider four points: current point,    */
            /* previous point same row, point opposite current point in  */
            /* previous row, point after current point in previous row.  */
            /* The object is to fill all points inside the two triangles.*/
            /*                                                           */
            /* lastrow[col].x/y___ lastrow[next]                         */
            /* /        1                 /                              */
            /* /                1         /                              */
            /* /                       1  /                              */
            /* oldrow/col ________ trow/col                              */
            /*************************************************************/

            if (currow && !col)
               putatriangle(lastrow[next], lastrow[col], cur, cur.color);
            if (currow && col)  /* skip first row and first column */
            {
               if (col == 1)
                  putatriangle(lastrow[col], oldlast, old, old.color);

               if (col < lastdot)
                  putatriangle(lastrow[next], lastrow[col], cur, cur.color);
               putatriangle(old, lastrow[col], cur, cur.color);
            }
            break;

         case 4:                /* "solid fill" */
            if (SPHERE)
            {
               if (persp)
               {
                  old.x = (int) (xcenter >> 16);
                  old.y = (int) (ycenter >> 16);
               }
               else
               {
                  old.x = (int) xcenter;
                  old.y = (int) ycenter;
               }
            }
            else
            {
               lv[0] = col;
               lv[1] = currow;
               lv[2] = 0;

               /* apply fudge bit shift for integer math */
               lv[0] = lv[0] << 16;
               lv[1] = lv[1] << 16;
               /* Since 0, unnecessary lv[2] = lv[2] << 16; */

               if (longvmultpersp(lv, llm, lv0, lv, lview, 16))
               {
                  cur = bad;
                  f_cur = f_bad;
                  goto loopbottom;      /* another goto ! */
               }

               /* Round and fudge back to original  */
               old.x = (int) ((lv[0] + 32768L) >> 16);
               old.y = (int) ((lv[1] + 32768L) >> 16);
            }
            if (old.x < 0)
               old.x = 0;
            if (old.x >= xdots)
               old.x = xdots - 1;
            if (old.y < 0)
               old.y = 0;
            if (old.y >= ydots)
               old.y = ydots - 1;
            draw_line(old.x, old.y, cur.x, cur.y, cur.color);
            break;

         case 5:
         case 6:
            /* light-source modulated fill */
            if (currow && col)  /* skip first row and first column */
            {
               if (f_cur.color < bad_check || f_old.color < bad_check ||
                   f_lastrow[col].color < bad_check)
                  break;

               v1[0] = f_cur.x - f_old.x;
               v1[1] = f_cur.y - f_old.y;
               v1[2] = f_cur.color - f_old.color;

               v2[0] = f_lastrow[col].x - f_cur.x;
               v2[1] = f_lastrow[col].y - f_cur.y;
               v2[2] = f_lastrow[col].color - f_cur.color;

               cross_product(v1, v2, cross);

               /* normalize cross - and check if non-zero */
               if (normalize_vector(cross))
               {
                  if (debugflag)
                  {
                     static FCODE msg[] = {"debug, cur.color=bad"};
                     stopmsg(0, msg);
                  }
                  cur.color = (int)(f_cur.color = bad.color);
               }
               else
               {
                  /* line-wise averaging scheme */
                  if (LIGHTAVG > 0)
                  {
                     if (crossnotinit)
                     {
                        /* initialize array of old normal vectors */
                        crossavg[0] = cross[0];
                        crossavg[1] = cross[1];
                        crossavg[2] = cross[2];
                        crossnotinit = 0;
                     }
                     tmpcross[0] = (crossavg[0] * LIGHTAVG + cross[0]) /
                        (LIGHTAVG + 1);
                     tmpcross[1] = (crossavg[1] * LIGHTAVG + cross[1]) /
                        (LIGHTAVG + 1);
                     tmpcross[2] = (crossavg[2] * LIGHTAVG + cross[2]) /
                        (LIGHTAVG + 1);
                     cross[0] = tmpcross[0];
                     cross[1] = tmpcross[1];
                     cross[2] = tmpcross[2];
                     if (normalize_vector(cross))
                     {
                        /* this shouldn't happen */
                        if (debugflag)
                        {
                           static FCODE msg[] = {"debug, normal vector err2"};
                           stopmsg(0, msg);
                           /* use next instead if you ever need details:
                            * static char far tmp[] = {"debug, vector err"};
                            * char msg[200]; #ifndef XFRACT
                            * sprintf(msg,"%Fs\n%f %f %f\n%f %f %f\n%f %f
                            * %f", #else sprintf(msg,"%s\n%f %f %f\n%f %f
                            * %f\n%f %f %f", #endif tmp, f_cur.x, f_cur.y,
                            * f_cur.color, f_lastrow[col].x,
                            * f_lastrow[col].y, f_lastrow[col].color,
                            * f_lastrow[col-1].x,
                            * f_lastrow[col-1].y,f_lastrow[col-1].color);
                            * stopmsg(0,msg); */
                        }
                        cur.color = (int)(f_cur.color = colors);
                     }
                  }
                  crossavg[0] = tmpcross[0];
                  crossavg[1] = tmpcross[1];
                  crossavg[2] = tmpcross[2];

                  /* dot product of unit vectors is cos of angle between */
                  /* we will use this value to shade surface */

                  cur.color = (int) (1 + (colors - 2) *
                               (1.0 - dot_product(cross, light_direction)));
               }
               /* if colors out of range, set them to min or max color index
                * but avoid background index. This makes colors "opaque" so
                * SOMETHING plots. These conditions shouldn't happen but just
                * in case                                        */
               if (cur.color < 1)       /* prevent transparent colors */
                  cur.color = 1;/* avoid background */
               if (cur.color > colors - 1)
                  cur.color = colors - 1;

               /* why "col < 2"? So we have sufficient geometry for the fill */
               /* algorithm, which needs previous point in same row to have  */
               /* already been calculated (variable old)                 */
               /* fix ragged left margin in preview */
               if (col == 1 && currow > 1)
                  putatriangle(lastrow[next], lastrow[col], cur, cur.color);

               if (col < 2 || currow < 2)       /* don't have valid colors
                                                 * yet */
                  break;

               if (col < lastdot)
                  putatriangle(lastrow[next], lastrow[col], cur, cur.color);
               putatriangle(old, lastrow[col], cur, cur.color);

               plot = standardplot;
            }
            break;
         }                      /* End of CASE statement for fill type  */
       loopbottom:
         if (RAY || (FILLTYPE != 0 && FILLTYPE != 4))
         {
            /* for triangle and grid fill purposes */
            oldlast = lastrow[col];
            old = lastrow[col] = cur;

            /* for illumination model purposes */
            f_old = f_lastrow[col] = f_cur;
            if (currow && RAY && col >= lastdot)
               /* if we're at the end of a row, close the object */
            {
               end_object(tout);
               tout = 0;
               if (ferror(File_Ptr1))
               {
                  fclose(File_Ptr1);
                  remove(light_name);
                  File_Error(ray_name, 2);
                  return (-1);
               }
            }
         }
         col++;
      }                         /* End of while statement for plotting line  */
   RO++;
 reallythebottom:

   /* stuff that HAS to be done, even in preview mode, goes here */
   if (SPHERE)
   {
      /* incremental sin/cos phi calc */
      if (currow == 0)
      {
         sinphi = oldsinphi2;
         cosphi = oldcosphi2;
      }
      else
      {
         sinphi = twocosdeltaphi * oldsinphi2 - oldsinphi1;
         cosphi = twocosdeltaphi * oldcosphi2 - oldcosphi1;
         oldsinphi1 = oldsinphi2;
         oldsinphi2 = sinphi;
         oldcosphi1 = oldcosphi2;
         oldcosphi2 = cosphi;
      }
   }
   return (0);                  /* decoder needs to know all is well !!! */
}

/* vector version of line draw */
static void _fastcall vdraw_line(double *v1, double *v2, int color)
{
   int x1, y1, x2, y2;
   x1 = (int) v1[0];
   y1 = (int) v1[1];
   x2 = (int) v2[0];
   y2 = (int) v2[1];
   draw_line(x1, y1, x2, y2, color);
}

static void corners(MATRIX m, int show, double *pxmin, double *pymin, double *pzmin, double *pxmax, double *pymax, double *pzmax)
{
   int i, j;
   VECTOR S[2][4];              /* Holds the top an bottom points,
                                 * S[0][]=bottom */

   /* define corners of box fractal is in in x,y,z plane "b" stands for
    * "bottom" - these points are the corners of the screen in the x-y plane.
    * The "t"'s stand for Top - they are the top of the cube where 255 color
    * points hit. */

   *pxmin = *pymin = *pzmin = (int) INT_MAX;
   *pxmax = *pymax = *pzmax = (int) INT_MIN;

   for (j = 0; j < 4; ++j)
      for (i = 0; i < 3; i++)
         S[0][j][i] = S[1][j][i] = 0;

   S[0][1][0] = S[0][2][0] = S[1][1][0] = S[1][2][0] = xdots - 1;
   S[0][2][1] = S[0][3][1] = S[1][2][1] = S[1][3][1] = ydots - 1;
   S[1][0][2] = S[1][1][2] = S[1][2][2] = S[1][3][2] = zcoord - 1;

   for (i = 0; i < 4; ++i)
   {
      /* transform points */
      vmult(S[0][i], m, S[0][i]);
      vmult(S[1][i], m, S[1][i]);

      /* update minimums and maximums */
      if (S[0][i][0] <= *pxmin)
         *pxmin = S[0][i][0];
      if (S[0][i][0] >= *pxmax)
         *pxmax = S[0][i][0];
      if (S[1][i][0] <= *pxmin)
         *pxmin = S[1][i][0];
      if (S[1][i][0] >= *pxmax)
         *pxmax = S[1][i][0];
      if (S[0][i][1] <= *pymin)
         *pymin = S[0][i][1];
      if (S[0][i][1] >= *pymax)
         *pymax = S[0][i][1];
      if (S[1][i][1] <= *pymin)
         *pymin = S[1][i][1];
      if (S[1][i][1] >= *pymax)
         *pymax = S[1][i][1];
      if (S[0][i][2] <= *pzmin)
         *pzmin = S[0][i][2];
      if (S[0][i][2] >= *pzmax)
         *pzmax = S[0][i][2];
      if (S[1][i][2] <= *pzmin)
         *pzmin = S[1][i][2];
      if (S[1][i][2] >= *pzmax)
         *pzmax = S[1][i][2];
   }

   if (show)
   {
      if (persp)
      {
         for (i = 0; i < 4; i++)
         {
            perspective(S[0][i]);
            perspective(S[1][i]);
         }
      }

      /* Keep the box surrounding the fractal */
      for (j = 0; j < 2; j++)
         for (i = 0; i < 4; ++i)
         {
            S[j][i][0] += xxadjust;
            S[j][i][1] += yyadjust;
         }

      draw_rect(S[0][0], S[0][1], S[0][2], S[0][3], 2, 1);      /* Bottom */

      draw_rect(S[0][0], S[1][0], S[0][1], S[1][1], 5, 0);      /* Sides */
      draw_rect(S[0][2], S[1][2], S[0][3], S[1][3], 6, 0);

      draw_rect(S[1][0], S[1][1], S[1][2], S[1][3], 8, 1);      /* Top */
   }
}

/* This function draws a vector from origin[] to direct[] and a box
        around it. The vector and box are transformed or not depending on
        FILLTYPE.
*/

static void draw_light_box(double *origin, double *direct, MATRIX light_m)
{
   VECTOR S[2][4];
   int i, j;
   double temp;

   S[1][0][0] = S[0][0][0] = origin[0];
   S[1][0][1] = S[0][0][1] = origin[1];

   S[1][0][2] = direct[2];

   for (i = 0; i < 2; i++)
   {
      S[i][1][0] = S[i][0][0];
      S[i][1][1] = direct[1];
      S[i][1][2] = S[i][0][2];
      S[i][2][0] = direct[0];
      S[i][2][1] = S[i][1][1];
      S[i][2][2] = S[i][0][2];
      S[i][3][0] = S[i][2][0];
      S[i][3][1] = S[i][0][1];
      S[i][3][2] = S[i][0][2];
   }

   /* transform the corners if necessary */
   if (FILLTYPE == 6)
      for (i = 0; i < 4; i++)
      {
         vmult(S[0][i], light_m, S[0][i]);
         vmult(S[1][i], light_m, S[1][i]);
      }

   /* always use perspective to aid viewing */
   temp = view[2];              /* save perspective distance for a later
                                 * restore */
   view[2] = -P * 300.0 / 100.0;

   for (i = 0; i < 4; i++)
   {
      perspective(S[0][i]);
      perspective(S[1][i]);
   }
   view[2] = temp;              /* Restore perspective distance */

   /* Adjust for aspect */
   for (i = 0; i < 4; i++)
   {
      S[0][i][0] = S[0][i][0] * aspect;
      S[1][i][0] = S[1][i][0] * aspect;
   }

   /* draw box connecting transformed points. NOTE order and COLORS */
   draw_rect(S[0][0], S[0][1], S[0][2], S[0][3], 2, 1);

   vdraw_line(S[0][0], S[1][2], 8);

   /* sides */
   draw_rect(S[0][0], S[1][0], S[0][1], S[1][1], 4, 0);
   draw_rect(S[0][2], S[1][2], S[0][3], S[1][3], 5, 0);

   draw_rect(S[1][0], S[1][1], S[1][2], S[1][3], 3, 1);

   /* Draw the "arrow head" */
   for (i = -3; i < 4; i++)
      for (j = -3; j < 4; j++)
         if (abs(i) + abs(j) < 6)
            plot((int) (S[1][2][0] + i), (int) (S[1][2][1] + j), 10);
}

static void draw_rect(VECTOR V0, VECTOR V1, VECTOR V2, VECTOR V3, int color, int rect)
{
   VECTOR V[4];
   int i;

   /* Since V[2] is not used by vdraw_line don't bother setting it */
   for (i = 0; i < 2; i++)
   {
      V[0][i] = V0[i];
      V[1][i] = V1[i];
      V[2][i] = V2[i];
      V[3][i] = V3[i];
   }
   if (rect)                    /* Draw a rectangle */
   {
      for (i = 0; i < 4; i++)
         if (fabs(V[i][0] - V[(i + 1) % 4][0]) < -2 * bad_check &&
             fabs(V[i][1] - V[(i + 1) % 4][1]) < -2 * bad_check)
            vdraw_line(V[i], V[(i + 1) % 4], color);
   }
   else
      /* Draw 2 lines instead */
   {
      for (i = 0; i < 3; i += 2)
         if (fabs(V[i][0] - V[i + 1][0]) < -2 * bad_check &&
             fabs(V[i][1] - V[i + 1][1]) < -2 * bad_check)
            vdraw_line(V[i], V[i + 1], color);
   }
   return;
}

/* replacement for plot - builds a table of min and max x's instead of plot */
/* called by draw_line as part of triangle fill routine */
static void _fastcall putminmax(int x, int y, int color)
{
   color = 0; /* to supress warning only */
   if (y >= 0 && y < ydots)
   {
      if (x < minmax_x[y].minx)
         minmax_x[y].minx = x;
      if (x > minmax_x[y].maxx)
         minmax_x[y].maxx = x;
   }
}

/*
        This routine fills in a triangle. Extreme left and right values for
        each row are calculated by calling the line function for the sides.
        Then rows are filled in with horizontal lines
*/
#define MAXOFFSCREEN  2    /* allow two of three points to be off screen */

static void _fastcall putatriangle(struct point pt1, struct point pt2, struct point pt3, int color)
{
   int miny, maxy;
   int x, y, xlim;

   /* Too many points off the screen? */
   if ((offscreen(pt1) + offscreen(pt2) + offscreen(pt3)) > MAXOFFSCREEN)
      return;

   p1 = pt1;                    /* needed by interpcolor */
   p2 = pt2;
   p3 = pt3;

   /* fast way if single point or single line */
   if (p1.y == p2.y && p1.x == p2.x)
   {
      plot = fillplot;
      if (p1.y == p3.y && p1.x == p3.x)
         (*plot) (p1.x, p1.y, color);
      else
         draw_line(p1.x, p1.y, p3.x, p3.y, color);
      plot = normalplot;
      return;
   }
   else if ((p3.y == p1.y && p3.x == p1.x) || (p3.y == p2.y && p3.x == p2.x))
   {
      plot = fillplot;
      draw_line(p1.x, p1.y, p2.x, p2.y, color);
      plot = normalplot;
      return;
   }

   /* find min max y */
   miny = maxy = p1.y;
   if (p2.y < miny)
      miny = p2.y;
   else
      maxy = p2.y;
   if (p3.y < miny)
      miny = p3.y;
   else if (p3.y > maxy)
      maxy = p3.y;

   /* only worried about values on screen */
   if (miny < 0)
      miny = 0;
   if (maxy >= ydots)
      maxy = ydots - 1;

   for (y = miny; y <= maxy; y++)
   {
      minmax_x[y].minx = (int) INT_MAX;
      minmax_x[y].maxx = (int) INT_MIN;
   }

   /* set plot to "fake" plot function */
   plot = putminmax;

   /* build table of extreme x's of triangle */
   draw_line(p1.x, p1.y, p2.x, p2.y, 0);
   draw_line(p2.x, p2.y, p3.x, p3.y, 0);
   draw_line(p3.x, p3.y, p1.x, p1.y, 0);

   for (y = miny; y <= maxy; y++)
   {
      xlim = minmax_x[y].maxx;
      for (x = minmax_x[y].minx; x <= xlim; x++)
         (*fillplot) (x, y, color);
   }
   plot = normalplot;
}

static int _fastcall offscreen(struct point pt)
{
   if (pt.x >= 0)
      if (pt.x < xdots)
         if (pt.y >= 0)
            if (pt.y < ydots)
               return (0);      /* point is ok */
   if (abs(pt.x) > 0 - bad_check || abs(pt.y) > 0 - bad_check)
      return (99);              /* point is bad */
   return (1);                  /* point is off the screen */
}

static void _fastcall clipcolor(int x, int y, int color)
{
   if (0 <= x && x < xdots &&
       0 <= y && y < ydots &&
       0 <= color && color < filecolors)
   {
      standardplot(x, y, color);

      if (Targa_Out)
         /* standardplot modifies color in these types */
         if (!(glassestype == 1 || glassestype == 2))
            targa_color(x, y, color);
   }
}

/*********************************************************************/
/* This function is the same as clipcolor but checks for color being */
/* in transparent range. Intended to be called only if transparency  */
/* has been enabled.                                                 */
/*********************************************************************/

static void _fastcall T_clipcolor(int x, int y, int color)
{
   if (0 <= x && x < xdots &&   /* is the point on screen?  */
       0 <= y && y < ydots &&   /* Yes?  */
       0 <= color && color < colors &&  /* Colors in valid range?  */
   /* Lets make sure its not a transparent color  */
       (transparent[0] > color || color > transparent[1]))
   {
      standardplot(x, y, color);/* I guess we can plot then  */
      if (Targa_Out)
         /* standardplot modifies color in these types */
         if (!(glassestype == 1 || glassestype == 2))
            targa_color(x, y, color);
   }
}

/************************************************************************/
/* A substitute for plotcolor that interpolates the colors according    */
/* to the x and y values of three points (p1,p2,p3) which are static in */
/* this routine                                                         */
/*                                                                      */
/*      In Light source modes, color is light value, not actual color   */
/*      Real_Color always contains the actual color                     */
/************************************************************************/

static void _fastcall interpcolor(int x, int y, int color)
{
   int D, d1, d2, d3;

   /* this distance formula is not the usual one - but it has the virtue that
    * it uses ONLY additions (almost) and it DOES go to zero as the points
    * get close. */

   d1 = abs(p1.x - x) + abs(p1.y - y);
   d2 = abs(p2.x - x) + abs(p2.y - y);
   d3 = abs(p3.x - x) + abs(p3.y - y);

   D = (d1 + d2 + d3) << 1;
   if (D)
   {  /* calculate a weighted average of colors long casts prevent integer
         overflow. This can evaluate to zero */
      color = (int) (((long) (d2 + d3) * (long) p1.color +
                      (long) (d1 + d3) * (long) p2.color +
                      (long) (d1 + d2) * (long) p3.color) / D);
   }

   if (0 <= x && x < xdots &&
       0 <= y && y < ydots &&
       0 <= color && color < colors &&
       (transparent[1] == 0 || (int) Real_Color > transparent[1] ||
        transparent[0] > (int) Real_Color))
   {
      if (Targa_Out)
         /* standardplot modifies color in these types */
         if (!(glassestype == 1 || glassestype == 2))
            D = targa_color(x, y, color);

      if (FILLTYPE >= 5) {
         if (Real_V && Targa_Out)
            color = D;
         else
         {
            color = (1 + (unsigned) color * IAmbient) / 256;
            if (color == 0)
               color = 1;
         }
      }
      standardplot(x, y, color);
   }
}

/*
        In non light source modes, both color and Real_Color contain the
        actual pixel color. In light source modes, color contains the
        light value, and Real_Color contains the origninal color

        This routine takes a pixel modifies it for lightshading if appropriate
        and plots it in a Targa file. Used in plot3d.c
*/

int _fastcall targa_color(int x, int y, int color)
{
   unsigned long H, S, V;
   BYTE RGB[3];

   if (FILLTYPE == 2 || glassestype == 1 || glassestype == 2 || truecolor)
      Real_Color = (BYTE)color;       /* So Targa gets interpolated color */

   switch (truemode)
   {
      case 0:
      default:
      {
         RGB[0] = (BYTE)(dacbox[Real_Color][0] << 2); /* Move color space to */
         RGB[1] = (BYTE)(dacbox[Real_Color][1] << 2); /* 256 color primaries */
         RGB[2] = (BYTE)(dacbox[Real_Color][2] << 2); /* from 64 colors */
         break;
      }
      case 1:
      {
         RGB[0] = (BYTE)((realcoloriter >> 16) & 0xff);  /* red   */
         RGB[1] = (BYTE)((realcoloriter >> 8 ) & 0xff);  /* green */
         RGB[2] = (BYTE)((realcoloriter      ) & 0xff);  /* blue  */
         break;
      }
   }

   /* Now lets convert it to HSV */
   R_H(RGB[0], RGB[1], RGB[2], &H, &S, &V);

   /* Modify original S and V components */
   if (FILLTYPE > 4 && !(glassestype == 1 || glassestype == 2))
      /* Adjust for Ambient */
      V = (V * (65535L - (unsigned) (color * IAmbient))) / 65535L;

   if (haze)
   {
      /* Haze lowers sat of colors */
      S = (unsigned long) (S * HAZE_MULT) / 100;
      if (V >= 32640)           /* Haze reduces contrast */
      {
         V = V - 32640;
         V = (unsigned long) ((V * HAZE_MULT) / 100);
         V = V + 32640;
      }
      else
      {
         V = 32640 - V;
         V = (unsigned long) ((V * HAZE_MULT) / 100);
         V = 32640 - V;
      }
   }
   /* Now lets convert it back to RGB. Original Hue, modified Sat and Val */
   H_R(&RGB[0], &RGB[1], &RGB[2], H, S, V);

   if (Real_V)
      V = (35 * (int) RGB[0] + 45 * (int) RGB[1] + 20 * (int) RGB[2]) / 100;

   /* Now write the color triple to its transformed location */
   /* on the disk. */
   targa_writedisk(x + sxoffs, y + syoffs, RGB[0], RGB[1], RGB[2]);

   return ((int) (255 - V));
}

static int set_pixel_buff(BYTE * pixels, BYTE far * fraction, unsigned linelen)
{
   int i;
   if ((evenoddrow++ & 1) == 0) /* even rows are color value */
   {
      for (i = 0; i < (int) linelen; i++)       /* add the fractional part in
                                                 * odd row */
         fraction[i] = pixels[i];
      return (1);
   }
   else
      /* swap */
   {
      BYTE tmp;
      for (i = 0; i < (int) linelen; i++)       /* swap so pixel has color */
      {
         tmp = pixels[i];
         pixels[i] = fraction[i];
         fraction[i] = tmp;
      }
   }
   return (0);
}

/**************************************************************************

  Common routine for printing error messages to the screen for Targa
    and other files

**************************************************************************/

#ifndef XFRACT
static char s_f[] = "%Fs%Fs";
static char s_fff[] = "%Fs%Fs%Fs";
#else
static char s_f[] = "%s%s";
static char s_fff[] = "%s%s%s";
#endif
static FCODE OOPS[] = {"OOPS, "};
static FCODE E1[] = {"can't handle this type of file.\n"};
static FCODE str1[] = {"couldn't open  < "};
static FCODE str3[] = {"image wrong size\n"};
static FCODE outofdisk[] = {"ran out of disk space. < "};

static void File_Error(char *File_Name1, int ERROR)
{
   char msgbuf[200];

   error = ERROR;
   switch (ERROR)
   {
   case 1:                      /* Can't Open */
#ifndef XFRACT
      sprintf(msgbuf, "%Fs%Fs%s >", (char far *)OOPS, (char far *)str1, File_Name1);
#else
      sprintf(msgbuf, "%s%s%s >", OOPS, str1, File_Name1);
#endif
      break;
   case 2:                      /* Not enough room */
#ifndef XFRACT
      sprintf(msgbuf, "%Fs%Fs%s >", (char far *)OOPS, (char far *)outofdisk, File_Name1);
#else
      sprintf(msgbuf, "%s%s%s >", OOPS, outofdisk, File_Name1);
#endif
      break;
   case 3:                      /* Image wrong size */
      sprintf(msgbuf, s_f, (char far *)OOPS, (char far *)str3);
      break;
   case 4:                      /* Wrong file type */
      sprintf(msgbuf, s_f, (char far *)OOPS, (char far *)E1);
      break;
   }
   stopmsg(0, msgbuf);
   return;
}


/************************************************************************/
/*                                                                      */
/*   This function opens a TARGA_24 file for reading and writing. If    */
/*   its a new file, (overlay == 0) it writes a header. If it is to     */
/*   overlay an existing file (overlay == 1) it copies the original     */
/*   header whose lenght and validity was determined in                 */
/*   Targa_validate.                                                    */
/*                                                                      */
/*   It Verifies there is enough disk space, and leaves the file        */
/*   at the start of the display data area.                             */
/*                                                                      */
/*   If this is an overlay, closes source and copies to "targa_temp"    */
/*   If there is an error close the file.                               */
/*                                                                      */
/* **********************************************************************/

int startdisk1(char *File_Name2, FILE * Source, int overlay)
{
   int i, j, k, inc;
   FILE *fps;

   /* Open File for both reading and writing */
   if ((fps = dir_fopen(workdir,File_Name2, "w+b")) == NULL)
   {
      File_Error(File_Name2, 1);
      return (-1);              /* Oops, somethings wrong! */
   }

   inc = 1;                     /* Assume we are overlaying a file */

   /* Write the header */
   if (overlay)                 /* We are overlaying a file */
      for (i = 0; i < T_header_24; i++) /* Copy the header from the Source */
         fputc(fgetc(Source), fps);
   else
   {                            /* Write header for a new file */
      /* ID field size = 0, No color map, Targa type 2 file */
      for (i = 0; i < 12; i++)
      {
         if (i == 0 && truecolor != 0)
         {
            set_upr_lwr();
            fputc(4, fps); /* make room to write an extra number */
            T_header_24 = 18 + 4;
         }
         else if (i == 2)
            fputc(i, fps);
         else
            fputc(0, fps);
      }
      /* Write image size  */
      for (i = 0; i < 4; i++)
         fputc(upr_lwr[i], fps);
      fputc(T24, fps);          /* Targa 24 file */
      fputc(T32, fps);          /* Image at upper left */
      inc = 3;
   }

   if(truecolor) /* write maxit */
   {
      fputc((BYTE)(maxit       & 0xff), fps);
      fputc((BYTE)((maxit>>8 ) & 0xff), fps);
      fputc((BYTE)((maxit>>16) & 0xff), fps);
      fputc((BYTE)((maxit>>24) & 0xff), fps);
   }

   /* Finished with the header, now lets work on the display area  */
   for (i = 0; i < ydots; i++)  /* "clear the screen" (write to the disk) */
   {
      for (j = 0; j < line_length1; j = j + inc)
      {
         if (overlay)
            fputc(fgetc(Source), fps);
         else
            for (k = 2; k > -1; k--)
               fputc(back_color[k], fps);       /* Targa order (B, G, R) */
      }
      if (ferror(fps))
      {
         /* Almost certainly not enough disk space  */
         fclose(fps);
         if(overlay)
            fclose(Source);
         dir_remove(workdir,File_Name2);
         File_Error(File_Name2, 2);
         return (-2);
      }
      if (keypressed())
         return (-3);
   }

   if (targa_startdisk(fps, T_header_24) != 0)
   {
      enddisk();
      dir_remove(workdir,File_Name2);
      return (-4);
   }
   return (0);
}

int targa_validate(char *File_Name)
{
   FILE *fp;
   int i;
#if 0
   int j = 0;
#endif

   /* Attempt to open source file for reading */
   if ((fp = dir_fopen(workdir,File_Name, "rb")) == NULL)
   {
      File_Error(File_Name, 1);
      return (-1);              /* Oops, file does not exist */
   }

   T_header_24 += fgetc(fp);    /* Check ID field and adjust header size */

   if (fgetc(fp))               /* Make sure this is an unmapped file */
   {
      File_Error(File_Name, 4);
      return (-1);
   }

   if (fgetc(fp) != 2)          /* Make sure it is a type 2 file */
   {
      File_Error(File_Name, 4);
      return (-1);
   }

   /* Skip color map specification */
   for (i = 0; i < 5; i++)
      fgetc(fp);

   for (i = 0; i < 4; i++)
   {
      /* Check image origin */
      fgetc(fp);
#if 0
      if (j != 0)
      {
         File_Error(File_Name, 4);
         return (-1);
      }
#endif
   }
   /* Check Image specs */
   for (i = 0; i < 4; i++)
      if (fgetc(fp) != (int) upr_lwr[i])
      {
         File_Error(File_Name, 3);
         return (-1);
      }

   if (fgetc(fp) != (int) T24)
      error = 4;                /* Is it a targa 24 file? */
   if (fgetc(fp) != (int) T32)
      error = 4;                /* Is the origin at the upper left? */
   if (error == 4)
   {
      File_Error(File_Name, 4);
      return (-1);
   }
   rewind(fp);

   /* Now that we know its a good file, create a working copy */
   if (startdisk1(targa_temp, fp, 1))
      return (-1);

   fclose(fp);                  /* Close the source */

   T_Safe = 1;                  /* Original file successfully copied to
                                 * targa_temp */
   return (0);
}

static int R_H(BYTE R, BYTE G, BYTE B, unsigned long *H, unsigned long *S, unsigned long *V)
{
   unsigned long R1, G1, B1, DENOM;
   BYTE MIN;

   *V = R;
   MIN = G;
   if (R < G)
   {
      *V = G;
      MIN = R;
      if (G < B)
         *V = B;
      if (B < R)
         MIN = B;
   }
   else
   {
      if (B < G)
         MIN = B;
      if (R < B)
         *V = B;
   }
   DENOM = *V - MIN;
   if (*V != 0 && DENOM != 0)
   {
      *S = ((DENOM << 16) / *V) - 1;
   }
   else
      *S = 0;      /* Color is black! and Sat has no meaning */
   if (*S == 0)    /* R=G=B => shade of grey and Hue has no meaning */
   {
      *H = 0;
      *V = *V << 8;
      return (1);               /* v or s or both are 0 */
   }
   if (*V == MIN)
   {
      *H = 0;
      *V = *V << 8;
      return (0);
   }
   R1 = (((*V - R) * 60) << 6) / DENOM; /* distance of color from red   */
   G1 = (((*V - G) * 60) << 6) / DENOM; /* distance of color from green */
   B1 = (((*V - B) * 60) << 6) / DENOM; /* distance of color from blue  */
   if (*V == R) {
      if (MIN == G)
         *H = (300 << 6) + B1;
      else
         *H = (60 << 6) - G1;
   }
   if (*V == G) {
      if (MIN == B)
         *H = (60 << 6) + R1;
      else
         *H = (180 << 6) - B1;
   }
   if (*V == B) {
      if (MIN == R)
         *H = (180 << 6) + G1;
      else
         *H = (300 << 6) - R1;
    }
   *V = *V << 8;
   return (0);
}

static int H_R(BYTE *R, BYTE *G, BYTE *B, unsigned long H, unsigned long S, unsigned long V)
{
   unsigned long P1, P2, P3;
   int RMD, I;

   if (H >= 23040)
      H = H % 23040;            /* Makes h circular  */
   I = (int) (H / 3840);
   RMD = (int) (H % 3840);      /* RMD = fractional part of H    */

   P1 = ((V * (65535L - S)) / 65280L) >> 8;
   P2 = (((V * (65535L - (S * RMD) / 3840)) / 65280L) - 1) >> 8;
   P3 = (((V * (65535L - (S * (3840 - RMD)) / 3840)) / 65280L)) >> 8;
   V = V >> 8;
   switch (I)
   {
   case 0:
      *R = (BYTE) V;
      *G = (BYTE) P3;
      *B = (BYTE) P1;
      break;
   case 1:
      *R = (BYTE) P2;
      *G = (BYTE) V;
      *B = (BYTE) P1;
      break;
   case 2:
      *R = (BYTE) P1;
      *G = (BYTE) V;
      *B = (BYTE) P3;
      break;
   case 3:
      *R = (BYTE) P1;
      *G = (BYTE) P2;
      *B = (BYTE) V;
      break;
   case 4:
      *R = (BYTE) P3;
      *G = (BYTE) P1;
      *B = (BYTE) V;
      break;
   case 5:
      *R = (BYTE) V;
      *G = (BYTE) P1;
      *B = (BYTE) P2;
      break;
   }
   return (0);
}


/***************************************************************************/
/*                                                                         */
/* EB & DG fiddled with outputs for Rayshade so they work. with v4.x.      */
/* EB == eli brandt.     ebrandt@jarthur.claremont.edu                     */
/* DG == dan goldwater.  daniel_goldwater@brown.edu & dgold@math.umass.edu */
/*  (NOTE: all the stuff we fiddled with is commented with "EB & DG" )     */
/* general raytracing code info/notes:                                     */
/*                                                                         */
/*  ray == 0 means no raytracer output  ray == 7 is for dxf                */
/*  ray == 1 is for dkb/pov             ray == 4 is for mtv                */
/*  ray == 2 is for vivid               ray == 5 is for rayshade           */
/*  ray == 3 is for raw                 ray == 6 is for acrospin           */
/*                                                                         */
/*  rayshade needs counterclockwise triangles.  raytracers that support    */
/*  the 'heightfield' primitive include rayshade and pov.  anyone want to  */
/*  write code to make heightfields?  they are *MUCH* faster to trace than */
/*  triangles when doing landscapes...                                     */
/*                                                                         */
/*  stuff EB & DG changed:                                                 */
/*  made the rayshade output create a "grid" aggregate object (one of      */
/*  rayshade's primitives), instead  of a global grid.  as a result, the   */
/*  grid can be optimized based on the number of triangles.                */
/*  the z component of the grid can always be 1 since the surface formed   */
/*  by the triangles is flat                                               */
/*  (ie, it doesnt curve over itself).  this is a major optimization.      */
/*  the x and y grid size is also optimized for a 4:3 aspect ratio image,  */
/*  to get the fewest possible traingles in each grid square.              */
/*  also, we fixed the rayshade code so it actually produces output that   */
/*  works with rayshade.                                                   */
/*  (maybe the old code was for a really old version of rayshade?).        */
/*                                                                         */
/***************************************************************************/

/********************************************************************/
/*                                                                  */
/*  This routine writes a header to a ray tracer data file. It      */
/*  Identifies the version of FRACTINT which created it an the      */
/*  key 3D parameters in effect at the time.                        */
/*                                                                  */
/********************************************************************/

static FCODE declare[] = {"DECLARE       "};
static FCODE frac_default[] = {"F_Dflt"};
static FCODE s_color[] = {"COLOR  "};
static FCODE dflt[] = {"RED 0.8 GREEN 0.4 BLUE 0.1\n"};
static FCODE d_color[] = {"0.8 0.4 0.1"};
static FCODE r_surf[] = {"0.95 0.05 5 0 0\n"};
static FCODE surf[] = {"surf={diff="};
/* EB & DG: changed "surface T" to "applysurf" and "diff" to "diffuse" */
static FCODE rs_surf[] = {"applysurf diffuse "};
static FCODE end[] = {"END_"};
static FCODE plane[] = {"PLANE"};
static FCODE m1[] = {"-1.0 "};
static FCODE one[] = {" 1.0 "};
static FCODE z[] = {" 0.0 "};
static FCODE bnd_by[] = {" BOUNDED_BY\n"};
static FCODE end_bnd[] = {" END_BOUND\n"};
static FCODE inter[] = {"INTERSECTION\n"};
#ifndef XFRACT
static char fmt[] = "   %Fs <%Fs%Fs%Fs> % #4.3f %Fs%Fs\n";
#else
static char fmt[] = "   %s <%s%s%s> % #4.3f %s%s\n";
#endif
static char dxf_begin[] =
{"  0\nSECTION\n  2\nTABLES\n  0\nTABLE\n  2\nLAYER\n\
 70\n     2\n  0\nLAYER\n  2\n0\n 70\n     0\n 62\n     7\n  6\nCONTINUOUS\n\
  0\nLAYER\n  2\nFRACTAL\n 70\n    64\n 62\n     1\n  6\nCONTINUOUS\n  0\n\
ENDTAB\n  0\nENDSEC\n  0\nSECTION\n  2\nENTITIES\n"};
static char dxf_3dface[] = {"  0\n3DFACE\n  8\nFRACTAL\n 62\n%3d\n"};
static char dxf_vertex[] = {"%3d\n%g\n"};
static char dxf_end[] = {"  0\nENDSEC\n  0\nEOF\n"};
static FCODE composite[] = {"COMPOSITE"};
static FCODE object[] = {"OBJECT"};
static FCODE triangle[] = {"TRIANGLE "};
static FCODE l_tri[] = {"triangle"};
static FCODE texture[] = {"TEXTURE\n"};
/* static FCODE end_texture[] = {" END_TEXTURE\n"}; */
static FCODE red[] = {"RED"};
static FCODE green[] = {"GREEN"};
static FCODE blue[] = {"BLUE"};
static FCODE frac_texture[] = {"      AMBIENT 0.25 DIFFUSE 0.75"};
static FCODE polygon[] = {"polygon={points=3;"};
static FCODE vertex[] = {" vertex =  "};
static FCODE d_vert[] = {"      <"};
static char f1[] = "% #4.4f ";
/* EB & DG: changed this to much better values */
static FCODE grid[] =
{"screen 640 480\neyep 0 2.1 0.8\nlookp 0 0 -0.95\nlight 1 point -2 1 1.5\n"};
static FCODE grid2[] = {"background .3 0 0\nreport verbose\n"};

static char s_n[] = "\n";
static char f2[] = "R%dC%d R%dC%d\n";
static FCODE ray_comment1[] =
   {"/* make a gridded aggregate. this size grid is fast for landscapes. */\n"};
static FCODE ray_comment2[] =
   {"/* make z grid = 1 always for landscapes. */\n\n"};
static FCODE grid3[] = {"grid 33 25 1\n"};

static int _fastcall RAY_Header(void)
{
   /* Open the ray tracing output file */
   check_writefile(ray_name, ".ray");
   if ((File_Ptr1 = fopen(ray_name, "w")) == NULL)
      return (-1);              /* Oops, somethings wrong! */

   if (RAY == 2)
      fprintf(File_Ptr1, "//");
   if (RAY == 4)
      fprintf(File_Ptr1, "#");
   if (RAY == 5)
      fprintf(File_Ptr1, "/*\n");
   if (RAY == 6)
      fprintf(File_Ptr1, "--");
   if (RAY == 7)
      fprintf(File_Ptr1, dxf_begin);

   if (RAY != 7)
      fprintf(File_Ptr1, banner, (char far *)s3, release / 100., (char far *)s3a);

   if (RAY == 5)
      fprintf(File_Ptr1, "*/\n");


   /* Set the default color */
   if (RAY == 1)
   {
      fprintf(File_Ptr1, s_f, (char far *)declare, (char far *)frac_default);
      fprintf(File_Ptr1, " = ");
      fprintf(File_Ptr1, s_f, (char far *)s_color, (char far *)dflt);
   }
   if (BRIEF)
   {
      if (RAY == 2)
      {
         fprintf(File_Ptr1, s_f, (char far *)surf, (char far *)d_color);
         fprintf(File_Ptr1, ";}\n");
      }
      if (RAY == 4)
      {
         fprintf(File_Ptr1, "f ");
         fprintf(File_Ptr1, s_f, (char far *)d_color, (char far *)r_surf);
      }
      if (RAY == 5)
         fprintf(File_Ptr1, s_f, (char far *)rs_surf, (char far *)d_color);
   }
   if (RAY != 7)
      fprintf(File_Ptr1, s_n);

   /* EB & DG: open "grid" opject, a speedy way to do aggregates in rayshade */
   if (RAY == 5)
      fprintf(File_Ptr1, s_fff, (char far *)ray_comment1, (char far *)ray_comment2, (char far *)grid3);

   if (RAY == 6)
#ifndef XFRACT
      fprintf(File_Ptr1, "%Fs", (char far *)acro_s1);
#else
      fprintf(File_Ptr1, "%s", acro_s1);
#endif

   return (0);
}


/********************************************************************/
/*                                                                  */
/*  This routine describes the triangle to the ray tracer, it       */
/*  sets the color of the triangle to the average of the color      */
/*  of its verticies and sets the light parameters to arbitrary     */
/*  values.                                                         */
/*                                                                  */
/*  Note: numcolors (number of colors in the source                 */
/*  file) is used instead of colors (number of colors avail. with   */
/*  display) so you can generate ray trace files with your LCD      */
/*  or monochrome display                                           */
/*                                                                  */
/********************************************************************/

static int _fastcall out_triangle(struct f_point pt1, struct f_point pt2, struct f_point pt3, int c1, int c2, int c3)
{
   int i, j;
   float c[3];
   float pt_t[3][3];

   /* Normalize each vertex to screen size and adjust coordinate system */
   pt_t[0][0] = 2 * pt1.x / xdots - 1;
   pt_t[0][1] = (2 * pt1.y / ydots - 1);
   pt_t[0][2] = -2 * pt1.color / numcolors - 1;
   pt_t[1][0] = 2 * pt2.x / xdots - 1;
   pt_t[1][1] = (2 * pt2.y / ydots - 1);
   pt_t[1][2] = -2 * pt2.color / numcolors - 1;
   pt_t[2][0] = 2 * pt3.x / xdots - 1;
   pt_t[2][1] = (2 * pt3.y / ydots - 1);
   pt_t[2][2] = -2 * pt3.color / numcolors - 1;

   /* Color of triangle is average of colors of its verticies */
   if (!BRIEF)
      for (i = 0; i <= 2; i++)
#ifdef __SVR4
         c[i] = (float) ((int)(dacbox[c1][i] + dacbox[c2][i] + dacbox[c3][i])
            / (3 * 63));
#else
         c[i] = (float) (dacbox[c1][i] + dacbox[c2][i] + dacbox[c3][i])
            / (3 * 63);
#endif

   /* get rid of degenerate triangles: any two points equal */
   if ((pt_t[0][0] == pt_t[1][0] &&
        pt_t[0][1] == pt_t[1][1] &&
        pt_t[0][2] == pt_t[1][2]) ||

       (pt_t[0][0] == pt_t[2][0] &&
        pt_t[0][1] == pt_t[2][1] &&
        pt_t[0][2] == pt_t[2][2]) ||

       (pt_t[2][0] == pt_t[1][0] &&
        pt_t[2][1] == pt_t[1][1] &&
        pt_t[2][2] == pt_t[1][2]))
      return (0);

   /* Describe the triangle */
#ifndef XFRACT
   if (RAY == 1)
      fprintf(File_Ptr1, " %Fs\n  %Fs", (char far *)object, (char far *)triangle);
   if (RAY == 2 && !BRIEF)
      fprintf(File_Ptr1, "%Fs", (char far *)surf);
#else
   if (RAY == 1)
      fprintf(File_Ptr1, " %s\n  %s", object, triangle);
   if (RAY == 2 && !BRIEF)
      fprintf(File_Ptr1, "%s", surf);
#endif
   if (RAY == 4 && !BRIEF)
      fprintf(File_Ptr1, "f");
   if (RAY == 5 && !BRIEF)
#ifndef XFRACT
      fprintf(File_Ptr1, "%Fs", (char far *)rs_surf);
#else
      fprintf(File_Ptr1, "%s", rs_surf);
#endif

   if (!BRIEF && RAY != 1 && RAY != 7)
      for (i = 0; i <= 2; i++)
         fprintf(File_Ptr1, f1, c[i]);

   if (RAY == 2)
   {
      if (!BRIEF)
         fprintf(File_Ptr1, ";}\n");
#ifndef XFRACT
      fprintf(File_Ptr1, "%Fs", (char far *)polygon);
#else
      fprintf(File_Ptr1, "%s", polygon);
#endif
   }
   if (RAY == 4)
   {
      if (!BRIEF)
#ifndef XFRACT
         fprintf(File_Ptr1, "%Fs", (char far *)r_surf);
#else
         fprintf(File_Ptr1, "%s", r_surf);
#endif
      fprintf(File_Ptr1, "p 3");
   }
   if (RAY == 5)
   {
      if (!BRIEF)
         fprintf(File_Ptr1, s_n);
      /* EB & DG: removed "T" after "triangle" */
#ifndef XFRACT
      fprintf(File_Ptr1, "%Fs", (char far *)l_tri);
#else
      fprintf(File_Ptr1, "%s", l_tri);
#endif
   }

   if (RAY == 7)
      fprintf(File_Ptr1, dxf_3dface, min(255, max(1, c1)));

   for (i = 0; i <= 2; i++)     /* Describe each  Vertex  */
   {
      if (RAY != 7)
         fprintf(File_Ptr1, s_n);

#ifndef XFRACT
      if (RAY == 1)
         fprintf(File_Ptr1, "%Fs", (char far *)d_vert);
      if (RAY == 2)
         fprintf(File_Ptr1, "%Fs", (char far *)vertex);
#else
      if (RAY == 1)
         fprintf(File_Ptr1, "%s", d_vert);
      if (RAY == 2)
         fprintf(File_Ptr1, "%s", vertex);
#endif
      if (RAY > 3 && RAY != 7)
         fprintf(File_Ptr1, " ");

      for (j = 0; j <= 2; j++)
      {
         if (RAY == 7)
         {
            /* write 3dface entity to dxf file */
            fprintf(File_Ptr1, dxf_vertex, 10 * (j + 1) + i, pt_t[i][j]);
            if (i == 2)         /* 3dface needs 4 vertecies */
               fprintf(File_Ptr1, dxf_vertex, 10 * (j + 1) + i + 1,
                  pt_t[i][j]);
         }
         else if (!(RAY == 4 || RAY == 5))
            fprintf(File_Ptr1, f1, pt_t[i][j]); /* Right handed */
         else
            fprintf(File_Ptr1, f1, pt_t[2 - i][j]);     /* Left handed */
      }

      if (RAY == 1)
         fprintf(File_Ptr1, ">");
      if (RAY == 2)
         fprintf(File_Ptr1, ";");
   }

   if (RAY == 1)
   {
#ifndef XFRACT
      fprintf(File_Ptr1, " %Fs%Fs\n", (char far *)end, (char far *)triangle);
#else
      fprintf(File_Ptr1, " %s%s\n", end, triangle);
#endif
      if (!BRIEF)
      {
#ifndef XFRACT
         fprintf(File_Ptr1, "  %Fs"
                 "      %Fs%Fs% #4.4f %Fs% #4.4f %Fs% #4.4f\n"
                 "%Fs"
                 " %Fs%Fs",
#else
         fprintf(File_Ptr1,
                 "  %s   %s%s% #4.4f %s% #4.4f %s% #4.4f\n%s %s%s",
#endif
                 (char far *)texture,
                 (char far *)s_color,
                 (char far *)red,   c[0],
                 (char far *)green, c[1],
                 (char far *)blue,  c[2],
                 (char far *)frac_texture,
                 (char far *)end,
                 (char far *)texture);
      }
#ifndef XFRACT
      fprintf(File_Ptr1, "  %Fs%Fs  %Fs%Fs",
#else
      fprintf(File_Ptr1, "  %s%s  %s%s",
#endif
              (char far *)s_color, (char far *)frac_default,
              (char far *)end, (char far *)object);
      triangle_bounds(pt_t);    /* update bounding info */
   }
   if (RAY == 2)
      fprintf(File_Ptr1, "}");
   if (RAY == 3 && !BRIEF)
      fprintf(File_Ptr1, s_n);

   if (RAY != 7)
      fprintf(File_Ptr1, s_n);

   return (0);
}

/********************************************************************/
/*                                                                  */
/*  This routine calculates the min and max values of a triangle    */
/*  for use in creating ray tracer data files. The values of min    */
/*  and max x, y, and z are assumed to be global.                   */
/*                                                                  */
/********************************************************************/

static void _fastcall triangle_bounds(float pt_t[3][3])
{
   int i, j;

   for (i = 0; i <= 2; i++)
      for (j = 0; j <= 2; j++)
      {
         if (pt_t[i][j] < min_xyz[j])
            min_xyz[j] = pt_t[i][j];
         if (pt_t[i][j] > max_xyz[j])
            max_xyz[j] = pt_t[i][j];
      }
   return;
}

/********************************************************************/
/*                                                                  */
/*  This routine starts a composite object for ray trace data files */
/*                                                                  */
/********************************************************************/

static int _fastcall start_object(void)
{
   if (RAY != 1)
      return (0);

   /* Reset the min/max values, for bounding box  */
   min_xyz[0] = min_xyz[1] = min_xyz[2] = (float)999999.0;
   max_xyz[0] = max_xyz[1] = max_xyz[2] = (float)-999999.0;

#ifndef XFRACT
   fprintf(File_Ptr1, "%Fs\n", (char far *)composite);
#else
   fprintf(File_Ptr1, "%s\n", composite);
#endif
   return (0);
}

/********************************************************************/
/*                                                                  */
/*  This routine adds a bounding box for the triangles drawn        */
/*  in the last block and completes the composite object created.   */
/*  It uses the globals min and max x,y and z calculated in         */
/*  z calculated in Triangle_Bounds().                              */
/*                                                                  */
/********************************************************************/

static int _fastcall end_object(int triout)
{
   if (RAY == 7)
      return (0);
   if (RAY == 1)
   {
      if (triout)
      {
         /* Make sure the bounding box is slightly larger than the object */
         int i;
         for (i = 0; i <= 2; i++)
         {
            if (min_xyz[i] == max_xyz[i])
            {
               min_xyz[i] -= (float)0.01;
               max_xyz[i] += (float)0.01;
            }
            else
            {
               min_xyz[i] -= (max_xyz[i] - min_xyz[i]) * (float)0.01;
               max_xyz[i] += (max_xyz[i] - min_xyz[i]) * (float)0.01;
            }
         }

         /* Add the bounding box info */
#ifndef XFRACT
         fprintf(File_Ptr1, "%Fs  %Fs", (char far *)bnd_by, (char far *)inter);
#else
         fprintf(File_Ptr1, "%s  %s", bnd_by, inter);
#endif
         fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)m1, (char far *)z, (char far *)z, -min_xyz[0], (char far *)end, (char far *)plane);
         fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)one, (char far *)z, (char far *)z, max_xyz[0], (char far *)end, (char far *)plane);
         fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)z, (char far *)m1, (char far *)z, -min_xyz[1], (char far *)end, (char far *)plane);
         fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)z, (char far *)one, (char far *)z, max_xyz[1], (char far *)end, (char far *)plane);
         fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)z, (char far *)z, (char far *)m1, -min_xyz[2], (char far *)end, (char far *)plane);
         fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)z, (char far *)z, (char far *)one, max_xyz[2], (char far *)end, (char far *)plane);
#ifndef XFRACT
         fprintf(File_Ptr1, "  %Fs%Fs%Fs", (char far *)end,
                (char far *)inter, (char far *)end_bnd);
#else
         fprintf(File_Ptr1, "  %s%s%s", end, inter, end_bnd);
#endif
      }

      /* Complete the composite object statement */
#ifndef XFRACT
      fprintf(File_Ptr1, "%Fs%Fs\n", (char far *)end, (char far *)composite);
#else
      fprintf(File_Ptr1, "%s%s\n", end, composite);
#endif
   }

   if (RAY != 6 && RAY != 5)
      fprintf(File_Ptr1, s_n);    /* EB & DG: too many newlines */

   return (0);
}

static void line3d_cleanup(void)
{
   int i, j;
   if (RAY && File_Ptr1)
   {                            /* Finish up the ray tracing files */
      static FCODE n_ta[] = {"{ No. Of Triangles = "};
      if (RAY != 5 && RAY != 7)
         fprintf(File_Ptr1, s_n); /* EB & DG: too many newlines */
      if (RAY == 2)
         fprintf(File_Ptr1, "\n\n//");
      if (RAY == 4)
         fprintf(File_Ptr1, "\n\n#");

      if (RAY == 5)
#ifndef XFRACT
         /* EB & DG: end grid aggregate */
         fprintf(File_Ptr1, "end\n\n/*good landscape:*/\n%Fs%Fs\n/*",
            (char far *)grid, (char far *)grid2);
#else
         /* EB & DG: end grid aggregate */
         fprintf(File_Ptr1, "end\n\n/*good landscape:*/\n%s%s\n/*",
            grid, grid2);
#endif
      if (RAY == 6)
      {
#ifndef XFRACT
         fprintf(File_Ptr1, "%Fs", (char far *)acro_s2);
#else
         fprintf(File_Ptr1, "%s", acro_s2);
#endif
         for (i = 0; i < RO; i++)
            for (j = 0; j <= CO_MAX; j++)
            {
               if (j < CO_MAX)
                  fprintf(File_Ptr1, f2, i, j, i, j + 1);
               if (i < RO - 1)
                  fprintf(File_Ptr1, f2, i, j, i + 1, j);
               if (i && i < RO && j < CO_MAX)
                  fprintf(File_Ptr1, f2, i, j, i - 1, j + 1);
            }
         fprintf(File_Ptr1, "\n\n--");
      }
      if (RAY != 7)
#ifndef XFRACT
         fprintf(File_Ptr1, "%Fs%ld }*/\n\n", (char far *)n_ta, num_tris);
#else
         fprintf(File_Ptr1, "%s%ld }*/\n\n", n_ta, num_tris);
#endif
      if (RAY == 7)
         fprintf(File_Ptr1, dxf_end);
      fclose(File_Ptr1);
      File_Ptr1 = NULL;
   }
   if (Targa_Out)
   {                            /* Finish up targa files */
      T_header_24 = 18;         /* Reset Targa header size */
      enddisk();
      if (!debugflag && (!T_Safe || error) && Targa_Overlay)
      {
         dir_remove(workdir, light_name);
         rename(targa_temp, light_name);
      }
      if (!debugflag && Targa_Overlay)
         dir_remove(workdir, targa_temp);
   }
   error = T_Safe = 0;
}

static void set_upr_lwr(void)
{
   upr_lwr[0] = (BYTE)(xdots & 0xff);
   upr_lwr[1] = (BYTE)(xdots >> 8);
   upr_lwr[2] = (BYTE)(ydots & 0xff);
   upr_lwr[3] = (BYTE)(ydots >> 8);
   line_length1 = 3 * xdots;    /* line length @ 3 bytes per pixel  */
}

static int first_time(int linelen, VECTOR v)
{
   int err;
   MATRIX lightm;               /* m w/no trans, keeps obj. on screen */
   float twocosdeltatheta;
   double xval, yval, zval;     /* rotation values */
   /* corners of transformed xdotx by ydots x colors box */
   double xmin, ymin, zmin, xmax, ymax, zmax;
   int i, j;
   double v_length;
   VECTOR origin, direct, tmp;
   float theta, theta1, theta2; /* current,start,stop latitude */
   float phi1, phi2;            /* current start,stop longitude */
   float deltatheta;            /* increment of latitude */
   outln_cleanup = line3d_cleanup;

   calctime = evenoddrow = 0;
   /* mark as in-progress, and enable <tab> timer display */
   calc_status = 1;

   IAmbient = (unsigned int) (255 * (float) (100 - Ambient) / 100.0);
   if (IAmbient < 1)
      IAmbient = 1;

   num_tris = 0;

   /* Open file for RAY trace output and write header */
   if (RAY)
   {
      RAY_Header();
      xxadjust = yyadjust = 0;  /* Disable shifting in ray tracing */
      xshift = yshift = 0;
   }

   CO_MAX = CO = RO = 0;

   set_upr_lwr();
   error = 0;

   if (whichimage < 2)
      T_Safe = 0; /* Not safe yet to mess with the source image */

   if (Targa_Out && !((glassestype == 1 || glassestype == 2)
                 && whichimage == 2))
   {
      if (Targa_Overlay)
      {
         /* Make sure target file is a supportable Targa File */
         if (targa_validate(light_name))
            return (-1);
      }
      else
      {
         check_writefile(light_name, ".tga");
         if (startdisk1(light_name, NULL, 0))   /* Open new file */
            return (-1);
      }
   }

   rand_factor = 14 - RANDOMIZE;

   zcoord = filecolors;

   if((err=line3dmem()) != 0)
      return(err);


   /* get scale factors */
   sclx = XSCALE / 100.0;
   scly = YSCALE / 100.0;
   if (ROUGH)
      sclz = -ROUGH / 100.0;
   else
      rscale = sclz = -0.0001;  /* if rough=0 make it very flat but plot
                                 * something */

   /* aspect ratio calculation - assume screen is 4 x 3 */
   aspect = (double) xdots *.75 / (double) ydots;

   if (SPHERE == FALSE)         /* skip this slow stuff in sphere case */
   {
      /*********************************************************************/
      /* What is done here is to create a single matrix, m, which has      */
      /* scale, rotation, and shift all combined. This allows us to use    */
      /* a single matrix to transform any point. Additionally, we create   */
      /* two perspective vectors.                                          */
      /*                                                                   */
      /* Start with a unit matrix. Add scale and rotation. Then calculate  */
      /* the perspective vectors. Finally add enough translation to center */
      /* the final image plus whatever shift the user has set.             */
      /*********************************************************************/

      /* start with identity */
      identity(m);
      identity(lightm);

      /* translate so origin is in center of box, so that when we rotate */
      /* it, we do so through the center */
      trans((double) xdots / (-2.0), (double) ydots / (-2.0),
            (double) zcoord / (-2.0), m);
      trans((double) xdots / (-2.0), (double) ydots / (-2.0),
            (double) zcoord / (-2.0), lightm);

      /* apply scale factors */
      scale(sclx, scly, sclz, m);
      scale(sclx, scly, sclz, lightm);

      /* rotation values - converting from degrees to radians */
      xval = XROT / 57.29577;
      yval = YROT / 57.29577;
      zval = ZROT / 57.29577;

      if (RAY)
      {
         xval = yval = zval = 0;
      }

      xrot(xval, m);
      xrot(xval, lightm);
      yrot(yval, m);
      yrot(yval, lightm);
      zrot(zval, m);
      zrot(zval, lightm);

      /* Find values of translation that make all x,y,z negative */
      /* m current matrix */
      /* 0 means don't show box */
      /* returns minimum and maximum values of x,y,z in fractal */
      corners(m, 0, &xmin, &ymin, &zmin, &xmax, &ymax, &zmax);
   }

   /* perspective 3D vector - lview[2] == 0 means no perspective */

   /* set perspective flag */
   persp = 0;
   if (ZVIEWER != 0)
      persp = 1;

   /* set up view vector, and put viewer in center of screen */
   lview[0] = xdots >> 1;
   lview[1] = ydots >> 1;

   /* z value of user's eye - should be more negative than extreme negative
    * part of image */
   if (SPHERE)                  /* sphere case */
      lview[2] = -(long) ((double) ydots * (double) ZVIEWER / 100.0);
   else                         /* non-sphere case */
      lview[2] = (long) ((zmin - zmax) * (double) ZVIEWER / 100.0);

   view[0] = lview[0];
   view[1] = lview[1];
   view[2] = lview[2];
   lview[0] = lview[0] << 16;
   lview[1] = lview[1] << 16;
   lview[2] = lview[2] << 16;

   if (SPHERE == FALSE)         /* sphere skips this */
   {
      /* translate back exactly amount we translated earlier plus enough to
       * center image so maximum values are non-positive */
      trans(((double) xdots - xmax - xmin) / 2,
            ((double) ydots - ymax - ymin) / 2, -zmax, m);

      /* Keep the box centered and on screen regardless of shifts */
      trans(((double) xdots - xmax - xmin) / 2,
            ((double) ydots - ymax - ymin) / 2, -zmax, lightm);

      trans((double) (xshift), (double) (-yshift), 0.0, m);

      /* matrix m now contains ALL those transforms composed together !!
       * convert m to long integers shifted 16 bits */
      for (i = 0; i < 4; i++)
         for (j = 0; j < 4; j++)
            llm[i][j] = (long) (m[i][j] * 65536.0);

   }
   else
      /* sphere stuff goes here */
   {
      /* Sphere is on side - north pole on right. Top is -90 degrees
       * latitude; bottom 90 degrees */

      /* Map X to this LATITUDE range */
      theta1 = (float) (THETA1 * PI / 180.0);
      theta2 = (float) (THETA2 * PI / 180.0);

      /* Map Y to this LONGITUDE range */
      phi1 = (float) (PHI1 * PI / 180.0);
      phi2 = (float) (PHI2 * PI / 180.0);

      theta = theta1;

      /*********************************************************************/
      /* Thanks to Hugh Bray for the following idea: when calculating      */
      /* a table of evenly spaced sines or cosines, only a few initial     */
      /* values need be calculated, and the remaining values can be        */
      /* gotten from a derivative of the sine/cosine angle sum formula     */
      /* at the cost of one multiplication and one addition per value!     */
      /*                                                                   */
      /* This idea is applied once here to get a complete table for        */
      /* latitude, and near the bottom of this routine to incrementally    */
      /* calculate longitude.                                              */
      /*                                                                   */
      /* Precalculate 2*cos(deltaangle), sin(start) and sin(start+delta).  */
      /* Then apply recursively:                                           */
      /* sin(angle+2*delta) = sin(angle+delta) * 2cosdelta - sin(angle)    */
      /*                                                                   */
      /* Similarly for cosine. Neat!                                       */
      /*********************************************************************/

      deltatheta = (float) (theta2 - theta1) / (float) linelen;

      /* initial sin,cos theta */
      sinthetaarray[0] = (float) sin((double) theta);
      costhetaarray[0] = (float) cos((double) theta);
      sinthetaarray[1] = (float) sin((double) (theta + deltatheta));
      costhetaarray[1] = (float) cos((double) (theta + deltatheta));

      /* sin,cos delta theta */
      twocosdeltatheta = (float) (2.0 * cos((double) deltatheta));

      /* build table of other sin,cos with trig identity */
      for (i = 2; i < (int) linelen; i++)
      {
         sinthetaarray[i] = sinthetaarray[i - 1] * twocosdeltatheta -
            sinthetaarray[i - 2];
         costhetaarray[i] = costhetaarray[i - 1] * twocosdeltatheta -
            costhetaarray[i - 2];
      }

      /* now phi - these calculated as we go - get started here */
      deltaphi = (float) (phi2 - phi1) / (float) height;

      /* initial sin,cos phi */

      sinphi = oldsinphi1 = (float) sin((double) phi1);
      cosphi = oldcosphi1 = (float) cos((double) phi1);
      oldsinphi2 = (float) sin((double) (phi1 + deltaphi));
      oldcosphi2 = (float) cos((double) (phi1 + deltaphi));

      /* sin,cos delta phi */
      twocosdeltaphi = (float) (2.0 * cos((double) deltaphi));


      /* affects how rough planet terrain is */
      if (ROUGH)
         rscale = .3 * ROUGH / 100.0;

      /* radius of planet */
      R = (double) (ydots) / 2;

      /* precalculate factor */
      rXrscale = R * rscale;

      sclz = sclx = scly = RADIUS / 100.0;      /* Need x,y,z for RAY */

      /* adjust x scale factor for aspect */
      sclx *= aspect;

      /* precalculation factor used in sphere calc */
      Rfactor = rscale * R / (double) zcoord;

      if (persp)                /* precalculate fudge factor */
      {
         double radius;
         double zview;
         double angle;

         xcenter = xcenter << 16;
         ycenter = ycenter << 16;

         Rfactor *= 65536.0;
         R *= 65536.0;

         /* calculate z cutoff factor attempt to prevent out-of-view surfaces
          * from being written */
         zview = -(long) ((double) ydots * (double) ZVIEWER / 100.0);
         radius = (double) (ydots) / 2;
         angle = atan(-radius / (zview + radius));
         zcutoff = -radius - sin(angle) * radius;
         zcutoff *= 1.1;        /* for safety */
         zcutoff *= 65536L;
      }
   }

   /* set fill plot function */
   if (FILLTYPE != 3)
      fillplot = interpcolor;
   else
   {
      fillplot = clipcolor;

      if (transparent[0] || transparent[1])
         /* If transparent colors are set */
         fillplot = T_clipcolor;/* Use the transparent plot function  */
   }

   /* Both Sphere and Normal 3D */
   direct[0] = light_direction[0] = XLIGHT;
   direct[1] = light_direction[1] = -YLIGHT;
   direct[2] = light_direction[2] = ZLIGHT;

   /* Needed because sclz = -ROUGH/100 and light_direction is transformed in
    * FILLTYPE 6 but not in 5. */
   if (FILLTYPE == 5)
      direct[2] = light_direction[2] = -ZLIGHT;

   if (FILLTYPE == 6)           /* transform light direction */
   {
      /* Think of light direction  as a vector with tail at (0,0,0) and head
       * at (light_direction). We apply the transformation to BOTH head and
       * tail and take the difference */

      v[0] = 0.0;
      v[1] = 0.0;
      v[2] = 0.0;
      vmult(v, m, v);
      vmult(light_direction, m, light_direction);

      for (i = 0; i < 3; i++)
         light_direction[i] -= v[i];
   }
   normalize_vector(light_direction);

   if (preview && showbox)
   {
      normalize_vector(direct);

      /* move light vector to be more clear with grey scale maps */
      origin[0] = (3 * xdots) / 16;
      origin[1] = (3 * ydots) / 4;
      if (FILLTYPE == 6)
         origin[1] = (11 * ydots) / 16;

      origin[2] = 0.0;

      v_length = min(xdots, ydots) / 2;
      if (persp && ZVIEWER <= P)
         v_length *= (long) (P + 600) / ((long) (ZVIEWER + 600) * 2);

      /* Set direct[] to point from origin[] in direction of untransformed
       * light_direction (direct[]). */
      for (i = 0; i < 3; i++)
         direct[i] = origin[i] + direct[i] * v_length;

      /* center light box */
      for (i = 0; i < 2; i++)
      {
         tmp[i] = (direct[i] - origin[i]) / 2;
         origin[i] -= tmp[i];
         direct[i] -= tmp[i];
      }

      /* Draw light source vector and box containing it, draw_light_box will
       * transform them if necessary. */
      draw_light_box(origin, direct, lightm);
      /* draw box around original field of view to help visualize effect of
       * rotations 1 means show box - xmin etc. do nothing here */
      if (!SPHERE)
         corners(m, 1, &xmin, &ymin, &zmin, &xmax, &ymax, &zmax);
   }

   /* bad has values caught by clipping */
   f_bad.x = bad.x = bad_value;
   f_bad.y = bad.y = bad_value;
   f_bad.color = bad.color = bad_value;
   for (i = 0; i < (int) linelen; i++)
   {
      lastrow[i] = bad;
      f_lastrow[i] = f_bad;
   }
   got_status = 3;
   return (0);
} /* end of once-per-image intializations */

/*
   This pragma prevents optimizer failure in MSC/C++ 7.0. Program compiles ok
   without pragma, but error message is real ugly, paraphrasing loosely,
   something like "optimizer screwed up big time, call Bill Gates collect ...
   (Note: commented out pragma because we removed the compiler "/Og" option
    in MAKEFRACT.BAT - left these notes as a warning...
*/
#ifdef _MSC_VER
#if (_MSC_VER >= 600)
/* #pragma optimize( "g", off ) */
#endif
#endif

static int line3dmem(void)
{
   /*********************************************************************/
   /*  Memory allocation - assumptions - a 64K segment starting at      */
   /*  extraseg has been grabbed. It may have other purposes elsewhere, */
   /*  but it is assumed that it is totally free for use here. Our      */
   /*  strategy is to assign all the far pointers needed here to various*/
   /*  spots in the extra segment, depending on the pixel dimensions of */
   /*  the video mode, and check whether we have run out. There is      */
   /*  basically one case where the extra segment is not big enough     */
   /*  -- SPHERE mode with a fill type that uses putatriangle() (array  */
   /*  minmax_x) at the largest legal resolution of MAXPIXELSxMAXPIXELS */
   /*  or thereabouts. In that case we use farmemalloc to grab memory   */
   /*  for minmax_x. This memory is never freed.                        */
   /*********************************************************************/
   long check_extra;  /* keep track ofd extraseg array use */

   /* lastrow stores the previous row of the original GIF image for
      the purpose of filling in gaps with triangle procedure */
   /* first 8k of extraseg now used in decoder TW 3/95 */
   lastrow = MK_FP(extraseg, 0);

   check_extra = sizeof(*lastrow) * xdots;
   if (SPHERE)
   {
      sinthetaarray = (float far *) (lastrow + xdots);
      check_extra += sizeof(*sinthetaarray) * xdots;
      costhetaarray = (float far *) (sinthetaarray + xdots);
      check_extra += sizeof(*costhetaarray) * xdots;
      f_lastrow = (struct f_point far *) (costhetaarray + xdots);
   }
   else
      f_lastrow = (struct f_point far *) (lastrow + xdots);
   check_extra += sizeof(*f_lastrow) * (xdots);
   if (pot16bit)
   {
      fraction = (BYTE far *) (f_lastrow + xdots);
      check_extra += sizeof(*fraction) * xdots;
   }
   minmax_x = (struct minmax *) NULL;

   /* these fill types call putatriangle which uses minmax_x */
   if (FILLTYPE == 2 || FILLTYPE == 3 || FILLTYPE == 5 || FILLTYPE == 6)
   {
      /* end of arrays if we use extra segement */
      check_extra += sizeof(struct minmax) * ydots;
      if (check_extra > (1L << 16))     /* run out of extra segment? */
      {
         static FCODE msg[] = {"farmemalloc minmax"};
         static struct minmax far *got_mem = NULL;
         if(debugflag == 2222)
            stopmsg(0,msg);
         /* not using extra segment so decrement check_extra */
         check_extra -= sizeof(struct minmax) * ydots;
         if (got_mem == NULL)
            got_mem = (struct minmax far *) (farmemalloc(OLDMAXPIXELS *
                                                    sizeof(struct minmax)));
         if (got_mem)
            minmax_x = got_mem;
         else
            return (-1);
      }
      else /* ok to use extra segment */
      {
         if (pot16bit)
            minmax_x = (struct minmax far *) (fraction + xdots);
         else
            minmax_x = (struct minmax far *) (f_lastrow + xdots);
      }
   }
   if (debugflag == 2222 || check_extra > (1L << 16))
   {
      char tmpmsg[70];
      static FCODE extramsg[] = {" of extra segment"};
#ifndef XFRACT
      sprintf(tmpmsg, "used %ld%Fs", check_extra, (char far *)extramsg);
#else
      sprintf(tmpmsg, "used %ld%s", check_extra, extramsg);
#endif
      stopmsg(4, tmpmsg);
   }
   return(0);
}

#ifdef _MSC_VER
#if (_MSC_VER >= 600)
#pragma optimize( "g", on )
#endif
#endif
