home *** CD-ROM | disk | FTP | other *** search
- /*
- MountainsAlgorithm.c
- A fractal mountain generating algorithm
- By Ben Haller 1/1/90
- Technical Assistance by Eli Meir
- ©1990 Ben Haller
- */
-
- #include <math.h>
- #include "Mountains.h"
-
- /* Imported declarations from MountainsToolbox.c */
- extern CursHandle watch;
- extern int wx,wy,wd;
- extern char colorF;
- extern char menuCk[5];
-
- /* Algorithmic declarations */
- #define maxIter 9L
-
- long bwPat[17][2]={ /* Black and white patterns for shading */
- {0xFFFFFFFF, 0xFFFFFFFF},
- {0xFFFBFFBF, 0xFFFBFFBF},
- {0xFFEEFFBB, 0xFFEEFFBB},
- {0xFFEEFFAA, 0xFFEEFFAA},
- {0xBBEEBBEE, 0xBBEEBBEE},
- {0xAA77AAFF, 0xAA77AAFF},
- {0xAA77AADD, 0xAA77AADD},
- {0xAA77AA55, 0xAA77AA55},
- {0xAA55AA55, 0xAA55AA55},
- {0xAA55AA11, 0xAA55AA11},
- {0xAA44AA11, 0xAA44AA11},
- {0xAA44AA00, 0xAA44AA00},
- {0x22882288, 0x22882288},
- {0x2200AA00, 0x2200AA00},
- {0x22008800, 0x22008800},
- {0x80000800, 0x80000800},
- {0x00000000, 0x00000000}
- };
-
- typedef struct { /* 3D point - double FP precision */
- double x,y,z;
- } point3;
-
- double xTh=-1.396; /* Angle of tilt (in radians) */
- double tm[3][3]; /* Transformation matrix */
- long *map; /* A pointer to the depth values of the map */
-
- /* Variables dependant on menu selections */
- long nIter; /* Number of iterations of divide-and-conquer */
- long mapS; /* Length of one side of the map */
- long mapD; /* Map dimension - equals mapS-1 */
-
- double contour; /* Power to raise heights to, to make contour */
- double roughness; /* Base to raise to get scale variance */
- long normalHeight; /* Normalized height */
- char profile; /* Profile number */
-
- /* Drawing variables - precalculated coefficients */
- double xc,sc;
-
- /* The Map #define lets us access map easily, although
- it is a variable-dimension array. */
- #define Map(x,y) map[(x)+mapS*(y)]
-
- /********************************************************************/
- /* */
- /* Calculation Routines */
- /* */
- /********************************************************************/
-
- /* Make the mountains a standard height, and simultaneously apply the contour transform */
- void NormalizeMap()
- {
- register long n,m,k;
- register long *a;
- register double i,z;
-
- a=map;
- m=0x80000000;
- for (n=mapS*mapS-1; n>=0; n--) /* Find highest point in map */
- if (*(a+n)>m)
- m=*(a+n);
- z=(double)m;
- z=pow(z,contour); /* Apply contour transform to it */
- z=(double)normalHeight/z; /* Find normalizing coefficient such that */
- /* the range will be the chosen height */
- for (n=mapS*mapS-1; n>=0; n--) { /* For each point in the map */
- k=*(a+n);
- if (k>0) { /* Special case for positive & neg. heights */
- i=(double)k;
- i=pow(i,contour)*z; /* Apply contour transform, normalize */
- *(a+n)=(long)i; /* Poke back in map */
- } else {
- i=(double)(-k);
- i=pow(i,contour)*z; /* Apply contour transform, normalize */
- *(a+n)=-((long)i); /* Poke back in map, preserving sign */
- }
- }
- }
-
- /* Returns a random long integer. This routine just generates two integers using */
- /* QuickDraw's random number generator and tacks them together. I have no idea how */
- /* good the numbers thus generated are, but I suspect not very good at all. But, */
- /* they're good enough. */
- long Rand(first, last)
- long first, last;
- {
- long r;
-
- do {
- r=(((long)Random())<<16)+Random();
- } while (r<0);
- return(r % (last - first + 1) + first);
- }
-
- /* Returns the maximum deviation a point could attain given an iteration depth. */
- /* The exact number returned depends on roughness, but this function is always */
- /* strictly decreasing monotonic for given depth ic. */
- long MaxDeviation(ic)
- long ic;
- {
- if (roughness>0)
- return ((long)(pow(roughness,(double)(ic-1L))*8.0));
- else return 100000L;
- }
-
- /* This routine deviates a point by a random amount from -m to m, m being the */
- /* maximum deviation for the given iteration. If roughness==0 (symbolic of */
- /* infinite smoothness), it is not deviated at all. */
- void DeviatePoint(long,long);
- void DeviatePoint(o,ic)
- long o,ic;
- {
- long v;
-
- if (roughness<0) return;
- v=MaxDeviation(ic);
- map[o]+=Rand(-v,v);
- }
-
- /* Passed a triangle with "side" (i.e. horizontal side) (sx1,sy1)-(sx2,sy2) */
- /* and "apex" (ax,ay). It calculates midpoints and recurses. Parameter */
- /* 'c' gives a standard iteration count */
- void IterCalc(long,long,long,long);
- void IterCalc(s1,s2,a,c)
- long s1,s2,a,c;
- {
- register long ns1,ns2,na;
-
- /* Decrement iteration count */
- c--;
-
- /* Find midpoints */
- ns1=(s1+a)>>1;
- ns2=(s2+a)>>1;
- na=(s1+s2)>>1;
-
- /* For each midpoint, if it hasn't already been set, set it to the */
- /* average of it's endpoints, and deviate by a random amount */
- if (map[ns1]==0x80000000L) {
- map[ns1]=(map[s1]+map[a])>>1;
- DeviatePoint(ns1,c);
- }
- if (map[ns2]==0x80000000L) {
- map[ns2]=(map[s2]+map[a])>>1;
- DeviatePoint(ns2,c);
- }
- if (map[na]==0x80000000L) {
- map[na]=(map[s1]+map[s2])>>1;
- DeviatePoint(na,c);
- }
-
- /* Iterate calculations on sub-triangles if we haven't yet reached */
- /* the maximum resolution of the map */
- if (ns1+1!=ns2) {
- IterCalc(s1,na,ns1,c);
- IterCalc(na,s2,ns2,c);
- IterCalc(ns1,ns2,na,c);
- IterCalc(ns1,ns2,a,c);
- }
- }
-
- /* Initialize the entire map to 0x80000000 (<not set> value) */
- void InitMap()
- {
- register long n;
- register long *a;
-
- a=map;
- for (n=mapS*mapS-1; n>=0; n--)
- *(a+n)=0x80000000L;
- }
-
- /* Initialize values from menu choices, allocate map, etc. */
- void InitMountains()
- {
- nIter=menuCk[0]+2;
- map=0L;
- mapD=1L<<nIter;
- mapS=mapD+1;
-
- contour=0.25*menuCk[1];
- if (menuCk[1]==9) contour=3.0;
- else if (menuCk[1]==10) contour=5.0;
-
- roughness=0.75+0.25*menuCk[2];
- if (menuCk[2]==7) roughness=2.75;
- else if (menuCk[2]==8) roughness=3.5;
- else if (menuCk[2]==9) roughness=5.0;
- else if (menuCk[2]==10) roughness=-1.0;
-
- normalHeight=10000L*menuCk[3];
-
- profile=menuCk[4];
-
- if (map==0L) map=NewPtr(mapS*mapS*4L);
- if (map==0L) ExitToShell(); /* Out of memory */
- InitMap();
- }
-
- /* Calculate mountain range using current parameters, profile, etc. */
- void CalcMountains()
- {
- register long q;
-
- SetCursor(*watch);
- InitMountains();
-
- /* Generate starting profile to build on */
- q=MaxDeviation(maxIter+1)/2;
- switch (profile) {
- case 1:
- Map(0,0)=q;
- Map(mapD,0)=0;
- Map(0,mapD)=0;
- Map(mapD,mapD)=-q;
- break;
- case 2:
- Map(0,0)=q;
- Map(mapD,0)=0;
- Map(0,mapD)=0;
- Map(mapD,mapD)=0;
- Map(mapD>>1,mapD>>1)=0;
- Map(mapD>>1,mapD)=0;
- Map(mapD,mapD>>1)=0;
- break;
- case 3:
- Map(0,0)=0;
- Map(mapD,0)=0;
- Map(0,mapD)=0;
- Map(mapD,mapD)=-q;
- break;
- case 4:
- Map(0,0)=0;
- Map(mapD,0)=0;
- Map(0,mapD)=0;
- Map(mapD,mapD)=0;
- Map(mapD>>1,mapD>>1)=q/2;
- Map(mapD>>1,0)=q;
- Map(0,mapD>>1)=q;
- break;
- }
-
- /* Generate each main triangle recursively */
- IterCalc(0L,mapS-1,mapS*mapS-1,maxIter+1);
- IterCalc(mapS*(mapS-1),mapS*mapS-1,0L,maxIter+1);
-
- /* Normalize to standard height, apply contour transform */
- NormalizeMap();
-
- SetCursor(&arrow);
- }
-
- /********************************************************************/
- /* */
- /* Drawing Routines */
- /* */
- /********************************************************************/
-
- /* Transform from map coordinates to screen coordinates*/
- void CalcPoint3(p)
- point3 *p;
- {
- register double xp,yp,zp;
-
- xp=(p->x*2-p->y+mapD)*xc;
- yp=(p->y*2)*xc;
- zp=p->z*0.00217;
-
- p->x=xp*sc;
- p->y=(yp*tm[1][1]+zp*tm[2][1]+230)*sc;
- p->z=(yp*tm[1][2]+zp*tm[2][2])*sc;
- }
-
- /* This routine actually draws a triangle, given by a set of three (x,y,z) triplets. */
- /* It determines the color and shading according to altitude and lighting, constructs */
- /* a QuickDraw polygon for the triangle, and draws it. */
- void _DrawTriangle(p)
- point3 *p;
- {
- double nx,ny,nz,lx,ly,lz,i;
- RGBColor c;
- PolyHandle ph;
- long color,slope;
-
- /* Figure out what base color our triangle will be : blue, green, or gray */
- if ((p[0].z==0.0) && (p[1].z==0.0) && (p[2].z==0.0))
- color=-1; /* blue */
- else {
- double az,treeline;
-
- treeline=.4*normalHeight+10000; /* Calculate treeline */
- az=(p[0].z+p[1].z+p[2].z)/3.0; /* Find avg. height */
- if (az>treeline+9000) color=150; /* gray */
- else if (az<treeline) color=0; /* green */
- else color=(az-treeline)/60; /* intermediate */
- }
-
- /* Transform into viewing space */
- CalcPoint3(p+0);
- CalcPoint3(p+1);
- CalcPoint3(p+2);
-
- /* Create a Quickdraw polygon to be the triangle in question */
- ph=OpenPoly();
- MoveTo((int)p[0].x,(int)p[0].y);
- LineTo((int)p[1].x,(int)p[1].y);
- LineTo((int)p[2].x,(int)p[2].y);
- LineTo((int)p[0].x,(int)p[0].y);
- ClosePoly();
-
- /* Calculate the normal vector to our triangle, by means of a cross product */
- {
- double v1x,v1y,v1z,v2x,v2y,v2z;
-
- v1x=p[1].x-p[0].x;
- v1y=p[1].y-p[0].y;
- v1z=p[1].z-p[0].z;
-
- v2x=p[2].x-p[0].x;
- v2y=p[2].y-p[0].y;
- v2z=p[2].z-p[0].z;
-
- nx=v1y*v2z-v1z*v2y;
- ny=v1z*v2x-v1x*v2z;
- nz=v1y*v2x-v1x*v2y;
- }
-
- /* Initialize our light source */
- lx=-1.0/sqrt(3.0);
- ly=-lx; lz=-lx;
-
- /* Figure out what color we want */
- if (color==-1) {
- c.red=0x9FFF; c.green=0x9FFF; c.blue=0xFFFF; /* Water */
- if (colorF) RGBForeColor(&c);
- else PenPat(bwPat[8]);
- } else {
- unsigned long cv[3];
-
- /* Start with green */
- cv[0]=0x00005555L; cv[1]=0x0000FFFFL; cv[2]=0x0000AAAAL;
-
- /* Blend to brown and white over treeline */
- cv[0]=((150L-color)*(cv[0]-0x00002000L))/150L+0x00002000L;
- cv[1]=(cv[1]*(150L-color))/150L;
- cv[2]=0x0000EFFFL-((150L-color)*(0x0000EFFFL-cv[2]))/150L;
-
- /* Move into an RGBColor structure */
- c.red=cv[0]; c.green=cv[1]; c.blue=cv[2];
-
- /* Scale brightness according to the incidence of light */
- i=((lx*nx+ly*ny+lz*nz)/sqrt(nx*nx+ny*ny+nz*nz))*0.5+0.5;
- c.blue*=i;
-
- if (colorF) { /* Pick our color */
- HSV2RGB(&c,&c);
- RGBForeColor(&c);
- } else { /* Pick a pattern based on brightness */
- int pat;
-
- pat=(((unsigned long)c.blue)*36L)/0x00010000L;
- pat-=10;
- if (pat<0) pat=0;
- if (color==150) {
- if (pat>16) pat=16;
- } else if (pat>15) pat=15;
- PenPat(bwPat[pat]);
- }
- }
-
- PaintPoly(ph); /* Draw our triangle */
- KillPoly(ph); /* Get rid of it */
- }
-
- /* Draw a given triangle. This routine is mainly concerned with the possibility that */
- /* a triangle could span the waterline. If this occurs, this procedure breaks it up */
- /* into three smaller triangles, each of which is either above or below water. All */
- /* actual drawing or coloration is delegated to _DrawTriangle, above. */
- void DrawTriangle(x1,y1,x2,y2,x3,y3)
- long x1,y1,x2,y2,x3,y3;
- {
- long z[3];
- point3 p[3];
-
- /* Get depths of the three points */
- z[0]=Map(x1,y1);
- z[1]=Map(x2,y2);
- z[2]=Map(x3,y3);
- if ((z[0]<=0) && (z[1]<=0) && (z[2]<=0)) { /* All underwater */
- p[0].x=x1; p[0].y=y1; p[0].z=0;
- p[1].x=x2; p[1].y=y2; p[1].z=0;
- p[2].x=x3; p[2].y=y3; p[2].z=0;
- _DrawTriangle(p);
- } else if ((z[0]<0) || (z[1]<0) || (z[2]<0)) { /* Some underwater - split at waterline */
- point3 ap,s[2],m[2];
- char w[3];
- int n;
- double f;
-
- p[0].x=x1; p[0].y=y1; p[0].z=z[0];
- p[1].x=x2; p[1].y=y2; p[1].z=z[1];
- p[2].x=x3; p[2].y=y3; p[2].z=z[2];
- for (n=0; n<3; n++)
- w[n]=(z[n]<0);
- if ((w[0]!=w[1]) && (w[0]!=w[2])) {
- ap=p[0]; s[0]=p[1]; s[1]=p[2];
- } else {
- if (w[1]!=w[0]) {
- s[1]=p[0]; ap=p[1]; s[0]=p[2];
- } else {
- s[0]=p[0]; s[1]=p[1]; ap=p[2];
- }
- }
-
- /* At this point, ap is the "odd man out" - either it is above water and the other */
- /* two are below, or it is below and the other two are above. Which corner s[0] */
- /* is and which s[1] is *is* important - if we get the wrong order, the normal */
- /* vector used to find the shading coefficient is the wrong sign. This is true */
- /* whenever we are manipulating corners - the ordering is always important. */
-
- /* Find the "midpoints" between ap and s[0]&s[1] - this is where we split our big */
- /* triangle into smaller triangles. Actually it is not a normal midpoint, but a */
- /* weighted midpoint, such that the z component is 0 - waterline. */
- for (n=0; n<2; n++) {
- f=-((double)ap.z)/(double)(s[n].z-ap.z);
-
- m[n].x=ap.x-(ap.x-s[n].x)*f;
- m[n].y=ap.y-(ap.y-s[n].y)*f;
- m[n].z=0;
- }
-
- /* Set whichever triangles are below water to 0 altitude */
- if (ap.z<0) ap.z=0;
- else {
- s[0].z=0; s[1].z=0;
- }
-
- /* Draw our three triangles */
- p[0]=ap; p[1]=m[0]; p[2]=m[1];
- _DrawTriangle(p);
- p[0]=m[0]; p[1]=s[0]; p[2]=s[1];
- _DrawTriangle(p);
- p[0]=m[0]; p[1]=s[1]; p[2]=m[1];
- _DrawTriangle(p);
- } else { /* All above water */
- p[0].x=x1; p[0].y=y1; p[0].z=z[0];
- p[1].x=x2; p[1].y=y2; p[1].z=z[1];
- p[2].x=x3; p[2].y=y3; p[2].z=z[2];
- _DrawTriangle(p);
- }
- }
-
- /* Draw the entire mountain range. Non-recursive. */
- void DrawMountains()
- {
- long x,y;
-
- if (map==0L) return;
- SetCursor(*watch);
-
- /* Set drawing coefficients (used in CalcPoint3) */
- xc=0.4073*(double)(1<<(maxIter-nIter));
- sc=((double)wd)/630.0;
-
- /* Make transformation matrix */
- tm[0][0]=1;
- tm[1][0]=0;
- tm[2][0]=0;
- tm[0][1]=0;
- tm[1][1]=cos(xTh);
- tm[2][1]=sin(xTh);
- tm[0][2]=0;
- tm[1][2]=-sin(xTh);
- tm[2][2]=cos(xTh);
-
- /* Go back to front, left to right, and draw each triangle */
- for (y=0; y<mapS-1; y++) {
- for (x=0; x<mapS-1; x++) {
- DrawTriangle(x,y,x,y+1,x+1,y+1);
- DrawTriangle(x,y,x+1,y+1,x+1,y);
- }
- }
-
- SetCursor(&arrow);
- }