blob: 5d385059ea20b28a3a6851390dada96e6ef091b7 [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
* sph.c
*
* DESCRIPTION
* This file contains all routines that operate on sphere objects.
*
*/
#include <stdio.h>
#include <math.h>
#include "rt.h"
/*
* Define sphere data structure.
*/
typedef struct sphere
{
POINT center; /* Center of sphere. */
REAL rad; /* Radius of sphere. */
REAL rad2; /* Radius squared of sphere. */
}
SPHERE;
/*
* NAME
* SphName - return the object name
*
* SYNOPSIS
* CHAR *SphName()
*
* RETURNS
* A pointer to the name string.
*/
CHAR *SphName()
{
return ("sphere");
}
/*
* NAME
* SphPrint - print the sphere object data to stdout
*
* SYNOPSIS
* VOID SphPrint(po)
* OBJECT *po; // Ptr to sphere object.
*
* RETURNS
* Nothing.
*/
VOID SphPrint(po)
OBJECT *po;
{
INT i;
SPHERE *ps; /* Ptr to sphere data. */
ELEMENT *pe; /* Ptr to sphere element. */
pe = po->pelem;
fprintf(stderr,"\tSphere object\n");
for (i = 0; i < po->numelements; i++)
{
ps = (SPHERE *)(pe->data);
fprintf(stderr,"\t\tcenter %f %f %f\n", ps->center[0], ps->center[1], ps->center[2]);
fprintf(stderr,"\t\t radius %f %f\n\n", ps->rad, ps->rad2);
pe++;
}
}
/*
* NAME
* SphElementBoundBox - compute sphere element bounding box
*
* SYNOPSIS
* VOID SphElementBoundBox(pe, ps)
* ELEMENT *pe; // Ptr to sphere element.
* SPHERE *ps; // Ptr to sphere data.
*
* RETURNS
* Nothing.
*/
VOID SphElementBoundBox(pe, ps)
ELEMENT *pe;
SPHERE *ps;
{
BBOX *pbb; /* Ptr to bounding box. */
pbb = &(pe->bv);
pbb->dnear[0] = ps->center[0] - ps->rad;
pbb->dnear[1] = ps->center[1] - ps->rad;
pbb->dnear[2] = ps->center[2] - ps->rad;
pbb->dfar[0] = ps->center[0] + ps->rad;
pbb->dfar[1] = ps->center[1] + ps->rad;
pbb->dfar[2] = ps->center[2] + ps->rad;
}
/*
* NAME
* SphBoundBox - compute sphere object bounding box
*
* SYNOPSIS
* VOID SphBoundBox(po)
* OBJECT *po; // Ptr to sphere object.
*
* RETURNS
* Nothing.
*/
VOID SphBoundBox(po)
OBJECT *po;
{
INT i;
SPHERE *ps; /* Ptr to sphere data. */
ELEMENT *pe; /* Ptr to sphere element. */
BBOX *pbb; /* Ptr to bounding box. */
REAL minx, maxx;
REAL miny, maxy;
REAL minz, maxz;
pe = po->pelem;
pbb = &(po->bv);
minx = miny = minz = HUGE_REAL;
maxx = maxy = maxz = -HUGE_REAL;
for (i = 0; i < po->numelements; i++)
{
ps = (SPHERE *)(pe->data);
SphElementBoundBox(pe, ps);
minx = Min(minx, pe->bv.dnear[0]);
miny = Min(miny, pe->bv.dnear[1]);
minz = Min(minz, pe->bv.dnear[2]);
maxx = Max(maxx, pe->bv.dfar[0]);
maxy = Max(maxy, pe->bv.dfar[1]);
maxz = Max(maxz, pe->bv.dfar[2]);
pe++;
}
pbb->dnear[0] = minx;
pbb->dnear[1] = miny;
pbb->dnear[2] = minz;
pbb->dfar[0] = maxx;
pbb->dfar[1] = maxy;
pbb->dfar[2] = maxz;
}
/*
* NAME
* SphNormal - compute sphere unit normal at given point on the surface
*
* SYNOPSIS
* VOID SphNormal(hit, Pi, Ni)
* IRECORD *hit; // Ptr to intersection record.
* POINT Pi; // Ipoint.
* POINT Ni; // Normal.
*
* NOTES
* The normal is the unit vector from the given point to the sphere
* center point.
*
* RETURNS
* Nothing.
*/
VOID SphNormal(hit, Pi, Ni)
IRECORD *hit;
POINT Pi;
POINT Ni;
{
ELEMENT *pe;
SPHERE *ps; /* Ptr to sphere data. */
/* Compute normal and make it a unit vector. */
pe = hit->pelem;
ps = (SPHERE *)pe->data;
VecSub(Ni, Pi, ps->center);
Ni[0] /= ps->rad;
Ni[1] /= ps->rad;
Ni[2] /= ps->rad;
}
/*
* NAME
* SphDataNormalize - normalize the sphere by the given normalization matrix
*
* SYNOPSIS
* VOID SphDataNormalize(po, normMat)
* OBJECT *po; // Ptr to sphere object.
* MATRIX normMat; // Normalization matrix.
*
* RETURNS
* Nothing.
*/
VOID SphDataNormalize(po, normMat)
OBJECT *po;
MATRIX normMat;
{
INT i;
SPHERE *ps; /* Ptr to sphere data. */
ELEMENT *pe; /* Ptr to sphere element. */
POINT surf_point; /* Point on surface. */
POINT center_point; /* Center point. */
POINT rad_vector; /* Radius vector. */
NormalizeBoundBox(&po->bv, normMat);
pe = po->pelem;
for (i = 0; i < po->numelements; i++)
{
ps = (SPHERE *)pe->data;
NormalizeBoundBox(&pe->bv, normMat);
surf_point[0] = ps->center[0] + ps->rad;
surf_point[1] = ps->center[1];
surf_point[2] = ps->center[2];
surf_point[3] = 1.0;
center_point[0] = ps->center[0];
center_point[1] = ps->center[1];
center_point[2] = ps->center[2];
center_point[3] = 1.0;
/* Transform center point. */
VecMatMult(center_point, normMat, center_point);
VecMatMult(surf_point, normMat, surf_point);
/* Find new radius. */
VecSub(rad_vector, surf_point, center_point);
VecCopy(ps->center, center_point);
ps->rad = VecLen(rad_vector);
ps->rad2 = ps->rad * ps->rad;
pe++;
}
}
/*
* NAME
* SphPeIntersect - calculate intersection of ray with sphere
*
* SYNOPSIS
* INT SphPeIntersect(pr, pe, hit)
* RAY *pr; // Ptr to incident ray.
* ELEMENT *pe; // Ptr to sphere element.
* IRECORD *hit; // Intersection recorder.
*
* NOTES
* Calculate intersection of ray, X = P + tD with sphere,
* | X - C | = r2. C = sphere center. r2 = radius squared.
* This solves t2 - 2t(D*V) + (V*V - r2) = 0 at
* t = (D*V) +/- sqrt((D*V)2 - (V*V) + r2 ),
* where t2 = t squared, V = C - P and |D| = 1.
*
* RETURNS
* The number of intersection points.
*/
INT SphPeIntersect(pr, pe, hit)
RAY *pr;
ELEMENT *pe;
IRECORD *hit;
{
INT nhits; /* Number of hits. */
REAL b, disc, t1, t2, vsq; /* Formula variables. */
SPHERE *ps; /* Ptr to sphere data. */
POINT V; /* C - P */
IRECORD *sphhit;
ps = (SPHERE *)(pe->data);
sphhit = hit;
VecSub(V, ps->center, pr->P); /* Ray from origin to center.*/
vsq = VecDot(V, V); /* Length sq of V. */
b = VecDot(V, pr->D); /* Perpendicular scale of V. */
if (vsq > ps->rad2 && b < RAYEPS) /* Behind ray origin. */
return (0);
disc = b*b - vsq + ps->rad2; /* Discriminate. */
if (disc < 0.0) /* Misses ray. */
return (0);
disc = sqrt(disc); /* Find intersection param. */
t2 = b + disc;
t1 = b - disc;
if (t2 <= RAYEPS) /* Behind ray origin. */
return (0);
nhits = 0;
if (t1 > RAYEPS) /* Entering sphere. */
{
IsectAdd(sphhit, t1, pe);
sphhit++;
nhits++;
}
IsectAdd(sphhit, t2, pe); /* Exiting sphere */
nhits++;
return (nhits);
}
/*
* NAME
* SphIntersect - call sphere object intersection routine
*
* SYNOPSIS
* INT SphIntersect(pr, po, hit)
* RAY *pr; // Ptr to incident ray.
* OBJECT *po; // Ptr to sphere object.
* IRECORD *hit; // Intersection record.
*
* RETURNS
* The number of intersections found.
*/
INT SphIntersect(pr, po, hit)
RAY *pr;
OBJECT *po;
IRECORD *hit;
{
INT i;
INT nhits; /* # hits in sphere. */
ELEMENT *pe; /* Ptr to sphere element. */
IRECORD newhit[2]; /* Hit list. */
/* Traverse sphere list to find intersections. */
nhits = 0;
pe = po->pelem;
hit[0].t = HUGE_REAL;
for (i = 0; i < po->numelements; i++)
{
if (SphPeIntersect(pr, pe, newhit))
if (newhit[0].t < hit[0].t)
{
nhits++;
hit[0].t = newhit[0].t;
hit[0].pelem = newhit[0].pelem;
}
pe++;
}
return (nhits);
}
/*
* NAME
* SphTransform - transform sphere object by a transformation matrix
*
* SYNOPSIS
* VOID SphTransform(po, xtrans, xinvT)
* OBJECT *po; // Ptr to sphere object.
* MATRIX xtrans; // Transformation matrix.
* MATRIX xinvT; // Transpose of inverse matrix.
*
* RETURNS
* Nothing.
*/
VOID SphTransform(po, xtrans, xinvT)
OBJECT *po;
MATRIX xtrans;
MATRIX xinvT;
{
INT i;
INT numelems; /* Number of object elements. */
REAL new_rad;
SPHERE *ps; /* Ptr to sphere data. */
ELEMENT *pe; /* Ptr to sphere element. */
POINT surf_point; /* Point on surface. */
POINT center_point; /* Center_point. */
POINT rad_vector; /* Radius vector. */
pe = po->pelem;
numelems = po->numelements;
for (i = 0; i < numelems; i++)
{
ps = (SPHERE *)pe->data;
/* See if radius has changed with a scale. */
surf_point[0] = ps->center[0] + ps->rad;
surf_point[1] = ps->center[1];
surf_point[2] = ps->center[2];
surf_point[3] = 1.0;
center_point[0] = ps->center[0];
center_point[1] = ps->center[1];
center_point[2] = ps->center[2];
center_point[3] = 1.0;
/* Transform center point. */
VecMatMult(center_point, xtrans, center_point);
VecMatMult(surf_point, xtrans, surf_point);
/* Find radius. */
VecSub(rad_vector, surf_point, center_point);
VecCopy(ps->center, center_point);
new_rad = VecLen(rad_vector);
if (new_rad != ps->rad)
{
ps->rad = new_rad;
ps->rad2 = ps->rad * ps->rad;
}
pe++;
}
}
/*
* NAME
* SphRead - read sphere object data from file
*
* SYNOPSIS
* VOID SphRead(po, pf)
* OBJECT *po; // Ptr to sphere object.
* FILE *pf; // Ptr to file.
*
* RETURNS
* Nothing.
*/
VOID SphRead(po, pf)
OBJECT *po;
FILE *pf;
{
INT i;
INT instat; /* Input counter */
SPHERE *ps; /* Ptr to sphere data. */
ELEMENT *pe; /* Ptr to sphere element. */
pe = po->pelem;
ps = GlobalMalloc(sizeof(SPHERE)*po->numelements, "sph.c");
for (i = 0; i < po->numelements; i++)
{
instat = fscanf(pf,"%lf %lf %lf %lf", &(ps->center[0]), &(ps->center[1]), &(ps->center[2]), &(ps->rad));
if (instat != 4)
{
printf("Error in SphRead: sphere %ld.\n", i);
exit(1);
}
ps->center[3] = 1.0; /* w initialization. */
ps->rad2 = ps->rad*ps->rad;
pe->data = (CHAR *)ps;
pe->parent = po;
SphElementBoundBox(pe, ps);
ps++;
pe++;
}
}