home *** CD-ROM | disk | FTP | other *** search
- /*-------------------------------------------------------------------------
-
- Fractal Landscape Generator v1.4
- Copyright (c) 1993 Steve Anger
- This program is freely distributable
-
- FRGEN is a utility to generate fractal landscapes and shapes using
- successive triangle sub-division. The fractal data can be output as
- POV-Ray 1.0/2.0 or Vivid scene description files or as raw triangle
- data.
-
- Notes for compiling: This program has been successfully compiled with
- Borland C++ 3.1 and DJGCC C/C++ 2.4.1. Under Turbo/Borland C++ compile
- with COMPACT or LARGE memory model with "Copy propagation" optimization
- disabled.
-
- CompuServe: 70714,3113
- YCCMR BBS: (708)358-5611
-
- Modified for Mac MPW C++ support 4/16/93 - Eduard [esp] Schwan
-
- -------------------------------------------------------------------------*/
-
- #include <fstream.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <math.h>
- #include <values.h>
- #include <time.h>
- #include "vect.h"
-
- #ifdef __TURBOC__
- #include <graphics.h>
- #include <conio.h>
- #include <dos.h>
- extern unsigned _stklen = 16000; // Larger stack size for recursion
- #endif
-
- #ifdef applec
- #include <CursorCtl.h> // for multitasking
- #endif
-
- typedef enum bool {false = 0, true = 1};
- typedef enum Format {POV10, POV20, VIVID, RAW, NONE};
-
- const char ver[] = "v1.4";
-
- // Function prototypes
- void usage (void);
- void abortmsg (char *msg, int exit_code);
- void add_ext (char *fname, char *ext, bool force);
- void swap (float &a, float &b);
- void process_args (int argc, char *argv[]);
- void file_args (void);
- void process_option (char *s);
- void write_options (void);
- bool read_triangle (Vector&a, Vector&b, Vector&c, int &fix_ab, int &fix_bc, int
- &fix_ca);
- Vector noise_vector (Vector&v, int level);
- void tri_fractal (Vector&a, Vector&b, Vector&c, int fix_ab, int fix_bc, int
- fix_ca, int level, Vector&vmin, Vector&vmax);
- void write_light (fstream &f, Vector pos);
- void write_camera (fstream &f, Vector pos, Vector target);
- void set_view (void);
- char upcase (char c);
- void plot_tri (const Vector&a, const Vector&b, const Vector&c);
- int init_display (void);
- void close_display (void);
- void check_abort (void);
-
-
- // Global variables
- fstream in, out, data;
- Vector viewpoint; // Where you're looking from
- Vector lookat; // Where you're looking at
- Vector nx, ny, nz; // Basis vectors for viewpoint
- Vector nscale; // Noise scaling factors
- Vector nbias; // Noise biasing factors
- Vector nfalloff; // Noise falloff factor
- int depth; // Depth of recursion
- int seed; // Seed for random number generator
- bool display; // Set to true if display preview enabled
- bool swap_yz; // Swap y and z coords on output
- Format format; // Output file format
- char infile[64]; // Input file name
- char mainfile[64]; // Output file name
- char datafile[64]; // Include file name
- long tri_count; // Count of triangles generated
- int input_line; // Current input line being parsed
- Vector gmin, gmax;
-
-
- int main (int argc, char* argv[])
- {
- cerr << "\nFractal Landscape Generator " << ver << " ";
- cerr << "Copyright (c) 1993 Steve Anger\n";
- cerr << "This program is freely distributable\n\n";
-
- srand ((unsigned)time(NULL));
- seed = (int)(10000.0 * rand() / (float)RAND_MAX);
- infile[0] = '\0';
- mainfile[0] = '\0';
- viewpoint = Vector (10.0, 10.0, -10.0);
- lookat = Vector (0.0, 0.0, 0.0);
- nscale = Vector (0.10, 0.10, 0.10);
- nbias = Vector (0.0, 0.0, 0.0);
- nfalloff = Vector (1.0, 1.0, 1.0);
- depth = 3;
- display = false;
- swap_yz = false;
- format = POV20;
- tri_count = 0;
- input_line = 0;
-
- for (int i = 1; i < argc; i++) {
- if (argv[i][0] != '-' && argv[i][0] != '/') {
- if (strlen(infile) == 0) {
- strcpy (infile, argv[i]);
- add_ext (infile, "fr", false);
- }
- else if (strlen(mainfile) == 0)
- strcpy (mainfile, argv[i]);
- else
- abortmsg ("Too many file names specified.", 1);
- }
- }
-
- if (strlen (infile) == 0) {
- usage();
- exit (1);
- }
-
- in.open (infile, ios::in);
- if (!in) abortmsg ("Error opening input file.", 1);
-
- file_args(); // Get options from input file
-
- process_args (argc, argv); // Get the command line options
-
- if (strlen (mainfile) == 0) {
- strcpy (mainfile, infile);
- strcpy (datafile, infile);
-
- switch (format) {
- case POV10:
- case POV20: add_ext (mainfile, "pov", true);
- add_ext (datafile, "inc", true); break;
- case VIVID: add_ext (mainfile, "v", true);
- add_ext (datafile, "vo", true); break;
- case RAW: add_ext (datafile, "raw", true); break;
- }
- }
-
- switch (format) {
- case POV10:
- case POV20: add_ext (mainfile, "pov", false);
- add_ext (datafile, "inc", false); break;
- case VIVID: add_ext (mainfile, "v", false);
- add_ext (datafile, "vo", false); break;
- case RAW: add_ext (datafile, "raw", false); break;
- }
-
- if (format != NONE) {
- if (format != RAW) {
- out.open (mainfile, ios::out);
- if (!out) abortmsg ("Error opening output file.", 1);
- }
-
- data.open (datafile, ios::out);
- if (!data) abortmsg ("Error opening output file.", 1);
- }
-
- if (depth < 1)
- abortmsg ("Recurse depth (-r) must be >= 1", 1);
-
- cout << "Generating fractal\n";
-
- if (display) {
- set_view();
-
- if (init_display() < 0)
- abortmsg ("Unable to initialize graphics display.", 1);
- }
-
- write_options();
-
- switch (format) {
- case POV10: out << "#declare FracTexture = texture {\n";
- out << " ambient 0.1 diffuse 0.8\n";
- out << " color red 1.0 green 1.0 blue 1.0\n";
- out << "}\n\n";
- out << "#include \"" << datafile << "\"\n\n";
- data << "composite {\n";
- break;
-
- case POV20: out << "#declare FracTexture = texture {\n";
- out << " finish { ambient 0.1 diffuse 0.8 }\n";
- out << " pigment { color red 1.0 green 1.0 blue 1.0 }\n";
- out << "}\n\n";
- out << "#include \"" << datafile << "\"\n\n";
- data << "union {\n";
- break;
-
- case VIVID: out << "surface {\n";
- out << " ambient 0.1 0.1 0.1\n";
- out << " diffuse 0.8 0.8 0.8\n";
- out << " shine 70, 1.0 1.0 1.0\n";
- out << "}\n\n";
- out << "#include " << datafile << "\n\n";
- break;
- }
-
- if (format == POV20)
- Vector::set_delim (',');
-
- Vector a, b, c, vmin, vmax;
- int fix_ab, fix_bc, fix_ca, tri_read = 0;
-
- Vector gmin = Vector (+MAXFLOAT, +MAXFLOAT, +MAXFLOAT);
- Vector gmax = Vector (-MAXFLOAT, -MAXFLOAT, -MAXFLOAT);
-
- while (read_triangle (a, b, c, fix_ab, fix_bc, fix_ca)) {
- tri_fractal (a, b, c, fix_ab, fix_bc, fix_ca, depth, vmin, vmax);
-
- gmin = min (gmin, vmin);
- gmax = max (gmax, vmax);
- tri_read++;
- }
-
- switch (format) {
- case POV10:
- data << "\tbounded_by { box { <" << gmin << "> <" << gmax << "> } }\n";
- data << "}\n\n";
- break;
-
- case POV20:
- data << "\n\ttexture { FracTexture }\n";
- data << "}\n\n";
- break;
- }
-
- if (format != RAW && format != NONE) {
- write_camera (out, viewpoint, lookat);
- write_light (out, viewpoint);
- }
-
- in.close();
-
- if (format != NONE) {
- data.close();
-
- if (format != RAW)
- out.close();
- }
-
- cerr << "\n" << tri_count << " triangles generated.\n";
-
- if (display) {
- cerr << "Press return.\n";
- getchar();
- close_display();
- }
-
- return 0;
- }
-
-
- void usage()
- {
- cerr << "Usage: frgen infile[.fr] [outfile] [options]\n";
- cerr << "Options: -sx, -sy, -sz Scale noise\n";
- cerr << " -bx, -by, -bz Bias noise\n";
- cerr << " -fx, -fy, -fz Set noise falloff\n";
- cerr << " -vx, -vy, -vz Set viewpoint position\n";
- cerr << " -lx, -ly, -lz Set lookat position\n";
- cerr << " -ennn Use nnn as seed for random number generator\n";
- cerr << " -rnnn Maximum recursion depth of nnn\n";
- cerr << " -d Display generated fractal on screen\n";
- cerr << " -op Output to POV-Ray 2.0 format (default)\n";
- cerr << " -op1 Output to POV-Ray 1.0 format\n";
- cerr << " -ov Output to Vivid 2.0 format\n";
- cerr << " -or Output to RAW triangle format\n";
- cerr << " -on Null output (for quick preview).\n";
- cerr << " -x Exchange Y and Z coords on output.\n";
- }
-
-
- void abortmsg (char *msg, int exit_code)
- {
- if (display)
- close_display();
-
- cerr << msg << "\n";
- exit (exit_code);
- }
-
-
- void add_ext (char *fname, char *ext, bool force)
- {
- int i;
-
- for (i = 0; i < strlen(fname); i++)
- if (fname[i] == '.') break;
-
- if (fname[i] == '\0' || force) {
- fname[i] = '.';
- strcpy (&fname[i+1], ext);
- }
- }
-
-
- void swap (float &a, float &b)
- {
- float temp = a;
- a = b;
- b = temp;
- }
-
-
- 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]);
- }
- }
-
-
- void file_args()
- {
- char line[100] = "";
- char last_ch = ' ';
-
- do {
- in.getline (line, 100);
- input_line++;
- } while (!in.eof() && line[0] == ';');
-
- if (in.eof())
- return;
-
- for (int i = 0; i < strlen (line); i++) {
- if ((line[i] == '-' || line[i] == '/') && isspace(last_ch))
- process_option (&line[i+1]);
-
- last_ch = line[i];
- }
- }
-
-
- void process_option (char *s)
- {
- switch (upcase(s[0])) {
- case 'V':
- switch (upcase(s[1])) {
- case 'X': viewpoint.x = atof(&s[2]); break;
- case 'Y': viewpoint.y = atof(&s[2]); break;
- case 'Z': viewpoint.z = atof(&s[2]); break;
- default : cerr << "Invalid option -v" << s[1] << "\n";
- }
- break;
-
- case 'L':
- switch (upcase(s[1])) {
- case 'X': lookat.x = atof(&s[2]); break;
- case 'Y': lookat.y = atof(&s[2]); break;
- case 'Z': lookat.z = atof(&s[2]); break;
- default : cerr << "Invalid option -l" << s[1] << "\n";
- }
- break;
-
- case 'S':
- switch (upcase(s[1])) {
- case 'X': nscale.x = atof(&s[2]); break;
- case 'Y': nscale.y = atof(&s[2]); break;
- case 'Z': nscale.z = atof(&s[2]); break;
- default : float value = atof(&s[1]);
- nscale = Vector (value, value, value);
- }
- break;
-
- case 'B':
- switch (upcase(s[1])) {
- case 'X': nbias.x = atof(&s[2]); break;
- case 'Y': nbias.y = atof(&s[2]); break;
- case 'Z': nbias.z = atof(&s[2]); break;
- default : float value = atof(&s[1]);
- nbias = Vector (value, value, value);
- }
- break;
-
- case 'F':
- switch (upcase(s[1])) {
- case 'X': nfalloff.x = atof(&s[2]); break;
- case 'Y': nfalloff.y = atof(&s[2]); break;
- case 'Z': nfalloff.z = atof(&s[2]); break;
- default : float value = atof(&s[1]);
- nfalloff = Vector (value, value, value);
- }
- break;
-
- case 'E': seed = atoi(&s[1]);
- break;
-
- case 'R': depth = atoi(&s[1]);
- break;
-
- case 'D': display = true;
- break;
-
- case 'X': swap_yz = true;
- break;
-
- case 'O' :
- switch (upcase(s[1])) {
- case 'P': format = (s[2] == '2') ? POV20 : POV10; break;
- case 'V': format = VIVID; break;
- case 'R': format = RAW; break;
- case 'N': format = NONE; break;
- default : cerr <<"Invalid option -o" << s[1] << ", ignored\n";
- }
- break;
-
-
- default : cerr << "Invalid option -" << s[1] << ", ignored\n";
- }
- }
-
-
- void write_options()
- {
- if (format == POV10 || format == POV20 || format == VIVID) {
- data << "/*\n";
- data << " Generated with FRGEN " << ver << " from file " << infile << "\n";
- data << " Options in effect:";
- data << " -sx" << nscale.x << " -sy" << nscale.y << " -sz" << nscale.z;
- data << " -bx" << nbias.x << " -by" << nbias.y << " -bz" << nbias.z << "\n";
- data << " -fx" << nfalloff.x << " -fy" << nfalloff.y << " -fz" << nfalloff.z;
- data << " -r" << depth << " -e" << seed << "\n";
- data << "*/\n\n";
- }
- else if (format == NONE)
- cout << "Seed: -e" << seed << "\n";
- }
-
-
- bool read_triangle (Vector &a, Vector &b, Vector &c,
- int &fix_ab, int &fix_bc, int &fix_ca)
- {
- char line[256] = "";
- char msg[40];
- int n;
-
- do {
- in.getline (line, 256);
- input_line++;
- } while (!in.eof() && line[0] == ';');
-
- if (in.eof())
- return false;
-
- a = Vector (0.0, 0.0, 0.0);
- b = Vector (0.0, 0.0, 0.0);
- c = Vector (0.0, 0.0, 0.0);
-
- fix_ab = 0;
- fix_bc = 0;
- fix_ca = 0;
-
- n = sscanf (line, "%f %f %f %f %f %f %f %f %f %d %d %d",
- &a.x, &a.y, &a.z, &b.x, &b.y, &b.z, &c.x, &c.y, &c.z,
- &fix_ab, &fix_bc, &fix_ca);
-
- if (n == EOF)
- return false;
-
- if (!(n == 9 || n == 12)) {
- sprintf (msg, "Error in input file, line %d", input_line);
- abortmsg (msg, 1);
- }
-
- return true;
- }
-
-
- Vector noise_vector (Vector &v, int level)
- {
- // Generate a random vector that is a function of the
- // vector v's position
- Vector noise, scale;
-
- scale.x = nscale.x * exp (level * log (nfalloff.x));
- scale.y = nscale.y * exp (level * log (nfalloff.y));
- scale.z = nscale.z * exp (level * log (nfalloff.z));
-
- // seed the rand # generator with a mish-mash of the x, y, and z coords
- srand (seed ^ (long)(-23465*v.x) ^ (long)(17365*v.y) ^ (long)(5364*v.z));
-
- noise.x = scale.x * (2.0*rand()/RAND_MAX - 1.0 + nbias.x);
- noise.y = scale.y * (2.0*rand()/RAND_MAX - 1.0 + nbias.y);
- noise.z = scale.z * (2.0*rand()/RAND_MAX - 1.0 + nbias.z);
-
- return noise;
- }
-
-
- void tri_fractal (Vector &a, Vector &b, Vector &c,
- int fix_ab, int fix_bc, int fix_ca, int level,
- Vector &vmin, Vector &vmax)
- {
- Vector ab, bc, ca;
- float ab_len, bc_len, ca_len;
-
- vmin = Vector (+MAXFLOAT, +MAXFLOAT, +MAXFLOAT);
- vmax = Vector (-MAXFLOAT, -MAXFLOAT, -MAXFLOAT);
-
- if (level == 0) {
- check_abort();
-
- if (swap_yz) {
- swap (a.y, a.z);
- swap (b.y, b.z);
- swap (c.y, c.z);
- }
-
- switch (format) {
- case POV10:
- data << "\t\ttriangle { <" << a << "> <" << b << "> <" << c << "> }\n";
- break;
-
- case POV20:
- data << "\ttriangle { <" << a << ">, <" << b << ">, <" << c << "> }\n";
- break;
-
- case VIVID:
- data << "polygon {\n";
- data << "\tpoints 3\n";
- data << "\tvertex " << a << "\n";
- data << "\tvertex " << b << "\n";
- data << "\tvertex " << c << "\n";
- data << "}\n\n";
- break;
-
- case RAW:
- data << a << " " << b << " " << c << "\n";
- break;
- }
-
- if (swap_yz) {
- swap (a.y, a.z);
- swap (b.y, b.z);
- swap (c.y, c.z);
- }
-
- tri_count++;
-
- if (display)
- plot_tri (a, b, c);
-
- vmin = min (vmin, a);
- vmin = min (vmin, b);
- vmin = min (vmin, c);
-
- vmax = max (vmax, a);
- vmax = max (vmax, b);
- vmax = max (vmax, c);
- }
- else {
- if (format == POV10) {
- if (level == 1) {
- data << "\tobject {\n";
- data << "\t\tunion {\n";
- }
- else if (level > 1)
- data << "composite {\n";
- }
-
- // Find the midpoints of the three line segments
- ab = (a + b)/2.0;
- bc = (b + c)/2.0;
- ca = (c + a)/2.0;
-
- ab_len = mag(a - b);
- bc_len = mag(b - c);
- ca_len = mag(c - a);
-
- // Compute the normal to the triangle
- Vector norm = (b - a) * (c - a);
- norm = norm/mag(norm);
-
- // Create some noise proportional to the edge lengths
- Vector noise_ab = ab_len * noise_vector (ab, depth - level);
- Vector noise_bc = bc_len * noise_vector (bc, depth - level);
- Vector noise_ca = ca_len * noise_vector (ca, depth - level);
-
- // Don't let any 'fixed' points move out of the plane
- // of the triangle (remove the noise's normal component)
- if (fix_ab) noise_ab = noise_ab * norm;
- if (fix_bc) noise_bc = noise_bc * norm;
- if (fix_ca) noise_ca = noise_ca * norm;
-
- // Perturb the three vectors
- ab = ab + noise_ab;
- bc = bc + noise_bc;
- ca = ca + noise_ca;
-
- Vector new_min[4], new_max[4];
-
- tri_fractal (a, ab, ca, fix_ab, 0, fix_ca, level-1, new_min[0], new_max[0]);
- tri_fractal (b, bc, ab, fix_bc, 0, fix_ab, level-1, new_min[1], new_max[1]);
- tri_fractal (c, ca, bc, fix_ca, 0, fix_bc, level-1, new_min[2], new_max[2]);
- tri_fractal (ab, bc, ca, 0, 0, 0, level-1, new_min[3], new_max[3]);
-
- for (int i = 0; i < 4; i++) {
- vmin = min (vmin, new_min[i]);
- vmax = max (vmax, new_max[i]);
- }
-
- if (format == POV10) {
- if (level == 1) {
- data << "\t\t}\n\n";
-
- data << "\t\ttexture { FracTexture }\n\n";
- data << "\t\tbounded_by { box { <" << vmin << "> <" << vmax << "> } }\n";
- data << "\t}\n\n";
- }
- else if (level > 1) {
- data << "\tbounded_by { box { <" << vmin << "> <" << vmax << "> } }\n";
- data << "}\n";
- }
- }
- }
- }
-
-
- void write_light (fstream &f, Vector pos)
- {
- switch (format) {
- case POV10:
- f << "object {\n";
- f << " light_source { <" << pos << "> color red 1 green 1 blue 1 }\n";
- f << "}\n\n";
- break;
-
- case POV20:
- f << "light_source { <" << pos << "> color rgb <1,1,1> }\n\n";
- break;
-
- case VIVID:
- f << "light {\n";
- f << " type point\n";
- f << " position " << pos << "\n";
- f << " color 1.0 1.0 1.0\n";
- f << "}\n\n";
- break;
- }
- }
-
-
- void write_camera (fstream &f, Vector pos, Vector target)
- {
- switch (format) {
- case POV10:
- f << "camera {\n";
- f << " location <" << pos << ">\n";
- f << " look_at <" << target << ">\n";
- f << "}\n\n";
- break;
-
- case POV20:
- f << "camera {\n";
- f << " location <" << pos << ">\n";
- f << " look_at <" << target << ">\n";
- f << "}\n\n";
- break;
-
- case VIVID:
- f << "studio {\n";
- f << " from " << pos << "\n";
- f << " at " << target << "\n";
- f << " up 0 0 1\n";
- f << " angle 60.0\n";
- f << " aspect 4/3\n";
- f << " resolution 320 200\n";
- f << " antialias none\n";
- f << "}\n\n";
- break;
- }
- }
-
-
- void set_view()
- {
- Vector sky;
-
- sky = Vector (0.0, 1.0, 0.0);
-
- // Calculate a set of base vectors for the new viewpoint
- nz = lookat - viewpoint;
- nz = nz/mag(nz);
-
- ny = sky - nz*(nz.y/(nz % nz));
- ny = ny/mag(ny);
-
- nx = 1.333 * nz * ny;
- }
-
-
- /* Convert character 'c' top upper case */
- char upcase (char c)
- {
- if (c >= 'a' && c <= 'z')
- c = c - 'a' + 'A';
-
- return c;
- }
-
-
- void plot_tri (const Vector &a, const Vector &b, const Vector &c)
- {
- #ifdef __TURBOC__
- Vector pa, pb, pc, ta, tb, tc;
- int ax, ay, bx, by, cx, cy;
-
- ta = a - viewpoint;
- tb = b - viewpoint;
- tc = c - viewpoint;
-
- pa = Vector (ta % nx, ta % ny, ta % nz);
- pb = Vector (tb % nx, tb % ny, tb % nz);
- pc = Vector (tc % nx, tc % ny, tc % nz);
-
- ax = int((0.5*pa.x/pa.z + 0.5)*getmaxx());
- bx = int((0.5*pb.x/pb.z + 0.5)*getmaxx());
- cx = int((0.5*pc.x/pc.z + 0.5)*getmaxx());
- ay = int((0.5 - pa.y/pa.z)*getmaxy());
- by = int((0.5 - pb.y/pb.z)*getmaxy());
- cy = int((0.5 - pc.y/pc.z)*getmaxy());
-
- setcolor (LIGHTBLUE);
- moveto (ax, ay);
- lineto (bx, by);
- lineto (cx, cy);
- lineto (ax, ay);
- #endif
- }
-
-
- int init_display()
- {
- #ifdef __TURBOC__
- int gdriver = VGA, gmode = VGAHI, errorcode;
-
- registerbgidriver (EGAVGA_driver);
-
- initgraph (&gdriver, &gmode, "");
- errorcode = graphresult();
- if (errorcode != grOk) {
- cerr << "ERROR:" << grapherrormsg (errorcode) << "\n\n";
- return -1;
- }
- #endif
-
- return 0;
- }
-
-
- void close_display()
- {
- #ifdef __TURBOC__
- restorecrtmode();
- #endif
- }
-
-
- void check_abort()
- {
- #ifdef __TURBOC__
- static int cnt = 0;
-
- ++cnt;
-
- if (cnt >= 10) {
- cnt = 0;
-
- if (kbhit())
- abortmsg ("Aborted!", 1);
- }
- #endif
- }
-
-
-