/* ------------------------------------------------------------------
 *
 * hfplant.c
 *
 * copyright 1998 Mark J. Stock
 *
 * v0.0 1998-07-31	initial version
 *
 * The object of hfplant is to be a tool for creating a Radiance
 * description of a collection of objects (trees, mainly) upon
 * a heightfield/surface. A heightfield, in the format of a
 * binary of ASCII, 8 or 16-bit, PGM file, is read in, calculations
 * are performed to obtain tree location, elevation, size, and 
 * angle, and an ASCII text file is written, ready for Radiance.
 *
 * ------------------------------------------------------------------ */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "const.h"

typedef struct tree_record *tree_pointer;
typedef struct tree_record {
   int type;			/* identifier of the tree, what type */
   unsigned char lod;		/* level of detail, 0 is no tree at all, etc.  */
   double x;			/* location */
   double y;
   double z;
   double scale;		/* scale of tree/object */
   double rot;			/* rotation, if any, in radians */
   tree_pointer next_tree;
} TREE;
tree_pointer tree_head;

int num_t;

typedef struct tree_characteristics {
   char filename[80];		/* file name of the Radiance object */
   unsigned char is_oct;	/* ==0 if it is a .rad or .norm file, ==1 if its an octree */
   /* may someday wish to add level-of-detail information here */
   int lower_elev_range;
   int upper_elev_range;
   double density;
   double lower_scale_range;
   double upper_scale_range;
} TREE_TYPE;
TREE_TYPE tree_type[2];

/*
 * All 2D arrays are indexed from top left corner, 1st index
 * is row number, 2nd index is column
 */

/* Important sizes */
int inheight;			/* returned from read_pgm_ushort */
int inwidth;
int scale_it = 0;		/* scale trees? 1==yes */
int rotate_it = 0;		/* rotate trees? 1==yes */


/* The heightfield array, values between -32k and 32k */
unsigned short int hf[MAX_SIZE][MAX_SIZE];

/* The tree mask file (PGM) */
unsigned char tm[MAX_SIZE][MAX_SIZE];

double find_slope(int,int);
double double_interpolate(double,double,double,double,double,double);
void add_tree(int,double,double,double,double,double);


main(int argc, char *argv[]) {

   /* initialize variables */
   int i,j,k;
   double temp_d;
   char progname[80];			/* name of executable */

   char hfinfile[80];			/* name of the input PGM heightfield file */
   char tminfile[80];			/* name of the input PGM tree mask file */
   char outfile[80];			/* name of the output PPM imagemap file */
   double x_min, y_min;
   double scale = 30.0;			/* used for scaling the heightfield horizontally */
   double vscale;			/*    and vertically */
   int use_vscale = 0;
   int use_ocean = 0;
   int ocean_level = 0;
   int num_trees_here;
   int useSlope = 0;			/* ==1 means colors vary with slope */
   double slope_thresh = 400;		/* if altitudes vary by more than this, its rocky */
   int useTemp = 0;			/* ==1 means colors vary with temperature */
   double tempF = 50;			/* average temperature, degrees Fahrenheit */
   int use_bounds = 0;
   double ysize;			/* output height, world units */
   double xsize;			/* output width */
   double y_bl, x_bl;
   double y_pos, x_pos;
   int min_elev;
   int max_elev;
   double slope;
   int write_rad = 1;

   (void) strcpy(tree_type[0].filename,"tree.rad");
   tree_type[0].is_oct = 0;


   /*
    * Parse the command-line options
    */
   (void) strcpy(progname,argv[0]);
   if (argc < 4) (void) Usage(progname,0);
   for (i=1; i<argc; i++) {
      if (strncmp(argv[i], "-help", 2) == 0)
         (void) Usage(progname,0);
      else if (strncmp(argv[i], "-infile", 2) == 0)
         (void) strcpy(hfinfile,argv[++i]); 
      else if (strncmp(argv[i], "-outfile", 3) == 0)
         (void) strcpy(outfile,argv[++i]); 
      else if (strncmp(argv[i], "-treemask", 2) == 0)
         (void) strcpy(tminfile,argv[++i]); 
      else if (strncmp(argv[i], "-bounds",2) == 0) {
         use_bounds = 1;
         min_elev = atoi(argv[++i]);
         max_elev = atoi(argv[++i]); }
      else if (strncmp(argv[i], "-scale", 2) == 0)
         scale = atof(argv[++i]);
      else if (strncmp(argv[i], "-s", 2) == 0) {
         useSlope = 1;			/* density varies with slope */
         if (strncmp(argv[i+1], "-", 1) != 0)
            slope_thresh = 400*atof(argv[++i]); }
      else if (strncmp(argv[i], "-t", 2) == 0) {
         useTemp = 1;			/*  trees vary with temperature */
         tempF = atof(argv[++i]); }
      else if (strncmp(argv[i], "-vertical", 2) == 0) {
         vscale = atof(argv[++i]);
         use_vscale = 1; }
      else if (strncmp(argv[i], "-ocean", 3) == 0) {
         ocean_level = atof(argv[++i]);
         use_ocean = 1; }
      else if (strncmp(argv[i], "-object", 2) == 0) {
         /* read in the data for an object */
         }
      else
         (void) Usage(progname,0);
   }

   /*
    * Read in heightfield and other data
    */

   /* Remember, you need to find out if the elevation data 
    * will come from a PGM heightfield or a wavefront-style
    * trimesh format */
   (void) read_pgm_ushort(hfinfile,hf);
   /* (void) read_tri_mesh(hfinfile,tri); */

   ysize = (double)(inheight-1)*scale;
   xsize = (double)(inwidth-1)*scale;
   /* center the stuff around 0,0 */
   y_min = ysize/2.0;
   x_min = -xsize/2.0;

   (void) read_pgm_char(tminfile,tm);


   /*
    * Calculate tree locations
    */

   /* find elevation of exact tree location */
   for (i=0; i<inheight-1; i++) {
      y_bl = -1.0*scale*(i+1) + y_min;
      /* fprintf(stderr,"%d  %lf\n",i,y_bl); */
      for (j=0; j<inwidth; j++) {
         /* find out how many trees are supposed to be here */
         num_trees_here = tm[i][j]/100;
         if (num_trees_here > 0) {
            x_bl = j*scale + x_min;
            /* fprintf(stderr,"%d trees at %d,%d: ",num_trees_here,i,j); */
            for (k=0; k<num_trees_here; k++) {
               x_pos = rand()/(RAND_MAX+1.0);
               y_pos = rand()/(RAND_MAX+1.0);
               temp_d = double_interpolate(hf[i+1][j], hf[i+1][j+1], hf[i][j+1], hf[i][j], x_pos, y_pos);
               if (use_ocean) temp_d = temp_d - ocean_level;
               (void) add_tree(0,30.0*x_pos+x_bl,30.0*y_pos+y_bl,temp_d,1.0,0.0);
               /* fprintf(stderr,"%lf, %lf, %lf\n",30.0*x_pos+x_bl,30.0*y_pos+y_bl,temp_d); */
            }
            if (useSlope && hf[i][j] > 0) {
               /* calculate slope using normal */
               /* find max 1st derivative */
               slope = find_slope(i,j);
               if (slope > slope_thresh) {
               }
            }
            /* fprintf(stderr,"\n"); */
         }
      }
   }


   /*
    * Write output image
    */

   if (write_rad)
      (void)write_rad_list(outfile);
   else
      (void)write_raw_list(outfile);


   /*
    * Write output statistics and quit
    */
   if (write_rad) write_rad_commands(outfile);
   (void) write_finish();
}


double find_slope(int i, int j) {

   double top, bottom, left, right;

   if (i==0) top = tm[0][j];
   else top =  tm[i-1][j];
   if (i==inheight-1) bottom = tm[inheight-1][j];
   else bottom = tm[i+1][j];
   if (j==0) left = tm[i][0];
   else left = tm[i][j-1];
   if (j==inwidth-1) right = tm[i][0];
   else right = tm[i][j+1];

   right = abs(left-right);
   left = abs(top-bottom);
   if (right > left) return(right);
   else return(left);

}


/*
 * Do a double interpolation using 4 values as the corners,
 * and a dx and dy fractions
 */
double double_interpolate(double bl, double br, double tr,
                          double tl, double dx, double dy) {

   double topx, bottomx, retval;

   topx = tl + dx*(tr-tl);
   bottomx = bl + dx*(br-bl);
   retval = bottomx + dy*(topx-bottomx);

   return(retval);
}


/*
 * Add a tree to the linked list
 */
void add_tree(int type,double x,double y,double z,double scale,double rot) {

   tree_pointer new_tree;
   tree_pointer old_tree_head;

   new_tree = (TREE *)malloc(sizeof(TREE));
   new_tree->type = type;
   new_tree->x = x;
   new_tree->y = y;
   new_tree->z = z;
   new_tree->scale = scale;
   new_tree->rot = rot;

   /* put the new tree at the head of the linked list */
   old_tree_head = tree_head;
   tree_head = new_tree;
   new_tree->next_tree = old_tree_head;

   /* increment the counter, and return */
   num_t++;
}


/*
 * Write a raw description of the trees, one usable for
 * parsing, and setting level-of-detail later!
 */
int write_raw_list(char filename[80]) {

   FILE* ofp;
   tree_pointer a_tree = tree_head;

   /* open the file for writing */
   ofp = fopen(filename,"w");
   if (ofp==NULL) {
      fprintf(stderr,"Could not open output file %s\n",filename);
      exit(0);
   }

   fprintf(ofp,"%d\n",num_t);
   while (a_tree) {
      fprintf(ofp,"%d %lf %lf %lf %lf %lf\n",a_tree->type,a_tree->x,
            a_tree->y,a_tree->z,a_tree->scale,a_tree->rot);
      a_tree = a_tree->next_tree;
   }

   fclose(ofp);
   return(0);
}


/*
 * Write a Radiance description of the trees, one ready
 * for rendering. Need to implement level of detail...somehow
 */
int write_rad_list(char filename[80]) {

   FILE* ofp;
   tree_pointer a_tree = tree_head;
   int count = 0;
   int argcount;

   /* open the file for writing */
   ofp = fopen(filename,"w");
   if (ofp==NULL) {
      fprintf(stderr,"Could not open output file %s\n",filename);
      exit(0);
   }

   fprintf(ofp,"# %d trees\n",num_t);
   while (a_tree) {
      if (tree_type[a_tree->type].is_oct) {
         /* write an instance */
         argcount = 5;
         if (scale_it) argcount = 7;
         if (rotate_it) argcount = 9;
         fprintf(ofp,"void instance t%d\n%d %s",count++,argcount,tree_type[a_tree->type].filename);
         if (scale_it) fprintf(ofp," -s %lf",a_tree->scale);
         if (rotate_it) fprintf(ofp," -rz %lf",a_tree->rot);
         fprintf(ofp," -t %lf %lf %lf\n0\n0\n\n",a_tree->x,a_tree->y,a_tree->z);
      } else {
         /* write an xform command */
         fprintf(ofp,"!xform -n t%d",count++);
         if (scale_it) fprintf(ofp," -s %lf",a_tree->scale);
         if (rotate_it) fprintf(ofp," -rz %lf",a_tree->rot);
         fprintf(ofp," -t %lf %lf %lf %s\n",a_tree->x,a_tree->y,a_tree->z,tree_type[a_tree->type].filename);
      }
      a_tree = a_tree->next_tree;
   }
   fprintf(stderr,"Wrote %d objects/trees.\n\n",count);

   fclose(ofp);
   return(0);
}


/*
 * Write Radiance and shell statements to help user
 */
int write_rad_commands(char outfile[80]) {

   fprintf(stdout,"Run the following commands:\n\n");
   fprintf(stdout,"oconv %s > placed_objects.oct\n",outfile);

   return(0);
}


/*
 * This function writes basic usage information to stderr,
 * and then quits. Too bad.
 */
int Usage(char progname[80],int status) {

   /* Usage for hfcolor */
   static char **cpp, *help_message[] =
   {
       "where options include:",
       "  -help          writes this help information",
       " ",
       "  -in name       name of heightfield PGM file, binary or raw",
       "  -tree name     tree mask PGM image file name",
       "  -out name      name of output file",
       " ",
       "  -s cutoff      use slope to determine image colors, slope>cutoff means rocks",
       " ",
       "  -bounds n n    minimum and maximum elevation in the input, def= -1000, 1000",
       "  -seed val      set the seed for the psuedo-random number generator",
       " ",
       "Options may be abbreviated to an unambiguous length.",
       NULL
   };

   fprintf(stderr, "usage:\n  %s [-options ...]\n\n", progname);
   for (cpp = help_message; *cpp; cpp++)
      fprintf(stderr, "%s\n", *cpp);
   exit(status);
   return(0);
}

