blob: 2588d57a761466cc6e560604b92cceff4b75e077 [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
* tri.c
*
* DESCRIPTION
* This file contains all routines that operate on triangle objects.
*
*/
#include <stdio.h>
#include <math.h>
#include "rt.h"
#define X_NORM 1
#define Y_NORM 2
#define Z_NORM 3
#define CLOCKWISE 1
#define COUNTER_CLOCKWISE 2
#define INSIDE(x, a) (((a) <= 0.0 && (x) >= (a) && (x) <= 0.0) || \
((a) > 0.0 && (x) >= 0.0 && (x) <= (a)))
/*
* Define triangle data structure.
*/
typedef struct tri
{
VEC3 norm; /* Face normal. */
REAL d; /* Plane equation D. */
VEC3 *vptr; /* Global vertex list pointer. */
VEC3 *nptr; /* Global normal list pointer. */
INT vindex[3]; /* Index of vertices. */
INT indx; /* Normal component max flag. */
BOOL norminterp; /* Do normal interpolation? */
BOOL vorder; /* Vertex order orientation. */
}
TRI;
/*
* NAME
* TriName - return the object name
*
* SYNOPSIS
* CHAR *TriName()
*
* RETURNS
* A pointer to the name string.
*/
CHAR *TriName()
{
return ("poly");
}
/*
* NAME
* TriPrint - print the triangle object data to stdout
*
* SYNOPSIS
* VOID TriPrint(po)
* OBJECT *po; // Ptr to triangle object.
*
* RETURNS
* Nothing.
*/
VOID TriPrint(po)
OBJECT *po;
{
INT i, j;
INT *vindex; /* Ptr to vertex index. */
VEC3 *vlist, *vptr; /* Ptr to vertex list. */
TRI *pt; /* Ptr to triangle data. */
ELEMENT *pe; /* Ptr to triangle element. */
pe = po->pelem;
fprintf(stderr, "\ttriangle: %ld triangles.\n", po->numelements);
for (i = 0; i < po->numelements; i++)
{
pt = (TRI *)pe->data;
fprintf(stderr, "\t\tVertices: 3 Plane eq: %f %f %f %f\n", pt->norm[0], pt->norm[1], pt->norm[2], pt->d);
vlist = pt->vptr;
vindex = pt->vindex;
for (j = 0; j < 3; j++)
{
vptr = vlist + (*vindex);
fprintf(stderr, "\t\t%f %f %f \n", (*vptr)[0], (*vptr)[1], (*vptr)[2]);
vindex++;
}
pe++;
}
}
/*
* NAME
* TriElementBoundBox - compute triangle element bounding box
*
* SYNOPSIS
* VOID TriElementBoundBox(pe, pt)
* ELEMENT *pe; // Ptr to triangle element.
* TRI *pt; // Ptr to triangle data.
*
* RETURNS
* Nothing.
*/
VOID TriElementBoundBox(pe, pt)
ELEMENT *pe;
TRI *pt;
{
INT i; /* Index. */
INT *vindex; /* Vertex index pointer. */
BBOX *pbb; /* Ptr to bounding box. */
VEC3 *vlist, *vptr; /* Vertex list pointer. */
REAL minx, maxx;
REAL miny, maxy;
REAL minz, maxz;
pbb = &(pe->bv);
minx = miny = minz = HUGE_REAL;
maxx = maxy = maxz = -HUGE_REAL;
vlist = pt->vptr;
vindex = pt->vindex;
for (i = 0; i < 3; i++)
{
vptr = vlist + (*vindex);
minx = Min(minx, (*vptr)[0]);
miny = Min(miny, (*vptr)[1]);
minz = Min(minz, (*vptr)[2]);
maxx = Max(maxx, (*vptr)[0]);
maxy = Max(maxy, (*vptr)[1]);
maxz = Max(maxz, (*vptr)[2]);
vindex++;
}
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
* TriBoundBox - compute triangle object bounding box
*
* SYNOPSIS
* VOID TriBoundBox(po)
* OBJECT *po; // Ptr to triangle object.
*
* RETURNS
* Nothing.
*/
VOID TriBoundBox(po)
OBJECT *po;
{
INT i;
TRI *pt; /* Ptr to triangle data. */
ELEMENT *pe; /* Ptr to triangle 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++)
{
pt = (TRI *)pe->data;
TriElementBoundBox(pe, pt);
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
* TriNormal - compute triangle unit normal at given point on the surface
*
* SYNOPSIS
* VOID TriNormal(hit, Pi, Ni)
* IRECORD *hit; // Ptr to intersection record.
* POINT Pi; // Ipoint.
* POINT Ni; // Normal.
*
* NOTES
* If no normal interpolation, the normal is the face normal. Otherwise,
* interpolate with the normal plane equation.
*
* RETURNS
* Nothing.
*/
VOID TriNormal(hit, Pi, Ni)
IRECORD *hit;
POINT Pi;
POINT Ni;
{
ELEMENT *pe;
TRI *pt; /* Ptr to triangle data. */
VEC3 *pn; /* Ptr to normal. */
VEC3 *n0, *n1, *n2;
/* Retrieve normal from hit triangle. */
pe = hit->pelem;
pt = (TRI *)pe->data;
pn = pt->nptr;
if (pt->norminterp)
{
if (pt->vorder == CLOCKWISE)
{
n0 = pn + pt->vindex[0];
n1 = pn + pt->vindex[1];
n2 = pn + pt->vindex[2];
}
else
{
n0 = pn + pt->vindex[0];
n1 = pn + pt->vindex[2];
n2 = pn + pt->vindex[1];
}
switch (pt->indx)
{
case X_NORM:
hit->b1 /= pt->norm[0];
hit->b2 /= pt->norm[0];
hit->b3 /= pt->norm[0];
break;
case Y_NORM:
hit->b1 /= pt->norm[1];
hit->b2 /= pt->norm[1];
hit->b3 /= pt->norm[1];
break;
case Z_NORM:
hit->b1 /= pt->norm[2];
hit->b2 /= pt->norm[2];
hit->b3 /= pt->norm[2];
break;
}
Ni[0] = hit->b1 * (*n0)[0] + hit->b2 * (*n1)[0] + hit->b3 * (*n2)[0];
Ni[1] = hit->b1 * (*n0)[1] + hit->b2 * (*n1)[1] + hit->b3 * (*n2)[1];
Ni[2] = hit->b1 * (*n0)[2] + hit->b2 * (*n1)[2] + hit->b3 * (*n2)[2];
VecNorm(Ni);
}
else
VecCopy(Ni, pt->norm);
}
/*
* NAME
* TriDataNormalize - normalize triangle data and bounding box
*
* SYNOPSIS
* VOID TriDataNormalize(po, normMat)
* OBJECT *po; // Ptr to triangle object.
* MATRIX normMat; // Normalization matrix.
*
* RETURNS
* Nothing.
*/
VOID TriDataNormalize(po, normMat)
OBJECT *po;
MATRIX normMat;
{
INT i;
POINT coord;
VEC3 *pv; /* Ptr to vertex list. */
TRI *pt; /* Ptr to triangle data. */
ELEMENT *pe; /* Ptr to triangle element. */
/* Normalize bounding box. */
pe = po->pelem;
NormalizeBoundBox(&po->bv, normMat);
/* Normalize vertex list. */
pt = (TRI *)pe->data;
pv = pt->vptr;
coord[0] = (*pv)[0];
coord[1] = (*pv)[1];
coord[2] = (*pv)[2];
coord[3] = 1.0;
/* Transform coordinate by xform matrix. */
while (coord[0] != HUGE_REAL && coord[1] != HUGE_REAL && coord[2] != HUGE_REAL)
{
VecMatMult(coord, normMat, coord);
(*pv)[0] = coord[0];
(*pv)[1] = coord[1];
(*pv)[2] = coord[2];
pv++;
coord[0] = (*pv)[0];
coord[1] = (*pv)[1];
coord[2] = (*pv)[2];
coord[3] = 1.0;
}
/* Normalize bounding boxes of object elements. */
for (i = 0; i < po->numelements; i++)
{
pt = (TRI *)pe->data;
NormalizeBoundBox(&pe->bv, normMat);
/* Find new d of plane equation. */
pv = pt->vptr + (*(pt->vindex));
pt->d = -(pt->norm[0]*(*pv)[0] + pt->norm[1]*(*pv)[1] + pt->norm[2]*(*pv)[2]);
pe++;
}
}
/*
* NAME
* TriPeIntersect - calculate intersection of ray with triangle
*
* SYNOPSIS
* INT TriPeIntersect(pr, pe, hit)
* RAY *pr; // Ptr to incident ray.
* ELEMENT *pe; // Triangle object
* IRECORD *hit; // Intersection record.
*
* NOTES
* Check first to see if ray is parallel to plane or if the intersection
* point with the plane is behind the eye.
*
* RETURNS
* The number of intersection points.
*/
INT TriPeIntersect(pr, pe, hit)
RAY *pr;
ELEMENT *pe;
IRECORD *hit;
{
INT i;
REAL Rd_dot_Pn; /* Polygon normal dot ray direction. */
REAL Ro_dot_Pn; /* Polygon normal dot ray origin. */
REAL q1, q2;
REAL tval; /* Intersection t distance value. */
VEC3 *v1, *v2, *v3; /* Vertex list pointers. */
VEC3 e1, e2, e3; /* Edge vectors. */
TRI *pt; /* Ptr to triangle data. */
pt = (TRI *)pe->data;
Rd_dot_Pn = VecDot(pt->norm, pr->D);
if (ABS(Rd_dot_Pn) < RAYEPS) /* Ray is parallel. */
return (0);
Ro_dot_Pn = VecDot(pt->norm, pr->P);
tval = -(pt->d + Ro_dot_Pn)/Rd_dot_Pn; /* Intersection distance. */
if (tval < RAYEPS) /* Intersects behind ray. */
return (0);
/*
* This algorithm works for vertices in counter-clockwise order,
* so reverse the vertices if they are clockwise.
*/
v1 = pt->vptr + pt->vindex[0]; /* Compute first vertex pointer. */
if (pt->vorder == COUNTER_CLOCKWISE)
{
v2 = pt->vptr + pt->vindex[2];
v3 = pt->vptr + pt->vindex[1];
}
else
{
v2 = pt->vptr + pt->vindex[1];
v3 = pt->vptr + pt->vindex[2];
}
e1[0] = (*v2)[0] - (*v1)[0];
e1[1] = (*v2)[1] - (*v1)[1];
e1[2] = (*v2)[2] - (*v1)[2];
e2[0] = (*v3)[0] - (*v2)[0];
e2[1] = (*v3)[1] - (*v2)[1];
e2[2] = (*v3)[2] - (*v2)[2];
e3[0] = (*v1)[0] - (*v3)[0];
e3[1] = (*v1)[1] - (*v3)[1];
e3[2] = (*v1)[2] - (*v3)[2];
/* Triangle containment. */
switch (pt->indx)
{
case X_NORM:
q1 = pr->P[1] + tval*pr->D[1];
q2 = pr->P[2] + tval*pr->D[2];
hit->b1 = e2[1] * (q2 - (*v2)[2]) - e2[2] * (q1 - (*v2)[1]);
if (!INSIDE(hit->b1, pt->norm[0]))
return (0);
hit->b2 = e3[1] * (q2 - (*v3)[2]) - e3[2] * (q1 - (*v3)[1]);
if (!INSIDE(hit->b2, pt->norm[0]))
return (0);
hit->b3 = e1[1] * (q2 - (*v1)[2]) - e1[2] * (q1 - (*v1)[1]);
if (!INSIDE(hit->b3, pt->norm[0]))
return (0);
break;
case Y_NORM:
q1 = pr->P[0] + tval*pr->D[0];
q2 = pr->P[2] + tval*pr->D[2];
hit->b1 = e2[2] * (q1 - (*v2)[0]) - e2[0] * (q2 - (*v2)[2]);
if (!INSIDE(hit->b1, pt->norm[1]))
return (0);
hit->b2 = e3[2] * (q1 - (*v3)[0]) - e3[0] * (q2 - (*v3)[2]);
if (!INSIDE(hit->b2, pt->norm[1]))
return (0);
hit->b3 = e1[2] * (q1 - (*v1)[0]) - e1[0] * (q2 - (*v1)[2]);
if (!INSIDE(hit->b3, pt->norm[1]))
return (0);
break;
case Z_NORM:
q1 = pr->P[0] + tval*pr->D[0];
q2 = pr->P[1] + tval*pr->D[1];
hit->b1 = e2[0] * (q2 - (*v2)[1]) - e2[1] * (q1 - (*v2)[0]);
if (!INSIDE(hit->b1, pt->norm[2]))
return (0);
hit->b2 = e3[0] * (q2 - (*v3)[1]) - e3[1] * (q1 - (*v3)[0]);
if (!INSIDE(hit->b2, pt->norm[2]))
return (0);
hit->b3 = e1[0] * (q2 - (*v1)[1]) - e1[1] * (q1 - (*v1)[0]);
if (!INSIDE(hit->b3, pt->norm[2]))
return (0);
break;
}
IsectAdd(hit, tval, pe);
return (1);
}
/*
* NAME
* TriIntersect - call triangle object intersection routine
*
* SYNOPSIS
* INT TriIntersect(pr, po, hit)
* RAY *pr; // Ptr to incident ray.
* OBJECT *po; // Ptr to triangle object.
* IRECORD *hit; // Intersection record.
*
* RETURNS
* The number of intersections found.
*/
INT TriIntersect(pr, po, hit)
RAY *pr;
OBJECT *po;
IRECORD *hit;
{
INT i;
INT nhits; /* # hits in polyhedra. */
ELEMENT *pe; /* Ptr to triangle element. */
IRECORD newhit; /* Hit list. */
/* Traverse triangle list to find intersections. */
nhits = 0;
pe = po->pelem;
hit[0].t = HUGE_REAL;
for (i = 0; i < po->numelements; i++)
{
if (TriPeIntersect(pr, pe, &newhit))
{
nhits++;
if (newhit.t < hit[0].t)
hit[0] = newhit;
}
pe++;
}
return (nhits);
}
/*
* NAME
* TriTransform - transform triangle object by a transformation matrix
*
* SYNOPSIS
* VOID TriTransform(po, xtrans, xinvT)
* OBJECT *po; // Ptr to triangle object.
* MATRIX xtrans; // Transformation matrix.
* MATRIX xinvT; // Transpose of inverse matrix.
*
* NOTES
* Routine must:
* - transform face normals
* - transform vertices
* - calculate plane equation d variables
*
* RETURNS
* Nothing.
*/
VOID TriTransform(po, xtrans, xinvT)
OBJECT *po;
MATRIX xtrans;
MATRIX xinvT;
{
INT i, j; /* Indices. */
INT numelems; /* # of elements. */
INT *vindex; /* Vertex index pointer. */
VEC3 *vptr, *vp; /* Vertex list pointers. */
VEC3 *nptr, *np; /* Normal list pointers. */
VEC3 *vp1, *vp2, *vp3;
VEC3 vec1, vec2;
VEC4 pnorm;
VEC4 norm, coord; /* Transform 4 vectors. */
TRI *pt; /* Ptr to triangle data. */
ELEMENT *pe; /* Ptr to triangle element. */
pe = po->pelem;
numelems = po->numelements;
/* Get vertex list address and transform vertices. */
pt = (TRI *)pe->data;
vptr = pt->vptr;
nptr = pt->nptr;
coord[0] = (*vptr)[0];
coord[1] = (*vptr)[1];
coord[2] = (*vptr)[2];
coord[3] = 1.0;
while (coord[0] != HUGE_REAL && coord[1] != HUGE_REAL && coord[2] != HUGE_REAL)
{
/* Transform coordinate by xform matrix. */
VecMatMult(coord, xtrans, coord);
(*vptr)[0] = coord[0];
(*vptr)[1] = coord[1];
(*vptr)[2] = coord[2];
vptr++;
coord[0] = (*vptr)[0];
coord[1] = (*vptr)[1];
coord[2] = (*vptr)[2];
coord[3] = 1.0;
/* Transform normals. */
norm[0] = (*nptr)[0];
norm[1] = (*nptr)[1];
norm[2] = (*nptr)[2];
norm[3] = 0.0;
VecMatMult(norm, xinvT, norm);
VecNorm(norm);
(*nptr)[0] = norm[0];
(*nptr)[1] = norm[1];
(*nptr)[2] = norm[2];
nptr++;
}
/* Transform triangle list. */
for (i = 0; i < numelems; i++)
{
pt = (TRI *)pe->data;
vindex = pt->vindex;
vptr = pt->vptr;
vp1 = vptr + (*vindex);
vindex++;
vp2 = vptr + (*vindex);
VecSub(vec1, (*vp2), (*vp1));
vindex++;
vp3 = vptr + (*vindex);
VecSub(vec2, (*vp3), (*vp1));
VecCross(pt->norm, vec1, vec2);
/* Calculate plane equation variable D. */
vp = pt->vptr + *(pt->vindex);
pt->d = -(pt->norm[0]*(*vp)[0] + pt->norm[1]*(*vp)[1] + pt->norm[2]*(*vp)[2]);
if (pt->norminterp)
{
vindex = pt->vindex;
np = pt->nptr + (*vindex);
VecCopy(pnorm, pt->norm);
VecNorm(pnorm);
if (VecDot(pnorm, (*np)) >= 0.0)
pt->vorder = CLOCKWISE;
else
{
pt->vorder = COUNTER_CLOCKWISE;
VecScale(pt->norm, -1.0, pt->norm);
pt->d = -pt->d;
}
}
/* Set best axis projection. */
norm[0] = ABS(pt->norm[0]);
norm[1] = ABS(pt->norm[1]);
norm[2] = ABS(pt->norm[2]);
if (norm[0] >= norm[1] && norm[0] >= norm[2])
pt->indx = X_NORM;
else
if (norm[1] >= norm[0] && norm[1] >= norm[2])
pt->indx = Y_NORM;
else
pt->indx = Z_NORM;
pe++;
}
}
/*
* NAME
* TriRead - read triangle object data from file
*
* SYNOPSIS
* VOID TriRead(po, pf)
* OBJECT *po; // Ptr to triangle object.
* FILE *pf; // Ptr to file.
*
* RETURNS
* Nothing.
*/
VOID TriRead(po, pf)
OBJECT *po;
FILE *pf;
{
INT i, j; /* Indices. */
INT instat; /* Read status. */
INT totalverts; /* Total # of vertices in tri mesh. */
CHAR normstr[5]; /* Face/vertex normal flag string. */
BOOL pnormals; /* Face normals present? */
BOOL vnormals; /* Vertex normals present? */
VEC3 *vlist, *vptr; /* Ptr to vertex list. */
VEC3 *nlist, *nptr; /* Ptr to normal list. */
TRI *pt; /* Ptr to triangle data. */
ELEMENT *pe; /* Ptr to triangle element. */
pe = po->pelem;
/* Allocate space for object data. */
instat = fscanf(pf, "%ld", &totalverts);
if (instat != 1)
{
printf("Error in TriRead: totalverts.\n");
exit(-1);
}
pt = GlobalMalloc(sizeof(TRI )*po->numelements, "tri.c");
vlist = GlobalMalloc(sizeof(VEC3)*(totalverts + 1), "tri.c");
nlist = GlobalMalloc(sizeof(VEC3)*totalverts, "tri.c");
vptr = vlist;
nptr = nlist;
/* Are triangle face normals supplied? */
instat = fscanf(pf, "%s\n", normstr);
if (instat != 1)
{
printf("Error in TriRead: face normal indicator.\n");
exit(-1);
}
pnormals = (normstr[2] == 'y' ? TRUE : FALSE);
/* Are vertex normal supplied? */
instat = fscanf(pf, "%s\n", normstr);
if (instat != 1)
{
printf("Error in TriRead: vertex normal indicator.\n");
exit(-1);
}
vnormals = (normstr[2] == 'y' ? TRUE : FALSE);
/* Read vertex list. */
for (i = 0; i < totalverts; i++)
{
instat = fscanf(pf, "%lf %lf %lf", &(*vptr)[0], &(*vptr)[1], &(*vptr)[2]);
if (instat != 3)
{
printf("Error in TriRead: vertex %ld.\n", i);
exit(-1);
}
if (vnormals)
{
instat = fscanf(pf, "%lf %lf %lf", &(*nptr)[0], &(*nptr)[1], &(*nptr)[2]);
if (instat != 3)
{
printf("Error in TriRead: vertex normal %ld.\n", i);
exit(-1);
}
nptr++;
}
vptr++;
}
(*vptr)[0] = HUGE_REAL;
(*vptr)[1] = HUGE_REAL;
(*vptr)[2] = HUGE_REAL;
/* Read triangle list. */
for (i = 0; i < po->numelements; i++)
{
if (pnormals)
{
instat = fscanf(pf, " %lf %lf %lf", &(pt->norm[0]), &(pt->norm[1]), &(pt->norm[2]));
if (instat != 3)
{
printf("Error in TriRead: face normal %ld.\n", i);
exit(-1);
}
}
pt->vptr = vlist;
pt->nptr = nlist;
pt->norminterp = vnormals;
instat = fscanf(pf, "%ld %ld %ld", &(pt->vindex[0]), &(pt->vindex[1]), &(pt->vindex[2]));
if (instat != 3)
{
printf("Error in TriRead: vertex index %ld.\n", i);
exit(-1);
}
pe->data = (CHAR *)pt;
pe->parent = po;
TriElementBoundBox(pe, pt);
pt++;
pe++;
}
/* Free normal storage if not interpolating. */
if (!vnormals)
GlobalFree(nlist);
}