blob: a6caae64037b38990ed75d7f36eadcc4cd2f55a3 [file] [log] [blame]
/*************************************************************************/
/* */
/* Copyright (c) 1994 Stanford University */
/* */
/* All rights reserved. */
/* */
/* Permission is given to use, copy, and modify this software for any */
/* non-commercial purpose as long as this copyright notice is not */
/* removed. All other uses, including redistribution in whole or in */
/* part, are forbidden without prior written permission. */
/* */
/* This software is provided with absolutely no warranty and no */
/* support. */
/* */
/*************************************************************************/
/*
* NAME
* trace.c
*
* DESCRIPTION
* This file contains the routines to process ray jobs from the work pool
* containing primary ray jobs.
*/
#include <stdio.h>
#include <math.h>
#include "rt.h"
/*
* NAME
* frand - generate random floating point number
*
* SYNOPSIS
* REAL frand()
* Used instead of standard srand for portability to other systems than Unix.
*
* RETURNS
* A random floating point number in the range 0 <= x < 1.
*/
REAL frand()
{
REAL r;
static LONG lLastRand = 0;
lLastRand = lLastRand*214013L + 2531011L;
r = (REAL)((lLastRand >> 16) & 0x7FFF)/32768.0;
return (r);
}
/*
* NAME
* GetRayJobFromBundle -
*
* SYNOPSIS
* BOOL GetRayJobFromBundle(job, x, y)
* RAYJOB *job; // Ray job from work pool.
* INT *x, *y; // Pixel address.
*
* DESCRIPTION
* This routine determines if there are any more primary rays left to
* process in the ray bundle job.
*
* The routine returns FALSE if the bundle processing is complete. If
* there are more jobs, the current x and y pixel position are returned
* through procedure parameters. Then, x and y are bundle is updated for
* the next inquiry and TRUE is returned.
*
* RETURNS
* See above.
*/
BOOL GetRayJobFromBundle(job, x, y)
RAYJOB *job;
INT *x, *y;
{
*x = job->xcurr; /* Set pixel address first. */
*y = job->ycurr;
if ((job->y + job->ylen) == job->ycurr) /* All done? */
return (FALSE);
job->xcurr++; /* Update to next pixel. */
if ((job->x +job->xlen) == job->xcurr )
{
job->xcurr = job->x; /* Go to new scanline. */
job->ycurr++;
}
return (TRUE);
}
/*
* NAME
* ConvertPrimRayJobToRayMsg - convert primary ray job to the ray message format
*
* SYNOPSIS
* VOID ConvertPrimRayJobToRayMsg(ray, x, y)
* RAY *ray; // Ray message.
* REAL x, y; // Pixel.
*
* DESCRIPTION
* This routine converts the primary ray job to the ray message format.
* The ray origin and direction are computed and the other ray message
* variables are initialized.
*
* Perspective Projection:
*
* Calculate ray direction thru pixel and transform it back to
* the world coordinate system. Origin of ray is eye position.
*
* Orthographic Projection:
*
* Calculate ray direction thru pixel and transform it back to
* the world coordinate system. Origin of ray must also be
* calculated.
* RETURNS
* Nothing.
*/
VOID ConvertPrimRayJobToRayMsg(ray, x, y)
RAY *ray;
REAL x, y;
{
VEC4 dir;
VEC4 origin;
if (View.projection == PT_PERSP)
{
dir[0] = -Display.scrHalfWidth + (x*Display.vWscale);
dir[1] = Display.scrHalfHeight - (y*Display.vHscale);
dir[2] = Display.scrDist;
dir[3] = 0.0;
/* Transform ray back to world. */
TransformViewRay(dir);
VecNorm(dir);
VecCopy(ray->D, dir);
VecCopy(ray->P, View.eye);
}
else
{
/* Orthographic projection. */
dir[0] = 0.0;
dir[1] = 0.0;
dir[2] = 1.0;
dir[3] = 0.0;
/* Transform ray back to world. */
TransformViewRay(dir);
VecNorm(dir);
VecCopy(ray->D, dir);
/* Calculate origin. */
origin[0] = -Display.scrHalfWidth + (x*Display.vWscale);
origin[1] = Display.scrHalfHeight - (y*Display.vHscale);
origin[2] = 0.0;
origin[3] = 1.0;
/* Transform origin back to world. */
TransformViewRay(origin);
VecCopy(ray->P, origin);
}
/* Initialize other fields of ray message. */
ray->level = 0;
ray->weight = 1.0/(REAL)NumSubRays;
LOCK(gm->ridlock);
ray->id = gm->rid++;
UNLOCK(gm->ridlock);
ray->x = (INT)x;
ray->y = (INT)y;
}
/*
* NAME
* RayTrace - process primary ray bundle jobs from the workpool
*
* SYNOPSIS
* VOID RayTrace(pid)
* INT pid; // Process id.
*
* DESCRIPTION
* Process primary ray bundle jobs from the workpool. Each ray job from
* the ray bundle list performs the following tasks.
*
* A ray bundle job is converted to a ray message and pushed
* on the raytree stack. A ray job consists of a pixel address
* while the ray message also contains the ray origin, direction,
* level in tree, ray weight, ray id, and grid parameters.
* Processing begins with the primary ray and secondary rays being
* pushed onto the stack as the raytree is built and processed.
* The raytree stack is used to avoid recursion.
*
* Processes all jobs on raytree stack.
*
* Calls routines for intersecting a ray with the environment and
* for shading the ray.
*
* RETURNS
* Nothing.
*/
VOID RayTrace(pid)
INT pid;
{
INT i, j;
INT x, y; /* Pixel address. */
REAL xx, yy;
VEC3 N; /* Normal at intersection. */
VEC3 Ipoint; /* Intersection point. */
COLOR c; /* Color for storing background. */
RAY *ray; /* Ray pointer. */
RAY rmsg; /* Ray message. */
RAYJOB job; /* Ray job from work pool. */
OBJECT *po; /* Ptr to object. */
BOOL hit; /* An object hit? */
IRECORD hitrecord; /* Intersection record. */
ray = &rmsg;
while (GetJobs(&job, pid) != WPS_EMPTY)
{
while (GetRayJobFromBundle(&job, &x, &y))
{
/* Convert the ray job to the ray message format. */
xx = (REAL)x;
yy = (REAL)y;
if (AntiAlias)
for (j = 0; j < NumSubRays; j++)
{
ConvertPrimRayJobToRayMsg(ray, xx + frand(), yy + frand());
PushRayTreeStack(ray, pid);
}
else
{
ConvertPrimRayJobToRayMsg(ray, xx, yy);
PushRayTreeStack(ray, pid);
}
while (PopRayTreeStack(ray, pid) != RTS_EMPTY)
{
/* Find which object is closest along the ray. */
switch (TraversalType)
{
case TT_LIST:
hit = Intersect(ray, &hitrecord);
break;
case TT_HUG:
hit = TraverseHierarchyUniform(ray, &hitrecord, pid);
break;
}
/* Process the object ray hit. */
if (hit)
{
/*
* Get parent object to be able to access
* object operations.
*/
po = hitrecord.pelem->parent;
/* Calculate intersection point. */
RayPoint(Ipoint, ray, hitrecord.t);
/* Calculate normal at this point. */
(*po->procs->normal)(&hitrecord, Ipoint, N);
/* Make sure normal is pointing toward ray origin. */
if ((VecDot(ray->D, N)) > 0.0)
VecNegate(N, N);
/*
* Compute shade at this point - will process
* shadow rays and add secondary reflection
* and refraction rays to ray tree stack
*/
Shade(Ipoint, N, ray, &hitrecord, pid);
}
else
{
/* Add background as pixel contribution. */
VecCopy(c, View.bkg);
VecScale(c, ray->weight, c);
AddPixelColor(c, ray->x, ray->y);
}
}
}
}
}