#### Texture Mapping: Orientation Independence {#Heading5} The double-DDA texture-mapping code presented in the previous chapter worked adequately, but there were two things about it that left me less than satisfied. One flaw was performance; I'll address that shortly. The other flaw was the way textures shifted noticeably as the orientations of the polygons onto which they were mapped changed. The previous chapter's code followed the standard polygon inside/outside rule for determining which pixels in the source texture map were to be mapped: Pixels that mapped exactly to the left and top destination edges were considered to be inside, and pixels that mapped exactly to the right and bottom destination edges were considered to be outside. That's fine for filling polygons, but when copying texture maps, it causes different edges of the texture map to be omitted, depending on the destination orientation, because different edges of the texture map correspond to the right and bottom destination edges, depending on the current rotation. Also, the previous chapter's code truncated to get integer source coordinates. This, together with the orientation problem, meant that when a texture turned upside down, it slowed one new row and one new column of pixels from the next row and column of the texture map. This asymmetry was quite visible, and not at all the desired effect. ![](images/57-01.jpg)\ **Figure 57.1**  *Gaps caused by mixing fixed-point and all-integer math.* Listing 57.1 is one solution to these problems. This code, which replaces the equivalently named function presented in the previous chapter (and, of course, is present in the X-Sharp archive in this chapter's subdirectory of the listings disk), makes no attempt to follow the standard polygon inside/outside rules when mapping the source. Instead, it advances a half-step into the texture map before drawing the first pixel, so pixels along all edges are half included. Rounding rather than truncation to texture-map coordinates is also performed. The result is that the texture map stays pretty much centered within the destination polygon as the destination rotates, with a much-reduced level of orientation-dependent asymmetry. **LISTING 57.1 L57-1.C** /* Texture-map-draw the scan line between two edges. Uses approach of pre-stepping 1/2 pixel into the source image and rounding to the nearest source pixel at each step, so that texture maps will appear reasonably similar at all angles. */ void ScanOutLine(EdgeScan * LeftEdge, EdgeScan * RightEdge) { Fixedpoint SourceX; Fixedpoint SourceY; int DestX = LeftEdge->DestX; int DestXMax = RightEdge->DestX; Fixedpoint DestWidth; Fixedpoint SourceStepX, SourceStepY; /* Nothing to do if fully X clipped */ if ((DestXMax <= ClipMinX) || (DestX >= ClipMaxX)) { return; } if ((DestXMax - DestX) <= 0) { return; /* nothing to draw */ } SourceX = LeftEdge->SourceX; SourceY = LeftEdge->SourceY; /* 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) */ SourceStepX = FixedDiv(RightEdge->SourceX - SourceX, DestWidth); SourceStepY = FixedDiv(RightEdge->SourceY - SourceY, DestWidth); /* Advance 1/2 step in the stepping direction, to space scanned pixels evenly between the left and right edges. (There's a slight inaccuracy in dividing negative numbers by 2 by shifting rather than dividing, but the inaccuracy is in the least significant bit, and we'll just live with it.) */ SourceX += SourceStepX >> 1; SourceY += SourceStepY >> 1; /* Clip right edge if necssary */ if (DestXMax > ClipMaxX) DestXMax = ClipMaxX; /* Clip left edge if necssary */ if (DestX < ClipMinX) { SourceX += FixedMul(SourceStepX, INT-TO-FIXED(ClipMinX - DestX)); SourceY += FixedMul(SourceStepY, INT-TO-FIXED(ClipMinX - DestX)); DestX = ClipMinX; } /* Scan across the destination scan line, updating the source image position accordingly */ for (; DestX