Listing 1. Perspective Texture Mapper
#include
#include

struct POINT3D {
	float X, Y, Z;
	float U, V;
};

void TextureMapTriangle( BITMAPINFO const *pDestInfo,
		BYTE *pDestBits, POINT3D const *pVertices,
		BITMAPINFO const *pTextureInfo,
		BYTE *pTextureBits );

/******** structures, inlines, and function declarations **********/

struct gradients {
	gradients( POINT3D const *pVertices );
	float aOneOverZ[3];				// 1/z for each vertex
	float aUOverZ[3];				// u/z for each vertex
	float aVOverZ[3];				// v/z for each vertex
	float dOneOverZdX, dOneOverZdY;	// d(1/z)/dX, d(1/z)/dY
	float dUOverZdX, dUOverZdY;		// d(u/z)/dX, d(u/z)/dY
	float dVOverZdX, dVOverZdY;		// d(v/z)/dX, d(v/z)/dY
};

struct edge {
	edge(gradients const &Gradients,
		POINT3D const *pVertices,
		int Top, int Bottom );
	inline int Step( void );

	float X, XStep;					// fractional x and dX/dY
	int Y, Height;					// current y and vert count
	float OneOverZ, OneOverZStep;		// 1/z and step
	float UOverZ, UOverZStep;			// u/z and step
	float VOverZ, VOverZStep;			// v/z and step
};

inline int edge::Step( void ) {
	X += XStep; Y++; Height„;
	UOverZ += UOverZStep; VOverZ += VOverZStep;
	OneOverZ += OneOverZStep;
	return Height;
}

void DrawScanLine( BITMAPINFO const *pDestInfo,
	BYTE *pDestBits, gradients const &Gradients,
	edge *pLeft, edge *pRight,
	BITMAPINFO const *pTextureInfo, BYTE *pTextureBits );

/******** TextureMapTriangle **********/

void TextureMapTriangle( BITMAPINFO const *pDestInfo,
		BYTE *pDestBits, POINT3D const *pVertices,
		BITMAPINFO const *pTextureInfo,
		BYTE *pTextureBits )
{
	int Top, Middle, Bottom;
	int MiddleCompare, BottomCompare;
	float Y0 = pVertices[0].Y;
	float Y1 = pVertices[1].Y;
	float Y2 = pVertices[2].Y;

	// sort vertices in y
	if(Y0 < Y1) {
		if(Y2 < Y0) {
			Top = 2; Middle = 0; Bottom = 1;
			MiddleCompare = 0; BottomCompare = 1;
		} else {
			Top = 0;
			if(Y1 < Y2) {
				Middle = 1; Bottom = 2;
				MiddleCompare = 1; BottomCompare = 2;
			} else {
				Middle = 2; Bottom = 1;
				MiddleCompare = 2; BottomCompare = 1;
			}
		}
	} else {
		if(Y2 < Y1) {
			Top = 2; Middle = 1; Bottom = 0;
			MiddleCompare = 1; BottomCompare = 0;
		} else {
			Top = 1;
			if(Y0 < Y2) {
				Middle = 0; Bottom = 2;
				MiddleCompare = 3; BottomCompare = 2;
			} else {
				Middle = 2; Bottom = 0;
				MiddleCompare = 2; BottomCompare = 3;
			}
		}
	}

	gradients Gradients(pVertices);
	edge TopToBottom(Gradients,pVertices,Top,Bottom);
	edge TopToMiddle(Gradients,pVertices,Top,Middle);
	edge MiddleToBottom(Gradients,pVertices,Middle,Bottom);
	edge *pLeft, *pRight;
	int MiddleIsLeft;

	// the triangle is clockwise, so
	// if bottom > middle then middle is right
	if(BottomCompare > MiddleCompare) {
		MiddleIsLeft = 0;
		pLeft = &TopToBottom; pRight = &TopToMiddle;
	} else {
		MiddleIsLeft = 1;
		pLeft = &TopToMiddle; pRight = &TopToBottom;
	}

	int Height = TopToMiddle.Height;

	while(Height„) {
		DrawScanLine(pDestInfo,pDestBits,Gradients,
			pLeft,pRight,pTextureInfo,pTextureBits);
		TopToMiddle.Step(); TopToBottom.Step();
	}

	Height = MiddleToBottom.Height;

	if(MiddleIsLeft) {
		pLeft = &MiddleToBottom; pRight = &TopToBottom;
	} else {
		pLeft = &TopToBottom; pRight = &MiddleToBottom;
	}
	
	while(Height„) {
		DrawScanLine(pDestInfo,pDestBits,Gradients,
			pLeft,pRight,pTextureInfo,pTextureBits);
		MiddleToBottom.Step(); TopToBottom.Step();
	}
}

/********** gradients constructor **********/

gradients::gradients( POINT3D const *pVertices )
{
	int Counter;
	
	float OneOverdX = 1 /(((pVertices[1].X - pVertices[2].X) *
			(pVertices[0].Y - pVertices[2].Y)) -
			((pVertices[0].X - pVertices[2].X) *
			(pVertices[1].Y - pVertices[2].Y)));

	float OneOverdY = -OneOverdX;

	for(Counter = 0;Counter < 3;Counter++) {
		float const OneOverZ = 1/pVertices[Counter].Z;
		aOneOverZ[Counter] = OneOverZ;
		aUOverZ[Counter] = pVertices[Counter].U * OneOverZ;
		aVOverZ[Counter] = pVertices[Counter].V * OneOverZ;
	}

	dOneOverZdX = OneOverdX * (((aOneOverZ[1] - aOneOverZ[2]) *
			(pVertices[0].Y - pVertices[2].Y)) -
			((aOneOverZ[0] - aOneOverZ[2]) *
			(pVertices[1].Y - pVertices[2].Y)));
	dOneOverZdY = OneOverdY * (((aOneOverZ[1] - aOneOverZ[2]) *
			(pVertices[0].X - pVertices[2].X)) -
			((aOneOverZ[0] - aOneOverZ[2]) *
			(pVertices[1].X - pVertices[2].X)));

	dUOverZdX = OneOverdX * (((aUOverZ[1] - aUOverZ[2]) *
			(pVertices[0].Y - pVertices[2].Y)) -
			((aUOverZ[0] - aUOverZ[2]) *
			(pVertices[1].Y - pVertices[2].Y)));
	dUOverZdY = OneOverdY * (((aUOverZ[1] - aUOverZ[2]) *
			(pVertices[0].X - pVertices[2].X)) -
			((aUOverZ[0] - aUOverZ[2]) *
			(pVertices[1].X - pVertices[2].X)));

	dVOverZdX = OneOverdX * (((aVOverZ[1] - aVOverZ[2]) *
			(pVertices[0].Y - pVertices[2].Y)) -
			((aVOverZ[0] - aVOverZ[2]) *
			(pVertices[1].Y - pVertices[2].Y)));
	dVOverZdY = OneOverdY * (((aVOverZ[1] - aVOverZ[2]) *
			(pVertices[0].X - pVertices[2].X)) -
			((aVOverZ[0] - aVOverZ[2]) *
			(pVertices[1].X - pVertices[2].X)));
}

/********** edge constructor ***********/

edge::edge( gradients const &Gradients,
		POINT3D const *pVertices, int Top, int Bottom )
{
	Y = ceil(pVertices[Top].Y);
	int YEnd = ceil(pVertices[Bottom].Y);
	Height = YEnd - Y;

	float YPrestep = Y - pVertices[Top].Y;
	
	float RealHeight = pVertices[Bottom].Y - pVertices[Top].Y;
	float RealWidth = pVertices[Bottom].X - pVertices[Top].X;

	X = ((RealWidth * YPrestep)/RealHeight) + pVertices[Top].X;
	XStep = RealWidth/RealHeight;
	float XPrestep = X - pVertices[Top].X;

	OneOverZ = Gradients.aOneOverZ[Top] +
		YPrestep * Gradients.dOneOverZdY +
		XPrestep * Gradients.dOneOverZdX;
	OneOverZStep = XStep *
		Gradients.dOneOverZdX + Gradients.dOneOverZdY;

	UOverZ = Gradients.aUOverZ[Top] +
		YPrestep * Gradients.dUOverZdY +
		XPrestep * Gradients.dUOverZdX;
	UOverZStep = XStep *
		Gradients.dUOverZdX + Gradients.dUOverZdY;

	VOverZ = Gradients.aVOverZ[Top] +
		YPrestep * Gradients.dVOverZdY +
		XPrestep * Gradients.dVOverZdX;
	VOverZStep = XStep *
		Gradients.dVOverZdX + Gradients.dVOverZdY;
}

/********** DrawScanLine ************/

void DrawScanLine( BITMAPINFO const *pDestInfo,
		BYTE *pDestBits, gradients const &Gradients,
		edge *pLeft, edge *pRight,
		BITMAPINFO const *pTextureInfo,
		BYTE *pTextureBits )
{
	// we assume dest and texture are top-down

	int DestWidthBytes =
		(pDestInfo->bmiHeader.biWidth + 3) & ~3;
	int TextureWidthBytes =
		(pTextureInfo->bmiHeader.biWidth + 3) & ~3;

	int XStart = ceil(pLeft->X);
	float XPrestep = XStart - pLeft->X;

	pDestBits += pLeft->Y * DestWidthBytes + XStart;

	int Width = ceil(pRight->X) - XStart;

	float OneOverZ = pLeft->OneOverZ +
			XPrestep * Gradients.dOneOverZdX;
	float UOverZ = pLeft->UOverZ +
			XPrestep * Gradients.dUOverZdX;
	float VOverZ = pLeft->VOverZ +
			XPrestep * Gradients.dVOverZdX;

	if(Width > 0) {
		while(Width„) {
			float Z = 1/OneOverZ;
			int U = UOverZ * Z;
			int V = VOverZ * Z;

			*(pDestBits++) = *(pTextureBits + U +
				(V * TextureWidthBytes));
	
			OneOverZ += Gradients.dOneOverZdX;
			UOverZ += Gradients.dUOverZdX;
			VOverZ += Gradients.dVOverZdX;
		}
	}
}


Discuss this article in the forums


Date this article was posted to GameDev.net: 7/16/1999
(Note that this date does not necessarily correspond to the date the article was written)

See Also:
Texture Mapping

© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!