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