home *** CD-ROM | disk | FTP | other *** search
- /*
- Program : PLANT v0.40a
- Purpose : Create plant-like objects for PoV Ray v1.0 and Polyray v1.6
- raytracers and also export to CTDS format.
- Created : 9/28/93
- By : Rob Bryerton CIS [73747,433]
- File : PLANT040.CPP
- Compiler: Microsoft C++ v8.00
- Model : Small
- Comments: Creates 'organic' plant-like objects using spheres...
- NOTE : this code is VERY MS specific and kinda shaky at this time
- */
-
- #include <graph.h> // for graphics functions
- #include <stdlib.h> // for srand(), rand()
- #include <time.h> // for randomize()
- #include <math.h> // for sin(), cos()
- #include <conio.h> // for getch()
- #include <fstream.h> // for disk access, cin, cout, etc
- #include <string.h> // for strcpy()
- #include <float.h> // for FLT_MIN, FLT_MAX, etc
-
- #define VERSION "0.40a"
- #define GREEN 2 // color indices
- #define RED 4
- #define LIGHTGREEN 10
- #define YELLOW 14
-
- enum bool {FALSE, TRUE};
- enum Format {POV,POLY,CTDS};
-
- struct Vector{ double x,y,z;};
-
- void bound_piece(void);
- void calc_bounds(void);
- void calc_branch(int size, double theta, double x, double y);
- void calc_cluster(int size, double x, double y);
- void close_display(void);
- void end_run(void);
- void get_inputs(void);
- void init_display(int v_mode);
- void new_union(void);
- void process_args (int argc, char* argv[]);
- void process_option (char* s);
- void reset_bounds(void);
- void set_precision(char* prec);
- void show_title(void);
- void usage(void);
- void write_header(void);
- void write_piece(void);
- void write_end(void);
-
- Format format = POV; // default output
- bool nest_bounds = TRUE;
- bool bound_sum = FALSE; // include bound stats w/ each bound written
- bool display = FALSE; // ****** change to FALSE for release version
- bool extended = FALSE; // ****** change to FALSE for release version
- int v_mode=0; // ****** change to 0 for release version 0-none 1-640x480 2-800x600 3-1024x768
- long bound_val=30,bound_count=0;// bnd every ...and count # bounds written for next nested
- long total_bounds = 1; // start w/1 for default global bounding box
- long counter=0; // total # of objects written
- char com[3] = "//"; // default comment char
- char filename[80]; // outfile name
- char union_name[80]; // for output file union name
- char buff[257]; // general use buffer
- char texture[80];
- clock_t start, finish; // ******** 2 lines for timer
- double duration;
- double basic_rad = 1.0,sphere_rad=1.0; // basic radius and current sphere radius
- double bound_radi = 0.0; // current max sph. radius for bound info
- double prev_sphere_rad,first_sphere_rad; // radii
- double ht_scale = 1.0; // flat or tall plant
- double internal_scale, user_scale, overall_scale; // fixes numbers so object is 2 units
- Vector ray = {0.0, 0.0, 0.0}; // outfile vector
- Vector min = {FLT_MAX, FLT_MAX, FLT_MAX}; // for nested bounding info
- Vector max = {FLT_MIN, FLT_MIN, FLT_MIN};
- Vector gmin = {FLT_MAX, FLT_MAX, FLT_MAX}; // for scene extent and
- Vector gmax = {FLT_MIN, FLT_MIN, FLT_MIN}; // global bounding info
-
- ofstream outfile; // these vars are used for basic shape calculations
- double pi=3.1415926535897932384626; // pi ...lower #'s change 'radius' of plant
- int nume=30; // numerator of probability ...left or rite ?
- int denom=100; // denominator of prob.
- int number=4; // number of branches per cluster
- double rad=4.5; // length of straight line segments
- double deltheta=0.1; // change in THETA (radians)
- int segs=60; // max objects & line segments per branch
- int size=60;
- double redux=3.0; // how much to divide # of segments...lower # means finer 'res.' (more objects)
- int min_len=1; // min # of line segments (spheres per branch)
- int x,y; // display vector...object vector is derived from this also
-
-
-
- //main
- int main(int argc, char* argv[])
- {
- if(argc>1)process_args (argc, argv);
-
- show_title();
- get_inputs(); // get user input and check for 'validity'
-
- outfile.open(filename,ios::out); // try to open disk file
- if(! outfile) exit(1); // if disk access error,exit
-
- if(display) init_display(v_mode); // try to set graphics display
-
- write_header();
- outfile.setf(ios::showpoint | ios::fixed);//outfile.precision(6);
- srand( (unsigned)time(NULL) ); // seed random number
-
- x=512,y=384; // set origin of cluster
- internal_scale = 0.1111; // default object size is
- overall_scale = internal_scale*user_scale; // approx.10x10x10
- size = segs;
- bound_val = segs*0.5; // seems to be the best figure as far as rendering
- // speed and bound accuracy are concerned
- if(display) cerr << "TOP VIEW (XZ)\n";
- cerr << "\nCalculating object... Press any key to abort\n\n";
- start = clock(); // ****************** start timer
-
- calc_cluster(size, x, y); // calculate plant and show progress
-
- if(format != CTDS) bound_piece(); // write final nested bound if nec.
- write_end();
- outfile.close; // close disk file
- finish = clock(); // ********************* stop timer
- duration = (double)(finish - start) / CLOCKS_PER_SEC; // *** calc. time
-
- if(display) {
- cerr << "Press any key to continue...";
- getch(); // wait for keypress
- close_display(); // restore video to previous state
- }
- cout << endl << counter << " spheres used to create " << filename;
- cout << "\nCalculation time : "<<duration<< " seconds\n"; // show time
- return(0);
- }
-
- /****************************** **************************
- ****************************** FUNCTION DEFINITIONS *************************
- ****************************** *************************/
-
- void bound_piece()
- {
- Vector cbmin,cbmax; // current bound min and max vectors
-
- cbmax.x = max.x + bound_radi;
- cbmin.x = min.x - bound_radi;
- cbmax.y = max.y + bound_radi;
- cbmin.y = min.y - bound_radi;
- cbmax.z = max.z + bound_radi;
- cbmin.z = min.z - bound_radi;
-
- switch(format){
- case POLY :
- if(nest_bounds){ outfile << " bounding_box < " <<cbmin.x<<", "<<cbmin.y<<", "<<cbmin.z<<" >,<"
- <<cbmax.x<<", "<<cbmax.y<<", "<<cbmax.z<<" >\n"
- << " }"; // close the UNION
- }
- else { outfile << " }";
- }
- break;
- case POV :
- if(nest_bounds){ outfile << " }\n"
- << " bounded_by{ \n"
- << " box{ < " << cbmin.x<<" "<<cbmin.y<<" "<<cbmin.z << "> < "
- << cbmax.x<<" "<<cbmax.y<<" "<<cbmax.z << "> }\n }\n"
- << " } ";
- }
- else { outfile << " }\n }";
- }
- }
- if(bound_sum){
- outfile <<com<<" Current bounding box info...\n"
- <<com<<" Min. x : " << cbmin.x << endl
- <<com<<" y : " << cbmin.y << endl
- <<com<<" z : " << cbmin.z << endl
- <<com<<" Max. x : " << cbmax.x << endl
- <<com<<" y : " << cbmax.y << endl
- <<com<<" z : " << cbmax.z;
- }
- outfile << endl;
- }
-
-
- void calc_bounds()
- {
- // compute min and max object vectors for current bounding box if needed
- if(format != CTDS && nest_bounds){
- min.x = __min(min.x,ray.x);
- max.x = __max(max.x,ray.x);
- min.y = __min(min.y,ray.y);
- max.y = __max(max.y,ray.y);
- min.z = __min(min.z,ray.z);
- max.z = __max(max.z,ray.z);
- }
-
- gmin.x = __min(gmin.x,ray.x); // now globally
- gmax.x = __max(gmax.x,ray.x);
- gmin.y = __min(gmin.y,ray.y);
- gmax.y = __max(gmax.y,ray.y);
- gmin.z = __min(gmin.z,ray.z);
- gmax.z = __max(gmax.z,ray.z);
- }
-
- void calc_branch(int size, double theta, double x, double y)
- {
- for(int j=0; j<size; j++)
- { // get rand. #, range 0 to denom
- int randy = rand() % denom;
- int chng = (randy<nume) ? -1 : 1; // left or right ??
- theta = theta + chng * deltheta; // new angle
- x = x + rad*sin(theta); // x and y
- y = y + rad*cos(theta); // of next point
-
- if(size<(segs*0.0667)){ ray.y =j+(segs-3)+(segs/redux)+((segs/redux)/redux);
- if(display) _setcolor(RED);
- if(j==0) strcpy(texture,"flower_tex");
- sphere_rad = basic_rad*1.2;
- }
- else if(size<(segs*0.22)){ ray.y =j+(segs-2)+(segs/redux);
- if(display) _setcolor(YELLOW);
- if(j==0) strcpy(texture,"sub_branch_tex");
- sphere_rad = basic_rad*1.45;
- }
- else if(size<(segs*0.675)){ ray.y =j+(segs-1);
- if(display) _setcolor(LIGHTGREEN);
- if(j==0) strcpy(texture,"branch_tex");
- sphere_rad = basic_rad*1.85;
- }
- else{ ray.y =j;
- if(display) _setcolor(GREEN); //dk green
- if(j==0) strcpy(texture,"stem_tex");
- sphere_rad = basic_rad*2.25;
- }
- if(display){
- if (v_mode==1) _lineto(int(x*0.625),int(y*0.625));
- else if(v_mode==2) _lineto(int (x*0.78125),int(y*0.78125));
- else _lineto(int(x),int(y)); // draw line
- }
-
- ray.x = ((double)(x-512)*0.3)*overall_scale; // translate sizes to
- ray.z = ((double)(y-384)*0.3)*overall_scale; // file sizes based on
- ray.y = ray.y*overall_scale*ht_scale; // scaling info...
- sphere_rad *= overall_scale;
-
- if(counter==0){ //first time around only
- prev_sphere_rad = sphere_rad;
- first_sphere_rad = sphere_rad;
- }
- bound_radi = prev_sphere_rad; //current bound, radius to add to bound
-
- if((format != CTDS) && // if it's POV or Polyray AND we're
- (bound_count==bound_val && nest_bounds || //ready for a bound (nested only) OR....
- prev_sphere_rad != sphere_rad && nest_bounds)// the sphere radius changed... time for a new bound
- ){ // (nested only) GOT IT ??? (pretty twisted)
- bound_piece();
- new_union();
- total_bounds++;
- reset_bounds(/*min,max*/);
- bound_count=1;
- write_piece();
- }
- else {
- bound_count++;
- write_piece();
- }
-
- prev_sphere_rad = sphere_rad;
- calc_bounds();
- counter++; // increment sphere count
- }
- if(size>min_len)
- { // if branch long enough
- int newsize = size / redux; // but smaller than before
- calc_cluster(newsize,x,y); // and display it
- }
- }
-
- void calc_cluster(int size, double x, double y)
- {
- if( kbhit()) end_run();
-
- for(int i=0; i<number; i++) // for each branch
- {
- double theta = i * 2 * pi / number;
-
- if(display){
- if (v_mode==1) _moveto(int(x*0.625),int(y*0.625));
- else if(v_mode==2) _moveto(int(x*0.78125),int(y*0.78125));
- else _moveto(int(x),int(y)); // get set to draw next line
- }
-
- // make a branch
- calc_branch(size, theta, x, y);
- }
- }
-
-
- void close_display()
- {
- _setvideomode(_DEFAULTMODE); // restore video to 'normal'
- }
-
- void end_run()
- {
- if(format != CTDS) bound_piece();
- write_end();
- if(display) close_display(); // restore video to previous state
- cerr << "\n--RUN ABORTED--\n";
- cout << counter << " spheres used to create " << filename << endl;
-
- exit(1);
- }
-
-
- void get_inputs()
- {
- int choice; double noise=1.0,pi_distort=1.0,sphere_mult=1.0,denom_mult=1.0;
-
- cout<< "\n1\tPOV\n2\tPolyray\n3\tCTDS\n" << "Number for format? [1]: ";
- cin.getline(buff,256); choice = atoi(buff);
- switch(choice){ case 2 : format = POLY; break; // output format
- case 3 : format = CTDS; break;
- default: format = POV;
- }
- cout << "Ouput filename? [plant.inc]: ";
- cin.getline(buff,256); strcpy(filename,buff);
- if(format != CTDS){
- cout << "Union name? [plant]: ";
- cin.getline(buff,256); strcpy(union_name,buff); }
- cout << "Number of branches (recursive) ? [4]:";
- cin.getline(buff,256); number = atoi(buff);
- cout << "Maximum # of spheres per branch ? [60]:";
- cin.getline(buff,256); segs = atoi(buff);
- cout << "Minimum # of spheres per branch ? [1]:";
- cin.getline(buff,256); min_len = atoi(buff);
- if(extended) {
- cout << "Divide branches by ? [3.0]:";
- cin.getline(buff,256); redux = atof(buff);
- cout << "Radial distortion (PI * x) ? [1.0]:";
- cin.getline(buff,256); pi_distort = atof(buff);
- cout << "Branch rotation factor ? [1.0]:";
- cin.getline(buff,256); denom_mult = atof(buff);
- cout << "Overall noise factor ? [1.0]:";
- cin.getline(buff,256); noise = atof(buff);
- cout << "Height scaling ? [1.0]:";
- cin.getline(buff,256); ht_scale = atof(buff);
- cout << "Sphere scaling ? [1.0]:";
- cin.getline(buff,256); sphere_mult = atof(buff); }
- cout << "Global scaling ? [1.0]: ";
- cin.getline(buff,256); user_scale = atof(buff);
-
- // Set up default values if applicable
- if(filename[0]=='\0') strcpy(filename,"plant.inc");
- if(union_name[0]=='\0') strcpy(union_name,"plant");
- if(min_len==0) min_len = 1;
- if(denom_mult==0.0) denom_mult = 1.0; denom *= denom_mult;
- if(number==0) number = 4; if(user_scale==0.0) user_scale = 1.0;
- if(pi_distort==0.0) pi_distort = 1.0; pi *= pi_distort;
- if(noise==0.0) noise = 1.0; deltheta *= noise;
- if(segs==0) segs = 60; if(redux==0.0) redux = 3.0;
- if(ht_scale==0.0) ht_scale = 1.0;
- if(sphere_mult==0.0) sphere_mult = 1.0; basic_rad *= sphere_mult;
- }
-
-
- void init_display(int v_mode)
- {
- switch(v_mode){
- case 3 : if(!_setvideomode(_XRES16COLOR)) {cerr << "\nCouldn't set 1024x768 graphics mode... trying 800x600\n"; v_mode = 2;}
- else break; /* 1024x768x16 */
- case 2 : if(!_setvideomode(_SRES16COLOR)) {cerr << "\nCouldn't set 800x600 graphics mode... trying 640x480\n"; v_mode = 1;}
- else break; /* 800x600x16 */
- case 1 : if(!_setvideomode(_VRES16COLOR)) {cerr << "\nCouldn't set 640x480 graphics mode... continuing without display\n"; v_mode = 0;}
- else break; /* 640x480x16 */
- default : display = FALSE; /* no display */
- }
- }
-
-
- void new_union()
- {
-
- switch(format){
- case POV :
- outfile << " object{\n union{\n"; break;
- case POLY :
- outfile << "+object{ \n"; break;
- }
-
- }
-
-
- void process_args (int argc, char* argv[])
- {
- for (int i = 1; i < argc; i++)
- {
- if (argv[i][0] == '-' || argv[i][0] == '/')
- process_option (&argv[i][1]);
- else
- {usage(); exit(1);}
- }
- }
-
-
- void process_option (char* s)
- {
- switch (toupper(s[0]))
- {
- case 'E': extended = TRUE; break;
- case 'P': set_precision(&s[1]); break;
- case 'D': display = TRUE; v_mode = atoi(&s[1]); break;
- case 'N': nest_bounds = FALSE; break;
- case 'S': bound_sum = TRUE; break;
- case '?': usage(); exit(0);
- case 'H': usage(); exit(0);
- default : usage(); exit(1);
- }
- }
-
- void reset_bounds()
- {
- min.x=FLT_MAX, min.y=FLT_MAX, min.z=FLT_MAX; //
- max.x=FLT_MIN, max.y=FLT_MIN, max.z=FLT_MIN; // reset for new bounding box
- }
-
- void set_precision(char* prec)
- {
- outfile.precision((atoi(prec) > 2 ? atoi(prec) : 6 ));
- }
-
- void show_title()
- {
- cout << "\nPlant v"<< VERSION << ", Copyright (c) 1993 Rob Bryerton\n"
- << "Creates a data file of a plant-like object for the PoV-Ray v1.0" << endl
- << "and Polyray v1.6 raytracers, and the Connect The Dots Smoother (CTDS).\n";
- }
-
- void write_header()
- {
- if(format==CTDS){ strcpy(com,";"); strcpy(union_name,"-----");}
-
- outfile
- <<com<<" File: "<<filename<<" Union Name: "<<union_name<< endl
- <<com<<" This data file created by PLANT.EXE v"<<VERSION<<" for the PoV Ray v1.0 and\n"
- <<com<<" Polyray v1.6 raytracers, and the Connect The Dots Smoother (CTDS).\n"
- <<com<<" ----------------SEE END OF LISTING FOR OBJECT EXTENTS----------------------\n\n";
- switch(format) {
- case POLY :
- outfile
- << "include \"..\\colors.inc\"\n"
- //<< "include \"..\\texture.inc\"\n\n"
- << "define stem_tex texture{ matte { color <0.0, 0.65, 0.0> } }\n"
- << "define branch_tex texture{ matte { color <0.0, 0.8, 0.0> } }\n"
- << "define sub_branch_tex texture{ matte { color yellow } }\n"
- << "define flower_tex texture{ matte { color red } }\n\n"
- << "define " << union_name << endl
- << "object{ \n object{\n"; break;
- case POV :
- outfile
- << "#declare stem_tex= texture{ diffuse 0.7 color green 0.65 } \n"
- << "#declare branch_tex= texture{ diffuse 0.7 color green 0.8 } \n"
- << "#declare sub_branch_tex= texture{ diffuse 0.7 color Yellow } \n"
- << "#declare flower_tex= texture{ diffuse 0.7 color Red } \n\n"
- << "declare "<<union_name<<" = composite{ \nobject{\n union{\n";
- }
- }
-
- void write_piece()
- {
- switch(format){
- case POLY :
- if(counter !=0 && bound_count!=1) outfile << " +"; // for Polyray union
- else outfile << " ";
- outfile << "object{ sphere < " <<ray.x<< ", " << ray.y << ", " <<ray.z<< " >, " << sphere_rad << " " << texture <<" }\n";
- break;
- case CTDS :
- if(sphere_rad != prev_sphere_rad) outfile << endl; //insert blank line for CTDS for each new sphere size for /m option
- outfile << ray.x<< " " << ray.y << " " <<ray.z<< " " << sphere_rad << endl;
- break;
- default : // (POV 1.0)
- outfile << " sphere{ < "<<ray.x<<" "<<ray.y<<" "<<ray.z<<" > "<<sphere_rad<<" texture{ " <<texture<< "} }\n";
- }
- }
-
-
-
- void write_end()
- {
- gmax.x += first_sphere_rad;
- gmin.x -= first_sphere_rad;
- gmax.y += first_sphere_rad;
- gmin.y -= first_sphere_rad;
- gmax.z += first_sphere_rad;
- gmin.z -= first_sphere_rad;
-
- switch(format){
- case POLY :
- outfile << " bounding_box < " <<gmin.x<<", "<<gmin.y<<", "<<gmin.z<<" >,<"
- <<gmax.x<<", "<<gmax.y<<", "<<gmax.z<<" >\n"
- << "}\n\n"; break; // close the UNION
-
- case POV :
- outfile << " bounded_by{\n"
- << " box{ < " << gmin.x<<" "<<gmin.y<<" "<<gmin.z << "> < "
- << gmax.x<<" "<<gmax.y<<" "<<gmax.z << "> }\n }\n"
- << "}\n\n"; // close composite
- } // this next block is output for ALL formats w/the appropriate comment char's
- if(format != CTDS){
- outfile <<com<<" Bounds written...."<< total_bounds << endl; }
- outfile <<com<<" Spheres written..."<< counter << endl << endl
- <<com<<" Object extents are as follows...\n"
- <<com<<" Min. x : " << gmin.x << endl
- <<com<<" y : " << gmin.y << endl
- <<com<<" z : " << gmin.z << endl
- <<com<<" Max. x : " << gmax.x << endl
- <<com<<" y : " << gmax.y << endl
- <<com<<" z : " << gmax.z << endl;
- }
-
-
-
- void usage()
- {
- cerr << "\n Plant v" << VERSION << endl
- << "Usage: plant [options]\n"
- << "Options: -d# enables 2D (xz) display of plant... # is the graphics mode.\n"
- << " 0=no display 1=640x480 2=800x600 3=1024x768 \n"
- << " Default value is 0.\n"
- << " -e Gives you a menu with an extended choice of options.\n"
- << " -n No nested bounds in POV or Polyray output.\n"
- << " -p# where # is the precision of the output file numbers.\n"
- << " Range is 3 - 9 with a default precision of 6 decimal places.\n"
- << " -s Include all bounding stats in output file. (POV and Polyray)\n"
- << "\nType PLANT with no parameters for the default menus and options.\n";
- }
-
-
-