/*********************************************
 *  TGAMOSAIC26 Reads in a type 2 TGA file and outputs a POV scene file rendering the original image 
 *  as a mosaic of various shapes.
 *  Copyright (C) 2000  Andrew Woodfin
 *
 *	This program is free software; you can redistribute it and/or modify it under the terms of 
 *	the GNU General Public License as published by the Free Software Foundation; either version 2
 *	of the License, or (at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,	but WITHOUT ANY WARRANTY; 
 *	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 *	See the	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License along with this program; 
 *	if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
 *	Boston, MA  02111-1307, USA.
 *
 *
 * Started by Andrew Woodfin and subsequently
 * extended by people as listed below.
 *
 *  JAC     -> Jon A. Cruz
 *
 * 2000.01.24 JAC: Initial cleanup for running on Linux
 *

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

/*********************************************
 * Simple config defs for now
 ********************************************/
#if defined( _WIN32 ) || defined( _MSDOS ) || defined( MSDOS ) || defined( __MSDOS__ )

#define HAVE_CONIO_H 1

#define MAXPATHLEN _MAX_PATH

#else

/* Define if you have the <sys/param.h> header file.  */
#define HAVE_SYS_PARAM_H 1

#define _GNU_SOURCE

#endif





#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#include <stdio.h>
#include <stddef.h>
#include <string.h>

#ifdef HAVE_CONIO_H
#include <conio.h>
#else
#define getch getchar
#endif

#include <stdlib.h>
#include <malloc.h>
//#include <alloc.h>
#include <math.h>
#include <time.h>

#define PI 3.1416
#define seedrand srand((unsigned)time(NULL));
#define getrandom( min, max ) ( ( rand() % (int)((max) - (min)) ) + (min) )

//#define DEBUG

#define FL_COLOR   0x0001
#define FL_ROTATE  0x0002
#define FL_OBJ     0x0004
#define FL_CYL     0x0008
#define FL_TGAFLIP 0x0010

#define FL_LINEAR_MASK  0x0060
#define FL_LINEAR       0x0020
#define FL_NONLINEAR    0x0040

#define FL_ANIM_MASK    0x0380
#define FL_ANIM_EXPLODE 0x0080
#define FL_ANIM_GROW    0x0100
#define FL_ANIM_RASTER  0x0200


//MS Visual C++ doesn't play well with these structures and file input?!

/*
      public int mIdLen; // 8-bit unsigned
      public int mColMapType; // 0 = no map, 1 = colormap
      public int mImageType; // 1=Colormapped, 2=true color, 3=B/W, |8 = compressed

      public int mFirstEntryIndex; // 16-bit unsigned. start index of palette data
      public int mColMapLen; // 16-bit unsigned. number of map entries present
      public int mColMapEntrySize; // 8-bit. # bits per color map entry

      public int mOriginX; // 16-bit unsigned.
      public int mOriginY; // 16-bit unsigned.

      public int mWidth; // 16-bit unsigned.
      public int mHeight; // 16-bit unsigned.

      public int mPixelDepth; // 8-bit unsigned.
      public int mImageDescr; // 8-bit unsigned.
*/

typedef struct header_type {		
   unsigned char idsize;
   unsigned char maptype;
   unsigned char imagetype;

   unsigned short mapOffset;
   unsigned short mapLen;
   unsigned char mapBitsPerEntry;

   unsigned short xorigin;
   unsigned short yorigin;

   unsigned short width;
   unsigned short height;

   unsigned char pixelsize;
   unsigned char descrip;
} HeaderType;

typedef struct imagedata_type {
   unsigned char blue;
   unsigned char green;
   unsigned char red;
} ImagedataType;



void Quit(int code);
FILE * ReadHeader(HeaderType *hdr, char *pInFileName, long *area, long *flags );
void ReadID( HeaderType *hdr, FILE *inputtga );
void ReadData( HeaderType *hdr, FILE *intputtga, short *buffer, int spacing );
short * AllocateBuffer( int spacing, long area, size_t *buffer_size );
void WritePov( HeaderType *hdr, FILE *destination, short *buffer, int spacing, char pixel, long flags );
char Lowercase(char letter);
void SineParam( double *fUserspace, char *pixel, long *flags );
int PointSpacing( HeaderType *hdr, double fUserspacec );
char getChoice();
int fread16Little( unsigned short *pDst, FILE *fp );

int main(int argc, char *argv[])
{
 HeaderType tgaHeader;
 char infilename[ MAXPATHLEN ], outfilename[ MAXPATHLEN ];
 int spacing = 0;
 FILE *inputtga;
 FILE *destination;

 double fUserspace;				//Value of pixel spacing defined by user input, in % of maximum image dimension
 long int area;

 size_t buffer_size;
 short *buffer;                    //Will hold image data

 char pixel;           //For selecting surface and pixel types
 long flags = FL_CYL;


 /*
 int z_size, sin_freq;          //For sinusoidal surface shape                //CHANGE FOR Z-Y
 */

 printf("*******************************************************\n");
 printf("* TGAMosaic ver 2.6 Copyright (C) 2000 Andrew Woodfin *\n");
 printf("* This program comes with NO WARRANTY. This is free   *\n");
 printf("* software and you may freely distribute it,under     *\n");
 printf("* the conditions outlined in the README file you      *\n");
 printf("* should have received with it.                       *\n");
 printf("* Contact Andy at awoodfin@mad.scientist.com          *\n");
 printf("*******************************************************\n");
 


/************************
 * Check command line   *
 * for input and output *
 * file names           *
 ************************/
 if (argc < 3 || argc > 3) {
   printf("\n TGAMosaic will take an uncompressed true-color tga file (type 2) and create a\n\n");
   printf("Persistence of Vision input file composed of tiny cubes or spheres\n");
   printf("corresponding to the image pixels. Give it a try!\n");
   printf("The object sizes will vary with pixel color.\n");
   printf("It's recommended that you reduce your input TGA size\n");
   printf("to something smaller than 640x480, otherwise your\n");
   printf("output scene file could have >10,000 objects!\n");
   printf("\n\n\n");
   printf("Command line format is :\n");
   printf("  TGAMOSAIC26 [input filename] [output filename]\n");
   printf("Of course, don't include the brackets, e.g., tgamosaic26 in.tga out.pov\n");
   Quit(0);
 }
 else {
   strcpy(infilename, argv[1]);
   strcpy(outfilename, argv[2]);
 }


 /****************************************************
 * Reading in TGA file header to get dimensions and  *
 * verify that it is indeed type 2, uncompressed     *
 * 24 bit TGA										 *
 ****************************************************/
 inputtga = ReadHeader( &tgaHeader, infilename, &area, &flags );

#ifdef DEBUG
printf("\nHeader read\n");
#endif



 /******************************************************
 * Receiving user input for determining file creation  *
 * parameters, e.g., cubes or spheres, pixel spacing,  *
 * and color of POV objects                            *
 ******************************************************/
 SineParam( &fUserspace, &pixel, &flags );

#ifdef DEBUG
printf("\nSineParameters determined\n");
#endif

 /****************************************************
 * Determines spacing between image pixels, i.e.,    *
 * what fraction of image pixels are sampled and used*
 * for creation of POV files.                   	 *
 ****************************************************/
 spacing = PointSpacing( &tgaHeader, fUserspace );


#ifdef DEBUG
printf("\nPointSpacing determined\n");
#endif


 buffer = AllocateBuffer( spacing, area, &buffer_size );

#ifdef DEBUG
printf("\nBuffer Allocated\n");
#endif

 ReadID( &tgaHeader, inputtga );


 ReadData( & tgaHeader, inputtga, buffer, spacing );
 
#ifdef DEBUG
printf("\nData read\n");
#endif

 fclose(inputtga);

#ifdef DEBUG
printf("\nInput file closed\n");
#endif

 if ((destination = fopen(outfilename, "w")) == NULL) {
   printf("Cannot create .pov file!!\n");
   Quit(2);
 }

#ifdef DEBUG
printf("\nOutput file created\n");
#endif

 seedrand;

 WritePov( &tgaHeader, destination, buffer, spacing, pixel, flags );

#ifdef DEBUG
printf("\nData written to output\n");
#endif

 fclose(destination);

 if (buffer != NULL) {
   free(buffer);
 }

 printf("\n    TGA File: %s  POV file : %s\n", infilename, outfilename);
 Quit(0);

 return 0;
}



void Quit(int code)
 {
  fcloseall();
  exit(code);
 }

 /****************************************************
 * Reading in TGA file header to get dimensions and  *
 * verify that it is indeed type 2, uncompressed     *
 * 24 bit TGA										 *
 * Requires no modification for version 2.0          *
 ****************************************************/
FILE * ReadHeader(HeaderType *hdr, char *pInFileName, long *area, long *flags )
 {
	char proceed;
  FILE* inputtga = NULL;
  char *errString = "Unable to read input .tga file\a\n";

  if ((inputtga = fopen(pInFileName, "rb")) == NULL) {
    printf("Cannot open file!!\n");
    Quit(2);
  }

  if (fread(&(hdr->idsize), 1, 1, inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }
  if (fread(&(hdr->maptype), 1, 1, inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }
  if (fread(&(hdr->imagetype), 1, 1, inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }

  if (fread16Little( &(hdr->mapOffset), inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }
  if (fread16Little( &(hdr->mapLen), inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }
  if (fread(&(hdr->mapBitsPerEntry), 1, 1, inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }


  if (fread16Little( &(hdr->xorigin), inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }
  if (fread16Little( &(hdr->yorigin), inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }

  if (fread16Little( &(hdr->width), inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }
  if (fread16Little( &(hdr->height), inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }

  if (fread(&(hdr->pixelsize), 1, 1, inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }
  if (fread(&(hdr->descrip), 1, 1, inputtga) != 1) {
    if (feof(inputtga)) Quit(0);
    printf(errString);
  }


  printf("Id size : %d",  hdr->idsize);
  printf("   Map Type : %d\n",  hdr->maptype);

  printf("Image Type : %d\n",  hdr->imagetype);

  printf("info1 : %d",  hdr->mapOffset);
  printf("   info2 : %d",  hdr->mapLen);
  printf("   info3 : %d\n",  hdr->mapBitsPerEntry);
  printf("xorig : %d",  hdr->xorigin);
  printf("    yorig : %d\n",  hdr->xorigin);

  printf("Width : %d",  hdr->width);
  printf("   Height: %d",  hdr->height);
  *area = (long int) hdr->width * hdr->height;
  printf("   Area : %ld\n", *area);

  printf("pixel size : %d",  hdr->pixelsize);
  printf("   image descriptor : %d\n\n",  hdr->descrip);

  if ( hdr->imagetype != 2) {
    printf("Input file must be an uncompressed true-color tga (i.e., Image Type 2).\n");
    Quit(0);
  }
  if (( hdr->height < 50) || ( hdr->width < 50)) {
    printf("Input file must have at least 50x50 resolution!\n");
    Quit(0);
  }

  //printf("Header descriptor yields ordering of %d\n", 32 & hdr->descrip);
  
  if ( !(32 & hdr->descrip) ) {
      (*flags) |= FL_TGAFLIP;
  }

  if(*area>300000) printf("**WARNING** You may want to reduce your image size to less than 640x480!");  
  printf("\n\n**WARNING** If these header lines look correct, enter c to continue!!\n");
  printf("Otherwise, enter anything else to exit!!!");
  proceed = getChoice();
  if (proceed != 'c') {
	  printf("\n\nProgram terminated!!\n\n");
	  Quit(2);
  }
  else printf("\n\n");

  return inputtga;
 }

void ReadID( HeaderType *hdr, FILE *inputtga )
 {
   if ( hdr->idsize > 0 ) {
     char *pTmp;
     pTmp = (char *)malloc( hdr->idsize + 1 );
     if (fread(pTmp, hdr->idsize, 1, inputtga) != 1) {
       printf( "error reading image ID string\n" );
     }
     free( pTmp );
   }
 }

void SineParam( double *fUserspace, char *pixel, long *flags )
 {
  char keystroke;
  char cUserspace[10];
  char surface;

  printf("\nEnter 0 if you want random object type, or anything else to choose.");
  keystroke = getChoice();
  if(keystroke == '0') {
	  (*flags) |= FL_OBJ;
	  *pixel='s';
  }
  else {
	  (*flags) &= ~FL_OBJ;
	  printf("\nWould you like the image made of (B)oxes, (S)pheres, or (C)ylinders?\n");
	  *pixel = getChoice();
  }

  printf("\n\nWhat pixel spacing would you like for sampling? Give percentage value\n");
  printf("\tof maximum image dimension (height or width).\n");
  printf("\tDo not exceed 3.5%% of max dimension. Larger values \n");
  printf("\tyield fewer pixels sampled.\n");
  printf("\tExample: A value of 2 for a 640x480 image will give \n");
  printf("\tspacing 0.02*640=12.8, so only every 13th pixel\n");
  printf("\tin horizontal and vertical dimensions will be sampled.\n");
  fgets( cUserspace, sizeof(cUserspace), stdin );
  *fUserspace = (double)atof(cUserspace);
  if(*fUserspace<0.8) *fUserspace=1.0;
  if(*fUserspace>3.5) *fUserspace=3.5;
  printf("Spacing will be %.2f percent of maximum image dimension.\n\n", *fUserspace);

  printf("Would you like all objects the same color or using image colors?\n");
  printf("Enter 0 for no color, any other key gives image colors.");
  //pixel = 's';
  surface = 'p';
  keystroke = getChoice();
  if(keystroke == '0') {
          (*flags) &= ~FL_COLOR;
	  printf("\nAll objects will be the same color.");
  }
  else {
	  (*flags) |= FL_COLOR;
	  printf("\nI'll use image colors.");
  }

  printf("\n\nEnter 0 if you want random rotation.. any other key gives none.");
  keystroke = getChoice();
  if ( keystroke == '0' ) {
      (*flags) |= FL_ROTATE;
  }
  else {
      (*flags) &= ~FL_ROTATE;
  }

  printf("\n\nEnter 0 if you want objects on a cylinder.. any other key gives a plane.");
  keystroke = getChoice();
  if (keystroke == '0') {
      (*flags) |= FL_CYL;
  } else {
      (*flags) &= ~FL_CYL;
  }

  printf("\n\nEnter 0 if you want linear size scaling, 1 for nonlinear, any key for none.");
  keystroke = getChoice();
  if (keystroke == '0') {
      (*flags) &= ~FL_LINEAR_MASK;
      (*flags) |= FL_LINEAR;
  } else if (keystroke == '1') {
      (*flags) &= ~FL_LINEAR_MASK;
      (*flags) |= FL_NONLINEAR;
  } else {
      (*flags) &= ~FL_LINEAR_MASK;
  }
//  printf("\nlinear_on=%d",linear_on);

  printf("\n\nFinally, enter 0 if you will be animating to explode the scene,\n,enter 1 to grow the objects, or 2 to raster.");
  keystroke = getChoice();
  if (keystroke == '0') {
      (*flags) &= ~FL_ANIM_MASK;
      (*flags) |= FL_ANIM_EXPLODE;
  } else if (keystroke == '1') {
      (*flags) &= ~FL_ANIM_MASK;
      (*flags) |= FL_ANIM_GROW;
  } else if (keystroke == '2') {
      (*flags) &= ~FL_ANIM_MASK;
      (*flags) |= FL_ANIM_RASTER;
  } else {
      (*flags) &= ~FL_ANIM_MASK;
  }


 }


int PointSpacing( HeaderType *hdr, double fUserspace )
 {
  double space_temp;
  int spacing;

  /******************************************
  *	I don't exactly recall why this section *
  * rounds as it does, but it works.		*
  ******************************************/
  if ( hdr->width >=  hdr->height) {				     //If image is wider than it is high, pixel spacing is determined by image width			
//    space_temp = 1.0*(double) hdr->width/100;		         //Temporary variable is 1% of width.
    space_temp = fUserspace*(double) hdr->width/100;		         //Temporary variable is 1% of width.
    if (((int)(10*space_temp) - 10*(int)space_temp) >= 1) {  //Determine
      spacing = (int)space_temp + 1;						 //Spacing = 1 + integer 1% of width, always rounded down
    }
    else {
      spacing = (int)space_temp;							 //Spacing = integer 1% of width, always rounded down
    }
  }
  else {                                                     //If image is higher than it is wide, pixel spacing is determined by image height
//    space_temp = 1.0*(double) hdr->height/100;              //Temporary variable is 1% of height.
    space_temp = fUserspace*(double) hdr->height/100;              //Temporary variable is 1% of height.
    if (((int)(10*space_temp) - 10*(int)space_temp) >= 1) {
      spacing = (int)space_temp + 1;
    }
    else {
      spacing = (int)space_temp;
    }
  }

  return spacing;
 }


short * AllocateBuffer( int spacing, long area, size_t *buffer_size )
 {
     short * buffer;

	*buffer_size = sizeof(short) * ( 3*area/(spacing*spacing) );  //buffer size for number of points needed
	if( ( buffer = (short *)malloc(2 * *buffer_size) ) == NULL )	{//allocate memory for buffer_size elements
		printf("\nInsufficient memory available\nI'm forced to terminate.\n"); //and check for success
		Quit(2);
	}
	else {
		printf("\nMemory successfully allocated of %ld elements.\n", (long)buffer_size);
	}

	return buffer;
 }

void ReadData( HeaderType *hdr, FILE *inputtga, short *buffer, int spacing )
 {
  int input_n=1;
  ImagedataType tgaData;
  int loop;
  int outerloop;

  /**************************************************
  * This design of loop is redundant since file position  *
  * indicator is set to skip rows immediately after *
  * completion of for loop. However, some call is   *
  * necessary to prevent runover past end-of-file.  *
  * Incrementing outerloop by "spacing" accomplishes*
  * this.				                               *
  **************************************************/
  /*
  for (outerloop = 0; outerloop <  hdr->height; outerloop++) {
		
	  if ((outerloop%spacing) == 0) {       
  */

  /*******************************************
  * This loop looks only at rows which are   *
  * separated by 'spacing' variable, where   *
  * file position indicator is set below.    *
  * 'outerloop' is incremented by 'spacing'  *
  * only to prevent end-of-file overrun, not *
  * for any numerical use of 'outerloop'.    *
  *******************************************/
  for (outerloop = 0; outerloop <  hdr->height; outerloop=outerloop+spacing) {


			for (loop = 0; loop < hdr->width; loop++) {  //Start at 0 so 0%2 == 0, etc.


				if (fread(&tgaData, sizeof(ImagedataType), 1, inputtga) != 1) {
					
					if (feof(inputtga)) {
						printf("End of file encountered1! outerloop:%d loop:%d\n", outerloop, loop);
						if ( buffer != NULL ) {
						  free(buffer);
						}

						Quit(0);           //feof() returns '1' if EOF reached
					}

					printf("Unable to read image data\a\n");
				}


				if (feof(inputtga)) {
					printf("End of file encountered2! outerloop:%d loop:%d\n", outerloop, loop);
					if ( buffer != NULL ) {
					  free(buffer);
					}
					Quit(0);           //feof() returns '1' if EOF reached
				}
	
				/*************************************************************************************
				* Transfer image pixel information for horizontal row of data to                     *
				* storage buffer only for pixels determined by spacing determined in PointSpacing(). *
				* 'else()' only appears for debugging purposes.										 *
				*************************************************************************************/
				if ((loop%spacing) == 0) {
												#ifdef DEBUG
												printf("\nAbout to write to buffer on loop=%d, outerloop=%d", loop, outerloop);
												#endif
					*(buffer+input_n) = (short)tgaData.red;
					++input_n;
					*(buffer+input_n) = (short)tgaData.green;
					++input_n;
					*(buffer+input_n) = (short)tgaData.blue;
					++input_n;
				}   //CLOSING BRACE FOR if((loop%spacing)
				else {
												#ifdef DEBUG
												printf("\nNo writing on loop=%d, outerloop=%d", loop, outerloop);
												#endif
				}
			}     //CLOSING BRACE FOR for(loop LOOP
      
			/**********************************************
			* Moving file position indicator to beginning *
			* of the row which is "spacing" rows down.    *
			**********************************************/
			if(fseek(inputtga, (spacing-1) * 3 * hdr->width, SEEK_CUR)) {   
				printf("Error setting file position indicator.\n");
				if ( buffer != NULL ) {
				    free(buffer);
				}
				Quit(1);
			}

//		}     //CLOSING BRACE FOR if((outerloop%spacing), which has been replaced with simpler loop


  }     //CLOSING BRACE FOR for(outerloop LOOP
 }



void WritePov( HeaderType *hdr, FILE *destination, short *buffer, int spacing, char pixel, long flags )
 {
  int output_n = 1;
  double x_coord;							
  double y_coord;
  double z_coord;									
  int x_rotate, z_rotate;						
  int flycount=0;										
  int x_rand, y_rand, z_rand;
  int objects = 0;										
  int wide, high;										
//  double x_look, z_look;
  double radius;						
  double cam_angle;
  double xcam, zcam;
  int xjit, zjit;

  int loop;
  int outerloop;


  fprintf(destination, "// This file was generated for POV-Ray\n");
  fprintf(destination, "// by Andy Woodfin's TGAMosaic ver 2.6\n");
  fprintf(destination, "// email for info at : andy@snakebite.com\n");
  fprintf(destination, "// S, B, and C are radius-width factors for (S)phere, (B)ox, or (C)ylinder.\n");
  fprintf(destination, "// H is cylinder object height factor.\n");
  fprintf(destination, "// RR is projecting cylinder radius.\n");
  fprintf(destination, "// J is sphere position jitter scale (wrt S).\n");
  if ( (flags & FL_ANIM_MASK) == FL_ANIM_GROW ) fprintf(destination, "// E is scale factor for animating object expansion.\n\n");

  /*****************************************************
  * This section calculates width(x) and height(z) of  *
  * solid color background, which is also used for     *
  * look_at coordinates of camera.					   *
  *****************************************************/						
  wide = (int)((hdr->width-1)/(spacing));						
  high = (int)((hdr->height-1)/(spacing)); 
  if(wide>=high)  cam_angle = 2.0*(180/PI)*(double)(atan2( (double)wide/4 , 50.0 ));
  else cam_angle = 2.0*(180/PI)*(double)atan2( (double)high/3 , 50.0 ); 


  fprintf(destination, "#include \"colors.inc\"\n");
  fprintf(destination, "#include \"stones.inc\"\n\n");
  fprintf(destination, "#declare B=1;\n");
  fprintf(destination, "#declare S=1;\n");
  fprintf(destination, "#declare C=1;\n");
  fprintf(destination, "#declare J=0.1;\n");
  fprintf(destination, "#declare H=0.5;\n");
  fprintf(destination, "#declare RR=%.3f;\n", (double)wide/4);
  fprintf(destination, "#declare T=clock;\n");
  if ( (flags & FL_ANIM_MASK) == FL_ANIM_GROW ) fprintf(destination, "#declare E=1;\n");
  if ( (flags & FL_ANIM_MASK) == FL_ANIM_RASTER ) {
	  fprintf(destination, "#declare FLY=50;\n");	  
	  fprintf(destination, "#declare STALL=clock*1000*FLY;\n");
  }
  fprintf(destination, "#declare BallTex=texture {T_Stone18}\n\n");


  
  if ( ! (flags & FL_CYL) ) {
      xcam = (-0.875 + 1.0+(double)wide/2)/2.0;
  } else {
      xcam=0.0;
  }
  zcam = (25.875 + 23.875-(double)high/2)/2.0;
/*
  x_look = (double)(wide)/4+0.125;  
  z_look = 24.875-(double)(high)/4;	  
*/

  printf("wide=%d\t high=%d\n",wide, high);

  fprintf(destination, "camera {\n");
/*  
  fprintf(destination, "  location <%.3f,50,-1.0>\n", x_look + 0.01*x_look);					
  fprintf(destination, "  look_at <%.3f,0.1,%.3f>\n", x_look, z_look);		
*/
  fprintf(destination, "  location <%.3f,50,%.3f>\n", xcam, zcam);					
  fprintf(destination, "  look_at <%.3f,0.1,%.3f>\n", xcam, zcam);
  
  fprintf(destination, "  angle %.3f\n", cam_angle + 0.1*cam_angle);					
  fprintf(destination, "}\n\n");

  fprintf(destination, "light_source {\n");
  fprintf(destination, "  <5.840,35.015,2.564>\n");								
  fprintf(destination, "  color rgb <1.000,1.000,1.000>\n");
  fprintf(destination, "}\n");
/*
  fprintf(destination, "light_source {\n");
  fprintf(destination, "  <-15.840, 2.564, 30.015>\n");
  fprintf(destination, "  color rgb <1.000, 1.000, 1.000>\n");
  fprintf(destination, "}\n");
*/
  fprintf(destination, "light_source {\n");
  fprintf(destination, " <2, 30.015, -5.564>\n");							
  fprintf(destination, " color rgb <1.000,1.000,1.000>\n");
  fprintf(destination, "}\n\n");

  fprintf(destination, "background {color White}\n\n");

  if(! (flags & FL_CYL) ) {
	fprintf(destination, "box {  //Background surface \n");
	fprintf(destination, " <-0.875,0.0,25.875>, ");									
	fprintf(destination, " <%f,-0.2,%f> \n", 1.0+(double)wide/2, (23.875-(double)high/2));		
	fprintf(destination, " pigment {color Black}\n");
	fprintf(destination, "}\n\n");
  }
  else {
	fprintf(destination, "cylinder {  //Background surface \n");
	fprintf(destination, " <0.0,0.0,25.875>, ");							
	fprintf(destination, " <0.0,0.0,%.3f>, RR \n", (23.875-(double)high/2) );		
	fprintf(destination, " pigment {color Black}\n");
	fprintf(destination, "}\n\n");
  }


  for (outerloop = 0; outerloop <  hdr->height; outerloop++) {
    if ((outerloop%spacing) == 0) {
      for (loop = 0; loop < hdr->width; loop++) {

	if ((loop%spacing) == 0) {
	  x_coord = (double)(loop)/(spacing*2)+0.125;					
	  if ( flags & FL_TGAFLIP ) {
		z_coord = 24.875-(double)(spacing*(hdr->height/spacing))/(spacing*2) + (double)(outerloop)/(spacing*2);			;
	  }
	  else z_coord = 24.875-(double)(outerloop)/(spacing*2);			
	  x_rotate = getrandom(0, 90) - 45;									
	  z_rotate = getrandom(0, 90) - 45;								
	  y_rand = getrandom(0, 20);								
	  x_rand = getrandom(0, 2*wide);
	  z_rand = getrandom(0, 2*high);
	  xjit = getrandom(0, 100);
	  zjit = getrandom(0, 100);

/*
	  switch(Lowercase(surface)) {
	    case 'p':
	     y_coord = 0.0;												//CHANGED
	     break;
	    case 's':
	     y_coord = sin(((double)x_coord/( hdr->width/spacing))*sin_freq*2*PI)*(double)z_size;	//CHANGED
	     break;
	    case 'r':
	     y_coord = (double)y_rand/20;								//CHANGED
	     break;
	    default:
	     y_coord = 1.0;												//CHANGED
	  }
*/
	  radius=(double)( *(buffer+output_n) + *(buffer+output_n+1) + *(buffer+output_n+2) )/765;
	  
	  if ( (flags & FL_LINEAR_MASK) == FL_LINEAR )
	    {
	    radius = (double)sqrt((double)radius);
	    }
	  else if( !(flags & FL_LINEAR_MASK) )
	    {
	    radius=1.0;
	    }

	  if ( flags & FL_OBJ ) {
		if( (x_rotate+45) <30) pixel='b';
		else if( (x_rotate+45) <60) pixel='s';
		else pixel='c';
	  }

	  switch(Lowercase(pixel)) {
	    case 'b':
		 y_coord = 0.15*(radius*radius);							//CHANGE FOR Z-Y
	     //if(radius<0.007) radius = 0.007;
	     fprintf(destination, "box {\n");
	     fprintf(destination, "  <-B*%.3f,-B*%.3f,-B*%.3f>, ",0.15*(radius*radius),0.15*(radius*radius),0.15*(radius*radius));	//CHANGE FOR Z-Y
		 fprintf(destination, "<B*%.3f,B*%.3f,B*%.3f>\n",0.15*(radius*radius),0.15*(radius*radius),0.15*(radius*radius));		//CHANGE FOR Z-Y
		 if ( (flags & FL_ANIM_MASK) == FL_ANIM_GROW ) fprintf(destination, "scale E*(T+0.01)\n");
	     break;
	    case 's':
		 y_coord = 0.25*(radius*radius);		//CHANGED
	     //if(radius<0.001) radius = 0.001;
	     fprintf(destination, "sphere {\n");
	     //fprintf(destination, "  <0,0,S*%.3f>,S*%.3f\n",radius,radius);
		 fprintf(destination, "  <0,0,0>,S*%.3f\n",(radius*radius));
	     if ( (flags & FL_ANIM_MASK) == FL_ANIM_GROW ) fprintf(destination, "scale E*(T+0.01)\n");
		 else fprintf(destination, "scale 0.25\n");
	     break;
		 case 'c':
		 y_coord = 0.0;							//CHANGED
		 if ( flags & FL_LINEAR_MASK ) {// linear or non-linear
			 if(radius < 0.010) radius=0.010;
		 }
	     fprintf(destination, "cylinder {\n");
	     //fprintf(destination, "  <0,0,S*%.3f>,S*%.3f\n",radius,radius);
		 fprintf(destination, "  <0,0,0>,<0,H*%.4f,0>,C*0.25\n",(radius*radius));
	     //fprintf(destination, "  scale 0.25\n");
		 if ( (flags & FL_ANIM_MASK) == FL_ANIM_GROW ) fprintf(destination, "scale <1,E*(T+0.01),1>\n");
	     break;
	    default:
	     fprintf(destination, "box {\n");
	     fprintf(destination, "  <0,0,0>,<0.30,0.30,0.30>\n");	
	  }
	  if ( flags & FL_COLOR ) {
		fprintf(destination, "  pigment {\n");
		fprintf(destination, "    color rgb");
		fprintf(destination, "<%.3f,%.3f,%.3f>\n", (double)*(buffer+output_n)/255, (double)*(buffer+output_n+1)/255, (double)*(buffer+output_n+2)/255);
	  }
	  else {
	    if((radius*radius)<0.001) radius = 0.03;
		fprintf(destination, "  texture {\n");
		fprintf(destination, "    BallTex \n");
		fprintf(destination, "    scale S*%.3f\n",(radius*radius));
		fprintf(destination, "    rotate <%d,0,%d> ",x_rotate, z_rotate);		//CHANGE FOR Z-Y
	  }
	  output_n = output_n+3;
	  objects++;
	  fprintf(destination, "  }\n");
	  if ( flags & FL_ROTATE ) fprintf(destination, "  rotate <%d, 0, %d>\n", x_rotate, z_rotate);			//CHANGED
	  fprintf(destination, "  translate  \n");
	  //fprintf(destination, "<%.3f, S*%.3f, %.3f>\n", x_coord, y_coord, z_coord);	//CHANGED
	  if ( (flags & FL_ANIM_MASK) == FL_ANIM_RASTER ) { 
		  fprintf(destination, "#if(STALL>FLY*%d)\n", flycount);	  
		  fprintf(destination, "    <%.3f, B*%.3f+(FLY*%d-STALL), %.3f>\n", x_coord, y_coord, flycount, z_coord);	
		  fprintf(destination, "#else\n");	  
	  }
	  if ( (flags & FL_ANIM_MASK) == FL_ANIM_EXPLODE ) fprintf(destination, "<%.3f+T*%d, S*%.3f+T*%d, %.3f+T*%d>\n", x_coord, x_rand-wide, y_coord, (3*y_rand-10), z_coord, z_rand-high);	//CHANGED
	  else { 
		  switch(Lowercase(pixel)) {
		  case 'b':
			  if(! (flags & FL_CYL) ) fprintf(destination, "    <%.3f, B*%.3f, %.3f>\n", x_coord, y_coord, z_coord);	
			  else fprintf(destination, "    <-(%.3f+RR)*cos(3.14*%.3f), (B*%.3f+RR)*sin(3.14*%.3f), %.3f>\n", y_coord, (double)loop/((double)hdr->width), y_coord, (double)loop/((double)hdr->width), z_coord);	 
		  break;
		  case 's':
			  if(! (flags & FL_CYL) ) fprintf(destination, "    <%.3f+S*J*%.3f, S*%.3f, %.3f+S*J*%.3f>\n", x_coord, ((double)xjit-50)/50, y_coord, z_coord, ((double)zjit-50)/50);	
			  else fprintf(destination, "    <-(%.3f+RR)*cos(3.14*%.3f), (S*%.3f+RR)*sin(3.14*%.3f), %.3f>\n", y_coord, (double)loop/((double)hdr->width), y_coord, (double)loop/((double)hdr->width), z_coord);	 
		  break;
		  case 'c':
			  if(! (flags & FL_CYL) ) fprintf(destination, "    <%.3f, C*%.3f, %.3f>\n", x_coord, y_coord, z_coord);	
			  else fprintf(destination, "    <-(%.3f+RR)*cos(3.14*%.3f), (C*%.3f+RR)*sin(3.14*%.3f), %.3f>\n", y_coord, (double)loop/((double)hdr->width), y_coord, (double)loop/((double)hdr->width), z_coord);	 
		  break;
		  default:
			  if(! (flags & FL_CYL) ) fprintf(destination, "    <%.3f, S*%.3f, %.3f>\n", x_coord, y_coord, z_coord);	
			  else fprintf(destination, "    <-(%.3f+RR)*cos(3.14*%.3f), (S*%.3f+RR)*sin(3.14*%.3f), %.3f>\n", y_coord, (double)loop/((double)hdr->width), y_coord, (double)loop/((double)hdr->width), z_coord);	 
		  }
	  }
	  if ( (flags & FL_ANIM_MASK) == FL_ANIM_RASTER ) fprintf(destination, "#end\n");	  
	  ++flycount;
	  fprintf(destination, "}\n\n");
	}  //CLOSING BRACE FOR if(loop%spacing)
      }   //CLOSING BRACE FOR width for loop
    }     //CLOSING BRACE FOR if(outerloop%spacing) loop
  }  //CLOSING BRACE FOR height for loop
//  printf("Surface variable : %c\n", Lowercase(surface));
//  printf("Amplitude : %d Frequency : %d\n", z_size, sin_freq);
  printf("%d objects created.", objects);
 }

char Lowercase(char letter)    //Just because I wanted to do it myself
 {
  char character;

  if (letter >= 45 && letter <= 90) {
    character = letter + 32;
  }
  else {
    character = letter;
  }
  return character;
 }


char getChoice()
 {
  char tmp[1024];
  char *result;
  char value = 0;

  printf( "\nEnter choice:" );
  result = fgets( tmp, sizeof(tmp), stdin );
  if ( result != NULL )
   {
   value = tmp[0];
   }

  return value;
 }

int fread16Little( unsigned short *pDst, FILE *fp )
 {
  unsigned char tmp[2];
  int readCount = 0;

  if ( pDst != NULL )
   {
   *pDst = 0;
   }

  readCount = fread( tmp, 2, 1, fp );
  if ( readCount > 0 )
   {
   *pDst = ( ( (unsigned short)tmp[1]) << 8 ) | ( 0x00ff & ( unsigned short)tmp[0]);
   }

  return readCount;
 }

