home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / ddjmag / ddj9209.zip / GRPHPRG.ASC < prev    next >
Text File  |  1992-08-10  |  14KB  |  308 lines

  1. _GRAPHICS PROGRAMMING COLUMN_
  2. by Michael Abrash
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. /* New header file entries related to texture-mapped polygons */
  8. /* Draws the polygon described by the point list PointList with a bitmap
  9.    texture mapped onto it */
  10. #define DRAW_TEXTURED_POLYGON(PointList,NumPoints,TexVerts,TexMap) \
  11.    Polygon.Length = NumPoints; Polygon.PointPtr = PointList;       \
  12.    DrawTexturedPolygon(&Polygon, TexVerts, TexMap);
  13. #define FIXED_TO_INT(FixedVal) ((int) (FixedVal >> 16))
  14. #define ROUND_FIXED_TO_INT(FixedVal) \
  15.    ((int) ((FixedVal + DOUBLE_TO_FIXED(0.5)) >> 16))
  16. /* Retrieves specified pixel from specified image bitmap of specified width. */
  17. #define GET_IMAGE_PIXEL(TexMapBits, TexMapWidth, X, Y) \
  18.    TexMapBits[(Y * TexMapWidth) + X]
  19. /* Masks to mark shading types in Face structure */
  20. #define NO_SHADING      0x0000
  21. #define AMBIENT_SHADING 0x0001
  22. #define DIFFUSE_SHADING 0x0002
  23. #define TEXTURE_MAPPED_SHADING 0x0004
  24. /* Describes a texture map */
  25. typedef struct {
  26.    int TexMapWidth;  /* texture map width in bytes */
  27.    char *TexMapBits; /* pointer to texture bitmap */
  28. } TextureMap;
  29.  
  30. /* Structure describing one face of an object (one polygon) */
  31. typedef struct {
  32.    int * VertNums;   /* pointer to list of indexes of this polygon's vertices
  33.                         in the object's vertex list. The first two indexes
  34.                         must select end and start points, respectively, of this
  35.                         polygon's unit normal vector. Second point should also
  36.                         be an active polygon vertex */
  37.    int NumVerts;     /* # of verts in face, not including the initial
  38.                         vertex, which must be the end of a unit normal vector
  39.                         that starts at the second index in VertNums */
  40.    int ColorIndex;   /* direct palette index; used only for non-shaded faces */
  41.    ModelColor FullColor; /* polygon's color */
  42.    int ShadingType;  /* none, ambient, diffuse, texture mapped, etc. */
  43.    TextureMap * TexMap; /* pointer to bitmap for texture mapping, if any */
  44.    Point * TexVerts; /* pointer to list of this polygon's vertices, in
  45.                         TextureMap coordinates. Index n must map to index
  46.                         n + 1 in VertNums, (the + 1 is to skip over the unit
  47.                         normal endpoint in VertNums) */
  48. } Face;
  49. extern void DrawTexturedPolygon(PointListHeader *, Point *, TextureMap *);
  50.  
  51.  
  52.  
  53. [LISTING TWO]
  54.  
  55. /* Draws a bitmap, mapped to a convex polygon (draws a texture-mapped polygon.)
  56.    "Convex" means that every horizontal line drawn through the polygon at any
  57.    point would cross exactly two active edges (neither horizontal lines nor 
  58.    zero-length edges count as active edges; both are acceptable anywhere in 
  59.    the polygon), and that the right & left edges never cross. Nonconvex 
  60.    polygons won't be drawn properly. Can't fail. */
  61. #include <stdio.h>
  62. #include <math.h>
  63. #include "polygon.h"
  64. /* Describes the current location and stepping, in both the source and
  65.    the destination, of an edge */
  66. typedef struct {
  67.    int Direction;    /* through edge list; 1 for a right edge (forward through
  68.                         vertex list), -1 for a left edge (backward
  69.                         through vertex list) */
  70.    int RemainingScans;  /* height left to scan out in dest */
  71.    int CurrentEnd;      /* vertex # of end of current edge */
  72.    Fixedpoint SourceX;     /* current X location in source for this edge */
  73.    Fixedpoint SourceY;     /* current Y location in source for this edge */
  74.    Fixedpoint SourceStepX; /* X step in source for Y step in dest of 1 */
  75.    Fixedpoint SourceStepY; /* Y step in source for Y step in dest of 1 */
  76.                         /* variables used for all-integer Bresenham's-type
  77.                            X stepping through the dest, needed for precise
  78.                            pixel placement to avoid gaps */
  79.    int DestX;           /* current X location in dest for this edge */
  80.    int DestXIntStep;    /* whole part of dest X step per scan-line Y step */
  81.    int DestXDirection;  /* -1 or 1 to indicate way X steps (left/right) */
  82.    int DestXErrTerm;    /* current error term for dest X stepping */
  83.    int DestXAdjUp;      /* amount to add to error term per scan line move */
  84.    int DestXAdjDown;    /* amount to subtract from error term when the
  85.                            error term turns over */
  86. } EdgeScan;
  87. int StepEdge(EdgeScan *);
  88. int SetUpEdge(EdgeScan *, int);
  89. void ScanOutLine(EdgeScan *, EdgeScan *);
  90. int GetImagePixel(char *, int, int, int);
  91. /* Statics to save time that would otherwise pass them to subroutines. */
  92. static int MaxVert, NumVerts, DestY;
  93. static Point * VertexPtr;
  94. static Point * TexVertsPtr;
  95. static char * TexMapBits;
  96. static int TexMapWidth;
  97. /* Draws a texture-mapped polygon, given a list of destination polygon
  98.    vertices, a list of corresponding source texture polygon vertices, and a
  99.    pointer to the source texture's descriptor. */
  100. void DrawTexturedPolygon(PointListHeader * Polygon, Point * TexVerts,
  101.    TextureMap * TexMap)
  102. {
  103.    int MinY, MaxY, MinVert, i;
  104.    EdgeScan LeftEdge, RightEdge;
  105.    NumVerts = Polygon->Length;
  106.    VertexPtr = Polygon->PointPtr;
  107.    TexVertsPtr = TexVerts;
  108.    TexMapBits = TexMap->TexMapBits;
  109.    TexMapWidth = TexMap->TexMapWidth;
  110.    /* Nothing to draw if less than 3 vertices */
  111.    if (NumVerts < 3) {
  112.       return;
  113.    }
  114.    /* Scan through the destination polygon vertices and find the top of the
  115.       left and right edges, taking advantage of our knowledge that vertices run
  116.       in a clockwise direction (else this polygon wouldn't be visible due to 
  117.       backface removal) */
  118.    MinY = 32767;
  119.    MaxY = -32768;
  120.    for (i=0; i<NumVerts; i++) {
  121.       if (VertexPtr[i].Y < MinY) {
  122.          MinY = VertexPtr[i].Y;
  123.          MinVert = i;
  124.       }
  125.       if (VertexPtr[i].Y > MaxY) {
  126.          MaxY = VertexPtr[i].Y;
  127.          MaxVert = i;
  128.       }
  129.    }
  130.    /* Reject flat (0-pixel-high) polygons */
  131.    if (MinY >= MaxY) {
  132.       return;
  133.    }
  134.    /* The destination Y coordinate is not edge specific; it applies to
  135.       both edges, since we always step Y by 1 */
  136.    DestY = MinY;
  137.    /* Set up to scan the initial left and right edges of the source and
  138.       destination polygons. We always step the destination polygon edges
  139.       by one in Y, so calculate the corresponding destination X step for
  140.       each edge, and then the corresponding source image X and Y steps */
  141.    LeftEdge.Direction = -1;   /* set up left edge first */
  142.    SetUpEdge(&LeftEdge, MinVert);
  143.    RightEdge.Direction = 1;   /* set up right edge */
  144.    SetUpEdge(&RightEdge, MinVert);
  145.    /* Step down destination edges one scan line at a time. At each scan
  146.       line, find the corresponding edge points in the source image. Scan
  147.       between the edge points in the source, drawing the corresponding
  148.       pixels across the current scan line in the destination polygon. (We
  149.       know which way the left and right edges run through the vertex list
  150.       because visible (non-backface-culled) polygons always have the vertices
  151.       in clockwise order as seen from the viewpoint) */
  152.    for (;;) {
  153.       /* Done if off bottom of clip rectangle */
  154.       if (DestY >= ClipMaxY) {
  155.          return;
  156.       }
  157.       /* Draw only if inside Y bounds of clip rectangle */
  158.       if (DestY >= ClipMinY) {
  159.          /* Draw the scan line between the two current edges */
  160.          ScanOutLine(&LeftEdge, &RightEdge);
  161.       }
  162.       /* Advance the source and destination polygon edges, ending if we've
  163.          scanned all the way to the bottom of the polygon */
  164.       if (!StepEdge(&LeftEdge)) {
  165.          break;
  166.       }
  167.       if (!StepEdge(&RightEdge)) {
  168.          break;
  169.       }
  170.       DestY++;
  171.    }
  172. }
  173. /* Steps an edge one scan line in the destination, and the corresponding
  174.    distance in the source. If an edge runs out, starts a new edge if there
  175.    is one. Returns 1 for success, or 0 if there are no more edges to scan. */
  176. int StepEdge(EdgeScan * Edge)
  177. {
  178.    /* Count off the scan line we stepped last time; if this edge is
  179.       finished, try to start another one */
  180.    if (--Edge->RemainingScans == 0) {
  181.       /* Set up the next edge; done if there is no next edge */
  182.       if (SetUpEdge(Edge, Edge->CurrentEnd) == 0) {
  183.          return(0);  /* no more edges; done drawing polygon */
  184.       }
  185.       return(1);     /* all set to draw the new edge */
  186.    }
  187.    /* Step the current source edge */
  188.    Edge->SourceX += Edge->SourceStepX;
  189.    Edge->SourceY += Edge->SourceStepY;
  190.    /* Step dest X with Bresenham-style variables, to get precise dest pixel
  191.       placement and avoid gaps */
  192.    Edge->DestX += Edge->DestXIntStep;  /* whole pixel step */
  193.    /* Do error term stuff for fractional pixel X step handling */
  194.    if ((Edge->DestXErrTerm += Edge->DestXAdjUp) > 0) {
  195.       Edge->DestX += Edge->DestXDirection;
  196.       Edge->DestXErrTerm -= Edge->DestXAdjDown;
  197.    }
  198.    return(1);
  199. }
  200. /* Sets up an edge to be scanned; the edge starts at StartVert and proceeds
  201.    in direction Edge->Direction through the vertex list. Edge->Direction must
  202.    be set prior to call; -1 to scan a left edge (backward through the vertex
  203.    list), 1 to scan a right edge (forward through the vertex list).
  204.    Automatically skips over 0-height edges. Returns 1 for success, or 0 if
  205.    there are no more edges to scan. */
  206. int SetUpEdge(EdgeScan * Edge, int StartVert)
  207. {
  208.    int NextVert, DestXWidth;
  209.    Fixedpoint DestYHeight;
  210.    for (;;) {
  211.       /* Done if this edge starts at the bottom vertex */
  212.       if (StartVert == MaxVert) {
  213.          return(0);
  214.       }
  215.       /* Advance to the next vertex, wrapping if we run off the start or end
  216.          of the vertex list */
  217.       NextVert = StartVert + Edge->Direction;
  218.       if (NextVert >= NumVerts) {
  219.          NextVert = 0;
  220.       } else if (NextVert < 0) {
  221.          NextVert = NumVerts - 1;
  222.       }
  223.       /* Calculate the variables for this edge and done if this is not a
  224.          zero-height edge */
  225.       if ((Edge->RemainingScans =
  226.             VertexPtr[NextVert].Y - VertexPtr[StartVert].Y) != 0) {
  227.          DestYHeight = INT_TO_FIXED(Edge->RemainingScans);
  228.          Edge->CurrentEnd = NextVert;
  229.          Edge->SourceX = INT_TO_FIXED(TexVertsPtr[StartVert].X);
  230.          Edge->SourceY = INT_TO_FIXED(TexVertsPtr[StartVert].Y);
  231.          Edge->SourceStepX = FixedDiv(INT_TO_FIXED(TexVertsPtr[NextVert].X) -
  232.                Edge->SourceX, DestYHeight);
  233.          Edge->SourceStepY = FixedDiv(INT_TO_FIXED(TexVertsPtr[NextVert].Y) -
  234.                Edge->SourceY, DestYHeight);
  235.          /* Set up Bresenham-style variables for dest X stepping */
  236.          Edge->DestX = VertexPtr[StartVert].X;
  237.          if ((DestXWidth =
  238.                (VertexPtr[NextVert].X - VertexPtr[StartVert].X)) < 0) {
  239.             /* Set up for drawing right to left */
  240.             Edge->DestXDirection = -1;
  241.             DestXWidth = -DestXWidth;
  242.             Edge->DestXErrTerm = 1 - Edge->RemainingScans;
  243.             Edge->DestXIntStep = -(DestXWidth / Edge->RemainingScans);
  244.          } else {
  245.             /* Set up for drawing left to right */
  246.             Edge->DestXDirection = 1;
  247.             Edge->DestXErrTerm = 0;
  248.             Edge->DestXIntStep = DestXWidth / Edge->RemainingScans;
  249.          }
  250.          Edge->DestXAdjUp = DestXWidth % Edge->RemainingScans;
  251.          Edge->DestXAdjDown = Edge->RemainingScans;
  252.          return(1);  /* success */
  253.       }
  254.       StartVert = NextVert;   /* keep looking for a non-0-height edge */
  255.    }
  256. }
  257. /* Texture-map-draw the scan line between two edges. */
  258. void ScanOutLine(EdgeScan * LeftEdge, EdgeScan * RightEdge)
  259. {
  260.    Fixedpoint SourceX = LeftEdge->SourceX;
  261.    Fixedpoint SourceY = LeftEdge->SourceY;
  262.    int DestX = LeftEdge->DestX;
  263.    int DestXMax = RightEdge->DestX;
  264.    Fixedpoint DestWidth;
  265.    Fixedpoint SourceXStep, SourceYStep;
  266.    /* Nothing to do if fully X clipped */
  267.    if ((DestXMax <= ClipMinX) || (DestX >= ClipMaxX)) {
  268.       return;
  269.    }
  270.    if ((DestXMax - DestX) <= 0) {
  271.       return;  /* nothing to draw */
  272.    }
  273.    /* Width of destination scan line, for scaling. Note: because this is an
  274.       integer-based scaling, it can have a total error of as much as nearly
  275.       one pixel. For more precise scaling, also maintain a fixed-point DestX
  276.       in each edge, and use it for scaling. If this is done, it will also
  277.       be necessary to nudge the source start coordinates to the right by an
  278.       amount corresponding to the distance from the the real (fixed-point)
  279.       DestX and the first pixel (at an integer X) to be drawn) */
  280.    DestWidth = INT_TO_FIXED(DestXMax - DestX);
  281.    /* Calculate source steps that correspond to each dest X step (across
  282.       the scan line) */
  283.    SourceXStep = FixedDiv(RightEdge->SourceX - SourceX, DestWidth);
  284.    SourceYStep = FixedDiv(RightEdge->SourceY - SourceY, DestWidth);
  285.    /* Clip right edge if necessary */
  286.    if (DestXMax > ClipMaxX) {
  287.       DestXMax = ClipMaxX;
  288.    }
  289.    /* Clip left edge if necssary */
  290.    if (DestX < ClipMinX) {
  291.       SourceX += SourceXStep * (ClipMinX - DestX);
  292.       SourceY += SourceYStep * (ClipMinX - DestX);
  293.       DestX = ClipMinX;
  294.    }
  295.    /* Scan across the destination scan line, updating the source image
  296.       position accordingly */
  297.    for (; DestX<DestXMax; DestX++) {
  298.       /* Get currently mapped pixel out of image and draw it to screen */
  299.       WritePixelX(DestX, DestY,
  300.             GET_IMAGE_PIXEL(TexMapBits, TexMapWidth,
  301.             FIXED_TO_INT(SourceX), FIXED_TO_INT(SourceY)) );
  302.       /* Point to the next source pixel */
  303.       SourceX += SourceXStep;
  304.       SourceY += SourceYStep;
  305.    }
  306. }
  307.  
  308.