home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / IRIT / POLY3DRS.ZIP / EVALCOLR.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-05  |  19.9 KB  |  534 lines

  1. /*****************************************************************************
  2. *   Routines to    evaluate vertices colors for all polygons in data. It is     *
  3. * assumed the parser has updated plane equations for all polygons at this    *
  4. * stage (if from reading it in, or by regenerating it), and they were mapped *
  5. * correctly using the global transformation applied to them.             *
  6. *   The vertices are updated for both Flat or Gouraud shading, and color is  *
  7. * evaluated for them, as a color index into the color table. Because of the  *
  8. * way the table is constructed and because two vertices in same polygon can  *
  9. * have two levels of same color, it is o.k. to interplated the color         *
  10. * indices (We assume no specular affects)!                     *
  11. *   As side affect, we use this pass to hash the polygon into global hash    *
  12. * table PolyHasTable, sorted by their Ymin values.                 *
  13. *                                         *
  14. * Written by:  Gershon Elber                Ver 2.0, Mar. 1990   *
  15. *****************************************************************************/
  16.  
  17. #ifdef __MSDOS__
  18. #include <stdlib.h>
  19. #endif /* __MSDOS__ */
  20.  
  21. #include <math.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <time.h>
  25. #include "program.h"
  26. #include "parser.h"
  27.  
  28. #define    MIN_VERTEX_HASH_SIZE    1000  /* Minimum number of entries in table. */
  29.  
  30. typedef struct VertexHashStruct {     /* Used to hash an object vertices. */
  31.     int Entry;
  32.     float Normal[3];
  33.     VertexStruct *PVertex;
  34. } VertexHashStruct;
  35.  
  36. static float MinNormalDotProd; /* Min. normal dot product to accept as same. */
  37. static int VertexHashSize = 0;    /* Size of vertex hash table size allocated. */
  38. static int PolyCount = 0;    /* Number of polygons processed in this module: */
  39.  
  40. static void PrepareAllObjects(FileDescription **FD);
  41. static void VisitObjectTree(BinTree *PBinTree);
  42. static void PrepareOneObject(ObjectStruct *PObject);
  43. static void AverageVrtxNormals(ObjectStruct *PObject);
  44. static void InsertVerticesToHashTable(VertexHashStruct *VerticesHashTable,
  45.                             ObjectStruct *PObject);
  46. static void UpdateVerticesFromHashTable(VertexHashStruct *VerticesHashTable,
  47.                             ObjectStruct *PObject);
  48. static int SameVertexAndNormals(VertexStruct *PVertex1, float *Normal1,
  49.                         VertexHashStruct *PHVertex2);
  50. static float EvalNormalsAngle(VertexStruct *PVertex1, float *Normal1,
  51.                         VertexHashStruct *PHVertex2);
  52. static int EvalVertexKey(VertexStruct *PVertex);
  53. static void EvalOnePolygon(PolygonStruct *PPolygon, int ColorIndex);
  54. static int CosineShading(float *Normal, int ColorIndex);
  55.  
  56. /*****************************************************************************
  57. * Routine to evaluate the vertices colors for both Flat/Gouraud shading, as  *
  58. * indices for the color table.                             *
  59. *****************************************************************************/
  60. void EvalVrtxColors(FileDescription **FD, int NumOfObjects, char **Objects)
  61. {
  62.     int    i;
  63.     long SaveTime = time(NULL);
  64.     struct ObjectStruct    *PObject;
  65.  
  66.     PolyHashTable = (PolygonStruct **)
  67.     MyMalloc(sizeof(PolygonStruct *) * ScreenYSize *
  68.                     ShadingInfo.SubSamplePixel);
  69.  
  70.     for    (i=0; i<ScreenYSize * ShadingInfo.SubSamplePixel; i++)
  71.     PolyHashTable[i] = NULL;
  72.  
  73.     fprintf(stderr, "\nPass 3, Polys [%4d] =      ", NumOfPolygons);
  74.  
  75.     if (NumOfObjects > 0)       /* There was something on command line... */
  76.     for (i=0; i<NumOfObjects; i++) {
  77.         if ((PObject=SearchObject(FD, *Objects)) != (ObjectStruct *) NULL)
  78.         PrepareOneObject(PObject);
  79.     }
  80.     else {            /* Prepare all objects by scanning object trees. */
  81.     PrepareAllObjects(FD);
  82.     }
  83.  
  84.     fprintf(stderr, ",  %ld seconds.", time(NULL) - SaveTime);
  85. }
  86.  
  87. /*****************************************************************************
  88. * Scan all objects.                                 *
  89. *****************************************************************************/
  90. static void PrepareAllObjects(FileDescription **FD)
  91. {
  92.     while (*FD)    VisitObjectTree((*FD++) -> ObjectPointer);
  93. }
  94.  
  95. /*****************************************************************************
  96. * Scanning all the object in tree PBinTree and prepare them.             *
  97. *****************************************************************************/
  98. static void VisitObjectTree(BinTree *PBinTree)
  99. {
  100.     if (PBinTree == (BinTree *)    NULL) return;
  101.  
  102.     VisitObjectTree(PBinTree -> right);
  103.  
  104.     PrepareOneObject(PBinTree -> Data.PObject);
  105.  
  106.     VisitObjectTree(PBinTree -> left);
  107. }
  108.  
  109. /*****************************************************************************
  110. * Routine to prepare one object Object.                         *
  111. *****************************************************************************/
  112. static void PrepareOneObject(ObjectStruct *PObject)
  113. {
  114.     static int WasPolyline = FALSE;
  115.     int Level, ColorIndex = PObject -> Color;
  116.     struct PolygonStruct *Ptemp, *PList = PObject -> PPolygon;
  117.  
  118.     if (GouraudFlag) {
  119.     /* Need to average all vertices normals from adjacent polygons,      */
  120.     /* so we can interpolate them, and select color for them directly.   */
  121.     AverageVrtxNormals(PObject);
  122.     }
  123.  
  124.     while (PList) {
  125.     Ptemp = PList -> Pnext;
  126.  
  127.     EvalOnePolygon(PList, ColorIndex);
  128.  
  129.     /* And add polygon into polygon hash table sorted by Ymin: */
  130.     if (!PList -> Polyline) {
  131.         /* If BackFacingFlag is TRUE, and this polygon has plane     */
  132.         /* suggesting it is back facing - dont insert to hash table. */
  133.         if (!(BackFacingFlag && PList -> Plane[2] < 0.0)) {
  134.         Level = PList -> Ymin;
  135.         Level = BOUND(Level, 0, ScreenYSize *
  136.                 ShadingInfo.SubSamplePixel);/* Be 100% safe. */
  137.         if (Level < ScreenYSize * ShadingInfo.SubSamplePixel) {
  138.             /* Concat to hash table at level Level: */
  139.             PList -> Pnext = PolyHashTable[Level];
  140.             PolyHashTable[Level] = PList;
  141.         }
  142.         }
  143.         else {
  144.         if (!GouraudFlag) PolyCount--;          /* One less polygon... */
  145.         }
  146.     }
  147.     else {
  148.         if (!WasPolyline) {
  149.         WasPolyline = TRUE;
  150.         if (MoreFlag)
  151.             fprintf(stderr, "\nPolylines are not supported, ignored\n");
  152.         }
  153.     }
  154.  
  155.     PList =    Ptemp;
  156.     }
  157. }
  158.  
  159. /*****************************************************************************
  160. * Routine to update average normals of adjacent polygons at common vertices  *
  161. * so gouraud shading can be applied. Does 2 passes on all object vertices:   *
  162. * 1. Combine (and average) common vertices - vertices with normal no differ  *
  163. *    more than NormalAvgDegree degrees.                         *
  164. * 2. For each vertex find its closest averaged normal as evaluated at 1, and *
  165. *    calculate color for it.                             *
  166. *****************************************************************************/
  167. static void AverageVrtxNormals(ObjectStruct *PObject)
  168. {
  169.     int i;
  170.     VertexHashStruct *VerticesHashTable;
  171.  
  172.     /* Prepare maximum degree allowed to merge two normals in cosine form:   */
  173.     MinNormalDotProd = cos(NormalAvgDegree * M_PI / 180.0);
  174.  
  175.     /* Allocate hash table twice as big as number of possible entries to     */
  176.     /* reduce the average hit ratio, with minimum of MIN_VERTEX_HASH_SIZE.   */
  177.     VertexHashSize = MAX(NumOfVertices * 2, MIN_VERTEX_HASH_SIZE);
  178.     VerticesHashTable = (VertexHashStruct *)
  179.     MyMalloc(VertexHashSize * sizeof(VertexHashStruct));
  180.     for (i=0; i<VertexHashSize; i++) VerticesHashTable[i].Entry = 0;
  181.  
  182.     InsertVerticesToHashTable(VerticesHashTable, PObject);
  183.  
  184.     UpdateVerticesFromHashTable(VerticesHashTable, PObject);
  185.  
  186.     free((char *) VerticesHashTable);
  187. }
  188.  
  189. /*****************************************************************************
  190. * Routine to insert all vertices in object into the hash table. Each vertex  *
  191. * is entered at place (key) as select via EvalVertexKey routine. If no place *
  192. * the next ones are scaned until free (NULL) is found (no double key fansy   *
  193. * hashing techniques...). Note however that while scanning the non NULL      *
  194. * entries the vertex position is compared for equality, and its normal for   *
  195. * equality upto AvgNormalDegree, and if both hold, the two are merged into   *
  196. * one vertex in that position but with averaged normal.                 *
  197. *****************************************************************************/
  198. static void InsertVerticesToHashTable(VertexHashStruct *VerticesHashTable,
  199.                             ObjectStruct *PObject)
  200. {
  201.     int i, Key;
  202.     float EntryRatio, *Normal, *OldNormal;
  203.     PolygonStruct *PPolygon;
  204.     VertexStruct *PVertex;
  205.  
  206.     for (PPolygon = PObject -> PPolygon; PPolygon != NULL;
  207.      PPolygon = PPolygon -> Pnext) {
  208.     Normal = PPolygon -> Plane;
  209.  
  210.     /* If BackFacingFlag is TRUE, and this polygon has plane    */
  211.     /* suggesting it is back facing - dont count it.        */
  212.     if (!(BackFacingFlag && PPolygon -> Plane[2] < 0.0))
  213.         PolyCount++;
  214.     fprintf(stderr, "\b\b\b\b\b%5d", PolyCount);
  215.  
  216.     for (PVertex = PPolygon -> PVertex; PVertex != NULL;
  217.          PVertex = PVertex -> Pnext) {
  218.         Key = EvalVertexKey(PVertex);
  219.         while (VerticesHashTable[Key].Entry != 0) {
  220.         /* Test if should be combined with old vertex: */
  221.         if (SameVertexAndNormals(PVertex, Normal,
  222.                             &VerticesHashTable[Key])) {
  223.             /* Megre the normals: */
  224.             EntryRatio = 1.0 / ++VerticesHashTable[Key].Entry;
  225.             OldNormal = VerticesHashTable[Key].Normal;
  226.             for (i=0; i<3; i++) OldNormal[i] =
  227.             OldNormal[i] * (1.0 - EntryRatio) +
  228.             Normal[i] * EntryRatio;
  229.             break;
  230.         }
  231.         Key = ++Key % VertexHashSize;
  232.         }
  233.         if (VerticesHashTable[Key].Entry == 0) {
  234.         /* Could not merge the vertex with old one - do it now: */
  235.         VerticesHashTable[Key].PVertex = PVertex;
  236.         GEN_COPY(VerticesHashTable[Key].Normal, Normal,
  237.                             3 * sizeof(float));
  238.         ++VerticesHashTable[Key].Entry;
  239.         }
  240.     }
  241.     }
  242. }
  243.  
  244. /*****************************************************************************
  245. * Routine to scan all vertices in object and update their normals to the     *
  246. * close one at that point as found in the hash table.                 *
  247. *****************************************************************************/
  248. static void UpdateVerticesFromHashTable(VertexHashStruct *VerticesHashTable,
  249.                             ObjectStruct *PObject)
  250. {
  251.     static Flip = FALSE;
  252.     int Key;
  253.     float *Normal, MaxCosAngle, CosAngle;
  254.     PolygonStruct *PPolygon;
  255.     VertexStruct *PVertex;
  256.     VertexHashStruct *PHVertex;
  257.  
  258.     for (PPolygon = PObject -> PPolygon; PPolygon != NULL;
  259.      PPolygon = PPolygon -> Pnext) {
  260.     Normal = PPolygon -> Plane;
  261.  
  262.     if (Flip) {
  263.         Flip = FALSE;
  264.         fprintf(stderr, "a\b");
  265.     }
  266.     else {
  267.         Flip = TRUE;
  268.         fprintf(stderr, "A\b");
  269.     }
  270.  
  271.     for (PVertex = PPolygon -> PVertex; PVertex != NULL;
  272.          PVertex = PVertex -> Pnext) {
  273.         PHVertex = NULL;
  274.         MaxCosAngle = -INFINITY;
  275.         Key = EvalVertexKey(PVertex);
  276.         while (VerticesHashTable[Key].Entry != 0) {
  277.         /* Search for the closest Normal at this vertex point: */
  278.         if ((CosAngle = (EvalNormalsAngle(PVertex, Normal,
  279.                     &VerticesHashTable[Key]))) > MaxCosAngle) {
  280.             PHVertex = &VerticesHashTable[Key];
  281.             MaxCosAngle = CosAngle;
  282.         }
  283.         Key = ++Key % VertexHashSize;
  284.         }
  285.         if (PVertex -> HasNormal) {
  286.         /* Use defined normal for this vertex, if has one. */
  287.         PVertex -> Color = CosineShading(PVertex -> Normal,
  288.                             PObject -> Color);
  289.         }
  290.         else
  291.         if (PHVertex) {    /* Should always be non NULL but who knows... */
  292.         PVertex -> Color = CosineShading(PHVertex -> Normal,
  293.                             PObject -> Color);
  294.         }
  295.         else {
  296.         PVertex -> Color = CosineShading(PPolygon -> Plane,
  297.                             PObject -> Color);
  298.         }
  299.     }
  300.     }
  301. }
  302.  
  303. /*****************************************************************************
  304. * Routine to compare two vertices to be exactly equal in their position and  *
  305. * to have same normal up to NormalAvgDegree.                     *
  306. *****************************************************************************/
  307. static int SameVertexAndNormals(VertexStruct *PVertex1, float *Normal1,
  308.                         VertexHashStruct *PHVertex2)
  309. {
  310.     float *Coord1 = PVertex1 -> Coord,
  311.       *Coord2 = PHVertex2 -> PVertex -> Coord,
  312.       *Normal2;
  313.  
  314.     if (!APX_EQ(Coord1[0], Coord2[0]) ||
  315.     !APX_EQ(Coord1[1], Coord2[1]) ||
  316.     !APX_EQ(Coord1[2], Coord2[2])) return FALSE;
  317.  
  318.     Normal2 = PHVertex2 -> Normal;
  319.  
  320.     return (Normal1[0] * Normal2[0] +
  321.         Normal1[1] * Normal2[1] +
  322.         Normal1[2] * Normal2[2] > MinNormalDotProd);
  323. }
  324.  
  325. /*****************************************************************************
  326. * Routine to evaluate the angle between the given two vertices if they are   *
  327. * the same, and return the angle cosine value. (-INFINITY is returned if     *
  328. * not same point...).                                 *
  329. *****************************************************************************/
  330. static float EvalNormalsAngle(VertexStruct *PVertex1, float *Normal1,
  331.                         VertexHashStruct *PHVertex2)
  332. {
  333.     float *Coord1 = PVertex1 -> Coord,
  334.       *Coord2 = PHVertex2 -> PVertex -> Coord,
  335.       *Normal2;
  336.  
  337.     if (!APX_EQ(Coord1[0], Coord2[0]) ||
  338.     !APX_EQ(Coord1[1], Coord2[1]) ||
  339.     !APX_EQ(Coord1[2], Coord2[2])) return -INFINITY;
  340.  
  341.     Normal2 = PHVertex2 -> Normal;
  342.  
  343.     return Normal1[0] * Normal2[0] +
  344.        Normal1[1] * Normal2[1] +
  345.        Normal1[2] * Normal2[2];
  346. }
  347.  
  348. /*****************************************************************************
  349. * Routine to evaluate integer key in the range 0 .. VertexHashSize - 1         *
  350. * for the given vertex PVertex.                             *
  351. *****************************************************************************/
  352. static int EvalVertexKey(VertexStruct *PVertex)
  353. {
  354.     int Key;
  355.     float *Coord = PVertex->Coord;
  356.  
  357.     Key = ((int) (((long) (Coord[0] * 239 + Coord[1] * 677 + Coord[2] * 109)) %
  358.                             VertexHashSize));
  359.  
  360.     return Key;
  361. }
  362.  
  363. /*****************************************************************************
  364. * Routine to update the vertices colors, which are indices tp the ColorMap   *
  365. * as save in the global ShadingInfo structure. Given Object color (as index  *
  366. * into table), the exact level is evaluationed using each polygon normal.    *
  367. *****************************************************************************/
  368. static void EvalOnePolygon(PolygonStruct *PPolygon, int ColorIndex)
  369. {
  370.     int Color;
  371.     struct VertexStruct *V = PPolygon -> PVertex;
  372.  
  373.     if (GouraudFlag) {
  374.     /* Vertices were already updated by the Object averaging of normals  */
  375.     /* routine (see AverageVrtxNormals routine).                 */
  376.     }
  377.     else {
  378.     fprintf(stderr, "\b\b\b\b\b%5d", ++PolyCount);
  379.  
  380.     /* This is much easier - set all vertices color to same Color.       */
  381.     Color = CosineShading(PPolygon -> Plane, ColorIndex);
  382.     do {
  383.         V -> Color = Color;
  384.         V = V -> Pnext;
  385.     }
  386.     while (V != NULL);
  387.     }
  388. }
  389.  
  390. /*****************************************************************************
  391. * Routine to evaluate cosine shading given a normal vector. Uses the global  *
  392. * shading structure ShadingInfo to evaluate it. Returns a color from the     *
  393. * color map defined for the current scene.                     *
  394. * It is assumed the given normal is normalized to size 1.0, and that the     *
  395. * intensity levels of the Object Color are saved in indices PObject -> Color *
  396. * to PObject -> Color + ShadingInfo.LevelsPerColor - 1.    These colors are     *
  397. * interpolated to begin from the ambient level, so we dont have to consider  *
  398. * ambient light here - just the cosine factor.                     *
  399. *****************************************************************************/
  400. static int CosineShading(float *Normal, int ColorIndex)
  401. {
  402.     int NewColorIndex;
  403.     double Intensity;
  404.  
  405.     if (ShadingInfo.TwoSources) {
  406.     /* Take the absolute value of the dot product as intensity: */
  407.     Intensity = Normal[0] * ShadingInfo.LightSource[0] +
  408.             Normal[1] * ShadingInfo.LightSource[1] +
  409.             Normal[2] * ShadingInfo.LightSource[2];
  410.     Intensity = ABS(Intensity);
  411.     }
  412.     else {
  413.     /* Dot product of two normals is in [-1..1] range. Make it [0..1]: */
  414.     Intensity = (Normal[0] * ShadingInfo.LightSource[0] +
  415.              Normal[1] * ShadingInfo.LightSource[1] +
  416.              Normal[2] * ShadingInfo.LightSource[2] + 1.0) * 0.5;
  417.     }
  418.  
  419.     NewColorIndex = ColorIndex +
  420.         ((int) ((ShadingInfo.LevelsPerColor - 1) * (1.0 - Intensity)));
  421.     /* This should never happen, but if it does, the error is so fatal       */
  422.     /* (generate different color tone instead...) we double check this one.  */
  423.     return BOUND(NewColorIndex, ColorIndex,
  424.                 ColorIndex + ShadingInfo.LevelsPerColor - 1);
  425. }
  426.  
  427. /*****************************************************************************
  428. * Routine to update plane equation of the given    polygon:             *
  429. *   It is assumed that at list 3 points in polygon do exists, and pick the   *
  430. * tuple that has biggest length for maximum accuracy.                 *
  431. *   Note the polygon must be convex.                         *
  432. *   if SetFlipDir, then it is assumed Plane equation is given and the         *
  433. * FlipPlaneDir entry in PPolygon is set to know if direction should be         *
  434. * flipped.                                     *
  435. *   Returns FALSE if failed to evaluate the PLANE equation.             *
  436. *****************************************************************************/
  437. int UpdateEqnPolygon(PolygonStruct *PPolygon, int SetFlipDir)
  438. {
  439.     int    i;
  440.     float MaxLen = 0.0, Len, V1[3], V2[3], *Coord, *CoordNext, *CoordNextNext,
  441.     Plane[3], MaxPlane[3];
  442.     struct VertexStruct *PList = PPolygon -> PVertex;
  443.  
  444.     do {    /* Search for 3 consequtive non-colinear point from polygon: */
  445.     Coord = PList -> Coord;
  446.     CoordNext = PList -> Pnext -> Coord;
  447.     CoordNextNext = PList -> Pnext -> Pnext -> Coord;
  448.  
  449.     if (!APX_PT_EQ(Coord, CoordNext) &&
  450.         !APX_PT_EQ(CoordNext, CoordNextNext)) {
  451.  
  452.         for (i=0; i<3; i++) {    /* Prepare two vectors on polygon plane */
  453.         V1[i] = Coord[i] - CoordNext[i];
  454.         V2[i] = CoordNext[i] - CoordNextNext[i];
  455.         }
  456.  
  457.         /* Find plane normal by a cross product of two vectors on plane: */
  458.         Plane[0] = V1[1] * V2[2] - V1[2] * V2[1];
  459.         Plane[1] = V1[2] * V2[0] - V1[0] * V2[2];
  460.         Plane[2] = V1[0] * V2[1] - V1[1] * V2[0];
  461.  
  462.         /* Find vector Len. - we are looking for the biggest: */
  463.         Len = sqrt(SQR(Plane[0]) + SQR(Plane[1]) + SQR(Plane[2]));
  464.         if (Len > MaxLen) {
  465.         for (i=0; i<3; i++) MaxPlane[i] = Plane[i];
  466.         MaxLen = Len;
  467.         }
  468.     }
  469.  
  470.     PList = PList -> Pnext;                  /* Try next tuple. */
  471.     } while (PList -> Pnext -> Pnext != NULL);
  472.  
  473.     if (ABS(MaxLen) < SQR(EPSILON)) { /* Fail to find 3 non-colinear points. */
  474.     if (MoreFlag) {
  475.         fprintf(stderr,
  476.             "\nError: Invalid polygon (%d) found in file (zero edge length/colinear vertices):\n",
  477.         PolyCount);
  478.         PrintPolyContent(PPolygon);
  479.     }
  480.     return FALSE;
  481.     }
  482.  
  483.     if (SetFlipDir) {
  484.     PPolygon -> FlipPlaneDir =
  485.          PPolygon -> Plane[0] * MaxPlane[0] +
  486.          PPolygon -> Plane[1] * MaxPlane[1] +
  487.          PPolygon -> Plane[2] * MaxPlane[2] < 0.0;
  488.     }
  489.  
  490.     for (i=0; i<3; i++)
  491.     PPolygon -> Plane[i] = (PPolygon -> FlipPlaneDir ? -1 : 1)
  492.                         * MaxPlane[i] / MaxLen;
  493.  
  494.     PPolygon ->    Plane[3] =
  495.     (- Coord[0] * PPolygon -> Plane[0]
  496.      - Coord[1] * PPolygon -> Plane[1]
  497.      - Coord[2] * PPolygon -> Plane[2]);
  498.  
  499.     return TRUE;
  500. }
  501.  
  502. /*****************************************************************************
  503. * Routine to evaluate the cross    product    of 3 points projected to Z = 0 plane *
  504. * and return the sign of the result (Only Z component).                 *
  505. *****************************************************************************/
  506. int CrossProd(float Pt1[3], float Pt2[3], float Pt3[3])
  507. {
  508.     float Zout;
  509.  
  510.     /* U = Pt2 - Pt1,  V = Pt3 - Pt2,        Zoutput    = Ux * Vy - Uy * Vx. */
  511.     Zout = (Pt2[0] - Pt1[0]) /*    Ux */  * (Pt3[1] - Pt2[1]) /* Vy */  -
  512.        (Pt2[1] - Pt1[1]) /*    Uy */  * (Pt3[0] - Pt2[0]) /* Vx */;
  513.     if (APX_EQ(Zout, 0.0)) return 0;
  514.     if (Zout < 0.0)
  515.      return    -1;
  516.     else return    1;
  517. }
  518.  
  519. /*****************************************************************************
  520. * Routine to print the content of a given edge:                     *
  521. *****************************************************************************/
  522. void PrintPolyContent(PolygonStruct *PPoly)
  523. {
  524.     struct VertexStruct *PList = PPoly -> PVertex;
  525.  
  526.     while (PList) {
  527.     fprintf(stderr, "   %12f %12f %12f\n",
  528.         PList -> Coord[0],
  529.         PList -> Coord[1],
  530.         PList -> Coord[2]);
  531.     PList =    PList -> Pnext;
  532.     }
  533. }
  534.