home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
GRAPHICS
/
rayshade.lzh
/
hf.c
< prev
next >
Wrap
Text File
|
1990-05-08
|
18KB
|
711 lines
/*
* hf.c
*
* Copyright (C) 1989, Craig E. Kolb
*
* This software may be freely copied, modified, and redistributed,
* provided that this copyright notice is preserved on all copies.
*
* There is no warranty or other guarantee of fitness for this software,
* it is provided solely . Bug reports or fixes may be sent
* to the author, who may or may not act on them as he desires.
*
* You may not include this software in a program or other software product
* without supplying the source, or without informing the end-user that the
* source is available for no extra charge.
*
* If you modify this software, you should include a notice giving the
* name of the person performing the modification, the date of modification,
* and the reason for such modification.
*
* $Id: hf.c,v 3.0.1.4 90/04/04 14:52:30 craig Exp $
*
* $Log: hf.c,v $
* Revision 3.0.1.4 90/04/04 14:52:30 craig
* patch5: Lint removal.
*
* Revision 3.0.1.3 90/02/12 13:30:14 craig
* patch4: Fixed bug involving computation of hf->lsize[i]
*
* Revision 3.0.1.2 89/12/06 16:33:23 craig
* patch2: Added calls to new error/warning routines.
*
* Revision 3.0.1.1 89/11/16 20:44:28 craig
* patch1: Fixed typos in code comments.
*
* Revision 3.0 89/10/27 02:05:51 craig
* Baseline for first official release.
*
*/
#include <stdio.h>
#include <math.h>
#include "typedefs.h"
#include "funcdefs.h"
#include "constants.h"
/*
* Any height values <= HF_UNSET is not considered to be part of the
* height field. Any trianges containing such a vertex will not be
* rendered. This allows one to render non-square height fields.
*/
#define HF_UNSET (-1000.)
/*
* Number of datapoints in a single cell. If you've got loads of memory,
* decrease this number. The 'optimal' number is quite system-dependent,
* but something around 20 seems to work well. For systems which don't
* have much memory, this constant should be greater than or equal to
* the largest height field which will be rendered, thus making the
* algorithm non-hierarchical.
*/
#define BESTSIZE 16
/*
* Size of triangle cache.
*/
#define CACHESIZE 6
/*
* Used to differentiate between the two triangles used to represent a cell:
* a------d
* |\ |
* | \TRI2| TRI2 == c-->d-->a-->c
* | \ |
* | \ |
* | \ |
* |TRI1 \| TRI1 == c-->a-->b-->c
* b------c
*/
#define TRI1 1
#define TRI2 2
double DDA2D(), CheckCell(), intHftri();
float minalt(), maxalt();
typedef struct {
int stepX, stepY;
double tDX, tDY;
float minz, maxz;
int outX, outY;
Vector cp, pDX, pDY;
} Trav2D;
hfTri *CreateHfTriangle(), *GetQueuedTri();
Object *
makhf(surf, filename)
char *surf, *filename;
{
Hf *hf;
Object *newobj;
Primitive *prim;
FILE *fp;
float val, *maxptr, *minptr;
int i, j;
fp = fopen(filename, "r");
if (fp == (FILE *)NULL)
yyerror("Cannot open heightfield file \"%s\".", filename);
prim = mallocprim();
newobj = new_object(NULL, HF, (char *)prim, (Trans *)NULL);
prim->type = HF;
prim->surf = find_surface(surf);
hf = (Hf *)Malloc(sizeof(Hf));
prim->objpnt.p_hf = hf;
/*
* Make the following an option someday.
*/
hf->BestSize = BESTSIZE;
/*
* Store the inverse for faster computation.
*/
hf->iBestSize = 1. / (float)hf->BestSize;
/*
* Get HF size.
*/
if (fread((char *)&hf->size, sizeof(int), 1, fp) == 0)
yyerror("Cannot read height field size.");
hf->data = (float **)share_malloc(hf->size * sizeof(float *));
for (i = 0; i < hf->size; i++) {
hf->data[i] = (float *)share_malloc(hf->size * sizeof(float));
/*
* Read in row of HF data.
*/
if (fread((char *)hf->data[i],sizeof(float),hf->size,fp) != hf->size)
yyerror("Not enough heightfield data.");
for (j = 0; j < hf->size; j++) {
val = hf->data[i][j];
if (val <= HF_UNSET) {
hf->data[i][j] = HF_UNSET;
/*
* Don't include the point in min/max
* calculations.
*/
continue;
}
if (val > hf->maxz)
hf->maxz = val;
if (val < hf->minz)
hf->minz = val;
}
}
(void)fclose(fp);
/*
* Allocate levels of grid. hf->levels = log base BestSize of hf->size
*/
for (i = hf->size, hf->levels = 0; i > hf->BestSize; i /= hf->BestSize,
hf->levels++)
;
hf->levels++;
hf->qsize = CACHESIZE;
hf->q = (hfTri **)Calloc((unsigned)hf->qsize, sizeof(hfTri *));
hf->qtail = 0;
hf->lsize = (int *)share_malloc(hf->levels * sizeof(int));
hf->spacing = (float *)share_malloc(hf->levels * sizeof(float));
hf->boundsmax = (float ***)share_malloc(hf->levels * sizeof(float **));
hf->boundsmin = (float ***)share_malloc(hf->levels * sizeof(float **));
hf->spacing[0] = hf->size -1;
hf->lsize[0] = (int)hf->spacing[0];
hf->boundsmax[0] = (float **)share_malloc(hf->lsize[0]*sizeof(float *));
hf->boundsmin[0] = (float **)share_malloc(hf->lsize[0]*sizeof(float *));
/*
* Compute initial bounding boxes
*/
for (i = 0; i < hf->lsize[0]; i++) {
hf->boundsmax[0][i]=(float *)share_malloc(hf->lsize[0]*sizeof(float));
hf->boundsmin[0][i]=(float *)share_malloc(hf->lsize[0]*sizeof(float));
maxptr = hf->boundsmax[0][i];
minptr = hf->boundsmin[0][i];
for (j = 0; j < hf->lsize[0]; j++) {
*maxptr++ = maxalt(i, j, hf->data) + EPSILON;
*minptr++ = minalt(i, j, hf->data) - EPSILON;
}
}
for (i = 1; i < hf->levels; i++) {
hf->spacing[i] = hf->spacing[i-1] * hf->iBestSize;
hf->lsize[i] = (int)hf->spacing[i];
if ((double)hf->lsize[i] != hf->spacing[i])
hf->lsize[i]++;
hf->boundsmax[i]=(float **)share_malloc(hf->lsize[i]*sizeof(float *));
hf->boundsmin[i]=(float **)share_malloc(hf->lsize[i]*sizeof(float *));
for (j = 0; j < hf->lsize[i]; j++) {
hf->boundsmax[i][j] = (float *)share_malloc(hf->lsize[i] *
sizeof(float));
hf->boundsmin[i][j] = (float *)share_malloc(hf->lsize[i] *
sizeof(float));
}
integrate_grid(hf, i);
}
hf->boundbox[LOW][X] = hf->boundbox[LOW][Y] = 0;
hf->boundbox[HIGH][X] = hf->boundbox[HIGH][Y] = 1;
hf->boundbox[LOW][Z] = hf->minz;
hf->boundbox[HIGH][Z] = hf->maxz;
return newobj;
}
/*
* Intersect ray with height field.
*/
double
inthf(pos, ray, obj)
Vector *pos, *ray;
Primitive *obj;
{
Hf *hf;
Ray tmpray;
Vector hitpos;
double offset, dist;
Trav2D trav;
extern unsigned long primtests[];
primtests[HF]++;
hf = obj->objpnt.p_hf;
/*
* Find where we hit the hf cube.
*/
if (OutOfBounds(pos, hf->boundbox)) {
tmpray.pos = *pos;
tmpray.dir = *ray;
offset = IntBounds(&tmpray, hf->boundbox);
if (offset <= 0.)
/* Never hit hf. */
return 0.;
hitpos.x = pos->x + ray->x * offset;
hitpos.y = pos->y + ray->y * offset;
hitpos.z = pos->z + ray->z * offset;
} else
hitpos = *pos;
/*
* Find out in which cell "hitpoint" is.
*/
if (equal(hitpos.x, 1.))
hitpos.x -= EPSILON;
if (equal(hitpos.y, 1.))
hitpos.y -= EPSILON;
if (ray->x < 0.) {
trav.stepX = trav.outX = -1;
trav.tDX = -1. / (ray->x * hf->spacing[hf->levels -1]);
} else if (ray->x > 0.) {
trav.stepX = 1;
trav.outX = hf->lsize[hf->levels -1];
/*
* (1./size) / ray
*/
trav.tDX = 1. / (ray->x * hf->spacing[hf->levels -1]);
}
if (ray->y < 0.) {
trav.stepY = trav.outY = -1;
trav.tDY = -1. / (ray->y * hf->spacing[hf->levels -1]);
} else if (ray->y > 0.) {
trav.stepY = 1;
trav.outY = hf->lsize[hf->levels -1];
trav.tDY = 1. / (ray->y * hf->spacing[hf->levels -1]);
}
trav.pDX.x = ray->x * trav.tDX;
trav.pDX.y = ray->y * trav.tDX;
trav.pDX.z = ray->z * trav.tDX;
trav.pDY.x = ray->x * trav.tDY;
trav.pDY.y = ray->y * trav.tDY;
trav.pDY.z = ray->z * trav.tDY;
trav.cp = hitpos;
trav.minz = hf->minz;
trav.maxz = hf->maxz;
dist = DDA2D(hf, pos, ray, hf->levels -1, &trav);
return dist;
}
/*
* Traverse the grid using a modified DDA algorithm. If the extent of
* the ray over a cell intersects the bounding volume defined by the
* four corners of the cell, either recurse or perform ray/surface
* intersection test.
*/
double
DDA2D(hf, pos, ray, level, trav)
Hf *hf;
Vector *pos, *ray;
int level;
Trav2D *trav;
{
int x, y, size, posZ;
float **boundsmin, **boundsmax, spacing;
double dist, tX, tY;
Trav2D newtrav;
Vector nxp, nyp;
size = hf->lsize[level];
spacing = hf->spacing[level];
posZ = (ray->z > 0.);
x = trav->cp.x * hf->spacing[level];
y = trav->cp.y * hf->spacing[level];
boundsmax = hf->boundsmax[level];
boundsmin = hf->boundsmin[level];
if (trav->outX > size) trav->outX = size;
if (trav->outY > size) trav->outY = size;
if (trav->outX < 0) trav->outX = -1;
if (trav->outY < 0) trav->outY = -1;
if (ray->x < 0.) {
tX = (x /spacing - trav->cp.x) / ray->x;
} else if (ray->x > 0.)
tX = ((x+1)/spacing - trav->cp.x) / ray->x;
else
tX = FAR_AWAY;
if (ray->y < 0.) {
tY = (y /spacing - trav->cp.y) / ray->y;
} else if (ray->y > 0.)
tY = ((y+1)/spacing - trav->cp.y) / ray->y;
else
tY = FAR_AWAY;
nxp.x = trav->cp.x + tX * ray->x;
nxp.y = trav->cp.y + tX * ray->y;
nxp.z = trav->cp.z + tX * ray->z;
nyp.x = trav->cp.x + tY * ray->x;
nyp.y = trav->cp.y + tY * ray->y;
nyp.z = trav->cp.z + tY * ray->z;
do {
if (tX < tY) {
if ((posZ && trav->cp.z <= boundsmax[y][x] &&
nxp.z >= boundsmin[y][x]) ||
(!posZ && trav->cp.z >= boundsmin[y][x] &&
nxp.z <= boundsmax[y][x])) {
if (level) {
/*
* Recurve -- compute constants
* needed for next level.
* Nicely enough, this just
* involves a few multiplications.
*/
newtrav = *trav;
newtrav.tDX *= hf->iBestSize;
newtrav.tDY *= hf->iBestSize;
newtrav.maxz = boundsmax[y][x];
newtrav.minz = boundsmin[y][x];
if (ray->x < 0.)
newtrav.outX=hf->BestSize*x-1;
else
newtrav.outX=hf->BestSize*(x+1);
if (ray->y < 0.)
newtrav.outY=hf->BestSize*y-1;
else
newtrav.outY=hf->BestSize*(y+1);
newtrav.pDX.x *= hf->iBestSize;
newtrav.pDX.y *= hf->iBestSize;
newtrav.pDX.z *= hf->iBestSize;
newtrav.pDY.x *= hf->iBestSize;
newtrav.pDY.y *= hf->iBestSize;
newtrav.pDY.z *= hf->iBestSize;
dist = DDA2D(hf,pos,ray,level-1,
&newtrav);
} else
dist = CheckCell(x,y,hf,ray,pos);
if (dist > EPSILON)
return dist;
}
x += trav->stepX; /* Move in X */
if (x == trav->outX) /* If outside, quit */
return 0.;
tX += trav->tDX; /* Update position on ray */
trav->cp = nxp; /* cur pos gets next pos */
nxp.x += trav->pDX.x; /* Compute next pos */
nxp.y += trav->pDX.y;
nxp.z += trav->pDX.z;
} else {
if ((posZ && trav->cp.z <= boundsmax[y][x] &&
nyp.z >= boundsmin[y][x]) ||
(!posZ && trav->cp.z >= boundsmin[y][x] &&
nyp.z <= boundsmax[y][x])) {
if (level) {
/* Recurse */
newtrav = *trav;
newtrav.tDX *= hf->iBestSize;
newtrav.tDY *= hf->iBestSize;
newtrav.maxz = boundsmax[y][x];
newtrav.minz = boundsmin[y][x];
if (ray->x < 0.)
newtrav.outX=hf->BestSize*x-1;
else
newtrav.outX=hf->BestSize*(x+1);
if (ray->y < 0.)
newtrav.outY=hf->BestSize*y-1;
else
newtrav.outY=hf->BestSize*(y+1);
newtrav.pDX.x *= hf->iBestSize;
newtrav.pDX.y *= hf->iBestSize;
newtrav.pDX.z *= hf->iBestSize;
newtrav.pDY.x *= hf->iBestSize;
newtrav.pDY.y *= hf->iBestSize;
newtrav.pDY.z *= hf->iBestSize;
dist = DDA2D(hf,pos,ray,level-1,
&newtrav);
} else
dist = CheckCell(x,y,hf,ray,pos);
if (dist > EPSILON)
return dist;
}
y += trav->stepY;
if (y == trav->outY)
return 0.;
tY += trav->tDY;
trav->cp = nyp;
nyp.x += trav->pDY.x;
nyp.y += trav->pDY.y;
nyp.z += trav->pDY.z;
}
} while ((trav->cp.x <= 1. && trav->cp.y <= 1.) &&
((posZ && trav->cp.z <= trav->maxz) ||
(!posZ && trav->cp.z >= trav->minz)));
/*
* while ((we're inside the horizontal bounding box)
* (usually caught by outX & outY, but
* it's possible to go "too far" due to
* the fact that our levels of grids do
* not "nest" exactly if gridsize%BestSize != 0)
* and
* ((if ray->z is positive and we haven't gone through
* the upper bounding plane) or
* (if ray->z is negative and we haven't gone through
* the lower bounding plane)));
*/
return 0.;
}
/*
* Check for ray/cell intersection
*/
double
CheckCell(x, y, hf, ray, pos)
int x, y;
Hf *hf;
Vector *ray, *pos;
{
hfTri *tri1, *tri2;
double d1, d2;
d1 = d2 = FAR_AWAY;
if (tri1 = CreateHfTriangle(hf, x, y, x+1, y, x, y+1, TRI1))
d1 = intHftri(ray, pos, tri1);
if (tri2 = CreateHfTriangle(hf, x+1, y, x+1, y+1, x, y+1, TRI2))
d2 = intHftri(ray, pos, tri2);
if (d1 == FAR_AWAY && d2 == FAR_AWAY)
return 0;
if (d1 < d2) {
hf->hittri = *tri1;
return d1;
}
hf->hittri = *tri2;
return d2;
}
hfTri *
CreateHfTriangle(hf, x1, y1, x2, y2, x3, y3, which)
Hf *hf;
int x1, y1, x2, y2, x3, y3, which;
{
hfTri *tri;
double xid, yid;
Vector tmp1, tmp2;
/*
* Don't use triangles with "unset" vertices.
*/
if (hf->data[y1][x1] == HF_UNSET ||
hf->data[y2][x2] == HF_UNSET ||
hf->data[y3][x3] == HF_UNSET)
return (hfTri *)0;
xid = (double)x1 / (double)(hf->size -1);
yid = (double)y1 / (double)(hf->size -1);
if ((tri = GetQueuedTri(hf, xid, yid, which)) != (hfTri *)0)
return tri;
tri = (hfTri *)Malloc(sizeof(hfTri));
tri->type = which;
tri->v1.x = xid;
tri->v1.y = yid;
tri->v1.z = hf->data[y1][x1];
tri->v2.x = (double)x2 / (double)(hf->size-1);
tri->v2.y = (double)y2 / (double)(hf->size-1);
tri->v2.z = hf->data[y2][x2];
tri->v3.x = (double)x3 / (double)(hf->size-1);
tri->v3.y = (double)y3 / (double)(hf->size-1);
tri->v3.z = hf->data[y3][x3];
tmp1.x = tri->v2.x - tri->v1.x;
tmp1.y = tri->v2.y - tri->v1.y;
tmp1.z = tri->v2.z - tri->v1.z;
tmp2.x = tri->v3.x - tri->v1.x;
tmp2.y = tri->v3.y - tri->v1.y;
tmp2.z = tri->v3.z - tri->v1.z;
(void)crossp(&tri->norm, &tmp1, &tmp2);
tri->d = -dotp(&tri->v1, &tri->norm);
QueueTri(hf, tri);
return tri;
}
/*
* Intersect ray with right isoscoles triangle, the hypotenuse of which
* has slope of -1.
*/
double
intHftri(ray, pos, tri)
hfTri *tri;
Vector *pos, *ray;
{
double u, v, dist, xpos, ypos;
u = dotp(&tri->norm, pos) + tri->d;
v = dotp(&tri->norm, ray);
if ((u <= 0. || v > -EPSILON) && (u >= 0. && v < EPSILON))
return FAR_AWAY;
dist = -u / v;
if (dist < EPSILON)
return FAR_AWAY;
xpos = pos->x + dist*ray->x;
ypos = pos->y + dist*ray->y;
if (tri->type == TRI1 && xpos >= tri->v1.x && ypos >= tri->v1.y &&
xpos + ypos <= tri->v2.x + tri->v2.y)
return dist;
if (tri->type == TRI2 && xpos <= tri->v2.x && ypos <= tri->v2.y &&
xpos + ypos >= tri->v1.x + tri->v1.y)
return dist;
return FAR_AWAY;
}
/*
* Compute normal to height field.
*/
/*ARGSUSED*/
nrmhf(pos, prim, nrm)
Vector *pos, *nrm;
Primitive *prim;
{
*nrm = prim->objpnt.p_hf->hittri.norm;
}
/*
* Compute heightfield bounding box.
*/
hfextent(obj, bounds)
Primitive *obj;
double bounds[2][3];
{
Hf *hf = obj->objpnt.p_hf;
/*
* By default, height fields are centered at (0.5, 0.5, 0.)
*/
bounds[LOW][X] = bounds[LOW][Y] = 0;
bounds[HIGH][X] = bounds[HIGH][Y] = 1;
bounds[LOW][Z] = hf->minz;
bounds[HIGH][Z] = hf->maxz;
}
/*
* Build min/max altitude value arrays for the given grid level.
*/
integrate_grid(hf, level)
Hf *hf;
int level;
{
int i, j, k, l, ii, ji;
float max_alt, min_alt;
float **maxinto, **mininto, **frommax, **frommin, *minptr, *maxptr;
int insize, fromsize, fact;
maxinto = hf->boundsmax[level];
mininto = hf->boundsmin[level];
insize = hf->lsize[level];
frommax = hf->boundsmax[level-1];
frommin = hf->boundsmin[level-1];
fact = hf->BestSize;
fromsize = hf->lsize[level-1];
ii = 0;
for (i = 0; i < insize; i++) {
ji = 0;
for (j = 0; j < insize; j++) {
max_alt = HF_UNSET;
min_alt = -HF_UNSET;
for (k = 0; k <= fact; k++) {
if (ii+k >= fromsize)
continue;
maxptr = &frommax[ii+k][ji];
minptr = &frommin[ii+k][ji];
for (l = 0; l <= fact; l++,maxptr++,minptr++) {
if (ji+l >= fromsize)
continue;
if (*maxptr > max_alt)
max_alt = *maxptr;
if (*minptr < min_alt)
min_alt = *minptr;
}
}
maxinto[i][j] = max_alt + EPSILON;
mininto[i][j] = min_alt - EPSILON;
ji += fact;
}
ii += fact;
}
}
/*
* Place the given triangle in the triangle cache.
*/
QueueTri(hf, tri)
Hf *hf;
hfTri *tri;
{
if (hf->q[hf->qtail]) /* Free old triangle data */
free((char *)hf->q[hf->qtail]);
hf->q[hf->qtail] = tri; /* Put on tail */
hf->qtail = (hf->qtail + 1) % hf->qsize; /* Increment tail */
}
/*
* Search list of cached trianges to see if this triangle has been
* cached. If so, return a pointer to it. If not, return null pointer.
*/
hfTri *
GetQueuedTri(hf, x, y, flag)
Hf *hf;
double x, y;
int flag;
{
register int i;
register hfTri **tmp;
for (i = 0, tmp = hf->q; i < hf->qsize; i++, tmp++) {
if (*tmp && (*tmp)->v1.x == x && (*tmp)->v1.y == y &&
(*tmp)->type == flag)
return *tmp; /* vertices & flag match, return it */
}
return (hfTri *)0;
}
/*
* Return maximum height of cell indexed by y,x. This could be done
* as a macro, but many C compliers will choke on it.
*/
float
minalt(y,x,data)
int x, y;
float **data;
{
float min_alt;
min_alt = min(data[y][x], data[y+1][x]);
min_alt = min(min_alt, data[y][x+1]);
min_alt = min(min_alt, data[y+1][x+1]);
return min_alt;
}
/*
* Return maximum cell height, as above.
*/
float
maxalt(y,x,data)
int x, y;
float **data;
{
float max_alt;
max_alt = max(data[y][x], data[y+1][x]);
max_alt = max(max_alt, data[y][x+1]);
max_alt = max(max_alt, data[y+1][x+1]);
return max_alt;
}