#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'libray/libobj/hf.c' <<'END_OF_FILE' X/* X * hf.c X * X * Copyright (C) 1989, 1991, Craig E. Kolb X * All rights reserved. X * X * This software may be freely copied, modified, and redistributed X * provided that this copyright notice is preserved on all copies. X * X * You may not distribute this software, in whole or in part, as part of X * any commercial product without the express consent of the authors. X * X * There is no warranty or other guarantee of fitness of this software X * for any purpose. It is provided solely "as is". X * X * $Id: hf.c,v 4.0 91/07/17 14:38:15 kolb Exp Locker: kolb $ X * X * $Log: hf.c,v $ X * Revision 4.0 91/07/17 14:38:15 kolb X * Initial version. X * X */ X#include "geom.h" X#include "hf.h" X Xstatic Methods *iHfMethods = NULL; Xstatic char hfName[] = "heighfield"; X Xstatic void integrate_grid(), QueueTri(); Xstatic int DDA2D(), CheckCell(); Xstatic Float intHftri(); Xstatic float minalt(), maxalt(); X Xtypedef struct { X int stepX, stepY; X Float tDX, tDY; X float minz, maxz; X int outX, outY; X Vector cp, pDX, pDY; X} Trav2D; X XhfTri *CreateHfTriangle(), *GetQueuedTri(); X Xunsigned long HFTests, HFHits; X XHf * XHfCreate(filename) Xchar *filename; X{ X Hf *hf; X FILE *fp; X float val, *maxptr, *minptr; X int i, j; X X fp = fopen(filename, "r"); X if (fp == (FILE *)NULL) { X RLerror(RL_ABORT, "Cannot open heightfield file \"%s\".", X filename); X return (Hf *)NULL; X } X X hf = (Hf *)Malloc(sizeof(Hf)); X /* X * Make the following an option someday. X */ X hf->BestSize = BESTSIZE; X /* X * Store the inverse for faster computation. X */ X hf->iBestSize = 1. / (float)hf->BestSize; X /* X * Get HF size. X */ X if (fread((char *)&hf->size, sizeof(int), 1, fp) == 0) { X RLerror(RL_ABORT, "Cannot read height field size."); X return (Hf *)NULL; X } X X hf->data = (float **)share_malloc(hf->size * sizeof(float *)); X for (i = 0; i < hf->size; i++) { X hf->data[i] = (float *)share_malloc(hf->size * sizeof(float)); X /* X * Read in row of HF data. X */ X if (fread((char *)hf->data[i],sizeof(float),hf->size,fp) X != hf->size) { X RLerror(RL_ABORT, "Not enough heightfield data."); X return (Hf *)NULL; X } X for (j = 0; j < hf->size; j++) { X val = hf->data[i][j]; X if (val <= HF_UNSET) { X hf->data[i][j] = HF_UNSET; X /* X * Don't include the point in min/max X * calculations. X */ X continue; X } X if (val > hf->maxz) X hf->maxz = val; X if (val < hf->minz) X hf->minz = val; X } X } X (void)fclose(fp); X /* X * Allocate levels of grid. hf->levels = log base BestSize of hf->size X */ X for (i = hf->size, hf->levels = 0; i > hf->BestSize; i /= hf->BestSize, X hf->levels++) X ; X hf->levels++; X hf->qsize = CACHESIZE; X hf->q = (hfTri **)Calloc((unsigned)hf->qsize, sizeof(hfTri *)); X hf->qtail = 0; X X hf->lsize = (int *)share_malloc(hf->levels * sizeof(int)); X hf->spacing = (float *)share_malloc(hf->levels * sizeof(float)); X hf->boundsmax = (float ***)share_malloc(hf->levels * sizeof(float **)); X hf->boundsmin = (float ***)share_malloc(hf->levels * sizeof(float **)); X X hf->spacing[0] = hf->size -1; X hf->lsize[0] = (int)hf->spacing[0]; X hf->boundsmax[0] = (float **)share_malloc(hf->lsize[0]*sizeof(float *)); X hf->boundsmin[0] = (float **)share_malloc(hf->lsize[0]*sizeof(float *)); X /* X * Compute initial bounding boxes X */ X for (i = 0; i < hf->lsize[0]; i++) { X hf->boundsmax[0][i]=(float *)share_malloc(hf->lsize[0]*sizeof(float)); X hf->boundsmin[0][i]=(float *)share_malloc(hf->lsize[0]*sizeof(float)); X maxptr = hf->boundsmax[0][i]; X minptr = hf->boundsmin[0][i]; X for (j = 0; j < hf->lsize[0]; j++) { X *maxptr++ = maxalt(i, j, hf->data) + EPSILON; X *minptr++ = minalt(i, j, hf->data) - EPSILON; X } X } X X for (i = 1; i < hf->levels; i++) { X hf->spacing[i] = hf->spacing[i-1] * hf->iBestSize; X hf->lsize[i] = (int)hf->spacing[i]; X if ((Float)hf->lsize[i] != hf->spacing[i]) X hf->lsize[i]++; X hf->boundsmax[i]=(float **)share_malloc(hf->lsize[i]*sizeof(float *)); X hf->boundsmin[i]=(float **)share_malloc(hf->lsize[i]*sizeof(float *)); X for (j = 0; j < hf->lsize[i]; j++) { X hf->boundsmax[i][j] = (float *)share_malloc(hf->lsize[i] * X sizeof(float)); X hf->boundsmin[i][j] = (float *)share_malloc(hf->lsize[i] * X sizeof(float)); X } X integrate_grid(hf, i); X } X X hf->boundbox[LOW][X] = hf->boundbox[LOW][Y] = 0; X hf->boundbox[HIGH][X] = hf->boundbox[HIGH][Y] = 1; X hf->boundbox[LOW][Z] = hf->minz; X hf->boundbox[HIGH][Z] = hf->maxz; X X return hf; X} X XMethods * XHfMethods() X{ X if (iHfMethods == (Methods *)NULL) { X iHfMethods = MethodsCreate(); X iHfMethods->create = (GeomCreateFunc *)HfCreate; X iHfMethods->methods = HfMethods; X iHfMethods->name = HfName; X iHfMethods->intersect = HfIntersect; X iHfMethods->normal = HfNormal; X iHfMethods->uv = HfUV; X iHfMethods->bounds = HfBounds; X iHfMethods->stats = HfStats; X iHfMethods->checkbounds = TRUE; X iHfMethods->closed = FALSE; X } X return iHfMethods; X} X X/* X * Intersect ray with height field. X */ Xint XHfIntersect(hf, ray, mindist, maxdist) XHf *hf; XRay *ray; XFloat mindist, *maxdist; X{ X Vector hitpos; X Float offset; X Trav2D trav; X X HFTests++; X X /* X * Find where we hit the hf cube. X */ X VecAddScaled(ray->pos, mindist, ray->dir, &hitpos); X if (OutOfBounds(&hitpos, hf->boundbox)) { X offset = *maxdist; X if (!BoundsIntersect(ray, hf->boundbox, mindist, &offset)) X return FALSE; X hitpos.x = ray->pos.x + ray->dir.x * offset; X hitpos.y = ray->pos.y + ray->dir.y * offset; X hitpos.z = ray->pos.z + ray->dir.z * offset; X } else X hitpos = ray->pos; X /* X * Find out in which cell "hitpoint" is. X */ X if (equal(hitpos.x, 1.)) X hitpos.x -= EPSILON; X if (equal(hitpos.y, 1.)) X hitpos.y -= EPSILON; X X if (ray->dir.x < 0.) { X trav.stepX = trav.outX = -1; X trav.tDX = -1. / (ray->dir.x * hf->spacing[hf->levels -1]); X } else if (ray->dir.x > 0.) { X trav.stepX = 1; X trav.outX = hf->lsize[hf->levels -1]; X /* X * (1./size) / ray X */ X trav.tDX = 1. / (ray->dir.x * hf->spacing[hf->levels -1]); X } X X if (ray->dir.y < 0.) { X trav.stepY = trav.outY = -1; X trav.tDY = -1. / (ray->dir.y * hf->spacing[hf->levels -1]); X } else if (ray->dir.y > 0.) { X trav.stepY = 1; X trav.outY = hf->lsize[hf->levels -1]; X trav.tDY = 1. / (ray->dir.y * hf->spacing[hf->levels -1]); X } X X trav.pDX.x = ray->dir.x * trav.tDX; X trav.pDX.y = ray->dir.y * trav.tDX; X trav.pDX.z = ray->dir.z * trav.tDX; X trav.pDY.x = ray->dir.x * trav.tDY; X trav.pDY.y = ray->dir.y * trav.tDY; X trav.pDY.z = ray->dir.z * trav.tDY; X X trav.cp = hitpos; X trav.minz = hf->minz; X trav.maxz = hf->maxz; X if (DDA2D(hf, &ray->pos, &ray->dir, hf->levels -1, &trav, maxdist)) { X HFHits++; X return TRUE; X } X return FALSE; X} X X/* X * Traverse the grid using a modified DDA algorithm. If the extent of X * the ray over a cell intersects the bounding volume defined by the X * four corners of the cell, either recurse or perform ray/surface X * intersection test. X */ Xstatic int XDDA2D(hf, pos, ray, level, trav, maxdist) XHf *hf; XVector *pos, *ray; Xint level; XTrav2D *trav; XFloat *maxdist; X{ X int x, y, size, posZ; X float **boundsmin, **boundsmax, spacing; X Float tX, tY; X Trav2D newtrav; X Vector nxp, nyp; X X size = hf->lsize[level]; X spacing = hf->spacing[level]; X X posZ = (ray->z > 0.); X X x = trav->cp.x * hf->spacing[level]; X if (x == size) X x--; X y = trav->cp.y * hf->spacing[level]; X if (y == size) X y--; X boundsmax = hf->boundsmax[level]; X boundsmin = hf->boundsmin[level]; X X if (trav->outX > size) trav->outX = size; X if (trav->outY > size) trav->outY = size; X if (trav->outX < 0) trav->outX = -1; X if (trav->outY < 0) trav->outY = -1; X X if (ray->x < 0.) { X tX = (x /spacing - trav->cp.x) / ray->x; X } else if (ray->x > 0.) X tX = ((x+1)/spacing - trav->cp.x) / ray->x; X else X tX = FAR_AWAY; X if (ray->y < 0.) { X tY = (y /spacing - trav->cp.y) / ray->y; X } else if (ray->y > 0.) X tY = ((y+1)/spacing - trav->cp.y) / ray->y; X else X tY = FAR_AWAY; X X nxp.x = trav->cp.x + tX * ray->x; X nxp.y = trav->cp.y + tX * ray->y; X nxp.z = trav->cp.z + tX * ray->z; X X nyp.x = trav->cp.x + tY * ray->x; X nyp.y = trav->cp.y + tY * ray->y; X nyp.z = trav->cp.z + tY * ray->z; X X do { X if (tX < tY) { X if ((posZ && trav->cp.z <= boundsmax[y][x] && X nxp.z >= boundsmin[y][x]) || X (!posZ && trav->cp.z >= boundsmin[y][x] && X nxp.z <= boundsmax[y][x])) { X if (level) { X /* X * Recurse -- compute constants X * needed for next level. X * Nicely enough, this just X * involves a few multiplications. X */ X newtrav = *trav; X newtrav.tDX *= hf->iBestSize; X newtrav.tDY *= hf->iBestSize; X newtrav.maxz = boundsmax[y][x]; X newtrav.minz = boundsmin[y][x]; X if (ray->x < 0.) X newtrav.outX=hf->BestSize*x-1; X else X newtrav.outX=hf->BestSize*(x+1); X if (ray->y < 0.) X newtrav.outY=hf->BestSize*y-1; X else X newtrav.outY=hf->BestSize*(y+1); X newtrav.pDX.x *= hf->iBestSize; X newtrav.pDX.y *= hf->iBestSize; X newtrav.pDX.z *= hf->iBestSize; X newtrav.pDY.x *= hf->iBestSize; X newtrav.pDY.y *= hf->iBestSize; X newtrav.pDY.z *= hf->iBestSize; X if (DDA2D(hf,pos,ray,level-1, X &newtrav, maxdist)) X return TRUE; X } else if (CheckCell(x,y,hf,ray,pos,maxdist)) X return TRUE; X } X x += trav->stepX; /* Move in X */ X if (*maxdist < tX || x == trav->outX) X /* If outside, quit */ X return FALSE; X tX += trav->tDX; /* Update position on ray */ X trav->cp = nxp; /* cur pos gets next pos */ X nxp.x += trav->pDX.x; /* Compute next pos */ X nxp.y += trav->pDX.y; X nxp.z += trav->pDX.z; X } else { X if ((posZ && trav->cp.z <= boundsmax[y][x] && X nyp.z >= boundsmin[y][x]) || X (!posZ && trav->cp.z >= boundsmin[y][x] && X nyp.z <= boundsmax[y][x])) { X if (level) { X /* Recurse */ X newtrav = *trav; X newtrav.tDX *= hf->iBestSize; X newtrav.tDY *= hf->iBestSize; X newtrav.maxz = boundsmax[y][x]; X newtrav.minz = boundsmin[y][x]; X if (ray->x < 0.) X newtrav.outX=hf->BestSize*x-1; X else X newtrav.outX=hf->BestSize*(x+1); X if (ray->y < 0.) X newtrav.outY=hf->BestSize*y-1; X else X newtrav.outY=hf->BestSize*(y+1); X newtrav.pDX.x *= hf->iBestSize; X newtrav.pDX.y *= hf->iBestSize; X newtrav.pDX.z *= hf->iBestSize; X newtrav.pDY.x *= hf->iBestSize; X newtrav.pDY.y *= hf->iBestSize; X newtrav.pDY.z *= hf->iBestSize; X if (DDA2D(hf,pos,ray,level-1, X &newtrav, maxdist)) X return TRUE; X } else if (CheckCell(x,y,hf,ray,pos,maxdist)) X return TRUE; X } X y += trav->stepY; X if (*maxdist < tY || y == trav->outY) X return FALSE; X tY += trav->tDY; X trav->cp = nyp; X nyp.x += trav->pDY.x; X nyp.y += trav->pDY.y; X nyp.z += trav->pDY.z; X } X } while ((trav->cp.x <= 1. && trav->cp.y <= 1.) && X ((posZ && trav->cp.z <= trav->maxz) || X (!posZ && trav->cp.z >= trav->minz))); X X /* X * while ((we're inside the horizontal bounding box) X * (usually caught by outX & outY, but X * it's possible to go "too far" due to X * the fact that our levels of grids do X * not "nest" exactly if gridsize%BestSize != 0) X * and X * ((if ray->z is positive and we haven't gone through X * the upper bounding plane) or X * (if ray->z is negative and we haven't gone through X * the lower bounding plane))); X */ X X return FALSE; X} X X/* X * Check for ray/cell intersection X */ Xstatic int XCheckCell(x, y, hf, ray, pos, maxdist) Xint x, y; XHf *hf; XVector *ray, *pos; XFloat *maxdist; X{ X hfTri *tri1, *tri2; X Float d1, d2; X X d1 = d2 = FAR_AWAY; X X if (tri1 = CreateHfTriangle(hf, x, y, x+1, y, x, y+1, TRI1)) X d1 = intHftri(ray, pos, tri1); X if (tri2 = CreateHfTriangle(hf, x+1, y, x+1, y+1, x, y+1, TRI2)) X d2 = intHftri(ray, pos, tri2); X X if (d1 == FAR_AWAY && d2 == FAR_AWAY) X return FALSE; X X if (d1 < d2) { X if (d1 < *maxdist) { X hf->hittri = *tri1; X *maxdist = d1; X return TRUE; X } X return FALSE; X } X X if (d2 < *maxdist) { X hf->hittri = *tri2; X *maxdist = d2; X return TRUE; X } X return FALSE; X} X Xstatic hfTri * XCreateHfTriangle(hf, x1, y1, x2, y2, x3, y3, which) XHf *hf; Xint x1, y1, x2, y2, x3, y3, which; X{ X hfTri *tri; X Float xid, yid; X Vector tmp1, tmp2; X X /* X * Don't use triangles with "unset" vertices. X */ X if (hf->data[y1][x1] == HF_UNSET || X hf->data[y2][x2] == HF_UNSET || X hf->data[y3][x3] == HF_UNSET) X return (hfTri *)0; X X xid = (Float)x1 / (Float)(hf->size -1); X yid = (Float)y1 / (Float)(hf->size -1); X X if ((tri = GetQueuedTri(hf, xid, yid, which)) != (hfTri *)0) X return tri; X X tri = (hfTri *)Malloc(sizeof(hfTri)); X X tri->type = which; X tri->v1.x = xid; X tri->v1.y = yid; X tri->v1.z = hf->data[y1][x1]; X tri->v2.x = (Float)x2 / (Float)(hf->size-1); X tri->v2.y = (Float)y2 / (Float)(hf->size-1); X tri->v2.z = hf->data[y2][x2]; X tri->v3.x = (Float)x3 / (Float)(hf->size-1); X tri->v3.y = (Float)y3 / (Float)(hf->size-1); X tri->v3.z = hf->data[y3][x3]; X X tmp1.x = tri->v2.x - tri->v1.x; X tmp1.y = tri->v2.y - tri->v1.y; X tmp1.z = tri->v2.z - tri->v1.z; X tmp2.x = tri->v3.x - tri->v1.x; X tmp2.y = tri->v3.y - tri->v1.y; X tmp2.z = tri->v3.z - tri->v1.z; X X (void)VecNormCross(&tmp1, &tmp2, &tri->norm); X X tri->d = -dotp(&tri->v1, &tri->norm); X X QueueTri(hf, tri); X return tri; X} X X/* X * Intersect ray with right isoscoles triangle, the hypotenuse of which X * has slope of -1. X */ Xstatic Float XintHftri(ray, pos, tri) XhfTri *tri; XVector *pos, *ray; X{ X Float u, v, dist, xpos, ypos; X X u = dotp(&tri->norm, pos) + tri->d; X v = dotp(&tri->norm, ray); X X if ((u <= 0. || v > -EPSILON) && (u >= 0. && v < EPSILON)) X return FAR_AWAY; X X dist = -u / v; X X if (dist < EPSILON) X return FAR_AWAY; X X xpos = pos->x + dist*ray->x; X ypos = pos->y + dist*ray->y; X X if (tri->type == TRI1 && xpos >= tri->v1.x && ypos >= tri->v1.y && X xpos + ypos <= tri->v2.x + tri->v2.y) X return dist; X if (tri->type == TRI2 && xpos <= tri->v2.x && ypos <= tri->v2.y && X xpos + ypos >= tri->v1.x + tri->v1.y) X return dist; X return FAR_AWAY; X} X X/* X * Compute normal to height field. X */ X/*ARGSUSED*/ Xint XHfNormal(hf, pos, nrm, gnrm) XHf *hf; XVector *pos, *nrm, *gnrm; X{ X *gnrm = *nrm = hf->hittri.norm; X return FALSE; X} X X/*ARGSUSED*/ Xvoid XHfUV(hf, pos, norm, uv, dpdu, dpdv) XHf *hf; XVector *pos, *norm, *dpdu, *dpdv; XVec2d *uv; X{ X uv->u = pos->x; X uv->v = pos->y; X if (dpdu) { X dpdu->x = 1.; X dpdu->y = dpdv->z = 0.; X dpdv->x = dpdv->z = 0.; X dpdv->y = 1.; X } X} X X/* X * Compute heightfield bounding box. X */ Xvoid XHfBounds(hf, bounds) XHf *hf; XFloat bounds[2][3]; X{ X /* X * By default, height fields are centered at (0.5, 0.5, 0.) X */ X bounds[LOW][X] = bounds[LOW][Y] = 0; X bounds[HIGH][X] = bounds[HIGH][Y] = 1; X bounds[LOW][Z] = hf->minz; X bounds[HIGH][Z] = hf->maxz; X} X X/* X * Build min/max altitude value arrays for the given grid level. X */ Xstatic void Xintegrate_grid(hf, level) XHf *hf; Xint level; X{ X int i, j, k, l, ii, ji; X float max_alt, min_alt; X float **maxinto, **mininto, **frommax, **frommin, *minptr, *maxptr; X int insize, fromsize, fact; X X maxinto = hf->boundsmax[level]; X mininto = hf->boundsmin[level]; X insize = hf->lsize[level]; X frommax = hf->boundsmax[level-1]; X frommin = hf->boundsmin[level-1]; X fact = hf->BestSize; X fromsize = hf->lsize[level-1]; X X ii = 0; X X for (i = 0; i < insize; i++) { X ji = 0; X for (j = 0; j < insize; j++) { X max_alt = HF_UNSET; X min_alt = -HF_UNSET; X for (k = 0; k <= fact; k++) { X if (ii+k >= fromsize) X continue; X maxptr = &frommax[ii+k][ji]; X minptr = &frommin[ii+k][ji]; X for (l = 0; l <= fact; l++,maxptr++,minptr++) { X if (ji+l >= fromsize) X continue; X if (*maxptr > max_alt) X max_alt = *maxptr; X if (*minptr < min_alt) X min_alt = *minptr; X } X } X maxinto[i][j] = max_alt + EPSILON; X mininto[i][j] = min_alt - EPSILON; X ji += fact; X } X ii += fact; X } X} X X/* X * Place the given triangle in the triangle cache. X */ Xstatic void XQueueTri(hf, tri) XHf *hf; XhfTri *tri; X{ X if (hf->q[hf->qtail]) /* Free old triangle data */ X free((voidstar)hf->q[hf->qtail]); X hf->q[hf->qtail] = tri; /* Put on tail */ X hf->qtail = (hf->qtail + 1) % hf->qsize; /* Increment tail */ X} X X/* X * Search list of cached trianges to see if this triangle has been X * cached. If so, return a pointer to it. If not, return null pointer. X */ Xstatic hfTri * XGetQueuedTri(hf, x, y, flag) XHf *hf; XFloat x, y; Xint flag; X{ X register int i; X register hfTri **tmp; X X for (i = 0, tmp = hf->q; i < hf->qsize; i++, tmp++) { X if (*tmp && (*tmp)->v1.x == x && (*tmp)->v1.y == y && X (*tmp)->type == flag) X return *tmp; /* vertices & flag match, return it */ X } X X return (hfTri *)0; X} X X/* X * Return maximum height of cell indexed by y,x. This could be done X * as a macro, but many C compliers will choke on it. X */ Xstatic float Xminalt(y,x,data) Xint x, y; Xfloat **data; X{ X float min_alt; X X min_alt = min(data[y][x], data[y+1][x]); X min_alt = min(min_alt, data[y][x+1]); X min_alt = min(min_alt, data[y+1][x+1]); X return min_alt; X} X X/* X * Return maximum cell height, as above. X */ Xstatic float Xmaxalt(y,x,data) Xint x, y; Xfloat **data; X{ X float max_alt; X X max_alt = max(data[y][x], data[y+1][x]); X max_alt = max(max_alt, data[y][x+1]); X max_alt = max(max_alt, data[y+1][x+1]); X return max_alt; X} X Xchar * XHfName() X{ X return hfName; X} X Xvoid XHfStats(tests, hits) Xunsigned long *tests, *hits; X{ X *tests = HFTests; X *hits = HFHits; X} X Xvoid XHfMethodRegister(meth) XUserMethodType meth; X{ X if (iHfMethods) X iHfMethods->user = meth; X} END_OF_FILE if test 17333 -ne `wc -c <'libray/libobj/hf.c'`; then echo shar: \"'libray/libobj/hf.c'\" unpacked with wrong size! fi # end of 'libray/libobj/hf.c' fi if test -f 'raypaint/render.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'raypaint/render.c'\" else echo shar: Extracting \"'raypaint/render.c'\" \(17440 characters\) sed "s/^X//" >'raypaint/render.c' <<'END_OF_FILE' X/* X * render.c X * X * Copyright (C) 1989, 1991 Craig E. Kolb, Rod G. Bogart X * X * This software may be freely copied, modified, and redistributed X * provided that this copyright notice is preserved on all copies. X * X * You may not distribute this software, in whole or in part, as part of X * any commercial product without the express consent of the authors. X * X * There is no warranty or other guarantee of fitness of this software X * for any purpose. It is provided solely "as is". X * X * $Id: render.c,v 4.0 91/07/17 17:37:09 kolb Exp Locker: kolb $ X * X * $Log: render.c,v $ X * Revision 4.0 91/07/17 17:37:09 kolb X * Initial version. X * X */ X X#include "rayshade.h" X#include "libcommon/sampling.h" X#include "libsurf/atmosphere.h" X#include "viewing.h" X#include "options.h" X#include "stats.h" X#include "picture.h" X X/* X * "Dither matrices" used to encode the 'number' of a ray that samples a X * particular portion of a pixel. Hand-coding is ugly, but... X */ Xstatic int *SampleNumbers; Xstatic int OneSample[1] = {0}; Xstatic int TwoSamples[4] = {0, 2, X 3, 1}; Xstatic int ThreeSamples[9] = {0, 2, 7, X 6, 5, 1, X 3, 8, 4}; Xstatic int FourSamples[16] = { 0, 8, 2, 10, X 12, 4, 14, 6, X 3, 11, 1, 9, X 15, 7, 13, 5}; Xstatic int FiveSamples[25] = { 0, 8, 23, 17, 2, X 19, 12, 4, 20, 15, X 3, 21, 16, 9, 6, X 14, 10, 24, 1, 13, X 22, 7, 18, 11, 5}; Xstatic int SixSamples[36] = { 6, 32, 3, 34, 35, 1, X 7, 11, 27, 28, 8, 30, X 24, 14, 16, 15, 23, 19, X 13, 20, 22, 21, 17, 18, X 25, 29, 10, 9, 26, 12, X 36, 5, 33, 4, 2, 31}; Xstatic int SevenSamples[49] = {22, 47, 16, 41, 10, 35, 4, X 5, 23, 48, 17, 42, 11, 29, X 30, 6, 24, 49, 18, 36, 12, X 13, 31, 7, 25, 43, 19, 37, X 38, 14, 32, 1, 26, 44, 20, X 21, 39, 8, 33, 2, 27, 45, X 46, 15, 40, 9, 34, 3, 28}; Xstatic int EightSamples[64] = { 8, 58, 59, 5, 4, 62, 63, 1, X 49, 15, 14, 52, 53, 11, 10, 56, X 41, 23, 22, 44, 45, 19, 18, 48, X 32, 34, 35, 29, 28, 38, 39, 25, X 40, 26, 27, 37, 36, 30, 31, 33, X 17, 47, 46, 20, 21, 43, 42, 24, X 9, 55, 54, 12, 13, 51, 50, 16, X 64, 2, 3, 61, 60, 6, 7, 57}; X#define RFAC 0.299 X#define GFAC 0.587 X#define BFAC 0.114 X X#define NOT_CLOSED 0 X#define CLOSED_PARTIAL 1 X#define CLOSED_SUPER 2 X/* X * If a region has area < MINAREA, it is considered to be "closed", X * (a permanent leaf). Regions that meet this criterion X * are drawn pixel-by-pixel rather. X */ X#define MINAREA 9 X X#define SQ_AREA(s) (((s)->xsize+1)*((s)->ysize+1)) X X#define PRIORITY(s) ((s)->var * SQ_AREA(s)) X X#define INTENSITY(p) ((RFAC*(p)[0] + GFAC*(p)[1] + BFAC*(p)[2])/255.) X X#define OVERLAPS_RECT(s) (!Rectmode || \ X ((s)->xpos <= Rectx1 && \ X (s)->ypos <= Recty1 && \ X (s)->xpos+(s)->xsize >= Rectx0 && \ X (s)->ypos+(s)->ysize >= Recty0)) X Xtypedef unsigned char RGB[3]; X Xstatic RGB **Image; Xstatic char **SampleMap; X X/* X * Sample square X */ Xtypedef struct SSquare { X short xpos, ypos, xsize, ysize; X short depth; X short leaf, closed; X float mean, var, prio; X struct SSquare *child[4], *parent; X} SSquare; X XSSquare *SSquares, *SSquareCreate(), *SSquareInstall(), *SSquareSelect(), X *SSquareFetchAtMouse(); X XFloat SSquareComputeLeafVar(); X XRay TopRay; /* Top-level ray. */ Xint Rectmode = FALSE, X Rectx0, Recty0, Rectx1, Recty1; Xint SuperSampleMode = 0; X#define SSCLOSED (SuperSampleMode + 1) X XRender(argc, argv) Xint argc; Xchar **argv; X{ X /* X * Do an adaptive trace, displaying results in a X * window as we go. X */ X SSquare *cursq; X Pixel *pixelbuf; X int y, x; X X /* X * The top-level ray TopRay always has as its origin the X * eye position and as its medium NULL, indicating that it X * is passing through a medium with index of refraction X * equal to DefIndex. X */ X TopRay.pos = Camera.pos; X TopRay.media = (Medium *)0; X TopRay.depth = 0; X /* X * Doesn't handle motion blur yet. X */ X TopRay.time = Options.framestart; X X GraphicsInit(Screen.xsize, Screen.ysize, "rayview"); X /* X * Allocate array of samples. X */ X Image=(RGB **)Malloc(Screen.ysize*sizeof(RGB *)); X SampleMap = (char **)Malloc(Screen.ysize * sizeof(char *)); X for (y = 0; y < Screen.ysize; y++) { X Image[y]=(RGB *)Calloc(Screen.xsize, sizeof(RGB)); X SampleMap[y] = (char *)Calloc(Screen.xsize, sizeof(char)); X } X switch (Sampling.sidesamples) { X case 1: X SampleNumbers = OneSample; X break; X case 2: X SampleNumbers = TwoSamples; X break; X case 3: X SampleNumbers = ThreeSamples; X break; X case 4: X SampleNumbers = FourSamples; X break; X case 5: X SampleNumbers = FiveSamples; X break; X case 6: X SampleNumbers = SixSamples; X break; X case 7: X SampleNumbers = SevenSamples; X break; X case 8: X SampleNumbers = EightSamples; X break; X default: X RLerror(RL_PANIC, X "Sorry, %d rays/pixel not supported.\n", X Sampling.totsamples); X } X /* X * Take initial four samples X */ X SSquareSample(0, 0, FALSE); X SSquareSample(Screen.xsize -1, 0, FALSE); X SSquareSample(Screen.xsize -1, Screen.ysize -1, FALSE); X SSquareSample(0, Screen.ysize -1, FALSE); X SSquares = SSquareInstall(0, 0, Screen.xsize -1, Screen.ysize -1, X 0, (SSquare *) NULL); X X for (y = 1; y <= 5; y++) { X /* X * Create and draw every region at depth y. X */ X SSquareDivideToDepth(SSquares, y); X } X X X while ((cursq = SSquareSelect(SSquares)) != (SSquare *)NULL) { X SSquareDivide(cursq); X if (GraphicsRedraw()) X SSquareDraw(SSquares); X if (GraphicsMiddleMouseEvent()) X SSetRectMode(); X } X X /* X * Finished the image; write to image file. X */ X pixelbuf = (Pixel *)Malloc(Screen.xsize * sizeof(Pixel)); X PictureStart(argv); X for (y = 0; y < Screen.ysize; y++) { X /* X * This is really disgusting. X */ X for (x = 0; x < Screen.xsize; x++) { X pixelbuf[x].r = Image[y][x][0] / 255.; X pixelbuf[x].g = Image[y][x][1] / 255.; X pixelbuf[x].b = Image[y][x][2] / 255.; X pixelbuf[x].alpha = SampleMap[y][x]; X } X PictureWriteLine(pixelbuf); X } X PictureEnd(); X free((voidstar)pixelbuf); X} X XFloat XSampleTime(sampnum) Xint sampnum; X{ X Float window, jitter = 0.0, res; X X if (Options.shutterspeed <= 0.) X return Options.framestart; X if (Options.jitter) X jitter = nrand(); X window = Options.shutterspeed / Sampling.totsamples; X res = Options.framestart + window * (sampnum + jitter); X TimeSet(res); X return res; X} X XSSetRectMode() X{ X int x1,y1,x2,y2; X X if (Rectmode) { X Rectmode = FALSE; X RecomputePriority(SSquares); X } X GraphicsGetMousePos(&x1, &y1); X while (GraphicsMiddleMouseEvent()) X ; X GraphicsGetMousePos(&x2, &y2); X if (x1 < x2) { X Rectx0 = (x1 < 0) ? 0 : x1; X Rectx1 = (x2 >= Screen.xsize) ? Screen.xsize - 1 : x2; X } else { X Rectx0 = (x2 < 0) ? 0 : x2; X Rectx1 = (x1 >= Screen.xsize) ? Screen.xsize - 1 : x1; X } if (y1 < y2) { X Recty0 = (y1 < 0) ? 0 : y1; X Recty1 = (y2 >= Screen.ysize) ? Screen.ysize - 1 : y2; X } else { X Recty0 = (y2 < 0) ? 0 : y2; X Recty1 = (y1 >= Screen.ysize) ? Screen.ysize - 1 : y1; X } X Rectmode = TRUE; X /* setup current rect */ X (void)RecomputePriority(SSquares); X} X XRecomputePriority(sq) XSSquare *sq; X{ X Float maxp; X X if (!OVERLAPS_RECT(sq)) { X sq->closed = SSCLOSED; X return FALSE; X } X X if (sq->leaf) { X if (SQ_AREA(sq) >= MINAREA) X sq->closed = NOT_CLOSED; X return TRUE; X } X maxp = 0.; X if (RecomputePriority(sq->child[0])) X maxp = max(maxp, sq->child[0]->prio); X if (RecomputePriority(sq->child[1])) X maxp = max(maxp, sq->child[1]->prio); X if (RecomputePriority(sq->child[2])) X maxp = max(maxp, sq->child[2]->prio); X if (RecomputePriority(sq->child[3])) X maxp = max(maxp, sq->child[3]->prio); X sq->prio = maxp; X#if 0 X if ((sq->child[0]->closed == CLOSED_SUPER) && X (sq->child[1]->closed == CLOSED_SUPER) && X (sq->child[2]->closed == CLOSED_SUPER) && X (sq->child[3]->closed == CLOSED_SUPER)) X sq->closed = CLOSED_SUPER; X else if (sq->child[0]->closed && sq->child[1]->closed && X sq->child[2]->closed && sq->child[3]->closed) X sq->closed = CLOSED_PARTIAL; X else X sq->closed = NOT_CLOSED; X#else X if ((sq->child[0]->closed >= SSCLOSED) && X (sq->child[1]->closed >= SSCLOSED) && X (sq->child[2]->closed >= SSCLOSED) && X (sq->child[3]->closed >= SSCLOSED)) X sq->closed = SSCLOSED; X else X sq->closed = NOT_CLOSED; X#endif X return TRUE; X} X XSSquareSample(x, y, supersample) Xint x, y, supersample; X{ X Float upos, vpos, u, v; X int xx, yy, xp, sampnum, usamp, vsamp; X Pixel ctmp; X Pixel p; X extern unsigned char correct(); X X if (SampleMap[y][x] >= 128 + supersample) X /* already a sample there */ X return; X SampleMap[y][x] = 128 + supersample; X if (supersample) { X p.r = p.g = p.b = p.alpha = 0; X sampnum = 0; X xp = x + Screen.minx; X vpos = Screen.miny + y - 0.5*Sampling.filterwidth; X for (yy = 0; yy < Sampling.sidesamples; yy++, X vpos += Sampling.filterdelta) { X upos = xp - 0.5*Sampling.filterwidth; X for (xx = 0; xx < Sampling.sidesamples; xx++, X upos += Sampling.filterdelta) { X if (Options.jitter) { X u = upos + nrand()*Sampling.filterdelta; X v = vpos + nrand()*Sampling.filterdelta; X } else { X u = upos; X v = vpos; X } X TopRay.time = SampleTime(SampleNumbers[sampnum]); X SampleScreen(u, v, &TopRay, &ctmp, X SampleNumbers[sampnum]); X p.r += ctmp.r*Sampling.filter[xx][yy]; X p.g += ctmp.g*Sampling.filter[xx][yy]; X p.b += ctmp.b*Sampling.filter[xx][yy]; X if (++sampnum == Sampling.totsamples) X sampnum = 0; X } X } X } X else { X sampnum = nrand() * Sampling.totsamples; X usamp = sampnum % Sampling.sidesamples; X vsamp = sampnum / Sampling.sidesamples; X X vpos = Screen.miny + y - 0.5*Sampling.filterwidth X + vsamp * Sampling.filterdelta; X upos = x + Screen.minx - 0.5*Sampling.filterwidth + X usamp*Sampling.filterdelta; X if (Options.jitter) { X vpos += nrand()*Sampling.filterdelta; X upos += nrand()*Sampling.filterdelta; X } X TopRay.time = SampleTime(SampleNumbers[sampnum]); X SampleScreen(upos, vpos, &TopRay, &p, SampleNumbers[sampnum]); X } X Image[y][x][0] = CORRECT(p.r); X Image[y][x][1] = CORRECT(p.g); X Image[y][x][2] = CORRECT(p.b); X} X XSSquare * XSSquareCreate(xp, yp, xs, ys, d, parent) Xint xp, yp, xs, ys, d; XSSquare *parent; X{ X SSquare *new; X Float i1, i2, i3, i4; X X new = (SSquare *)Calloc(1, sizeof(SSquare)); X new->xpos = xp; new->ypos = yp; X new->xsize = xs; new->ysize = ys; X new->depth = d; X new->parent = parent; X i1 = INTENSITY(Image[new->ypos][new->xpos]); X i2 = INTENSITY(Image[new->ypos+new->ysize][new->xpos]); X i3 = INTENSITY(Image[new->ypos+new->ysize][new->xpos+new->xsize]); X i4 = INTENSITY(Image[new->ypos][new->xpos+new->xsize]); X new->mean = 0.25 * (i1+i2+i3+i4); X if (SQ_AREA(new) < MINAREA) { X new->prio = 0; X new->closed = SSCLOSED; X } else { X new->var = SSquareComputeLeafVar(new, i1, i2, i3, i4); X new->prio = PRIORITY(new); X new->closed = NOT_CLOSED; X } X new->leaf = TRUE; X return new; X} X XFloat XSSquareComputeLeafVar(sq, i1, i2, i3, i4) XSSquare *sq; XFloat i1, i2, i3, i4; X{ X Float res, diff; X X diff = i1 - sq->mean; X res = diff*diff; X diff = i2 - sq->mean; X res += diff*diff; X diff = i3 - sq->mean; X res += diff*diff; X diff = i4 - sq->mean; X return res + diff*diff; X} X XSSquareDivideToDepth(sq, d) XSSquare *sq; Xint d; X{ X if (sq->depth == d) X return; X if (sq->leaf) X SSquareDivide(sq); X SSquareDivideToDepth(sq->child[0], d); X SSquareDivideToDepth(sq->child[1], d); X SSquareDivideToDepth(sq->child[2], d); X SSquareDivideToDepth(sq->child[3], d); X} X XSSquareDivide(sq) XSSquare *sq; X{ X int lowx, lowy, midx, midy, hix, hiy; X int newxsize, newysize, ndepth, supersample; X /* X * Divide the square into fourths by tracing 12 X * new samples if necessary. X */ X newxsize = sq->xsize / 2; X newysize = sq->ysize / 2; X lowx = sq->xpos; lowy = sq->ypos; X midx = sq->xpos + newxsize; X midy = sq->ypos + newysize; X hix = sq->xpos + sq->xsize; X hiy = sq->ypos + sq->ysize; X ndepth = sq->depth + 1; X /* create new samples */ X supersample = FALSE; X SSquareSample(midx, lowy, supersample); X SSquareSample(lowx, midy, supersample); X SSquareSample(midx, midy, supersample); X SSquareSample(hix, midy, supersample); X SSquareSample(midx, hiy, supersample); X#ifdef SHARED_EDGES X /* create and draw new squares */ X sq->child[0] = SSquareInstall(lowx,lowy,newxsize,newysize,ndepth,sq); X sq->child[1] = SSquareInstall(midx, lowy, sq->xsize - newxsize, X newysize, ndepth,sq); X sq->child[2] = SSquareInstall(lowx, midy, newxsize, X sq->ysize - newysize, ndepth,sq); X sq->child[3] = SSquareInstall(midx, midy, sq->xsize - newxsize, X sq->ysize - newysize, ndepth,sq); X#else X /* X * draw additional samples in order to subdivide such that X * edges of regions do not overlap X */ X SSquareSample(midx +1, lowy, supersample); X SSquareSample(midx +1, midy, supersample); X SSquareSample(lowx, midy +1, supersample); X SSquareSample(midx, midy +1, supersample); X SSquareSample(midx +1, midy +1, supersample); X SSquareSample(hix, midy +1, supersample); X SSquareSample(midx +1, hiy, supersample); X X /* create and draw new squares */ X sq->child[0] = SSquareInstall(lowx,lowy, X newxsize,newysize,ndepth,sq); X sq->child[1] = SSquareInstall(midx+1, lowy, sq->xsize - newxsize -1, X newysize, ndepth,sq); X sq->child[2] = SSquareInstall(lowx, midy+1, newxsize, X sq->ysize - newysize-1, ndepth,sq); X sq->child[3] = SSquareInstall(midx+1, midy+1, sq->xsize - newxsize-1, X sq->ysize - newysize-1, ndepth,sq); X#endif X sq->leaf = FALSE; X /* X * Recompute parent's mean and variance. X */ X if (OVERLAPS_RECT(sq)) X SSquareRecomputeStats(sq); X} X XSSquareRecomputeStats(sq) XSSquare *sq; X{ X Float maxp; X int in[4], closeflag; X X in[0] = OVERLAPS_RECT(sq->child[0]); X in[1] = OVERLAPS_RECT(sq->child[1]); X in[2] = OVERLAPS_RECT(sq->child[2]); X in[3] = OVERLAPS_RECT(sq->child[3]); X X if ((in[0] && (sq->child[0]->closed < SSCLOSED)) || X (in[1] && (sq->child[1]->closed < SSCLOSED)) || X (in[2] && (sq->child[2]->closed < SSCLOSED)) || X (in[3] && (sq->child[3]->closed < SSCLOSED))) { X maxp = 0.; X if (in[0]) X maxp = max(maxp, sq->child[0]->prio); X if (in[1]) X maxp = max(maxp, sq->child[1]->prio); X if (in[2]) X maxp = max(maxp, sq->child[2]->prio); X if (in[3]) X maxp = max(maxp, sq->child[3]->prio); X sq->closed = NOT_CLOSED; X sq->prio = maxp; X } else if ((sq->child[0]->closed == CLOSED_SUPER) && X (sq->child[1]->closed == CLOSED_SUPER) && X (sq->child[2]->closed == CLOSED_SUPER) && X (sq->child[3]->closed == CLOSED_SUPER)) { X sq->closed = CLOSED_SUPER; X sq->prio = 0; X#if 0 X } else if ((!in[0] || sq->child[0]->closed >= SSCLOSED) && X (!in[1] || sq->child[1]->closed >= SSCLOSED) && X (!in[2] || sq->child[2]->closed >= SSCLOSED) && X (!in[3] || sq->child[3]->closed >= SSCLOSED)) { X sq->closed = SSCLOSED; X sq->prio = 0; X#endif X } else { X sq->closed = SSCLOSED; X sq->prio = 0; X } X if (sq->parent) X SSquareRecomputeStats(sq->parent); X} X XSSquare * XSSquareInstall(xp, yp, xs, ys, d, parent) Xint xp, yp, xs, ys, d; XSSquare *parent; X{ X SSquare *new; X X new = SSquareCreate(xp, yp, xs, ys, d, parent); X SSquareDraw(new); X return new; X} X XSSquare * XSSquareSelect(list) XSSquare *list; X{ X int i; X SSquare *res, *which; X X /* X * If mousebutton is pressed, X * find the square in which the mouse is located and X * return that if not a leaf (pixel-sized square). X */ X if (GraphicsLeftMouseEvent()) { X SuperSampleMode = 0; X if ((res = SSquareFetchAtMouse(list)) != (SSquare *)NULL) X return res; X } X else if (GraphicsRightMouseEvent()) { X SuperSampleMode = 1; X if ((res = SSquareFetchAtMouse(list)) != (SSquare *)NULL) { X return res; X } X } X if (list->closed >= SSCLOSED) { X if (Rectmode) { X Rectmode = FALSE; X RecomputePriority(SSquares); X return SSquareSelect(list); X } X return (SSquare *)NULL; X } X /* X * Otherwise, find the square with the greatest X * 'priority'. X */ X res = list; X while (res && !res->leaf) { X which = (SSquare *)NULL; X for (i = 0; i < 4; i++) { X if ((res->child[i]->closed < SSCLOSED) && X OVERLAPS_RECT(res->child[i])) { X which = res->child[i]; X break; X } X } X while (++i < 4) { X if ((res->child[i]->closed < SSCLOSED) && X which->prio < res->child[i]->prio && X OVERLAPS_RECT(res->child[i])) X which = res->child[i]; X } X res = which; X } X return res; X} X XSSquare * XSSquareFetchAtMouse(list) XSSquare *list; X{ X SSquare *res; X int x, y; X X /* X * Get mouse position. X */ X GraphicsGetMousePos(&x, &y); X res = list; X while (!res->leaf && (res->closed < SSCLOSED)) { X /* X * Find in which child the mouse is located. X */ X if (x < res->child[1]->xpos) { X if (y < res->child[2]->ypos) X res = res->child[0]; X else X res = res->child[2]; X } else if (y < res->child[3]->ypos) X res = res->child[1]; X else X res = res->child[3]; X } X if (res->closed >= SSCLOSED) X return (SSquare *)NULL; X return res; X} X XSSquareDraw(sq) XSSquare *sq; X{ X if (SQ_AREA(sq) >= MINAREA) X GraphicsDrawRectangle(sq->xpos, sq->ypos, sq->xsize, sq->ysize, X Image[sq->ypos][sq->xpos], X Image[sq->ypos][sq->xpos+sq->xsize], X Image[sq->ypos+sq->ysize][sq->xpos+sq->xsize], X Image[sq->ypos+sq->ysize][sq->xpos]); X else X DrawPixels(sq->xpos, sq->ypos, sq->xsize, sq->ysize); X if (!sq->leaf) { X SSquareDraw(sq->child[0]); X SSquareDraw(sq->child[1]); X SSquareDraw(sq->child[2]); X SSquareDraw(sq->child[3]); X } X} X XDrawPixels(xp, yp, xs, ys) Xint xp, yp, xs, ys; X{ X int x, y, xi, yi; X X yi = yp; X for (y = 0; y <= ys; y++, yi++) { X xi = xp; X for (x = 0; x <= xs; x++, xi++) { X SSquareSample(xi, yi, SuperSampleMode); X GraphicsDrawPixel(xi, yi, Image[yi][xi]); X } X } X} END_OF_FILE if test 17440 -ne `wc -c <'raypaint/render.c'`; then echo shar: \"'raypaint/render.c'\" unpacked with wrong size! fi # end of 'raypaint/render.c' fi echo shar: End of archive 16 \(of 19\). cp /dev/null ark16isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 19 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0