[comp.sources.atari.st] v01i078: raymovi2 -- Raytrace movie generator part01/02

koreth@ssyx.ucsc.edu.ucsc.edu (Steven Grimm) (12/20/88)

Submitted-by: aking@bbn.com (Allen King)
Posting-number: Volume 1, Issue 78
Archive-name: raymovi2/part01

			RAYMOVI.PRG

   RAYMOVI.PRG renders 3-dimensional scenes in a world of reflective and 
refractive spheres, using ray tracing techniques. Animated sequences of 
frames can be generated, showing reflective balls linked and bouncing in 
a gravitational field. They resemble a string of bouncing pearls.

   RAYMOVI.PRG runs only on a color ST in low resolution. 1 Meg is required 
to generate images with resolutions greater than one ray per pixel or for 
replay of movies from RAM. Operation with only 512K is possible, but will 
produce slightly aliased pictures (one ray per pixel) which can only be 
displayed statically, slowly from floppy, or from hard disk (if you have 
a disk blaster program).

	    --------------------------------------------------

   The files in RAYMOV2S.SHAR, the source sharfile, are:

C Sources:  	BLASTRAM.C, MAIN.C, WNDOWS.C, FILE.C, MOVIE.C, SHADE.C, 
		FIND.C, SUPPORT.C
Include files:	RTD.H, STYLE.H, COLORS.H, EXTERN.H
Pgm Generation: MAKEFILE, BLASTRAM.LNK, RAYMOVI.LNK
RAYMOVI.DOK	documentation
PEARLS		the numeric description of an interesting scene

   RAYMOVI was last compiled with ALCYON. The MAKEFILE and *.LNK are 
(therefore) pretty crufty. Use them accordingly.

   RAYMOVI requires an 8K stack. Modify your copy of gemstart.s (or
whatever defines your stack size) appropriately.


	Allen King	aking  pebbles.bbn.com  ucbvax
	30 Gibson St	1-617-449-3359 evenings	
	Needham Ma.	
	02192

#------------------------------ cut here -----------------------------
#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
# This is part 1 of a multipart archive                                    
# do not concatenate these parts, unpack them in order with /bin/sh        
#
#	Run the following text with /bin/sh to create:
#	  BLASTRAM.C
#	  BLASTRAM.LNK
#	  COLORS.H
#	  EXTERN.H
#	  FILE.C
#	  FIND.C
#	  MAIN.C
#	  MAKEFILE
#	  MOVIE.C
#	  PEARLS
#	  RAYMOVI.DOK
#	  RAYMOVI.LNK
#	  README
#	  RTD.H
#	  SHADE.C
#	  STYLE.H
#	  SUPPORT.C
#	  WNDOWS.C
#
if test -r s2_seq_.tmp
then echo "Must unpack archives in sequence!"
     next=`cat s2_seq_.tmp`; echo "Please unpack part $next next"
     exit 1; fi
sed 's/^X//' << 'SHAR_EOF' > BLASTRAM.C &&
X#include <osbind.h>
X#include <stdio.h>
X
Xint XMIN, YMIN, XMAX, YMAX;
X
XwOnewSize(x,y, w,h)
Xint x,y, w,h;
X{	XMIN = x;	YMIN = y;
X	XMAX = x+w+2;	YMAX = y+h+2;
X}
X
X#define colors(r,g,b) 1000*(2*(r)+1)/16, 1000*(2*(g)+1)/16, 1000*(2*(b)+1)/16
Xint oldClut[16][3], newClut[16][3] = {
Xcolors(7,7,7),	/* all white-ish */
Xcolors(0,0,0),	/* black */
Xcolors(0,0,1),	/* somewhat lighter */
Xcolors(1,1,1),
Xcolors(1,1,2),
Xcolors(2,2,2),
Xcolors(2,2,3),
Xcolors(3,3,3),
Xcolors(3,3,4),
Xcolors(4,4,4),
Xcolors(4,4,5),
Xcolors(5,5,5),
Xcolors(5,5,6),
Xcolors(6,6,6),
Xcolors(6,6,7),
Xcolors(6,7,7)};	/* almost white */
X
Xint wHandle;
XFILE *fp, *fopen();
Xchar outname[] = "blastram";
Xlong orig_base;
Xfloat speed = 12000.0;
Xlong Ispeed = 12000;
X
X#define bytesPscreen 32000
X
Xmain (argc, argv) 
Xint argc;
Xchar *argv[];
X{	long mem_avail, l;
X	char *mem_for_screen;
X	char (*Pscreen)[bytesPscreen];
X	int i, j, Nscreens, screens_read, delay;
X	char fname[20];
X
X	if (argc != 2)
X		panic("usage: blastram <fname_prefix> (sans the XXX.PI1)\n");
X	wHandle = w_open(0, "pearle", 0);	/* open window (typeless, menueless) */
X	w_lClut(newClut, oldClut);
X	v_hide_c(wHandle);
X	orig_base = Physbase();
X
X	mem_avail = Malloc((long)-1);
X	mem_for_screen = Malloc (mem_avail);
X	printf("\033E%ld bytes memory available.\n", mem_avail);
X
X	Nscreens = (mem_avail - 0x100 - ((long)mem_for_screen & 0xff)) 
X							/ bytesPscreen;
X	Pscreen = (char *) (((long)mem_for_screen | 0xff) + 1);
X
X	printf("%d screens Max.\n\n", Nscreens);
X
X	for (i=1; i<999 & Nscreens > 0;)
X	{	sprintf(fname,"%s%03d.pi1", argv[1], i);
X		printf("file %s -- ", fname);
X		if (f_read(fname, Pscreen+screens_read) >= 0)
X		{	screens_read++;
X			--Nscreens;
X			printf("to screen %d.\n", screens_read);
X			i++;
X		}
X		else
X		{	printf("not found.\n");
X			if (screens_read)		
X			{	char key;
Xreask:				printf("\nType 'c', 'd' or 'e':\n");
X				printf("c: CONTINUE with another diskette\n");
X				printf("d: DISPLAY what's been loaded\n");
X				printf("e: EXIT\n");
X				key = Cconin();
X				printf("\n");
X				Mediach('a'-'a'); 
X				if (key == 'c')
X					continue;
X				else if (key == 'd')
X					break;
X				else if (key == 'e')
X					exit(0);
X				else
X				{	printf("\nAnswer 'c', 'd', or 'e'\n\n");
X					goto reask;
X	}	}	}	}
X	for (i=0; 1; i = (i + kbd() + screens_read)%screens_read)
X	{	Setscreen((long)-1, Pscreen + i, -1);
X		for (l=0; l < Ispeed; l++)	
X			delay++;
X}	}
X
Xint freeze = 0;
X
Xkbd()
X{	char key, fname[20];
X	int found_first = 0, fr1;
X
X	if (Cconis() == 0 && freeze==0)
X		return(1);
X
X	key = Cconin();
X	while (1)
X	{	switch(key)
X		{   case ('?'):	/* help */
X			Setscreen((long)-1, orig_base, -1);
X			printf("\033E\n\n       BLASTRAM.C\n\
X			\n '-' back up one frame\
X			\n '+' forward one frame\
X			\n ' ' freeze on frame\
X			\n 'f' faster\
X			\n 's' slower\
X			\n <all else> play forward\
X			\n use reset to exit\
X			\n");
X			key = Cconin();
X			printf("\033E");
X			break;
X		    case ('-'):				/* back up one */
X			freeze = 1;
X			return (-1);
X		    case ('+'): case ('='):		/* forward one */
X			freeze = 1;
X			return (1);
X		    case (' '):				/* freeze on */
X			freeze = 1;
X			return (0);
X		    case ('f'):				/* faster */
X			speed /= 1.2;
X			Ispeed = speed;
X			return (1);
X		    case ('s'):				/* slower */
X			speed *= 1.2;
X			Ispeed = speed;
X			return (1);
X/*		    case ('e'):
X			Setscreen((long)-1, orig_base, -1);
X			w_lClut(oldClut, (long)0);
X			w_close();
X			exit(0);/* the above leaves desktop in a wierd state */
X		    default:
X			freeze = 0;
X			return (1);
X}	}	}
X	
Xpanic(s,l1,l2)
Xchar *s;
Xlong l1,l2;
X{	printf("\n***** FATAL ERROR *****\n");
X	printf(s,l1,l2);
X	printf("\nPress any key to continue\n");
X	Cconin();
X	exit();
X	atoi();
X}
SHAR_EOF
chmod 0600 BLASTRAM.C || echo "restore of BLASTRAM.C fails"
sed 's/^X//' << 'SHAR_EOF' > BLASTRAM.LNK &&
X\include\gemstart.o blastram.o file.o wndows.o
X\include\aesbind \include\vdibind \include\gemlib \include\osbind.o 
X\include\libf
SHAR_EOF
chmod 0600 BLASTRAM.LNK || echo "restore of BLASTRAM.LNK fails"
sed 's/^X//' << 'SHAR_EOF' > COLORS.H &&
X/* colors.h	defines palletes 
X
XOBJECTS:	|	cr	cg	cb
X----------------+-------------------------------
Xballs:		|
X   white	|	1	1	1
X   red		|	1	0	0
X		|
Xfloor:		|
X   black cracks	|	0	0	0
X   white tiles 	|	1	1	1
X   blue tiles	|	0	0	1
X
X
XMODE (#colors)	      generated intensities	|	color number output
Xind:			cr	cg	cb	|	white	red	blue
X------------------------------------------------+------------------------------
X1			-ign-	-ign-	x	|	1-15,0	-
X2			x	-ign-	x	|	1-6,0	8-15
X3			x	x	x	|	1-5,0	1,6-10	1,11-15
X
X/**/
X			/* ind = 1: 1 color */
Xcolors(7,7,7)	/* all white-ish */
Xcolors(0,0,0)	/* black */
Xcolors(0,0,1)	/* somewhat lighter */
Xcolors(1,1,1)
Xcolors(1,1,2)
Xcolors(2,2,2)		/* note: since there are only 8 grey scales in the */
Xcolors(2,2,3)		/*       hardware (per color) and we generate 16   */
Xcolors(3,3,3)		/*	 some hue variation will be produced as	   */
Xcolors(3,3,4)		/*	 the intensity varies			   */
Xcolors(4,4,4)
Xcolors(4,4,5)
Xcolors(5,5,5)
Xcolors(5,5,6)
Xcolors(6,6,6)
Xcolors(6,6,7)
Xcolors(6,7,7)	/* almost white */
X
X			/* ind = 2: 2 color */
Xcolors(7,7,7)	/* white */
Xcolors(0,0,0)
Xcolors(1,1,1)
Xcolors(2,2,2)
Xcolors(3,3,3)
Xcolors(4,4,4)
Xcolors(5,5,5)
Xcolors(6,6,6)
X
Xcolors(0,0,0)	/* red */
Xcolors(1,0,0)
Xcolors(2,0,0)
Xcolors(3,0,0)
Xcolors(4,0,0)
Xcolors(5,0,0)
Xcolors(6,0,0)
Xcolors(7,0,0)
X
X			/* ind = 3: 3 color */
Xcolors(7,7,7)	/* white */
Xcolors(0,0,0)	
Xcolors(2,2,2)
Xcolors(4,4,4)
Xcolors(5,5,5)
Xcolors(6,6,6)
X
Xcolors(3,0,0)	/* red */
Xcolors(4,0,0)
Xcolors(5,0,0)
Xcolors(6,0,0)
Xcolors(7,0,0)
X
Xcolors(0,0,3)	/* blue */
Xcolors(0,0,4)
Xcolors(0,0,5)
Xcolors(0,0,6)
Xcolors(0,0,7)
SHAR_EOF
chmod 0600 COLORS.H || echo "restore of COLORS.H fails"
sed 's/^X//' << 'SHAR_EOF' > EXTERN.H &&
Xextern struct ball bl[];
Xextern struct sphere ls;
Xextern int level,nob;
X
X
SHAR_EOF
chmod 0600 EXTERN.H || echo "restore of EXTERN.H fails"
sed 's/^X//' << 'SHAR_EOF' > FILE.C &&
X#include "\include\gemdefs.h"
X#include "\include\osbind.h"
X#include "rtd.h"
Xint debug;
X
X#define GRMODE   0      	/* graphics mode 0 (320x200) */
X#define FSIZE  32034    	/* size of DEGAS save-file */
Xint	vs_handle;		/* virtual screen (workstation) handle */
Xint *clut_fetch();
Xint read_pal[16];
X
X/* the first FSIZE bytes of the file is DEGAS */
X
X/* save screen data to disk in DEGAS-like format */
X
Xf_write(fn)
Xchar *fn;
X{
X   char *scrnp;
X   int fd, n;
X   short gmode[1];
X
X   if (fn == 0)
X	return (-1);
X   scrnp = (char *)Physbase() ;  /* get pointer to display frame */
X   *gmode = GRMODE ;
X
X   if ((fd = creatb(fn, 0755)) < 0) 
X	return(-1);
X
X   n = write(fd, gmode, 2);		/* write graphics mode */
X   n += write(fd, clut_fetch() /*palet(ind)*/, 32);	/* write palette */
X   n += write(fd, scrnp, 32000);	/* write screen data */
X   close(fd);
X
X   if (n != FSIZE)
X   {  unlink(fn);
X      panic("Not enough space on disk to write file\n(need 32K)");
X   }
X   return(0) ;
X}
X
X/* read file (DEGAS format) */
Xint f_read(fn, buf)
Xchar *fn, *buf;
X{
X   char *scrnp;
X   int fd, n;
X   short gmode[1];
X
X   if (fn == 0)
X	return (-1);
X   if ((fd = openb(fn, 0)) < 0) 
X	return(-1);
X
X   n = read(fd, gmode, 2);		/* read graphics mode */
X   n += read(fd, &read_pal, 32);	/* read palette */
X   n += read(fd, (buf? buf: (char *)Physbase()), 32000);
X   close(fd);				/* read screen data */
X   
X   if (n != FSIZE)
X	return(-2);			/* file read error: file too short */
X   clut_put(&read_pal);
X
X
X   return(0);
X}
X
Xint scramble[16] = {0, 2, 3, 6, 4, 7, 5, 8, 9, 10, 11, 14, 12, 15, 13, 1};
X
Xint *clut_fetch()
X{	int *p = read_pal, i;
X	int rgb[3], red, green, blue;
X
X	for (i=0; i<16; i++)
X	{	vq_color(vs_handle, scramble[i], 0, rgb);
X		red   = rgb[0] / 125;	
X		green = rgb[1] / 125;
X		blue  = rgb[2] / 125;
X		if (debug)
X			printf("cF(%d>%d): %d %d %d ->%d %d %d\n",i,scramble[i],
X				rgb[0], rgb[1], rgb[2], red, green, blue);
X		*p++ = (red*0x100 + green*0x10 + blue);
X	}
X	return(read_pal);
X}
X
X#define clr(r) 1000*(2*(r)+1)/16;
X
Xclut_put(p)
Xint *p;
X{	int i;
X	int rgb[3], red, green, blue;
X	for (i=0; i<16; i++, p++)
X	{	rgb[0] = clr( *p >>8 & 0xf);	/* red */
X		rgb[1] = clr( *p >>4 & 0xf);	/* green */
X		rgb[2] = clr( *p & 0xf);	/* blue */
X		vs_color(vs_handle, scramble[i], rgb);
X}	}
SHAR_EOF
chmod 0600 FILE.C || echo "restore of FILE.C fails"
sed 's/^X//' << 'SHAR_EOF' > FIND.C &&
X#include <math.h>
X#include "rtd.h"
X#include "extern.h"
X
X/* find where a ray leaves a sphere */
X
Xdouble  findo (m, s)
X    struct mat *m;
X    struct sphere  *s;
X{
X    struct vector   foops;
X    double  t;
X
X    /* foops is the rotated vector for the center of the sphere
X       with respect to the beginning of the ray */
X
X    mt_vec (&foops, m, &(s->cent));
X
X    /* find out if the center of the sphere is within range.
X        (it should be for this...)*/
X
X    t = s->rad * s->rad - foops.y * foops.y - foops.z * foops.z;
X
X    /*return distance from original entry. we can find the point 
X       when we get out*/
X
X    if (t > 0)
X	t = foops.x + sqrt (t);
X    else
X	t = 0;
X
X    return (t);
X}
X
X
X/* find where a ray next hits (enters or exits) a sphere */
X
Xdouble  findx (m, s)
X    struct mat *m;
X    struct sphere  *s;
X{
X    struct vector   foops;
X    double  t;
X
X    /* foops is the rotated vector for the center of the sphere
X       with respect to the beginning of the ray */
X    mt_vec (&foops, m, &s->cent);
X    t = s->rad*s->rad - foops.y*foops.y - foops.z*foops.z;
X    if (t < 0)
X	t = HUGE;		   /* doesn't touch, return flag */
X    else
X    {	t = sqrt(t);
X	if (foops.x > t + EPSILON)
X		t = foops.x - t;   /* return distance to entering */
X	else if (foops.x > - t + EPSILON)
X		t = -foops.x - t;  /* return minus distance to exiting */
X	else
X		t = HUGE;
X    }
X    return (t);
X}
X
X/* see above. the only difference is that 
Xthe value returned is foops.x - sqrt(t) */
X
Xdouble  find (m, s)
X    struct mat *m;
X    struct sphere  *s;
X{
X    struct vector   foops;
X    double  t;
X
X    mt_vec (&foops, m, &(s->cent));
X
X    t = s->rad * s->rad - foops.y * foops.y - foops.z * foops.z;
X
X    if (t > 0)
X	t = foops.x - sqrt (t);
X    else
X	t = 0;
X
X    return (t);
X}
X
X
X/* returns value telling how much a sphere
Xis occluding the light source */
X
Xdouble  finds (m, s)
X    struct mat *m;
X    struct sphere  *s;
X{
X    struct vector   foops;
X    double  t;
X
X    mt_vec (&foops, m, &(s->cent));
X
X    t = s->rad - sqrt (foops.y * foops.y + foops.z * foops.z);
X
X    if (t > 0)
X	t = t / foops.x;
X    else
X	t = 0;
X
X    return (t);	   /* radians between ray and edge of intersecting sphere*/
X}
X
X
X/* gets amount of diffuse light hitting a point */
X
Xfloat   shadow (p, rd, gn, blu)
Xstruct vector  *p;
Xdouble *rd, *gn, *blu;
X{
X    struct mat  trans;
X    struct sphere   ss;
X    struct vector   d;
X    int     c,
X            i;
X    double  l,
X            k,
X            x,
X            y,
X            z,
X            finds ();
X
X    l = 0.0;
X    c = -1;
X    sv (&d, &(ls.cent), p);
X    vecl (&d);  /*!!*/
X    vexzl (&d); /*!!*/
X    mt (&(d), &trans);
X
X    /* get maximum obscurment */
X
X    for (i = 0; i < nob; i++) {
X	ss.rad = bl[i].s.rad;
X	sv (&(ss.cent), &(bl[i].s.cent), p);
X	if ((k = finds (&trans, &ss)) > l) {
X	    c = i;
X	    l = k;
X	}
X    }
X
X    if (c == -1)
X	k = 255.0;
X    else {	
X	k = 1.0 - l * d.l / ls.rad;
X	if (k < 0.0)	/* l 	      = angle to move to get ray out of sphere*/
X	    k = 0.0;	/* d.l/ls.rad = 1/angle light source subtends */
X	else
X	    k *= 255.0;
X    }
X    *rd = *gn = *blu = k;	/* all colors the same for now */
X}
X
X
SHAR_EOF
chmod 0600 FIND.C || echo "restore of FIND.C fails"
sed 's/^X//' << 'SHAR_EOF' > MAIN.C &&
X/* main.c 
X *
X *****************************************************************
X *                                                               *
X *                      basic ray tracing:                       *
X *    spheres and a floor (reflection, refraction and diffuse)   *
X *		programmer: friedrich knauss			 *
X *                       7-4-86 to 7-21-86                       *
X *		ported to st: allen king 5/87, added:		 *
X *			limited color				 *
X *			low rez display first			 *
X *			.25 to 16 rays per pixel 		 *
X *			object definitions in file		 *
X *			multiple-frame movies			 *
X *			bouncing and gravity			 *
X *                                                               *
X *****************************************************************/
X
X#include <stdio.h>
X#include <math.h>
X#include <osbind.h>
X#include <gemdefs.h>
X#include "rtd.h"
X#define dprintf if (debug) printf
X
X/* these definitions describe a window in the x-y plane
X   that the whole thing is viewd through. */
X
Xint XMIN, YMIN, XMAX, YMAX;
Xfloat xmin, ymax;		/* floating pt versions */
X
XwOnewSize(x,y, w,h)
Xint x,y, w,h;
X{	XMIN = x;	YMIN = y;
X	XMAX = x+w+2;	YMAX = y+h+2;
X}
X
X/* Nomenclature notes: 
X *	1. <a>P<b> stands for "the number of <a>'s PER <b>
X *	2. r stands for rays
X *	3. p stands for pixels
X *	4. s stands for either side of the screen
X *	5. the suffix "Max" denotes the max number compiled into the program
X *
X * Note: these are linear (not area) conversions. A value of 2 rPp (rays PER
X * pixel) is applied in both x and y directions to get an area (and more
X * true to life) conversion of 4 (square) rays through each (square) pixel.
X */
X    float rPp;			/* rays PER pixel (on a (linear) side)*/
X#   define rPpMax 4
X				/* maximum rays PER pixel (ditto) */
X    int pPs;			/* pixels PER side (of screen) */
X    int rPs;			/* rays PER side (of screen) */
X    int rPsMax;			/* Max rays PER side (of screen) */
X
X#define INC  1
X#define INCY  (1.25/rPpMax)
X#define INCX  (1.0/rPpMax)
X
Xint colorInd;			/* color index (1->b/w, 2->2tones, 3->3tones)*/
X
X#define colors(r,g,b) 1000*(2*(r)+1)/16, 1000*(2*(g)+1)/16, 1000*(2*(b)+1)/16,
Xint oldClut[16][3], trClut[][3] = 
X{
X#include "colors.h"
X0};
X
Xstruct 
X{	int (*pal)[][3];
X	int Navg;
X} cConfig[] = {{trClut, 16}, {trClut+16, 32}, {trClut+32, 43}};
X
XFILE *fp, *fopen();
Xchar getline();
Xchar outname[6] = "pearl";
Xint wHandle;
Xint pxy_array[4];
X
X#define Nball 15
Xstruct ball bl[Nball];
Xint     level,
X        nob;
Xstruct vector vp;
Xfloat gravity = 0.0, attraction = 0.0;
Xint step, Step = 1000, bounce = 1;;
Xint fr=0, frames=1;
X
Xint debug =0;
X
Xstruct sphere   ls;
X
Xmain ()
X{   static float    xco,
X                    yco, yup;
X    struct ray  rr;
X    int     h, i, j, k;
X    int c, cr, cg, cb;
X    int x0, y0, x1, y1, dx, dy, dmax, xMax, yMax;
X    double red, green, blue;				/* was int */
X    long ctr, BavgN;
X    float tmp;
X
X    char str[200], *buf;
X    int Navg, *avg, *Bavg;
X
X    rPp = 1.0;/* defaults: */
X    colorInd = 1;
X
X/*    graf_mouse(M_OFF, 0L);/**/
X    printf("\033E");
X    do
X    {	printf("                RAYMOVI.PRG\n\n");
X	printf("Enter scene descriptor\nfilename: ");
X	if (getline(str, sizeof(str)) == '\003')
X		exit(0);
X	printf("\nreading file %s\n", str);
X    } while(scene_inz(str) == 0);
X
X    printf("\033E");
X
X    wHandle = w_open(0, "raymovi", 0);	/* open window (typeless, menueless) */
X    w_lClut(cConfig[colorInd-1].pal, oldClut);
X    v_hide_c(wHandle);
X
X    xmin = XMIN;	ymax = YMAX;
X    dx = XMAX-XMIN;
X    dy = YMAX-YMIN;
X    if (rPp > 1.0001)
X    {	BavgN = (long)dx * dy * colorInd;
X	Bavg = Malloc(BavgN * sizeof(int));
X	if (Bavg == 0)
X	{   long l1;
X	    printf("limited to 1 ray/pixel due to insufficient RAM\n");
X	    if (rPp > 1.0)
X		rPp = 1.0;
X	    for (l1=0; l1<200000; l1++);
X	}   
X	for (tmp = 1.0; rPp >= 1.0001; tmp *= 2.0, rPp /= 2.0);
X    }
X    else
X	for (tmp = 1.0; rPp < .5001; tmp /= 2.0, rPp *= 2.0);
X    rPp = tmp;
X    dmax = (dx>dy)? dx: dy;
X    for (i=dmax-1,       pPs=1;     i>0   ; i>>=1, pPs<<=1);
X    for (i=rPp*pPs-0.5,    rPs=1;   i>0   ; i>>=1, rPs<<=1);
X    rPsMax = rPpMax * pPs;
X    xMax = ((long)rPsMax*dx)/pPs;	yMax = ((long)rPsMax*dy)/pPs;
X
X    for (; fr<=frames; fr++) 
X    {  	int len, len1, p34, x, y, Elen, Ey;
Xredraw:	
X	if (Bavg) for (avg = Bavg, ctr=0; ctr<BavgN; ctr++)
X		*avg++ = 0;
X	Navg = cConfig[colorInd-1].Navg;
X	ctr = 0;
X	for (len = rPsMax; len>= rPsMax/rPs; len /=2)
X	{   p34 = 3;
X	    if (len>=rPpMax)
X	    {	len1 = len/rPpMax - 1;
X		Elen = 0;
X	    }else
X	    {	len1 = 0;
X	        Elen = rPpMax -1;
X		Navg *= 4;
X	    }
X	    for (y=0; y<yMax; y += len)
X	    {	p34 = (p34 | 1) ^ 2;
X		if (Elen)
X		    Ey = (((y + len)&Elen) == 0);
X		y0 = y/rPpMax;
X		y1 = y0 + YMIN;
X		pxy_array[1] = y1;
X		pxy_array[3] = y1 + len1;
X		yco = ymax - INCY*y;
X
X		for (x=0; x<xMax; x += len)
X		{   if ((p34 ^=1) == 0 && ctr !=0)
X			continue;
X		    ctr++;
X		    x0 = x/rPpMax;
X		    x1 = x0 + XMIN;
X		    pxy_array[0] = x1;
X		    pxy_array[2] = x1 + len1;
X		    xco = xmin + INCX*x;
X
X	        /* define the ray through a pixel; find out value for that pixel */
X	 	    mv (xco, yco, 0.0, &(rr.org));
X		    sv (&(rr.dir), &(rr.org), &vp);
X		    shade (&rr, &red, &green, &blue);   /* find out color */
X
X		    cb = blue;
X		    cr = red;
X		    cg = green;
X		    if (Bavg)		/* compute sum for all rays */
X		    {   register int *array;
X			array = Bavg + ((long)y0*dx+x0+1)*colorInd;
X			switch(colorInd)
X			{ case 3:
X			    cg = *--array += cg;
X			  case 2:
X			    cr = *--array += cr;
X			  case 1:
X			    cb = *--array += cb;
X		    }   }
X
X		    if (Elen==0 || Ey && ((x+len)&Elen) == 0 )
X		    {	switch(colorInd)
X			{ case 1:			/* white (and black): */
X			    c = (cb/Navg + 1) & 0xf;		/* white */
X			    break;
X			  case 2:			/* white and red: */
X			    if (2*cr < 3*cb)
X				c = cb/Navg+1 & 7; 		/* white */
X			    else c = (cr/Navg & 7) + 8;		/* red */
X			    break;
X			  case 3:			/* white, red, blue: */
X			    if (4*cr > 3*(cg+cb))
X			    {	c = cr/Navg % 6;		/* red */
X				if (c) c += 5; /* red */
X				else c = 1;
X			    }
X			    else if (4*cb > 3*(cr+cg))
X			    {	c = cb/Navg % 6;		/* blue */
X				if (c) c += 10; /* red */
X				else c = 1;
X			    }
X			    else c = (cg/Navg+1) % 6;	 	/* white */
X			    break;
X			}
X			dprintf("%d,%d,%d -> %d\n", cr, cg, cb, c);
X		    	vsf_color(wHandle, c);	/*draw colored box*/
X			v_bar(wHandle, pxy_array);
X		    }
X		    if ((ctr&15)== 0 && kbd() == -1)
X			goto redraw;
X		}
X		sprintf(str, "%s%03d:     RAYMOVI.PRG     rpp=%05.3f", 
X			outname, fr, (float)ctr/((long)dx*dy));
X		di_header(1, str);
X	}   } 	
X	sprintf(str, "%s%03d.pi1", outname, fr);
X	f_write(str, colorInd-1);
X
X	movie(&bl, nob, step, Step, gravity, bounce, attraction);
X	write_ckpt("checkpt");
X	}
Xexit:
X    w_lClut(oldClut, (long)0);
X    w_close();
X}
X
Xkbd()
X{	char key, fname[20];
X	int found_first = 0, fr1, rval=0;
X
X	if (Cconis() == 0)
X		return(0);
X
X	key = Cconin();
X	while (1)
X	{	sprintf(fname, "%s%03d.pi1", outname, fr);
X		switch(key)
X		{   case ('?'): case ('\0'):		/* help */
X			printf("\033E\n\n       RayMovi\n\
X			\nw - write \"%s\" from screen,\
X			\nr - read \"%s\" to screen,\
X			\np - play movie \"%sXXX.pi1\
X			\ne - exit\n\n", fname, fname, outname);
X			key = Cconin();
X			printf("\033E");
X			rval = -1;
X			break;
X		    case ('D'):
X			debug = 1-debug;
X			return (rval);
X		    case ('w'):
X			f_write(fname, colorInd-1);
X			return (rval);
X		    case ('r'):	case ('p'):
X			fr1 = 1;
X			while (key == 'p' || key == 'r')
X			{	sprintf(fname,"%s%03d.pi1", outname, fr1++);
X				if (f_read(fname, (long)0) <0)
X				{	if (found_first)
X					{	fr1 = 1;
X						found_first = 0;
X				}	}
X				else
X					found_first = 1;
X				if (Cconis() != 0 || key == 'r' && found_first)
X					key = Cconin();
X			}
X			rval = -1;
X			break;
X		    case ('e'): case('\003'):
X			w_lClut(oldClut, (long)0);
X			w_close();
X			exit(0);
X		    default:
X			return(rval);
X}	}	}
X
Xscene_inz(inz_file)
Xchar *inz_file;
X{   int h, i, j, k; char str[200], *buf;
X    if ((fp=fopen(inz_file, "r"))==0)
X    {	printf("can't open file '%s'\n", inz_file);
X	return(0);
X    }
X    j=0, h=0;
X    while (1)
X    {	int linenum;
X    	buf = str;
X	for (k=0; (i = getc(fp)) != '\n' && i != EOF && ++k<sizeof(str)-1;)
X		*buf++ = i;
X	*buf = '\0';
X	linenum++;
X	printf("%d: %s\n", linenum, str);
X	buf = str;
X	if (i == EOF)
X	    break;		
X	switch (buf[0])
X	{   case ('p'):		/* physical ball properties */
X		if (j >= Nball)
X			panic("**** ERROR: too many balls (%d >= %d)", j,Nball);
X		if (7 !=sscanf(buf+1," %f,%f,%f,%f,%f,%f,%f", &bl[j].s.cent.x,
X			&bl[j].v.x, &bl[j].s.cent.y, &bl[j].v.y,&bl[j].s.cent.z,
X			&bl[j].v.z, &bl[j].s.rad)) goto errors;
X		j++;
X		break;
X	    case ('o'):		/* optical ball properties */
X		if (h >= Nball)
X			panic("**** ERROR: Too many balls (%d >= %d)", h,Nball);
X		if(8 !=sscanf(buf+1," %f,%f,%f,%f,%f,%f,%f,%f", &bl[h].ior,
X			&bl[h].rfr, &bl[h].rfl, &bl[h].dif, &bl[h].amb, 
X			&bl[h].red, &bl[h].green, &bl[h].blue)) goto errors;
X	        bl[h].ior *= bl[h].ior;
X		h++;
X		break;
X	    case ('v'):		/* define viewpoint */
X		if (3 != sscanf(buf+1, " %f,%f,%f", &vp.x, &vp.y, &vp.z))
X			goto errors;
X		break;
X	    case ('l'):  /* define light source (rad = how fuzzy shadows are) */
X		if (4 != sscanf(buf+1, " %f,%f,%f,%f", &ls.cent.x, &ls.cent.y,
X			&ls.cent.z, &ls.rad)) goto errors;
X		break;
X	    case ('r'):  /* define number of rays per pixel and color scheme */
X		if (2 != sscanf(buf+1, " %f, %d", &rPp, &colorInd))
X			goto errors;
X		colorInd = colorInd<1? 1: colorInd>=3? 3: colorInd;
X		rPp = sqrt(rPp>16.0? 16.0: rPp);
X		break;
X	    case ('g'):	 /* gravity, etc */
X		if (3 != sscanf(buf+1, " %f, %f, %d", &gravity, &attraction, 
X			&bounce)) goto errors;
X		gravity /= Step * 10;
X		break;	
X	    case ('f'):	 /* define the filename */
X		while(*(++buf) == ' ');	/* skip leading spaces */
X		for (i=0; i<5 && *buf!=','; i++)
X			outname[i] = *buf++;
X		outname[i] = 0;
X
X		if (2 != sscanf(buf+1, "%d,%d", &fr, &frames))
X			goto errors;
X		break;
X	    case ('\t'):  /* comment */
X	    case (' '):
X	    case ('\0'):
X		break;
X	    default:
X	    errors:
X		printf("**** ERROR in line %d:\n%s\n", linenum, buf);
X    }	}
X    fclose(fp);	
X    if (h != j)
X	panic("Number of 'o' lines (%ld) mismatches\n number of 'p' lines (%ld)\n", 
X		(long)h, (long)j);
X    nob = j;
X    return(1);
X}
X
Xwrite_ckpt(name)
Xchar *name;
X{	int i;
X	if ((fp = fopen(name, "w")) != 0)
X	{
X	    fprintf(fp, "f %s, %d, %d\n", outname, fr+1, frames);
X	    for (i=0; i<nob; i++)
X	    {	fprintf(fp, "p %f,%f,%f,%f,%f,%f,%f\n", bl[i].s.cent.x, 
X			bl[i].v.x, bl[i].s.cent.y, bl[i].v.y, bl[i].s.cent.z, 
X			bl[i].v.z, bl[i].s.rad);
X		fprintf(fp, "o %f,%f,%f,%f,%f,%f, %f,%f,%f\n\n",sqrt(bl[i].ior),
X			bl[i].rfr, bl[i].rfl, bl[i].dif, bl[i].amb, 
X			bl[i].red, bl[i].green,bl[i].blue);
X	    }
X	    fprintf(fp, "v %f,%f,%f\n", vp.x, vp.y,vp.z);
X	    fprintf(fp, "l %f,%f,%f,%f\n", 
X		ls.cent.x, ls.cent.y, ls.cent.z, ls.rad);
X	    fprintf(fp, "r %f, %d\n", rPp*rPp, colorInd);
X	    fprintf(fp, "g %f, %f, %d\n", gravity*Step* 10, attraction, bounce);
X	    fclose(fp);	
X}	}
X
Xchar getline(str, sizeofstr)
Xchar *str;
Xint sizeofstr;
X{   char *buf, c;
X    int k, i;
X
X    buf = str;
X    for (k = sizeofstr; k > 0;)
X    {	c = i = Cconin();
X	if (c == '\n' || c == '\r' || c == ' ')
X		return (c);
X	else if (c == '\003' || c == '\214')	/* ^c or Undo */
X		return ('\003');
X	else if (c == '\b')
X	{	if (k < sizeofstr)
X		{	*--buf = 0;
X			k++;
X		}else
X			printf(" ");
X	}
X	else
X	{	*buf++ = c;
X		*buf = '\0';
X		k--;
X}   }	}
X
Xpanic(s,l1,l2)
Xchar *s;
Xlong l1,l2;
X{	printf("\n");
X	printf(s,l1,l2);
X	printf("\npress any key to continue");
X	Cconin();
X
X	if (wHandle)
X	{	w_lClut(oldClut, (long)0);
X		w_close();
X	}
X	exit(1);
X	atoi();
X}
SHAR_EOF
chmod 0600 MAIN.C || echo "restore of MAIN.C fails"
sed 's/^X//' << 'SHAR_EOF' > MAKEFILE &&
XNAME1 = raymovi
XOBJS1 = gemstart.o main.o wndows.o file.o movie.o shade.o find.o support.o
X
XNAME2 = blastram
XOBJS2 = gemstart.o blastram.o wndows.o file.o 
X
XNAME3 = bug
XOBJS3 = bug.o 
X#---------------------------------------- invariant:
XT = g:
XBIN = c:\bin
X
XCPFLAGS = -i c:\include\ -DATARIST
XFpFlag = -f
XC0FLAGS = 
XC1FLAGS = 
XASFLAGS = -l -u -s $(BIN)\ 
X
X# -s d:\ -f d:\ ,,,   -i c:\bin\as68symb.dat
X
X.c.o:
X	@$(ECHO) *********     $*.C:     *********
X	@$(RM) $*.o
X	$(BIN)\$(CP68) $(CPFLAGS) $*.c $(T)\$*.i
X	$(BIN)\$(C068) $(T)\$*.i $(T)\$*.1 $(T)\$*.2 $(T)\$*.3 $(FpFlag) $(C0FLAGS)
X	@$(RM) $(T)\$*.i
X	$(BIN)\$(C168) $(T)\$*.1 $(T)\$*.2 $(T)\$*.s $(FpFlag) $(C1FLAGS)
X	@$(RM) $(T)\$*.1 $(T)\$*.2
X#	$(BIN)\$(AS) $(ASFLAGS) $(T)\$*.s
X#	@$(CP) $(T)\$*.o $*.o
X	$(BIN)\mac.prg -6 -o $*.o $(T)\$*.s
X	@$(RM) $(T)\$*.s 		# $(T)\$*.o
X
X.s.o:
X	@$(ECHO) *********     $*.S:     *********
X#	$(BIN)\$(AS) $(ASFLAGS) $*.s
X	$(BIN)\mac.prg -6 -o $*.o $*.s
X
X$(NAME1).prg : $(OBJS1)
X	@$(ECHO) 
X	$(BIN)\aln.prg -s -u -o $(NAME1).prg -c $(NAME1).lnk
X	@$(ECHO) * * * * * * * *  $(NAME1).ttp made  * * * * * * * * 
X
X$(NAME2).ttp : $(OBJS2)
X	@$(ECHO) 
X	$(BIN)\aln.prg -s -u -o $(NAME2).prg -c $(NAME2).lnk
X	@$(CP) $(NAME2).prg $(NAME2).ttp
X	@$(RM) $(NAME2).prg
X	@$(ECHO) * * * * * * * *  $(NAME).ttp made  * * * * * * * * 
X
X$(NAME3).prg : $(OBJS3)
X	@$(ECHO) 
X	$(BIN)\aln.prg -s -u -o $(NAME3).prg -c $(NAME3).lnk
X	@$(ECHO) * * * * * * * *  $(NAME3).ttp made  * * * * * * * * 
X
SHAR_EOF
chmod 0600 MAKEFILE || echo "restore of MAKEFILE fails"
sed 's/^X//' << 'SHAR_EOF' > MOVIE.C &&
X/* movie.c		inertia, gravity, bouncing */
X
X#include "rtd.h"
X#include <math.h>
X
Xdouble dot();
Xfloat tx43;
X#define cube(a) ((tx43=a)*(tx43)*(tx43))
X#define density 1.0
X
Xmovie(bl, nob, step, Step, gravity, bounce, attraction)
Xstruct ball *bl;
Xint nob, step, Step, bounce;
Xfloat gravity, attraction;
X{	register int i, j;
X	register struct ball *bli, *blj;
X	struct vector f;
X	for (step = 0; step<Step; step++)
X	{	for (i=0, bli=bl; i<nob; i++, bli++)
X		{	if (bounce) for (j=i+1, blj=bli+1; j<nob; j++, blj++)
X			{	register float 
X				      massi, massj, /* mass of balls (i&j) */
X				      fi, fj,	    /* mass fraction (i&j) */
X				      tmp;
X				struct vector cmv,  /* center mass velocity*/
X				       rvi, rvj,    /* relative vel (i&j)  */
X				       d;	    /* distance between i&j*/
X				sv(&d, &bli->s.cent, &blj->s.cent);
X				vecl(&d);
X				if (d.l <= bli->s.rad + blj->s.rad)
X				{	tmp = bli->s.rad; massi = tmp*tmp*tmp;
X					tmp = blj->s.rad; massj = tmp*tmp*tmp;
X					fi = massi/(massi+massj);
X					fj = massj/(massi+massj);
X					cmv.x = fi*bli->v.x + fj*blj->v.x;
X					cmv.y = fi*bli->v.y + fj*blj->v.y;
X					cmv.z = fi*bli->v.z + fj*blj->v.z;
X
X					  /*    rvi = bli->v - cmv      */
X					sv(&rvi, &bli->v, &cmv);
X					tmp = dot(&d,&rvi);
X					  /*    d = 2*|d.rvi|/|d.d|*d   */	
X					scamult(2.0*tmp/(d.l*d.l),&d);
X					  /*    bli->v = d - rvi + cmv  */	
X					sv(&rvi, &rvi,&d);
X					av(&bli->v, &cmv, &rvi);
X
X					vecl(&d);
X					sv(&rvj, &blj->v, &cmv);
X					tmp = dot(&d,&rvj);
X					scamult(2.0*tmp/(d.l*d.l),&d);
X					sv(&rvj, &rvj,&d);
X					av(&blj->v, &cmv, &rvj);
X			}	}
X			if (attraction != 0.0) 
X			{	register float dl, tmp;
X				struct vector d;
X				f.x = f.y = f.z = 0.0;
X				for (j=0, blj=bl; j<nob; j++, blj++) if (i != j)
X				{
X					sv(&d, &bli->s.cent, &blj->s.cent);
X					dl = sqrt(d.x*d.x + d.y*d.y + d.z*d.z);
X					tmp = blj->s.rad/dl; tmp=tmp*tmp*tmp;
X					scamult(tmp, &d); /* mass/(r*r) */
X					av(&f, &f, &d);
X				}
X				scamult(attraction/(density*Step), &f);
X				sv(&bli->v,  &bli->v, &f);
X		}	}
X		for (i=0, bli=bl; i<nob; i++, bli++)
X		{	bli->s.cent.x += bli->v.x/Step;
X
X			bli->v.y -= gravity;
X			bli->s.cent.y += bli->v.y/Step;
X			if (bli->s.cent.y < bli->s.rad && bli->v.y < 0)
X				bli->v.y = - bli->v.y;
X
X			bli->s.cent.z += bli->v.z/Step;
X			if (bli->s.cent.z < bli->s.rad && bli->v.z < 0)
X				bli->v.z = - bli->v.z;
X}	}	}
SHAR_EOF
chmod 0600 MOVIE.C || echo "restore of MOVIE.C fails"
sed 's/^X//' << 'SHAR_EOF' > PEARLS &&
Xf pearl, 1, 24			PEARL001.PI1,...,PEARL024.PI1
Xr 1.0, 3			medium-rez (1.0 rays per pixel), 3 colors
Xg 32.0, 0.0, 1			gravity, no mutual attraction, bounce
X
X  (x     vx,	y,     vy    z,     vz) radius
Xp 195.0,-20,	130.0,  0,   250.0, 10,	30.0 	these are 4 linked balls:
Xp 150.0, -5, 	100.0, 20,   250.0, 10, 30.0 	"
Xp 105.0,  5,     70.0, 20,   250.0,-20, 30.0 	"
Xp 60.0,  20,     40.0,  0,   250.0,-20, 30.0 	"
Xp  0.0, -10,    300.0, 20,   600.0,  0,300.0	this is the big ball
Xp 200.0, -8,     50.0, 35,    60.0, 30, 50.0	a medium size (lone) ball
X
X  ior  refr refl difu amb    red grn blue
Xo 0.0, 0.0, 0.3, 0.6, 0.1,   1.0,0.0,0.0   diffuse red, somewhat reflective
Xo 0.0, 0.0, 0.6, 0.3, 0.1,   1.0,1.0,1.0   reflective, somewhat white
Xo 0.0, 0.0, 0.9, 0.0, 0.1,   1.0,1.0,1.0   reflective, some ambient
Xo 0.0, 0.0, 0.0, 0.8, 0.2,   1.0,0.0,0.0   diffuse red, some ambient
Xo 0.0, 0.0, 0.7, 0.1, 0.2,   1.0,1.0,1.0   reflective and ambient (the big one)
Xo 1.6, 0.8, 0.0, 0.0, 0.2,   1.0,1.0,1.0   refractive
X
Xv 215.0, 	100.0,       -550.0		viewpoint
Xl 150.0, 	950.0,          0.0, 	100.0	light source
SHAR_EOF
chmod 0600 PEARLS || echo "restore of PEARLS fails"
sed 's/^X//' << 'SHAR_EOF' > RAYMOVI.DOK &&
X			RAYMOVI.PRG
X
X   RAYMOVI.PRG renders 3-dimensional scenes in a world of reflective and 
Xrefractive spheres, using ray tracing techniques. Movies formed from
Xsequences of frames can be generated, showing reflective balls linked and 
Xbouncing in a gravitational field. They resemble a string of bouncing pearls.
X
X   The amount of computation required to produce one ray traced image is 
Xenormous -- on an ST it is measured in hours. To get animated effects,
Ximages must be precomputed in (overnight) batch runs. RAYMOVI provides the 
Xability to save sequences of images on disk in Degas PI1 format, for later 
Xreplay by BLASTRAM.TTP (also included).
X
X   RAYMOVI.PRG generates a low resolution approximation of each picture first,
Xfollowed by refinement to successively higher resolutions. In this way, 
Xa rough image is seen quickly for (somewhat) interactive debugging. Scenes 
Xcan be changed without recompilation of RAYMOVI.PRG -- the size, position, 
Xvelocity and optical properties of the balls in the scenes are specified 
Xnumerically in the initialization files.
X
X   The ray tracing heart of RAYMOVI was taken from net.sources, documented
XFredrich Knauss of University of California, San Diego. I've ported it to 
Xthe ST, added animation, (primitive) color, and the various other features
Xyou see here. I place RAYMOVI in the public domain, for non-comercial use only.
X
X   RAYMOVI.PRG runs only on a color ST in low resolution. 1 Meg is required 
Xto generate images with resolutions greater than one ray per pixel or for 
Xreplay of movies from RAM. Operation with only 512K is possible, but will 
Xproduce slightly aliased pictures (one ray per pixel) which can only be 
Xdisplayed statically, slowly from floppy, or from hard disk (if you have 
Xa disk blaster program).
X
X   The rest of this document contains sections entitled Getting Started, 
XConfiguration File Format, and Checkpointing. The sources are available 
Xupon request. With enough interest I could post them.
X
X
X
X			GETTING STARTED
X
X   The screen must be set to low resolution mode before running RAYMOVI.PRG.
XOnce started, '?' will give help. Keys 'r' reads and 'w' writes the current 
Xscreen using the current disk file, whose name is displayed in the upper 
Xleft corner of the screen. Key 'e' exits. The average number of rays shot 
Xthrough each pixel is also displayed in the margin and updated as the 
Xcomputation develops.
X
X   After unpacking RAYMOVI, the first logical thing to do is to start it up 
Xwith the configuration file PEARLS.  After a few minutes, you'll see 
Xa rough image, with the resolution increasing as you watch. You might 
Xwait an hour or so to get complete development, but if you get bored, hit 
Xthe 'r' key. This will read the one pre-computed picture PEARL001.PI1 which 
Xcame in the "arc".
X
X   Now put RAYMOVI aside until you are willing to dedicate a large chunk of
XST time to make a movie. When you have 10-20 hours free, start RAYMOVI,
Xagain with PEARLS. (Before you start, be sure you have a few hundred K 
Xfree on the disk for the images which will be produced.) To view the movie
Xproduced, exit ('e') and start BLASTRAM.TTP, supplying the argument
XPEARL in the dialog box. With BLASTRAM running, type '?' for help. 
XCheckpointing may be used if you wish to add more frames to the movie.
X
X
X
X			CHECKPOINTING
X
X
X   Since RAYMOVI.PRG runs are so long, checkpointing is important. (Actually
XI put it in because my sons rebooted the ST at 3:00 every day after school!). 
SHAR_EOF
echo "End of part 1, continue with part 2"
echo "2" > s2_seq_.tmp
exit 0