home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- ** **
- ** TRACE1.C A wee little ray-tracing program for IBM-XT, **
- ** Cubicomp CS-5, and Microsoft-C. **
- ** **
- ** K.A.Bjorke 10 July 1984 **
- ** (C) 1984 5th Generation Digital Research **
- ** **
- *************************************************************************/
-
- /*
- .fo /* TRACE1.C Page # */
- (dot commands in some comments for listing with wordstar)
- */
-
- #include <stdio.h>
- #include <conio.h> /* All I/O current thru monitors */
-
- #define VERSION "1.00"
-
- #define LIGHTSOURCE 1 /* enumerated object types */
- #define GLASS 2
- #define PLANE 3
-
- #define MAXLITES 4 /* maximum Number of each enumerated type above */
- #define MAXBALLS 6
- #define MAXPOLYS 8
-
- #define NULL 0
- #define FALSE 0
- #define TRUE 1
- #define ZERO 0.0
-
- #define AIR 0.985 /* per-unit transmission in open air (close to 1) */
- #define INFINITY 65565 /* maximum scene-space value */
- #define MAXLEVEL 10 /* maximum levels of recursive tracing */
- #define BRILLIANCE transmit /* to aid in "cheating" */
- #define BACKGROUND ZERO /* intensity if there's nothing there */
-
- #define XREG 0x0300 /* Cubicomp Control-Register Ports */
- #define YREG 0x0302
- #define DATA 0x0304
- #define RED 0x0306
- #define GREEN 0x0308
- #define BLUE 0x030A
- #define CONTROL 0x030C
- #define COMMAND 0x030E
-
- #define MAXSCAN 512 /* miscellaneous Machine Equates */
- #define CENTER (MAXSCAN/2)
- #define MAXGREY 256 /* Number of gray scales for monochrome picture */
-
- /*
- .pa
- */
-
- /*********************************
- ** External Functions **
- *********************************/
-
- extern sqrt(); /* pointer to float as argument!! */
-
- /*****************************************
- ** Global structures & types **
- *****************************************/
-
- /* typedef int (*PFI)(); */ /* used in plane structure definition */
- typedef float VECTOR[3]; /* used a lot */
-
- struct ball {
- int center[3];
- int radius, r2; /* r2 = radius * radius */
- float k, spec; /* refract, reflect */
- float transmit; /* cheat and use transmit as intensity */
- } sphere[MAXBALLS], light[MAXLITES];
-
- struct beam {
- VECTOR origin;
- VECTOR direction;
- float ac, bc, cc, dc; /* cc & dc are PARTIAL quadratic terms */
- };
-
- struct plane {
- float aco, bco, cco, dco; /* as in ax+by+cz+d=0 */
- float diff; /* assume only diffuse reflections */
- VECTOR normal; /* normal-to-plane */
- /* PFI style; */ /* function which would define bounds */
- } poly[MAXPOLYS];
-
- struct strike {
- float t;
- int type;
- int index;
- };
-
- /************************************
- ** Other Global Variables **
- ************************************/
-
- VECTOR viewpoint; /* camera position */
- int lightsnow, ballsnow, polysnow; /* how many for this pic? */
- float intamb; /* ambient light */
- float greys[MAXSCAN+1]; /* used for anti-aliasing */
- /* FILE *fpt; */ /* general-purpose file pointer */
- /* int _fmode = 0x8000; */ /* binary file mode */
- char title[32]; /* title for picture */
- int px, py; /* current screen coordinates */
- /*
- .pa
- */
-
- /*********************************
- ** **
- ** Code Begins Here **
- ** **
- *********************************/
-
- main(argc,argv)
- int argc;
- char *argv[];
- {
- advertise(); /* (C) 1984 */
- if (argc != 1)
- tutor(); /* arguments? what? */
- /* else... */
- read_script(); /* set up scene */
- grey_map(); /* create lookup table */
- init_screen(); /* clear screen and set for continuous write */
- draw_pic(); /* trace like crazy */
- }
-
- /***********************
- ** Vector dot product **
- ***********************/
- float dotprod(a,b)
- float *a, *b;
- {
- int i;
- float q;
- q = ZERO;
- for (i = 0; i < 3; ++i)
- q += (*a++ * (*b++));
- return (q);
- }
-
- /*
- .pa
- */
-
- /*********************************
- ** Recursive Ray-Trace Function **
- *********************************/
- float trace_ray(zap, medium, level)
- struct beam *zap;
- float medium;
- int level;
- {
- int i, j, m, ct;
- float med2, result, falloff;
- VECTOR util; /* utility vector */
- struct strike intersect[MAXLITES+MAXBALLS+MAXPOLYS+1];
- struct beam newray; /* created by GLASS objects */
-
- result = ZERO;
- if ((--level == 0) || (medium == ZERO))
- return(result); /* to prevent endless reflections */
- /* & skip opaque objects */
- ct = 0;
- prepare_beam(zap);
- blank_intersects(&intersect, (MAXLITES+MAXBALLS+MAXPOLYS));
- /* now, search for intersections with all objects... */
- for (i = 0; i < lightsnow; ++i) {
- if (cross_ball(&light[i], zap, &intersect[ct].t) != FALSE) {
- intersect[ct].index = i;
- intersect[ct++].type = LIGHTSOURCE;
- }
- }
- for (i = 0; i < ballsnow; ++i) {
- if (cross_ball(&sphere[i], zap, &intersect[ct].t) != FALSE) {
- intersect[ct].index = i;
- intersect[ct++].type = GLASS;
- }
- }
- for (i = 0; i < polysnow; ++i) {
- if (cross_poly(&poly[i], zap, &intersect[ct].t) != FALSE) {
- intersect[ct].index = i;
- intersect[ct++].type = PLANE;
- }
- }
- if (ct == 0)
- return((result = BACKGROUND)); /* nuthin */
- /* else... */
- i = sort_by_t(&intersect); /* now pick nearest */
- j = intersect[i].index; /* for convenience */
- falloff = medium / intersect[i].t; /* this can get used a lot */
- /* [Continued]
- .pa
- */
-
- if (intersect[i].type == LIGHTSOURCE) { /* simplest case */
- result = light[j].BRILLIANCE * falloff;
- } else if (intersect[i].type == GLASS) {
- /* going in or out of sphere? */
- med2 = (sphere[j].transmit == medium) ?
- AIR : sphere[j].transmit;
- /* calculate REFRACTION */
- for (m = 0; m < 3; ++m)
- util[m] = sphere[j].center[m] -
- (newray.origin[m] = (zap->origin[m] +
- intersect[i].t * (zap->direction[m])));
- normalize(util); /* used as normal vector */
- /* note normal goes INTO the sphere */
- /* this is because zap does, too */
- for (m = 0; m < 3; ++m)
- newray.direction[m] = zap->direction[m] +
- sphere[j].k * util[m];
- /* deflect newray in direction of normal */
- result = trace_ray(&newray, med2, level) * falloff;
-
- /* now add REFLECTION */
- med2 = 2 * dotprod(util, &zap->direction[0]);
- /* N.L = cos(angle_of_incidence) */
- for (m = 0; m < 3; ++m)
- newray.direction[m] = zap->direction[m] -
- util[m] * med2;
- /* for regular vectors, incid + reflect = */
- /* normal * 2 cos(angle_of_incidence) */
- /* note reversal of signs! */
- result += trace_ray(&newray, medium, level) *
- sphere[j].spec * falloff;
- } else { /* must be a plane, then */
- result = intamb * poly[j].diff * falloff;
- for (m = 0; m < 3; ++m)
- util[m] = zap->origin[m] +
- intersect[i].t * zap->direction[m];
- for (m = 0; m < lightsnow; ++m) {
- visible(m, i, util, medium, falloff, &result);
- }
- }
- return(result);
- }
-
- /*
- .pa
- */
-
- /************************
- ** Actual drawing loop **
- ************************/
- draw_pic()
- {
- int i;
- struct beam laser; /* to stress point sampling */
- float intensity, prev, avg;
-
- cprintf("\n\n\n\nNow Drawing %s.\n",title);
- for (i = 0; i < 3; ++i)
- laser.origin[i] = viewpoint[i]; /* consistent */
- for (py = 0; py <= MAXSCAN; ++py) { /* scan rows */
- for (px = 0; px <= MAXSCAN; ++px) { /* scan columns */
- prev = intensity;
- laser.direction[0] = px - CENTER;
- laser.direction[1] = py - CENTER;
- laser.direction[2] = -viewpoint[2];
- /* now do all the real work: */
- intensity = trace_ray(&laser, AIR, MAXLEVEL);
- if ((px != 0) && (py != 0)) {
- avg = (intensity + prev + greys[px] +
- greys[px - 1]) / 4;
- if (avg > (MAXGREY-1))
- avg = (MAXGREY-1);
- write_pixel((px-1), (py-1), (int)avg);
- /* avg is anti-aliased */
- }
- greys[((px == 0) ? MAXSCAN : (px-1))] = prev;
- }
- }
- }
-
- /*
- .pa
- */
-
- /***********************************************************************
- ** find out if a lightsource is visible from some point and add it in **
- ***********************************************************************/
- int visible(i,f,point, medium, falloff, value)
- int i,f; /* light & poly indices */
- float point[]; /* point of incidence */
- float medium; /* local transmission value */
- float falloff; /* falloff to origin of first ray */
- float *value; /* cumulative intensity */
- {
- int m, j, ct;
- float dist;
- struct strike testint[MAXBALLS+MAXPOLYS+1];
- struct beam testray;
- /* we only have to check intersects of light-stoppers */
-
- ct = 0;
- blank_intersects(testint,MAXLITES);
- /* point testray at light[i] */
- for (m = 0; m < 3; ++m) {
- testray.origin[m] = point[m];
- testray.direction[m] = light[i].center[m] - point[m];
- }
- prepare_beam(&testray);
- cross_ball(&light[i], &testray, &dist); /* get distance to light */
- for (j = 0; j < ballsnow; ++j) {
- if (cross_ball(&sphere[j], &testray, &testint[ct].t) != NULL) {
- testint[ct].index = j;
- testint[ct++].type = GLASS;
- }
- }
- for (j = 0; j < polysnow; ++j) {
- if (cross_poly(&poly[j], &testray, &testint[ct].t) != NULL) {
- testint[ct].index = j;
- testint[ct++].type = PLANE;
- }
- }
- /* if (ct == 0) then trivial accept -- all clear */
- if (ct != 0) {
- j = sort_by_t(testint);
- if (testint[j].t < dist)
- return(FALSE); /* something between us and light */
- }
- /* light is visible, so... */
- /* Lambert: ir = ia*ka+ip*kd(L.N) */
- *value += (light[i].BRILLIANCE * poly[f].diff *
- dotprod(&poly[f].normal[0], &testray.direction[0]) *
- medium / dist) * falloff;
- /* note no assumption of AIR made */
- return(TRUE);
- }
-
- /*
- .pa
- */
-
- /**********************************************************
- ** Normalize direction vector & Pre-calculate Quadratics **
- **********************************************************/
- prepare_beam(illum)
- struct beam *illum;
- {
- int i;
- illum->ac = illum->bc = illum->cc = 0;
- normalize(&illum->direction[0]);
- for (i = 0; i < 3; ++i) {
- illum->ac += illum->direction[i] * illum->direction[i];
- illum->bc += illum->direction[i] * illum->origin[i];
- illum->cc += illum->origin[i] * illum->origin[i];
- }
- illum->bc *= 2; /* quadratics will be the same for every ball struc */
- illum->ac *= 2; /* only change in equation will be center[] & r2... */
- illum->dc = illum->bc * illum->bc;
- }
-
- /*
- .pa
- */
-
- /*********************************************************************
- ** calculate ray-ball intersections - return TRUE if any valid ones **
- *********************************************************************/
- int cross_ball(globe, ray, t1)
- struct ball *globe;
- struct beam *ray;
- float *t1;
- {
- float c, d, t2;
- c = ray->cc - globe->r2;
- d = ray->dc - 2 * ray->ac * c; /* quadratic determinant */
- if (d < 0)
- return (FALSE); /* no real solutions */
- /* else... */
- if (d == 0) { /* one real solution */
- *t1 = -(ray->bc / ray->ac);
- /* return ( (*t1 <= 0) ? FALSE : TRUE); */
- if (*t1 <= 0)
- return(FALSE); /* but in the wrong direction */
- /* else... */
- return(TRUE); /* if it's okay */
- }
- /* else two real solutions */
- sqrt(&d); /* now in the form: */
- /* - b +- sqrt(d)/(2*a) */
- *t1 = -(ray->bc + d) / ray->ac;
- t2 = -(ray->bc - d) / ray->ac;
- if ((t2 <= 0) && (*t1 <= 0))
- return(FALSE); /* both behind current position */
- if (t2 <= 0)
- return(TRUE); /* t1 must be okay */
- *t1 = (*t1 <= 0) ? t2 : /* t2 must be okay */
- min(*t1, t2); /* else just take the closer one */
- return(TRUE);
- }
-
- /*
- .pa
- */
-
- /***********************************************************************
- ** calculate ray-plane intersections - return TRUE if valid one found **
- ***********************************************************************/
- int cross_poly(flat, ray, t)
- struct plane *flat;
- struct beam *ray;
- float *t;
- {
- float dt;
- dt = flat->aco * ray->direction[0] +
- flat->bco * ray->direction[1] +
- flat->cco * ray->direction[2];
- if (dt == 0)
- return (FALSE); /* ray and plane are parallel */
- /* else */
- /* simple linear math */
- *t = -( flat->aco * ray->origin[0] +
- flat->bco * ray->origin[1] +
- flat->cco * ray->origin[2] +
- flat->dco) / dt;
- return(TRUE);
- }
-
- /*
- .pa
- */
-
- /**************************
- ** Normalize a 3d vector **
- **************************/
- normalize(vect)
- float vect[];
- {
- int i;
- float magnitude;
- magnitude = 0;
- for (i = 0; i <3; ++i)
- magnitude += vect[i] * vect[i];
- sqrt(&magnitude); /* length of [a b c] = sqrt(a^2 + b^2 + c^2) */
- for (i = 0; i < 3; ++i)
- vect[i] /= magnitude;
- }
-
- /*
- .pa
- */
-
- /********************************************
- ** find the nearest intersection in a list **
- ********************************************/
- int sort_by_t(intersect)
- struct strike intersect[];
- {
- int i, j;
- float t2;
- t2 = INFINITY;
- for (i = 0; intersect[i].type != NULL; ++i)
- j = (intersect[i].t < t2) ? i : j;
- return(j);
- }
-
- /*********************************************************
- ** blank out types in an intersection list (initialize) **
- *********************************************************/
- blank_intersects(intersect, top)
- struct strike intersect[];
- int top;
- {
- int i;
- for (i = 0; i <= top; ++i)
- intersect[i].type = NULL;
- }
-
- /*******************************************
- ** set up the Cubicomp screen for drawing **
- *******************************************/
- init_screen()
- {
- outw(CONTROL, 0x0000); /* frame 0, no masks, normal */
- outw(DATA, 0);
- outp(CONTROL, 0x0010); /* clear screen to black */
- while ((inp(CONTROL+1) && 0x0080) == NULL); /* wait for ready */
- outw(XREG, 0);
- outw(YREG, 0); /* point to upper-left corner for counting */
- }
-
- /*******************************************************
- ** write a value to the current pixel & count forward **
- *******************************************************/
- write_pixel(x, y, value)
- int x, y, value; /* this routine actually throws away x & y */
- /* since the Cubicomp auto-increments */
- {
- outw(DATA, value);
- outp(CONTROL, 0x0021); /* write and increment */
- }
-
- /*
- .pa
- */
-
- /*****************************************
- ** Make a grey-scale map, 0-(MAXGREY-1) **
- *****************************************/
- grey_map()
- {
- int i;
- for (i = 0; i < MAXGREY; ++i) {
- outp(RED, i); /* same value to all regs */
- outp(GREEN, i);
- outp(BLUE, i);
- outw(DATA, i); /* and as table address */
- outp(CONTROL, 4); /* Map-write */
- while((inp(CONTROL+1) && 0x0080) == NULL); /* wait... */
- }
- }
-
- /********************************************************
- ** send a 16-bit value thru the IBM's 8-bit data lines **
- ********************************************************/
- outw(port,wrd)
- int port, wrd;
- {
- outp(port,(wrd & 0x00FF));
- outp(++port,(wrd >> 8));
- }
-
- /************************
- ** toot one's own horn **
- ************************/
- advertise()
- {
- int i;
- for (i = 0; i < 24; ++i)
- putch('\n',stderr); /* crude clear-monitor */
- cputs("Trace1\tVersion ");
- cputs(VERSION);
- cputs("\tK. A. Bjorke\n");
- cputs("(C) 1984 5th Generation Digital\n\n");
- }
-
- /*
- .pa
- */
-
- /**********************************
- ** respond to a bad command-line **
- **********************************/
- tutor()
- {
- cputs("Correct Command Line Format:\n");
- cputs("\tTRACE1 [No Args]\n");
- exit(1);
- }
-
- /*****************************************************
- ** Read script file (from keyboard in this version) **
- *****************************************************/
- read_script()
- {
- int i, j;
- char xyz[3];
- xyz[0] = 'X'; xyz[1] = 'Y'; xyz[2] = 'Z';
- lightsnow = ballsnow = polysnow = 0;
- cputs("Enter Title of Image: ");
- cgets(title);
- cputs("\nEnter \'Camera\' Viewpoint as XYZ triplet:\n");
- for (j = 0; j < 3; ++j) {
- putch(xyz[j]);
- cputs(": ");
- cscanf("%d", &viewpoint[j]);
- }
- cputs("\nEnter the Intensity of Ambient Light: ");
- cscanf("%f",intamb);
- cputs("\nNumber of Light Sources: ");
- cscanf("%d", &lightsnow);
- for (i = 0; i < lightsnow; ++i) {
- cprintf("\n\nLIGHT SOURCE %1d:",(i + 1));
- cputs("\nEnter Center Point as XYZ triplet:\n");
- for (j = 0; j < 3; ++j) {
- putch(xyz[j]);
- cputs(": ");
- cscanf("%d", &light[i].center[j]);
- }
- cputs("\nRadius: ");
- cscanf("%d", &light[i].radius);
- cputs("\nIntensity: ");
- cscanf("%f", &light[i].BRILLIANCE);
- light[i].r2 = light[i].radius * light[i].radius;
- }
- /* [Continued]
- .pa
- */
- cputs("\nNumber of \'Glass\' Spheres: ");
- cscanf("%d", &ballsnow);
- for (i = 0; i < lightsnow; ++i) {
- cprintf("\n\nSPHERE %1d:",(i + 1));
- cputs("\nEnter Center Point as XYZ triplet:\n");
- for (j = 0; j < 3; ++j) {
- putch(xyz[j]);
- cputs(": ");
- cscanf("%d", &sphere[i].center[j]);
- }
- cputs("\nRadius: ");
- cscanf("%d", &sphere[i].radius);
- cputs("\nIndex of Reflection: ");
- cscanf("%f", &sphere[i].spec);
- cputs("\nIndex of Refraction: ");
- cscanf("%f", &sphere[i].k);
- cputs("\nTransmission per unit of material: ");
- cscanf("%f", &sphere[i].transmit);
- sphere[i].r2 = sphere[i].radius * light[i].radius;
- }
- cputs("\n\nEnter Number of Arbitrary Planes: ");
- cscanf("%d",&polysnow);
- for (i = 0; i < polysnow; ++i) {
- cprintf("\n\nPLANE %1d\n",(i + 1));
- cputs("\tEnter coefficients of the form:\n");
- cputs("\t Ax + By + Cz + D = 0");
- cputs("\nA: ");
- cscanf("%f", &poly[i].aco);
- cputs("\nB: ");
- cscanf("%f", &poly[i].bco);
- cputs("\nC: ");
- cscanf("%f", &poly[i].cco);
- cputs("\nD: ");
- cscanf("%f", &poly[i].dco);
- cputs("\n Plane Grey-Scale Value: ");
- cscanf("%f", &poly[i].diff);
- poly[i].normal[0] = poly[i].aco;
- poly[i].normal[1] = poly[i].bco;
- poly[i].normal[2] = poly[i].cco;
- normalize(&poly[i].normal[0]); /* precalculate normal */
- }
- }
-
- /*
- .pa
- */
-
- /***************************
- ** If file not found, etc **
- ***************************/
- nofile(s)
- char s[];
- {
- cputs("Error! - Can't open ");
- cputs(s);
- cputs("!\n");
- exit(2);
- }
-
-
- /* eof */