/* Draws all visible faces in the specified polygon-based object.  The
   object must have previously been transformed and projected, so that
   all vertex arrays are filled in. Ambient and diffuse shading are
   supported. */

#include "polygon.h"

void DrawPObject(PObject * ObjectToXform)
{
   int i, j, NumFaces = ObjectToXform->NumFaces, NumVertices;
   int * VertNumsPtr, Spot;
   Face * FacePtr = ObjectToXform->FaceList;
   Point * ScreenPoints = ObjectToXform->ScreenVertexList;
   PointListHeader Polygon;
   Fixedpoint Diffusion;
   ModelColor ColorTemp;
   ModelIntensity IntensityTemp;
   Point3 UnitNormal, *NormalStartpoint, *NormalEndpoint;
   long v1, v2, w1, w2;
   Point Vertices[MAX_POLY_LENGTH];

   /* Draw each visible face (polygon) of the object in turn */
   for (i=0; i<NumFaces; i++, FacePtr++) {
      /* Remember where we can find the start and end of the polygon's
         unit normal in view space, and skip over the unit normal endpoint
         entry. The end and start points of the unit normal to the polygon
         must be the first and second entries in the polgyon's vertex list.
         Note that the second point is also an active polygon vertex */
      VertNumsPtr = FacePtr->VertNums;
      NormalEndpoint = &ObjectToXform->XformedVertexList[*VertNumsPtr++];
      NormalStartpoint = &ObjectToXform->XformedVertexList[*VertNumsPtr];

      /* Copy over the face's vertices from the vertex list */
      NumVertices = FacePtr->NumVerts;
      for (j=0; j<NumVertices; j++)
         Vertices[j] = ScreenPoints[*VertNumsPtr++];

      /* Draw only if outside face showing (if the normal to the polygon
         in screen coordinates points toward the viewer; that is, has a
         positive Z component) */
      v1 = Vertices[1].X - Vertices[0].X;
      w1 = Vertices[NumVertices-1].X - Vertices[0].X;
      v2 = Vertices[1].Y - Vertices[0].Y;
      w2 = Vertices[NumVertices-1].Y - Vertices[0].Y;
      if ((v1*w2 - v2*w1) > 0) {
         /* It is facing the screen, so draw */
         /* Appropriately adjust the extent of the rectangle used to
            erase this object later */
         for (j=0; j<NumVertices; j++) {
            if (Vertices[j].X >
                  ObjectToXform->EraseRect[NonDisplayedPage].Right)
               if (Vertices[j].X < SCREEN_WIDTH)
                  ObjectToXform->EraseRect[NonDisplayedPage].Right =
                        Vertices[j].X;
               else ObjectToXform->EraseRect[NonDisplayedPage].Right =
                     SCREEN_WIDTH;
            if (Vertices[j].Y >
                  ObjectToXform->EraseRect[NonDisplayedPage].Bottom)
               if (Vertices[j].Y < SCREEN_HEIGHT)
                  ObjectToXform->EraseRect[NonDisplayedPage].Bottom =
                        Vertices[j].Y;
               else ObjectToXform->EraseRect[NonDisplayedPage].Bottom=
                     SCREEN_HEIGHT;
            if (Vertices[j].X <
                  ObjectToXform->EraseRect[NonDisplayedPage].Left)
               if (Vertices[j].X > 0)
                  ObjectToXform->EraseRect[NonDisplayedPage].Left =
                        Vertices[j].X;
               else ObjectToXform->EraseRect[NonDisplayedPage].Left=0;
            if (Vertices[j].Y <
                  ObjectToXform->EraseRect[NonDisplayedPage].Top)
               if (Vertices[j].Y > 0)
                  ObjectToXform->EraseRect[NonDisplayedPage].Top =
                        Vertices[j].Y;
               else ObjectToXform->EraseRect[NonDisplayedPage].Top=0;
         }

         /* See if there's any shading */
         if (FacePtr->ShadingType == NO_SHADING) {
            /* No shading in effect, so just draw */
            DRAW_POLYGON(Vertices, NumVertices, FacePtr->ColorIndex, 0, 0);
         } else if (FacePtr->ShadingType & TEXTURE_MAPPED_SHADING) {
            /* Texture mapping in effect; this precludes illuminated
                shading */
            DRAW_TEXTURED_POLYGON(Vertices, NumVertices, FacePtr->TexVerts,
                  FacePtr->TexMap);
         } else {
            /* Handle shading */

            /* Do ambient shading, if enabled */
            if (AmbientOn && (FacePtr->ShadingType & AMBIENT_SHADING)) {
               /* Use the ambient shading component */
               IntensityTemp = AmbientIntensity;
            } else {
               SET_INTENSITY(IntensityTemp, 0, 0, 0);
            }

            /* Do diffuse shading, if enabled */
            if (FacePtr->ShadingType & DIFFUSE_SHADING) {
               /* Calculate the unit normal for this polygon, for use in dot
                  products */
               UnitNormal.X = NormalEndpoint->X - NormalStartpoint->X;
               UnitNormal.Y = NormalEndpoint->Y - NormalStartpoint->Y;
               UnitNormal.Z = NormalEndpoint->Z - NormalStartpoint->Z;
               /* Calculate the diffuse shading component for each active
                  spotlight */
               for (Spot=0; Spot<MAX_SPOTS; Spot++) {
                  if (SpotOn[Spot] != 0) {
                     /* Spot is on, so sum, for each color component, the
                        intensity, accounting for the angle of the light rays
                        relative to the orientation of the polygon */
                     /* Calculate cosine of angle between the light and the
                        polygon normal; skip if spot is shining from behind
                        the polygon */
                     if ((Diffusion = DOT_PRODUCT(SpotDirectionView[Spot],
                           UnitNormal)) > 0) {
                        IntensityTemp.Red +=
                              FixedMul(SpotIntensity[Spot].Red, Diffusion);
                        IntensityTemp.Green +=
                              FixedMul(SpotIntensity[Spot].Green, Diffusion);
                        IntensityTemp.Blue +=
                              FixedMul(SpotIntensity[Spot].Blue, Diffusion);
                     }
                  }
               }
            }

            /* Convert the drawing color to the desired fraction of the
               brightest possible color */
            IntensityAdjustColor(&ColorTemp, &FacePtr->FullColor,
                  &IntensityTemp);

            /* Draw with the cumulative shading, converting from the general
               color representation to the best-match color index */
            DRAW_POLYGON(Vertices, NumVertices,
                  ModelColorToColorIndex(&ColorTemp), 0, 0);
         }
      }
   }
}

