home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
GRAPHICS
/
rayshade.lzh
/
poly.c
< prev
next >
Wrap
Text File
|
1990-05-08
|
8KB
|
310 lines
/*
* poly.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: poly.c,v 3.0.1.2 90/04/04 19:01:19 craig Exp $
*
* $Log: poly.c,v $
* Revision 3.0.1.2 90/04/04 19:01:19 craig
* patch5: Corrected free()-related problems.
*
* Revision 3.0.1.1 89/12/06 16:33:38 craig
* patch2: Added calls to new error/warning routines.
*
* Revision 3.0 89/10/27 02:05:59 craig
* Baseline for first official release.
*
*/
#include <stdio.h>
#include <math.h>
#include "constants.h"
#include "typedefs.h"
#include "funcdefs.h"
/*
* Create a reference to a polygon with vertices equal to those
* on the linked-list "plist."
*/
Object *
makpoly(surf, plist, npoints)
char *surf;
PointList *plist;
int npoints;
{
Polygon *poly;
Primitive *prim;
Object *newobj;
double indexval;
Vector edge1, edge2, anorm;
PointList *cur, *pltmp;
int i;
extern int TrashBadPoly;
prim = mallocprim();
prim->type = POLY;
prim->surf = find_surface(surf);
poly = (Polygon *)Malloc(sizeof(Polygon));
prim->objpnt.p_poly = poly;
newobj = new_object(NULL, POLY, (char *)prim, (Trans *)NULL);
/*
* Allocate space for the vertices.
*/
poly->points = (Vector *)Malloc((unsigned)(npoints*sizeof(Vector)));
poly->npoints = npoints;
/*
* Copy the vertices from the linked list to the array, freeing
* the linked list as we go so that the caller doesn't have
* to worry about doing so.
*/
i = npoints -1;
for(cur = plist; cur != (PointList *)0; cur = pltmp) {
poly->points[i--] = cur->vec;
pltmp = cur->next;
free((char *)cur);
}
/*
* Find normal to polygon. Check all edges before giving
* up, just to be relatively nice about things.
*/
vecsub(poly->points[1], poly->points[0], &edge1);
for(i = 1;i < poly->npoints;i++) {
if(dotp(&edge1, &edge1) == 0.) {
if (TrashBadPoly) {
free((char *)poly->points);
free((char *)poly);
free((char *)prim);
free((char *)newobj);
return (Object *)0;
}
}
vecsub(poly->points[(i+1)%npoints], poly->points[i], &edge2);
if(crossp(&poly->norm, &edge1, &edge2) != 0.)
break;
edge1 = edge2;
}
if(i >= poly->npoints) {
/*
* If we walked all the way through the list,
* then we didn't find a valid normal vector -- we
* must have a degenerate polygon of some sort.
*/
yywarning("Degenerate polygon.\n");
free((char *)poly->points);
free((char *)poly);
free((char *)prim);
free((char *)newobj);
return (Object *)0;
}
/*
* Compute and store the plane constant.
*/
poly->d = dotp(&poly->norm, &poly->points[0]);
/*
* Find which part of the normal vector is "dominant." This
* is used to turn the point-in-polygon test into a 2D problem.
*/
anorm.x = abs(poly->norm.x);
anorm.y = abs(poly->norm.y);
anorm.z = abs(poly->norm.z);
indexval = max(anorm.y, anorm.z);
indexval = max(anorm.x, indexval);
if(indexval == anorm.x)
poly->index = XNORMAL;
else if(indexval == anorm.y)
poly->index = YNORMAL;
else
poly->index = ZNORMAL;
return newobj;
}
/*
* Quadrants are defined as:
* |
* 1 | 0
* |
* -------c--------
* |
* 2 | 3
* |
*/
#define quadrant(p, c) ((p.u<c.u) ? ((p.v<c.v) ? 2 : 1) : ((p.v<c.v) ? 3 : 0))
/*
* Project a point in 3-space to the plane whose normal is indicated by "i."
*/
#define project(r, p, i) {switch(i) { \
case XNORMAL: \
r.u = p.y; \
r.v = p.z; \
break; \
case YNORMAL: \
r.u = p.x; \
r.v = p.z; \
break; \
case ZNORMAL: \
r.u = p.x; \
r.v = p.y; \
break; \
} }
/*
* Perform ray-polygon intersection test.
*/
double
intpoly(pos, ray, obj)
Vector *pos, *ray;
Primitive *obj;
{
register Polygon *poly;
register int winding, i;
int quad, lastquad;
double dist, left, right;
Vec2d center, cur, last;
extern unsigned long primtests[];
primtests[POLY]++;
poly = obj->objpnt.p_poly;
/*
* First, find where ray hits polygon plane, projecting
* along the polygon's dominant normal component.
*/
dist = dotp(&poly->norm, ray);
if(dist == 0.)
/*
* No intersection with polygon plane.
*/
return 0.;
dist = (poly->d - dotp(&poly->norm, pos)) / dist;
if(dist <= 0.)
/*
* The intersection point is behind the ray origin.
*/
return 0.;
/*
* Compute the point of intersection, projected appropriately.
*/
if(poly->index == XNORMAL) {
center.u = pos->y + dist * ray->y;
center.v = pos->z + dist * ray->z;
} else if(poly->index == YNORMAL) {
center.v = pos->z + dist * ray->z;
center.u = pos->x + dist * ray->x;
} else {
center.u = pos->x + dist * ray->x;
center.v = pos->y + dist * ray->y;
}
/*
* Is the point inside the polygon?
*
* Compute the winding number by finding the quadrant each
* polygon point lies in with respect to the the point in
* question, and computing a "delta" (winding number). If we
* end up going around in a complete circle around
* the point (winding number is non-zero at the end), then
* we're inside. Otherwise, the point is outside.
*
* Note that we can turn this into a 2D problem by projecting
* all the points along the axis defined by poly->index, which
* is the "dominant" part of the polygon's normal vector.
*/
winding = 0;
project(last, poly->points[poly->npoints -1], poly->index);
lastquad = quadrant(last, center);
for(i = 0;i < poly->npoints; i++) {
project(cur, poly->points[i], poly->index);
quad = quadrant(cur, center);
if(quad != lastquad) {
if(((lastquad + 1) & 3) == quad)
winding++;
else if(((quad + 1) & 3) == lastquad)
winding--;
else {
/*
* Find where edge crosses
* center's X axis.
*/
right = last.u - cur.u;
left = last.v - cur.v;
left *= center.u - last.u;
if(left + last.v * right > right * center.v)
winding += 2;
else
winding -= 2;
}
lastquad = quad;
}
last = cur;
}
return (winding == 0 ? 0. : dist);
}
/*
* Return the normal to the polygon surface.
*/
/*ARGSUSED*/
nrmpoly(pos, obj, nrm)
Vector *pos, *nrm;
Primitive *obj;
{
*nrm = obj->objpnt.p_poly->norm;
}
/*
* Compute the extent of a polygon
*/
polyextent(obj, bounds)
Primitive *obj;
double bounds[2][3];
{
register Polygon *poly;
register int i;
poly = obj->objpnt.p_poly;
bounds[LOW][X] = bounds[HIGH][X] = poly->points[0].x;
bounds[LOW][Y] = bounds[HIGH][Y] = poly->points[0].y;
bounds[LOW][Z] = bounds[HIGH][Z] = poly->points[0].z;
for(i = 1;i < poly->npoints;i++) {
if(poly->points[i].x < bounds[LOW][X])
bounds[LOW][X] = poly->points[i].x;
if(poly->points[i].x > bounds[HIGH][X])
bounds[HIGH][X] = poly->points[i].x;
if(poly->points[i].y < bounds[LOW][Y])
bounds[LOW][Y] = poly->points[i].y;
if(poly->points[i].y > bounds[HIGH][Y])
bounds[HIGH][Y] = poly->points[i].y;
if(poly->points[i].z < bounds[LOW][Z])
bounds[LOW][Z] = poly->points[i].z;
if(poly->points[i].z > bounds[HIGH][Z])
bounds[HIGH][Z] = poly->points[i].z;
}
}