FRACTALS\FLY.C  ·  C  ·  14.2 KB  ·  1991-10-01  ·  from WhatPC_Issue-27_Oct-1991-FluxEngine-360KB
/*

	TurboC code for realtime fractal fly-through's, given suitably
	quick hardware! Based on fractal article by Richard Bartual in
	Program Now Oct 88, this implementation by Neil Newell Dec 88.

	Compile using a 'large data' model if you want to use size 5.

*/

#include	<alloc.h>
#include	<dos.h>
#include	<conio.h>
#include	<stdio.h>
#include	<stdlib.h>

/* Define video mode - this can be CGA, EGA or VGA. Note that if CGA or EGA
   modes are selected the line drawing and graphics control code routines are
   not compiled to allow linking with assembler versions for speed */

#define	EGA	1

/* Factors and colours for each possible display type */

#if defined(CGA)
#define	WIDTH	320
#define	DEPTH	200
#define	GFXMODE	4
#define	XFAC	128
#define	YFAC	128
#define	GREEN	2
#define	BROWN	2
#define	GREY	3
#define	BLUE	1
#define	EMRALD	2
#define	YELLOW	3
#define	WHITE	3
#endif

#if defined(EGA)
#define	WIDTH	640
#define	DEPTH	350
#define	GFXMODE	16
#define	XFAC	256
#define	YFAC	256
#define	GREEN	2
#define	BROWN	6
#define	GREY	8
#define	BLUE	9
#define	EMRALD	10
#define	YELLOW	14
#define	WHITE	15
#endif

#if defined(VGA)
#define	WIDTH	640
#define	DEPTH	480
#define	GFXMODE	18
#define	XFAC	256
#define	YFAC	256
#define	GREEN	2
#define	BROWN	6
#define	GREY	8
#define	BLUE	9
#define	EMRALD	10
#define	YELLOW	14
#define	WHITE	15
#endif

/* Screen and default viewpoint values */

#define	I_XPOS	SIZE/2
#define	I_YPOS	0
#define	I_ELEV	SIZE/4

#define	x_min	0
#define	x_max	WIDTH-1
#define	y_min	0
#define	y_max	DEPTH-1

#define	X_OFFSET WIDTH/2
#define	Y_OFFSET DEPTH/2

/* Other stuff */

#define	TXTMODE	3		/* Text mode */

#define	MAXSIZE	128		/* Max landscape size */

#define	COLFAC	MAXSIZE/SIZE /* Colour factor - colour algorithm designed for
				use with SIZE = MAXSIZE */
/* Key codes */

#define	CUP	72	/* Movement keys */
#define	CUD	80
#define	CUL	75
#define	CUR	77
#define	IN1	43
#define	OUT1	45
#define	IN2	60
#define	OUT2	62
#define	CR	13
#define	CTRLC	3
#define	ESCAPE	27
#define	UPPERQ	81
#define	LOWERQ	113

char	square[MAXSIZE+1][MAXSIZE+1];	/* Fractal array */
int	elev,xpos,ypos,range;
int	plotcol,oldmode,number;
int	SIZE;
int	*sx,*sy;
char	*sc;

/* Main program */

main()
{
int	i,j,driver,mode,ch;


printf	("\nFractal fly-through demo program.");
printf	("\n");
printf	("\nUse cursor keys to move viewpoint up, down, left, right");
printf	("\nUse + and - to fly in and out");
printf	("\nUse Q to quit, Enter for new scene");
printf	("\n");
printf	("\nHave fun!\n");

/* Get initalise scene number */

printf	("\nInitial scene number? (0..65535) ");
scanf	("%d",&number);

/* Get landscape size required (read as power of 2) */

SIZE=0;
while (SIZE>5 || SIZE < 1) {
	printf	("\nSize of landscape? (1..5) ");
	scanf	("%d",&SIZE);
	printf	("\n");
	}

	SIZE=1<<(SIZE+2);

/* Get and save current video mode */

oldmode = getmode();

/* Allocate room for screen array */

	sx=(int *)malloc((SIZE+1)*(SIZE+1)*sizeof(int));
	sy=(int *)malloc((SIZE+1)*(SIZE+1)*sizeof(int));
	sc=malloc((SIZE+1)*(SIZE+1));

	if (sx==NULL || sy == NULL || sc == NULL) {
		printf ("\n\nNot enough memory!\n\n");
		return;
		}

/* Initialise landscape array */

start_fractal(number);

/* Fly through it */

gfxon();
change_views();
gfxoff();		/* Restore initial mode */

/* Release screen array */

free(sx);
free(sy);
free(sc);

}

/* Routine to generate the fractal - the argument is the fractal number
   and is used to initialise the random number generator */

start_fractal(fracno)
int	fracno;
{
int	x,y,distance;

srand(fracno);

for(y=0;y<=SIZE;y++)
	for (x=0;x<=SIZE;x++)
		square[x][y] = 0;

distance = SIZE;

square[0][0] = random(distance) - distance/2;
square[0][SIZE] =random(distance) - distance/2;
square[SIZE][0] = random(distance) - distance/2;
square[SIZE][SIZE] =random(distance) - distance/2;

distance = SIZE/2;
plot_midpoint(0,0,distance);
plot_midpoint(0,distance,distance);
plot_midpoint(distance,0,distance);
plot_midpoint(distance,distance,distance);


/* Unless roll_fractal generates new landscape, we are going to keep flying
   over the same one and therefore we'd better make it symmetrical somehow
   otherwise some interesting discontinuities will appear */

for(y=0;y<SIZE/2;y++)
	for (x=0;x<=SIZE;x++)
		square[x][y] = square[x][SIZE-y];

for (x=0;x<=SIZE;x++)
	square[x][0] = square[x][0]+random(3)-1;

/* The following code makes all negative entries in the landscape zero, 
   which is considered 'sea level'. Remove if not wanted. */

for (x=0;x<=SIZE;x++)
	for(y=0;y<=SIZE;y++)
		if(square[x][y] < 0)
			square[x][y] = 0;

}

/* Roll the fractal a row for the flythrough. If a new row of landscape data
   was generated here continuously variable landscapes would result. As it is,
   the effect that is being generated here can be acheived equally well by
   cycling the row at which the drawing is started. */

roll_fractal()
{
/*
int	x,y,to;

for(y=SIZE;y>=0;y--) {
	if (y==SIZE)
		to = 0;
	else
		to = y+1;
	for (x=0;x<=SIZE;x++)
		square[x][to] = square[x][y];
	}
*/
}

/* Fractal midpoint generation */

plot_midpoint(x,y,half_dist)
int	x,y,half_dist;
{
int	distance;

if (half_dist > 1) {
	distance = half_dist;
	half_dist = half_dist/2;

	if (square[x+half_dist][y]==0)
		square[x+half_dist][y] = (square [x][y] +
		 square[x+distance][y])/2 + random(half_dist) -
		  half_dist/2;

	if (square[x][y+half_dist]==0)
		square[x][y+half_dist] = (square [x][y] +
		 square[x][y+distance])/2 + random(half_dist) -
		  half_dist/2;

	if (square[x+half_dist][y+distance]==0)
		square[x+half_dist][y+distance] = 
		 (square [x][y+distance] +
		   square[x+distance][y+distance])/2 +
		    random(half_dist) - half_dist/2;

	if (square[x+distance][y+half_dist]==0)
		square[x+distance][y+half_dist] =
		 (square [x+distance][y] +
		  square[x+distance][y+distance])/2 +
		   random(half_dist) - half_dist/2;

	square[x+distance][y+half_dist] = 
	 (square[x+distance][y] + square[x+distance][y+distance])/2 +
	  random(half_dist) - half_dist/2;

	square[x+half_dist][y+half_dist] = (square[x][y+half_dist] +
	 square[x+distance][y+half_dist]+square[x+half_dist][y] +
	  square[x+half_dist][y+distance])/4 + random(half_dist) -
	   half_dist;

	plot_midpoint(x,y,half_dist);
	plot_midpoint(x+half_dist,y,half_dist);
	plot_midpoint(x,y+half_dist,half_dist);
	plot_midpoint(x+half_dist,y+half_dist,half_dist);
	}
}

/* Routine to fly us through the fractal and allow us to adjust our view */

change_views()
{
int	ch = CR;

while (ch != LOWERQ && ch != UPPERQ && ch != ESCAPE && ch != CTRLC) {	/* While not quit */

	if (ch==CR) {	/* If carriage return, generate new fractal */
		txton();
		printf ("Generating fractal....\n");
		start_fractal(number++);
		xpos = I_XPOS;
		ypos = I_YPOS;
		elev = I_ELEV;
		range = SIZE*2;
		gfxon();
		}

/* Cursor key handling, moves viewpoint about */

	if (ch == CUP &&(elev <=75))
		elev++;

	if (ch == CUD &&(elev > -40))
		elev--;

	if (ch == CUL && (xpos > 1))
		xpos--;

	if (ch == CUR && (xpos < SIZE-1))
		xpos++;

/* Range keys */

	if ((ch == IN1 || ch == IN2) && (ypos < SIZE-4))
		ypos++;

	if ((ch == OUT1 || ch == OUT2) && (ypos > 4))
		ypos--;

/* Roll and display fractal unless a key's been hit */

	if (kbhit() == 0) {
		roll_fractal();		/* See comments in routine */
		three_d();		/* Project landscape */
		if (--range<0)		/* Adjust range to make us "fly" */
			range = SIZE;
		ch = 0;
		}
	else
		ch = getch();

	}
}

/* Routine to map the fractal onto the screen display */

three_d()
{
int	*screenx,*screeny;	/* Declare first for register allocation */
int	x,y,ix,iy,z,nrows;
char	*scrcol;

screenx=sx;	/* Initialise screen array pointers */
screeny=sy;
scrcol=sc;

nrows = 0;	/* Number of rows projected */

/* Convert the fractal data into screen co-ordinates
  (Note: Could be speeded up as screenx array only needs calculating once) */

for (y=ypos+1;y<=SIZE;y++) {
	iy = (y+range)%(SIZE+1);
	if (elev*YFAC/(SIZE+1-y) > Y_OFFSET+Y_OFFSET/10)
		break;
	nrows++;
	z = SIZE+2-y;
	for (x=0;x<=SIZE;x++) {
		*screenx++ = X_OFFSET + (x-xpos)*XFAC /z;
		*screeny++ = Y_OFFSET - (square[x][iy] - elev) * YFAC /z;
		*scrcol++ = plane_colour(x,iy);
		}
	}

screenx=sx;	/* Point to start to screen data */
screeny=sy;
scrcol=sc;

/* Draw lines connectings points in screen arrays - note that normally only
   2 sides of a 4 sided polygon need be drawn, except when plotting along
   the edge of the array */

clr_draw_page();	/* See comments in routine */

for (y=ypos+1;y<=SIZE;y++) {
	if (--nrows < 1)
		break;
	for (x=0;x<SIZE;x++) {
		ix = *(screenx+SIZE+1+1);
		iy = *(screeny+SIZE+1+1);
		plotcol = *scrcol;		/* Get line colour */
		xline(ix,iy,*(screenx+1),*(screeny+1),plotcol); 
		xline(ix,iy,*(screenx+SIZE+1),*(screeny+SIZE+1),plotcol);
		if (x==0 || y == ypos+1) {
			xline(*screenx,*screeny,*(screenx+1),*(screeny+1),plotcol); 
			xline(*screenx,*screeny,*(screenx+SIZE+1),*(screeny+SIZE+1),plotcol);
			}
		*screenx++;
		*screeny++;
		*scrcol++;
		}
	*screenx++;
	*screeny++;
	*scrcol++;
	}

/* Drawing complete */

	update();	/* Make draw page the active display page, clear
			   new draw page */
}

/* Decide on a colour for a given polygon. The rules used here are if it's
   at sea level, it's blue, if it's just above the sea it's yellow (beach)
   turning light green, dark green and then white with increasing altitude.
   Steep slopes are brown below a certain height (landslip) and grey above
   (bare rock). Also, levelish areas near the snow line are white as snow
   would accumlate on them */

plane_colour(x,y)
int	x,y;
{
int	max_height,min_height,dif,col;

/* Find highest point in polygon */

	max_height = square[x][y];

	if (square[x+1][y] > max_height)
		max_height = square[x+1][y];

	if (square[x][y+1] > max_height)
		max_height = square[x][y+1];

	if (square[x+1][y+1] > max_height)
		max_height = square[x+1][y+1];

/* Find minimum height in polygon */

	min_height = square[x][y];

	if (square[x+1][y] < min_height)
		min_height = square[x+1][y];

	if (square[x][y+1] < min_height)
		min_height = square[x][y+1];

	if (square[x+1][y+1] < min_height)
		min_height = square[x+1][y+1];

/* Get slope */

	dif = max_height-min_height;

/* Adjust colours for size scaling */

	min_height = min_height*COLFAC;
	max_height = max_height*COLFAC;

/* Assign colours */

	col = YELLOW;		/* Beach */

	if (max_height > 3) 	/* If above the beach */
		col = EMRALD;	/* Light green */

	if (max_height > 6 && dif > 2) 	/* If above the beach and steep */
		col = GREEN;	/* Dark green */

	if (max_height <=0)	/* On/Under Sea */
		col = BLUE;

	if (dif > 4) {
		if (max_height > 18)
			col = GREY;	/* Grey cliffs */
		else
			col = BROWN;	/* Brown cliffs */
		}

	if (max_height > 15 && (dif<3))	/* Snow on levelish surfaces */
			col = WHITE;

	if (max_height > 35 )	/* Snow */
			col = WHITE;

	if (col==WHITE && min_height == 0)	/* For small 'scapes */
			col = GREEN;

/* Return colour */

	return(col);
}

/* Random number function used to return a value in the range 0 to number-1 */

random(number)
int	number;
{
	return rand()%number;
}

/* Select text mode */

txton()
{
	_AH = 0;
	_AL = TXTMODE;
	geninterrupt(0x10);
}

/* Clear the draw page - note that update() can do this instead in systems
   with more than one page of video memory */

clr_draw_page()
{
#if defined(VGA)
	gfxon();	/* Crass screen clear */
#endif
}

/* Select grafix mode */

gfxon()
{
	_AH = 0;
	_AL = GFXMODE;
	geninterrupt(0x10);	/* Enter grafix mode */

#if defined(CGA)
	_AH = 0xb;
	_BH = 0;
	_BL = 0;		/* Background colour */

	geninterrupt(0x10);
	_AH = 0xb;
	_BH = 1;
	_BL = 0;		/* 0 = Green/Red/Brown pallette */
	geninterrupt(0x10);
#endif

}

/* Restore original mode */

gfxoff()
{
	_AH=0;
	_AL=oldmode;
	geninterrupt(0x10);
}

/* Get current mode */

getmode()
{
int	i;

	_AH = 15;
	geninterrupt(0x10);
	i = _AL;
	return i;
}

/************************ General grafix stuff **************************/

/* Note this stuff is only defined if EGA & CGA are not defined, this is
   because program is normally linked with machine code drivers if these
   video modes are wanted */

#if !defined(EGA) && !defined(CGA)
xline(xa,ya,xb,yb,colour)
int	xa,ya,xb,yb,colour;
{
	plotcol = colour;
	line(xa,ya,xb,yb);
}

/* Line drawing - note that very short lines are plotted as 2 points */

line(xa,ya,xb,yb)
int	xa,ya,xb,yb;
{
register int i,err;
int	xp,yp,xe,ye,deltax,deltay,dir;

	deltax = abs(xb-xa);
	deltay = abs(yb-ya);

	if (deltax <=1 && deltay <=1) {
		plot (xa,ya,plotcol);
		plot (xb,yb,plotcol);
		return;
		}

	if (deltax >= deltay) {
		if (xa>xb) {
			xp = xb;
			yp = yb;
			ye = ya;
			}
		else {
			xp = xa;
			yp = ya;
			ye = yb;
			}
		dir = 1;
		if (ye-yp < 0 )
			dir = -1;
		err = 0;
		for (i=0;i<deltax;i++) {
			plot(xp,yp,plotcol);
			xp++;
			err = err+deltay;
			if (err >= deltax) {
				err = err-deltax;
				yp = yp+dir;
				}
			}
		}
/* Handling when deltay is greater than deltax */
	else {
		if (ya>yb) {
			yp = yb;
			xp = xb;
			xe = xa;
			}
		else {
			yp = ya;
			xp = xa;
			xe = xb;
			}
		dir = 1;
		if (xp-xe > 0)
			dir = -1;
		err = 0;
		for (i=0;i<deltay;i++) {
			plot(xp,yp,plotcol);
			yp++;
			err = err+deltax;
			if (err >= deltay) {
				err = err-deltay;
				xp = xp+dir;
				}
			}
		}
}

/* Plot a point on the grafix display. If using a display system that allows
   multiple pages, it is preferable to plot into a background page that is
   then switched into display by update() */

plot(x,y,col)
int	x,y,col;
{
	if (x < x_min || y < y_min || x > x_max || y > y_max)
			return;

	_AL = col;
	_AH = 0xC;
	_CX = x;
	_DX = y;	
	_BH = 0;
	geninterrupt(0x10);
	return;
}

/* If using a grafix system that allows plotting in a background page that
   can then be switched into active use, use this routine to perform the
   page switch */

update()
{
}
#endif