/**************************************************************************** * polygon.c * * This module implements functions that manipulate polygons. * * This module was written by Dieter Bayer [DB]. * * from Persistence of Vision(tm) Ray Tracer * Copyright 1996 Persistence of Vision Team *--------------------------------------------------------------------------- * NOTICE: This source code file is provided so that users may experiment * with enhancements to POV-Ray and to port the software to platforms other * than those supported by the POV-Ray Team. There are strict rules under * which you are permitted to use this file. The rules are in the file * named POVLEGAL.DOC which should be distributed with this file. If * POVLEGAL.DOC is not available or for more info please contact the POV-Ray * Team Coordinator by leaving a message in CompuServe's Graphics Developer's * Forum. The latest version of POV-Ray may be found there as well. * * This program is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * *****************************************************************************/ /**************************************************************************** * * Explanation: * * Syntax: * * --- * * The "inside polygon"-test was taken from: * * E. Haines, "An Introduction to Ray-Tracing", ..., pp. 53-59 * * --- * * May 1994 : Creation. [DB] * * Oct 1994 : Changed polygon structure. Polygon points are now stored * in a seperate structure. This - together with the use of * a transformation - allows to keep just one point definition * for multiple copies of one polygon. [DB] * *****************************************************************************/ #include "frame.h" #include "vector.h" #include "povproto.h" #include "matrices.h" #include "objects.h" #include "polygon.h" #include "povray.h" /***************************************************************************** * Local preprocessor defines ******************************************************************************/ /* Minimal intersection depth for a valid intersection. */ #define DEPTH_TOLERANCE 1.0e-8 /* If |x| < ZERO_TOLERANCE x is assumed to be 0. */ #define ZERO_TOLERANCE 1.0e-10 /***************************************************************************** * Static functions ******************************************************************************/ static int intersect_poylgon PARAMS((RAY *Ray, POLYGON *Polyg, DBL *Depth)); static int in_polygon PARAMS((int Number, UV_VECT *Points, DBL u, DBL v)); static int All_Polygon_Intersections PARAMS((OBJECT *Object, RAY *Ray, ISTACK *Depth_Stack)); static int Inside_Polygon PARAMS((VECTOR point, OBJECT *Object)); static void Polygon_Normal PARAMS((VECTOR Result, OBJECT *Object, INTERSECTION *Inter)); static void *Copy_Polygon PARAMS((OBJECT *Object)); static void Translate_Polygon PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Rotate_Polygon PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Scale_Polygon PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Transform_Polygon PARAMS((OBJECT *Object, TRANSFORM *Trans)); static void Invert_Polygon PARAMS((OBJECT *Object)); static void Destroy_Polygon PARAMS((OBJECT *Object)); static void Compute_Polygon_BBox PARAMS((POLYGON *Polyg)); /***************************************************************************** * Local variables ******************************************************************************/ static METHODS Polygon_Methods = { All_Polygon_Intersections, Inside_Polygon, Polygon_Normal, Copy_Polygon, Translate_Polygon, Rotate_Polygon, Scale_Polygon, Transform_Polygon, Invert_Polygon, Destroy_Polygon }; /***************************************************************************** * * FUNCTION * * All_Polygon_Intersections * * INPUT * * Object - Object * Ray - Ray * Depth_Stack - Intersection stack * * OUTPUT * * Depth_Stack * * RETURNS * * int - TRUE, if a intersection was found * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Determine ray/polygon intersection and clip intersection found. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static int All_Polygon_Intersections(Object, Ray, Depth_Stack) OBJECT *Object; RAY *Ray; ISTACK *Depth_Stack; { DBL Depth; VECTOR IPoint; if (intersect_poylgon(Ray, (POLYGON *)Object, &Depth)) { VEvaluateRay(IPoint, Ray->Initial, Depth, Ray->Direction); if (Point_In_Clip(IPoint, Object->Clip)) { push_entry(Depth, IPoint, Object, Depth_Stack); return(TRUE); } } return(FALSE); } /***************************************************************************** * * FUNCTION * * intersect_poylgon * * INPUT * * Ray - Ray * Polyg - Polygon * Depth - Depth of intersection found * * OUTPUT * * Depth * * RETURNS * * int - TRUE if intersection found * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Determine ray/polygon intersection. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static int intersect_poylgon(Ray, Polyg, Depth) RAY *Ray; POLYGON *Polyg; DBL *Depth; { DBL x, y, len; VECTOR p, d; /* Don't test degenerate polygons. */ if (Test_Flag(Polyg, DEGENERATE_FLAG)) { return(FALSE); } Increase_Counter(stats[Ray_Polygon_Tests]); /* Transform the ray into the polygon space. */ MInvTransPoint(p, Ray->Initial, Polyg->Trans); MInvTransDirection(d, Ray->Direction, Polyg->Trans); VLength(len, d); VInverseScaleEq(d, len); /* Intersect ray with the plane in which the polygon lies. */ if (fabs(d[Z]) < ZERO_TOLERANCE) { return(FALSE); } *Depth = -p[Z] / d[Z]; if ((*Depth < DEPTH_TOLERANCE) || (*Depth > Max_Distance)) { return(FALSE); } /* Does the intersection point lie inside the polygon? */ x = p[X] + *Depth * d[X]; y = p[Y] + *Depth * d[Y]; if (in_polygon(Polyg->Data->Number, Polyg->Data->Points, x, y)) { Increase_Counter(stats[Ray_Polygon_Tests_Succeeded]); *Depth /= len; return (TRUE); } else { return (FALSE); } } /***************************************************************************** * * FUNCTION * * Inside_Polygon * * INPUT * * IPoint - Intersection point * Object - Object * * OUTPUT * * RETURNS * * int - always FALSE * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Polygons can't be used in CSG. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static int Inside_Polygon(IPoint, Object) VECTOR IPoint; OBJECT *Object; { return(FALSE); } /***************************************************************************** * * FUNCTION * * Polygon_Normal * * INPUT * * Result - Normal vector * Object - Object * Inter - Intersection found * * OUTPUT * * Result * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Calculate the normal of the polygon in a given point. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Polygon_Normal(Result, Object, Inter) OBJECT *Object; VECTOR Result; INTERSECTION *Inter; { Assign_Vector(Result, ((POLYGON *)Object)->S_Normal); } /***************************************************************************** * * FUNCTION * * Translate_Polygon * * INPUT * * Object - Object * Vector - Translation vector * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Translate a polygon. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Translate_Polygon(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { Transform_Polygon(Object, Trans); } /***************************************************************************** * * FUNCTION * * Rotate_Polygon * * INPUT * * Object - Object * Vector - Rotation vector * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Rotate a polygon. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Rotate_Polygon(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { Transform_Polygon(Object, Trans); } /***************************************************************************** * * FUNCTION * * Scale_Polygon * * INPUT * * Object - Object * Vector - Scaling vector * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Scale a polygon. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Scale_Polygon(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { Transform_Polygon(Object, Trans); } /***************************************************************************** * * FUNCTION * * Transform_Polygon * * INPUT * * Object - Object * Trans - Transformation to apply * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Transform a polygon by transforming all points * and recalculating the polygon. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Transform_Polygon(Object, Trans) OBJECT *Object; TRANSFORM *Trans; { VECTOR N; POLYGON *Polyg = (POLYGON *)Object; Compose_Transforms(Polyg->Trans, Trans); Make_Vector(N, 0.0, 0.0, 1.0); MTransNormal(Polyg->S_Normal, N, Polyg->Trans); VNormalizeEq(Polyg->S_Normal); Compute_Polygon_BBox(Polyg); } /***************************************************************************** * * FUNCTION * * Invert_Polygon * * INPUT * * Object - Object * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Invert a polygon. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Invert_Polygon(Object) OBJECT *Object; { } /***************************************************************************** * * FUNCTION * * Create_Polygon * * INPUT * * OUTPUT * * RETURNS * * POLYGON * - new polygon * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Create a new polygon. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ POLYGON *Create_Polygon() { POLYGON *New; New = (POLYGON *)POV_MALLOC(sizeof(POLYGON), "polygon"); INIT_OBJECT_FIELDS(New,POLYGON_OBJECT,&Polygon_Methods) New->Trans = Create_Transform(); Make_Vector(New->S_Normal, 0.0, 0.0, 1.0); New->Data = NULL; return (New); } /***************************************************************************** * * FUNCTION * * Copy_Polygon * * INPUT * * Object - Object * * OUTPUT * * RETURNS * * void * - New polygon * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Copy a polygon structure. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void *Copy_Polygon(Object) OBJECT *Object; { POLYGON *New, *Polyg = (POLYGON *)Object; New = Create_Polygon (); /* Get rid of the transformation created in Create_Polygon(). */ Destroy_Transform(New->Trans); /* Copy polygon. */ *New = *Polyg; New->Trans = Copy_Transform(Polyg->Trans); New->Data->References++; return (New); } /***************************************************************************** * * FUNCTION * * Destroy_Polygon * * INPUT * * Object - Object * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Destroy a polygon. * * CHANGES * * May 1994 : Creation. * * Dec 1994 : Fixed memory leakage. [DB] * ******************************************************************************/ static void Destroy_Polygon(Object) OBJECT *Object; { POLYGON *Polyg = (POLYGON *)Object; if (--(Polyg->Data->References) == 0) { POV_FREE (Polyg->Data->Points); POV_FREE (Polyg->Data); } Destroy_Transform(Polyg->Trans); POV_FREE (Object); } /***************************************************************************** * * FUNCTION * * Compute_Polygon * * INPUT * * Polyg - Polygon * Points - 3D points describing the polygon * * OUTPUT * * Polyg * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Compute the following things for a given polygon: * * - Polygon transformation * * - Array of 2d points describing the shape of the polygon * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ void Compute_Polygon(Polyg, Number, Points) POLYGON *Polyg; int Number; VECTOR *Points; { int i; DBL x, y, z, d; VECTOR o, u, v, w, N; MATRIX a, b; /* Create polygon data. */ if (Polyg->Data == NULL) { Polyg->Data = (POLYGON_DATA *)POV_MALLOC(sizeof(POLYGON_DATA), "polygon points"); Polyg->Data->References = 1; Polyg->Data->Number = Number; Polyg->Data->Points = (UV_VECT *)POV_MALLOC(Number*sizeof(UV_VECT), "polygon points"); } else { Error("Polygon data already computed."); } /* Get polygon's coordinate system (one of the many possible) */ Assign_Vector(o, Points[0]); /* Find valid, i.e. non-zero u vector. */ for (i = 1; i < Number; i++) { VSub(u, Points[i], o); if (VSumSqr(u) > EPSILON) { break; } } if (i == Number) { Set_Flag(Polyg, DEGENERATE_FLAG); Warning(0.0, "Points in polygon are co-linear. Ignoring polygon.\n"); } /* Find valid, i.e. non-zero v and w vectors. */ for (i++; i < Number; i++) { VSub(v, Points[i], o); VCross(w, u, v); if ((VSumSqr(v) > EPSILON) && (VSumSqr(w) > EPSILON)) { break; } } if (i == Number) { Set_Flag(Polyg, DEGENERATE_FLAG); Warning(0.0, "Points in polygon are co-linear. Ignoring polygon.\n"); } VCross(u, v, w); VCross(v, w, u); VNormalize(u, u); VNormalize(v, v); VNormalize(w, w); MIdentity(a); MIdentity(b); a[3][0] = -o[X]; a[3][1] = -o[Y]; a[3][2] = -o[Z]; b[0][0] = u[X]; b[1][0] = u[Y]; b[2][0] = u[Z]; b[0][1] = v[X]; b[1][1] = v[Y]; b[2][1] = v[Z]; b[0][2] = w[X]; b[1][2] = w[Y]; b[2][2] = w[Z]; MTimes(Polyg->Trans->inverse, a, b); MInvers(Polyg->Trans->matrix, Polyg->Trans->inverse); /* Project points onto the u,v-plane (3D --> 2D) */ for (i = 0; i < Number; i++) { x = Points[i][X] - o[X]; y = Points[i][Y] - o[Y]; z = Points[i][Z] - o[Z]; d = x * w[X] + y * w[Y] + z * w[Z]; if (fabs(d) > ZERO_TOLERANCE) { Set_Flag(Polyg, DEGENERATE_FLAG); Warning(0.0, "Points in polygon are not co-planar. Ignoring polygons.\n"); } Polyg->Data->Points[i][X] = x * u[X] + y * u[Y] + z * u[Z]; Polyg->Data->Points[i][Y] = x * v[X] + y * v[Y] + z * v[Z]; } Make_Vector(N, 0.0, 0.0, 1.0); MTransNormal(Polyg->S_Normal, N, Polyg->Trans); VNormalizeEq(Polyg->S_Normal); Compute_Polygon_BBox(Polyg); } /***************************************************************************** * * FUNCTION * * Compute_Polygon_BBox * * INPUT * * Polyg - Polygon * * OUTPUT * * Polyg * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Calculate the bounding box of a polygon. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Compute_Polygon_BBox(Polyg) POLYGON *Polyg; { int i; VECTOR p, Puv, Min, Max; Min[X] = Min[Y] = Min[Z] = BOUND_HUGE; Max[X] = Max[Y] = Max[Z] = -BOUND_HUGE; for (i = 0; i < Polyg->Data->Number; i++) { Puv[X] = Polyg->Data->Points[i][X]; Puv[Y] = Polyg->Data->Points[i][Y]; Puv[Z] = 0.0; MTransPoint(p, Puv, Polyg->Trans); Min[X] = min(Min[X], p[X]); Min[Y] = min(Min[Y], p[Y]); Min[Z] = min(Min[Z], p[Z]); Max[X] = max(Max[X], p[X]); Max[Y] = max(Max[Y], p[Y]); Max[Z] = max(Max[Z], p[Z]); } Make_BBox_from_min_max(Polyg->BBox, Min, Max); if (fabs(Polyg->BBox.Lengths[X]) < Small_Tolerance) { Polyg->BBox.Lower_Left[X] -= Small_Tolerance; Polyg->BBox.Lengths[X] += 2.0 * Small_Tolerance; } if (fabs(Polyg->BBox.Lengths[Y]) < Small_Tolerance) { Polyg->BBox.Lower_Left[Y] -= Small_Tolerance; Polyg->BBox.Lengths[Y] += 2.0 * Small_Tolerance; } if (fabs(Polyg->BBox.Lengths[Z]) < Small_Tolerance) { Polyg->BBox.Lower_Left[Z] -= Small_Tolerance; Polyg->BBox.Lengths[Z] += 2.0 * Small_Tolerance; } } /***************************************************************************** * * FUNCTION * * in_polygon * * INPUT * * Number - Number of points * Points - Points describing polygon's shape * u, v - 2D-coordinates of the point to test * * OUTPUT * * RETURNS * * int - TRUE, if inside * * AUTHOR * * Eric Haines, 3D/Eye Inc, erich@eye.com * * DESCRIPTION * * ======= Crossings Multiply algorithm =================================== * * This version is usually somewhat faster than the original published in * Graphics Gems IV; by turning the division for testing the X axis crossing * into a tricky multiplication test this part of the test became faster, * which had the additional effect of making the test for "both to left or * both to right" a bit slower for triangles than simply computing the * intersection each time. The main increase is in triangle testing speed, * which was about 15% faster; all other polygon complexities were pretty much * the same as before. On machines where division is very expensive (not the * case on the HP 9000 series on which I tested) this test should be much * faster overall than the old code. Your mileage may (in fact, will) vary, * depending on the machine and the test data, but in general I believe this * code is both shorter and faster. This test was inspired by unpublished * Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson. * Related work by Samosky is in: * * Samosky, Joseph, "SectionView: A system for interactively specifying and * visualizing sections through three-dimensional medical image data", * M.S. Thesis, Department of Electrical Engineering and Computer Science, * Massachusetts Institute of Technology, 1993. * * * Shoot a test ray along +X axis. The strategy is to compare vertex Y values * to the testing point's Y and quickly discard edges which are entirely to one * side of the test ray. * * CHANGES * * - * ******************************************************************************/ static int in_polygon(Number, Points, u, v) int Number; UV_VECT *Points; DBL u, v; { register int i, yflag0, yflag1, inside_flag; register DBL ty, tx; register DBL *vtx0, *vtx1, *first; tx = u; ty = v; vtx0 = &Points[0][X]; vtx1 = &Points[1][X]; first = vtx0; /* get test bit for above/below X axis */ yflag0 = (vtx0[Y] >= ty); inside_flag = FALSE; for (i = 1; i < Number; ) { yflag1 = (vtx1[Y] >= ty); /* * Check if endpoints straddle (are on opposite sides) of X axis * (i.e. the Y's differ); if so, +X ray could intersect this edge. * The old test also checked whether the endpoints are both to the * right or to the left of the test point. However, given the faster * intersection point computation used below, this test was found to * be a break-even proposition for most polygons and a loser for * triangles (where 50% or more of the edges which survive this test * will cross quadrants and so have to have the X intersection computed * anyway). I credit Joseph Samosky with inspiring me to try dropping * the "both left or both right" part of my code. */ if (yflag0 != yflag1) { /* * Check intersection of pgon segment with +X ray. * Note if >= point's X; if so, the ray hits it. * The division operation is avoided for the ">=" test by checking * the sign of the first vertex wrto the test point; idea inspired * by Joseph Samosky's and Mark Haigh-Hutchinson's different * polygon inclusion tests. */ if (((vtx1[Y]-ty) * (vtx0[X]-vtx1[X]) >= (vtx1[X]-tx) * (vtx0[Y]-vtx1[Y])) == yflag1) { inside_flag = !inside_flag; } } /* Move to the next pair of vertices, retaining info as possible. */ if ((i < Number-2) && (vtx1[X] == first[X]) && (vtx1[Y] == first[Y])) { vtx0 = &Points[++i][X]; vtx1 = &Points[++i][X]; yflag0 = (vtx0[Y] >= ty); first = vtx0; } else { vtx0 = vtx1; vtx1 = &Points[++i][X]; yflag0 = yflag1; } } return(inside_flag); }