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