home *** CD-ROM | disk | FTP | other *** search
- /* Draws a bitmap, mapped to a convex polygon (draws a texture-mapped
- polygon). "Convex" means that every horizontal line drawn through
- the polygon at any point would cross exactly two active edges
- (neither horizontal lines nor zero-length edges count as active
- edges; both are acceptable anywhere in the polygon), and that the
- right & left edges never cross. Nonconvex polygons won't be drawn
- properly. Can't fail. */
-
- #include <stdio.h>
- #include <math.h>
- #include "polygon.h"
-
- /* Describes the current location and stepping, in both the source and
- the destination, of an edge */
- typedef struct {
- int Direction; /* through edge list; 1 for a right edge (forward
- through vertex list), -1 for a left edge (backward
- through vertex list) */
- int RemainingScans; /* height left to scan out in dest */
- int CurrentEnd; /* vertex # of end of current edge */
- Fixedpoint SourceX; /* current X location in source for this edge */
- Fixedpoint SourceY; /* current Y location in source for this edge */
- Fixedpoint SourceStepX; /* X step in source for Y step in dest of 1 */
- Fixedpoint SourceStepY; /* Y step in source for Y step in dest of 1 */
- /* variables used for all-integer Bresenham's-type
- X stepping through the dest, needed for precise
- pixel placement to avoid gaps */
- int DestX; /* current X location in dest for this edge */
- int DestXIntStep; /* whole part of dest X step per scan-line Y step */
- int DestXDirection; /* -1 or 1 to indicate which way X steps
- (left/right) */
- int DestXErrTerm; /* current error term for dest X stepping */
- int DestXAdjUp; /* amount to add to error term per scan line move */
- int DestXAdjDown; /* amount to subtract from error term when the
- error term turns over */
- } EdgeScan;
-
- int StepEdge(EdgeScan *);
- int SetUpEdge(EdgeScan *, int);
- void ScanOutLine(EdgeScan *, EdgeScan *);
- int GetImagePixel(char *, int, int, int);
-
- /* Statics to save time that would otherwise be spent passing them to
- subroutines. */
- static int MaxVert, NumVerts, DestY;
- static Point * VertexPtr;
- static Point * TexVertsPtr;
- static char * TexMapBits;
- static int TexMapWidth;
-
- /* Draws a texture-mapped polygon, given a list of destination polygon
- vertices, a list of corresponding source texture polygon vertices, and a
- pointer to the source texture's descriptor. */
- void DrawTexturedPolygon(PointListHeader * Polygon, Point * TexVerts,
- TextureMap * TexMap)
- {
- int MinY, MaxY, MinVert, i;
- EdgeScan LeftEdge, RightEdge;
-
- NumVerts = Polygon->Length;
- VertexPtr = Polygon->PointPtr;
- TexVertsPtr = TexVerts;
- TexMapBits = TexMap->TexMapBits;
- TexMapWidth = TexMap->TexMapWidth;
-
- /* Nothing to draw if less than 3 vertices */
- if (NumVerts < 3) {
- return;
- }
-
- /* Scan through the destination polygon vertices and find the top of
- the left and right edges, taking advantage of our knowledge that
- vertices run in a clockwise direction (else this polygon wouldn't
- be visible due to backface removal) */
- MinY = 32767;
- MaxY = -32768;
- for (i=0; i<NumVerts; i++) {
- if (VertexPtr[i].Y < MinY) {
- MinY = VertexPtr[i].Y;
- MinVert = i;
- }
- if (VertexPtr[i].Y > MaxY) {
- MaxY = VertexPtr[i].Y;
- MaxVert = i;
- }
- }
-
- /* Reject flat (0-pixel-high) polygons */
- if (MinY >= MaxY) {
- return;
- }
-
- /* The destination Y coordinate is not edge specific; it applies to
- both edges, since we always step Y by 1 */
- DestY = MinY;
-
- /* Set up to scan the initial left and right edges of the source and
- destination polygons. We always step the destination polygon edges
- by one in Y, so calculate the corresponding destination X step for
- each edge, and then the corresponding source image X and Y steps */
- LeftEdge.Direction = -1; /* set up left edge first */
- SetUpEdge(&LeftEdge, MinVert);
- RightEdge.Direction = 1; /* set up right edge */
- SetUpEdge(&RightEdge, MinVert);
-
- /* Step down destination edges one scan line at a time. At each scan
- line, find the corresponding edge points in the source image. Scan
- between the edge points in the source, drawing the corresponding
- pixels across the current scan line in the destination polygon. (We
- know which way the left and right edges run through the vertex list
- because visible (non-backface-culled) polygons always have the vertices
- in clockwise order as seen from the viewpoint) */
- for (;;) {
- /* Done if off bottom of clip rectangle */
- if (DestY >= ClipMaxY) {
- return;
- }
-
- /* Draw only if inside Y bounds of clip rectangle */
- if (DestY >= ClipMinY) {
- /* Draw the scan line between the two current edges */
- ScanOutLine(&LeftEdge, &RightEdge);
- }
-
- /* Advance the source and destination polygon edges, ending if we've
- scanned all the way to the bottom of the polygon */
- if (!StepEdge(&LeftEdge)) {
- break;
- }
- if (!StepEdge(&RightEdge)) {
- break;
- }
- DestY++;
- }
- }
-
- /* Steps an edge one scan line in the destination, and the corresponding
- distance in the source. If an edge runs out, starts a new edge if there
- is one. Returns 1 for success, or 0 if there are no more edges to scan. */
- int StepEdge(EdgeScan * Edge)
- {
- /* Count off the scan line we stepped last time; if this edge is
- finished, try to start another one */
- if (--Edge->RemainingScans == 0) {
- /* Set up the next edge; done if there is no next edge */
- if (SetUpEdge(Edge, Edge->CurrentEnd) == 0) {
- return(0); /* no more edges; done drawing polygon */
- }
- return(1); /* all set to draw the new edge */
- }
-
- /* Step the current source edge */
- Edge->SourceX += Edge->SourceStepX;
- Edge->SourceY += Edge->SourceStepY;
-
- /* Step dest X with Bresenham-style variables, to get precise dest pixel
- placement and avoid gaps */
- Edge->DestX += Edge->DestXIntStep; /* whole pixel step */
- /* Do error term stuff for fractional pixel X step handling */
- if ((Edge->DestXErrTerm += Edge->DestXAdjUp) > 0) {
- Edge->DestX += Edge->DestXDirection;
- Edge->DestXErrTerm -= Edge->DestXAdjDown;
- }
-
- return(1);
- }
-
- /* Sets up an edge to be scanned; the edge starts at StartVert and proceeds
- in direction Edge->Direction through the vertex list. Edge->Direction must
- be set prior to call; -1 to scan a left edge (backward through the vertex
- list), 1 to scan a right edge (forward through the vertex list).
- Automatically skips over 0-height edges. Returns 1 for success, or 0 if
- there are no more edges to scan. */
- int SetUpEdge(EdgeScan * Edge, int StartVert)
- {
- int NextVert, DestXWidth;
- Fixedpoint DestYHeight;
-
- for (;;) {
- /* Done if this edge starts at the bottom vertex */
- if (StartVert == MaxVert) {
- return(0);
- }
-
- /* Advance to the next vertex, wrapping if we run off the start or end
- of the vertex list */
- NextVert = StartVert + Edge->Direction;
- if (NextVert >= NumVerts) {
- NextVert = 0;
- } else if (NextVert < 0) {
- NextVert = NumVerts - 1;
- }
-
- /* Calculate the variables for this edge and done if this is not a
- zero-height edge */
- if ((Edge->RemainingScans =
- VertexPtr[NextVert].Y - VertexPtr[StartVert].Y) != 0) {
- DestYHeight = INT_TO_FIXED(Edge->RemainingScans);
- Edge->CurrentEnd = NextVert;
- Edge->SourceX = INT_TO_FIXED(TexVertsPtr[StartVert].X);
- Edge->SourceY = INT_TO_FIXED(TexVertsPtr[StartVert].Y);
- Edge->SourceStepX = FixedDiv(INT_TO_FIXED(TexVertsPtr[NextVert].X) -
- Edge->SourceX, DestYHeight);
- Edge->SourceStepY = FixedDiv(INT_TO_FIXED(TexVertsPtr[NextVert].Y) -
- Edge->SourceY, DestYHeight);
-
- /* Set up Bresenham-style variables for dest X stepping */
- Edge->DestX = VertexPtr[StartVert].X;
- if ((DestXWidth =
- (VertexPtr[NextVert].X - VertexPtr[StartVert].X)) < 0) {
- /* Set up for drawing right to left */
- Edge->DestXDirection = -1;
- DestXWidth = -DestXWidth;
- Edge->DestXErrTerm = 1 - Edge->RemainingScans;
- Edge->DestXIntStep = -(DestXWidth / Edge->RemainingScans);
- } else {
- /* Set up for drawing left to right */
- Edge->DestXDirection = 1;
- Edge->DestXErrTerm = 0;
- Edge->DestXIntStep = DestXWidth / Edge->RemainingScans;
- }
- Edge->DestXAdjUp = DestXWidth % Edge->RemainingScans;
- Edge->DestXAdjDown = Edge->RemainingScans;
- return(1); /* success */
- }
- StartVert = NextVert; /* keep looking for a non-0-height edge */
- }
- }
-
- /* Texture-map-draw the scan line between two edges. */
- void ScanOutLine(EdgeScan * LeftEdge, EdgeScan * RightEdge)
- {
- Fixedpoint SourceX = LeftEdge->SourceX;
- Fixedpoint SourceY = LeftEdge->SourceY;
- int DestX = LeftEdge->DestX;
- int DestXMax = RightEdge->DestX;
- Fixedpoint DestWidth;
- Fixedpoint SourceXStep, SourceYStep;
-
- /* Nothing to do if fully X clipped */
- if ((DestXMax <= ClipMinX) || (DestX >= ClipMaxX)) {
- return;
- }
-
- if ((DestXMax - DestX) <= 0) {
- return; /* nothing to draw */
- }
-
- /* Width of destination scan line, for scaling. Note: because this is an
- integer-based scaling, it can have a total error of as much as nearly
- one pixel. For more precise scaling, also maintain a fixed-point DestX
- in each edge, and use it for scaling. If this is done, it will also
- be necessary to nudge the source start coordinates to the right by an
- amount corresponding to the distance from the the real (fixed-point)
- DestX and the first pixel (at an integer X) to be drawn) */
- DestWidth = INT_TO_FIXED(DestXMax - DestX);
-
- /* Calculate source steps that correspond to each dest X step (across
- the scan line) */
- SourceXStep = FixedDiv(RightEdge->SourceX - SourceX, DestWidth);
- SourceYStep = FixedDiv(RightEdge->SourceY - SourceY, DestWidth);
-
- /* Clip right edge if necessary */
- if (DestXMax > ClipMaxX) {
- DestXMax = ClipMaxX;
- }
-
- /* Clip left edge if necssary */
- if (DestX < ClipMinX) {
- SourceX += SourceXStep * (ClipMinX - DestX);
- SourceY += SourceYStep * (ClipMinX - DestX);
- DestX = ClipMinX;
- }
-
- /* Scan across the destination scan line, updating the source image
- position accordingly */
- for (; DestX<DestXMax; DestX++) {
-
- /* Get the currently mapped pixel out of the image and draw it to
- the screen */
- WritePixelX(DestX, DestY,
- GET_IMAGE_PIXEL(TexMapBits, TexMapWidth,
- FIXED_TO_INT(SourceX), FIXED_TO_INT(SourceY)) );
-
- /* Point to the next source pixel */
- SourceX += SourceXStep;
- SourceY += SourceYStep;
- }
- }