home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- * Routines to evaluate vertices colors for all polygons in data. It is *
- * assumed the parser has updated plane equations for all polygons at this *
- * stage (if from reading it in, or by regenerating it), and they were mapped *
- * correctly using the global transformation applied to them. *
- * The vertices are updated for both Flat or Gouraud shading, and color is *
- * evaluated for them, as a color index into the color table. Because of the *
- * way the table is constructed and because two vertices in same polygon can *
- * have two levels of same color, it is o.k. to interplated the color *
- * indices (We assume no specular affects)! *
- * As side affect, we use this pass to hash the polygon into global hash *
- * table PolyHasTable, sorted by their Ymin values. *
- * *
- * Written by: Gershon Elber Ver 2.0, Mar. 1990 *
- *****************************************************************************/
-
- #ifdef __MSDOS__
- #include <stdlib.h>
- #endif /* __MSDOS__ */
-
- #include <math.h>
- #include <stdio.h>
- #include <string.h>
- #include <time.h>
- #include "program.h"
- #include "parser.h"
-
- #define MIN_VERTEX_HASH_SIZE 1000 /* Minimum number of entries in table. */
-
- typedef struct VertexHashStruct { /* Used to hash an object vertices. */
- int Entry;
- float Normal[3];
- VertexStruct *PVertex;
- } VertexHashStruct;
-
- static float MinNormalDotProd; /* Min. normal dot product to accept as same. */
- static int VertexHashSize = 0; /* Size of vertex hash table size allocated. */
- static int PolyCount = 0; /* Number of polygons processed in this module: */
-
- static void PrepareAllObjects(FileDescription **FD);
- static void VisitObjectTree(BinTree *PBinTree);
- static void PrepareOneObject(ObjectStruct *PObject);
- static void AverageVrtxNormals(ObjectStruct *PObject);
- static void InsertVerticesToHashTable(VertexHashStruct *VerticesHashTable,
- ObjectStruct *PObject);
- static void UpdateVerticesFromHashTable(VertexHashStruct *VerticesHashTable,
- ObjectStruct *PObject);
- static int SameVertexAndNormals(VertexStruct *PVertex1, float *Normal1,
- VertexHashStruct *PHVertex2);
- static float EvalNormalsAngle(VertexStruct *PVertex1, float *Normal1,
- VertexHashStruct *PHVertex2);
- static int EvalVertexKey(VertexStruct *PVertex);
- static void EvalOnePolygon(PolygonStruct *PPolygon, int ColorIndex);
- static int CosineShading(float *Normal, int ColorIndex);
-
- /*****************************************************************************
- * Routine to evaluate the vertices colors for both Flat/Gouraud shading, as *
- * indices for the color table. *
- *****************************************************************************/
- void EvalVrtxColors(FileDescription **FD, int NumOfObjects, char **Objects)
- {
- int i;
- long SaveTime = time(NULL);
- struct ObjectStruct *PObject;
-
- PolyHashTable = (PolygonStruct **)
- MyMalloc(sizeof(PolygonStruct *) * ScreenYSize *
- ShadingInfo.SubSamplePixel);
-
- for (i=0; i<ScreenYSize * ShadingInfo.SubSamplePixel; i++)
- PolyHashTable[i] = NULL;
-
- fprintf(stderr, "\nPass 3, Polys [%4d] = ", NumOfPolygons);
-
- if (NumOfObjects > 0) /* There was something on command line... */
- for (i=0; i<NumOfObjects; i++) {
- if ((PObject=SearchObject(FD, *Objects)) != (ObjectStruct *) NULL)
- PrepareOneObject(PObject);
- }
- else { /* Prepare all objects by scanning object trees. */
- PrepareAllObjects(FD);
- }
-
- fprintf(stderr, ", %ld seconds.", time(NULL) - SaveTime);
- }
-
- /*****************************************************************************
- * Scan all objects. *
- *****************************************************************************/
- static void PrepareAllObjects(FileDescription **FD)
- {
- while (*FD) VisitObjectTree((*FD++) -> ObjectPointer);
- }
-
- /*****************************************************************************
- * Scanning all the object in tree PBinTree and prepare them. *
- *****************************************************************************/
- static void VisitObjectTree(BinTree *PBinTree)
- {
- if (PBinTree == (BinTree *) NULL) return;
-
- VisitObjectTree(PBinTree -> right);
-
- PrepareOneObject(PBinTree -> Data.PObject);
-
- VisitObjectTree(PBinTree -> left);
- }
-
- /*****************************************************************************
- * Routine to prepare one object Object. *
- *****************************************************************************/
- static void PrepareOneObject(ObjectStruct *PObject)
- {
- static int WasPolyline = FALSE;
- int Level, ColorIndex = PObject -> Color;
- struct PolygonStruct *Ptemp, *PList = PObject -> PPolygon;
-
- if (GouraudFlag) {
- /* Need to average all vertices normals from adjacent polygons, */
- /* so we can interpolate them, and select color for them directly. */
- AverageVrtxNormals(PObject);
- }
-
- while (PList) {
- Ptemp = PList -> Pnext;
-
- EvalOnePolygon(PList, ColorIndex);
-
- /* And add polygon into polygon hash table sorted by Ymin: */
- if (!PList -> Polyline) {
- /* If BackFacingFlag is TRUE, and this polygon has plane */
- /* suggesting it is back facing - dont insert to hash table. */
- if (!(BackFacingFlag && PList -> Plane[2] < 0.0)) {
- Level = PList -> Ymin;
- Level = BOUND(Level, 0, ScreenYSize *
- ShadingInfo.SubSamplePixel);/* Be 100% safe. */
- if (Level < ScreenYSize * ShadingInfo.SubSamplePixel) {
- /* Concat to hash table at level Level: */
- PList -> Pnext = PolyHashTable[Level];
- PolyHashTable[Level] = PList;
- }
- }
- else {
- if (!GouraudFlag) PolyCount--; /* One less polygon... */
- }
- }
- else {
- if (!WasPolyline) {
- WasPolyline = TRUE;
- if (MoreFlag)
- fprintf(stderr, "\nPolylines are not supported, ignored\n");
- }
- }
-
- PList = Ptemp;
- }
- }
-
- /*****************************************************************************
- * Routine to update average normals of adjacent polygons at common vertices *
- * so gouraud shading can be applied. Does 2 passes on all object vertices: *
- * 1. Combine (and average) common vertices - vertices with normal no differ *
- * more than NormalAvgDegree degrees. *
- * 2. For each vertex find its closest averaged normal as evaluated at 1, and *
- * calculate color for it. *
- *****************************************************************************/
- static void AverageVrtxNormals(ObjectStruct *PObject)
- {
- int i;
- VertexHashStruct *VerticesHashTable;
-
- /* Prepare maximum degree allowed to merge two normals in cosine form: */
- MinNormalDotProd = cos(NormalAvgDegree * M_PI / 180.0);
-
- /* Allocate hash table twice as big as number of possible entries to */
- /* reduce the average hit ratio, with minimum of MIN_VERTEX_HASH_SIZE. */
- VertexHashSize = MAX(NumOfVertices * 2, MIN_VERTEX_HASH_SIZE);
- VerticesHashTable = (VertexHashStruct *)
- MyMalloc(VertexHashSize * sizeof(VertexHashStruct));
- for (i=0; i<VertexHashSize; i++) VerticesHashTable[i].Entry = 0;
-
- InsertVerticesToHashTable(VerticesHashTable, PObject);
-
- UpdateVerticesFromHashTable(VerticesHashTable, PObject);
-
- free((char *) VerticesHashTable);
- }
-
- /*****************************************************************************
- * Routine to insert all vertices in object into the hash table. Each vertex *
- * is entered at place (key) as select via EvalVertexKey routine. If no place *
- * the next ones are scaned until free (NULL) is found (no double key fansy *
- * hashing techniques...). Note however that while scanning the non NULL *
- * entries the vertex position is compared for equality, and its normal for *
- * equality upto AvgNormalDegree, and if both hold, the two are merged into *
- * one vertex in that position but with averaged normal. *
- *****************************************************************************/
- static void InsertVerticesToHashTable(VertexHashStruct *VerticesHashTable,
- ObjectStruct *PObject)
- {
- int i, Key;
- float EntryRatio, *Normal, *OldNormal;
- PolygonStruct *PPolygon;
- VertexStruct *PVertex;
-
- for (PPolygon = PObject -> PPolygon; PPolygon != NULL;
- PPolygon = PPolygon -> Pnext) {
- Normal = PPolygon -> Plane;
-
- /* If BackFacingFlag is TRUE, and this polygon has plane */
- /* suggesting it is back facing - dont count it. */
- if (!(BackFacingFlag && PPolygon -> Plane[2] < 0.0))
- PolyCount++;
- fprintf(stderr, "\b\b\b\b\b%5d", PolyCount);
-
- for (PVertex = PPolygon -> PVertex; PVertex != NULL;
- PVertex = PVertex -> Pnext) {
- Key = EvalVertexKey(PVertex);
- while (VerticesHashTable[Key].Entry != 0) {
- /* Test if should be combined with old vertex: */
- if (SameVertexAndNormals(PVertex, Normal,
- &VerticesHashTable[Key])) {
- /* Megre the normals: */
- EntryRatio = 1.0 / ++VerticesHashTable[Key].Entry;
- OldNormal = VerticesHashTable[Key].Normal;
- for (i=0; i<3; i++) OldNormal[i] =
- OldNormal[i] * (1.0 - EntryRatio) +
- Normal[i] * EntryRatio;
- break;
- }
- Key = ++Key % VertexHashSize;
- }
- if (VerticesHashTable[Key].Entry == 0) {
- /* Could not merge the vertex with old one - do it now: */
- VerticesHashTable[Key].PVertex = PVertex;
- GEN_COPY(VerticesHashTable[Key].Normal, Normal,
- 3 * sizeof(float));
- ++VerticesHashTable[Key].Entry;
- }
- }
- }
- }
-
- /*****************************************************************************
- * Routine to scan all vertices in object and update their normals to the *
- * close one at that point as found in the hash table. *
- *****************************************************************************/
- static void UpdateVerticesFromHashTable(VertexHashStruct *VerticesHashTable,
- ObjectStruct *PObject)
- {
- static Flip = FALSE;
- int Key;
- float *Normal, MaxCosAngle, CosAngle;
- PolygonStruct *PPolygon;
- VertexStruct *PVertex;
- VertexHashStruct *PHVertex;
-
- for (PPolygon = PObject -> PPolygon; PPolygon != NULL;
- PPolygon = PPolygon -> Pnext) {
- Normal = PPolygon -> Plane;
-
- if (Flip) {
- Flip = FALSE;
- fprintf(stderr, "a\b");
- }
- else {
- Flip = TRUE;
- fprintf(stderr, "A\b");
- }
-
- for (PVertex = PPolygon -> PVertex; PVertex != NULL;
- PVertex = PVertex -> Pnext) {
- PHVertex = NULL;
- MaxCosAngle = -INFINITY;
- Key = EvalVertexKey(PVertex);
- while (VerticesHashTable[Key].Entry != 0) {
- /* Search for the closest Normal at this vertex point: */
- if ((CosAngle = (EvalNormalsAngle(PVertex, Normal,
- &VerticesHashTable[Key]))) > MaxCosAngle) {
- PHVertex = &VerticesHashTable[Key];
- MaxCosAngle = CosAngle;
- }
- Key = ++Key % VertexHashSize;
- }
- if (PVertex -> HasNormal) {
- /* Use defined normal for this vertex, if has one. */
- PVertex -> Color = CosineShading(PVertex -> Normal,
- PObject -> Color);
- }
- else
- if (PHVertex) { /* Should always be non NULL but who knows... */
- PVertex -> Color = CosineShading(PHVertex -> Normal,
- PObject -> Color);
- }
- else {
- PVertex -> Color = CosineShading(PPolygon -> Plane,
- PObject -> Color);
- }
- }
- }
- }
-
- /*****************************************************************************
- * Routine to compare two vertices to be exactly equal in their position and *
- * to have same normal up to NormalAvgDegree. *
- *****************************************************************************/
- static int SameVertexAndNormals(VertexStruct *PVertex1, float *Normal1,
- VertexHashStruct *PHVertex2)
- {
- float *Coord1 = PVertex1 -> Coord,
- *Coord2 = PHVertex2 -> PVertex -> Coord,
- *Normal2;
-
- if (!APX_EQ(Coord1[0], Coord2[0]) ||
- !APX_EQ(Coord1[1], Coord2[1]) ||
- !APX_EQ(Coord1[2], Coord2[2])) return FALSE;
-
- Normal2 = PHVertex2 -> Normal;
-
- return (Normal1[0] * Normal2[0] +
- Normal1[1] * Normal2[1] +
- Normal1[2] * Normal2[2] > MinNormalDotProd);
- }
-
- /*****************************************************************************
- * Routine to evaluate the angle between the given two vertices if they are *
- * the same, and return the angle cosine value. (-INFINITY is returned if *
- * not same point...). *
- *****************************************************************************/
- static float EvalNormalsAngle(VertexStruct *PVertex1, float *Normal1,
- VertexHashStruct *PHVertex2)
- {
- float *Coord1 = PVertex1 -> Coord,
- *Coord2 = PHVertex2 -> PVertex -> Coord,
- *Normal2;
-
- if (!APX_EQ(Coord1[0], Coord2[0]) ||
- !APX_EQ(Coord1[1], Coord2[1]) ||
- !APX_EQ(Coord1[2], Coord2[2])) return -INFINITY;
-
- Normal2 = PHVertex2 -> Normal;
-
- return Normal1[0] * Normal2[0] +
- Normal1[1] * Normal2[1] +
- Normal1[2] * Normal2[2];
- }
-
- /*****************************************************************************
- * Routine to evaluate integer key in the range 0 .. VertexHashSize - 1 *
- * for the given vertex PVertex. *
- *****************************************************************************/
- static int EvalVertexKey(VertexStruct *PVertex)
- {
- int Key;
- float *Coord = PVertex->Coord;
-
- Key = ((int) (((long) (Coord[0] * 239 + Coord[1] * 677 + Coord[2] * 109)) %
- VertexHashSize));
-
- return Key;
- }
-
- /*****************************************************************************
- * Routine to update the vertices colors, which are indices tp the ColorMap *
- * as save in the global ShadingInfo structure. Given Object color (as index *
- * into table), the exact level is evaluationed using each polygon normal. *
- *****************************************************************************/
- static void EvalOnePolygon(PolygonStruct *PPolygon, int ColorIndex)
- {
- int Color;
- struct VertexStruct *V = PPolygon -> PVertex;
-
- if (GouraudFlag) {
- /* Vertices were already updated by the Object averaging of normals */
- /* routine (see AverageVrtxNormals routine). */
- }
- else {
- fprintf(stderr, "\b\b\b\b\b%5d", ++PolyCount);
-
- /* This is much easier - set all vertices color to same Color. */
- Color = CosineShading(PPolygon -> Plane, ColorIndex);
- do {
- V -> Color = Color;
- V = V -> Pnext;
- }
- while (V != NULL);
- }
- }
-
- /*****************************************************************************
- * Routine to evaluate cosine shading given a normal vector. Uses the global *
- * shading structure ShadingInfo to evaluate it. Returns a color from the *
- * color map defined for the current scene. *
- * It is assumed the given normal is normalized to size 1.0, and that the *
- * intensity levels of the Object Color are saved in indices PObject -> Color *
- * to PObject -> Color + ShadingInfo.LevelsPerColor - 1. These colors are *
- * interpolated to begin from the ambient level, so we dont have to consider *
- * ambient light here - just the cosine factor. *
- *****************************************************************************/
- static int CosineShading(float *Normal, int ColorIndex)
- {
- int NewColorIndex;
- double Intensity;
-
- if (ShadingInfo.TwoSources) {
- /* Take the absolute value of the dot product as intensity: */
- Intensity = Normal[0] * ShadingInfo.LightSource[0] +
- Normal[1] * ShadingInfo.LightSource[1] +
- Normal[2] * ShadingInfo.LightSource[2];
- Intensity = ABS(Intensity);
- }
- else {
- /* Dot product of two normals is in [-1..1] range. Make it [0..1]: */
- Intensity = (Normal[0] * ShadingInfo.LightSource[0] +
- Normal[1] * ShadingInfo.LightSource[1] +
- Normal[2] * ShadingInfo.LightSource[2] + 1.0) * 0.5;
- }
-
- NewColorIndex = ColorIndex +
- ((int) ((ShadingInfo.LevelsPerColor - 1) * (1.0 - Intensity)));
- /* This should never happen, but if it does, the error is so fatal */
- /* (generate different color tone instead...) we double check this one. */
- return BOUND(NewColorIndex, ColorIndex,
- ColorIndex + ShadingInfo.LevelsPerColor - 1);
- }
-
- /*****************************************************************************
- * Routine to update plane equation of the given polygon: *
- * It is assumed that at list 3 points in polygon do exists, and pick the *
- * tuple that has biggest length for maximum accuracy. *
- * Note the polygon must be convex. *
- * if SetFlipDir, then it is assumed Plane equation is given and the *
- * FlipPlaneDir entry in PPolygon is set to know if direction should be *
- * flipped. *
- * Returns FALSE if failed to evaluate the PLANE equation. *
- *****************************************************************************/
- int UpdateEqnPolygon(PolygonStruct *PPolygon, int SetFlipDir)
- {
- int i;
- float MaxLen = 0.0, Len, V1[3], V2[3], *Coord, *CoordNext, *CoordNextNext,
- Plane[3], MaxPlane[3];
- struct VertexStruct *PList = PPolygon -> PVertex;
-
- do { /* Search for 3 consequtive non-colinear point from polygon: */
- Coord = PList -> Coord;
- CoordNext = PList -> Pnext -> Coord;
- CoordNextNext = PList -> Pnext -> Pnext -> Coord;
-
- if (!APX_PT_EQ(Coord, CoordNext) &&
- !APX_PT_EQ(CoordNext, CoordNextNext)) {
-
- for (i=0; i<3; i++) { /* Prepare two vectors on polygon plane */
- V1[i] = Coord[i] - CoordNext[i];
- V2[i] = CoordNext[i] - CoordNextNext[i];
- }
-
- /* Find plane normal by a cross product of two vectors on plane: */
- Plane[0] = V1[1] * V2[2] - V1[2] * V2[1];
- Plane[1] = V1[2] * V2[0] - V1[0] * V2[2];
- Plane[2] = V1[0] * V2[1] - V1[1] * V2[0];
-
- /* Find vector Len. - we are looking for the biggest: */
- Len = sqrt(SQR(Plane[0]) + SQR(Plane[1]) + SQR(Plane[2]));
- if (Len > MaxLen) {
- for (i=0; i<3; i++) MaxPlane[i] = Plane[i];
- MaxLen = Len;
- }
- }
-
- PList = PList -> Pnext; /* Try next tuple. */
- } while (PList -> Pnext -> Pnext != NULL);
-
- if (ABS(MaxLen) < SQR(EPSILON)) { /* Fail to find 3 non-colinear points. */
- if (MoreFlag) {
- fprintf(stderr,
- "\nError: Invalid polygon (%d) found in file (zero edge length/colinear vertices):\n",
- PolyCount);
- PrintPolyContent(PPolygon);
- }
- return FALSE;
- }
-
- if (SetFlipDir) {
- PPolygon -> FlipPlaneDir =
- PPolygon -> Plane[0] * MaxPlane[0] +
- PPolygon -> Plane[1] * MaxPlane[1] +
- PPolygon -> Plane[2] * MaxPlane[2] < 0.0;
- }
-
- for (i=0; i<3; i++)
- PPolygon -> Plane[i] = (PPolygon -> FlipPlaneDir ? -1 : 1)
- * MaxPlane[i] / MaxLen;
-
- PPolygon -> Plane[3] =
- (- Coord[0] * PPolygon -> Plane[0]
- - Coord[1] * PPolygon -> Plane[1]
- - Coord[2] * PPolygon -> Plane[2]);
-
- return TRUE;
- }
-
- /*****************************************************************************
- * Routine to evaluate the cross product of 3 points projected to Z = 0 plane *
- * and return the sign of the result (Only Z component). *
- *****************************************************************************/
- int CrossProd(float Pt1[3], float Pt2[3], float Pt3[3])
- {
- float Zout;
-
- /* U = Pt2 - Pt1, V = Pt3 - Pt2, Zoutput = Ux * Vy - Uy * Vx. */
- Zout = (Pt2[0] - Pt1[0]) /* Ux */ * (Pt3[1] - Pt2[1]) /* Vy */ -
- (Pt2[1] - Pt1[1]) /* Uy */ * (Pt3[0] - Pt2[0]) /* Vx */;
- if (APX_EQ(Zout, 0.0)) return 0;
- if (Zout < 0.0)
- return -1;
- else return 1;
- }
-
- /*****************************************************************************
- * Routine to print the content of a given edge: *
- *****************************************************************************/
- void PrintPolyContent(PolygonStruct *PPoly)
- {
- struct VertexStruct *PList = PPoly -> PVertex;
-
- while (PList) {
- fprintf(stderr, " %12f %12f %12f\n",
- PList -> Coord[0],
- PList -> Coord[1],
- PList -> Coord[2]);
- PList = PList -> Pnext;
- }
- }
-