home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
RBBS in a Box Volume 1 #3.1
/
RBBSIABOX31.cdr
/
trac
/
trace1.c
< prev
next >
Wrap
Text File
|
1990-09-29
|
18KB
|
657 lines
/*************************************************************************
** **
** 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 */