blob: 00f26e8b9eaf404e5992ad47f1873864ee6a7ead [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
* hutv.c
*
* DESCRIPTION
*
*/
#include <stdio.h>
#include <math.h>
#include "rt.h"
/*
* eps_t is a small increment in t to be added to t_in to insure that the
* point is inside a cell not on the boundary.
*/
REAL eps_t = 1.0E-10;
/*
* NAME
* prn_tv_stats - print out HU traversal statistics -- not implemented in
* this version
*
* SYNOPSIS
* VOID prn_tv_stats()
*
* RETURNS
* Nothing.
*/
VOID prn_tv_stats()
{
}
/*
* NAME
* send_ray(r, v) - send ray to remote cluster
*
* SYNOPSIS
* INT send_ray(r, v)
* RAY *r;
* VOXEL *v;
*
* DESCRIPTION
* Not implemented yet.
*
* RETURNS
* Nothing, yet.
*/
INT send_ray(r, v)
RAY *r;
VOXEL *v;
{
}
/*
* NAME
* lookup_hashtable -
*
* SYNOPSIS
* VOXEL *lookup_hashtable(indx, g)
* INT indx;
* GRID *g;
*
* DESCRIPTION
* Hashtable is a table of all non-empty cells in the grid.
* hashtable[num_buckets] is indexed by index1D mod num_buckets,
* num_buckets and n, the # of cells per axis, should be relatively
* prime, grids at different levels may have different num_buckets. This
* routine assumes that there exists a non-empty voxel in the table.
*
* RETURNS
*
*/
VOXEL *lookup_hashtable(indx, g)
INT indx;
GRID *g;
{
INT i;
VOXEL *v;
i = indx - ((indx / g->num_buckets) * g->num_buckets);
v = g->hashtable[i];
while (v->id != indx)
{
v = v->next;
if (v == NULL)
{
fprintf(stderr, "hashtable error \n");
exit(-1);
}
}
return (v);
}
/*
* NAME
* lookup_emptycells -
*
* SYNOPSIS
* INT lookup_emptycells(indx, g)
* INT indx;
* GRID *g;
*
* DESCRIPTION
* Emptycells is a packed array of bits, one bit per cell in the grid. A
* 1 indicates that the cell is empty and therefore there is no entry for
* that cell in the hashtable.
*
* RETURNS
*
*/
INT lookup_emptycells(indx, g)
INT indx;
GRID *g;
{
INT i, w, r, num_bits;
UINT p, b;
num_bits = sizeof(UINT)*8;
w = indx / num_bits;
r = indx - w * num_bits;
p = g->emptycells[w];
b = p & (MSB >> r);
i = b > 0 ? EMPTY : NONEMPTY;
return (i);
}
/*
* NAME
* pop_up_a_grid -
*
* SYNOPSIS
* VOID pop_up_a_grid(r)
* RAY *r;
*
* DESCRIPTION
* Pop_up_a_grid takes RAYINFO & a grid pointer off the top of the stack
* and discards it which puts the ray in the voxel containing the grid
* being left.
*
* RETURNS
* Nothing.
*/
VOID pop_up_a_grid(r)
RAY *r;
{
RAYINFO *old_ri;
old_ri = r->ri;
r->ri = old_ri->next;
free_rayinfo(r);
}
/*
* NAME
* push_down_grid -
*
* SYNOPSIS
* VOID push_down_grid(r, v)
* RAY *r;
* VOXEL *v; /* Voxel containing the new grid.
*
* DESCRIPTION
* push_down_grid creates rayinfo for the new grid being entered and puts
* it on top of the stack.
*
* RETURNS
* Nothing.
*/
VOID push_down_grid(r, v)
RAY *r;
VOXEL *v;
{
INT n; /* # cells per axis in new grid. */
INT small;
INT i_in, i_out, i, il, ih;
REAL wc[3]; /* Entry point of grid in world coord.*/
REAL ti;
REAL del1, del2;
REAL t_in, t_out, tl, th;
REAL t[6], min[3];
GRID *new_g;
RAYINFO *new_ri;
RAYINFO *old_ri;
old_ri = r->ri;
new_g = (GRID *)v->cell;
new_ri = ma_rayinfo(r);
r->ri = new_ri;
new_ri->next = old_ri; /* Push new RAYINFO onto the stack. */
new_ri->grid = new_g;
n = new_g->indx_inc[1];
new_ri->delta[0] = old_ri->delta[0]/n;
new_ri->delta[1] = old_ri->delta[1]/n;
new_ri->delta[2] = old_ri->delta[2]/n;
if (old_ri->t_in >= 0.0)
{
/* Ray origin outside the voxel. */
new_ri->entry_plane = old_ri->entry_plane;
new_ri->t_in = old_ri->t_in;
ti = old_ri->t_in + eps_t;
}
else
{
/* Ray origin inside the voxel. */
ti= 0.0;
new_ri->entry_plane = -1; /* no entry plane calculated */
new_ri->t_in = old_ri->t_in;
}
wc[0] = ti*r->D[0] + r->P[0];
wc[1] = ti*r->D[1] + r->P[1];
wc[2] = ti*r->D[2] + r->P[2];
new_ri->index3D[0] = (int)((wc[0] - new_g->min[0]) / new_g->cellsize[0]);
if (new_ri->index3D[0] < 0)
new_ri->index3D[0] = 0;
if (new_ri->index3D[0] >= n)
new_ri->index3D[0] = n - 1;
new_ri->index3D[1] = (int)((wc[1] - new_g->min[1]) / new_g->cellsize[1]);
if (new_ri->index3D[1] < 0)
new_ri->index3D[1] = 0;
if (new_ri->index3D[1] >= n)
new_ri->index3D[1] = n - 1;
new_ri->index3D[2] = (int)((wc[2] - new_g->min[2]) / new_g->cellsize[2]);
if (new_ri->index3D[2] < 0)
new_ri->index3D[2] = 0;
if (new_ri->index3D[2] >= n)
new_ri->index3D[2] = n - 1;
new_ri->index1D = new_ri->index3D[0] + new_ri->index3D[1]*n + new_ri->index3D[2]*new_g->indx_inc[2];
new_ri->indx_inc1D[0] = r->indx_inc3D[0];
new_ri->indx_inc1D[1] = r->indx_inc3D[1]*n;
new_ri->indx_inc1D[2] = r->indx_inc3D[2]*new_g->indx_inc[2];
switch (new_ri->entry_plane)
{
case 3: /* max X */
new_ri->entry_plane = 0;
case 0: /* min X */
new_ri->d[0] = new_ri->t_in + new_ri->delta[0];
del1 = fmod((double)(old_ri->d[1] - new_ri->t_in),
(double)new_ri->delta[1]);
if (del1 <= eps_t)
new_ri->d[1] = new_ri->t_in + new_ri->delta[1];
else
new_ri->d[1] = new_ri->t_in + del1;
del2= fmod((double)(old_ri->d[2] - new_ri->t_in),
(double)new_ri->delta[2]);
if (del2 <= eps_t)
new_ri->d[2] = new_ri->t_in + new_ri->delta[2];
else
new_ri->d[2] = new_ri->t_in + del2;
small = new_ri->d[0] <= new_ri->d[1] ? 0 : 1;
small = new_ri->d[small] <= new_ri->d[2] ? small : 2;
new_ri->t_out = new_ri->d[small];
new_ri->exit_plane = small;
break;
case 4: /* max Y */
new_ri->entry_plane = 1;
case 1: /* min Y */
new_ri->d[1] = new_ri->t_in + new_ri->delta[1];
del1 = fmod((double)(old_ri->d[0] - new_ri->t_in),
(double)new_ri->delta[0]);
if (del1 <= eps_t)
new_ri->d[0] = new_ri->t_in + new_ri->delta[0];
else
new_ri->d[0] = new_ri->t_in + del1;
del2 = fmod((double)(old_ri->d[2] - new_ri->t_in),
(double)new_ri->delta[2]);
if (del2 <= eps_t)
new_ri->d[2] = new_ri->t_in + new_ri->delta[2];
else
new_ri->d[2] = new_ri->t_in + del2;
small = new_ri->d[0] <= new_ri->d[1] ? 0 : 1;
small = new_ri->d[small] <= new_ri->d[2] ? small : 2;
new_ri->t_out = new_ri->d[small];
new_ri->exit_plane = small;
break;
case 5: /* max Z */
new_ri->entry_plane = 2;
case 2: /* min Z */
new_ri->d[2] = new_ri->t_in + new_ri->delta[2];
del1 = fmod((double)(old_ri->d[0] - new_ri->t_in),
(double)new_ri->delta[0]);
if (del1 <= eps_t)
new_ri->d[0] = new_ri->t_in + new_ri->delta[0];
else
new_ri->d[0] = new_ri->t_in + del1;
del2 = fmod((double)(old_ri->d[1] - new_ri->t_in),
(double)new_ri->delta[1]);
if (del2 <= eps_t)
new_ri->d[1] = new_ri->t_in + new_ri->delta[1];
else
new_ri->d[1] = new_ri->t_in + del2;
small = new_ri->d[0] <= new_ri->d[1] ? 0 : 1;
small = new_ri->d[small] <= new_ri->d[2] ? small : 2;
new_ri->t_out = new_ri->d[small];
new_ri->exit_plane = small;
break;
case -1: /* No entry plane calculated, origin inside voxel. */
min[0] = new_g->min[0] + new_ri->index3D[0]*new_g->cellsize[0];
min[1] = new_g->min[1] + new_ri->index3D[1]*new_g->cellsize[1];
min[2] = new_g->min[2] + new_ri->index3D[2]*new_g->cellsize[2];
if (r->D[0] == 0.0)
{
if (r->P[0] >= min[0] && r->P[0] <= min[0] + new_g->cellsize[0])
t[0] = -HUGE_REAL;
else
t[0] = HUGE_REAL;
}
else
t[0] = (min[0] - r->P[0]) / r->D[0];
if (r->D[1] == 0.0)
{
if (r->P[1] >= min[1] && r->P[1] <= min[1] + new_g->cellsize[1])
t[1] = -HUGE_REAL;
else
t[1] = HUGE_REAL;
}
else
t[1] = (min[1] - r->P[1]) / r->D[1];
if (r->D[2] == 0.0)
{
if (r->P[2] >= min[2] && r->P[2] <= min[2] + new_g->cellsize[2])
t[2] = -HUGE_REAL;
else
t[2] = HUGE_REAL;
}
else
t[2] = (min[2] - r->P[2]) / r->D[2];
if (r->D[0] == 0.0)
{
if (r->P[0] >= min[0] && r->P[0] <= min[0] + new_g->cellsize[0])
t[3] = HUGE_REAL;
else
t[3] = HUGE_REAL;
}
else
t[3] = (min[0] + new_g->cellsize[0] - r->P[0]) / r->D[0];
if (r->D[1] == 0.0)
{
if (r->P[1] >= min[1] && r->P[1] <= min[1] + new_g->cellsize[1])
t[4] = HUGE_REAL;
else
t[4] = HUGE_REAL;
}
else
t[4] = (min[1] + new_g->cellsize[1] - r->P[1]) / r->D[1];
if (r->D[2] == 0.0)
{
if (r->P[2] >= min[2] && r->P[2] <= min[2] + new_g->cellsize[2])
t[5] = HUGE_REAL;
else
t[5] = HUGE_REAL;
}
else
t[5] = (min[2] + new_g->cellsize[2] - r->P[2]) / r->D[2];
t_in = -HUGE_REAL;
i_in = -1;
t_out = HUGE_REAL;
i_out = -1;
for (i = 0; i < 3; i++)
{
if (t[i] < t[i + 3])
{
tl = t[i];
il = i;
th = t[i + 3];
ih = i + 3;
}
else
{
tl = t[i + 3];
il = i + 3;
th = t[i];
ih = i;
}
new_ri->d[i] = th;
if (t_in < tl) /* max min */
{
t_in = tl;
i_in = il;
}
if (t_out > th) /* min max */
{
t_out = th;
i_out = ih;
}
}
if ((t_in > t_out) || (t_out < 0.0))
{
fprintf(stderr, "push_down_grid: Ray origin not in voxel \n");
exit(-1);
}
if (i_in > 2)
i_in -= 3;
if (i_out > 2)
i_out -= 3;
new_ri->entry_plane = i_in;
new_ri->t_in = t_in;
new_ri->t_out = t_out;
new_ri->exit_plane = i_out;
break;
}
}
/*
* NAME
* step_grid -
*
* SYNOPSIS
* INT step_grid(r)
* RAY *r;
*
* DESCRIPTION
* Step to next voxel on grid and return index1D, updating RAYINFO in the
* process, return -1 if step carries off the current grid, index1D in
* RAYINFO is not valid if off the grid. Assume that ray & RAYINFO are
* all initialized and that the current point along the ray is in the
* cell indentified by index1D & index3D[3]. The corresponding grid and
* RAYINFO are on the top of their stacks resp.
*
* RETURNS
* Nothing.
*/
INT step_grid(r)
RAY *r;
{
INT n; /* # cells per axis in grid. */
INT small; /* Index of closest cell boundary. */
INT *indx;
RAY *ra;
GRID *gr;
RAYINFO *rinfo;
ra = r;
rinfo = r->ri;
gr = rinfo->grid;
indx = gr->indx_inc;
n = indx[1]; /* n = r->ri->grid->indx_inc[1]; */
rinfo->t_in = rinfo->t_out;
rinfo->index3D[rinfo->exit_plane] += r->indx_inc3D[rinfo->exit_plane];
rinfo->entry_plane = rinfo->exit_plane;
if (rinfo->index3D[rinfo->exit_plane] < 0 || rinfo->index3D[rinfo->exit_plane] >= n)
{
/* Out of range, off the grid. */
return (-1);
}
else
{
/* Within current grid. */
rinfo->d[rinfo->exit_plane] += rinfo->delta[rinfo->exit_plane];
rinfo->index1D += rinfo->indx_inc1D[rinfo->exit_plane];
/* Update exit info. */
small = rinfo->d[0] <= rinfo->d[1] ? 0 : 1;
small = rinfo->d[small] <= rinfo->d[2] ? small : 2;
rinfo->t_out = rinfo->d[small];
rinfo->exit_plane = small;
return (rinfo->index1D);
}
}
/*
* NAME
* next_voxel -
*
* SYNOPSIS
* INT next_voxel(r)
* RAY *r;
*
* DESCRIPTION
* Next_voxel() may move up or down in the heirarchy and make appropriate
* adjustments to the stack. The routine returns the index1D of the next
* voxel. An index == -1 indicates the ray exited the space. Assume
* that ray & RAYINFO are all initialized and that the current point
* along the ray is in the cell indentified by index1D & index3D[3]. The
* corresponding grid and RAYINFO are on the top of their stacks resp.
*
* RETURNS
*
*/
INT next_voxel(r)
RAY *r;
{
INT indx;
GRID *gr;
VOXEL *v;
RAYINFO *rinfo;
while ((indx = step_grid(r)) == -1)
{
/* Step carried off grid. */
rinfo = r->ri;
gr = rinfo->grid;
if (gr->subdiv_level != 0)
{
pop_up_a_grid(r);
indx = r->ri->index1D;
}
else
{
/* Top level & off grid. */
/* Exited world space. */
return (-1);
}
}
return (indx);
}
/*
* NAME
* next_nonempty_voxel -
*
* SYNOPSIS
* VOXEL *next_nonempty_voxel(r)
* RAY *r;
*
* DESCRIPTION
* Next_nonempty_voxel() may move up or down in the heirarchy and make
* appropriate adjustments to the stack. The routine returns a pointer
* to the next nonempty voxel. A zero pointer indicates the ray exited
* the space. Assume that ray & RAYINFO are all initialized and that the
* current point along the ray is in the cell indentified by index1D &
* index3D[3]. The corresponding grid and RAYINFO are on the top of
* their stacks resp.
*
* RETURNS
* A pointer to the next nonempty voxel.
*/
VOXEL *next_nonempty_voxel(r)
RAY *r;
{
INT indx;
VOXEL *v;
GRID *gr;
RAYINFO *rinfo;
indx = next_voxel(r);
if (indx < 0)
return (NULL);
rinfo = r->ri;
gr = rinfo->grid;
while (lookup_emptycells(indx, gr) == EMPTY)
{
indx = next_voxel(r);
if (indx < 0) {
return (NULL);
}
rinfo = r->ri;
gr = rinfo->grid;
}
/* Found a nonempty cell. */
v = lookup_hashtable(indx, gr);
return (v);
}
/*
* NAME
* next_nonempty_leaf -
*
* SYNOPSIS
* VOXEL *next_nonempty_leaf(r , step, status)
* RAY *r;
* INT step;
* INT *status;
*
* Step = 1 implies step & move up/down in the heirarchy to a leaf node.
* Step = 0 implies move through the heirarchy to a nonempty leaf node,
* stepping to the next voxel if necessary.
*
* DESCRIPTION
* Next_nonempty_leaf steps to the next nonempty leaf node moving up or
* down in the heirarchy as required and makes appropriate adjustments to
* the stack. If the routine is called with step = 0, then the ray must
* be in a nonempty voxel to begin with and will move to the first
* nonempty leaf without stepping if possible.
*
* The routine returns a pointer to the next nonempty voxel. A zero
* pointer indicates that a nonempty voxel was not found and the status
* word can be consulted to determine why. If the voxel is in a remote
* cluster, the ray is sent to that cluster and NULL is returned.
*
* Assume that ray & RAYINFO are all initialized and that the current
* point along the ray is in the cell indentified by index1D &
* index3D[3]. The corresponding RAYINFO are on the top of their stacks
* resp.
*
* RETURNS
* Nothing.
*/
VOXEL *next_nonempty_leaf(r , step, status)
RAY *r;
INT step;
INT *status;
{
INT indx;
GRID *ng;
VOXEL *v;
RAYINFO *rinfo;
if (step == STEP)
v = next_nonempty_voxel(r);
else
{
/* Only used by init_ray when step == 0. */
rinfo = r->ri;
v = lookup_hashtable(rinfo->index1D, rinfo->grid);
}
if (v == NULL)
{
*status = EXITED_WORLD;
return (v);
}
/* Nonempty voxel either a GRID or a nonempty leaf. */
while (v->celltype == REMOTE_GRID || v->celltype == GSM_GRID)
{
if (v->celltype == REMOTE_GRID)
{
send_ray(r, v);
*status = SENT_TO_REMOTE;
return (NULL);
}
push_down_grid(r, v);
rinfo = r->ri;
indx = rinfo->index1D;
if (lookup_emptycells(indx, rinfo->grid) != EMPTY)
{
v = lookup_hashtable(indx, rinfo->grid);
if (v->celltype != REMOTE_GRID && v->celltype != GSM_GRID)
{
/* Nonempty leaf. */
if (v->celltype == REMOTE_VOXEL)
{
send_ray(r, v);
*status = SENT_TO_REMOTE;
return (NULL);
}
return (v);
}
/* Nonempty grid so do another while loop. */
}
else
{
v = next_nonempty_leaf(r, STEP, status);
return (v);
}
}
return (v);
}
/*
* NAME
* init_ray -
*
* SYNOPSIS
* VOXEL *init_ray(r, g)
* RAY *r;
* GRID *g; // World grid.
*
* DESCRIPTION
* Add rayinfo to a ray & intersect with world grid and find the initial
* nonempty leaf voxel.
*
* RETURNS
* If the ray hits a nonempty leaf voxel a pointer to that voxel is
* returned, otherwise NULL is returned.
*/
VOXEL *init_ray(r, g)
RAY *r;
GRID *g;
{
INT status;
INT indx, grid_id;
INT i_in, i_out, i, il, ih;
REAL t_in, t_out, tl, th;
REAL t[6];
VOXEL *v;
GRID *gr;
RAYINFO *ri;
reset_rayinfo(r);
if (r->D[0] == 0.0)
{
if (r->P[0] >= g->min[0] && r->P[0] <= g->min[0] + g->cellsize[0])
t[0] = -HUGE_REAL;
else
t[0] = HUGE_REAL;
}
else
t[0] = (g->min[0] - r->P[0]) / r->D[0];
if (r->D[1] == 0.0)
{
if (r->P[1] >= g->min[1] && r->P[1] <= g->min[1] + g->cellsize[1])
t[1] = -HUGE_REAL;
else
t[1] = HUGE_REAL;
}
else
t[1] = (g->min[1] - r->P[1]) / r->D[1];
if (r->D[2] == 0.0)
{
if (r->P[2] >= g->min[2] && r->P[2] <= g->min[2] + g->cellsize[2])
t[2] = -HUGE_REAL;
else
t[2] = HUGE_REAL;
}
else
t[2] = (g->min[2] - r->P[2]) / r->D[2];
if (r->D[0] == 0.0)
{
if (r->P[0] >= g->min[0] && r->P[0] <= g->min[0] + g->cellsize[0])
t[3] = HUGE_REAL;
else
t[3] = HUGE_REAL;
}
else
t[3] = (g->min[0] + g->cellsize[0] - r->P[0]) / r->D[0];
if (r->D[1] == 0.0)
{
if (r->P[1] >= g->min[1] && r->P[1] <= g->min[1] + g->cellsize[1])
t[4] = HUGE_REAL;
else
t[4] = HUGE_REAL;
}
else
t[4] = (g->min[1] + g->cellsize[1] - r->P[1]) / r->D[1];
if (r->D[2] == 0.0)
{
if (r->P[2] >= g->min[2] && r->P[2] <= g->min[2] + g->cellsize[2])
t[5] = HUGE_REAL;
else
t[5] = HUGE_REAL;
}
else
t[5] = (g->min[2] + g->cellsize[2] - r->P[2]) / r->D[2];
t_in = -HUGE_REAL;
i_in = -1;
t_out = HUGE_REAL;
i_out = -1;
for (i = 0 ; i < 3; i++)
{
if (t[i] < t[i + 3])
{
tl = t[i];
il = i;
th = t[i + 3];
ih = i+3;
}
else
{
tl = t[i + 3];
il = i + 3;
th = t[i];
ih = i;
}
if (t_in < tl) /* max min */
{
t_in = tl;
i_in = il;
}
if (t_out > th) /* min max */
{
t_out = th;
i_out = ih;
}
}
if (t_in >= t_out || t_out < 0.0)
{
return (NULL); /* Ray missed world cube. */
}
ri = ma_rayinfo(r);
r->ri = ri;
ri->grid = g;
/* Store exit t[]s. */
if (t[0] >= t[3])
ri->d[0] = t[0];
else
ri->d[0] = t[3];
if (t[1] >= t[4])
ri->d[1] = t[1];
else
ri->d[1] = t[4];
if (t[2] >= t[5])
ri->d[2] = t[2];
else
ri->d[2] = t[5];
if (i_in > 2)
i_in -= 3;
if (i_out > 2)
i_out -= 3;
ri->entry_plane = i_in;
ri->t_in = t_in;
ri->t_out = t_out;
ri->exit_plane = i_out;
ri->delta[0] = r->D[0] == 0.0 ? HUGE_REAL : g->cellsize[0] / ABS(r->D[0]);
ri->delta[1] = r->D[1] == 0.0 ? HUGE_REAL : g->cellsize[1] / ABS(r->D[1]);
ri->delta[2] = r->D[2] == 0.0 ? HUGE_REAL : g->cellsize[2] / ABS(r->D[2]);
ri->index3D[0] = 0;
ri->index3D[1] = 0;
ri->index3D[2] = 0;
r->indx_inc3D[0] = r->D[0] >= 0.0 ? 1 : -1;
r->indx_inc3D[1] = r->D[1] >= 0.0 ? 1 : -1;
r->indx_inc3D[2] = r->D[2] >= 0.0 ? 1 : -1;
ri->index1D = 0;
ri->indx_inc1D[0] = r->D[0] >= 0.0 ? 1 : -1;
ri->indx_inc1D[1] = r->D[1] >= 0.0 ? 1 : -1;
ri->indx_inc1D[2] = r->D[2] >= 0.0 ? 1 : -1;
ri->next = NULL;
/*
* At this point the ray is in the top grid in the hierarchy it
* must now descend to a leaf cell.
*/
if ((v = next_nonempty_leaf(r, NO_STEP, &status)) != NULL)
{
ri = r->ri;
indx = ri->index1D;
gr = ri->grid;
grid_id = gr->id;
}
else
{
return (NULL);
}
return (v);
}