home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
gondwana.ecr.mu.oz.au/pub/
/
Graphics.tar
/
Graphics
/
atomart.tar.gz
/
atomart.tar
/
shade.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-06-27
|
7KB
|
330 lines
/*
* the shading routines and their utilities
*/
#include <stdio.h>
#include <math.h>
#include "atomart.h"
#include "macro.h"
#include "gram.h"
extern object *oblist;
extern light *lights;
extern hlist *fhlist;
extern int maxhitlevel;
extern short raynumber;
extern pixel backcol;
extern colour ambient;
extern double power();
extern hlist *trace();
extern float randtable[], *randp, *erandp;
/*
* checklight
*
* is the light l visible to us from loc? TRUE if
* it is, FALSE if it isn't. lr is returned as the ray
* hitting l from loc, and col is the intensity value of
* the light.
*/
int
checklight(l, loc, lr, col, hitlevel)
light *l;
vector *loc;
ray *lr;
pixel *col;
int hitlevel;
{
object *o;
vector dir;
hlist *hit, *p, *lp;
register float dist, fact, t, peturb;
colour base;
if (l->type == DIRECTIONAL && l->cosang == 0.0) {
lr->dir = l->dir;
lr->org = *loc;
dist = HUGE_DIST;
} else {
vsub(lr->dir, l->org, *loc);
normalise(lr->dir);
dir = lr->dir;
if (l->type == DIRECTIONAL && dprod(dir, l->dir) < l->cosang)
return(FALSE);
lr->org = *loc;
if (fabs(dir.x) > fabs(dir.y))
if (fabs(dir.x) > fabs(dir.z))
dist = fabs((l->org.x - loc->x) / dir.x);
else
dist = fabs((l->org.z - loc->z) / dir.z);
else
if (fabs(dir.y) > fabs(dir.z))
dist = fabs((l->org.y - loc->y) / dir.y);
else
dist = fabs((l->org.z - loc->z) / dir.z);
}
/* aim at a random point on the light's sphere */
if (l->rad != 0.0 && dist != 0.0) {
fact = l->rad / dist;
peturb = (randnum() < 0.5) ? randnum() : -randnum();
lr->dir.x += peturb * fact;
peturb = (randnum() < 0.5) ? randnum() : -randnum();
lr->dir.y += peturb * fact;
peturb = (randnum() < 0.5) ? randnum() : -randnum();
lr->dir.z += peturb * fact;
normalise(lr->dir);
}
lr->raynumber = raynumber++;
o = l->lasthits[hitlevel];
if (o != (object *)NULL) {
if ((hit = o->intersects(lr, o)) == (hlist *)NULL)
hit = trace(lr, oblist);
else if (hit->t >= dist)
hit = trace(lr, oblist);
} else
hit = trace(lr, oblist);
base.r = l->c.r;
base.g = l->c.g;
base.b = l->c.b;
o = (object *)NULL;
while (hit != (hlist *)NULL && hit->obj->s.trans != 0.0) {
p = hit->nxt;
if (hit->t < dist && o != hit->obj) {
o = hit->obj;
if (p != (hlist *)NULL && p->obj == o && o->s.absorb != 0.0) {
fact = 1.0 - o->s.absorb * (p->t - hit->t);
if (fact < 0.0)
fact = 0.0;
base.r *= o->s.trans * fact;
base.g *= o->s.trans * fact;
base.b *= o->s.trans * fact;
} else {
base.r *= o->s.trans;
base.g *= o->s.trans;
base.b *= o->s.trans;
}
}
release(hit);
if (p == (hlist *)NULL) {
lr->org.x += lr->dir.x * hit->t;
lr->org.y += lr->dir.y * hit->t;
lr->org.z += lr->dir.z * hit->t;
lr->raynumber = raynumber++;
hit = trace(lr, oblist);
} else {
hit = p;
}
}
if (hit != (hlist *)NULL) {
t = hit->t;
if (t >= dist)
l->lasthits[hitlevel] = (object *)NULL;
else
l->lasthits[hitlevel] = hit->obj;
for (lp = hit; lp != (hlist *)NULL; lp = p) {
p = lp->nxt;
release(lp);
}
} else {
l->lasthits[hitlevel] = (object *)NULL;
t = dist;
}
col->r = base.r;
col->g = base.g;
col->b = base.b;
/* maybe the light is closer */
return(t >= dist);
}
/*
* nextobj
*
* return the list of hits where the first hit in the list isn't
* on object o.
*/
hlist *
nextobj(list, o)
hlist *list;
object *o;
{
hlist *lp;
while (list != (hlist *)NULL && list->obj == o) {
lp = list;
list = list->nxt;
release(lp);
}
return(list);
}
/*
* shade
*
* returns the shading info for an object with reflection and/or
* transparency.
*/
void
shade(pix, i, hit, hitlevel)
pixel *pix;
register ray *i;
hlist *hit;
int hitlevel;
{
hlist *nxthit, *p, *lp;
light *l;
pixel objcol, othercol;
ray lr, ir;
vector n, ns, mid;
tlist *tp;
register object *o;
register int count;
register float fact, prod, ksfact, kdfact;
ir.dir = i->dir;
smult(ir.dir, hit->t);
vadd(ir.org, i->org, ir.dir);
o = hit->obj;
o->normal(&n, &ir.org, o, hit->type);
if (dprod(n, i->dir) > 0.0) /* normal facing away from us */
smult(n, -1.0);
ir.dir = i->dir;
kdfact = o->s.kd;
ksfact = o->s.ks;
objcol.r = o->s.c.r;
objcol.g = o->s.c.g;
objcol.b = o->s.c.b;
for (tp = o->s.txtlist; tp != (tlist *)NULL; tp = tp->nxt)
tp->txtcol(o, &tp->txt, &ir.org, &n, &objcol, hit->type);
pix->r = objcol.r * o->s.a.r;
pix->g = objcol.g * o->s.a.g;
pix->b = objcol.b * o->s.a.b;
for (l = lights; l != (light *)NULL; l = l->nxt)
for (count = 0; count != l->rays; count++) {
if (checklight(l, &ir.org, &lr, &othercol, hitlevel)) {
prod = dprod(n, lr.dir);
if (prod > 0.0) {
fact = prod * kdfact;
pix->r += fact * objcol.r * othercol.r;
pix->g += fact * objcol.g * othercol.g;
pix->b += fact * objcol.b * othercol.b;
if (o->s.ks != 0.0) {
ns = n;
smult(ns, -2 * prod);
vadd(mid, lr.dir, ns);
normalise(mid);
fact = dprod(ir.dir, mid);
if (fact > 0.0) {
fact = power(fact, o->s.ksexp) * ksfact;
pix->r += fact * othercol.r;
pix->g += fact * othercol.g;
pix->b += fact * othercol.b;
}
}
}
}
}
if (o->s.refl != 0.0 && hitlevel < maxhitlevel) {
prod = 2 * dprod(ir.dir, n);
smult(n, prod);
vsub(ir.dir, ir.dir, n);
normalise(ir.dir);
ir.raynumber = raynumber++;
nxthit = trace(&ir, oblist);
fact = o->s.refl;
if (nxthit != (hlist *)NULL) {
shade(&objcol, &ir, nxthit, hitlevel + 1);
pix->r += fact * objcol.r;
pix->g += fact * objcol.g;
pix->b += fact * objcol.b;
} else {
pix->r += fact * backcol.r;
pix->g += fact * backcol.g;
pix->b += fact * backcol.b;
}
for (lp = nxthit; lp != (hlist *)NULL; lp = p) {
p = lp->nxt;
release(lp);
}
}
if (o->s.trans != 0.0 && hitlevel < maxhitlevel) {
fact = o->s.trans;
pix->r = (1.0 - fact) * pix->r;
pix->g = (1.0 - fact) * pix->g;
pix->b = (1.0 - fact) * pix->b;
ir.dir = i->dir;
ir.raynumber = raynumber++;
nxthit = trace(&ir, oblist);
if (nxthit != (hlist *)NULL) {
shade(&objcol, &ir, nxthit, hitlevel + 1);
pix->r += fact * objcol.r;
pix->g += fact * objcol.g;
pix->b += fact * objcol.b;
} else {
pix->r += fact * backcol.r;
pix->g += fact * backcol.g;
pix->b += fact * backcol.b;
}
for (lp = nxthit; lp != (hlist *)NULL; lp = p) {
p = lp->nxt;
release(lp);
}
}
if (pix->r > 1.0)
pix->r = 1.0;
if (pix->g > 1.0)
pix->g = 1.0;
if (pix->b > 1.0)
pix->b = 1.0;
}